Repository: memo/ofxMSAFluid
Branch: master
Commit: b42412365279
Files: 25
Total size: 114.6 KB
Directory structure:
gitextract_0z8_2c1k/
├── .gitignore
├── README.md
├── addon_config.mk
├── example/
│ ├── Makefile
│ ├── addons.make
│ ├── config.make
│ ├── example.qbs
│ ├── example.sln
│ ├── example.vcxproj
│ └── src/
│ ├── Particle.cpp
│ ├── Particle.h
│ ├── ParticleSystem.cpp
│ ├── ParticleSystem.h
│ ├── main.cpp
│ ├── testApp.cpp
│ └── testApp.h
├── license.md
└── src/
├── MSAFluid.h
├── MSAFluidDrawerBase.cpp
├── MSAFluidDrawerBase.h
├── MSAFluidDrawerGl-Cinder.h
├── MSAFluidDrawerGl-OF.h
├── MSAFluidParticleUpdater.h
├── MSAFluidSolver.cpp
└── MSAFluidSolver.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
#########################
# openFrameworks patterns
#########################
# build files
openFrameworks.a
openFrameworksDebug.a
openFrameworksUniversal.a
libs/openFrameworksCompiled/lib/*/*
!libs/openFrameworksCompiled/lib/*/.gitkeep
# apothecary
scripts/apothecary
# rule to avoid non-official addons going into git
# see addons/.gitignore
addons/*
# rule to avoid non-official apps going into git
# see apps/.gitignore
apps/*
# rule to ignore compiled / downloaded libs
/libs/*
!/libs/openFrameworks
!/libs/openFrameworksCompiled
# also, see examples/.gitignore
#########################
# general
#########################
[Bb]uild/
[Oo]bj/
*.o
examples/**/[Dd]ebug*/
examples/**/[Rr]elease*/
examples/**/gcc-debug/
examples/**/gcc-release/
tests/**/[Dd]ebug*/
tests/**/[Rr]elease*/
tests/**/gcc-debug/
tests/**/gcc-release/
*.mode*
*.app/
*.pyc
.svn/
*.log
*.cpp.eep
*.cpp.elf
*.cpp.hex
#########################
# IDE
#########################
# XCode
*.pbxuser
*.perspective
*.perspectivev3
*.mode1v3
*.mode2v3
# XCode 4
xcuserdata
*.xcworkspace
# Code::Blocks
*.depend
*.layout
# Visual Studio
*.sdf
*.opensdf
*.suo
*.pdb
*.ilk
*.aps
ipch/
**/.vs/*
# Eclipse
.metadata
local.properties
.externalToolBuilders
# Android Studio
.idea
.gradle
gradle
gradlew
gradlew.bat
# QtCreator
*.qbs.user
*.pro.user
*.pri
#########################
# operating system
#########################
# Linux
*~
# KDE
.directory
.AppleDouble
# OSX
.DS_Store
*.swp
*~.nib
# Thumbnails
._*
examples/ios/**/mediaAssets
# Windows
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
# Android
.csettings
/libs/openFrameworksCompiled/project/android/paths.make
# Android Studio
*.iml
#########################
# miscellaneous
#########################
.mailmap
/apps*/
#########################
# core examples
#########################
bin/*
!bin/data/
**/bin/*
!**/bin/data/
#########################
# IDE
#########################
# XCode
*.xcodeproj
Project.xcconfig
openFrameworks-Info.plist
ofxiOS-Info.plist
ofxiOS_Prefix.pch
*/*/Default*.png
*/*/Icon*.png
# Code::Blocks
# *.cbp
# *.workspace
# Visual Studio
# *.sln
# *.vcxproj
*.vcxproj.user
*.vcxproj.filters
icon.rc
# Eclipse
.cproject
.project
.settings/
# QtCreator
# *.qbs
*.qbs.user*
*.pro
*.pro.user
qtc_Desktop_*
#########################
# operating system
#########################
# Linux
# Makefile
# config.make
# Leave Android files in until project generation works
!/android/*/Makefile
!/android/*/config.make
# Android
android/*/test link
android/*/gen
android/*/res/raw
libOFAndroidApp*.so
gdbserver
gdb.setup
libneondetection.so
Application.mk
Android.mk
!android/*/.cproject
!android/*/.project
!android/*/.settings
!android/*/.settings/*
================================================
FILE: README.md
================================================
ofxMSAFluid
=====================================
Introduction
------------
C++ openFrameworks addon for solving and drawing 2D fluid systems based on Navier-Stokes equations and Jos Stam's paper "Real-Time Fluid Dynamics for Games" [http://www.dgp.toronto.edu/people/stam/reality/Research/pdf/GDC03.pdf](http://www.dgp.toronto.edu/people/stam/reality/Research/pdf/GDC03.pdf)
Demo at [www.memo.tv/msafluid/](http://www.memo.tv/msafluid)
Other useful resources and implementations I looked at while building this library:
- 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)
Licence
-------
The code in this repository is available under the [MIT License](https://secure.wikimedia.org/wikipedia/en/wiki/Mit_license).
Copyright (c) 2008-2012 Memo Akten, [www.memo.tv](http://www.memo.tv)
The Mega Super Awesome Visuals Company
Installation
------------
Copy to your openFrameworks/addons folder.
Dependencies
------------
- MSACore
Compatibility
------------
openFrameworks 0072
I am generally testing only with [openFrameworks](www.openframeworks.cc), however it should work with [Cinder](www.libcinder.org) too. If it doesn't, please file an issue.
Known issues
------------
Probably will not work with Cinder without some (minor) changes
Version history
------------
### v2.1 23/09/2012
- compatible with OF0072
- renamed (uppercase) MSA namespace to (lowercase) msa. (kept MSA as an alias for backwards compatibility)
- all classes are now inside a new namespace 'msa::fluid::'
### v2.0
- move to centralized MSALibs (requires MSACore)
- everything is msa:: namespace
- u[] and v[] condensed to (Vec2f uv[])
- r[], g[], b[] condensed to (Vec3f color[])
- unified API for getting and setting info:
- all vel & colors set and get with the structs
- all getters and setters have 3 functions, index, (i, j), Vec2f pos
### v1.2 02/05/2009
- unified API with processing.org version
- solver u, v, r, g, b arrays now public
- drawer can incDrawMode and decDrawMode
- loads of optimizations by Maa (http://www.lagraine.com/ - new content coming soon)
### v1.1 07/04/2009
- changed license to revised BSD (a lot more more permissive than GPL)
### v1.0
- added RGB or monochrome functionality (enableRGB())
- vector drawing implemented
- get and set info much improved
- added draw mode system
- setup() now only takes dimensions, other parameters have their own setters
### v0.9 04/12/08
- initial version
================================================
FILE: addon_config.mk
================================================
meta:
ADDON_NAME = ofxMSAFluid
ADDON_DESCRIPTION = C++ openFrameworks addon for Stam style Fluid Solver
ADDON_AUTHOR = Memo Akten, www.memo.tv
ADDON_TAGS = "Fluid"
ADDON_URL = https://github.com/memo/ofxMSAFluid
common:
# dependencies with other addons, a list of them separated by spaces
# or use += in several lines
ADDON_DEPENDENCIES = ofxMSACore
================================================
FILE: example/Makefile
================================================
# Attempt to load a config.make file.
# If none is found, project defaults in config.project.make will be used.
ifneq ($(wildcard config.make),)
include config.make
endif
# make sure the the OF_ROOT location is defined
ifndef OF_ROOT
OF_ROOT=$(realpath ../../..)
endif
# call the project makefile!
include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk
================================================
FILE: example/addons.make
================================================
ofxMSACore
ofxMSAFluid
ofxMSAInteractiveObject
ofxSimpleGuiToo
ofxXmlSettings
================================================
FILE: example/config.make
================================================
################################################################################
# CONFIGURE PROJECT MAKEFILE (optional)
# This file is where we make project specific configurations.
################################################################################
################################################################################
# OF ROOT
# The location of your root openFrameworks installation
# (default) OF_ROOT = ../../..
################################################################################
# OF_ROOT = ../../..
################################################################################
# PROJECT ROOT
# The location of the project - a starting place for searching for files
# (default) PROJECT_ROOT = . (this directory)
#
################################################################################
# PROJECT_ROOT = .
################################################################################
# PROJECT SPECIFIC CHECKS
# This is a project defined section to create internal makefile flags to
# conditionally enable or disable the addition of various features within
# this makefile. For instance, if you want to make changes based on whether
# GTK is installed, one might test that here and create a variable to check.
################################################################################
# None
################################################################################
# PROJECT EXTERNAL SOURCE PATHS
# These are fully qualified paths that are not within the PROJECT_ROOT folder.
# Like source folders in the PROJECT_ROOT, these paths are subject to
# exlclusion via the PROJECT_EXLCUSIONS list.
#
# (default) PROJECT_EXTERNAL_SOURCE_PATHS = (blank)
#
# Note: Leave a leading space when adding list items with the += operator
################################################################################
# PROJECT_EXTERNAL_SOURCE_PATHS =
################################################################################
# PROJECT EXCLUSIONS
# These makefiles assume that all folders in your current project directory
# and any listed in the PROJECT_EXTERNAL_SOURCH_PATHS are are valid locations
# to look for source code. The any folders or files that match any of the
# items in the PROJECT_EXCLUSIONS list below will be ignored.
#
# Each item in the PROJECT_EXCLUSIONS list will be treated as a complete
# string unless teh user adds a wildcard (%) operator to match subdirectories.
# GNU make only allows one wildcard for matching. The second wildcard (%) is
# treated literally.
#
# (default) PROJECT_EXCLUSIONS = (blank)
#
# Will automatically exclude the following:
#
# $(PROJECT_ROOT)/bin%
# $(PROJECT_ROOT)/obj%
# $(PROJECT_ROOT)/%.xcodeproj
#
# Note: Leave a leading space when adding list items with the += operator
################################################################################
# PROJECT_EXCLUSIONS =
################################################################################
# PROJECT LINKER FLAGS
# These flags will be sent to the linker when compiling the executable.
#
# (default) PROJECT_LDFLAGS = -Wl,-rpath=./libs
#
# Note: Leave a leading space when adding list items with the += operator
#
# Currently, shared libraries that are needed are copied to the
# $(PROJECT_ROOT)/bin/libs directory. The following LDFLAGS tell the linker to
# add a runtime path to search for those shared libraries, since they aren't
# incorporated directly into the final executable application binary.
################################################################################
# PROJECT_LDFLAGS=-Wl,-rpath=./libs
################################################################################
# PROJECT DEFINES
# Create a space-delimited list of DEFINES. The list will be converted into
# CFLAGS with the "-D" flag later in the makefile.
#
# (default) PROJECT_DEFINES = (blank)
#
# Note: Leave a leading space when adding list items with the += operator
################################################################################
# PROJECT_DEFINES =
################################################################################
# PROJECT CFLAGS
# This is a list of fully qualified CFLAGS required when compiling for this
# project. These CFLAGS will be used IN ADDITION TO the PLATFORM_CFLAGS
# defined in your platform specific core configuration files. These flags are
# presented to the compiler BEFORE the PROJECT_OPTIMIZATION_CFLAGS below.
#
# (default) PROJECT_CFLAGS = (blank)
#
# Note: Before adding PROJECT_CFLAGS, note that the PLATFORM_CFLAGS defined in
# your platform specific configuration file will be applied by default and
# further flags here may not be needed.
#
# Note: Leave a leading space when adding list items with the += operator
################################################################################
# PROJECT_CFLAGS =
################################################################################
# PROJECT OPTIMIZATION CFLAGS
# These are lists of CFLAGS that are target-specific. While any flags could
# be conditionally added, they are usually limited to optimization flags.
# These flags are added BEFORE the PROJECT_CFLAGS.
#
# PROJECT_OPTIMIZATION_CFLAGS_RELEASE flags are only applied to RELEASE targets.
#
# (default) PROJECT_OPTIMIZATION_CFLAGS_RELEASE = (blank)
#
# PROJECT_OPTIMIZATION_CFLAGS_DEBUG flags are only applied to DEBUG targets.
#
# (default) PROJECT_OPTIMIZATION_CFLAGS_DEBUG = (blank)
#
# Note: Before adding PROJECT_OPTIMIZATION_CFLAGS, please note that the
# PLATFORM_OPTIMIZATION_CFLAGS defined in your platform specific configuration
# file will be applied by default and further optimization flags here may not
# be needed.
#
# Note: Leave a leading space when adding list items with the += operator
################################################################################
# PROJECT_OPTIMIZATION_CFLAGS_RELEASE =
# PROJECT_OPTIMIZATION_CFLAGS_DEBUG =
################################################################################
# PROJECT COMPILERS
# Custom compilers can be set for CC and CXX
# (default) PROJECT_CXX = (blank)
# (default) PROJECT_CC = (blank)
# Note: Leave a leading space when adding list items with the += operator
################################################################################
# PROJECT_CXX =
# PROJECT_CC =
================================================
FILE: example/example.qbs
================================================
import qbs
import qbs.Process
import qbs.File
import qbs.FileInfo
import qbs.TextFile
import "../../../libs/openFrameworksCompiled/project/qtcreator/ofApp.qbs" as ofApp
Project{
property string of_root: '../../..'
ofApp {
name: { return FileInfo.baseName(sourceDirectory) }
files: [
'src/*',
]
of.addons: [
'ofxMSAFluid',
'ofxSimpleGuiToo'
]
// additional flags for the project. the of module sets some
// flags by default to add the core libraries, search paths...
// this flags can be augmented through the following properties:
of.pkgConfigs: [] // list of additional system pkgs to include
of.includePaths: [] // include search paths
of.cFlags: [] // flags passed to the c compiler
of.cxxFlags: [] // flags passed to the c++ compiler
of.linkerFlags: [] // flags passed to the linker
of.defines: [] // defines are passed as -D to the compiler
// and can be checked with #ifdef or #if in the code
of.frameworks: [] // osx only, additional frameworks to link with the project
of.staticLibraries: [] // static libraries
of.dynamicLibraries: [] // dynamic libraries
// create a console window when the application start
consoleApplication: true
// other flags can be set through the cpp module: http://doc.qt.io/qbs/cpp-module.html
// eg: this will enable ccache when compiling
//
// cpp.compilerWrapper: 'ccache'
Depends{
name: "cpp"
}
// common rules that parse the include search paths, core libraries...
Depends{
name: "of"
}
// dependency with the OF library
Depends{
name: "openFrameworks"
}
}
property bool makeOF: true // use makfiles to compile the OF library
// will compile OF only once for all your projects
// otherwise compiled per project with qbs
property bool precompileOfMain: false // precompile ofMain.h
// faster to recompile when including ofMain.h
// but might use a lot of space per project
references: [FileInfo.joinPaths(of_root, "/libs/openFrameworksCompiled/project/qtcreator/openFrameworks.qbs")]
}
================================================
FILE: example/example.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{7FD42DF7-442E-479A-BA76-D0022F99702A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openframeworksLib", "..\..\..\libs\openFrameworksCompiled\project\vs\openframeworksLib.vcxproj", "{5837595D-ACA9-485C-8E76-729040CE4B0B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7FD42DF7-442E-479A-BA76-D0022F99702A}.Debug|Win32.ActiveCfg = Debug|Win32
{7FD42DF7-442E-479A-BA76-D0022F99702A}.Debug|Win32.Build.0 = Debug|Win32
{7FD42DF7-442E-479A-BA76-D0022F99702A}.Debug|x64.ActiveCfg = Debug|x64
{7FD42DF7-442E-479A-BA76-D0022F99702A}.Debug|x64.Build.0 = Debug|x64
{7FD42DF7-442E-479A-BA76-D0022F99702A}.Release|Win32.ActiveCfg = Release|Win32
{7FD42DF7-442E-479A-BA76-D0022F99702A}.Release|Win32.Build.0 = Release|Win32
{7FD42DF7-442E-479A-BA76-D0022F99702A}.Release|x64.ActiveCfg = Release|x64
{7FD42DF7-442E-479A-BA76-D0022F99702A}.Release|x64.Build.0 = Release|x64
{5837595D-ACA9-485C-8E76-729040CE4B0B}.Debug|Win32.ActiveCfg = Debug|Win32
{5837595D-ACA9-485C-8E76-729040CE4B0B}.Debug|Win32.Build.0 = Debug|Win32
{5837595D-ACA9-485C-8E76-729040CE4B0B}.Debug|x64.ActiveCfg = Debug|x64
{5837595D-ACA9-485C-8E76-729040CE4B0B}.Debug|x64.Build.0 = Debug|x64
{5837595D-ACA9-485C-8E76-729040CE4B0B}.Release|Win32.ActiveCfg = Release|Win32
{5837595D-ACA9-485C-8E76-729040CE4B0B}.Release|Win32.Build.0 = Release|Win32
{5837595D-ACA9-485C-8E76-729040CE4B0B}.Release|x64.ActiveCfg = Release|x64
{5837595D-ACA9-485C-8E76-729040CE4B0B}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
================================================
FILE: example/example.vcxproj
================================================
Debug
Win32
Debug
x64
Release
Win32
Release
x64
$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))
$(LatestTargetPlatformVersion)
$(WindowsTargetPlatformVersion)
{7FD42DF7-442E-479A-BA76-D0022F99702A}
Win32Proj
example
Application
Unicode
v141
Application
Unicode
v141
Application
Unicode
true
v141
Application
Unicode
true
v141
bin\
obj\$(Platform)\$(Configuration)\
$(ProjectName)_debug
true
true
bin\
obj\$(Platform)\$(Configuration)\
$(ProjectName)_debug
true
true
bin\
obj\$(Platform)\$(Configuration)\
false
bin\
obj\$(Platform)\$(Configuration)\
false
Disabled
EnableFastChecks
MSA_HOST_OPENFRAMEWORKS;%(PreprocessorDefinitions)
MultiThreadedDebugDLL
Level3
%(AdditionalIncludeDirectories);src;..\..\..\addons\ofxMSACore\src;..\..\..\addons\ofxMSAFluid\src;..\..\..\addons\ofxMSAInteractiveObject\src;..\..\..\addons\ofxSimpleGuiToo\src;..\..\..\addons\ofxSimpleGuiToo\src\Controls;..\..\..\addons\ofxXmlSettings\libs;..\..\..\addons\ofxXmlSettings\src
CompileAsCpp
$(IntDir)
true
Console
false
%(AdditionalDependencies)
%(AdditionalLibraryDirectories)
Disabled
EnableFastChecks
MSA_HOST_OPENFRAMEWORKS;%(PreprocessorDefinitions)
MultiThreadedDebugDLL
Level3
%(AdditionalIncludeDirectories);src;..\..\..\addons\ofxMSACore\src;..\..\..\addons\ofxMSAFluid\src;..\..\..\addons\ofxMSAInteractiveObject\src;..\..\..\addons\ofxSimpleGuiToo\src;..\..\..\addons\ofxSimpleGuiToo\src\Controls;..\..\..\addons\ofxXmlSettings\libs;..\..\..\addons\ofxXmlSettings\src
CompileAsCpp
true
$(IntDir)
true
Console
false
%(AdditionalDependencies)
%(AdditionalLibraryDirectories)
false
MSA_HOST_OPENFRAMEWORKS;%(PreprocessorDefinitions)
MultiThreadedDLL
Level3
%(AdditionalIncludeDirectories);src;..\..\..\addons\ofxMSACore\src;..\..\..\addons\ofxMSAFluid\src;..\..\..\addons\ofxMSAInteractiveObject\src;..\..\..\addons\ofxSimpleGuiToo\src;..\..\..\addons\ofxSimpleGuiToo\src\Controls;..\..\..\addons\ofxXmlSettings\libs;..\..\..\addons\ofxXmlSettings\src
CompileAsCpp
true
$(IntDir)
false
false
Console
true
true
false
%(AdditionalDependencies)
%(AdditionalLibraryDirectories)
false
MSA_HOST_OPENFRAMEWORKS;%(PreprocessorDefinitions)
MultiThreadedDLL
Level3
%(AdditionalIncludeDirectories);src;..\..\..\addons\ofxMSACore\src;..\..\..\addons\ofxMSAFluid\src;..\..\..\addons\ofxMSAInteractiveObject\src;..\..\..\addons\ofxSimpleGuiToo\src;..\..\..\addons\ofxSimpleGuiToo\src\Controls;..\..\..\addons\ofxXmlSettings\libs;..\..\..\addons\ofxXmlSettings\src
CompileAsCpp
$(IntDir)
false
false
Console
true
true
false
%(AdditionalDependencies)
%(AdditionalLibraryDirectories)
{5837595d-aca9-485c-8e76-729040ce4b0b}
/D_DEBUG %(AdditionalOptions)
/D_DEBUG %(AdditionalOptions)
$(OF_ROOT)\libs\openFrameworksCompiled\project\vs
================================================
FILE: example/src/Particle.cpp
================================================
/*
* Particle.cpp
* ofxMSAFluid Demo
*
* Created by Mehmet Akten on 02/05/2009.
* Copyright 2009 MSA Visuals Ltd.. All rights reserved.
*
*/
#include "Particle.h"
static const float MOMENTUM = 0.5f;
static const float FLUID_FORCE = 0.6f;
void Particle::init(float x, float y) {
pos = ofVec2f( x, y );
vel = ofVec2f(0, 0);
radius = 5;
alpha = msa::Rand::randFloat( 0.3f, 1 );
mass = msa::Rand::randFloat( 0.1f, 1 );
}
void Particle::update( const msa::fluid::Solver &solver, const ofVec2f &windowSize, const ofVec2f &invWindowSize ) {
// only update if particle is visible
if( alpha == 0 )
return;
vel = solver.getVelocityAtPos( pos * invWindowSize ) * (mass * FLUID_FORCE ) * windowSize + vel * MOMENTUM;
pos += vel;
// bounce of edges
if( pos.x < 0 ) {
pos.x = 0;
vel.x *= -1;
}
else if( pos.x > windowSize.x ) {
pos.x = windowSize.x;
vel.x *= -1;
}
if( pos.y < 0 ) {
pos.y = 0;
vel.y *= -1;
}
else if( pos.y > windowSize.y ) {
pos.y = windowSize.y;
vel.y *= -1;
}
// hackish way to make particles glitter when the slow down a lot
// if( vel.squareLength() < 1 ) {
// vel += msa::Rand::randVec2f() * 0.5f;
// }
// fade out a bit (and kill if alpha == 0);
alpha *= 0.999f;
if( alpha < 0.01f )
alpha = 0;
}
void Particle::updateVertexArrays( bool drawingFluid, const ofVec2f &invWindowSize, int i, float* posBuffer, float* colBuffer) {
int vi = i * 4;
posBuffer[vi++] = pos.x - vel.x;
posBuffer[vi++] = pos.y - vel.y;
posBuffer[vi++] = pos.x;
posBuffer[vi++] = pos.y;
int ci = i * 6;
if( drawingFluid ) {
// if drawing fluid, draw lines as black & white
colBuffer[ci++] = alpha;
colBuffer[ci++] = alpha;
colBuffer[ci++] = alpha;
colBuffer[ci++] = alpha;
colBuffer[ci++] = alpha;
colBuffer[ci++] = alpha;
} else {
// otherwise, use color
float vxNorm = vel.x * invWindowSize.x;
float vyNorm = vel.y * invWindowSize.y;
float v2 = vxNorm * vxNorm + vyNorm * vyNorm;
#define VMAX 0.013f
if(v2>VMAX*VMAX) v2 = VMAX*VMAX;
float satInc = mass > 0.5 ? mass * mass * mass : 0;
satInc *= satInc * satInc * satInc;
ofColor color;
color.setHsb(0, v2 * 255.0f / ( VMAX * VMAX ) + satInc, ofLerp(0.5, 1, mass) * alpha * 255.0f);
colBuffer[ci++] = color.r;
colBuffer[ci++] = color.g;
colBuffer[ci++] = color.b;
colBuffer[ci++] = color.r;
colBuffer[ci++] = color.g;
colBuffer[ci++] = color.b;
}
}
================================================
FILE: example/src/Particle.h
================================================
/*
* Particle.h
* ofxMSAFluid Demo
*
* Created by Mehmet Akten on 02/05/2009.
* Copyright 2009 MSA Visuals Ltd.. All rights reserved.
*
*/
#pragma once
#include "MSACore.h"
#include "MSAFluidSolver.h"
class Particle {
public:
ofVec2f pos, vel;
float radius;
float alpha;
float mass;
void init(float x, float y);
void update( const msa::fluid::Solver &solver, const ofVec2f &windowSize, const ofVec2f &invWindowSize );
void updateVertexArrays( bool drawingFluid, const ofVec2f &invWindowSize, int i, float* posBuffer, float* colBuffer);
};
================================================
FILE: example/src/ParticleSystem.cpp
================================================
/*
* ParticleSystem.cpp
* ofxMSAFluid Demo
*
* Created by Mehmet Akten on 02/05/2009.
* Copyright 2009 MSA Visuals Ltd.. All rights reserved.
*
*/
#include "ParticleSystem.h"
ParticleSystem::ParticleSystem() {
curIndex = 0;
}
void ParticleSystem::updateAndDraw(const msa::fluid::Solver &solver, ofVec2f windowSize, bool drawingFluid) {
ofVec2f invWindowSize(1.0f / windowSize.x, 1.0f / windowSize.y);
glEnable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE,GL_ONE);
// glEnable(GL_LINE_SMOOTH);
ofSetLineWidth(1);
for(int i=0; i 0) {
particles[i].update(solver, windowSize, invWindowSize);
particles[i].updateVertexArrays(drawingFluid, invWindowSize, i, posArray, colArray);
}
}
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, posArray);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(3, GL_FLOAT, 0, colArray);
glDrawArrays(GL_LINES, 0, MAX_PARTICLES * 2);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisable(GL_BLEND);
}
void ParticleSystem::addParticles(const ofVec2f &pos, int count){
for(int i=0; i= MAX_PARTICLES) curIndex = 0;
}
================================================
FILE: example/src/ParticleSystem.h
================================================
/*
* ParticleSystem.h
* ofxMSAFluid Demo
*
* Created by Mehmet Akten on 02/05/2009.
* Copyright 2009 MSA Visuals Ltd.. All rights reserved.
*
*/
#pragma once
#include "Particle.h"
#define MAX_PARTICLES 50000
class ParticleSystem {
public:
float posArray[MAX_PARTICLES * 2 * 2];
float colArray[MAX_PARTICLES * 3 * 2];
int curIndex;
Particle particles[MAX_PARTICLES];
ParticleSystem();
void updateAndDraw(const msa::fluid::Solver &aSolver, ofVec2f windowSize, bool drawingFluid);
void addParticles(const ofVec2f &pos, int count);
void addParticle(const ofVec2f &pos);
};
================================================
FILE: example/src/main.cpp
================================================
#include "testApp.h"
int main( ){
ofSetupOpenGL(1024, 768, OF_WINDOW); // <-------- setup the GL context
ofRunApp(new testApp);
}
================================================
FILE: example/src/testApp.cpp
================================================
#include "testApp.h"
float tuioXScaler = 1;
float tuioYScaler = 1;
//--------------------------------------------------------------
void testApp::setup() {
//for(int i=0; i 0) {
pos.x = ofClamp(pos.x, 0.0f, 1.0f);
pos.y = ofClamp(pos.y, 0.0f, 1.0f);
int index = fluidSolver.getIndexForPos(pos);
if(addColor) {
// Color drawColor(CM_HSV, (getElapsedFrames() % 360) / 360.0f, 1, 1);
ofColor drawColor;
drawColor.setHsb((ofGetFrameNum() % 255), 255, 255);
fluidSolver.addColorAtIndex(index, drawColor * colorMult);
if(drawParticles)
particleSystem.addParticles(pos * ofVec2f(ofGetWindowSize()), 10);
}
if(addForce)
fluidSolver.addForceAtIndex(index, vel * velocityMult);
}
}
void testApp::update(){
if(resizeFluid) {
fluidSolver.setSize(fluidCellsX, fluidCellsX / msa::getWindowAspectRatio());
fluidDrawer.setup(&fluidSolver);
resizeFluid = false;
}
#ifdef USE_TUIO
tuioClient.getMessage();
// do finger stuff
listcursorList = tuioClient.getTuioCursors();
for(list::iterator it=cursorList.begin(); it != cursorList.end(); it++) {
ofxTuioCursor *tcur = (*it);
float vx = tcur->getXSpeed() * tuioCursorSpeedMult;
float vy = tcur->getYSpeed() * tuioCursorSpeedMult;
if(vx == 0 && vy == 0) {
vx = ofRandom(-tuioStationaryForce, tuioStationaryForce);
vy = ofRandom(-tuioStationaryForce, tuioStationaryForce);
}
addToFluid(ofVec2f(tcur->getX() * tuioXScaler, tcur->getY() * tuioYScaler), ofVec2f(vx, vy), true, true);
}
#endif
fluidSolver.update();
}
void testApp::draw(){
if(drawFluid) {
ofClear(0);
glColor3f(1, 1, 1);
fluidDrawer.draw(0, 0, ofGetWidth(), ofGetHeight());
} else {
// if(ofGetFrameNum()%5==0)
fadeToColor(0, 0, 0, 0.01);
}
if(drawParticles)
particleSystem.updateAndDraw(fluidSolver, ofGetWindowSize(), drawFluid);
// ofDrawBitmapString(sz, 50, 50);
#ifdef USE_GUI
gui.draw();
#endif
}
void testApp::keyPressed (int key){
switch(key) {
case '1':
fluidDrawer.setDrawMode(msa::fluid::kDrawColor);
break;
case '2':
fluidDrawer.setDrawMode(msa::fluid::kDrawMotion);
break;
case '3':
fluidDrawer.setDrawMode(msa::fluid::kDrawSpeed);
break;
case '4':
fluidDrawer.setDrawMode(msa::fluid::kDrawVectors);
break;
case 'd':
drawFluid ^= true;
break;
case 'p':
drawParticles ^= true;
break;
case 'f':
ofToggleFullscreen();
break;
case 'r':
fluidSolver.reset();
break;
case 'b': {
// Timer timer;
// const int ITERS = 3000;
// timer.start();
// for(int i = 0; i < ITERS; ++i) fluidSolver.update();
// timer.stop();
// cout << ITERS << " iterations took " << timer.getSeconds() << " seconds." << std::endl;
}
break;
}
}
//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y){
ofVec2f eventPos = ofVec2f(x, y);
ofVec2f mouseNorm = ofVec2f(eventPos) / ofGetWindowSize();
ofVec2f mouseVel = ofVec2f(eventPos - pMouse) / ofGetWindowSize();
addToFluid(mouseNorm, mouseVel, true, true);
pMouse = eventPos;
}
void testApp::mouseDragged(int x, int y, int button) {
ofVec2f eventPos = ofVec2f(x, y);
ofVec2f mouseNorm = ofVec2f(eventPos) / ofGetWindowSize();
ofVec2f mouseVel = ofVec2f(eventPos - pMouse) / ofGetWindowSize();
addToFluid(mouseNorm, mouseVel, false, true);
pMouse = eventPos;
}
================================================
FILE: example/src/testApp.h
================================================
#pragma once
#include "MSAFluid.h"
//#include "MSATimer.h"
#include "ParticleSystem.h"
#include "ofMain.h"
// comment this line out if you don't wanna use TUIO
// you will need ofxTUIO & ofxOsc
//#define USE_TUIO
// comment this line out if you don't wanna use the GUI
// you will need ofxSimpleGuiToo, ofxMSAInteractiveObject & ofxXmlSettings
// if you don't use the GUI, you won't be able to see the fluid parameters
#define USE_GUI
#ifdef USE_TUIO
#include "ofxTuio.h"
#define tuioCursorSpeedMult 0.5 // the iphone screen is so small, easy to rack up huge velocities! need to scale down
#define tuioStationaryForce 0.001f // force exerted when cursor is stationary
#endif
#ifdef USE_GUI
#include "ofxSimpleGuiToo.h"
#endif
class testApp : public ofBaseApp {
public:
float colorMult;
float velocityMult;
int fluidCellsX;
bool resizeFluid;
bool drawFluid;
bool drawParticles;
// TODO
struct {
int count;
struct Disturber {
ofVec2f pos;
ofVec2f vel;
float amp;
};
float noiseAmp;
float noiseFreq;
bool draw;
vector forces;
} randForce;
msa::fluid::Solver fluidSolver;
msa::fluid::DrawerGl fluidDrawer;
ParticleSystem particleSystem;
ofVec2f pMouse;
float vectorLength;
void setup();
void update();
void draw();
void keyPressed (int key);
void mouseMoved(int x, int y );
void mouseDragged(int x, int y, int button);
void fadeToColor(float r, float g, float b, float speed);
void addToFluid(ofVec2f pos, ofVec2f vel, bool addColor, bool addForce);
#ifdef USE_TUIO
ofxTuioClient tuioClient;
#endif
};
================================================
FILE: license.md
================================================
The code in this repository is available under the [MIT License](https://secure.wikimedia.org/wikipedia/en/wiki/Mit_license).
Copyright (c) 2008-2012 Memo Akten, [www.memo.tv](http://www.memo.tv)
The Mega Super Awesome Visuals Company
// April-Mai 2009 optimized and extended by Maa (http://www.lagraine.com/ - new content coming soon)
/* Portions Copyright (c) 2010, The Cinder Project, http://libcinder.org */
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: src/MSAFluid.h
================================================
#pragma once
#include "MSAFluidSolver.h"
#if defined( MSA_HOST_OPENFRAMEWORKS )
#include "MSAFluidDrawerGl-OF.h"
#elif defined( MSA_HOST_CINDER )
#include "MSAFluidDrawerGl-Cinder.h"
#endif
================================================
FILE: src/MSAFluidDrawerBase.cpp
================================================
/***********************************************************************
This class is for drawing a fluidsolver using the openFrameworks texture
************************************************************************/
#include "MSAFluidDrawerBase.h"
namespace msa {
namespace fluid {
//--------------------------------------------------------------
vector& getDrawModeTitles(){
static vector drawModeTitles;
if(drawModeTitles.size() == 0) {
drawModeTitles.push_back("kDrawColor");
drawModeTitles.push_back("kDrawMotion");
drawModeTitles.push_back("kDrawSpeed");
drawModeTitles.push_back("kDrawVectors");
}
return drawModeTitles;
}
//--------------------------------------------------------------
DrawerBase::DrawerBase() {
_pixels = NULL;
_fluidSolver = NULL;
_didICreateTheFluid = false;
enabled = true;
useAdditiveBlending = false;
brightness = 1;
doInvert = false;
velDrawMult = 1;
vectorSkipCount = 0;
enableAlpha(false);
setDrawMode(kDrawColor);
}
//--------------------------------------------------------------
DrawerBase::~DrawerBase() {
deleteFluidSolver();
}
//--------------------------------------------------------------
Solver* DrawerBase::setup(int NX, int NY) {
deleteFluidSolver();
_fluidSolver = new Solver;
_fluidSolver->setup(NX, NY);
allocatePixels();
createTexture();
return _fluidSolver;
}
//--------------------------------------------------------------
Solver* DrawerBase::setup(Solver* f) {
deleteFluidSolver();
_fluidSolver = f;
allocatePixels();
createTexture();
return _fluidSolver;
}
//--------------------------------------------------------------
Solver* DrawerBase::getFluidSolver() {
return _fluidSolver;
}
//--------------------------------------------------------------
void DrawerBase::enableAlpha(bool b) {
_alphaEnabled = b;
if(_alphaEnabled) {
_glType = GL_RGBA;
_bpp = 4;
} else {
_glType = GL_RGB;
_bpp = 3;
}
if(isFluidReady()) {
allocatePixels();
createTexture();
}
}
//--------------------------------------------------------------
void DrawerBase::allocatePixels() {
if(_pixels) delete []_pixels;
int texWidth = _fluidSolver->getWidth()-2;
int texHeight =_fluidSolver->getHeight()-2;
_pixels = new unsigned char[texWidth * texHeight * _bpp];
}
//--------------------------------------------------------------
void DrawerBase::reset() {
if(!isFluidReady()) {
printf("DrawerBase::reset() - Fluid not ready\n");
return;
}
_fluidSolver->reset();
}
//--------------------------------------------------------------
void DrawerBase::update() {
if(!isFluidReady()) {
printf("DrawerBase::updateFluid() - Fluid not ready\n");
return;
}
_fluidSolver->update();
}
//--------------------------------------------------------------
void DrawerBase::setDrawMode(DrawMode newDrawMode) {
drawMode = newDrawMode;
if(drawMode < 0) drawMode = (DrawMode)(kDrawCount-1);
else if(drawMode >= kDrawCount) drawMode = (DrawMode)0;
}
//--------------------------------------------------------------
void DrawerBase::incDrawMode() {
setDrawMode((DrawMode)((int)drawMode + 1));
}
//--------------------------------------------------------------
void DrawerBase::decDrawMode() {
setDrawMode((DrawMode)(drawMode - 1));
}
//--------------------------------------------------------------
DrawMode DrawerBase::getDrawMode() {
return drawMode;
}
//--------------------------------------------------------------
string DrawerBase::getDrawModeName() {
return getDrawModeTitles().at(drawMode);
}
//--------------------------------------------------------------
void DrawerBase::draw(float x, float y) const {
if(enabled == false) return;
draw(x, y, getWindowWidth(), getWindowHeight());
}
//--------------------------------------------------------------
void DrawerBase::draw(float x, float y, float renderWidth, float renderHeight) const {
if(enabled == false) return;
switch(drawMode) {
case kDrawColor:
drawColor(x, y, renderWidth, renderHeight);
break;
case kDrawMotion:
drawMotion(x, y, renderWidth, renderHeight);
break;
case kDrawSpeed:
drawSpeed(x, y, renderWidth, renderHeight);
break;
case kDrawVectors:
drawVectors(x, y, renderWidth, renderHeight);
break;
default:
break;
}
}
//--------------------------------------------------------------
void DrawerBase::drawColor(float x, float y, float renderWidth, float renderHeight, bool withAlpha) const {
if(enabled == false) return;
ofPushStyle();
if(useAdditiveBlending) ofEnableBlendMode(OF_BLENDMODE_ADD);
else ofDisableAlphaBlending();
int fw = _fluidSolver->getWidth();
int fh = _fluidSolver->getHeight();
Vec2f vel;
Color color;
int index = 0;
for(int j=1; j < fh-1; j++) {
for(int i=1; i < fw-1; i++) {
_fluidSolver->getInfoAtCell(i, j, &vel, &color);
int r = (unsigned char)min(color.r * 255 * brightness, 255.0f);
int g = (unsigned char)min(color.g * 255 * brightness, 255.0f);
int b = (unsigned char)min(color.b * 255 * brightness, 255.0f);
if(doInvert) {
r = 255 - r;
g = 255 - g;
b = 255 - b;
}
_pixels[index++] = r;
_pixels[index++] = g;
_pixels[index++] = b;
if(_alphaEnabled) _pixels[index++] = withAlpha ? max(b, max(r, g)) : 255;
}
}
updateTexture();
drawTexture(x, y, renderWidth, renderHeight);
ofPopStyle();
}
//--------------------------------------------------------------
void DrawerBase::drawMotion(float x, float y, float renderWidth, float renderHeight, bool withAlpha) const {
if(enabled == false) return;
ofPushStyle();
if(useAdditiveBlending) ofEnableBlendMode(OF_BLENDMODE_ADD);
else ofDisableAlphaBlending();
int fw = _fluidSolver->getWidth();
int fh = _fluidSolver->getHeight();
Vec2f vel;
int index = 0;
for(int j=1; j < fh-1; j++) {
for(int i=1; i < fw-1; i++) {
_fluidSolver->getInfoAtCell(i, j, &vel, NULL);
float speed2 = fabs(vel.x) * fw + fabs(vel.y) * fh;
int speed = (int)min(speed2 * 255 * brightness, 255.0f);
_pixels[index++] = (unsigned char)min(fabs(vel.x) * fw * 255 * brightness, 255.0f);
_pixels[index++] = (unsigned char)min(fabs(vel.y) * fh * 255 * brightness, 255.0f);
_pixels[index++] = (unsigned char)0;
if(_alphaEnabled) _pixels[index++] = withAlpha ? speed : 255;
}
}
updateTexture();
drawTexture(x, y, renderWidth, renderHeight);
ofPopStyle();
}
//--------------------------------------------------------------
void DrawerBase::drawSpeed(float x, float y, float renderWidth, float renderHeight, bool withAlpha) const {
if(enabled == false) return;
ofPushStyle();
if(useAdditiveBlending) ofEnableBlendMode(OF_BLENDMODE_ADD);
else ofDisableAlphaBlending();
int fw = _fluidSolver->getWidth();
int fh = _fluidSolver->getHeight();
Vec2f vel;
int index = 0;
for(int j=1; j < fh-1; j++) {
for(int i=1; i < fw-1; i++) {
_fluidSolver->getInfoAtCell(i, j, &vel, NULL);
float speed2 = fabs(vel.x) * fw + fabs(vel.y) * fh;
int speed = (int)min(speed2 * 255 * brightness, 255.0f);
_pixels[index++] = (unsigned char)speed;
_pixels[index++] = (unsigned char)speed;
_pixels[index++] = (unsigned char)speed;
if(_alphaEnabled) _pixels[index++] = withAlpha ? speed : 255;
}
}
updateTexture();
drawTexture(x, y, renderWidth, renderHeight);
ofPopStyle();
}
//--------------------------------------------------------------
void DrawerBase::drawVectors(float x, float y, float renderWidth, float renderHeight) const {
if(enabled == false) return;
ofPushStyle();
if(useAdditiveBlending) ofEnableBlendMode(OF_BLENDMODE_ADD);
else ofDisableAlphaBlending();
int fw = _fluidSolver->getWidth();
int fh = _fluidSolver->getHeight();
// int xStep = renderWidth / 10; // every 10 pixels
// int yStep = renderHeight / 10; // every 10 pixels
ofPushMatrix();
ofTranslate(x, y, 0);
ofScale(renderWidth/(fw-2), renderHeight/(fh-2), 1.0);
float maxVel = 5.0f/20000;
Vec2f vel;
float vt = velDrawThreshold * _fluidSolver->getInvWidth() * _fluidSolver->getInvHeight();
vt *= vt;
ofMesh mesh;
for (int j=0; jgetVelocityAtCell(i+1, j+1);
float d2 = vel.lengthSquared();
if(d2>vt) {
if(d2 > maxVel * maxVel) {
float mult = maxVel * maxVel/ d2;
// float mult = (d2 - maxVel * maxVel) / d2;
vel.x *= mult;
vel.y *= mult;
}
vel *= velDrawMult * 50000;
float b = mapRange(d2, vt, maxVel, 0.0f, brightness);
b = brightness*255;
mesh.addColor(ofColor::black);
mesh.addVertex(ofVec3f(pos));
mesh.addColor(ofColor(b, b, b));
mesh.addVertex(ofVec3f(pos + vel));
//ofSetColor(b, b, b);
//ofDrawLine(i, j, i + vel.x, j + vel.y);
//ofSetPolyMode(OF_POLY_WINDING_POSITIVE);
//ofNoFill();
//ofBeginShape();
//ofSetColor(0, 0, 0);
//ofVertex(i, j);
//ofSetColor(b, b, b);
//ofVertex(i + vel.x, j + vel.y);
//ofEndShape();
}
}
}
mesh.setMode(OF_PRIMITIVE_LINES);
mesh.drawWireframe();
ofPopMatrix();
ofPopStyle();
}
//--------------------------------------------------------------
void DrawerBase::deleteFluidSolver() {
// printf("DrawerBase::deleteFluidSolver()\n");
if(_fluidSolver && _didICreateTheFluid) {
delete _fluidSolver;
_fluidSolver = NULL;
if(_pixels) delete []_pixels;
_pixels = NULL;
deleteTexture();
}
}
//--------------------------------------------------------------
bool DrawerBase::isFluidReady() {
if(!_fluidSolver) {
printf("DrawerBase::isFluidReady() - No fluid solver\n");
return false;
}
if(!_fluidSolver->isInited()) {
printf("DrawerBase::isFluidReady() - Fluid solver not initialized yet, call init()\n");
return false;
}
return true;
}
}
}
================================================
FILE: src/MSAFluidDrawerBase.h
================================================
/***********************************************************************
This class is for drawing a fluidsolver using the OpenFrameworks texture
************************************************************************/
#pragma once
#include "MSACore.h"
#include "MSAFluidSolver.h"
namespace msa {
namespace fluid {
typedef enum {
kDrawColor,
kDrawMotion,
kDrawSpeed,
kDrawVectors,
kDrawCount
} DrawMode;
vector& getDrawModeTitles();
class DrawerBase
#ifdef MSA_HOST_OPENFRAMEWORKS
: public ofBaseDraws
#endif
{
public:
bool enabled;
bool doInvert;
bool useAdditiveBlending;
float brightness;
float velDrawThreshold;
float velDrawMult;
int vectorSkipCount;
DrawMode drawMode;
DrawerBase();
virtual ~DrawerBase();
Solver* setup(int NX = FLUID_DEFAULT_NX, int NY = FLUID_DEFAULT_NY);
Solver* setup(Solver* f);
Solver* getFluidSolver();
void enableAlpha(bool b);
void update();
void draw(float x, float y) const override;
void draw(float x, float y, float renderWidth, float renderHeight) const override; // this one does chooses one of the below based on drawmode
void drawColor(float x, float y, float renderWidth, float renderHeight, bool withAlpha = false) const;
void drawMotion(float x, float y, float renderWidth, float renderHeight, bool withAlpha = false) const;
void drawSpeed(float x, float y, float renderWidth, float renderHeight, bool withAlpha = false) const;
void drawVectors(float x, float y, float renderWidth, float renderHeight) const;
void reset();
// float getWidth() const override {}
//float getHeight() const override {}
void setDrawMode(DrawMode newDrawMode);
void incDrawMode();
void decDrawMode();
DrawMode getDrawMode();
string getDrawModeName();
protected:
unsigned char *_pixels; // pixels array to be drawn
int _glType; // GL_RGB or GL_RGBA
bool _alphaEnabled;
int _bpp; // 3 or 4
Solver *_fluidSolver;
bool _didICreateTheFluid; // TODO: replace with shared pointer
void allocatePixels();
virtual void createTexture() = 0; // override to create a texture
virtual void updateTexture() const = 0; // override to update the texture from the pixels array
virtual void deleteTexture() = 0; // override to delete the texture
virtual void drawTexture(float x, float y, float w, float h) const = 0; // override to draw texture
void deleteFluidSolver();
bool isFluidReady();
};
}
}
================================================
FILE: src/MSAFluidDrawerGl-Cinder.h
================================================
/***********************************************************************
This class is for drawing a fluidsolver using the OpenFrameworks texture
/***********************************************************************
Copyright (c) 2008, 2009, 2010, Memo Akten, www.memo.tv
*** The Mega Super Awesome Visuals Company ***
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of MSA Visuals nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***********************************************************************/
#pragma once
#include "MSAFluidDrawerBase.h"
#include "cinder/Surface.h"
#include "cinder/gl/Texture.h"
namespace msa {
class FluidDrawerGl : public FluidDrawerBase {
public:
float getWidth() {
return tex.getWidth();
}
float getHeight() {
return tex.getHeight();
}
protected:
ci::Surface8u surface;
ci::gl::Texture tex;
void createTexture() {
int texWidth = _fluidSolver->getWidth()-2;
int texHeight =_fluidSolver->getHeight()-2;
surface = ci::Surface8u( _pixels, texWidth, texHeight, false, ci::SurfaceChannelOrder::RGB );
ci::gl::Texture::Format format;
format.setInternalFormat( _glType );
tex = ci::gl::Texture( texWidth, texHeight, format );
}
void updateTexture() {
tex.update( surface );
}
void deleteTexture() {
tex.reset();
}
void drawTexture(float x, float y, float w, float h) {
ci::gl::draw( tex, ci::Rectf( x, y, x + w, y + h ) );
}
};
}
================================================
FILE: src/MSAFluidDrawerGl-OF.h
================================================
/***********************************************************************
This class is for drawing a fluidsolver using the OpenFrameworks texture
************************************************************************/
#pragma once
#include "MSAFluidDrawerBase.h"
namespace msa {
namespace fluid {
class DrawerGl : public DrawerBase {
public:
float getWidth() const override {
return tex.getWidth();
}
float getHeight() const override {
return tex.getHeight();
}
ofTexture& getTextureReference() {
return tex;
}
protected:
mutable ofTexture tex;
void createTexture() override {
int texWidth = _fluidSolver->getWidth()-2;
int texHeight =_fluidSolver->getHeight()-2;
tex.allocate(texWidth, texHeight, _glType);
}
void updateTexture() const override {
tex.loadData(_pixels, (int)tex.getWidth(), (int)tex.getHeight(), _glType);
}
void deleteTexture() override {
tex.clear();
}
void drawTexture(float x, float y, float w, float h) const override {
tex.draw(x, y, w, h);
}
};
}
}
================================================
FILE: src/MSAFluidParticleUpdater.h
================================================
/***********************************************************************
This class interfaces the fluid solver with ofxMSAPhysics (applies fluid velocity as forces to particles)
***********************************************************************/
// only include this if you have MSAPhysics as well
#pragma once
#include "MSAFluid.h"
#include "MSAPhysicsParticleUpdater.h"
namespace msa {
template
class FluidParticleUpdater : public Physics::ParticleUpdaterT {
public:
float strength;
Solver *fluidSolver;
FluidParticleUpdater() {
fluidSolver = NULL;
}
void update(Physics::ParticleT *p) {
if(fluidSolver) {
Vec2f fluidVel = fluidSolver->getVelocityAtPos(p->getPosition()* p->getParams()->worldSizeInv);
float invMass = p->getInvMass();
p->addVelocity(fluidVel.x * invMass * strength, fluidVel.y * invMass * strength, 0);
}
}
};
}
================================================
FILE: src/MSAFluidSolver.cpp
================================================
/***********************************************************************
This class is for the actual solver (doesn't draw anything)
************************************************************************/
#include "MSAFluidSolver.h"
namespace msa {
namespace fluid {
Solver::Solver()
:density(NULL)
,densityOld(NULL)
,uv(NULL)
,uvOld(NULL)
,color(NULL)
,colorOld(NULL)
,curl(NULL)
,_isInited(false)
{
}
//--------------------------------------------------------------
Solver& Solver::setSize(int NX, int NY)
{
_NX = NX;
_NY = NY;
_numCells = (_NX + 2) * (_NY + 2);
_invNX = 1.0f / _NX;
_invNY = 1.0f / _NY;
_invNumCells = 1.0f / _numCells;
width = getWidth();
height = getHeight();
invWidth = 1.0f/width;
invHeight = 1.0f/height;
reset();
return *this;
}
//--------------------------------------------------------------
Solver& Solver::setup(int NX, int NY)
{
setDeltaT();
setFadeSpeed();
setSolverIterations();
enableVorticityConfinement(false);
setWrap(false, false);
//maa
viscocity = FLUID_DEFAULT_VISC;
colorDiffusion = FLUID_DEFAULT_COLOR_DIFFUSION;
return setSize(NX, NY);
}
//--------------------------------------------------------------
Solver& Solver::setDeltaT(float deltaT) {
this->deltaT = deltaT;
return *this;
}
//--------------------------------------------------------------
Solver& Solver::setFadeSpeed(float fadeSpeed) {
this->fadeSpeed = fadeSpeed;
return *this;
}
//--------------------------------------------------------------
Solver& Solver::setSolverIterations(int solverIterations) {
this->solverIterations = solverIterations;
return *this;
}
//--------------------------------------------------------------
// whether fluid is RGB or monochrome (if only pressure / velocity is needed no need to update 3 channels)
Solver& Solver::enableRGB(bool doRGB) {
this->doRGB = doRGB;
return *this;
}
//--------------------------------------------------------------
Solver& Solver::enableVorticityConfinement(bool b) {
doVorticityConfinement = b;
return *this;
}
//--------------------------------------------------------------
bool Solver::getVorticityConfinement() {
return doVorticityConfinement;
}
//--------------------------------------------------------------
Solver& Solver::setWrap(bool bx, bool by) {
wrap_x = bx;
wrap_y = by;
return *this;
}
//--------------------------------------------------------------
bool Solver::isInited() const {
return _isInited;
}
//--------------------------------------------------------------
Solver::~Solver() {
destroy();
}
//--------------------------------------------------------------
void Solver::destroy() {
_isInited = false;
if(density) delete []density;
if(densityOld) delete []densityOld;
if(color) delete []color;
if(colorOld) delete []colorOld;
if(uv) delete []uv;
if(uvOld) delete []uvOld;
if(curl) delete []curl;
}
//--------------------------------------------------------------
void Solver::reset() {
destroy();
_isInited = true;
density = new float[_numCells];
densityOld = new float[_numCells];
color = new Vec3f[_numCells];
colorOld = new Vec3f[_numCells];
uv = new Vec2f[_numCells];
uvOld = new Vec2f[_numCells];
curl = new float[_numCells];
for (int i = _numCells-1; i>=0; --i) {
density[i] = 0;
densityOld[i] = 0;
color[i] = Vec3f::zero();
colorOld[i] = Vec3f::zero();
uv[i] = Vec2f::zero();
uvOld[i] = Vec2f::zero();
curl[i] = 0.0f;
}
}
//--------------------------------------------------------------
// return total number of cells (_NX+2) * (_NY+2)
int Solver::getNumCells() const {
return _numCells;
}
//--------------------------------------------------------------
int Solver::getWidth() const {
return _NX + 2;
}
//--------------------------------------------------------------
int Solver::getHeight() const {
return _NY + 2;
}
//--------------------------------------------------------------
float Solver::getInvWidth() const {
return invWidth;
}
//--------------------------------------------------------------
float Solver::getInvHeight() const {
return invHeight;
}
//--------------------------------------------------------------
Vec2f Solver::getSize() {
return Vec2f(getWidth(), getHeight());
}
//--------------------------------------------------------------
Vec2f Solver::getInvSize() {
return Vec2f(invWidth, invHeight);
}
//--------------------------------------------------------------
Solver& Solver::setVisc(float newVisc) {
viscocity = newVisc;
return *this;
}
//--------------------------------------------------------------
// returns current viscocity
float Solver::getVisc() const {
return viscocity;
}
//--------------------------------------------------------------
Solver& Solver::setColorDiffusion(float diff)
{
colorDiffusion = diff;
return *this;
}
//--------------------------------------------------------------
float Solver::getColorDiffusion()
{
return colorDiffusion;
}
//--------------------------------------------------------------
// returns average density of fluid
float Solver::getAvgDensity() const {
return _avgDensity;
}
//--------------------------------------------------------------
// returns average uniformity
float Solver::getUniformity() const {
return _uniformity;
}
//--------------------------------------------------------------
float Solver::getAvgSpeed() const {
return _avgSpeed;
}
//--------------------------------------------------------------
// Curl and vorticityConfinement based on code by Alexander McKenzie
float Solver::calcCurl(int i, int j)
{
float du_dy = uv[FLUID_IX(i, j + 1)].x - uv[FLUID_IX(i, j - 1)].x;
float dv_dx = uv[FLUID_IX(i + 1, j)].y - uv[FLUID_IX(i - 1, j)].y;
return (du_dy - dv_dx) * 0.5f; // for optimization should be moved to later and done with another operation
}
//--------------------------------------------------------------
void Solver::vorticityConfinement(Vec2f* Fvc_xy) {
float dw_dx, dw_dy;
float length;
float v;
// Calculate magnitude of calcCurl(u,v) for each cell. (|w|)
for (int j = _NY; j > 0; --j)
{
for (int i = _NX; i > 0; --i)
{
curl[FLUID_IX(i, j)] = fabs(calcCurl(i, j));
}
}
for (int j = _NY-1; j > 1; --j) //for (int j = 2; j < _NY; j++)
{
for (int i = _NX-1; i > 1; --i) //for (int i = 2; i < _NX; i++)
{
// Find derivative of the magnitude (_N = del |w|)
dw_dx = (curl[FLUID_IX(i + 1, j)] - curl[FLUID_IX(i - 1, j)]); // was * 0.5f; now done later with 2./lenght
dw_dy = (curl[FLUID_IX(i, j + 1)] - curl[FLUID_IX(i, j - 1)]); // was * 0.5f;
// Calculate vector length. (|_N|)
// Add small factor to prevent divide by zeros.
length = (float) sqrt(dw_dx * dw_dx + dw_dy * dw_dy) + 0.000001f;
// N = (_N/|_N|)
length = 2./length; // the 2. come from the previous * 0.5
dw_dx *= length;
dw_dy *= length;
v = calcCurl(i, j);
// N x w
Fvc_xy[FLUID_IX(i, j)].x = dw_dy * -v;
Fvc_xy[FLUID_IX(i, j)].y = dw_dx * v;
}
}
}
//--------------------------------------------------------------
void Solver::update() {
addSource(uv, uvOld);
if(doVorticityConfinement)
{
vorticityConfinement(uvOld);
addSource(uv, uvOld);
}
SWAP(uv, uvOld);
diffuseUV(viscocity);
project(uv, uvOld);
SWAP(uv, uvOld);
advect2d(uv, uvOld);
project(uv, uvOld);
if(doRGB)
{
addSource(color, colorOld);
SWAP(color, colorOld);
if(colorDiffusion!=0. && deltaT!=0.)
{
diffuseRGB(0, colorDiffusion);
SWAP(color, colorOld);
}
advectRGB(0, uv);
fadeRGB();
}
else
{
addSource(density, densityOld);
SWAP(density, densityOld);
if(colorDiffusion!=0. && deltaT!=0.) {
diffuse(0, density, densityOld, colorDiffusion);
SWAP(density, densityOld);
}
advect(0, density, densityOld, uv);
fadeDensity();
}
}
#define ZERO_THRESH 1e-9 // if value falls under this, set to zero (to avoid denormal slowdown)
#define CHECK_ZERO(p) if(fabsf(p)=0; --i) {
// clear old values
uvOld[i] = Vec2f::zero();
densityOld[i] = 0;
// calc avg speed
_avgSpeed += uv[i].x * uv[i].x + uv[i].y * uv[i].y;
// calc avg density
tmp = min(1.0f, density[i]);
_avgDensity += tmp; // add it up
// calc deviation (for uniformity)
currentDeviation = tmp - _avgDensity;
totalDeviations += currentDeviation * currentDeviation;
// fade out old
density[i] = tmp * holdAmount;
CHECK_ZERO(density[i]);
CHECK_ZERO(uv[i].x);
CHECK_ZERO(uv[i].y);
if(doVorticityConfinement) CHECK_ZERO(curl[i]);
}
_avgDensity *= _invNumCells;
_avgSpeed *= _invNumCells;
// println("%.3f\n", _avgSpeed);
_uniformity = 1.0f / (1 + totalDeviations * _invNumCells); // 0: very wide distribution, 1: very uniform
}
//--------------------------------------------------------------
void Solver::fadeRGB() {
// I want the fluid to gradually fade out so the screen doesn't fill. the amount it fades out depends on how full it is, and how uniform (i.e. boring) the fluid is...
// float holdAmount = 1 - _avgDensity * _avgDensity * fadeSpeed; // this is how fast the density will decay depending on how full the screen currently is
float holdAmount = 1 - fadeSpeed;
_avgDensity = 0;
_avgSpeed = 0;
float totalDeviations = 0;
float currentDeviation;
Vec3f tmp;
_avgSpeed = 0;
for (int i = _numCells-1; i >=0; --i)
{
// clear old values
uvOld[i] = Vec2f::zero();
colorOld[i] = Vec3f::zero();
// calc avg speed
_avgSpeed += uv[i].x * uv[i].x + uv[i].y * uv[i].y;
// calc avg density
tmp.x = min(1.0f, color[i].x);
tmp.y = min(1.0f, color[i].y);
tmp.z = min(1.0f, color[i].z);
// tmp.a = min(1.0f, color[i].a);
// float density = max(tmp.a, max(tmp.r, max(tmp.g, tmp.b)));
// float density = max(tmp.r, max(tmp.g, tmp.b));
float density = max(tmp.x, max(tmp.y, tmp.z));
_avgDensity += density; // add it up
// calc deviation (for _uniformity)
currentDeviation = density - _avgDensity;
totalDeviations += currentDeviation * currentDeviation;
// fade out old
color[i] = tmp * holdAmount;
CHECK_ZERO(color[i].x);
CHECK_ZERO(color[i].y);
CHECK_ZERO(color[i].z);
// CHECK_ZERO(color[i].a);
CHECK_ZERO(uv[i].x);
CHECK_ZERO(uv[i].y);
if(doVorticityConfinement) CHECK_ZERO(curl[i]);
}
_avgDensity *= _invNumCells;
_avgSpeed *= _invNumCells;
//println("%.3f\n", _avgDensity);
_uniformity = 1.0f / (1 + totalDeviations * _invNumCells); // 0: very wide distribution, 1: very uniform
}
// void Solver::addSourceUV()
// {
// for (int i = _numCells-1; i >=0; --i) {
// uv[i] += deltaT * uvOld[i];
// }
// }
//
// void Solver::addSourceRGB()
// {
// for (int i = _numCells-1; i >=0; --i) {
// color[i] += deltaT * colorOld[i];
// }
// }
//
// void Solver::addSource(float* x, float* x0) {
// for (int i = _numCells-1; i >=0; --i) {
// x[i] += deltaT * x0[i];
// }
// }
//--------------------------------------------------------------
void Solver::advect(int bound, float* d, const float* d0, const Vec2f* duv) {
int i0, j0, i1, j1;
float x, y, s0, t0, s1, t1;
int index;
const float dt0x = deltaT * _NX;
const float dt0y = deltaT * _NY;
for (int j = _NY; j > 0; --j)
{
for (int i = _NX; i > 0; --i)
{
index = FLUID_IX(i, j);
x = i - dt0x * duv[index].x;
y = j - dt0y * duv[index].y;
if (x > _NX + 0.5) x = _NX + 0.5f;
if (x < 0.5) x = 0.5f;
i0 = (int) x;
i1 = i0 + 1;
if (y > _NY + 0.5) y = _NY + 0.5f;
if (y < 0.5) y = 0.5f;
j0 = (int) y;
j1 = j0 + 1;
s1 = x - i0;
s0 = 1 - s1;
t1 = y - j0;
t0 = 1 - t1;
d[index] = s0 * (t0 * d0[FLUID_IX(i0, j0)] + t1 * d0[FLUID_IX(i0, j1)])
+ s1 * (t0 * d0[FLUID_IX(i1, j0)] + t1 * d0[FLUID_IX(i1, j1)]);
}
}
setBoundary(bound, d);
}
//--------------------------------------------------------------
// d d0 du dv
// advect(1, u, uOld, uOld, vOld);
// advect(2, v, vOld, uOld, vOld);
void Solver::advect2d(Vec2f *uv, const Vec2f *duv) {
int i0, j0, i1, j1;
float s0, t0, s1, t1;
int index;
const float dt0x = deltaT * _NX;
const float dt0y = deltaT * _NY;
for (int j = _NY; j > 0; --j)
{
for (int i = _NX; i > 0; --i)
{
index = FLUID_IX(i, j);
float x = i - dt0x * duv[index].x;
float y = j - dt0y * duv[index].y;
if (x > _NX + 0.5) x = _NX + 0.5f;
if (x < 0.5) x = 0.5f;
i0 = (int) x;
i1 = i0 + 1;
if (y > _NY + 0.5) y = _NY + 0.5f;
if (y < 0.5) y = 0.5f;
j0 = (int) y;
j1 = j0 + 1;
s1 = x - i0;
s0 = 1 - s1;
t1 = y - j0;
t0 = 1 - t1;
uv[index].x = s0 * (t0 * duv[FLUID_IX(i0, j0)].x + t1 * duv[FLUID_IX(i0, j1)].x)
+ s1 * (t0 * duv[FLUID_IX(i1, j0)].x + t1 * duv[FLUID_IX(i1, j1)].x);
uv[index].y = s0 * (t0 * duv[FLUID_IX(i0, j0)].y + t1 * duv[FLUID_IX(i0, j1)].y)
+ s1 * (t0 * duv[FLUID_IX(i1, j0)].y + t1 * duv[FLUID_IX(i1, j1)].y);
}
}
setBoundary2d(1, uv);
setBoundary2d(2, uv);
}
//--------------------------------------------------------------
void Solver::advectRGB(int bound, const Vec2f* duv) {
int i0, j0;
float x, y, s0, t0, s1, t1, dt0x, dt0y;
int index;
dt0x = deltaT * _NX;
dt0y = deltaT * _NY;
for (int j = _NY; j > 0; --j)
{
for (int i = _NX; i > 0; --i)
{
index = FLUID_IX(i, j);
x = i - dt0x * duv[index].x;
y = j - dt0y * duv[index].y;
if (x > _NX + 0.5) x = _NX + 0.5f;
if (x < 0.5) x = 0.5f;
i0 = (int) x;
if (y > _NY + 0.5) y = _NY + 0.5f;
if (y < 0.5) y = 0.5f;
j0 = (int) y;
s1 = x - i0;
s0 = 1 - s1;
t1 = y - j0;
t0 = 1 - t1;
i0 = FLUID_IX(i0, j0); //we don't need col/row index any more but index in 1 dimension
j0 = i0 + (_NX + 2);
color[index] = (colorOld[i0] * t0 + colorOld[j0] * t1) * s0 + (colorOld[i0+1] * t0 + colorOld[j0+1] * t1) * s1;
}
}
setBoundaryRGB();
}
//--------------------------------------------------------------
void Solver::diffuse(int bound, float* c, float* c0, float diff)
{
float a = deltaT * diff * _NX * _NY; //todo find the exact strategy for using _NX and _NY in the factors
linearSolver(bound, c, c0, a, 1.0 + 4 * a);
}
//--------------------------------------------------------------
void Solver::diffuseRGB(int bound, float diff)
{
float a = deltaT * diff * _NX * _NY;
linearSolverRGB(a, 1.0 + 4 * a);
}
//--------------------------------------------------------------
void Solver::diffuseUV(float diff)
{
float a = deltaT * diff * _NX * _NY;
linearSolverUV(a, 1.0 + 4 * a);
}
//--------------------------------------------------------------
void Solver::project(Vec2f* xy, Vec2f* pDiv)
{
float h;
int index;
int step_x = _NX + 2;
h = - 0.5f / _NX;
for (int j = _NY; j > 0; --j)
{
index = FLUID_IX(_NX, j);
for (int i = _NX; i > 0; --i)
{
pDiv[index].x = h * (xy[index+1].x - xy[index-1].x + xy[index+step_x].y - xy[index-step_x].y);
pDiv[index].y = 0;
--index;
}
}
setBoundary02d(reinterpret_cast(&pDiv[0].x));
setBoundary02d(reinterpret_cast(&pDiv[0].y));
linearSolverProject(pDiv);
float fx = 0.5f * _NX;
float fy = 0.5f * _NY; //maa change it from _NX to _NY
for (int j = _NY; j > 0; --j)
{
index = FLUID_IX(_NX, j);
for (int i = _NX; i > 0; --i)
{
xy[index].x -= fx * (pDiv[index+1].x - pDiv[index-1].x);
xy[index].y -= fy * (pDiv[index+step_x].x - pDiv[index-step_x].x);
--index;
}
}
setBoundary2d(1, xy);
setBoundary2d(2, xy);
}
//--------------------------------------------------------------
// Gauss-Seidel relaxation
void Solver::linearSolver(int bound, float* __restrict x, const float* __restrict x0, float a, float c)
{
int step_x = _NX + 2;
int index;
c = 1. / c;
for (int k = solverIterations; k > 0; --k) // MEMO
{
for (int j = _NY; j > 0 ; --j)
{
index = FLUID_IX(_NX, j);
for (int i = _NX; i > 0 ; --i)
{
x[index] = ((x[index-1] + x[index+1] + x[index - step_x] + x[index + step_x]) * a + x0[index]) * c;
--index;
}
}
setBoundary(bound, x);
}
}
//--------------------------------------------------------------
void Solver::linearSolverProject(Vec2f* __restrict pdiv)
{
int step_x = _NX + 2;
int index;
for (int k = solverIterations; k > 0; --k) {
for (int j = _NY; j > 0 ; --j) {
index = FLUID_IX(_NX, j);
float prev = pdiv[index+1].x;
for (int i = _NX; i > 0 ; --i)
{
prev = (pdiv[index-1].x + prev + pdiv[index - step_x].x + pdiv[index + step_x].x + pdiv[index].y) * .25;
pdiv[index].x = prev;
--index;
}
}
setBoundary02d(reinterpret_cast(&pdiv[0].x));
}
}
//--------------------------------------------------------------
void Solver::linearSolverRGB(float a, float c)
{
int index3, index4, index;
int step_x = _NX + 2;
c = 1. / c;
for (int k = solverIterations; k > 0; --k) // MEMO
{
for (int j = _NY; j > 0 ; --j)
{
index = FLUID_IX(_NX, j);
//index1 = index - 1; //FLUID_IX(i-1, j);
//index2 = index + 1; //FLUID_IX(i+1, j);
index3 = index - step_x; //FLUID_IX(i, j-1);
index4 = index + step_x; //FLUID_IX(i, j+1);
for (int i = _NX; i > 0 ; --i)
{
color[index] = ((color[index-1] + color[index+1] + color[index3] + color[index4]) * a + colorOld[index]) * c;
--index;
--index3;
--index4;
}
}
setBoundaryRGB();
}
}
//--------------------------------------------------------------
void Solver::linearSolverUV(float a, float c)
{
int index;
int step_x = _NX + 2;
c = 1. / c;
Vec2f* __restrict localUV = uv;
const Vec2f* __restrict localOldUV = uvOld;
for (int k = solverIterations; k > 0; --k) // MEMO
{
for (int j = _NY; j > 0 ; --j)
{
index = FLUID_IX(_NX, j);
float prevU = localUV[index+1].x;
float prevV = localUV[index+1].y;
for (int i = _NX; i > 0 ; --i)
{
prevU = ((localUV[index-1].x + prevU + localUV[index - step_x].x + localUV[index + step_x].x) * a + localOldUV[index].x) * c;
prevV = ((localUV[index-1].y + prevV + localUV[index - step_x].y + localUV[index + step_x].y) * a + localOldUV[index].y) * c;
localUV[index].x = prevU;
localUV[index].y = prevV;
--index;
}
}
setBoundary2d(1, uv);
}
}
//--------------------------------------------------------------
void Solver::setBoundary(int bound, float* x)
{
int dst1, dst2, src1, src2;
int step = FLUID_IX(0, 1) - FLUID_IX(0, 0);
dst1 = FLUID_IX(0, 1);
src1 = FLUID_IX(1, 1);
dst2 = FLUID_IX(_NX+1, 1);
src2 = FLUID_IX(_NX, 1);
if(wrap_x)
SWAP(src1, src2);
if(bound == 1 && !wrap_x)
for (int i = _NY; i > 0; --i)
{
x[dst1] = -x[src1]; dst1 += step; src1 += step;
x[dst2] = -x[src2]; dst2 += step; src2 += step;
}
else
for (int i = _NY; i > 0; --i)
{
x[dst1] = x[src1]; dst1 += step; src1 += step;
x[dst2] = x[src2]; dst2 += step; src2 += step;
}
dst1 = FLUID_IX(1, 0);
src1 = FLUID_IX(1, 1);
dst2 = FLUID_IX(1, _NY+1);
src2 = FLUID_IX(1, _NY);
if(wrap_y)
SWAP(src1, src2);
if(bound == 2 && !wrap_y)
for (int i = _NX; i > 0; --i)
{
x[dst1++] = -x[src1++];
x[dst2++] = -x[src2++];
}
else
for (int i = _NX; i > 0; --i)
{
x[dst1++] = x[src1++];
x[dst2++] = x[src2++];
}
x[FLUID_IX(0, 0)] = 0.5f * (x[FLUID_IX(1, 0)] + x[FLUID_IX(0, 1)]);
x[FLUID_IX(0, _NY+1)] = 0.5f * (x[FLUID_IX(1, _NY+1)] + x[FLUID_IX(0, _NY)]);
x[FLUID_IX(_NX+1, 0)] = 0.5f * (x[FLUID_IX(_NX, 0)] + x[FLUID_IX(_NX+1, 1)]);
x[FLUID_IX(_NX+1, _NY+1)] = 0.5f * (x[FLUID_IX(_NX, _NY+1)] + x[FLUID_IX(_NX+1, _NY)]);
}
//--------------------------------------------------------------
void Solver::setBoundary02d(Vec2f* x)
{
int dst1, dst2, src1, src2;
int step = FLUID_IX(0, 1) - FLUID_IX(0, 0);
dst1 = FLUID_IX(0, 1);
src1 = FLUID_IX(1, 1);
dst2 = FLUID_IX(_NX+1, 1);
src2 = FLUID_IX(_NX, 1);
if(wrap_x)
SWAP(src1, src2);
for (int i = _NY; i > 0; --i)
{
x[dst1].x = x[src1].x; dst1 += step; src1 += step;
x[dst2].x = x[src2].x; dst2 += step; src2 += step;
}
dst1 = FLUID_IX(1, 0);
src1 = FLUID_IX(1, 1);
dst2 = FLUID_IX(1, _NY+1);
src2 = FLUID_IX(1, _NY);
if(wrap_y)
SWAP(src1, src2);
for (int i = _NX; i > 0; --i)
{
x[dst1++] = x[src1++];
x[dst2++] = x[src2++];
}
x[FLUID_IX(0, 0)].x = 0.5f * (x[FLUID_IX(1, 0)].x + x[FLUID_IX(0, 1)].x);
x[FLUID_IX(0, _NY+1)].x = 0.5f * (x[FLUID_IX(1, _NY+1)].x + x[FLUID_IX(0, _NY)].x);
x[FLUID_IX(_NX+1, 0)].x = 0.5f * (x[FLUID_IX(_NX, 0)].x + x[FLUID_IX(_NX+1, 1)].x);
x[FLUID_IX(_NX+1, _NY+1)].x = 0.5f * (x[FLUID_IX(_NX, _NY+1)].x + x[FLUID_IX(_NX+1, _NY)].x);
}
//--------------------------------------------------------------
void Solver::setBoundary2d(int bound, Vec2f *xy)
{
int dst1, dst2, src1, src2;
int step = FLUID_IX(0, 1) - FLUID_IX(0, 0);
dst1 = FLUID_IX(0, 1);
src1 = FLUID_IX(1, 1);
dst2 = FLUID_IX(_NX+1, 1);
src2 = FLUID_IX(_NX, 1);
if(wrap_x)
SWAP(src1, src2);
if(bound == 1 && !wrap_x)
for (int i = _NY; i > 0; --i)
{
xy[dst1].x = -xy[src1].x; dst1 += step; src1 += step;
xy[dst2].x = -xy[src2].x; dst2 += step; src2 += step;
}
else
for (int i = _NY; i > 0; --i)
{
xy[dst1].x = xy[src1].x; dst1 += step; src1 += step;
xy[dst2].x = xy[src2].x; dst2 += step; src2 += step;
}
dst1 = FLUID_IX(1, 0);
src1 = FLUID_IX(1, 1);
dst2 = FLUID_IX(1, _NY+1);
src2 = FLUID_IX(1, _NY);
if(wrap_y)
SWAP(src1, src2);
if(bound == 2 && !wrap_y)
for (int i = _NX; i > 0; --i)
{
xy[dst1++].y = -xy[src1++].y;
xy[dst2++].y = -xy[src2++].y;
}
else
for (int i = _NX; i > 0; --i)
{
xy[dst1++].y = xy[src1++].y;
xy[dst2++].y = xy[src2++].y;
}
xy[FLUID_IX(0, 0)][bound-1] = 0.5f * (xy[FLUID_IX(1, 0)][bound-1] + xy[FLUID_IX(0, 1)][bound-1]);
xy[FLUID_IX(0, _NY+1)][bound-1] = 0.5f * (xy[FLUID_IX(1, _NY+1)][bound-1] + xy[FLUID_IX(0, _NY)][bound-1]);
xy[FLUID_IX(_NX+1, 0)][bound-1] = 0.5f * (xy[FLUID_IX(_NX, 0)][bound-1] + xy[FLUID_IX(_NX+1, 1)][bound-1]);
xy[FLUID_IX(_NX+1, _NY+1)][bound-1] = 0.5f * (xy[FLUID_IX(_NX, _NY+1)][bound-1] + xy[FLUID_IX(_NX+1, _NY)][bound-1]);
}
//#define CPY_RGB(d, s) { r[d] = r[s]; g[d] = g[s]; b[d] = b[s]; }
//#define CPY_RGB_NEG(d, s) { r[d] = -r[s]; g[d] = -g[s]; b[d] = -b[s]; }
//--------------------------------------------------------------
void Solver::setBoundaryRGB()
{
int dst1, dst2, src1, src2;
int step = FLUID_IX(0, 1) - FLUID_IX(0, 0);
dst1 = FLUID_IX(0, 1);
src1 = FLUID_IX(1, 1);
dst2 = FLUID_IX(_NX+1, 1);
src2 = FLUID_IX(_NX, 1);
if(wrap_x)
SWAP(src1, src2);
for (int i = _NY; i > 0; --i)
{
color[dst1] = color[src1];
dst1 += step;
src1 += step;
color[dst2] = color[src2];
dst2 += step;
src2 += step;
}
dst1 = FLUID_IX(1, 0);
src1 = FLUID_IX(1, 1);
dst2 = FLUID_IX(1, _NY+1);
src2 = FLUID_IX(1, _NY);
if(wrap_y)
SWAP(src1, src2);
for (int i = _NX; i > 0; --i)
{
color[dst1] = color[src1];
++dst1;
++src1;
color[dst2] = color[src2];
++dst2;
++src2;
}
}
//--------------------------------------------------------------
void Solver::randomizeColor() {
for (int i = getWidth()-1; i > 0; --i)
{
for (int j = getHeight()-1; j > 0; --j)
{
int index = FLUID_IX(i, j);
color[index] = Vec3f(Rand::randFloat(), Rand::randFloat(), Rand::randFloat());
density[index] = Rand::randFloat();
}
}
}
}
}
================================================
FILE: src/MSAFluidSolver.h
================================================
/***********************************************************************
This class is for the actual solver (doesn't draw anything)
************************************************************************/
#pragma once
#include "MSACore.h"
namespace msa {
namespace fluid {
// do not change these values, you can override them using the solver methods
#define FLUID_DEFAULT_NX 100
#define FLUID_DEFAULT_NY 100
#define FLUID_DEFAULT_DT 0.04f //Maa 25fps
#define FLUID_DEFAULT_VISC 0.0001f
#define FLUID_DEFAULT_COLOR_DIFFUSION 0.0f
#define FLUID_DEFAULT_FADESPEED 0.03f
#define FLUID_DEFAULT_SOLVER_ITERATIONS 10
#define FLUID_IX(i, j) ((i) + (_NX + 2) *(j))
class Solver {
public:
Solver();
virtual ~Solver();
Solver& setup(int NX = FLUID_DEFAULT_NX, int NY = FLUID_DEFAULT_NY);
Solver& setSize(int NX = FLUID_DEFAULT_NX, int NY = FLUID_DEFAULT_NY);
// solve one step of the fluid solver
void update();
// clear all forces in fluid and reset
void reset();
// get fluid cell index for cell coordinates or normalized position
inline int getIndexForCell(int i, int j) const;
inline int getIndexForPos(const Vec2f &pos) const;
// get color and/or vel at any point in the fluid.
// pass pointers to Vec2f (for velocity) and Color (for color) and they get filled with the info
// leave any pointer NULL if you don't want that info
inline void getInfoAtIndex(int index, Vec2f *vel, Color *color = NULL) const;
inline void getInfoAtCell(int i, int j, Vec2f *vel, Color *color = NULL) const;
inline void getInfoAtPos(const Vec2f &pos, Vec2f *vel, Color *color = NULL) const;
// get just velocity
inline Vec2f getVelocityAtIndex(int index) const;
inline Vec2f getVelocityAtCell(int i, int j) const;
inline Vec2f getVelocityAtPos(const Vec2f &pos) const;
// get just color
inline Color getColorAtIndex(int index) const;
inline Color getColorAtCell(int i, int j) const;
inline Color getColorAtPos(const Vec2f &pos) const;
// add force (at cell index, cell coordinates, or normalized position)
inline void addForceAtIndex(int index, const Vec2f &force);
inline void addForceAtCell(int i, int j, const Vec2f &force);
inline void addForceAtPos(const Vec2f &pos, const Vec2f &force);
// add color (at cell index, cell coordinates, or normalized position)
inline void addColorAtIndex(int index, const Color &color);
inline void addColorAtCell(int i, int j, const Color &color);
inline void addColorAtPos(const Vec2f &pos, const Color &color);
// fill with random color at every cell
void randomizeColor();
// return number of cells and dimensions
int getNumCells() const;
int getWidth() const;
int getHeight() const;
float getInvWidth() const;
float getInvHeight() const;
Vec2f getSize();
Vec2f getInvSize();
bool isInited() const;
// accessors for viscocity, it will lerp to the target at lerpspeed
Solver& setVisc(float newVisc);
float getVisc() const;
// accessors for color diffusion
// if diff == 0, color diffusion is not performed
// ** COLOR DIFFUSION IS SLOW!
Solver& setColorDiffusion(float diff);
float getColorDiffusion();
Solver& enableRGB(bool isRGB);
Solver& setDeltaT(float deltaT = FLUID_DEFAULT_DT);
Solver& setFadeSpeed(float fadeSpeed = FLUID_DEFAULT_FADESPEED);
Solver& setSolverIterations(int solverIterations = FLUID_DEFAULT_SOLVER_ITERATIONS);
Solver& enableVorticityConfinement(bool b);
bool getVorticityConfinement();
Solver& setWrap(bool bx, bool by);
// returns average density of fluid
float getAvgDensity() const;
// returns average _uniformity
float getUniformity() const;
// returns average speed of fluid
float getAvgSpeed() const;
// allocate an array large enough to hold information for u, v, r, g, OR b
float* alloc() { return new float[_numCells]; }
float *density, *densityOld; // used if not RGB
Vec3f *color, *colorOld; // used for RGB
Vec2f *uv, *uvOld;
float *curl;
bool doRGB; // for monochrome, update only density
bool doVorticityConfinement;
int solverIterations;
float colorDiffusion;
float viscocity;
float fadeSpeed;
float deltaT;
bool wrap_x;
bool wrap_y;
protected:
float width;
float height;
float invWidth;
float invHeight;
int _NX, _NY, _numCells;
float _invNX, _invNY, _invNumCells;
bool _isInited;
float *_tmp;
float _avgDensity; // this will hold the average color of the last frame (how full it is)
float _uniformity; // this will hold the _uniformity of the last frame (how uniform the color is);
float _avgSpeed;
void destroy();
inline float calcCurl(int i, int j);
void vorticityConfinement(Vec2f *Fvc_xy);
template
void addSource(T *x, T *x0);
void advect(int b, float *d, const float *d0, const Vec2f *duv);
void advect2d(Vec2f *uv, const Vec2f *duv);
void advectRGB(int b, const Vec2f *duv);
void diffuse(int b, float *c, float *c0, float diff);
void diffuseRGB(int b, float diff);
void diffuseUV(float diff);
void project(Vec2f *xy, Vec2f *pDiv);
void linearSolver(int b, float *x, const float *x0, float a, float c);
void linearSolverProject(Vec2f *pdiv);
void linearSolverRGB(float a, float c);
void linearSolverUV(float a, float c);
void setBoundary(int b, float *x);
void setBoundary02d(Vec2f* x);
void setBoundary2d(int b, Vec2f *xy);
void setBoundaryRGB();
void fadeDensity();
void fadeRGB();
};
//-------- get index
inline int Solver::getIndexForCell(int i, int j) const {
i = clamp(i, 1, _NX);
j = clamp(j, 1, _NY);
return FLUID_IX(i, j);
}
inline int Solver::getIndexForPos(const Vec2f &pos) const {
return getIndexForCell((int)floor(pos.x * width), (int)floor(pos.y * height));
}
//-------- get info
inline void Solver::getInfoAtIndex(int index, Vec2f *vel, Color *color) const {
if(vel) *vel = getVelocityAtIndex(index);
if(color) *color = getColorAtIndex(index);
}
inline void Solver::getInfoAtCell(int i, int j, Vec2f *vel, Color *color) const {
getInfoAtIndex(getIndexForCell(i, j), vel, color);
}
inline void Solver::getInfoAtPos(const Vec2f &pos, Vec2f *vel, Color *color) const {
getInfoAtIndex(getIndexForPos(pos), vel, color);
}
//-------- get velocity
inline Vec2f Solver::getVelocityAtIndex(int index) const {
return uv[index];
}
inline Vec2f Solver::getVelocityAtCell(int i, int j) const {
return getVelocityAtIndex(getIndexForCell(i, j));
}
inline Vec2f Solver::getVelocityAtPos(const Vec2f &pos) const {
return getVelocityAtIndex(getIndexForPos(pos));
}
//-------- get color
inline Color Solver::getColorAtIndex(int index) const {
if(doRGB) {
return Color(this->color[index].x, this->color[index].y, this->color[index].z);
} else {
return Color(density[index], density[index], density[index]);
}
}
inline Color Solver::getColorAtCell(int i, int j) const {
return getColorAtIndex(getIndexForCell(i, j));
}
inline Color Solver::getColorAtPos(const Vec2f &pos) const {
return getColorAtIndex(getIndexForPos(pos));
}
//-------- add force
inline void Solver::addForceAtIndex(int index, const Vec2f &force) {
uv[index] += force;
}
inline void Solver::addForceAtCell(int i, int j, const Vec2f &force) {
addForceAtIndex(getIndexForCell(i, j), force);
}
inline void Solver::addForceAtPos(const Vec2f &pos, const Vec2f &force) {
addForceAtIndex(getIndexForPos(pos), force);
}
//-------- add color
inline void Solver::addColorAtIndex(int index, const Color &color) {
if(doRGB) {
colorOld[index] += Vec3f(color.r, color.g, color.b);
} else {
density[index] += color.r;
}
}
inline void Solver::addColorAtCell(int i, int j, const Color &color) {
addColorAtIndex(getIndexForCell(i, j), color);
}
inline void Solver::addColorAtPos(const Vec2f &pos, const Color &color) {
addColorAtIndex(getIndexForPos(pos), color);
}
template
void Solver::addSource(T *x, T *x0) {
for(int i = _numCells-1; i >=0; --i) {
x[i] += x0[i] * deltaT;
}
}
}
}