/* ====================================================================== demo.c --- protoype to show off the simple solver ---------------------------------------------------------------------- Author : Jos Stam (jstam@aw.sgi.com) Creation Date : Jan 9 2003 Description: This code is a simple prototype that demonstrates how to use the code provided in my GDC2003 paper entitles "Real-Time Fluid Dynamics for Games". This code uses OpenGL and GLUT for graphics and interface ======================================================================= */ #include <stdlib.h> #include <stdio.h> #include <GL/glut.h> /* macros */ #define IX(i,j) ((i)+(N+2)*(j)) /* external definitions (from solver.c) */ extern void dens_step ( int N, float * x, float * x0, float * u, float * v, float diff, float dt ); extern void vel_step ( int N, float * u, float * v, float * u0, float * v0, float visc, float dt ); /* global variables */ static int N; static float dt, diff, visc; static float force, source; static int dvel; static float * u, * v, * u_prev, * v_prev; static float * dens, * dens_prev; static int win_id; static int win_x, win_y; static int mouse_down[3]; static int omx, omy, mx, my; /* ---------------------------------------------------------------------- free/clear/allocate simulation data ---------------------------------------------------------------------- */ static void free_data ( void ) { if ( u ) free ( u ); if ( v ) free ( v ); if ( u_prev ) free ( u_prev ); if ( v_prev ) free ( v_prev ); if ( dens ) free ( dens ); if ( dens_prev ) free ( dens_prev ); } static void clear_data ( void ) { int i, size=(N+2)*(N+2); for ( i=0 ; i<size ; i++ ) { u[i] = v[i] = u_prev[i] = v_prev[i] = dens[i] = dens_prev[i] = 0.0f; } } static int allocate_data ( void ) { int size = (N+2)*(N+2); u = (float *) malloc ( size*sizeof(float) ); v = (float *) malloc ( size*sizeof(float) ); u_prev = (float *) malloc ( size*sizeof(float) ); v_prev = (float *) malloc ( size*sizeof(float) ); dens = (float *) malloc ( size*sizeof(float) ); dens_prev = (float *) malloc ( size*sizeof(float) ); if ( !u || !v || !u_prev || !v_prev || !dens || !dens_prev ) { fprintf ( stderr, "cannot allocate data\n" ); return ( 0 ); } return ( 1 ); } /* ---------------------------------------------------------------------- OpenGL specific drawing routines ---------------------------------------------------------------------- */ static void pre_display ( void ) { glViewport ( 0, 0, win_x, win_y ); glMatrixMode ( GL_PROJECTION ); glLoadIdentity (); gluOrtho2D ( 0.0, 1.0, 0.0, 1.0 ); glClearColor ( 0.0f, 0.0f, 0.0f, 1.0f ); glClear ( GL_COLOR_BUFFER_BIT ); } static void post_display ( void ) { glutSwapBuffers (); } static void draw_velocity ( void ) { int i, j; float x, y, h; h = 1.0f/N; glColor3f ( 1.0f, 1.0f, 1.0f ); glLineWidth ( 1.0f ); glBegin ( GL_LINES ); for ( i=1 ; i<=N ; i++ ) { x = (i-0.5f)*h; for ( j=1 ; j<=N ; j++ ) { y = (j-0.5f)*h; glVertex2f ( x, y ); glVertex2f ( x+u[IX(i,j)], y+v[IX(i,j)] ); } } glEnd (); } static void draw_density ( void ) { int i, j; float x, y, h, d00, d01, d10, d11; h = 1.0f/N; glBegin ( GL_QUADS ); for ( i=0 ; i<=N ; i++ ) { x = (i-0.5f)*h; for ( j=0 ; j<=N ; j++ ) { y = (j-0.5f)*h; d00 = dens[IX(i,j)]; d01 = dens[IX(i,j+1)]; d10 = dens[IX(i+1,j)]; d11 = dens[IX(i+1,j+1)]; glColor3f ( d00, d00, d00 ); glVertex2f ( x, y ); glColor3f ( d10, d10, d10 ); glVertex2f ( x+h, y ); glColor3f ( d11, d11, d11 ); glVertex2f ( x+h, y+h ); glColor3f ( d01, d01, d01 ); glVertex2f ( x, y+h ); } } glEnd (); } /* ---------------------------------------------------------------------- relates mouse movements to forces sources ---------------------------------------------------------------------- */ static void get_from_UI ( float * d, float * u, float * v ) { int i, j, size = (N+2)*(N+2); for ( i=0 ; i<size ; i++ ) { u[i] = v[i] = d[i] = 0.0f; } if ( !mouse_down[0] && !mouse_down[2] ) return; i = (int)(( mx /(float)win_x)*N+1); j = (int)(((win_y-my)/(float)win_y)*N+1); if ( i<1 || i>N || j<1 || j>N ) return; if ( mouse_down[0] ) { u[IX(i,j)] = force * (mx-omx); v[IX(i,j)] = force * (omy-my); } if ( mouse_down[2] ) { d[IX(i,j)] = source; } omx = mx; omy = my; return; } /* ---------------------------------------------------------------------- GLUT callback routines ---------------------------------------------------------------------- */ static void key_func ( unsigned char key, int x, int y ) { switch ( key ) { case 'c': case 'C': clear_data (); break; case 'q': case 'Q': free_data (); exit ( 0 ); break; case 'v': case 'V': dvel = !dvel; break; } } static void mouse_func ( int button, int state, int x, int y ) { omx = mx = x; omx = my = y; mouse_down[button] = state == GLUT_DOWN; } static void motion_func ( int x, int y ) { mx = x; my = y; } static void reshape_func ( int width, int height ) { glutSetWindow ( win_id ); glutReshapeWindow ( width, height ); win_x = width; win_y = height; } static void idle_func ( void ) { get_from_UI ( dens_prev, u_prev, v_prev ); vel_step ( N, u, v, u_prev, v_prev, visc, dt ); dens_step ( N, dens, dens_prev, u, v, diff, dt ); glutSetWindow ( win_id ); glutPostRedisplay (); } static void display_func ( void ) { pre_display (); if ( dvel ) draw_velocity (); else draw_density (); post_display (); } /* ---------------------------------------------------------------------- open_glut_window --- open a glut compatible window and set callbacks ---------------------------------------------------------------------- */ static void open_glut_window ( void ) { glutInitDisplayMode ( GLUT_RGBA | GLUT_DOUBLE ); glutInitWindowPosition ( 0, 0 ); glutInitWindowSize ( win_x, win_y ); win_id = glutCreateWindow ( "Alias | wavefront" ); glClearColor ( 0.0f, 0.0f, 0.0f, 1.0f ); glClear ( GL_COLOR_BUFFER_BIT ); glutSwapBuffers (); glClear ( GL_COLOR_BUFFER_BIT ); glutSwapBuffers (); pre_display (); glutKeyboardFunc ( key_func ); glutMouseFunc ( mouse_func ); glutMotionFunc ( motion_func ); glutReshapeFunc ( reshape_func ); glutIdleFunc ( idle_func ); glutDisplayFunc ( display_func ); } /* ---------------------------------------------------------------------- main --- main routine ---------------------------------------------------------------------- */ int main ( int argc, char ** argv ) { glutInit ( &argc, argv ); if ( argc != 1 && argc != 6 ) { fprintf ( stderr, "usage : %s N dt diff visc force source\n", argv[0] ); fprintf ( stderr, "where:\n" );\ fprintf ( stderr, "\t N : grid resolution\n" ); fprintf ( stderr, "\t dt : time step\n" ); fprintf ( stderr, "\t diff : diffusion rate of the density\n" ); fprintf ( stderr, "\t visc : viscosity of the fluid\n" ); fprintf ( stderr, "\t force : scales the mouse movement that generate a force\n" ); fprintf ( stderr, "\t source : amount of density that will be deposited\n" ); exit ( 1 ); } if ( argc == 1 ) { N = 64; dt = 0.1f; diff = 0.0f; visc = 0.0f; force = 5.0f; source = 100.0f; fprintf ( stderr, "Using defaults : N=%d dt=%g diff=%g visc=%g force = %g source=%g\n", N, dt, diff, visc, force, source ); } else { N = atoi(argv[1]); dt = atof(argv[2]); diff = atof(argv[3]); visc = atof(argv[4]); force = atof(argv[5]); source = atof(argv[6]); } printf ( "\n\nHow to use this demo:\n\n" ); printf ( "\t Add densities with the right mouse button\n" ); printf ( "\t Add velocities with the left mouse button and dragging the mouse\n" ); printf ( "\t Toggle density/velocity display with the 'v' key\n" ); printf ( "\t Clear the simulation by pressing the 'c' key\n" ); printf ( "\t Quit by pressing the 'q' key\n" ); dvel = 0; if ( !allocate_data () ) exit ( 1 ); clear_data (); win_x = 512; win_y = 512; open_glut_window (); glutMainLoop (); exit ( 0 ); }