svn merge -r 16320:16369 https://svn.blender.org/svnroot/bf-blender/trunk/blender
authorDaniel Genrich <daniel.genrich@gmx.net>
Fri, 5 Sep 2008 00:12:01 +0000 (00:12 +0000)
committerDaniel Genrich <daniel.genrich@gmx.net>
Fri, 5 Sep 2008 00:12:01 +0000 (00:12 +0000)
52 files changed:
config/linux2-config.py
intern/elbeem/CMakeLists.txt
intern/elbeem/SConscript
intern/elbeem/extern/elbeem.h
intern/elbeem/intern/controlparticles.cpp [new file with mode: 0644]
intern/elbeem/intern/controlparticles.h [new file with mode: 0644]
intern/elbeem/intern/elbeem.cpp
intern/elbeem/intern/elbeem.h [deleted file]
intern/elbeem/intern/elbeem_control.cpp [new file with mode: 0644]
intern/elbeem/intern/elbeem_control.h [new file with mode: 0644]
intern/elbeem/intern/isosurface.cpp
intern/elbeem/intern/mvmcoords.cpp [new file with mode: 0644]
intern/elbeem/intern/mvmcoords.h [new file with mode: 0644]
intern/elbeem/intern/ntl_geometryclass.h
intern/elbeem/intern/ntl_geometryobject.cpp
intern/elbeem/intern/ntl_geometryobject.h
intern/elbeem/intern/ntl_ray.cpp
intern/elbeem/intern/ntl_ray.h
intern/elbeem/intern/ntl_world.cpp
intern/elbeem/intern/simulation_object.cpp
intern/elbeem/intern/solver_adap.cpp
intern/elbeem/intern/solver_class.h
intern/elbeem/intern/solver_control.cpp [new file with mode: 0644]
intern/elbeem/intern/solver_control.h [new file with mode: 0644]
intern/elbeem/intern/solver_init.cpp
intern/elbeem/intern/solver_main.cpp
intern/elbeem/intern/solver_relax.h
source/blender/blenkernel/BKE_DerivedMesh.h
source/blender/blenkernel/BKE_fluidsim.h [new file with mode: 0644]
source/blender/blenkernel/BKE_pointcache.h
source/blender/blenkernel/intern/DerivedMesh.c
source/blender/blenkernel/intern/cdderivedmesh.c
source/blender/blenkernel/intern/depsgraph.c
source/blender/blenkernel/intern/fluidsim.c [new file with mode: 0644]
source/blender/blenkernel/intern/ipo.c
source/blender/blenkernel/intern/modifier.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/particle_system.c
source/blender/blenkernel/intern/pointcache.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/makesdna/DNA_ipo_types.h
source/blender/makesdna/DNA_modifier_types.h
source/blender/makesdna/DNA_object_fluidsim.h
source/blender/render/intern/source/convertblender.c
source/blender/src/buttons_editing.c
source/blender/src/buttons_object.c
source/blender/src/editipo.c
source/blender/src/editipo_lib.c
source/blender/src/editmesh.c
source/blender/src/fluidsim.c
source/blender/src/header_ipo.c

index fe4325361a83b4874abda7fedc749fae31fbdb1e..332af28977f88a5e13924e445b0e0398fd4e2ca8 100644 (file)
@@ -166,8 +166,8 @@ CCFLAGS = ['-pipe','-fPIC','-funsigned-char','-fno-strict-aliasing']
 
 CPPFLAGS = ['-DXP_UNIX']
 CXXFLAGS = ['-pipe','-fPIC','-funsigned-char','-fno-strict-aliasing']
-REL_CFLAGS = ['-O2']
-REL_CCFLAGS = ['-O2']
+REL_CFLAGS = ['-O3']
+REL_CCFLAGS = ['-O3']
 ##BF_DEPEND = 'true'
 ##
 ##AR = ar
index bd88bc0c5f7587522050773450ce1261fccdaea5..d73d0c3e06dabc117d1671d83f794e7c9cc79ebc 100644 (file)
 #
 # ***** END GPL LICENSE BLOCK *****
 
-SET(INC ${PNG_INC} ${ZLIB_INC} ${SDL_INC})
+SET(INC ${PNG_INC} ${ZLIB_INC} ${SDL_INC} extern)
 
 FILE(GLOB SRC intern/*.cpp)
 
-ADD_DEFINITIONS(-DNOGUI -DELBEEM_BLENDER=1)
+ADD_DEFINITIONS(-DNOGUI -DELBEEM_BLENDER=1 -DLBM_INCLUDE_CONTROL=1)
 IF(WINDOWS)
     ADD_DEFINITIONS(-DUSE_MSVC6FIXES)
 ENDIF(WINDOWS)
index bdcb050798731480fef9681b3f77f408d57303d6..5ee8da8a071494a195f6b64b4d441f2e2667b290 100644 (file)
@@ -5,13 +5,14 @@ Import('env')
 
 sources = env.Glob('intern/*.cpp')
 
-defs = ' NOGUI ELBEEM_BLENDER=1'
+defs = 'NOGUI ELBEEM_BLENDER=1 LBM_INCLUDE_CONTROL=1'
 
 if env['WITH_BF_OPENMP'] == 1:
     defs += ' PARALLEL'
 
 if env['OURPLATFORM']=='win32-vc':
     defs += ' USE_MSVC6FIXES'
-incs = env['BF_PNG_INC'] + ' ' + env['BF_ZLIB_INC'] + ' ' +env['BF_SDL_INC']
+incs = env['BF_PNG_INC'] + ' ' + env['BF_ZLIB_INC'] + ' ' +env['BF_SDL_INC'] 
+incs += ' extern '
 
 env.BlenderLib ('bf_elbeem', sources, Split(incs), Split(defs), libtype='blender', priority=0 )
index 2a594dd07e65147fd443513bba72b9207009f0ad..e29890aba4437cbe9169871b513ef7939c49d909 100644 (file)
@@ -32,7 +32,7 @@ typedef struct elbeemSimulationSettings {
   short version;
        /* id number of simulation domain, needed if more than a
         * single domain should be simulated */
-       short domainId;
+       short domainId; // unused within blender
 
        /* geometrical extent */
        float geoStart[3], geoSize[3];
@@ -97,10 +97,13 @@ typedef struct elbeemSimulationSettings {
 
 
 // defines for elbeemMesh->type below
+/* please keep in sync with DNA_object_fluidsim.h */
 #define OB_FLUIDSIM_FLUID       4
 #define OB_FLUIDSIM_OBSTACLE    8
 #define OB_FLUIDSIM_INFLOW      16
 #define OB_FLUIDSIM_OUTFLOW     32
+#define OB_FLUIDSIM_PARTICLE    64
+#define OB_FLUIDSIM_CONTROL    128
 
 // defines for elbeemMesh->obstacleType below
 #define FLUIDSIM_OBSTACLE_NOSLIP     1
@@ -113,7 +116,7 @@ typedef struct elbeemSimulationSettings {
 
 // a single mesh object
 typedef struct elbeemMesh {
-  /* obstacle,fluid or inflow... */
+  /* obstacle,fluid or inflow or control ... */
   short type;
        /* id of simulation domain it belongs to */
        short parentDomainId;
@@ -155,6 +158,20 @@ typedef struct elbeemMesh {
 
        /* name of the mesh, mostly for debugging */
        const char *name;
+       
+       /* fluid control settings */
+       float cpsTimeStart;
+       float cpsTimeEnd;
+       float cpsQuality;
+       
+       int channelSizeAttractforceStrength;
+       float *channelAttractforceStrength;
+       int channelSizeAttractforceRadius;
+       float *channelAttractforceRadius;
+       int channelSizeVelocityforceStrength;
+       float *channelVelocityforceStrength;
+       int channelSizeVelocityforceRadius;
+       float *channelVelocityforceRadius;
 } elbeemMesh;
 
 // API functions
@@ -170,6 +187,9 @@ void elbeemResetSettings(struct elbeemSimulationSettings*);
 // start fluidsim init (returns !=0 upon failure)
 int elbeemInit(void);
 
+// frees fluidsim
+int elbeemFree(void);
+
 // start fluidsim init (returns !=0 upon failure)
 int elbeemAddDomain(struct elbeemSimulationSettings*);
 
@@ -223,6 +243,7 @@ double elbeemEstimateMemreq(int res,
 // structs, for these use OB_xxx defines above
 
 /*! fluid geometry init types */
+// type "int" used, so max is 8
 #define FGI_FLAGSTART   16
 #define FGI_FLUID                        (1<<(FGI_FLAGSTART+ 0))
 #define FGI_NO_FLUID     (1<<(FGI_FLAGSTART+ 1))
@@ -232,6 +253,7 @@ double elbeemEstimateMemreq(int res,
 #define FGI_NO_BND               (1<<(FGI_FLAGSTART+ 5))
 #define FGI_MBNDINFLOW (1<<(FGI_FLAGSTART+ 6))
 #define FGI_MBNDOUTFLOW        (1<<(FGI_FLAGSTART+ 7))
+#define FGI_CONTROL    (1<<(FGI_FLAGSTART+ 8))
 
 // all boundary types at once
 #define FGI_ALLBOUNDS ( FGI_BNDNO | FGI_BNDFREE | FGI_BNDPART | FGI_MBNDINFLOW | FGI_MBNDOUTFLOW )
diff --git a/intern/elbeem/intern/controlparticles.cpp b/intern/elbeem/intern/controlparticles.cpp
new file mode 100644 (file)
index 0000000..41e761c
--- /dev/null
@@ -0,0 +1,1459 @@
+// --------------------------------------------------------------------------
+//
+// El'Beem - the visual lattice boltzmann freesurface simulator
+// All code distributed as part of El'Beem is covered by the version 2 of the 
+// GNU General Public License. See the file COPYING for details.  
+//
+// Copyright 2008 Nils Thuerey , Richard Keiser, Mark Pauly, Ulrich Ruede
+//
+// implementation of control particle handling
+//
+// --------------------------------------------------------------------------
+
+// indicator for LBM inclusion
+#include "ntl_geometrymodel.h"
+#include "ntl_world.h"
+#include "solver_class.h"
+#include "controlparticles.h"
+#include "mvmcoords.h"
+#include <zlib.h>
+
+#ifndef sqrtf
+#define sqrtf sqrt
+#endif
+
+// brute force circle test init in initTimeArray
+// replaced by mDebugInit
+//#define CP_FORCECIRCLEINIT 0
+
+
+void ControlParticles::initBlenderTest() {
+       mPartSets.clear();
+
+       ControlParticleSet cps;
+       mPartSets.push_back(cps);
+       int setCnt = mPartSets.size()-1;
+       ControlParticle p; 
+
+       // set for time zero
+       mPartSets[setCnt].time = 0.;
+
+       // add single particle 
+       p.reset();
+       p.pos = LbmVec(0.5, 0.5, -0.5);
+       mPartSets[setCnt].particles.push_back(p);
+
+       // add second set for animation
+       mPartSets.push_back(cps);
+       setCnt = mPartSets.size()-1;
+       mPartSets[setCnt].time = 0.15;
+
+       // insert new position
+       p.reset();
+       p.pos = LbmVec(-0.5, -0.5, 0.5);
+       mPartSets[setCnt].particles.push_back(p);
+
+       // applyTrafos();
+       initTime(0. , 1.);
+}
+
+// blender control object gets converted to mvm flui control object
+int ControlParticles::initFromObject(ntlGeometryObjModel *model) {
+       vector<ntlTriangle> triangles;
+       vector<ntlVec3Gfx> vertices;
+       vector<ntlVec3Gfx> normals;
+       
+       /*
+       model->loadBobjModel(string(infile));
+       
+       model->setLoaded(true);
+       
+       model->setGeoInitId(gid);
+       
+       
+       printf("a animated? %d\n", model->getIsAnimated());
+       printf("b animated? %d\n", model->getMeshAnimated());
+       */
+       
+       model->setGeoInitType(FGI_FLUID);
+       
+       model->getTriangles(mCPSTimeStart, &triangles, &vertices, &normals, 1 ); 
+       // model->applyTransformation(mCPSTimeStart, &vertices, &normals, 0, vertices.size(), true);
+       
+       // valid mesh?
+       if(triangles.size() <= 0) {
+               return 0;
+       }
+
+       ntlRenderGlobals *glob = new ntlRenderGlobals;
+       ntlScene *genscene = new ntlScene( glob, false );
+       genscene->addGeoClass(model);
+       genscene->addGeoObject(model);
+       genscene->buildScene(0., false);
+       char treeFlag = (1<<(4+model->getGeoInitId()));
+
+       ntlTree *tree = new ntlTree( 
+       15, 8,  // TREEwarning - fixed values for depth & maxtriangles here...
+       genscene, treeFlag );
+
+       // TODO? use params
+       ntlVec3Gfx start,end;
+       model->getExtends(start,end);
+       /*
+       printf("start - x: %f, y: %f, z: %f\n", start[0], start[1], start[2]);
+       printf("end   - x: %f, y: %f, z: %f\n", end[0], end[1], end[2]);
+       printf("mCPSWidth: %f\n");
+*/
+       LbmFloat width = mCPSWidth;
+       if(width<=LBM_EPSILON) { errMsg("ControlParticles::initFromMVMCMesh","Invalid mCPSWidth! "<<mCPSWidth); width=mCPSWidth=0.1; }
+       ntlVec3Gfx org = start+ntlVec3Gfx(width*0.5);
+       gfxReal distance = -1.;
+       vector<ntlVec3Gfx> inspos;
+       int approxmax = (int)( ((end[0]-start[0])/width)*((end[1]-start[1])/width)*((end[2]-start[2])/width) );
+
+       // printf("distance: %f, width: %f\n", distance, width);
+       
+       while(org[2]<end[2]) {
+               while(org[1]<end[1]) {
+                       while(org[0]<end[0]) {
+                               if(checkPointInside(tree, org, distance)) {
+                                       inspos.push_back(org);
+                               }
+                               // TODO optimize, use distance
+                               org[0] += width;
+                       }
+                       org[1] += width;
+                       org[0] = start[0];
+               }
+               org[2] += width;
+               org[1] = start[1];
+       }
+       
+       // printf("inspos.size(): %d\n", inspos.size());
+
+       MeanValueMeshCoords mvm;
+       mvm.calculateMVMCs(vertices,triangles, inspos, mCPSWeightFac);
+       vector<ntlVec3Gfx> ninspos;
+       mvm.transfer(vertices, ninspos);
+
+       // init first set, check dist
+       ControlParticleSet firstcps; //T
+       mPartSets.push_back(firstcps);
+       mPartSets[mPartSets.size()-1].time = mCPSTimeStart;
+       vector<bool> useCP;
+
+       for(int i=0; i<(int)inspos.size(); i++) {
+               ControlParticle p; p.reset();
+               p.pos = vec2L(inspos[i]);
+               
+               double cpdist = norm(inspos[i]-ninspos[i]);
+               bool usecpv = true;
+
+               mPartSets[mPartSets.size()-1].particles.push_back(p);
+               useCP.push_back(usecpv);
+       }
+
+       // init further sets, temporal mesh sampling
+       double tsampling = mCPSTimestep;
+       // printf("tsampling: %f, ninspos.size(): %d, mCPSTimeEnd: %f\n", tsampling, ninspos.size(), mCPSTimeEnd);
+       
+       int totcnt = (int)( (mCPSTimeEnd-mCPSTimeStart)/tsampling ), tcnt=0;
+       for(double t=mCPSTimeStart+tsampling; ((t<mCPSTimeEnd) && (ninspos.size()>0.)); t+=tsampling) {
+               ControlParticleSet nextcps; //T
+               mPartSets.push_back(nextcps);
+               mPartSets[mPartSets.size()-1].time = (gfxReal)t;
+
+               vertices.clear(); triangles.clear(); normals.clear();
+               model->getTriangles(t, &triangles, &vertices, &normals, 1 );
+               mvm.transfer(vertices, ninspos);
+               
+               tcnt++;
+               for(size_t i=0; i < ninspos.size(); i++) {
+                       
+                       if(useCP[i]) {
+                               ControlParticle p; p.reset();
+                               p.pos = vec2L(ninspos[i]);
+                               mPartSets[mPartSets.size()-1].particles.push_back(p);
+                       }
+               }
+       }
+       
+       model->setGeoInitType(FGI_CONTROL);
+
+       delete tree;
+       delete genscene;
+       delete glob;
+       
+       // do reverse here
+       if(model->getGeoPartSlipValue())
+       {
+               mirrorTime();
+       }
+       
+       return 1;
+}
+
+
+// init all zero / defaults for a single particle
+void ControlParticle::reset() {
+       pos = LbmVec(0.,0.,0.);
+       vel = LbmVec(0.,0.,0.);
+       influence = 1.;
+       size = 1.;
+#ifndef LBMDIM
+#ifdef MAIN_2D
+       rotaxis = LbmVec(0.,1.,0.); // SPH xz
+#else // MAIN_2D
+       // 3d - roate in xy plane, vortex
+       rotaxis = LbmVec(0.,0.,1.);
+       // 3d - rotate for wave
+       //rotaxis = LbmVec(0.,1.,0.);
+#endif // MAIN_2D
+#else // LBMDIM
+       rotaxis = LbmVec(0.,1.,0.); // LBM xy , is swapped afterwards
+#endif // LBMDIM
+
+       density = 0.;
+       densityWeight = 0.;
+       avgVelAcc = avgVel = LbmVec(0.);
+       avgVelWeight = 0.;
+}
+
+
+// default preset/empty init
+ControlParticles::ControlParticles() :
+       _influenceTangential(0.f),
+       _influenceAttraction(0.f),
+       _influenceVelocity(0.f),
+       _influenceMaxdist(0.f),
+       _radiusAtt(1.0f),
+       _radiusVel(1.0f),
+       _radiusMinMaxd(2.0f),
+       _radiusMaxd(3.0f),
+       _currTime(-1.0), _currTimestep(1.),
+       _initTimeScale(1.), 
+       _initPartOffset(0.), _initPartScale(1.),
+       _initLastPartOffset(0.), _initLastPartScale(1.),
+       _initMirror(""),
+       _fluidSpacing(1.), _kernelWeight(-1.),
+       _charLength(1.), _charLengthInv(1.),
+       mvCPSStart(-10000.), mvCPSEnd(10000.),
+       mCPSWidth(0.1), mCPSTimestep(0.02), // was 0.05
+       mCPSTimeStart(0.), mCPSTimeEnd(0.5), mCPSWeightFac(1.),
+       mDebugInit(0)
+{
+       _radiusAtt = 0.15f;
+       _radiusVel = 0.15f;
+       _radiusMinMaxd = 0.16f;
+       _radiusMaxd = 0.3;
+
+       _influenceAttraction = 0.f;
+       _influenceTangential = 0.f;
+       _influenceVelocity = 0.f;
+       // 3d tests */
+}
+
+
+ControlParticles::~ControlParticles() {
+       // nothing to do...
+}
+
+LbmFloat ControlParticles::getControlTimStart() {
+       if(mPartSets.size()>0) { return mPartSets[0].time; }
+       return -1000.;
+}
+LbmFloat ControlParticles::getControlTimEnd() {
+       if(mPartSets.size()>0) { return mPartSets[mPartSets.size()-1].time; }
+       return -1000.;
+}
+
+// calculate for delta t
+void ControlParticles::setInfluenceVelocity(LbmFloat set, LbmFloat dt) {
+       const LbmFloat dtInter = 0.01;
+       LbmFloat facFv = 1.-set; //cparts->getInfluenceVelocity();
+       // mLevel[mMaxRefine].timestep
+       LbmFloat facNv = (LbmFloat)( 1.-pow( (double)facFv, (double)(dt/dtInter)) );
+       //errMsg("vwcalc","ts:"<<dt<< " its:"<<(dt/dtInter) <<" fv"<<facFv<<" nv"<<facNv<<" test:"<< pow( (double)(1.-facNv),(double)(dtInter/dt))      );
+       _influenceVelocity = facNv;
+}
+
+int ControlParticles::initExampleSet()
+{
+       // unused
+       return 0;
+}
+
+int ControlParticles::getTotalSize()
+{
+       int s=0;
+       for(int i=0; i<(int)mPartSets.size(); i++) {
+               s+= mPartSets[i].particles.size();
+       }
+       return s;
+}
+
+// --------------------------------------------------------------------------
+// load positions & timing from text file
+// WARNING - make sure file has unix format, no win/dos linefeeds...
+#define LINE_LEN 100
+int ControlParticles::initFromTextFile(string filename)
+{
+       const bool debugRead = false;
+       char line[LINE_LEN];
+       line[LINE_LEN-1] = '\0';
+       mPartSets.clear();
+       if(filename.size()<2) return 0;
+
+       // HACK , use "cparts" suffix as old
+       // e.g. "cpart2" as new
+       if(filename[ filename.size()-1 ]=='s') {
+               return initFromTextFileOld(filename);
+       }
+
+       FILE *infile = fopen(filename.c_str(), "r");
+       if(!infile) {
+               errMsg("ControlParticles::initFromTextFile","unable to open '"<<filename<<"' " );
+               // try to open as gz sequence
+               if(initFromBinaryFile(filename)) { return 1; }
+               // try mesh MVCM generation
+               if(initFromMVCMesh(filename)) { return 1; }
+               // failed...
+               return 0;
+       }
+
+       int haveNo = false;
+       int haveScale = false;
+       int haveTime = false;
+       int noParts = -1;
+       int partCnt = 0;
+       int setCnt = 0;
+       //ControlParticle p; p.reset();
+       // scale times by constant factor while reading
+       LbmFloat timeScale= 1.0;
+       int lineCnt = 0;
+       bool abortParse = false;
+#define LASTCP mPartSets[setCnt].particles[ mPartSets[setCnt].particles.size()-1 ]
+
+       while( (!feof(infile)) && (!abortParse)) {
+               lineCnt++;
+               fgets(line, LINE_LEN, infile);
+
+               //if(debugRead) printf("\nDEBUG%d r '%s'\n",lineCnt, line);
+               if(!line) continue;
+               int len = (int)strlen(line);
+
+               // skip empty lines and comments (#,//)
+               if(len<1) continue;
+               if( (line[0]=='#') || (line[0]=='\n') ) continue;
+               if((len>1) && (line[0]=='/' && line[1]=='/')) continue;
+
+               // debug remove newline
+               if((len>=1)&&(line[len-1]=='\n')) line[len-1]='\0';
+
+               switch(line[0]) {
+
+               case 'N': { // total number of particles, more for debugging...
+                       noParts = atoi(line+2);
+                       if(noParts<=0) {
+                               errMsg("ControlParticles::initFromTextFile","file '"<<filename<<"' - invalid no of particles "<<noParts);
+                               mPartSets.clear(); fclose(infile); return 0;
+                       }
+                       if(debugRead) printf("CPDEBUG%d no parts '%d'\n",lineCnt, noParts );
+                       haveNo = true;
+                       } break;
+
+               case 'T': { // global time scale
+                       timeScale *= (LbmFloat)atof(line+2);
+                       if(debugRead) printf("ControlParticles::initFromTextFile - line %d , set timescale '%f', org %f\n",lineCnt, timeScale , _initTimeScale);
+                       if(timeScale==0.) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error: timescale = 0.! reseting to 1 ...\n",lineCnt); timeScale=1.; }
+                       haveScale = true;
+                       } break;
+
+               case 'I': { // influence settings, overrides others as of now...
+                       float val = (LbmFloat)atof(line+3);
+                       const char *setvar = "[invalid]";
+                       switch(line[1]) {
+                               //case 'f': { _influenceFalloff = val; setvar = "falloff"; } break;
+                               case 't': { _influenceTangential = val; setvar = "tangential"; } break;
+                               case 'a': { _influenceAttraction = val; setvar = "attraction"; } break;
+                               case 'v': { _influenceVelocity = val; setvar = "velocity"; } break;
+                               case 'm': { _influenceMaxdist = val; setvar = "maxdist"; } break;
+                               default: 
+                                       fprintf(stdout,"ControlParticles::initFromTextFile (%s) - line %d , invalid influence setting %c, %f\n",filename.c_str() ,lineCnt, line[1], val);
+                       }
+                       if(debugRead) printf("CPDEBUG%d set influence '%s'=%f \n",lineCnt, setvar, val);
+                       } break;
+
+               case 'R': { // radius settings, overrides others as of now...
+                       float val = (LbmFloat)atof(line+3);
+                       const char *setvar = "[invalid]";
+                       switch(line[1]) {
+                               case 'a': { _radiusAtt = val; setvar = "r_attraction"; } break;
+                               case 'v': { _radiusVel = val; setvar = "r_velocity"; } break;
+                               case 'm': { _radiusMaxd = val; setvar = "r_maxdist"; } break;
+                               default: 
+                                       fprintf(stdout,"ControlParticles::initFromTextFile (%s) - line %d , invalid influence setting %c, %f\n",filename.c_str() ,lineCnt, line[1], val);
+                       }
+                       if(debugRead) printf("CPDEBUG%d set influence '%s'=%f \n",lineCnt, setvar, val);
+                       } break;
+
+               case 'S': { // new particle set at time T
+                       ControlParticleSet cps;
+                       mPartSets.push_back(cps);
+                       setCnt = (int)mPartSets.size()-1;
+
+                       LbmFloat val = (LbmFloat)atof(line+2);
+                       mPartSets[setCnt].time = val * timeScale;
+                       if(debugRead) printf("CPDEBUG%d new set, time '%f', %d\n",lineCnt, mPartSets[setCnt].time, setCnt );
+                       haveTime = true;
+                       partCnt = -1;
+                       } break;
+
+               case 'P':   // new particle with pos
+               case 'n': { // new particle without pos
+                               if((!haveTime)||(setCnt<0)) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error: set missing!\n",lineCnt); abortParse=true; break; }
+                               partCnt++;
+                               if(partCnt>=noParts) {
+                                       if(debugRead) printf("CPDEBUG%d partset done \n",lineCnt);
+                                       haveTime = false;
+                               } else {
+                                       ControlParticle p; p.reset();
+                                       mPartSets[setCnt].particles.push_back(p);
+                               }
+                       } 
+                       // only new part, or new with pos?
+                       if(line[0] == 'n') break;
+
+               // particle properties
+
+               case 'p': { // new particle set at time T
+                       if((!haveTime)||(setCnt<0)||(mPartSets[setCnt].particles.size()<1)) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error|p: particle missing!\n",lineCnt); abortParse=true; break; }
+                       float px=0.,py=0.,pz=0.;
+                       if( sscanf(line+2,"%f %f %f",&px,&py,&pz) != 3) {
+                               fprintf(stdout,"CPDEBUG%d, unable to parse position!\n",lineCnt); abortParse=true; break; 
+                       }
+                       if(!(finite(px)&&finite(py)&&finite(pz))) { px=py=pz=0.; }
+                       LASTCP.pos[0] = px;
+                       LASTCP.pos[1] = py;
+                       LASTCP.pos[2] = pz; 
+                       if(debugRead) printf("CPDEBUG%d part%d,%d: position %f,%f,%f \n",lineCnt,setCnt,partCnt, px,py,pz);
+                       } break;
+
+               case 's': { // particle size
+                       if((!haveTime)||(setCnt<0)||(mPartSets[setCnt].particles.size()<1)) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error|s: particle missing!\n",lineCnt); abortParse=true; break; }
+                       float ps=1.;
+                       if( sscanf(line+2,"%f",&ps) != 1) {
+                               fprintf(stdout,"CPDEBUG%d, unable to parse size!\n",lineCnt); abortParse=true; break; 
+                       }
+                       if(!(finite(ps))) { ps=0.; }
+                       LASTCP.size = ps;
+                       if(debugRead) printf("CPDEBUG%d part%d,%d: size %f \n",lineCnt,setCnt,partCnt, ps);
+                       } break;
+
+               case 'i': { // particle influence
+                       if((!haveTime)||(setCnt<0)||(mPartSets[setCnt].particles.size()<1)) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error|i: particle missing!\n",lineCnt); abortParse=true; break; }
+                       float pinf=1.;
+                       if( sscanf(line+2,"%f",&pinf) != 1) {
+                               fprintf(stdout,"CPDEBUG%d, unable to parse size!\n",lineCnt); abortParse=true; break; 
+                       }
+                       if(!(finite(pinf))) { pinf=0.; }
+                       LASTCP.influence = pinf;
+                       if(debugRead) printf("CPDEBUG%d part%d,%d: influence %f \n",lineCnt,setCnt,partCnt, pinf);
+                       } break;
+
+               case 'a': { // rotation axis
+                       if((!haveTime)||(setCnt<0)||(mPartSets[setCnt].particles.size()<1)) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error|a: particle missing!\n",lineCnt); abortParse=true; break; }
+                       float px=0.,py=0.,pz=0.;
+                       if( sscanf(line+2,"%f %f %f",&px,&py,&pz) != 3) {
+                               fprintf(stdout,"CPDEBUG%d, unable to parse rotaxis!\n",lineCnt); abortParse=true; break; 
+                       }
+                       if(!(finite(px)&&finite(py)&&finite(pz))) { px=py=pz=0.; }
+                       LASTCP.rotaxis[0] = px;
+                       LASTCP.rotaxis[1] = py;
+                       LASTCP.rotaxis[2] = pz; 
+                       if(debugRead) printf("CPDEBUG%d part%d,%d: rotaxis %f,%f,%f \n",lineCnt,setCnt,partCnt, px,py,pz);
+                       } break;
+
+
+               default:
+                       if(debugRead) printf("CPDEBUG%d ignored: '%s'\n",lineCnt, line );
+                       break;
+               }
+       }
+       if(debugRead && abortParse) printf("CPDEBUG aborted parsing after set... %d\n",(int)mPartSets.size() );
+
+       // sanity check
+       for(int i=0; i<(int)mPartSets.size(); i++) {
+               if( (int)mPartSets[i].particles.size()!=noParts) {
+                       fprintf(stdout,"ControlParticles::initFromTextFile (%s) - invalid no of particles in set %d, is:%d, shouldbe:%d \n",filename.c_str() ,i,(int)mPartSets[i].particles.size(), noParts);
+                       mPartSets.clear();
+                       fclose(infile);
+                       return 0;
+               }
+       }
+
+       // print stats
+       printf("ControlParticles::initFromTextFile (%s): Read %d sets, each %d particles\n",filename.c_str() ,
+                       (int)mPartSets.size(), noParts );
+       if(mPartSets.size()>0) {
+               printf("ControlParticles::initFromTextFile (%s): Time: %f,%f\n",filename.c_str() ,mPartSets[0].time, mPartSets[mPartSets.size()-1].time );
+       }
+       
+       // done...
+       fclose(infile);
+       applyTrafos();
+       return 1;
+}
+
+
+int ControlParticles::initFromTextFileOld(string filename)
+{
+       const bool debugRead = false;
+       char line[LINE_LEN];
+       line[LINE_LEN-1] = '\0';
+       mPartSets.clear();
+       if(filename.size()<1) return 0;
+
+       FILE *infile = fopen(filename.c_str(), "r");
+       if(!infile) {
+               fprintf(stdout,"ControlParticles::initFromTextFileOld - unable to open '%s'\n",filename.c_str() );
+               return 0;
+       }
+
+       int haveNo = false;
+       int haveScale = false;
+       int haveTime = false;
+       int noParts = -1;
+       int coordCnt = 0;
+       int partCnt = 0;
+       int setCnt = 0;
+       ControlParticle p; p.reset();
+       // scale times by constant factor while reading
+       LbmFloat timeScale= 1.0;
+       int lineCnt = 0;
+
+       while(!feof(infile)) {
+               lineCnt++;
+               fgets(line, LINE_LEN, infile);
+
+               if(debugRead) printf("\nDEBUG%d r '%s'\n",lineCnt, line);
+
+               if(!line) continue;
+               int len = (int)strlen(line);
+
+               // skip empty lines and comments (#,//)
+               if(len<1) continue;
+               if( (line[0]=='#') || (line[0]=='\n') ) continue;
+               if((len>1) && (line[0]=='/' && line[1]=='/')) continue;
+
+               // debug remove newline
+               if((len>=1)&&(line[len-1]=='\n')) line[len-1]='\0';
+
+               // first read no. of particles
+               if(!haveNo) {
+                       noParts = atoi(line);
+                       if(noParts<=0) {
+                               fprintf(stdout,"ControlParticles::initFromTextFileOld - invalid no of particles %d\n",noParts);
+                               mPartSets.clear();
+                               fclose(infile);
+                               return 0;
+                       }
+                       if(debugRead) printf("DEBUG%d noparts '%d'\n",lineCnt, noParts );
+                       haveNo = true;
+               } 
+
+               // then read time scale
+               else if(!haveScale) {
+                       timeScale *= (LbmFloat)atof(line);
+                       if(debugRead) printf("DEBUG%d tsc '%f', org %f\n",lineCnt, timeScale , _initTimeScale);
+                       haveScale = true;
+               } 
+
+               // then get set time
+               else if(!haveTime) {
+                       ControlParticleSet cps;
+                       mPartSets.push_back(cps);
+                       setCnt = (int)mPartSets.size()-1;
+
+                       LbmFloat val = (LbmFloat)atof(line);
+                       mPartSets[setCnt].time = val * timeScale;
+                       if(debugRead) printf("DEBUG%d time '%f', %d\n",lineCnt, mPartSets[setCnt].time, setCnt );
+                       haveTime = true;
+               }
+
+               // default read all parts
+               else {
+                       LbmFloat val = (LbmFloat)atof(line);
+                       if(debugRead) printf("DEBUG: l%d s%d,particle%d '%f' %d,%d/%d\n",lineCnt,(int)mPartSets.size(),(int)mPartSets[setCnt].particles.size(), val ,coordCnt,partCnt,noParts);
+                       p.pos[coordCnt] = val;
+                       coordCnt++;
+                       if(coordCnt>=3) {
+                               mPartSets[setCnt].particles.push_back(p);
+                               p.reset();
+                               coordCnt=0;
+                               partCnt++;
+                       }
+                       if(partCnt>=noParts) {
+                               partCnt = 0;
+                               haveTime = false;
+                       }
+                       //if(debugRead) printf("DEBUG%d par2 %d,%d/%d\n",lineCnt, coordCnt,partCnt,noParts);
+               }
+               //read pos, vel ...
+       }
+
+       // sanity check
+       for(int i=0; i<(int)mPartSets.size(); i++) {
+               if( (int)mPartSets[i].particles.size()!=noParts) {
+                       fprintf(stdout,"ControlParticles::initFromTextFileOld - invalid no of particles in set %d, is:%d, shouldbe:%d \n",i,(int)mPartSets[i].particles.size(), noParts);
+                       mPartSets.clear();
+                       fclose(infile);
+                       return 0;
+               }
+       }
+       // print stats
+       printf("ControlParticles::initFromTextFileOld: Read %d sets, each %d particles\n",
+                       (int)mPartSets.size(), noParts );
+       if(mPartSets.size()>0) {
+               printf("ControlParticles::initFromTextFileOld: Time: %f,%f\n",mPartSets[0].time, mPartSets[mPartSets.size()-1].time );
+       }
+       
+       // done...
+       fclose(infile);
+       applyTrafos();
+       return 1;
+}
+
+// load positions & timing from gzipped binary file
+int ControlParticles::initFromBinaryFile(string filename) {
+       mPartSets.clear();
+       if(filename.size()<1) return 0;
+       int fileNotFound=0;
+       int fileFound=0;
+       char ofile[256];
+
+       for(int set=0; ((set<10000)&&(fileNotFound<10)); set++) {
+               snprintf(ofile,256,"%s%04d.gz",filename.c_str(),set);
+               //errMsg("ControlParticle::initFromBinaryFile","set"<<set<<" notf"<<fileNotFound<<" ff"<<fileFound);
+
+               gzFile gzf;
+               gzf = gzopen(ofile, "rb");
+               if (!gzf) {
+                       //errMsg("ControlParticles::initFromBinaryFile","Unable to open file for reading '"<<ofile<<"' "); 
+                       fileNotFound++;
+                       continue;
+               }
+               fileNotFound=0;
+               fileFound++;
+
+               ControlParticleSet cps;
+               mPartSets.push_back(cps);
+               int setCnt = (int)mPartSets.size()-1;
+               //LbmFloat val = (LbmFloat)atof(line+2);
+               mPartSets[setCnt].time = (gfxReal)set;
+
+               int totpart = 0;
+               gzread(gzf, &totpart, sizeof(totpart));
+
+               for(int a=0; a<totpart; a++) {
+                       int ptype=0;
+                       float psize=0.0;
+                       ntlVec3Gfx ppos,pvel;
+                       gzread(gzf, &ptype, sizeof( ptype )); 
+                       gzread(gzf, &psize, sizeof( float )); 
+
+                       for(int j=0; j<3; j++) { gzread(gzf, &ppos[j], sizeof( float )); }
+                       for(int j=0; j<3; j++) { gzread(gzf, &pvel[j], sizeof( float )); }
+
+                       ControlParticle p; 
+                       p.reset();
+                       p.pos = vec2L(ppos);
+                       mPartSets[setCnt].particles.push_back(p);
+               } 
+
+               gzclose(gzf);
+               //errMsg("ControlParticle::initFromBinaryFile","Read set "<<ofile<<", #"<<mPartSets[setCnt].particles.size() ); // DEBUG
+       } // sets
+
+       if(fileFound==0) return 0;
+       applyTrafos();
+       return 1;
+}
+
+int globCPIProblems =0;
+bool ControlParticles::checkPointInside(ntlTree *tree, ntlVec3Gfx org, gfxReal &distance) {
+       // warning - stripped down version of geoInitCheckPointInside
+       const int globGeoInitDebug = 0;
+       const int  flags = FGI_FLUID;
+       org += ntlVec3Gfx(0.0001);
+       ntlVec3Gfx dir = ntlVec3Gfx(1.0, 0.0, 0.0);
+       int OId = -1;
+       ntlRay ray(org, dir, 0, 1.0, NULL);
+       bool done = false;
+       bool inside = false;
+       int mGiObjInside = 0; 
+       LbmFloat mGiObjDistance = -1.0; 
+       LbmFloat giObjFirstHistSide = 0; 
+       
+       // if not inside, return distance to first hit
+       gfxReal firstHit=-1.0;
+       int     firstOId = -1;
+       if(globGeoInitDebug) errMsg("IIIstart"," isect "<<org);
+
+       while(!done) {
+               // find first inside intersection
+               ntlTriangle *triIns = NULL;
+               distance = -1.0;
+               ntlVec3Gfx normal(0.0);
+               tree->intersectX(ray,distance,normal, triIns, flags, true);
+               if(triIns) {
+                       ntlVec3Gfx norg = ray.getOrigin() + ray.getDirection()*distance;
+                       LbmFloat orientation = dot(normal, dir);
+                       OId = triIns->getObjectId();
+                       if(orientation<=0.0) {
+                               // outside hit
+                               normal *= -1.0;
+                               mGiObjInside++;
+                               if(giObjFirstHistSide==0) giObjFirstHistSide = 1;
+                               if(globGeoInitDebug) errMsg("IIO"," oid:"<<OId<<" org"<<org<<" norg"<<norg<<" orient:"<<orientation);
+                       } else {
+                               // inside hit
+                               mGiObjInside++;
+                               if(mGiObjDistance<0.0) mGiObjDistance = distance;
+                               if(globGeoInitDebug) errMsg("III"," oid:"<<OId<<" org"<<org<<" norg"<<norg<<" orient:"<<orientation);
+                               if(giObjFirstHistSide==0) giObjFirstHistSide = -1;
+                       }
+                       norg += normal * getVecEpsilon();
+                       ray = ntlRay(norg, dir, 0, 1.0, NULL);
+                       // remember first hit distance, in case we're not 
+                       // inside anything
+                       if(firstHit<0.0) {
+                               firstHit = distance;
+                               firstOId = OId;
+                       }
+               } else {
+                       // no more intersections... return false
+                       done = true;
+               }
+       }
+
+       distance = -1.0;
+       if(mGiObjInside>0) {
+               bool mess = false;
+               if((mGiObjInside%2)==1) {
+                       if(giObjFirstHistSide != -1) mess=true;
+               } else {
+                       if(giObjFirstHistSide !=  1) mess=true;
+               }
+               if(mess) {
+                       // ?
+                       //errMsg("IIIproblem","At "<<org<<" obj  inside:"<<mGiObjInside<<" firstside:"<<giObjFirstHistSide );
+                       globCPIProblems++;
+                       mGiObjInside++; // believe first hit side...
+               }
+       }
+
+       if(globGeoInitDebug) errMsg("CHIII"," ins="<<mGiObjInside<<" t"<<mGiObjDistance<<" d"<<distance);
+       if(((mGiObjInside%2)==1)&&(mGiObjDistance>0.0)) {
+               if(  (distance<0.0)                             || // first intersection -> good
+                               ((distance>0.0)&&(distance>mGiObjDistance)) // more than one intersection -> use closest one
+                       ) {                                             
+                       distance = mGiObjDistance;
+                       OId = 0;
+                       inside = true;
+               } 
+       }
+
+       if(!inside) {
+               distance = firstHit;
+               OId = firstOId;
+       }
+       if(globGeoInitDebug) errMsg("CHIII","ins"<<inside<<"  fh"<<firstHit<<" fo"<<firstOId<<" - h"<<distance<<" o"<<OId);
+
+       return inside;
+}
+int ControlParticles::initFromMVCMesh(string filename) {
+       myTime_t mvmstart = getTime(); 
+       ntlGeometryObjModel *model = new ntlGeometryObjModel();
+       int gid=1;
+       char infile[256];
+       vector<ntlTriangle> triangles;
+       vector<ntlVec3Gfx> vertices;
+       vector<ntlVec3Gfx> normals;
+       snprintf(infile,256,"%s.bobj.gz", filename.c_str() );
+       model->loadBobjModel(string(infile));
+       model->setLoaded(true);
+       model->setGeoInitId(gid);
+       model->setGeoInitType(FGI_FLUID);
+       debMsgStd("ControlParticles::initFromMVMCMesh",DM_MSG,"infile:"<<string(infile) ,4);
+
+       //getTriangles(double t,  vector<ntlTriangle> *triangles, vector<ntlVec3Gfx> *vertices, vector<ntlVec3Gfx> *normals, int objectId );
+       model->getTriangles(mCPSTimeStart, &triangles, &vertices, &normals, 1 ); 
+       debMsgStd("ControlParticles::initFromMVMCMesh",DM_MSG," tris:"<<triangles.size()<<" verts:"<<vertices.size()<<" norms:"<<normals.size() , 2);
+       
+       // valid mesh?
+       if(triangles.size() <= 0) {
+               return 0;
+       }
+
+       ntlRenderGlobals *glob = new ntlRenderGlobals;
+       ntlScene *genscene = new ntlScene( glob, false );
+       genscene->addGeoClass(model);
+       genscene->addGeoObject(model);
+       genscene->buildScene(0., false);
+       char treeFlag = (1<<(4+gid));
+
+       ntlTree *tree = new ntlTree( 
+                       15, 8,  // TREEwarning - fixed values for depth & maxtriangles here...
+                       genscene, treeFlag );
+
+       // TODO? use params
+       ntlVec3Gfx start,end;
+       model->getExtends(start,end);
+
+       LbmFloat width = mCPSWidth;
+       if(width<=LBM_EPSILON) { errMsg("ControlParticles::initFromMVMCMesh","Invalid mCPSWidth! "<<mCPSWidth); width=mCPSWidth=0.1; }
+       ntlVec3Gfx org = start+ntlVec3Gfx(width*0.5);
+       gfxReal distance = -1.;
+       vector<ntlVec3Gfx> inspos;
+       int approxmax = (int)( ((end[0]-start[0])/width)*((end[1]-start[1])/width)*((end[2]-start[2])/width) );
+
+       debMsgStd("ControlParticles::initFromMVMCMesh",DM_MSG,"start"<<start<<" end"<<end<<" w="<<width<<" maxp:"<<approxmax, 5);
+       while(org[2]<end[2]) {
+               while(org[1]<end[1]) {
+                       while(org[0]<end[0]) {
+                               if(checkPointInside(tree, org, distance)) {
+                                       inspos.push_back(org);
+                                       //inspos.push_back(org+ntlVec3Gfx(width));
+                                       //inspos.push_back(start+end*0.5);
+                               }
+                               // TODO optimize, use distance
+                               org[0] += width;
+                       }
+                       org[1] += width;
+                       org[0] = start[0];
+               }
+               org[2] += width;
+               org[1] = start[1];
+       }
+       debMsgStd("ControlParticles::initFromMVMCMesh",DM_MSG,"points: "<<inspos.size()<<" initproblems: "<<globCPIProblems,5 );
+
+       MeanValueMeshCoords mvm;
+       mvm.calculateMVMCs(vertices,triangles, inspos, mCPSWeightFac);
+       vector<ntlVec3Gfx> ninspos;
+       mvm.transfer(vertices, ninspos);
+
+       // init first set, check dist
+       ControlParticleSet firstcps; //T
+       mPartSets.push_back(firstcps);
+       mPartSets[mPartSets.size()-1].time = (gfxReal)0.;
+       vector<bool> useCP;
+       bool debugPos=false;
+
+       for(int i=0; i<(int)inspos.size(); i++) {
+               ControlParticle p; p.reset();
+               p.pos = vec2L(inspos[i]);
+               //errMsg("COMP "," "<<inspos[i]<<" vs "<<ninspos[i] );
+               double cpdist = norm(inspos[i]-ninspos[i]);
+               bool usecpv = true;
+               if(debugPos) errMsg("COMP "," "<<cpdist<<usecpv);
+
+               mPartSets[mPartSets.size()-1].particles.push_back(p);
+               useCP.push_back(usecpv);
+       }
+
+       // init further sets, temporal mesh sampling
+       double tsampling = mCPSTimestep;
+       int totcnt = (int)( (mCPSTimeEnd-mCPSTimeStart)/tsampling ), tcnt=0;
+       for(double t=mCPSTimeStart+tsampling; ((t<mCPSTimeEnd) && (ninspos.size()>0.)); t+=tsampling) {
+               ControlParticleSet nextcps; //T
+               mPartSets.push_back(nextcps);
+               mPartSets[mPartSets.size()-1].time = (gfxReal)t;
+
+               vertices.clear(); triangles.clear(); normals.clear();
+               model->getTriangles(t, &triangles, &vertices, &normals, 1 );
+               mvm.transfer(vertices, ninspos);
+               if(tcnt%(totcnt/10)==1) debMsgStd("MeanValueMeshCoords::calculateMVMCs",DM_MSG,"Transferring animation, frame: "<<tcnt<<"/"<<totcnt,5 );
+               tcnt++;
+               for(int i=0; i<(int)ninspos.size(); i++) {
+                       if(debugPos) errMsg("COMP "," "<<norm(inspos[i]-ninspos[i]) );
+                       if(useCP[i]) {
+                               ControlParticle p; p.reset();
+                               p.pos = vec2L(ninspos[i]);
+                               mPartSets[mPartSets.size()-1].particles.push_back(p);
+                       }
+               }
+       }
+
+       applyTrafos();
+
+       myTime_t mvmend = getTime(); 
+       debMsgStd("ControlParticle::initFromMVMCMesh",DM_MSG,"t:"<<getTimeString(mvmend-mvmstart)<<" ",7 );
+       delete tree;
+       delete genscene;
+       delete glob;
+//exit(1); // DEBUG
+       return 1;
+}
+
+#define TRISWAP(v,a,b) { LbmFloat tmp = (v)[b]; (v)[b]=(v)[a]; (v)[a]=tmp; }
+#define TRISWAPALL(v,a,b) {  \
+                       TRISWAP( (v).pos     ,a,b ); \
+                       TRISWAP( (v).vel     ,a,b ); \
+                       TRISWAP( (v).rotaxis ,a,b ); }
+
+// helper function for LBM 2D -> swap Y and Z components everywhere
+void ControlParticles::swapCoords(int a, int b) {
+       //return;
+       for(int i=0; i<(int)mPartSets.size(); i++) {
+               for(int j=0; j<(int)mPartSets[i].particles.size(); j++) {
+                       TRISWAPALL( mPartSets[i].particles[j],a,b );
+               }
+       }
+}
+
+// helper function for LBM 2D -> mirror time
+void ControlParticles::mirrorTime() {
+       LbmFloat maxtime = mPartSets[mPartSets.size()-1].time;
+       const bool debugTimeswap = false;
+       
+       for(int i=0; i<(int)mPartSets.size(); i++) {
+               mPartSets[i].time = maxtime - mPartSets[i].time;
+       }
+
+       for(int i=0; i<(int)mPartSets.size()/2; i++) {
+               ControlParticleSet cps = mPartSets[i];
+               if(debugTimeswap) errMsg("TIMESWAP", " s"<<i<<","<<mPartSets[i].time<<"  and s"<<(mPartSets.size()-1-i)<<","<< mPartSets[mPartSets.size()-1-i].time <<"  mt:"<<maxtime );
+               mPartSets[i] = mPartSets[mPartSets.size()-1-i];
+               mPartSets[mPartSets.size()-1-i] = cps;
+       }
+
+       for(int i=0; i<(int)mPartSets.size(); i++) {
+               if(debugTimeswap) errMsg("TIMESWAP", "done: s"<<i<<","<<mPartSets[i].time<<"  "<<mPartSets[i].particles.size() );
+       }
+}
+
+// apply init transformations
+void ControlParticles::applyTrafos() {
+       // apply trafos
+       for(int i=0; i<(int)mPartSets.size(); i++) {
+               mPartSets[i].time *= _initTimeScale;
+               /*for(int j=0; j<(int)mPartSets[i].particles.size(); j++) {
+                       for(int k=0; k<3; k++) {
+                               mPartSets[i].particles[j].pos[k] *= _initPartScale[k];
+                               mPartSets[i].particles[j].pos[k] += _initPartOffset[k];
+                       }
+               } now done in initarray */
+       }
+
+       // mirror coords...
+       for(int l=0; l<(int)_initMirror.length(); l++) {
+               switch(_initMirror[l]) {
+               case 'X':
+               case 'x':
+                       //printf("ControlParticles::applyTrafos - mirror x\n");
+                       swapCoords(1,2);
+                       break;
+               case 'Y':
+               case 'y':
+                       //printf("ControlParticles::applyTrafos - mirror y\n");
+                       swapCoords(0,2);
+                       break;
+               case 'Z':
+               case 'z':
+                       //printf("ControlParticles::applyTrafos - mirror z\n");
+                       swapCoords(0,1);
+                       break;
+               case 'T':
+               case 't':
+                       //printf("ControlParticles::applyTrafos - mirror time\n");
+                       mirrorTime();
+                       break;
+               case ' ':
+               case '-':
+               case '\n':
+                       break;
+               default:
+                       //printf("ControlParticles::applyTrafos - mirror unknown %c !?\n", _initMirror[l] );
+                       break;
+               }
+       }
+
+       // reset 2d positions
+#if (CP_PROJECT2D==1) && ( defined(MAIN_2D) || LBMDIM==2 )
+       for(size_t j=0; j<mPartSets.size(); j++) 
+               for(size_t i=0; i<mPartSets[j].particles.size(); i++) {
+                       // DEBUG 
+                       mPartSets[j].particles[i].pos[1] = 0.f;
+               }
+#endif
+
+#if defined(LBMDIM) 
+       //? if( (getenv("ELBEEM_CPINFILE")) || (getenv("ELBEEM_CPOUTFILE")) ){ 
+               // gui control test, don swap...
+       //? } else {
+               //? swapCoords(1,2); // LBM 2D -> swap Y and Z components everywhere
+       //? }
+#endif
+
+       initTime(0.f, 0.f);
+}
+
+#undef TRISWAP
+
+// --------------------------------------------------------------------------
+// init for a given time
+void ControlParticles::initTime(LbmFloat t, LbmFloat dt) 
+{
+       //fprintf(stdout, "CPINITTIME init %f\n",t);
+       _currTime = t;
+       if(mPartSets.size()<1) return;
+
+       // init zero velocities
+       initTimeArray(t, _particles);
+
+       // calculate velocities from prev. timestep?
+       if(dt>0.) {
+               _currTimestep = dt;
+               std::vector<ControlParticle> prevparts;
+               initTimeArray(t-dt, prevparts);
+               LbmFloat invdt = 1.0/dt;
+               for(size_t j=0; j<_particles.size(); j++) {
+                       ControlParticle &p = _particles[j];
+                       ControlParticle &prevp = prevparts[j];
+                       for(int k=0; k<3; k++) {
+                               p.pos[k] *= _initPartScale[k];
+                               p.pos[k] += _initPartOffset[k];
+                               prevp.pos[k] *= _initLastPartScale[k];
+                               prevp.pos[k] += _initLastPartOffset[k];
+                       }
+                       p.vel = (p.pos - prevp.pos)*invdt;
+               }
+
+               if(0) {
+                       LbmVec avgvel(0.);
+                       for(size_t j=0; j<_particles.size(); j++) {
+                               avgvel += _particles[j].vel;
+                       }
+                       avgvel /= (LbmFloat)_particles.size();
+                       //fprintf(stdout," AVGVEL %f,%f,%f \n",avgvel[0],avgvel[1],avgvel[2]); // DEBUG
+               }
+       }
+}
+
+// helper, init given array
+void ControlParticles::initTimeArray(LbmFloat t, std::vector<ControlParticle> &parts) {
+       if(mPartSets.size()<1) return;
+
+       if(parts.size()!=mPartSets[0].particles.size()) {
+               //fprintf(stdout,"PRES \n");
+               parts.resize(mPartSets[0].particles.size());
+               // TODO reset all?
+               for(size_t j=0; j<parts.size(); j++) {
+                       parts[j].reset();
+               }
+       }
+       if(parts.size()<1) return;
+
+       // debug inits
+       if(mDebugInit==1) {
+               // hard coded circle init
+               for(size_t j=0; j<mPartSets[0].particles.size(); j++) {
+                       ControlParticle p = mPartSets[0].particles[j];
+                       // remember old
+                       p.density = parts[j].density;
+                       p.densityWeight = parts[j].densityWeight;
+                       p.avgVel = parts[j].avgVel;
+                       p.avgVelAcc = parts[j].avgVelAcc;
+                       p.avgVelWeight = parts[j].avgVelWeight;
+                       LbmVec ppos(0.); { // DEBUG
+                       const float tscale=10.;
+                       const float tprevo = 0.33;
+                       const LbmVec toff(50,50,0);
+                       const LbmVec oscale(30,30,0);
+                       ppos[0] =  cos(tscale* t - tprevo*(float)j + M_PI -0.1) * oscale[0] + toff[0];
+                       ppos[1] = -sin(tscale* t - tprevo*(float)j + M_PI -0.1) * oscale[1] + toff[1];
+                       ppos[2] =                               toff[2]; } // DEBUG
+                       p.pos = ppos;
+                       parts[j] = p;
+                       //errMsg("ControlParticle::initTimeArray","j:"<<j<<" p:"<<parts[j].pos );
+               }
+               return;
+       }
+       else if(mDebugInit==2) {
+               // hard coded spiral init
+               const float tscale=-10.;
+               const float tprevo = 0.33;
+               LbmVec   toff(50,0,-50);
+               const LbmVec oscale(20,20,0);
+               toff[2] += 30. * t +30.;
+               for(size_t j=0; j<mPartSets[0].particles.size(); j++) {
+                       ControlParticle p = mPartSets[0].particles[j];
+                       // remember old
+                       p.density = parts[j].density;
+                       p.densityWeight = parts[j].densityWeight;
+                       p.avgVel = parts[j].avgVel;
+                       p.avgVelAcc = parts[j].avgVelAcc;
+                       p.avgVelWeight = parts[j].avgVelWeight;
+                       LbmVec ppos(0.); 
+                       ppos[1] =                               toff[2]; 
+                       LbmFloat zscal = (ppos[1]+100.)/200.;
+                       ppos[0] =  cos(tscale* t - tprevo*(float)j + M_PI -0.1) * oscale[0]*zscal + toff[0];
+                       ppos[2] = -sin(tscale* t - tprevo*(float)j + M_PI -0.1) * oscale[1]*zscal + toff[1];
+                       p.pos = ppos;
+                       parts[j] = p;
+
+                       toff[2] += 0.25;
+               }
+               return;
+       }
+
+       // use first set
+       if((t<=mPartSets[0].time)||(mPartSets.size()==1)) {
+               //fprintf(stdout,"PINI %f \n", t);
+               //parts = mPartSets[0].particles;
+               const int i=0;
+               for(size_t j=0; j<mPartSets[i].particles.size(); j++) {
+                       ControlParticle p = mPartSets[i].particles[j];
+                       // remember old
+                       p.density = parts[j].density;
+                       p.densityWeight = parts[j].densityWeight;
+                       p.avgVel = parts[j].avgVel;
+                       p.avgVelAcc = parts[j].avgVelAcc;
+                       p.avgVelWeight = parts[j].avgVelWeight;
+                       parts[j] = p;
+               }
+               return;
+       }
+
+       for(int i=0; i<(int)mPartSets.size()-1; i++) {
+               if((mPartSets[i].time<=t) && (mPartSets[i+1].time>t)) {
+                       LbmFloat d = mPartSets[i+1].time-mPartSets[i].time;
+                       LbmFloat f = (t-mPartSets[i].time)/d;
+                       LbmFloat omf = 1.0f - f;
+       
+                       for(size_t j=0; j<mPartSets[i].particles.size(); j++) {
+                               ControlParticle *src1=&mPartSets[i  ].particles[j];
+                               ControlParticle *src2=&mPartSets[i+1].particles[j];
+                               ControlParticle &p = parts[j];
+                               // do linear interpolation
+                               p.pos     = src1->pos * omf     + src2->pos *f;
+                               p.vel     = LbmVec(0.); // reset, calculated later on src1->vel * omf     + src2->vel *f;
+                               p.rotaxis = src1->rotaxis * omf + src2->rotaxis *f;
+                               p.influence = src1->influence * omf + src2->influence *f;
+                               p.size    = src1->size * omf    + src2->size *f;
+                               // dont modify: density, densityWeight
+                       }
+               }
+       }
+
+       // after last?
+       if(t>=mPartSets[ mPartSets.size() -1 ].time) {
+               //parts = mPartSets[ mPartSets.size() -1 ].particles;
+               const int i= (int)mPartSets.size() -1;
+               for(size_t j=0; j<mPartSets[i].particles.size(); j++) {
+                       ControlParticle p = mPartSets[i].particles[j];
+                       // restore
+                       p.density = parts[j].density;
+                       p.densityWeight = parts[j].densityWeight;
+                       p.avgVel = parts[j].avgVel;
+                       p.avgVelAcc = parts[j].avgVelAcc;
+                       p.avgVelWeight = parts[j].avgVelWeight;
+                       parts[j] = p;
+               }
+       }
+}
+
+
+
+
+// --------------------------------------------------------------------------
+
+#define DEBUG_MODVEL 0
+
+// recalculate 
+void ControlParticles::calculateKernelWeight() {
+       const bool debugKernel = true;
+
+       // calculate kernel area with respect to particlesize/cellsize
+       LbmFloat kernelw = -1.;
+       LbmFloat kernelnorm = -1.;
+       LbmFloat krad = (_radiusAtt*0.75); // FIXME  use real cone approximation...?
+       //krad = (_influenceFalloff*1.);
+#if (CP_PROJECT2D==1) && (defined(MAIN_2D) || LBMDIM==2)
+       kernelw = CP_PI*krad*krad;
+       kernelnorm = 1.0 / (_fluidSpacing * _fluidSpacing);
+#else // 2D
+       kernelw = CP_PI*krad*krad*krad* (4./3.);
+       kernelnorm = 1.0 / (_fluidSpacing * _fluidSpacing * _fluidSpacing);
+#endif // MAIN_2D
+
+       if(debugKernel) debMsgStd("ControlParticles::calculateKernelWeight",DM_MSG,"kw"<<kernelw<<", norm"<<
+                       kernelnorm<<", w*n="<<(kernelw*kernelnorm)<<", rad"<<krad<<", sp"<<_fluidSpacing<<"  ", 7);
+       LbmFloat kernelws = kernelw*kernelnorm;
+       _kernelWeight = kernelws;
+       if(debugKernel) debMsgStd("ControlParticles::calculateKernelWeight",DM_MSG,"influence f="<<_radiusAtt<<" t="<<
+                       _influenceTangential<<" a="<<_influenceAttraction<<" v="<<_influenceVelocity<<" kweight="<<_kernelWeight, 7);
+       if(_kernelWeight<=0.) {
+               errMsg("ControlParticles::calculateKernelWeight", "invalid kernel! "<<_kernelWeight<<", resetting");
+               _kernelWeight = 1.;
+       }
+}
+
+void 
+ControlParticles::prepareControl(LbmFloat simtime, LbmFloat dt, ControlParticles *motion) {
+       debMsgStd("ControlParticle::prepareControl",DM_MSG," simtime="<<simtime<<" dt="<<dt<<" ", 5);
+
+       //fprintf(stdout,"PREPARE \n");
+       LbmFloat avgdw = 0.;
+       for(size_t i=0; i<_particles.size(); i++) {
+               ControlParticle *cp = &_particles[i];
+
+               if(this->getInfluenceAttraction()<0.) {
+                       cp->density= 
+                       cp->densityWeight = 1.0;
+                       continue;
+               } 
+
+               // normalize by kernel 
+               //cp->densityWeight = (1.0 - (cp->density / _kernelWeight)); // store last
+#if (CP_PROJECT2D==1) && (defined(MAIN_2D) || LBMDIM==2)
+               cp->densityWeight = (1.0 - (cp->density / (_kernelWeight*cp->size*cp->size) )); // store last
+#else // 2D
+               cp->densityWeight = (1.0 - (cp->density / (_kernelWeight*cp->size*cp->size*cp->size) )); // store last
+#endif // MAIN_2D
+
+               if(i<10) debMsgStd("ControlParticle::prepareControl",DM_MSG,"kernelDebug i="<<i<<" densWei="<<cp->densityWeight<<" 1/kw"<<(1.0/_kernelWeight)<<" cpdensity="<<cp->density, 9 );
+               if(cp->densityWeight<0.) cp->densityWeight=0.;
+               if(cp->densityWeight>1.) cp->densityWeight=1.;
+               
+               avgdw += cp->densityWeight;
+               // reset for next step
+               cp->density = 0.; 
+
+               if(cp->avgVelWeight>0.) {
+                cp->avgVel     = cp->avgVelAcc/cp->avgVelWeight; 
+                cp->avgVelWeight       = 0.; 
+                cp->avgVelAcc  = LbmVec(0.,0.,0.); 
+               }
+       }
+       //if(debugKernel) for(size_t i=0; i<_particles.size(); i++) { ControlParticle *cp = &_particles[i]; fprintf(stdout,"A %f,%f \n",cp->density,cp->densityWeight); }
+       avgdw /= (LbmFloat)(_particles.size());
+       //if(motion) { printf("ControlParticle::kernel: avgdw:%f,  kw%f, sp%f \n", avgdw, _kernelWeight, _fluidSpacing); }
+       
+       //if((simtime>=0.) && (simtime != _currTime)) 
+       initTime(simtime, dt);
+
+       if((motion) && (motion->getSize()>0)){
+               ControlParticle *motionp = motion->getParticle(0);
+               //printf("ControlParticle::prepareControl motion: pos[%f,%f,%f] vel[%f,%f,%f] \n", motionp->pos[0], motionp->pos[1], motionp->pos[2], motionp->vel[0], motionp->vel[1], motionp->vel[2] );
+               for(size_t i=0; i<_particles.size(); i++) {
+                       ControlParticle *cp = &_particles[i];
+                       cp->pos = cp->pos + motionp->pos;
+                       cp->vel = cp->vel + motionp->vel;
+                       cp->size = cp->size * motionp->size;
+                       cp->influence = cp->size * motionp->influence;
+               }
+       }
+       
+       // reset to radiusAtt by default
+       if(_radiusVel==0.) _radiusVel = _radiusAtt;
+       if(_radiusMinMaxd==0.) _radiusMinMaxd = _radiusAtt;
+       if(_radiusMaxd==0.) _radiusMaxd = 2.*_radiusAtt;
+       // has to be radiusVel<radiusAtt<radiusMinMaxd<radiusMaxd
+       if(_radiusVel>_radiusAtt) _radiusVel = _radiusAtt;
+       if(_radiusAtt>_radiusMinMaxd) _radiusAtt = _radiusMinMaxd;
+       if(_radiusMinMaxd>_radiusMaxd) _radiusMinMaxd = _radiusMaxd;
+
+       //printf("ControlParticle::radii vel:%f att:%f min:%f max:%f \n", _radiusVel,_radiusAtt,_radiusMinMaxd,_radiusMaxd);
+       // prepareControl done
+}
+
+void ControlParticles::finishControl(std::vector<ControlForces> &forces, LbmFloat iatt, LbmFloat ivel, LbmFloat imaxd) {
+
+       //const LbmFloat iatt  = this->getInfluenceAttraction() * this->getCurrTimestep();
+       //const LbmFloat ivel  = this->getInfluenceVelocity();
+       //const LbmFloat imaxd = this->getInfluenceMaxdist() * this->getCurrTimestep();
+       // prepare for usage
+       iatt  *= this->getCurrTimestep();
+       ivel  *= 1.; // not necessary!
+       imaxd *= this->getCurrTimestep();
+
+       // skip when size=0
+       for(int i=0; i<(int)forces.size(); i++) {
+               if(DEBUG_MODVEL) fprintf(stdout, "CPFORGF %d , wf:%f,f:%f,%f,%f  ,  v:%f,%f,%f   \n",i, forces[i].weightAtt, forces[i].forceAtt[0],forces[i].forceAtt[1],forces[i].forceAtt[2],   forces[i].forceVel[0], forces[i].forceVel[1], forces[i].forceVel[2] );
+               LbmFloat cfweight = forces[i].weightAtt; // always normalize
+               if((cfweight!=0.)&&(iatt!=0.)) {
+                       // multiple kernels, normalize - note this does not normalize in d>r/2 region
+                       if(ABS(cfweight)>1.) { cfweight = 1.0/cfweight; }
+                       // multiply iatt afterwards to allow stronger force
+                       cfweight *= iatt;
+                       forces[i].forceAtt *= cfweight;
+               } else {
+                       forces[i].weightAtt =  0.;
+                       forces[i].forceAtt = LbmVec(0.);
+               }
+
+               if( (cfweight==0.) && (imaxd>0.) && (forces[i].maxDistance>0.) ) {
+                       forces[i].forceMaxd *= imaxd;
+               } else {
+                       forces[i].maxDistance=  0.;
+                       forces[i].forceMaxd = LbmVec(0.);
+               }
+
+               LbmFloat cvweight = forces[i].weightVel; // always normalize
+               if(cvweight>0.) {
+                       forces[i].forceVel /= cvweight;
+                       forces[i].compAv /= cvweight;
+                       // now modify cvweight, and write back
+                       // important, cut at 1 - otherwise strong vel. influences...
+                       if(cvweight>1.) { cvweight = 1.; }
+                       // thus cvweight is in the range of 0..influenceVelocity, currently not normalized by numCParts
+                       cvweight *= ivel;
+                       if(cvweight<0.) cvweight=0.; if(cvweight>1.) cvweight=1.;
+                       // LBM, FIXME todo use relaxation factor
+                       //pvel = (cvel*0.5 * cvweight) + (pvel * (1.0-cvweight)); 
+                       forces[i].weightVel = cvweight;
+
+                       //errMsg("COMPAV","i"<<i<<" compav"<<forces[i].compAv<<" forcevel"<<forces[i].forceVel<<" ");
+               } else {
+                       forces[i].weightVel = 0.;
+                       if(forces[i].maxDistance==0.) forces[i].forceVel = LbmVec(0.);
+                       forces[i].compAvWeight = 0.;
+                       forces[i].compAv = LbmVec(0.);
+               }
+               if(DEBUG_MODVEL) fprintf(stdout, "CPFINIF %d , wf:%f,f:%f,%f,%f  ,  v:%f,%f,%f   \n",i, forces[i].weightAtt, forces[i].forceAtt[0],forces[i].forceAtt[1],forces[i].forceAtt[2],  forces[i].forceVel[0],forces[i].forceVel[1],forces[i].forceVel[2] );
+       }
+
+       // unused...
+       if(DEBUG_MODVEL) fprintf(stdout,"MFC iatt:%f,%f ivel:%f,%f ifmd:%f,%f \n", iatt,_radiusAtt, ivel,_radiusVel, imaxd, _radiusMaxd);
+       //for(size_t i=0; i<_particles.size(); i++) { ControlParticle *cp = &_particles[i]; fprintf(stdout," %f,%f,%f ",cp->density,cp->densityWeight, (1.0 - (12.0*cp->densityWeight))); }
+       //fprintf(stdout,"\n\nCP DONE \n\n\n");
+}
+
+
+// --------------------------------------------------------------------------
+// calculate forces at given position, and modify velocity
+// according to timestep
+void ControlParticles::calculateCpInfluenceOpt(ControlParticle *cp, LbmVec fluidpos, LbmVec fluidvel, ControlForces *force, LbmFloat fillFactor) {
+       // dont reset, only add...
+       // test distance, simple squared distance reject
+       const LbmFloat cpfo = _radiusAtt*cp->size;
+
+       LbmVec posDelta;
+       if(DEBUG_MODVEL) fprintf(stdout, "CP at %f,%f,%f bef fw:%f, f:%f,%f,%f  , vw:%f, v:%f,%f,%f   \n",fluidpos[0],fluidpos[1],fluidpos[2], force->weightAtt, force->forceAtt[0], force->forceAtt[1], force->forceAtt[2], force->weightVel, force->forceVel[0], force->forceVel[1], force->forceVel[2]);
+       posDelta        = cp->pos - fluidpos;
+#if LBMDIM==2 && (CP_PROJECT2D==1)
+       posDelta[2] = 0.; // project to xy plane, z-velocity should already be gone...
+#endif
+
+       const LbmFloat distsqr = posDelta[0]*posDelta[0]+posDelta[1]*posDelta[1]+posDelta[2]*posDelta[2];
+       if(DEBUG_MODVEL) fprintf(stdout, " Pd at %f,%f,%f d%f   \n",posDelta[0],posDelta[1],posDelta[2], distsqr);
+       // cut at influence=0.5 , scaling not really makes sense
+       if(cpfo*cpfo < distsqr) {
+               /*if(cp->influence>0.5) {
+                       if(force->weightAtt == 0.) {
+                               if(force->maxDistance*force->maxDistance > distsqr) {
+                               const LbmFloat dis = sqrtf((float)distsqr);
+                               const LbmFloat sc = dis-cpfo;
+                               force->maxDistance = dis;
+                               force->forceMaxd = (posDelta)*(sc/dis);
+                               }
+                       } } */
+               return;
+       }
+       force->weightAtt += 1e-6; // for distance
+       force->maxDistance = 0.; // necessary for SPH?
+
+       const LbmFloat pdistance = MAGNITUDE(posDelta);
+       LbmFloat pdistinv = 0.;
+       if(ABS(pdistance)>0.) pdistinv = 1./pdistance;
+       posDelta *= pdistinv;
+
+       LbmFloat falloffAtt = 0.; //CPKernel::kernel(cpfo * 1.0, pdistance);
+       const LbmFloat qac = pdistance / cpfo ;
+       if (qac < 1.0){ // return 0.;
+               if(qac < 0.5) falloffAtt =  1.0f;
+               else         falloffAtt = (1.0f - qac) * 2.0f;
+       }
+
+       // vorticity force:
+       // - //LbmVec forceVort; 
+       // - //CROSS(forceVort, posDelta, cp->rotaxis);
+       // - //NORMALIZE(forceVort);
+       // - if(falloffAtt>1.0) falloffAtt=1.0;
+
+#if (CP_PROJECT2D==1) && (defined(MAIN_2D) || LBMDIM==2)
+       // fillFactor *= 2.0 *0.75 * pdistance; // 2d>3d sampling
+#endif // (CP_PROJECT2D==1) && (defined(MAIN_2D) || LBMDIM==2)
+       
+       LbmFloat signum = getInfluenceAttraction() > 0.0 ? 1.0 : -1.0;  
+       cp->density += falloffAtt * fillFactor;
+       force->forceAtt +=  posDelta *cp->densityWeight *cp->influence *signum; 
+       force->weightAtt += falloffAtt*cp->densityWeight *cp->influence;
+       
+       LbmFloat falloffVel = 0.; //CPKernel::kernel(cpfo * 1.0, pdistance);
+       const LbmFloat cpfv = _radiusVel*cp->size;
+       if(cpfv*cpfv < distsqr) { return; }
+       const LbmFloat qvc = pdistance / cpfo ;
+       //if (qvc < 1.0){ 
+               //if(qvc < 0.5) falloffVel =  1.0f;
+               //else         falloffVel = (1.0f - qvc) * 2.0f;
+       //}
+       falloffVel = 1.-qvc;
+
+       LbmFloat pvWeight; // = (1.0-cp->densityWeight) * _currTimestep * falloffVel;
+       pvWeight = falloffVel *cp->influence; // std, without density influence
+       //pvWeight *= (1.0-cp->densityWeight); // use inverse density weight
+       //pvWeight *=      cp->densityWeight; // test, use density weight
+       LbmVec modvel(0.);
+       modvel += cp->vel * pvWeight;
+       //pvWeight = 1.; modvel = partVel; // DEBUG!?
+
+       if(pvWeight>0.) {
+               force->forceVel += modvel;
+               force->weightVel += pvWeight;
+
+               cp->avgVelWeight += falloffVel;
+               cp->avgVel += fluidvel;
+       } 
+       if(DEBUG_MODVEL) fprintf(stdout, "CP at %f,%f,%f aft fw:%f, f:%f,%f,%f  , vw:%f, v:%f,%f,%f   \n",fluidpos[0],fluidpos[1],fluidpos[2], force->weightAtt, force->forceAtt[0], force->forceAtt[1], force->forceAtt[2], force->weightVel, force->forceVel[0], force->forceVel[1], force->forceVel[2]);
+       return;
+}
+
+void ControlParticles::calculateMaxdForce(ControlParticle *cp, LbmVec fluidpos, ControlForces *force) {
+       if(force->weightAtt != 0.) return; // maxd force off
+       if(cp->influence <= 0.5) return;   // ignore
+
+       LbmVec posDelta;
+       //if(DEBUG_MODVEL) fprintf(stdout, "CP at %f,%f,%f bef fw:%f, f:%f,%f,%f  , vw:%f, v:%f,%f,%f   \n",fluidpos[0],fluidpos[1],fluidpos[2], force->weightAtt, force->forceAtt[0], force->forceAtt[1], force->forceAtt[2], force->weightVel, force->forceVel[0], force->forceVel[1], force->forceVel[2]);
+       posDelta        = cp->pos - fluidpos;
+#if LBMDIM==2 && (CP_PROJECT2D==1)
+       posDelta[2] = 0.; // project to xy plane, z-velocity should already be gone...
+#endif
+
+       // dont reset, only add...
+       // test distance, simple squared distance reject
+       const LbmFloat distsqr = posDelta[0]*posDelta[0]+posDelta[1]*posDelta[1]+posDelta[2]*posDelta[2];
+       
+       // closer cp found
+       if(force->maxDistance*force->maxDistance < distsqr) return;
+       
+       const LbmFloat dmin = _radiusMinMaxd*cp->size;
+       if(distsqr<dmin*dmin) return; // inside min
+       const LbmFloat dmax = _radiusMaxd*cp->size;
+       if(distsqr>dmax*dmax) return; // outside
+
+
+       if(DEBUG_MODVEL) fprintf(stdout, " Pd at %f,%f,%f d%f   \n",posDelta[0],posDelta[1],posDelta[2], distsqr);
+       // cut at influence=0.5 , scaling not really makes sense
+       const LbmFloat dis = sqrtf((float)distsqr);
+       //const LbmFloat sc = dis - dmin;
+       const LbmFloat sc = (dis-dmin)/(dmax-dmin); // scale from 0-1
+       force->maxDistance = dis;
+       force->forceMaxd = (posDelta/dis) * sc;
+       //debug errMsg("calculateMaxdForce","pos"<<fluidpos<<" dis"<<dis<<" sc"<<sc<<" dmin"<<dmin<<" maxd"<< force->maxDistance <<" fmd"<<force->forceMaxd );
+       return;
+}
+
diff --git a/intern/elbeem/intern/controlparticles.h b/intern/elbeem/intern/controlparticles.h
new file mode 100644 (file)
index 0000000..712dfc4
--- /dev/null
@@ -0,0 +1,301 @@
+// --------------------------------------------------------------------------
+//
+// El'Beem - the visual lattice boltzmann freesurface simulator
+// All code distributed as part of El'Beem is covered by the version 2 of the 
+// GNU General Public License. See the file COPYING for details.  
+//
+// Copyright 2008 Nils Thuerey , Richard Keiser, Mark Pauly, Ulrich Ruede
+//
+// control particle classes
+//
+// --------------------------------------------------------------------------
+
+#ifndef CONTROLPARTICLES_H
+#define CONTROLPARTICLES_H
+
+#include "ntl_geometrymodel.h"
+
+// indicator for LBM inclusion
+//#ifndef LBMDIM
+
+//#include <NxFoundation.h>
+//#include <vector>
+//class MultisphGUI;
+//#define NORMALIZE(a) a.normalize()
+//#define MAGNITUDE(a) a.magnitude()
+//#define CROSS(a,b,c) a.cross(b,c)
+//#define ABS(a) (a>0. ? (a) : -(a))
+//#include "cpdefines.h"
+
+//#else // LBMDIM
+
+// use compatibility defines
+//#define NORMALIZE(a) normalize(a)
+//#define MAGNITUDE(a) norm(a)
+//#define CROSS(a,b,c) a=cross(b,c)
+
+//#endif // LBMDIM
+
+#define MAGNITUDE(a) norm(a)
+
+// math.h compatibility
+#define CP_PI ((LbmFloat)3.14159265358979323846)
+
+// project 2d test cases onto plane?
+// if not, 3d distance is used for 2d sim as well
+#define CP_PROJECT2D 1
+
+
+// default init for mincpdist, ControlForces::maxDistance
+#define CPF_MAXDINIT 10000.
+
+// storage of influence for a fluid cell/particle in lbm/sph
+class ControlForces
+{
+public:
+       ControlForces() { };
+       ~ControlForces() {};
+
+       // attraction force
+       LbmFloat weightAtt;
+       LbmVec forceAtt;
+       // velocity influence
+       LbmFloat weightVel;
+       LbmVec forceVel;
+       // maximal distance influence, 
+       // first is max. distance to first control particle
+       // second attraction strength
+       LbmFloat maxDistance;
+       LbmVec forceMaxd;
+
+       LbmFloat compAvWeight;
+       LbmVec compAv;
+
+       void resetForces() {
+               weightAtt = weightVel = 0.;
+               maxDistance = CPF_MAXDINIT;
+               forceAtt = forceVel = forceMaxd = LbmVec(0.,0.,0.);
+               compAvWeight=0.; compAv=LbmVec(0.);
+       };
+};
+
+
+// single control particle
+class ControlParticle
+{
+public:
+       ControlParticle() { reset(); };
+       ~ControlParticle() {};
+
+       // control parameters
+       
+       // position
+       LbmVec pos;
+       // size (influences influence radius)
+       LbmFloat size;
+       // overall strength of influence
+       LbmFloat influence;
+       // rotation axis
+       LbmVec rotaxis;
+
+       // computed values
+
+       // velocity
+       LbmVec vel;
+       // computed density
+       LbmFloat density;
+       LbmFloat densityWeight;
+
+       LbmVec avgVel;
+       LbmVec avgVelAcc;
+       LbmFloat avgVelWeight;
+
+       // init all zero / defaults
+       void reset();
+};
+
+
+// container for a particle configuration at time t
+class ControlParticleSet
+{
+public:
+
+       // time of particle set
+       LbmFloat time;
+       // particle positions
+       std::vector<ControlParticle> particles;
+
+};
+
+
+// container & management of control particles
+class ControlParticles
+{
+public:
+       ControlParticles();
+       ~ControlParticles();
+
+       // reset datastructures for next influence step
+       // if motion object is given, particle 1 of second system is used for overall 
+       // position and speed offset
+       void prepareControl(LbmFloat simtime, LbmFloat dt, ControlParticles *motion);
+       // post control operations
+       void finishControl(std::vector<ControlForces> &forces, LbmFloat iatt, LbmFloat ivel, LbmFloat imaxd);
+       // recalculate 
+       void calculateKernelWeight();
+
+       // calculate forces at given position, and modify velocity
+       // according to timestep (from initControl)
+       void calculateCpInfluenceOpt (ControlParticle *cp, LbmVec fluidpos, LbmVec fluidvel, ControlForces *force, LbmFloat fillFactor);
+       void calculateMaxdForce      (ControlParticle *cp, LbmVec fluidpos, ControlForces *force);
+
+       // no. of particles
+       inline int getSize() { return (int)_particles.size(); }
+       int getTotalSize();
+       // get particle [i]
+       inline ControlParticle* getParticle(int i){ return &_particles[i]; }
+
+       // set influence parameters
+       void setInfluenceTangential(LbmFloat set) { _influenceTangential=set; }
+       void setInfluenceAttraction(LbmFloat set) { _influenceAttraction=set; }
+       void setInfluenceMaxdist(LbmFloat set)    { _influenceMaxdist=set; }
+       // calculate for delta t
+       void setInfluenceVelocity(LbmFloat set, LbmFloat dt);
+       // get influence parameters
+       inline LbmFloat getInfluenceAttraction()    { return _influenceAttraction; }
+       inline LbmFloat getInfluenceTangential()    { return _influenceTangential; }
+       inline LbmFloat getInfluenceVelocity()      { return _influenceVelocity; }
+       inline LbmFloat getInfluenceMaxdist()       { return _influenceMaxdist; }
+       inline LbmFloat getCurrTimestep()           { return _currTimestep; }
+
+       void setRadiusAtt(LbmFloat set)       { _radiusAtt=set; }
+       inline LbmFloat getRadiusAtt()        { return _radiusAtt; }
+       void setRadiusVel(LbmFloat set)       { _radiusVel=set; }
+       inline LbmFloat getRadiusVel()        { return _radiusVel; }
+       void setRadiusMaxd(LbmFloat set)      { _radiusMaxd=set; }
+       inline LbmFloat getRadiusMaxd()       { return _radiusMaxd; }
+       void setRadiusMinMaxd(LbmFloat set)   { _radiusMinMaxd=set; }
+       inline LbmFloat getRadiusMinMaxd()    { return _radiusMinMaxd; }
+
+       LbmFloat getControlTimStart();
+       LbmFloat getControlTimEnd();
+
+       // set/get characteristic length (and inverse)
+       void setCharLength(LbmFloat set)      { _charLength=set; _charLengthInv=1./_charLength; }
+       inline LbmFloat getCharLength()       { return _charLength;}
+       inline LbmFloat getCharLengthInv()    { return _charLengthInv;}
+
+       // set init parameters
+       void setInitTimeScale(LbmFloat set)  { _initTimeScale = set; };
+       void setInitMirror(string set)  { _initMirror = set; };
+       string getInitMirror()          { return _initMirror; };
+
+       void setLastOffset(LbmVec set) { _initLastPartOffset = set; };
+       void setLastScale(LbmVec set)  { _initLastPartScale = set; };
+       void setOffset(LbmVec set) { _initPartOffset = set; };
+       void setScale(LbmVec set)  { _initPartScale = set; };
+
+       // set/get cps params
+       void setCPSWith(LbmFloat set)       { mCPSWidth = set; };
+       void setCPSTimestep(LbmFloat set)   { mCPSTimestep = set; };
+       void setCPSTimeStart(LbmFloat set)  { mCPSTimeStart = set; };
+       void setCPSTimeEnd(LbmFloat set)    { mCPSTimeEnd = set; };
+       void setCPSMvmWeightFac(LbmFloat set) { mCPSWeightFac = set; };
+
+       LbmFloat getCPSWith()       { return mCPSWidth; };
+       LbmFloat getCPSTimestep()   { return mCPSTimestep; };
+       LbmFloat getCPSTimeStart()  { return mCPSTimeStart; };
+       LbmFloat getCPSTimeEnd()    { return mCPSTimeEnd; };
+       LbmFloat getCPSMvmWeightFac() { return mCPSWeightFac; };
+
+       void setDebugInit(int set)       { mDebugInit = set; };
+
+       // set init parameters
+       void setFluidSpacing(LbmFloat set)  { _fluidSpacing = set; };
+
+       // load positions & timing from text file
+       int initFromTextFile(string filename);
+       int initFromTextFileOld(string filename);
+       // load positions & timing from gzipped binary file
+       int initFromBinaryFile(string filename);
+       int initFromMVCMesh(string filename);
+       // init an example test case
+       int initExampleSet();
+
+       // init for a given time
+       void initTime(LbmFloat t, LbmFloat dt);
+
+       // blender test init
+       void initBlenderTest();
+       
+       int initFromObject(ntlGeometryObjModel *model);
+
+protected:
+       // sets influence params
+       friend class MultisphGUI;
+
+       // tangential and attraction influence
+       LbmFloat _influenceTangential, _influenceAttraction;
+       // direct velocity influence
+       LbmFloat _influenceVelocity;
+       // maximal distance influence
+       LbmFloat _influenceMaxdist;
+
+       // influence radii
+       LbmFloat _radiusAtt, _radiusVel, _radiusMinMaxd, _radiusMaxd;
+
+       // currently valid time & timestep
+       LbmFloat _currTime, _currTimestep;
+       // all particles
+       std::vector<ControlParticle> _particles;
+
+       // particle sets
+       std::vector<ControlParticleSet> mPartSets;
+
+       // additional parameters for initing particles
+       LbmFloat _initTimeScale;
+       LbmVec _initPartOffset;
+       LbmVec _initPartScale;
+       LbmVec _initLastPartOffset;
+       LbmVec _initLastPartScale;
+       // mirror particles for loading?
+       string _initMirror;
+
+       // row spacing paramter, e.g. use for approximation of kernel area/volume
+       LbmFloat _fluidSpacing;
+       // save current kernel weight
+       LbmFloat _kernelWeight;
+       // charateristic length in world coordinates for normalizatioon of forces
+       LbmFloat _charLength, _charLengthInv;
+
+
+       /*! do ani mesh CPS */
+       void calculateCPS(string filename);
+       //! ani mesh cps params 
+       ntlVec3Gfx mvCPSStart, mvCPSEnd;
+       gfxReal mCPSWidth, mCPSTimestep;
+       gfxReal mCPSTimeStart, mCPSTimeEnd;
+       gfxReal mCPSWeightFac;
+
+       int mDebugInit;
+
+       
+protected:
+       // apply init transformations
+       void applyTrafos();
+
+       // helper function for init -> swap components everywhere
+       void swapCoords(int a,int b);
+       // helper function for init -> mirror time
+       void mirrorTime();
+
+       // helper, init given array
+       void initTimeArray(LbmFloat t, std::vector<ControlParticle> &parts);
+
+       bool checkPointInside(ntlTree *tree, ntlVec3Gfx org, gfxReal &distance);
+};
+
+
+
+#endif
+
index b2779f51c3bea86a6035ab62728af8257f7ba3c5..f7923322d5b4e3e008197cfb14056ea7e3abc99f 100644 (file)
@@ -30,6 +30,7 @@ int guiRoiMaxLev=6, guiRoiMinLev=0;
 ntlWorld *gpWorld = NULL;
 
 
+
 // API
 
 // reset elbeemSimulationSettings struct with defaults
@@ -95,6 +96,13 @@ int elbeemInit() {
        return 0;
 }
 
+// fluidsim end
+extern "C" 
+int elbeemFree() {
+       
+       return 0;
+}
+
 // start fluidsim init
 extern "C" 
 int elbeemAddDomain(elbeemSimulationSettings *settings) {
@@ -158,13 +166,27 @@ void elbeemResetMesh(elbeemMesh *mesh) {
 
        /* name of the mesh, mostly for debugging */
        mesh->name = "[unnamed]";
+       
+       /* fluid control settings */
+       mesh->cpsTimeStart = 0;
+       mesh->cpsTimeEnd = 0;
+       mesh->cpsQuality = 0;
+       
+       mesh->channelSizeAttractforceStrength = 0;
+       mesh->channelAttractforceStrength = NULL;
+       mesh->channelSizeAttractforceRadius = 0;
+       mesh->channelAttractforceRadius = NULL;
+       mesh->channelSizeVelocityforceStrength = 0;
+       mesh->channelVelocityforceStrength = NULL;
+       mesh->channelSizeVelocityforceRadius = 0;
+       mesh->channelVelocityforceRadius = NULL;
 }
 
 int globalMeshCounter = 1;
 // add mesh as fluidsim object
 extern "C" 
 int elbeemAddMesh(elbeemMesh *mesh) {
-       int initType = -1;
+       int initType;
        if(getElbeemState() != SIMWORLD_INITIALIZING) { errFatal("elbeemAddMesh","World and domain not initialized, call elbeemInit and elbeemAddDomain before...", SIMWORLD_INITERROR); }
 
        switch(mesh->type) {
@@ -176,9 +198,9 @@ int elbeemAddMesh(elbeemMesh *mesh) {
                case OB_FLUIDSIM_FLUID: initType = FGI_FLUID; break;
                case OB_FLUIDSIM_INFLOW: initType = FGI_MBNDINFLOW; break;
                case OB_FLUIDSIM_OUTFLOW: initType = FGI_MBNDOUTFLOW; break;
+               case OB_FLUIDSIM_CONTROL: initType = FGI_CONTROL; break;
+               default: return 1; // invalid type
        }
-       // invalid type?
-       if(initType<0) return 1;
        
        ntlGeometryObjModel *obj = new ntlGeometryObjModel( );
        gpWorld->getRenderGlobals()->getSimScene()->addGeoClass( obj );
@@ -195,17 +217,34 @@ int elbeemAddMesh(elbeemMesh *mesh) {
        obj->setGeoInitId( mesh->parentDomainId+1 );
        obj->setGeoInitIntersect(true);
        obj->setGeoInitType(initType);
-       obj->setGeoPartSlipValue(mesh->obstaclePartslip);
+       
+       // abuse partslip value for control fluid: reverse control keys or not
+       if(initType == FGI_CONTROL)
+               obj->setGeoPartSlipValue(mesh->obstacleType);
+       else
+               obj->setGeoPartSlipValue(mesh->obstaclePartslip);
+       
        obj->setGeoImpactFactor(mesh->obstacleImpactFactor);
+       
+       /* fluid control features */
+       obj->setCpsTimeStart(mesh->cpsTimeStart);
+       obj->setCpsTimeEnd(mesh->cpsTimeEnd);
+       obj->setCpsQuality(mesh->cpsQuality);
+       
        if((mesh->volumeInitType<VOLUMEINIT_VOLUME)||(mesh->volumeInitType>VOLUMEINIT_BOTH)) mesh->volumeInitType = VOLUMEINIT_VOLUME;
        obj->setVolumeInit(mesh->volumeInitType);
        // use channel instead, obj->setInitialVelocity( ntlVec3Gfx(mesh->iniVelocity[0], mesh->iniVelocity[1], mesh->iniVelocity[2]) );
+       
        obj->initChannels(
                        mesh->channelSizeTranslation, mesh->channelTranslation, 
                        mesh->channelSizeRotation,    mesh->channelRotation, 
                        mesh->channelSizeScale,       mesh->channelScale,
                        mesh->channelSizeActive,      mesh->channelActive,
-                       mesh->channelSizeInitialVel,  mesh->channelInitialVel
+                       mesh->channelSizeInitialVel,  mesh->channelInitialVel,
+                       mesh->channelSizeAttractforceStrength,  mesh->channelAttractforceStrength,
+                       mesh->channelSizeAttractforceRadius,  mesh->channelAttractforceRadius,
+                       mesh->channelSizeVelocityforceStrength,  mesh->channelVelocityforceStrength,
+                       mesh->channelSizeVelocityforceRadius,  mesh->channelVelocityforceRadius
                );
        obj->setLocalCoordInivel( mesh->localInivelCoords );
 
@@ -227,6 +266,7 @@ int elbeemSimulate(void) {
                if(getElbeemState() != SIMWORLD_STOP) {
                        // ok, we're done...
                        delete gpWorld;
+                       
                        gpWorld = NULL;
                        debMsgStd("elbeemSimulate",DM_NOTIFY, "El'Beem simulation done, time: "<<getTimeString(timeend-timestart)<<".\n", 2 ); 
                } else {
diff --git a/intern/elbeem/intern/elbeem.h b/intern/elbeem/intern/elbeem.h
deleted file mode 100644 (file)
index 2a594dd..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/******************************************************************************
- *
- * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
- * All code distributed as part of El'Beem is covered by the version 2 of the 
- * GNU General Public License. See the file COPYING for details.
- * Copyright 2003-2006 Nils Thuerey
- *
- * API header
- */
-#ifndef ELBEEM_API_H
-#define ELBEEM_API_H
-
-
-// simulation run callback function type (elbeemSimulationSettings->runsimCallback)
-// best use with FLUIDSIM_CBxxx defines below.
-// >parameters
-// return values: 0=continue, 1=stop, 2=abort
-// data pointer: user data pointer from elbeemSimulationSettings->runsimUserData
-// status integer: 1=running simulation, 2=new frame saved
-// frame integer: if status is 1, contains current frame number
-typedef int (*elbeemRunSimulationCallback)(void *data, int status, int frame);
-#define FLUIDSIM_CBRET_CONTINUE    0
-#define FLUIDSIM_CBRET_STOP        1
-#define FLUIDSIM_CBRET_ABORT       2 
-#define FLUIDSIM_CBSTATUS_STEP     1 
-#define FLUIDSIM_CBSTATUS_NEWFRAME 2 
-
-
-// global settings for the simulation
-typedef struct elbeemSimulationSettings {
-  /* version number */
-  short version;
-       /* id number of simulation domain, needed if more than a
-        * single domain should be simulated */
-       short domainId;
-
-       /* geometrical extent */
-       float geoStart[3], geoSize[3];
-
-  /* resolutions */
-  short resolutionxyz;
-  short previewresxyz;
-  /* size of the domain in real units (meters along largest resolution x,y,z extent) */
-  float realsize;
-
-  /* fluid properties */
-  double viscosity;
-  /* gravity strength */
-  float gravity[3];
-  /* anim start end time */
-  float animStart, aniFrameTime;
-       /* no. of frames to simulate & output */
-       short noOfFrames;
-  /* g star param (LBM compressibility) */
-  float gstar;
-  /* activate refinement? */
-  short maxRefine;
-  /* probability for surface particle generation (0.0=off) */
-  float generateParticles;
-  /* amount of tracer particles to generate (0=off) */
-  int numTracerParticles;
-
-  /* store output path, and file prefix for baked fluid surface */
-  char outputPath[160+80];
-
-       /* channel for frame time, visc & gravity animations */
-       int channelSizeFrameTime;
-       float *channelFrameTime;
-       int channelSizeViscosity;
-       float *channelViscosity;
-       int channelSizeGravity;
-       float *channelGravity;  // vector
-
-       /* boundary types and settings for domain walls */
-       short domainobsType;
-       float domainobsPartslip;
-       /* generate speed vectors for vertices (e.g. for image based motion blur)*/
-       short generateVertexVectors;
-       /* strength of surface smoothing */
-       float surfaceSmoothing;
-       /* no. of surface subdivisions */
-       int   surfaceSubdivs;
-
-       /* global transformation to apply to fluidsim mesh */
-       float surfaceTrafo[4*4];
-
-       /* development variables, testing for upcoming releases...*/
-       float farFieldSize;
-
-       /* callback function to notify calling program of performed simulation steps
-        * or newly available frame data, if NULL it is ignored */
-       elbeemRunSimulationCallback runsimCallback;
-       /* pointer passed to runsimCallback for user data storage */
-       void* runsimUserData;
-
-} elbeemSimulationSettings;
-
-
-// defines for elbeemMesh->type below
-#define OB_FLUIDSIM_FLUID       4
-#define OB_FLUIDSIM_OBSTACLE    8
-#define OB_FLUIDSIM_INFLOW      16
-#define OB_FLUIDSIM_OUTFLOW     32
-
-// defines for elbeemMesh->obstacleType below
-#define FLUIDSIM_OBSTACLE_NOSLIP     1
-#define FLUIDSIM_OBSTACLE_PARTSLIP   2
-#define FLUIDSIM_OBSTACLE_FREESLIP   3
-
-#define OB_VOLUMEINIT_VOLUME 1
-#define OB_VOLUMEINIT_SHELL  2
-#define OB_VOLUMEINIT_BOTH   (OB_VOLUMEINIT_SHELL|OB_VOLUMEINIT_VOLUME)
-
-// a single mesh object
-typedef struct elbeemMesh {
-  /* obstacle,fluid or inflow... */
-  short type;
-       /* id of simulation domain it belongs to */
-       short parentDomainId;
-
-       /* vertices */
-  int numVertices;
-       float *vertices; // = float[n][3];
-       /* animated vertices */
-  int channelSizeVertices;
-       float *channelVertices; // = float[channelSizeVertices* (n*3+1) ];
-
-       /* triangles */
-       int   numTriangles;
-  int   *triangles; // = int[][3];
-
-       /* animation channels */
-       int channelSizeTranslation;
-       float *channelTranslation;
-       int channelSizeRotation;
-       float *channelRotation;
-       int channelSizeScale;
-       float *channelScale;
-       
-       /* active channel */
-       int channelSizeActive;
-       float *channelActive;
-       /* initial velocity channel (e.g. for inflow) */
-       int channelSizeInitialVel;
-       float *channelInitialVel; // vector
-       /* use initial velocity in object coordinates? (e.g. for rotation) */
-       short localInivelCoords;
-       /* boundary types and settings */
-       short obstacleType;
-       float obstaclePartslip;
-       /* amount of force transfer from fluid to obj, 0=off, 1=normal */
-       float obstacleImpactFactor;
-       /* init volume, shell or both? use OB_VOLUMEINIT_xxx defines above */
-       short volumeInitType;
-
-       /* name of the mesh, mostly for debugging */
-       const char *name;
-} elbeemMesh;
-
-// API functions
-
-#ifdef __cplusplus
-extern "C"  {
-#endif // __cplusplus
-
-// reset elbeemSimulationSettings struct with defaults
-void elbeemResetSettings(struct elbeemSimulationSettings*);
-// start fluidsim init (returns !=0 upon failure)
-int elbeemInit(void);
-
-// start fluidsim init (returns !=0 upon failure)
-int elbeemAddDomain(struct elbeemSimulationSettings*);
-
-// get failure message during simulation or init
-// if an error occured (the string is copied into buffer,
-// max. length = 256 chars )
-void elbeemGetErrorString(char *buffer);
-
-// reset elbeemMesh struct with zeroes
-void elbeemResetMesh(struct elbeemMesh*);
-
-// add mesh as fluidsim object
-int elbeemAddMesh(struct elbeemMesh*);
-
-// do the actual simulation
-int elbeemSimulate(void);
-
-// continue a previously stopped simulation
-int elbeemContinueSimulation(void);
-
-
-// helper functions 
-
-// simplify animation channels
-// returns if the channel and its size changed
-int elbeemSimplifyChannelFloat(float *channel, int *size);
-int elbeemSimplifyChannelVec3(float *channel, int *size);
-
-// helper functions implemented in utilities.cpp
-
-/* set elbeem debug output level (0=off to 10=full on) */
-void elbeemSetDebugLevel(int level);
-/* elbeem debug output function, prints if debug level >0 */
-void elbeemDebugOut(char *msg);
-
-/* estimate how much memory a given setup will require */
-double elbeemEstimateMemreq(int res,
-    float sx, float sy, float sz,
-    int refine, char *retstr);
-
-
-
-#ifdef __cplusplus
-}
-#endif // __cplusplus
-
-
-
-/******************************************************************************/
-// internal defines, do not use for initializing elbeemMesh
-// structs, for these use OB_xxx defines above
-
-/*! fluid geometry init types */
-#define FGI_FLAGSTART   16
-#define FGI_FLUID                        (1<<(FGI_FLAGSTART+ 0))
-#define FGI_NO_FLUID     (1<<(FGI_FLAGSTART+ 1))
-#define FGI_BNDNO                        (1<<(FGI_FLAGSTART+ 2))
-#define FGI_BNDFREE              (1<<(FGI_FLAGSTART+ 3))
-#define FGI_BNDPART              (1<<(FGI_FLAGSTART+ 4))
-#define FGI_NO_BND               (1<<(FGI_FLAGSTART+ 5))
-#define FGI_MBNDINFLOW (1<<(FGI_FLAGSTART+ 6))
-#define FGI_MBNDOUTFLOW        (1<<(FGI_FLAGSTART+ 7))
-
-// all boundary types at once
-#define FGI_ALLBOUNDS ( FGI_BNDNO | FGI_BNDFREE | FGI_BNDPART | FGI_MBNDINFLOW | FGI_MBNDOUTFLOW )
-
-
-#endif // ELBEEM_API_H
diff --git a/intern/elbeem/intern/elbeem_control.cpp b/intern/elbeem/intern/elbeem_control.cpp
new file mode 100644 (file)
index 0000000..800167b
--- /dev/null
@@ -0,0 +1,25 @@
+/******************************************************************************
+ *
+ * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
+ * All code distributed as part of El'Beem is covered by the version 2 of the 
+ * GNU General Public License. See the file COPYING for details.
+ * Copyright 2003-2006 Nils Thuerey
+ *
+ * Control API header
+ */
+
+#include "elbeem.h"
+#include "elbeem_control.h"
+
+// add mesh as fluidsim object
+int elbeemControlAddSet(struct elbeemControl*) {
+       
+       return 0;
+}
+
+int elbeemControlComputeMesh(struct elbeemMesh*) {
+       
+       
+       return 0;
+}
+
diff --git a/intern/elbeem/intern/elbeem_control.h b/intern/elbeem/intern/elbeem_control.h
new file mode 100644 (file)
index 0000000..70a58fe
--- /dev/null
@@ -0,0 +1,62 @@
+/******************************************************************************
+ *
+ * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
+ * All code distributed as part of El'Beem is covered by the version 2 of the 
+ * GNU General Public License. See the file COPYING for details.
+ * Copyright 2003-2006 Nils Thuerey
+ *
+ * Control API header
+ */
+#ifndef ELBEEMCONTROL_API_H
+#define ELBEEMCONTROL_API_H
+
+// a single control particle set
+typedef struct elbeemControl {
+       /* influence forces */
+       float influenceAttraction;
+       float *channelInfluenceAttraction;
+       float channelSizeInfluenceAttraction;
+
+       float influenceVelocity;
+       float *channelInfluenceVelocity;
+       float channelSizeInfluenceVelocity;
+
+       float influenceMaxdist;
+       float *channelInfluenceMaxdist;
+       float channelSizeInfluenceMaxdist;
+
+       /* influence force radii */
+       float radiusAttraction;
+       float *channelRadiusAttraction;
+       float channelSizeRadiusAttraction;
+
+       float radiusVelocity;
+       float *channelRadiusVelocity;
+       float channelSizeRadiusVelocity;
+
+       float radiusMindist;
+       float *channelRadiusMindist;
+       float channelSizeRadiusMindist;
+       float radiusMaxdist;
+       float *channelRadiusMaxdist;
+       float channelSizeRadiusMaxdist;
+
+       /* control particle positions/scale */
+       float offset[3];
+       float *channelOffset;
+       float channelSizeOffset;
+       
+       float scale[3];
+       float *channelScale;
+       float channelSizeScale;
+       
+} elbeemControl;
+
+
+// add mesh as fluidsim object
+int elbeemControlAddSet(struct elbeemControl*);
+
+// sample & track mesh control particles, TODO add return type...
+int elbeemControlComputeMesh(struct elbeemMesh*);
+
+#endif // ELBEEMCONTROL_API_H
index 1b0ba13c70710b55d4874bbb61de44ed0d542ba4..9925565b85d67f55fd6785d172db95ad5b33db9b 100644 (file)
 #include <algorithm>
 #include <stdio.h>
 
-#if (defined (__sun__) || defined (__sun)) || (!defined(linux) && (defined (__sparc) || defined (__sparc__)))
-#include <ieeefp.h>
-#endif
-
 // just use default rounding for platforms where its not available
 #ifndef round
 #define round(x) (x)
diff --git a/intern/elbeem/intern/mvmcoords.cpp b/intern/elbeem/intern/mvmcoords.cpp
new file mode 100644 (file)
index 0000000..ac954b9
--- /dev/null
@@ -0,0 +1,193 @@
+/******************************************************************************
+ *
+// El'Beem - the visual lattice boltzmann freesurface simulator
+// All code distributed as part of El'Beem is covered by the version 2 of the 
+// GNU General Public License. See the file COPYING for details.  
+//
+// Copyright 2008 Nils Thuerey , Richard Keiser, Mark Pauly, Ulrich Ruede
+//
+ *
+ * Mean Value Mesh Coords class
+ *
+ *****************************************************************************/
+
+#include "mvmcoords.h"
+#include <algorithm>
+using std::vector;
+
+void MeanValueMeshCoords::clear() 
+{
+       mVertices.resize(0);
+       mNumVerts = 0;
+}
+
+void MeanValueMeshCoords::calculateMVMCs(vector<ntlVec3Gfx> &reference_vertices, vector<ntlTriangle> &tris, 
+               vector<ntlVec3Gfx> &points, gfxReal numweights)
+{
+       clear();
+       mvmTransferPoint tds;
+       int mem = 0;
+       int i = 0;
+
+       mNumVerts = (int)reference_vertices.size();
+
+       for (vector<ntlVec3Gfx>::iterator iter = points.begin(); iter != points.end(); ++iter, ++i) {
+               /*
+               if(i%(points.size()/10)==1) debMsgStd("MeanValueMeshCoords::calculateMVMCs",DM_MSG,"Computing weights, points: "<<i<<"/"<<points.size(),5 );
+               */
+               tds.lastpos = *iter;
+               tds.weights.resize(0);  // clear
+               computeWeights(reference_vertices, tris, tds, numweights);
+               mem += (int)tds.weights.size();
+               mVertices.push_back(tds);
+       }    
+       int mbmem = mem * sizeof(mvmFloat) / (1024*1024);
+       debMsgStd("MeanValueMeshCoords::calculateMVMCs",DM_MSG,"vertices:"<<mNumVerts<<" points:"<<points.size()<<" weights:"<<mem<<", wmem:"<<mbmem<<"MB ",7 );
+}
+
+// from: mean value coordinates for closed triangular meshes
+// attention: fails if a point is exactly (or very close) to a vertex
+void MeanValueMeshCoords::computeWeights(vector<ntlVec3Gfx> &reference_vertices, vector<ntlTriangle>& tris,
+               mvmTransferPoint& tds, gfxReal numweights)
+{
+       const bool mvmFullDebug=false;
+       //const ntlVec3Gfx cEPS = 1.0e-6;
+       const mvmFloat cEPS = 1.0e-14;
+
+       //mvmFloat d[3], s[3], phi[3],c[3];
+       ntlVec3d u[3],c,d,s,phi;
+       int indices[3];
+
+       for (int i = 0; i < (int)reference_vertices.size(); ++i) {
+               tds.weights.push_back(mvmIndexWeight(i, 0.0));
+       }
+
+       // for each triangle
+       //for (vector<ntlTriangle>::iterator iter = tris.begin(); iter != tris.end();) {
+       for(int t=0; t<(int)tris.size(); t++) {
+
+               for (int i = 0; i < 3; ++i) { //, ++iter) {
+                       indices[i] = tris[t].getPoints()[i];
+                       u[i] = vec2D(reference_vertices[ indices[i] ]-tds.lastpos);
+                       d[i] = normalize(u[i]); //.normalize();        
+                       //assert(d[i] != 0.);
+                       if(mvmFullDebug) errMsg("MeanValueMeshCoords::computeWeights","t"<<t<<" i"<<indices[i] //<<" lp"<<tds.lastpos
+                                       <<" v"<<reference_vertices[indices[i]]<<" u"<<u[i]<<" ");
+                       // on vertex!
+                       //? if(d[i]<=0.) continue;
+               }
+               //for (int i = 0; i < 3; ++i) { errMsg("III"," "<<i             <<" i"<<indices[i]<<reference_vertices[ indices[i] ] ); }
+
+               // arcsin is not needed, see paper
+               phi[0] = 2.*asin( (mvmFloat)(0.5* norm(u[1]-u[2]) )  );
+               phi[1] = 2.*asin( (mvmFloat)(0.5* norm(u[0]-u[2]) )  );
+               phi[2] = 2.*asin( (mvmFloat)(0.5* norm(u[0]-u[1]) )  );
+               mvmFloat h = (phi[0] + phi[1] + phi[2])*0.5;
+               if (M_PI-h < cEPS) {
+                       if(mvmFullDebug) errMsg("MeanValueMeshCoords::computeWeights","point on triangle");
+                       tds.weights.resize(0);
+                       tds.weights.push_back( mvmIndexWeight(indices[0], sin(phi[0])*d[1]*d[2]));
+                       tds.weights.push_back( mvmIndexWeight(indices[1], sin(phi[1])*d[0]*d[2]));
+                       tds.weights.push_back( mvmIndexWeight(indices[2], sin(phi[2])*d[1]*d[0]));
+                       break;
+               }
+               mvmFloat sinh = 2.*sin(h);
+               c[0] = (sinh*sin(h-phi[0]))/(sin(phi[1])*sin(phi[2]))-1.;
+               c[1] = (sinh*sin(h-phi[1]))/(sin(phi[0])*sin(phi[2]))-1.;    
+               c[2] = (sinh*sin(h-phi[2]))/(sin(phi[0])*sin(phi[1]))-1.;   
+               if(mvmFullDebug) errMsg("MeanValueMeshCoords::computeWeights","c="<<c<<" phi="<<phi<<" d="<<d);
+               //if (c[0] > 1. || c[0] < 0. || c[1] > 1. || c[1] < 0. || c[2] > 1. || c[2] < 0.) continue;
+
+               s[0] = sqrtf((float)(1.-c[0]*c[0]));
+               s[1] = sqrtf((float)(1.-c[1]*c[1]));
+               s[2] = sqrtf((float)(1.-c[2]*c[2]));
+
+               if(mvmFullDebug) errMsg("MeanValueMeshCoords::computeWeights","s");
+               if (s[0] <= cEPS || s[1] <= cEPS || s[2] <= cEPS) {
+                       //MSG("position lies outside the triangle on the same plane -> ignore it");
+                       continue;
+               }
+               const mvmFloat u0x = u[0][0];
+               const mvmFloat u0y = u[0][1];
+               const mvmFloat u0z = u[0][2];
+               const mvmFloat u1x = u[1][0];
+               const mvmFloat u1y = u[1][1];
+               const mvmFloat u1z = u[1][2];
+               const mvmFloat u2x = u[2][0];
+               const mvmFloat u2y = u[2][1];
+               const mvmFloat u2z = u[2][2];
+               mvmFloat det = u0x*u1y*u2z - u0x*u1z*u2y + u0y*u1z*u2x - u0y*u1x*u2z + u0z*u1x*u2y - u0z*u1y*u2x;
+               //assert(det != 0.);
+               if (det < 0.) {
+                       s[0] = -s[0];
+                       s[1] = -s[1];
+                       s[2] = -s[2];
+               }
+
+               tds.weights[indices[0]].weight += (phi[0]-c[1]*phi[2]-c[2]*phi[1])/(d[0]*sin(phi[1])*s[2]);
+               tds.weights[indices[1]].weight += (phi[1]-c[2]*phi[0]-c[0]*phi[2])/(d[1]*sin(phi[2])*s[0]);
+               tds.weights[indices[2]].weight += (phi[2]-c[0]*phi[1]-c[1]*phi[0])/(d[2]*sin(phi[0])*s[1]);
+               if(mvmFullDebug) { errMsg("MeanValueMeshCoords::computeWeights","i"<<indices[0]<<" o"<<tds.weights[indices[0]].weight);
+               errMsg("MeanValueMeshCoords::computeWeights","i"<<indices[1]<<" o"<<tds.weights[indices[1]].weight);
+               errMsg("MeanValueMeshCoords::computeWeights","i"<<indices[2]<<" o"<<tds.weights[indices[2]].weight);
+               errMsg("MeanValueMeshCoords::computeWeights","\n\n\n"); }
+       }
+
+       //sort weights
+       if((numweights>0.)&& (numweights<1.) ) {
+       //if( ((int)tds.weights.size() > maxNumWeights) && (maxNumWeights > 0) ) {
+         int maxNumWeights = (int)(tds.weights.size()*numweights);
+               if(maxNumWeights<=0) maxNumWeights = 1;
+               std::sort(tds.weights.begin(), tds.weights.end(), std::greater<mvmIndexWeight>());
+               // only use maxNumWeights-th largest weights
+               tds.weights.resize(maxNumWeights);
+       }
+
+       // normalize weights
+       mvmFloat totalWeight = 0.;
+       for (vector<mvmIndexWeight>::const_iterator witer = tds.weights.begin();
+                       witer != tds.weights.end(); ++witer) {
+               totalWeight += witer->weight;
+       }
+       mvmFloat invTotalWeight;
+       if (totalWeight == 0.) {
+               if(mvmFullDebug) errMsg("MeanValueMeshCoords::computeWeights","totalWeight == 0");
+               invTotalWeight = 0.0;
+       } else {
+               invTotalWeight = 1.0/totalWeight;
+       }
+
+       for (vector<mvmIndexWeight>::iterator viter = tds.weights.begin();
+                       viter != tds.weights.end(); ++viter) {
+               viter->weight *= invTotalWeight;  
+               //assert(finite(viter->weight) != 0);
+               if(!finite(viter->weight)) viter->weight=0.;
+       }
+}
+
+void MeanValueMeshCoords::transfer(vector<ntlVec3Gfx> &vertices, vector<ntlVec3Gfx>& displacements) 
+{
+       displacements.resize(0);
+
+       //debMsgStd("MeanValueMeshCoords::transfer",DM_MSG,"vertices:"<<mNumVerts<<" curr_verts:"<<vertices.size()<<" ",7 );
+       if((int)vertices.size() != mNumVerts) {
+               errMsg("MeanValueMeshCoords::transfer","Different no of verts: "<<vertices.size()<<" vs "<<mNumVerts);
+               return;
+       }
+
+       for (vector<mvmTransferPoint>::iterator titer = mVertices.begin(); titer != mVertices.end(); ++titer) {
+               mvmTransferPoint &tds = *titer;
+               ntlVec3Gfx newpos(0.0);
+
+               for (vector<mvmIndexWeight>::iterator witer = tds.weights.begin();
+                               witer != tds.weights.end(); ++witer) {
+                       newpos += vertices[witer->index] * witer->weight;
+                       //errMsg("transfer","np"<<newpos<<" v"<<vertices[witer->index]<<" w"<< witer->weight);
+               }
+
+               displacements.push_back(newpos);
+               //displacements.push_back(newpos - tds.lastpos);
+               //tds.lastpos = newpos;
+       }
+}
+
diff --git a/intern/elbeem/intern/mvmcoords.h b/intern/elbeem/intern/mvmcoords.h
new file mode 100644 (file)
index 0000000..51b0958
--- /dev/null
@@ -0,0 +1,85 @@
+/******************************************************************************
+ *
+// El'Beem - the visual lattice boltzmann freesurface simulator
+// All code distributed as part of El'Beem is covered by the version 2 of the 
+// GNU General Public License. See the file COPYING for details.  
+//
+// Copyright 2008 Nils Thuerey , Richard Keiser, Mark Pauly, Ulrich Ruede
+//
+ *
+ * Mean Value Mesh Coords class
+ *
+ *****************************************************************************/
+
+#ifndef MVMCOORDS_H
+#define MVMCOORDS_H
+
+#include "utilities.h"
+#include "ntl_ray.h"
+#include <vector>
+#define mvmFloat double
+
+#ifdef WIN32
+#ifndef FREE_WINDOWS
+#include "float.h"
+#define isnan(n) _isnan(n)
+#define finite _finite
+#endif
+#endif
+
+// weight and triangle index
+class mvmIndexWeight {
+       public:
+
+               mvmIndexWeight() : weight(0.0) {}
+
+               mvmIndexWeight(int const& i, mvmFloat const& w) :
+                       weight(w), index(i) {}
+
+               // for sorting
+               bool operator> (mvmIndexWeight const& w) const { return this->weight > w.weight; } 
+               bool operator< (mvmIndexWeight const& w) const { return this->weight < w.weight; }
+
+               mvmFloat weight;
+               int index;
+};
+
+// transfer point with weights
+class mvmTransferPoint {
+       public:
+               //! position of transfer point
+               ntlVec3Gfx lastpos;
+               //! triangle weights
+               std::vector<mvmIndexWeight> weights;
+};
+
+
+//! compute mvmcs
+class MeanValueMeshCoords {
+
+       public:
+
+    MeanValueMeshCoords() {}
+    ~MeanValueMeshCoords() {
+        clear();
+    }
+
+    void clear();
+
+    void calculateMVMCs(std::vector<ntlVec3Gfx> &reference_vertices, 
+                               std::vector<ntlTriangle> &tris, std::vector<ntlVec3Gfx> &points, gfxReal numweights);
+    
+    void transfer(std::vector<ntlVec3Gfx> &vertices, std::vector<ntlVec3Gfx>& displacements);
+
+       protected:
+
+    void computeWeights(std::vector<ntlVec3Gfx> &reference_vertices, 
+                               std::vector<ntlTriangle> &tris, mvmTransferPoint& tds, gfxReal numweights);
+
+    std::vector<mvmTransferPoint> mVertices;
+    int mNumVerts;
+
+};
+
+#endif
+
index 9282371d1836707b759a62ca38493aacc63fc7c0..545f8c1d54f57caccfbcedd51a0d99283d2534a3 100644 (file)
@@ -37,6 +37,7 @@ class ntlGeometryClass
                //! Default destructor
                virtual ~ntlGeometryClass() {
                        delete mpAttrs; 
+                       delete mpSwsAttrs;
                };
 
                //! Return type id
index f2ebd5726820f750916bdfe18023d958a4af6028..c7a222af3e5f6a9652fa6ebcb60b3c19ccda9c0c 100644 (file)
@@ -41,7 +41,9 @@ ntlGeometryObject::ntlGeometryObject() :
        mCachedMovPoints(), mCachedMovNormals(),
        mTriangleDivs1(), mTriangleDivs2(), mTriangleDivs3(),
        mMovPntsInited(-100.0), mMaxMovPnt(-1),
-       mcGeoActive(1.)
+       mcGeoActive(1.),
+       mCpsTimeStart(0.), mCpsTimeEnd(1.0), mCpsQuality(10.),
+       mcAttrFStr(0.),mcAttrFRad(0.), mcVelFStr(0.), mcVelFRad(0.)
 { 
 };
 
@@ -82,20 +84,21 @@ bool ntlGeometryObject::checkIsAnimated() {
 /*****************************************************************************/
 /* Init attributes etc. of this object */
 /*****************************************************************************/
-#define GEOINIT_STRINGS  9
+#define GEOINIT_STRINGS  10
 static const char *initStringStrs[GEOINIT_STRINGS] = {
        "fluid",
        "bnd_no","bnd_noslip",
        "bnd_free","bnd_freeslip",
        "bnd_part","bnd_partslip",
-       "inflow", "outflow"
+       "inflow", "outflow", "control",
 };
 static int initStringTypes[GEOINIT_STRINGS] = {
        FGI_FLUID,
        FGI_BNDNO, FGI_BNDNO,
        FGI_BNDFREE, FGI_BNDFREE,
        FGI_BNDPART, FGI_BNDPART,
-       FGI_MBNDINFLOW, FGI_MBNDOUTFLOW
+       FGI_MBNDINFLOW, FGI_MBNDOUTFLOW, 
+       FGI_CONTROL
 };
 void ntlGeometryObject::initialize(ntlRenderGlobals *glob) 
 {
@@ -330,7 +333,11 @@ void ntlGeometryObject::sceneAddTriangleNoVert(int *trips,
 
 void ntlGeometryObject::initChannels(
                int nTrans, float *trans, int nRot, float *rot, int nScale, float *scale,
-               int nAct, float *act, int nIvel, float *ivel
+               int nAct, float *act, int nIvel, float *ivel,
+               int nAttrFStr, float *attrFStr,
+               int nAttrFRad, float *attrFRad,
+               int nVelFStr, float *velFStr,
+               int nVelFRad, float *velFRad
                ) {
        const bool debugInitc=true;
        if(debugInitc) { debMsgStd("ntlGeometryObject::initChannels",DM_MSG,"nt:"<<nTrans<<" nr:"<<nRot<<" ns:"<<nScale, 10); 
@@ -343,8 +350,15 @@ void ntlGeometryObject::initChannels(
        if((scale)&&(nScale>0)) {  ADD_CHANNEL_VEC(mcScale, nScale, scale); }
        if((act)&&(nAct>0)) {      ADD_CHANNEL_FLOAT(mcGeoActive, nAct, act); }
        if((ivel)&&(nIvel>0)) {    ADD_CHANNEL_VEC(mcInitialVelocity, nIvel, ivel); }
+       
+       /* fluid control channels */
+       if((attrFStr)&&(nAttrFStr>0)) { ADD_CHANNEL_FLOAT(mcAttrFStr, nAttrFStr, attrFStr); }
+       if((attrFRad)&&(nAttrFRad>0)) { ADD_CHANNEL_FLOAT(mcAttrFRad, nAttrFRad, attrFRad); }
+       if((velFStr)&&(nVelFStr>0)) {   ADD_CHANNEL_FLOAT(mcVelFStr, nAct, velFStr); }
+       if((velFRad)&&(nVelFRad>0)) {   ADD_CHANNEL_FLOAT(mcVelFRad, nVelFRad, velFRad); }
 
        checkIsAnimated();
+       
        if(debugInitc) { 
                debMsgStd("ntlGeometryObject::initChannels",DM_MSG,getName()<<
                                " nt:"<<mcTrans.accessValues().size()<<" nr:"<<mcRot.accessValues().size()<<
@@ -565,7 +579,7 @@ void ntlGeometryObject::initMovingPoints(double time, gfxReal featureSize) {
                }
        }
 
-       if(    (this-getMeshAnimated())
+       if(    (this->getMeshAnimated())
       || (mcTrans.accessValues().size()>1)  // VALIDATE
            || (mcRot.accessValues().size()>1) 
            || (mcScale.accessValues().size()>1) 
index a5afd6b220795fa928113d486fb69cb2aed217f4..666798385f6c6f70d11c106b96e00674aed38b5c 100644 (file)
@@ -97,11 +97,37 @@ class ntlGeometryObject : public ntlGeometryClass
                /*! Set/get the local inivel coords flag */
                inline bool getLocalCoordInivel() const { return mLocalCoordInivel; }
                inline void setLocalCoordInivel(bool set) { mLocalCoordInivel=set; }
-
+               
+               /****************************************/
+               /* fluid control features */
+               /****************************************/
+               /*! Set/get the particle control set attract force strength */
+               inline float getCpsTimeStart() const { return mCpsTimeStart; }
+               inline void setCpsTimeStart(float set) { mCpsTimeStart=set; }
+               
+               /*! Set/get the particle control set attract force strength */
+               inline float getCpsTimeEnd() const { return mCpsTimeEnd; }
+               inline void setCpsTimeEnd(float set) { mCpsTimeEnd=set; }
+               
+               /*! Set/get the particle control set quality */
+               inline float getCpsQuality() const { return mCpsQuality; }
+               inline void setCpsQuality(float set) { mCpsQuality=set; }
+               
+               inline AnimChannel<float> getCpsAttrFStr() const { return mcAttrFStr; }
+               inline AnimChannel<float> getCpsAttrFRad() const { return mcAttrFRad; }
+               inline AnimChannel<float> getCpsVelFStr() const { return mcVelFStr; }
+               inline AnimChannel<float> getCpsVelFRad() const { return mcVelFRad; }
+               
+               /****************************************/
+               
                /*! Init channels from float arrays (for elbeem API) */
                void initChannels(
                                int nTrans, float *trans, int nRot, float *rot, int nScale, float *scale,
-                         int nAct, float *act, int nIvel, float *ivel
+                               int nAct, float *act, int nIvel, float *ivel,
+                               int nAttrFStr, float *attrFStr,
+                               int nAttrFRad, float *attrFRad,
+                               int nVelFStr, float *velFStr,
+                               int nVelFRad, float *velFRad
                                );
 
                /*! is the object animated? */
@@ -202,6 +228,12 @@ class ntlGeometryObject : public ntlGeometryClass
 
                /*! animated channels for in/outflow on/off */
                AnimChannel<float> mcGeoActive;
+               
+               /* fluid control settings */
+               float mCpsTimeStart;
+               float mCpsTimeEnd;
+               float mCpsQuality;
+               AnimChannel<float> mcAttrFStr, mcAttrFRad, mcVelFStr, mcVelFRad;
 
        public:
 
index d6593b6b33570c046e1aef3b00eb306a1749f4e2..242b82085bede014a9c0f66651337bf62fde0f28 100644 (file)
@@ -688,7 +688,8 @@ ntlScene::~ntlScene()
        if(mpTree != NULL) delete mpTree;
 
        // cleanup lists, only if this is the rendering cleanup scene
-       if(mSceneDel) {
+       if(mSceneDel) 
+       {
                for (vector<ntlGeometryClass*>::iterator iter = mGeos.begin();
                                iter != mGeos.end(); iter++) {
                        //errMsg("ntlScene::~ntlScene","Deleting obj "<<(*iter)->getName() );
index 096d5fd61d3c24599ff7bc1a89fe6bb76a746f64..9b77fdcae281f7ca68e85c97d09d4b8effcb31ef 100644 (file)
@@ -20,6 +20,7 @@ class ntlRay;
 class ntlTree;
 class ntlScene;
 class ntlRenderGlobals;
+class ntlGeometryObject;
 
 //! store data for an intersection of a ray and a triangle
 // NOT YET USED
@@ -323,8 +324,8 @@ public:
   /* CONSTRUCTORS */
   /*! Default constructor */
   ntlScene( ntlRenderGlobals *glob, bool del=true );
-  /*! Default destructor */
-   ~ntlScene();
+  /*! Default destructor  */
+  ~ntlScene();
 
        /*! Add an object to the scene */
        inline void addGeoClass(ntlGeometryClass *geo) { 
index 21d9d63aac00d0d3e5fe5b61bf83b35d91fd2dbc..c7aa3495c930c4de88e3536d76ff1a561b73d624 100644 (file)
@@ -236,10 +236,15 @@ ntlWorld::~ntlWorld()
 {
        delete mpGlob->getRenderScene();
        delete mpGlob->getSimScene();
-  delete mpGlob;
-  delete mpLightList;
-  delete mpPropList;
-  delete mpSims;
+  
+       delete mpGlob;
+       
+       
+       // these get assigned to mpGlob but not freed there
+       delete mpLightList;
+       delete mpPropList; // materials
+       delete mpSims;
+  
 #ifndef NOGUI
        if(mpOpenGLRenderer) delete mpOpenGLRenderer;
 #endif // NOGUI
@@ -895,6 +900,8 @@ ntlRenderGlobals::ntlRenderGlobals() :
 ntlRenderGlobals::~ntlRenderGlobals() {
        if(mpOpenGlAttr) delete mpOpenGlAttr;
        if(mpBlenderAttr) delete mpBlenderAttr;
+       
+       
 }
 
 
index 9b47ae696afff4a2d784e0a00f23406d59034da1..81e056771fc920df1850543117c7a996b40f644c 100644 (file)
@@ -58,7 +58,8 @@ SimulationObject::~SimulationObject()
        if(mpGiTree)         delete mpGiTree;
        if(mpElbeemSettings) delete mpElbeemSettings;
        if(mpLbm)            delete mpLbm;
-  if(mpParam)          delete mpParam;
+       if(mpParam)          delete mpParam;
+       if(mpParts)          delete mpParts;
        debMsgStd("SimulationObject",DM_MSG,"El'Beem Done!\n",10);
 }
 
index 5616d8052325142e807ad85484b47c95603e104f..ef516a578bd1a5741d1b273b6f5a0f1bd55420de 100644 (file)
@@ -11,9 +11,7 @@
 #include "solver_relax.h"
 #include "particletracer.h"
 
-#if (defined (__sun__) || defined (__sun)) || (!defined(linux) && (defined (__sparc) || defined (__sparc__)))
-#include <ieeefp.h>
-#endif
+
 
 /*****************************************************************************/
 //! coarse step functions
index d46f065adfd6632ed9aa4b58479ce0a510b42c1a..310dd50617dc381b90747fbc58b6bfc51da7871b 100644 (file)
 #endif
 #endif
 
+#if LBM_INCLUDE_CONTROL==1
+#include "solver_control.h"
+#endif 
+
 #if LBM_INCLUDE_TESTSOLVERS==1
 #include "solver_test.h"
 #endif // LBM_INCLUDE_TESTSOLVERS==1
@@ -497,6 +501,14 @@ class LbmFsgrSolver :
                LbmFloat& debRAC(LbmFloat* s,int l);
 #              endif // FSGR_STRICT_DEBUG==1
 
+#              if LBM_INCLUDE_CONTROL==1
+               LbmControlData *mpControl;
+
+               void initCpdata();
+               void handleCpdata();
+               void cpDebugDisplay(int dispset); 
+#              endif // LBM_INCLUDE_CONTROL==1
+
                bool mUseTestdata;
 #              if LBM_INCLUDE_TESTSOLVERS==1
                // test functions
@@ -506,10 +518,6 @@ class LbmFsgrSolver :
                void handleTestdata();
                void set3dHeight(int ,int );
 
-               void initCpdata();
-               void handleCpdata();
-               void cpDebugDisplay(int dispset);
-
                int mMpNum,mMpIndex;
                int mOrgSizeX;
                LbmFloat mOrgStartX;
diff --git a/intern/elbeem/intern/solver_control.cpp b/intern/elbeem/intern/solver_control.cpp
new file mode 100644 (file)
index 0000000..15b9468
--- /dev/null
@@ -0,0 +1,1016 @@
+/******************************************************************************
+ *
+ * El'Beem - the visual lattice boltzmann freesurface simulator
+ * All code distributed as part of El'Beem is covered by the version 2 of the 
+ * GNU General Public License. See the file COPYING for details.  
+ *
+ * Copyright 2003-2008 Nils Thuerey 
+ *
+ * control extensions
+ *
+ *****************************************************************************/
+#include "solver_class.h"
+#include "solver_relax.h"
+#include "particletracer.h"
+
+#include "solver_control.h"
+
+#include "controlparticles.h"
+
+#include "elbeem.h"
+
+#include "ntl_geometrymodel.h"
+
+/******************************************************************************
+ * LbmControlData control set
+ *****************************************************************************/
+
+LbmControlSet::LbmControlSet() :
+       mCparts(NULL), mCpmotion(NULL), mContrPartFile(""), mCpmotionFile(""),
+       mcForceAtt(0.), mcForceVel(0.), mcForceMaxd(0.), 
+       mcRadiusAtt(0.), mcRadiusVel(0.), mcRadiusMind(0.), mcRadiusMaxd(0.), 
+       mcCpScale(1.), mcCpOffset(0.)
+{
+}
+LbmControlSet::~LbmControlSet() {
+       if(mCparts) delete mCparts;
+       if(mCpmotion) delete mCpmotion;
+}
+void LbmControlSet::initCparts() {
+       mCparts = new ControlParticles();
+       mCpmotion = new ControlParticles();
+}
+
+
+
+/******************************************************************************
+ * LbmControlData control
+ *****************************************************************************/
+
+LbmControlData::LbmControlData() : 
+       mSetForceStrength(0.),
+       mCons(), 
+       mCpUpdateInterval(8), // DG: was 16 --> causes problems (big sphere after some time), unstable
+       mCpOutfile(""), 
+       mCpForces(), mCpKernel(), mMdKernel(),
+       mDiffVelCon(1.),
+       mDebugCpscale(0.), 
+       mDebugVelScale(0.), 
+       mDebugCompavScale(0.), 
+       mDebugAttScale(0.), 
+       mDebugMaxdScale(0.),
+       mDebugAvgVelScale(0.)
+{
+}
+
+LbmControlData::~LbmControlData() 
+{
+       while (!mCons.empty()) {
+               delete mCons.back();  mCons.pop_back();
+       }
+}
+
+
+void LbmControlData::parseControldataAttrList(AttributeList *attr) {
+       // controlpart vars
+       mSetForceStrength = attr->readFloat("tforcestrength", mSetForceStrength,"LbmControlData", "mSetForceStrength", false);
+       //errMsg("tforcestrength set to "," "<<mSetForceStrength);
+       mCpUpdateInterval = attr->readInt("controlparticle_updateinterval", mCpUpdateInterval,"LbmControlData","mCpUpdateInterval", false);
+       // tracer output file
+       mCpOutfile = attr->readString("controlparticle_outfile",mCpOutfile,"LbmControlData","mCpOutfile", false);
+       if(getenv("ELBEEM_CPOUTFILE")) {
+               string outfile(getenv("ELBEEM_CPOUTFILE"));
+               mCpOutfile = outfile;
+               debMsgStd("LbmControlData::parseAttrList",DM_NOTIFY,"Using envvar ELBEEM_CPOUTFILE to set mCpOutfile to "<<outfile<<","<<mCpOutfile,7);
+       }
+
+       for(int cpii=0; cpii<10; cpii++) {
+               string suffix("");
+               //if(cpii>0)
+               {  suffix = string("0"); suffix[0]+=cpii; }
+               LbmControlSet *cset;
+               cset = new LbmControlSet();
+               cset->initCparts();
+
+               cset->mContrPartFile = attr->readString("controlparticle"+suffix+"_file",cset->mContrPartFile,"LbmControlData","cset->mContrPartFile", false);
+               if((cpii==0) && (getenv("ELBEEM_CPINFILE")) ) {
+                       string infile(getenv("ELBEEM_CPINFILE"));
+                       cset->mContrPartFile = infile;
+                       debMsgStd("LbmControlData::parseAttrList",DM_NOTIFY,"Using envvar ELBEEM_CPINFILE to set mContrPartFile to "<<infile<<","<<cset->mContrPartFile,7);
+               }
+
+               LbmFloat cpvort=0.;
+               cset->mcRadiusAtt =  attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_radiusatt", 0., "LbmControlData","mcRadiusAtt" );
+               cset->mcRadiusVel =  attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_radiusvel", 0., "LbmControlData","mcRadiusVel" );
+               cset->mcRadiusVel =  attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_radiusvel", 0., "LbmControlData","mcRadiusVel" );
+               cset->mCparts->setRadiusAtt(cset->mcRadiusAtt.get(0.));
+               cset->mCparts->setRadiusVel(cset->mcRadiusVel.get(0.));
+
+               // WARNING currently only for first set
+               //if(cpii==0) {
+               cset->mcForceAtt  =  attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_attraction", 0. , "LbmControlData","cset->mcForceAtt", false);
+               cset->mcForceVel  =  attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_velocity",   0. , "LbmControlData","mcForceVel", false);
+               cset->mcForceMaxd =  attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_maxdist",    0. , "LbmControlData","mcForceMaxd", false);
+               cset->mCparts->setInfluenceAttraction(cset->mcForceAtt.get(0.) );
+               // warning - stores temprorarily, value converted to dt dep. factor
+               cset->mCparts->setInfluenceVelocity(cset->mcForceVel.get(0.) , 0.01 ); // dummy dt
+               cset->mCparts->setInfluenceMaxdist(cset->mcForceMaxd.get(0.) );
+               cpvort =  attr->readFloat("controlparticle"+suffix+"_vorticity",   cpvort, "LbmControlData","cpvort", false);
+               cset->mCparts->setInfluenceTangential(cpvort);
+                       
+               cset->mcRadiusMind =  attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_radiusmin", cset->mcRadiusMind.get(0.), "LbmControlData","mcRadiusMind", false);
+               cset->mcRadiusMaxd =  attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_radiusmax", cset->mcRadiusMind.get(0.), "LbmControlData","mcRadiusMaxd", false);
+               cset->mCparts->setRadiusMinMaxd(cset->mcRadiusMind.get(0.));
+               cset->mCparts->setRadiusMaxd(cset->mcRadiusMaxd.get(0.));
+               //}
+
+               // now local...
+               //LbmVec cpOffset(0.), cpScale(1.);
+               LbmFloat cpTimescale = 1.;
+               string cpMirroring("");
+
+               //cset->mcCpOffset = attr->readChannelVec3f("controlparticle"+suffix+"_offset", ntlVec3f(0.),"LbmControlData","mcCpOffset", false);
+               //cset->mcCpScale =  attr->readChannelVec3f("controlparticle"+suffix+"_scale",  ntlVec3f(1.), "LbmControlData","mcCpScale", false);
+               cset->mcCpOffset = attr->readChannelVec3f("controlparticle"+suffix+"_offset", ntlVec3f(0.),"LbmControlData","mcCpOffset", false);
+               cset->mcCpScale =  attr->readChannelVec3f("controlparticle"+suffix+"_scale",  ntlVec3f(1.), "LbmControlData","mcCpScale", false);
+               cpTimescale =  attr->readFloat("controlparticle"+suffix+"_timescale",  cpTimescale, "LbmControlData","cpTimescale", false);
+               cpMirroring =  attr->readString("controlparticle"+suffix+"_mirror",  cpMirroring, "LbmControlData","cpMirroring", false);
+
+               LbmFloat cpsWidth = cset->mCparts->getCPSWith();
+               cpsWidth =  attr->readFloat("controlparticle"+suffix+"_cpswidth",  cpsWidth, "LbmControlData","cpsWidth", false);
+               LbmFloat cpsDt = cset->mCparts->getCPSTimestep();
+               cpsDt =  attr->readFloat("controlparticle"+suffix+"_cpstimestep",  cpsDt, "LbmControlData","cpsDt", false);
+               LbmFloat cpsTstart = cset->mCparts->getCPSTimeStart();
+               cpsTstart =  attr->readFloat("controlparticle"+suffix+"_cpststart",  cpsTstart, "LbmControlData","cpsTstart", false);
+               LbmFloat cpsTend = cset->mCparts->getCPSTimeEnd();
+               cpsTend =  attr->readFloat("controlparticle"+suffix+"_cpstend",  cpsTend, "LbmControlData","cpsTend", false);
+               LbmFloat cpsMvmfac = cset->mCparts->getCPSMvmWeightFac();
+               cpsMvmfac =  attr->readFloat("controlparticle"+suffix+"_cpsmvmfac",  cpsMvmfac, "LbmControlData","cpsMvmfac", false);
+               cset->mCparts->setCPSWith(cpsWidth);
+               cset->mCparts->setCPSTimestep(cpsDt);
+               cset->mCparts->setCPSTimeStart(cpsTstart);
+               cset->mCparts->setCPSTimeEnd(cpsTend);
+               cset->mCparts->setCPSMvmWeightFac(cpsMvmfac);
+
+               cset->mCparts->setOffset( vec2L(cset->mcCpOffset.get(0.)) );
+               cset->mCparts->setScale( vec2L(cset->mcCpScale.get(0.)) );
+               cset->mCparts->setInitTimeScale( cpTimescale );
+               cset->mCparts->setInitMirror( cpMirroring );
+
+               int mDebugInit = 0;
+               mDebugInit = attr->readInt("controlparticle"+suffix+"_debuginit", mDebugInit,"LbmControlData","mDebugInit", false);
+               cset->mCparts->setDebugInit(mDebugInit);
+
+               // motion particle settings
+               LbmVec mcpOffset(0.), mcpScale(1.);
+               LbmFloat mcpTimescale = 1.;
+               string mcpMirroring("");
+
+               cset->mCpmotionFile = attr->readString("cpmotion"+suffix+"_file",cset->mCpmotionFile,"LbmControlData","mCpmotionFile", false);
+               mcpTimescale =  attr->readFloat("cpmotion"+suffix+"_timescale",  mcpTimescale, "LbmControlData","mcpTimescale", false);
+               mcpMirroring =  attr->readString("cpmotion"+suffix+"_mirror",  mcpMirroring, "LbmControlData","mcpMirroring", false);
+               mcpOffset = vec2L( attr->readVec3d("cpmotion"+suffix+"_offset", vec2P(mcpOffset),"LbmControlData","cpOffset", false) );
+               mcpScale =  vec2L( attr->readVec3d("cpmotion"+suffix+"_scale",  vec2P(mcpScale), "LbmControlData","cpScale", false) );
+
+               cset->mCpmotion->setOffset( vec2L(mcpOffset) );
+               cset->mCpmotion->setScale( vec2L(mcpScale) );
+               cset->mCpmotion->setInitTimeScale( mcpTimescale );
+               cset->mCpmotion->setInitMirror( mcpMirroring );
+
+               if(cset->mContrPartFile.length()>1) {
+                       errMsg("LbmControlData","Using control particle set "<<cpii<<" file:"<<cset->mContrPartFile<<" cpmfile:"<<cset->mCpmotionFile<<" mirr:'"<<cset->mCpmotion->getInitMirror()<<"' " );
+                       mCons.push_back( cset );
+               } else {
+                       delete cset;
+               }
+       }
+
+       // debug, testing - make sure theres at least an empty set
+       if(mCons.size()<1) {
+               mCons.push_back( new LbmControlSet() );
+               mCons[0]->initCparts();
+       }
+
+       // take from first set
+       for(int cpii=1; cpii<(int)mCons.size(); cpii++) {
+               mCons[cpii]->mCparts->setRadiusMinMaxd( mCons[0]->mCparts->getRadiusMinMaxd() );
+               mCons[cpii]->mCparts->setRadiusMaxd(    mCons[0]->mCparts->getRadiusMaxd() );
+               mCons[cpii]->mCparts->setInfluenceAttraction( mCons[0]->mCparts->getInfluenceAttraction() );
+               mCons[cpii]->mCparts->setInfluenceTangential( mCons[0]->mCparts->getInfluenceTangential() );
+               mCons[cpii]->mCparts->setInfluenceVelocity( mCons[0]->mCparts->getInfluenceVelocity() , 0.01 ); // dummy dt
+               mCons[cpii]->mCparts->setInfluenceMaxdist( mCons[0]->mCparts->getInfluenceMaxdist() );
+       }
+       
+       // invert for usage in relax macro
+       mDiffVelCon =  1.-attr->readFloat("cpdiffvelcon",  mDiffVelCon, "LbmControlData","mDiffVelCon", false);
+
+       mDebugCpscale     =  attr->readFloat("cpdebug_cpscale",  mDebugCpscale, "LbmControlData","mDebugCpscale", false);
+       mDebugMaxdScale   =  attr->readFloat("cpdebug_maxdscale",  mDebugMaxdScale, "LbmControlData","mDebugMaxdScale", false);
+       mDebugAttScale    =  attr->readFloat("cpdebug_attscale",  mDebugAttScale, "LbmControlData","mDebugAttScale", false);
+       mDebugVelScale    =  attr->readFloat("cpdebug_velscale",  mDebugVelScale, "LbmControlData","mDebugVelScale", false);
+       mDebugCompavScale =  attr->readFloat("cpdebug_compavscale",  mDebugCompavScale, "LbmControlData","mDebugCompavScale", false);
+       mDebugAvgVelScale =  attr->readFloat("cpdebug_avgvelsc",  mDebugAvgVelScale, "LbmControlData","mDebugAvgVelScale", false);
+}
+
+
+void 
+LbmFsgrSolver::initCpdata()
+{
+       // enable for cps via env. vars
+       //if( (getenv("ELBEEM_CPINFILE")) || (getenv("ELBEEM_CPOUTFILE")) ){ mUseTestdata=1; }
+
+       
+       // manually switch on! if this is zero, nothing is done...
+       mpControl->mSetForceStrength = this->mTForceStrength = 1.;
+       mpControl->mCons.clear();
+       
+       // init all control fluid objects
+       int numobjs = (int)(mpGiObjects->size());
+       for(int o=0; o<numobjs; o++) {
+               ntlGeometryObjModel *obj = (ntlGeometryObjModel *)(*mpGiObjects)[o];
+               if(obj->getGeoInitType() & FGI_CONTROL) {
+                       // add new control set per object
+                       LbmControlSet *cset;
+
+                       cset = new LbmControlSet();
+                       cset->initCparts();
+       
+                       // dont load any file
+                       cset->mContrPartFile = string("");
+
+                       cset->mcForceAtt = obj->getCpsAttrFStr();
+                       cset->mcRadiusAtt = obj->getCpsAttrFRad();
+                       cset->mcForceVel = obj->getCpsVelFStr();
+                       cset->mcRadiusVel = obj->getCpsVelFRad();
+
+                       cset->mCparts->setCPSTimeStart(obj->getCpsTimeStart());
+                       cset->mCparts->setCPSTimeEnd(obj->getCpsTimeEnd());
+                       
+                       if(obj->getCpsQuality() > LBM_EPSILON)
+                               cset->mCparts->setCPSWith(1.0 / obj->getCpsQuality());
+                       
+                       // this value can be left at 0.5:
+                       cset->mCparts->setCPSMvmWeightFac(0.5);
+
+                       mpControl->mCons.push_back( cset );
+                       mpControl->mCons[mpControl->mCons.size()-1]->mCparts->initFromObject(obj);
+               }
+       }
+       
+       // NT blender integration manual test setup
+       if(0) {
+               // manually switch on! if this is zero, nothing is done...
+               mpControl->mSetForceStrength = this->mTForceStrength = 1.;
+               mpControl->mCons.clear();
+
+               // add new set
+               LbmControlSet *cset;
+
+               cset = new LbmControlSet();
+               cset->initCparts();
+               // dont load any file
+               cset->mContrPartFile = string("");
+
+               // set radii for attraction & velocity forces
+               // set strength of the forces
+               // don't set directly! but use channels: 
+               // mcForceAtt, mcForceVel, mcForceMaxd, mcRadiusAtt, mcRadiusVel, mcRadiusMind, mcRadiusMaxd etc.
+
+               // wrong: cset->mCparts->setInfluenceAttraction(1.15); cset->mCparts->setRadiusAtt(1.5);
+               // right, e.g., to init some constant values:
+               cset->mcForceAtt = AnimChannel<float>(0.2);
+               cset->mcRadiusAtt = AnimChannel<float>(0.75);
+               cset->mcForceVel = AnimChannel<float>(0.2);
+               cset->mcRadiusVel = AnimChannel<float>(0.75);
+
+               // this value can be left at 0.5:
+               cset->mCparts->setCPSMvmWeightFac(0.5);
+
+               mpControl->mCons.push_back( cset );
+
+               // instead of reading from file (cset->mContrPartFile), manually init some particles
+               mpControl->mCons[0]->mCparts->initBlenderTest();
+
+               // other values that might be interesting to change:
+               //cset->mCparts->setCPSTimestep(0.02);
+               //cset->mCparts->setCPSTimeStart(0.);
+               //cset->mCparts->setCPSTimeEnd(1.); 
+
+               //mpControl->mDiffVelCon = 1.; // more rigid velocity control, 0 (default) allows more turbulence
+       }
+
+       // control particle -------------------------------------------------------------------------------------
+
+       // init cppf stage, use set 0!
+       if(mCppfStage>0) {
+               if(mpControl->mCpOutfile.length()<1) mpControl->mCpOutfile = string("cpout"); // use getOutFilename !?
+               char strbuf[100];
+               const char *cpFormat = "_d%dcppf%d";
+
+               // initial coarse stage, no input
+               if(mCppfStage==1) {
+                       mpControl->mCons[0]->mContrPartFile = "";
+               } else {
+                       // read from prev stage
+                       snprintf(strbuf,100, cpFormat  ,LBMDIM,mCppfStage-1);
+                       mpControl->mCons[0]->mContrPartFile = mpControl->mCpOutfile;
+                       mpControl->mCons[0]->mContrPartFile += strbuf;
+                       mpControl->mCons[0]->mContrPartFile += ".cpart2";
+               }
+
+               snprintf(strbuf,100, cpFormat  ,LBMDIM,mCppfStage);
+               mpControl->mCpOutfile += strbuf;
+       } // */
+       
+       for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
+               ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
+               ControlParticles *cpmotion = mpControl->mCons[cpssi]->mCpmotion;
+
+               // now set with real dt
+               cparts->setInfluenceVelocity( mpControl->mCons[cpssi]->mcForceVel.get(0.), mLevel[mMaxRefine].timestep);
+               cparts->setCharLength( mLevel[mMaxRefine].nodeSize );
+               cparts->setCharLength( mLevel[mMaxRefine].nodeSize );
+               errMsg("LbmControlData","CppfStage "<<mCppfStage<<" in:"<<mpControl->mCons[cpssi]->mContrPartFile<<
+                               " out:"<<mpControl->mCpOutfile<<" cl:"<< cparts->getCharLength() );
+
+               // control particle test init
+               if(mpControl->mCons[cpssi]->mCpmotionFile.length()>=1) cpmotion->initFromTextFile(mpControl->mCons[cpssi]->mCpmotionFile);
+               // not really necessary...
+               //? cparts->setFluidSpacing( mLevel[mMaxRefine].nodeSize        ); // use grid coords!?
+               //? cparts->calculateKernelWeight();
+               //? debMsgStd("LbmFsgrSolver::initCpdata",DM_MSG,"ControlParticles - motion inited: "<<cparts->getSize() ,10);
+
+               // ensure both are on for env. var settings
+               // when no particles, but outfile enabled, initialize
+               const int lev = mMaxRefine;
+               if((mpParticles) && (mpControl->mCpOutfile.length()>=1) && (cpssi==0)) {
+                       // check if auto num
+                       if( (mpParticles->getNumInitialParticles()<=1) && 
+                                       (mpParticles->getNumParticles()<=1) ) { // initParticles done afterwards anyway
+                               int tracers = 0;
+                               const int workSet = mLevel[lev].setCurr;
+                               FSGR_FORIJK_BOUNDS(lev) { 
+                                       if(RFLAG(lev,i,j,k, workSet)&(CFFluid)) tracers++;
+                               }
+                               if(LBMDIM==3) tracers /= 8;
+                               else          tracers /= 4;
+                               mpParticles->setNumInitialParticles(tracers);
+                               mpParticles->setDumpTextFile(mpControl->mCpOutfile);
+                               debMsgStd("LbmFsgrSolver::initCpdata",DM_MSG,"ControlParticles - set tracers #"<<tracers<<", actual #"<<mpParticles->getNumParticles() ,10);
+                       }
+                       if(mpParticles->getDumpTextInterval()<=0.) {
+                               mpParticles->setDumpTextInterval(mLevel[lev].timestep * mLevel[lev].lSizex);
+                               debMsgStd("LbmFsgrSolver::initCpdata",DM_MSG,"ControlParticles - dump delta t not set, using dti="<< mpParticles->getDumpTextInterval()<<", sim dt="<<mLevel[lev].timestep, 5 );
+                       }
+                       mpParticles->setDumpParts(true); // DEBUG? also dump as particle system
+               }
+
+               if(mpControl->mCons[cpssi]->mContrPartFile.length()>=1) cparts->initFromTextFile(mpControl->mCons[cpssi]->mContrPartFile);
+               cparts->setFluidSpacing( mLevel[lev].nodeSize   ); // use grid coords!?
+               cparts->calculateKernelWeight();
+               debMsgStd("LbmFsgrSolver::initCpdata",DM_MSG,"ControlParticles mCons"<<cpssi<<" - inited, parts:"<<cparts->getTotalSize()<<","<<cparts->getSize()<<" dt:"<<mpParam->getTimestep()<<" control time:"<<cparts->getControlTimStart()<<" to "<<cparts->getControlTimEnd() ,10);
+       } // cpssi
+
+       if(getenv("ELBEEM_CPINFILE")) {
+               this->mTForceStrength = 1.0;
+       }
+       this->mTForceStrength = mpControl->mSetForceStrength;
+       if(mpControl->mCpOutfile.length()>=1) mpParticles->setDumpTextFile(mpControl->mCpOutfile);
+
+       // control particle  init end -------------------------------------------------------------------------------------
+
+       // make sure equiv to solver init
+       if(this->mTForceStrength>0.) { \
+               mpControl->mCpForces.resize( mMaxRefine+1 );
+               for(int lev = 0; lev<=mMaxRefine; lev++) {
+                       LONGINT rcellSize = (mLevel[lev].lSizex*mLevel[lev].lSizey*mLevel[lev].lSizez);
+                       debMsgStd("LbmFsgrSolver::initControl",DM_MSG,"mCpForces init, lev="<<lev<<" rcs:"<<(int)(rcellSize+4)<<","<<(rcellSize*sizeof(ControlForces)/(1024*1024)), 9 );
+                       mpControl->mCpForces[lev].resize( (int)(rcellSize+4) );
+                       //for(int i=0 ;i<rcellSize; i++) mpControl->mCpForces.push_back( ControlForces() );
+                       for(int i=0 ;i<rcellSize; i++) mpControl->mCpForces[lev][i].resetForces();
+               }
+       } // on?
+
+       debMsgStd("LbmFsgrSolver::initCpdata",DM_MSG,"ControlParticles #mCons "<<mpControl->mCons.size()<<" done", 6);
+}
+
+
+#define CPODEBUG 0
+//define CPINTER ((int)(mpControl->mCpUpdateInterval))
+
+#define KERN(x,y,z)    mpControl->mCpKernel[ (((z)*cpkarWidth + (y))*cpkarWidth + (x)) ]
+#define MDKERN(x,y,z)  mpControl->mMdKernel[ (((z)*mdkarWidth + (y))*mdkarWidth + (x)) ]
+
+#define BOUNDCHECK(x,low,high)  ( ((x)<low) ? low : (((x)>high) ? high : (x) )  )
+#define BOUNDSKIP(x,low,high)  ( ((x)<low) || ((x)>high) )
+
+void 
+LbmFsgrSolver::handleCpdata()
+{
+       myTime_t cpstart = getTime();
+       int cpChecks=0;
+       int cpInfs=0;
+       //debMsgStd("ControlData::handleCpdata",DM_MSG,"called... "<<this->mTForceStrength,1);
+
+       // add cp influence
+  if((true) && (this->mTForceStrength>0.)) {
+               // ok continue...
+       } // on off
+       else {
+               return;
+       }
+       
+       if((mpControl->mCpUpdateInterval<1) || (this->mStepCnt%mpControl->mCpUpdateInterval==0)) {
+               // do full reinit later on... 
+       }
+       else if(this->mStepCnt>mpControl->mCpUpdateInterval) {
+               // only reinit new cells
+               // TODO !? remove loop dependance!?
+#define NOFORCEENTRY(lev, i,j,k) (LBMGET_FORCE(lev, i,j,k).maxDistance==CPF_MAXDINIT) 
+               // interpolate missing
+               for(int lev=0; lev<=mMaxRefine; lev++) {
+               FSGR_FORIJK_BOUNDS(lev) { 
+                       if( (RFLAG(lev,i,j,k, mLevel[lev].setCurr)) & (CFFluid|CFInter) )  
+                       //if( (RFLAG(lev,i,j,k, mLevel[lev].setCurr)) & (CFInter) )  
+                       //if(0)
+                       { // only check new inter? RFLAG?check
+                               if(NOFORCEENTRY(lev, i,j,k)) {
+                                       //errMsg("CP","FE_MISSING at "<<PRINT_IJK<<" f"<<LBMGET_FORCE(lev, i,j,k).weightAtt<<" md"<<LBMGET_FORCE(lev, i,j,k).maxDistance );
+
+                                       LbmFloat nbs=0.;
+                                       ControlForces vals;
+                                       vals.resetForces(); vals.maxDistance = 0.;
+                                       for(int l=1; l<this->cDirNum; l++) { 
+                                               int ni=i+this->dfVecX[l], nj=j+this->dfVecY[l], nk=k+this->dfVecZ[l];
+                                               //errMsg("CP","FE_MISSING check "<<PRINT_VEC(ni,nj,nk)<<" f"<<LBMGET_FORCE(lev, ni,nj,nk).weightAtt<<" md"<<LBMGET_FORCE(lev, ni,nj,nk).maxDistance );
+                                               if(!NOFORCEENTRY(lev, ni,nj,nk)) {
+                                                       //? vals.weightAtt   += LBMGET_FORCE(lev, ni,nj,nk).weightAtt;
+                                                       //? vals.forceAtt    += LBMGET_FORCE(lev, ni,nj,nk).forceAtt;
+                                                       vals.maxDistance += LBMGET_FORCE(lev, ni,nj,nk).maxDistance;
+                                                       vals.forceMaxd   += LBMGET_FORCE(lev, ni,nj,nk).forceMaxd;
+                                                       vals.weightVel   += LBMGET_FORCE(lev, ni,nj,nk).weightVel;
+                                                       vals.forceVel    += LBMGET_FORCE(lev, ni,nj,nk).forceVel; 
+                                                       // ignore att/compAv/avgVel here for now
+                                                       nbs += 1.;
+                                               }
+                                       }
+                                       if(nbs>0.) {
+                                               nbs = 1./nbs;
+                                               //? LBMGET_FORCE(lev, i,j,k).weightAtt   =      vals.weightAtt*nbs;
+                                               //? LBMGET_FORCE(lev, i,j,k).forceAtt    = vals.forceAtt*nbs;
+                                               LBMGET_FORCE(lev, i,j,k).maxDistance = vals.maxDistance*nbs;
+                                               LBMGET_FORCE(lev, i,j,k).forceMaxd   =  vals.forceMaxd*nbs;
+                                               LBMGET_FORCE(lev, i,j,k).weightVel   =  vals.weightVel*nbs;
+                                               LBMGET_FORCE(lev, i,j,k).forceVel    = vals.forceVel*nbs;
+                                       }
+                                               /*ControlForces *ff = &LBMGET_FORCE(lev, i,j,k);  // DEBUG
+                                               errMsg("CP","FE_MISSING rec at "<<PRINT_IJK // DEBUG
+                                                               <<" w:"<<ff->weightAtt<<" wa:" <<PRINT_VEC( ff->forceAtt[0],ff->forceAtt[1],ff->forceAtt[2] )  
+                                                               <<" v:"<<ff->weightVel<<" wv:" <<PRINT_VEC( ff->forceVel[0],ff->forceVel[1],ff->forceVel[2] )  
+                                                               <<" v:"<<ff->maxDistance<<" wv:" <<PRINT_VEC( ff->forceMaxd[0],ff->forceMaxd[1],ff->forceMaxd[2] )  ); // DEBUG */
+                                       // else errMsg("CP","FE_MISSING rec at "<<PRINT_IJK<<" failed!"); // DEBUG
+                                       
+                               }
+                       }
+               }} // ijk, lev
+
+               // mStepCnt > mpControl->mCpUpdateInterval
+               return;
+       } else {
+               // nothing to do ...
+               return;
+       }
+
+       // reset
+       for(int lev=0; lev<=mMaxRefine; lev++) {
+               FSGR_FORIJK_BOUNDS(lev) { LBMGET_FORCE(lev,i,j,k).resetForces(); }
+       }
+       // do setup for coarsest level
+       const int coarseLev = 0;
+       const int fineLev = mMaxRefine;
+
+       // init for current time
+       for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
+               ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
+               LbmControlSet *cset = mpControl->mCons[cpssi];
+               
+               cparts->setRadiusAtt(cset->mcRadiusAtt.get(mSimulationTime));
+               cparts->setRadiusVel(cset->mcRadiusVel.get(mSimulationTime));
+               cparts->setInfluenceAttraction(cset->mcForceAtt.get(mSimulationTime) );
+               cparts->setInfluenceMaxdist(cset->mcForceMaxd.get(mSimulationTime) );
+               cparts->setRadiusMinMaxd(cset->mcRadiusMind.get(mSimulationTime));
+               cparts->setRadiusMaxd(cset->mcRadiusMaxd.get(mSimulationTime));
+               cparts->calculateKernelWeight(); // always necessary!?
+               cparts->setOffset( vec2L(cset->mcCpOffset.get(mSimulationTime)) );
+               cparts->setScale(  vec2L(cset->mcCpScale.get(mSimulationTime)) );
+
+               cparts->setInfluenceVelocity( cset->mcForceVel.get(mSimulationTime), mLevel[fineLev].timestep );
+               cparts->setLastOffset( vec2L(cset->mcCpOffset.get(mSimulationTime-mLevel[fineLev].timestep)) );
+               cparts->setLastScale(  vec2L(cset->mcCpScale.get(mSimulationTime-mLevel[fineLev].timestep)) );
+               
+       }
+
+       // check actual values
+       LbmFloat iatt  = ABS(mpControl->mCons[0]->mCparts->getInfluenceAttraction());
+       LbmFloat ivel  = mpControl->mCons[0]->mCparts->getInfluenceVelocity();
+       LbmFloat imaxd = mpControl->mCons[0]->mCparts->getInfluenceMaxdist();
+       //errMsg("FINCIT","iatt="<<iatt<<" ivel="<<ivel<<" imaxd="<<imaxd);
+       for(int cpssi=1; cpssi<(int)mpControl->mCons.size(); cpssi++) {
+               LbmFloat iatt2  = ABS(mpControl->mCons[cpssi]->mCparts->getInfluenceAttraction());
+               LbmFloat ivel2  = mpControl->mCons[cpssi]->mCparts->getInfluenceVelocity();
+               LbmFloat imaxd2 = mpControl->mCons[cpssi]->mCparts->getInfluenceMaxdist();
+               
+               // we allow negative attraction force here!
+               if(iatt2 > iatt)   iatt = iatt2;
+               
+               if(ivel2 >ivel)   ivel = ivel2;
+               if(imaxd2>imaxd)  imaxd= imaxd2;
+               //errMsg("FINCIT"," "<<cpssi<<" iatt2="<<iatt2<<" ivel2="<<ivel2<<" imaxd2="<<imaxd<<" NEW "<<" iatt="<<iatt<<" ivel="<<ivel<<" imaxd="<<imaxd);
+       }
+
+       if(iatt==0. && ivel==0. && imaxd==0.) {
+               debMsgStd("ControlData::initControl",DM_MSG,"Skipped, all zero...",4);
+               return;
+       }
+       //iatt  = mpControl->mCons[1]->mCparts->getInfluenceAttraction(); //ivel  = mpControl->mCons[1]->mCparts->getInfluenceVelocity(); //imaxd = mpControl->mCons[1]->mCparts->getInfluenceMaxdist(); // TTTTTT
+
+       // do control setup
+       for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
+               ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
+               ControlParticles *cpmotion = mpControl->mCons[cpssi]->mCpmotion;
+
+               // TEST!?
+               bool radmod = false;
+               const LbmFloat minRadSize = mLevel[coarseLev].nodeSize * 1.5;
+               if((cparts->getRadiusAtt()>0.) && (cparts->getRadiusAtt()<minRadSize) && (!radmod) ) {
+                       LbmFloat radfac = minRadSize / cparts->getRadiusAtt(); radmod=true;
+                       debMsgStd("ControlData::initControl",DM_MSG,"Modified radii att, fac="<<radfac, 7);
+                       cparts->setRadiusAtt(cparts->getRadiusAtt()*radfac);
+                       cparts->setRadiusVel(cparts->getRadiusVel()*radfac);
+                       cparts->setRadiusMaxd(cparts->getRadiusMaxd()*radfac);
+                       cparts->setRadiusMinMaxd(cparts->getRadiusMinMaxd()*radfac);
+               } else if((cparts->getRadiusVel()>0.) && (cparts->getRadiusVel()<minRadSize) && (!radmod) ) {
+                       LbmFloat radfac = minRadSize / cparts->getRadiusVel();
+                       debMsgStd("ControlData::initControl",DM_MSG,"Modified radii vel, fac="<<radfac, 7);
+                       cparts->setRadiusVel(cparts->getRadiusVel()*radfac);
+                       cparts->setRadiusMaxd(cparts->getRadiusMaxd()*radfac);
+                       cparts->setRadiusMinMaxd(cparts->getRadiusMinMaxd()*radfac);
+               } else if((cparts->getRadiusMaxd()>0.) && (cparts->getRadiusMaxd()<minRadSize) && (!radmod) ) {
+                       LbmFloat radfac = minRadSize / cparts->getRadiusMaxd();
+                       debMsgStd("ControlData::initControl",DM_MSG,"Modified radii maxd, fac="<<radfac, 7);
+                       cparts->setRadiusMaxd(cparts->getRadiusMaxd()*radfac);
+                       cparts->setRadiusMinMaxd(cparts->getRadiusMinMaxd()*radfac);
+               }
+               if(radmod) {
+                       debMsgStd("ControlData::initControl",DM_MSG,"Modified radii: att="<< 
+                               cparts->getRadiusAtt()<<", vel=" << cparts->getRadiusVel()<<", maxd=" <<
+                               cparts->getRadiusMaxd()<<", mind=" << cparts->getRadiusMinMaxd() ,5);
+               }
+
+               cpmotion->prepareControl( mSimulationTime+((LbmFloat)mpControl->mCpUpdateInterval)*(mpParam->getTimestep()), mpParam->getTimestep(), NULL );
+               cparts->prepareControl( mSimulationTime+((LbmFloat)mpControl->mCpUpdateInterval)*(mpParam->getTimestep()), mpParam->getTimestep(), cpmotion );
+       }
+
+       // do control...
+       for(int lev=0; lev<=mMaxRefine; lev++) {
+               LbmFloat levVolume = 1.;
+               LbmFloat levForceScale = 1.;
+               for(int ll=lev; ll<mMaxRefine; ll++) {
+                       if(LBMDIM==3) levVolume *= 8.;
+                       else levVolume *= 4.;
+                       levForceScale *= 2.;
+               }
+               errMsg("LbmFsgrSolver::handleCpdata","levVolume="<<levVolume<<" levForceScale="<<levForceScale );
+       //todo: scale velocity, att by level timestep!?
+               
+       for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
+               ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
+               // ControlParticles *cpmotion = mpControl->mCons[cpssi]->mCpmotion;
+               
+               // if control set is not active skip it
+               if((cparts->getControlTimStart() > mSimulationTime) || (cparts->getControlTimEnd() < mLastSimTime))
+               {
+                       continue;
+               }
+
+               const LbmFloat velLatticeScale = mLevel[lev].timestep/mLevel[lev].nodeSize;
+               LbmFloat gsx = ((mvGeoEnd[0]-mvGeoStart[0])/(LbmFloat)mLevel[lev].lSizex);
+               LbmFloat gsy = ((mvGeoEnd[1]-mvGeoStart[1])/(LbmFloat)mLevel[lev].lSizey);
+               LbmFloat gsz = ((mvGeoEnd[2]-mvGeoStart[2])/(LbmFloat)mLevel[lev].lSizez);
+#if LBMDIM==2
+               gsz = gsx;
+#endif
+               LbmFloat goffx = mvGeoStart[0];
+               LbmFloat goffy = mvGeoStart[1];
+               LbmFloat goffz = mvGeoStart[2];
+
+               //const LbmFloat cpwIncFac = 2.0;
+               // max to two thirds of domain size
+               const int cpw = MIN( mLevel[lev].lSizex/3, MAX( (int)( cparts->getRadiusAtt()  /gsx) +1  , 2) ); // normal kernel, att,vel
+               const int cpkarWidth = 2*cpw+1;
+               mpControl->mCpKernel.resize(cpkarWidth* cpkarWidth* cpkarWidth);
+               ControlParticle cpt; cpt.reset();
+               cpt.pos = LbmVec( (gsx*(LbmFloat)cpw)+goffx, (gsy*(LbmFloat)cpw)+goffy, (gsz*(LbmFloat)cpw)+goffz );  // optimize?
+               cpt.density = 0.5; cpt.densityWeight = 0.5;
+#if LBMDIM==3
+               for(int k= 0; k<cpkarWidth; ++k) {
+#else // LBMDIM==3
+               { int k = cpw;
+#endif 
+                       for(int j= 0; j<cpkarWidth; ++j) 
+                               for(int i= 0; i<cpkarWidth; ++i) {
+                                       KERN(i,j,k).resetForces();
+                                       //LbmFloat dx = i-cpw; LbmFloat dy = j-cpw; LbmFloat dz = k-cpw;
+                                       //LbmVec dv = ( LbmVec(dx,dy,dz) );
+                                       //LbmFloat dl = norm( dv ); //LbmVec dir = dv / dl;
+                                       LbmVec pos = LbmVec( (gsx*(LbmFloat)i)+goffx, (gsy*(LbmFloat)j)+goffy, (gsz*(LbmFloat)k)+goffz );  // optimize?
+                                       cparts->calculateCpInfluenceOpt( &cpt, pos, LbmVec(0,0,0), &KERN(i,j,k)  ,1. );
+                                       /*if((CPODEBUG)&&(k==cpw)) errMsg("kern"," at "<<PRINT_IJK<<" pos"<<pos<<" cpp"<<cpt.pos 
+                                               <<" wf:"<<KERN(i,j,k).weightAtt<<" wa:"<< PRINT_VEC( KERN(i,j,k).forceAtt[0],KERN(i,j,k).forceAtt[1],KERN(i,j,k).forceAtt[2] )  
+                                               <<" wf:"<<KERN(i,j,k).weightVel<<" wa:"<< PRINT_VEC( KERN(i,j,k).forceVel[0],KERN(i,j,k).forceVel[1],KERN(i,j,k).forceVel[2] )  
+                                               <<" wf:"<<KERN(i,j,k).maxDistance<<" wa:"<< PRINT_VEC( KERN(i,j,k).forceMaxd[0],KERN(i,j,k).forceMaxd[1],KERN(i,j,k).forceMaxd[2] )  ); // */
+                                       KERN(i,j,k).weightAtt *= 2.;
+                                       KERN(i,j,k).forceAtt *= 2.;
+                                       //KERN(i,j,k).forceAtt[1] *= 2.; KERN(i,j,k).forceAtt[2] *= 2.;
+                                       KERN(i,j,k).weightVel *= 2.;
+                                       KERN(i,j,k).forceVel *= 2.;
+                                       //KERN(i,j,k).forceVel[1] *= 2.; KERN(i,j,k).forceVel[2] *= 2.;
+                               }
+               }
+
+               if(CPODEBUG) errMsg("cpw"," = "<<cpw<<" f"<< cparts->getRadiusAtt()<<" gsx"<<gsx<<" kpw"<<cpkarWidth); // DEBUG
+               // first cp loop - add att and vel forces
+               for(int cppi=0; cppi<cparts->getSize(); cppi++) {
+                       ControlParticle *cp = cparts->getParticle(cppi);
+                       if(cp->influence<=0.) continue;
+                       const int cpi = (int)( (cp->pos[0]-goffx)/gsx );
+                       const int cpj = (int)( (cp->pos[1]-goffy)/gsy );
+                       int       cpk = (int)( (cp->pos[2]-goffz)/gsz );
+                       /*if( ((LBMDIM==3)&&(BOUNDSKIP(cpk - cpwsm, getForZMinBnd(), getForZMaxBnd(lev) ))) ||
+                               ((LBMDIM==3)&&(BOUNDSKIP(cpk + cpwsm, getForZMinBnd(), getForZMaxBnd(lev) ))) ||
+                               BOUNDSKIP(cpj - cpwsm, 0, mLevel[lev].lSizey ) ||
+                               BOUNDSKIP(cpj + cpwsm, 0, mLevel[lev].lSizey ) ||
+                               BOUNDSKIP(cpi - cpwsm, 0, mLevel[lev].lSizex ) ||
+                               BOUNDSKIP(cpi + cpwsm, 0, mLevel[lev].lSizex ) ) {
+                               continue;
+                               } // */
+                       int is,ie,js,je,ks,ke;
+                       ks = BOUNDCHECK(cpk - cpw, getForZMinBnd(), getForZMaxBnd(lev) );
+                       ke = BOUNDCHECK(cpk + cpw, getForZMinBnd(), getForZMaxBnd(lev) );
+                       js = BOUNDCHECK(cpj - cpw, 0, mLevel[lev].lSizey );
+                       je = BOUNDCHECK(cpj + cpw, 0, mLevel[lev].lSizey );
+                       is = BOUNDCHECK(cpi - cpw, 0, mLevel[lev].lSizex );
+                       ie = BOUNDCHECK(cpi + cpw, 0, mLevel[lev].lSizex );
+                       if(LBMDIM==2) { cpk = 0; ks = 0; ke = 1; }
+                       if(CPODEBUG) errMsg("cppft","i"<<cppi<<" cpw"<<cpw<<" gpos"<<PRINT_VEC(cpi,cpj,cpk)<<"   i:"<<is<<","<<ie<<"   j:"<<js<<","<<je<<"   k:"<<ks<<","<<ke<<" "); // DEBUG
+                       cpInfs++;
+
+                       for(int k= ks; k<ke; ++k) {
+                               for(int j= js; j<je; ++j) {
+
+                                       CellFlagType *pflag = &RFLAG(lev,is,j,k, mLevel[lev].setCurr);
+                                       ControlForces *kk = &KERN( is-cpi+cpw, j-cpj+cpw, k-cpk+cpw);
+                                       ControlForces *ff = &LBMGET_FORCE(lev,is,j,k); 
+                                       pflag--; kk--; ff--;
+
+                                       for(int i= is; i<ie; ++i) {
+                                               // first cp loop (att,vel)
+                                               pflag++; kk++; ff++;
+
+                                               //add weight for bnd cells
+                                               const LbmFloat pwforce = kk->weightAtt;
+                                               // control particle mod,
+                                               // dont add multiple CFFluid fsgr boundaries
+                                               if(lev==mMaxRefine) {
+                                                       //if( ( ((*pflag)&(CFFluid )) && (lev==mMaxRefine) ) ||
+                                                                       //( ((*pflag)&(CFGrNorm)) && (lev <mMaxRefine) ) ) {
+                                                       if((*pflag)&(CFFluid|CFUnused)) {
+                                                               // check not fromcoarse?
+                                                               cp->density += levVolume* kk->weightAtt; // old CFFluid
+                                                       } else if( (*pflag) & (CFEmpty) ) {  
+                                                               cp->density -= levVolume* 0.5; 
+                                                       } else { //if( ((*pflag) & (CFBnd)) ) {  
+                                                               cp->density -= levVolume* 0.2;  // penalty
+                                                       }
+                                               } else {
+                                                       //if((*pflag)&(CFGrNorm)) {
+                                                               //cp->density += levVolume* kk->weightAtt; // old CFFluid
+                                                       //} 
+                                               }
+                                               //else if(!((*pflag) & (CFUnused)) ) {  cp->density -= levVolume* 0.2; } // penalty
+
+                                               if( (*pflag) & (CFFluid|CFInter) )  // RFLAG_check
+                                               {
+
+                                                       cpChecks++;
+                                                       //const LbmFloat pwforce = kk->weightAtt;
+                                                       LbmFloat pwvel = kk->weightVel;
+                                                       if((pwforce==0.)&&(pwvel==0.)) { continue; }
+                                                       ff->weightAtt += 1e-6; // for distance
+
+                                                       if(pwforce>0.) {
+                                                               ff->weightAtt += pwforce *cp->densityWeight *cp->influence;
+                                                               ff->forceAtt += kk->forceAtt *levForceScale *cp->densityWeight *cp->influence;
+
+                                                               // old fill handling here
+                                                               const int workSet =mLevel[lev].setCurr;
+                                                               LbmFloat ux=0., uy=0., uz=0.;
+                                                               FORDF1{  
+                                                                       const LbmFloat dfn = QCELL(lev, i,j,k, workSet, l);
+                                                                       ux  += (this->dfDvecX[l]*dfn);
+                                                                       uy  += (this->dfDvecY[l]*dfn); 
+                                                                       uz  += (this->dfDvecZ[l]*dfn); 
+                                                               }
+                                                               // control particle mod
+                                                               cp->avgVelWeight += levVolume*pwforce;
+                                                               cp->avgVelAcc += LbmVec(ux,uy,uz) * levVolume*pwforce;
+                                                       }
+
+                                                       if(pwvel>0.) {
+                                                               // TODO make switch? vel.influence depends on density weight... 
+                                                               // (reduced lowering with 0.75 factor)
+                                                               pwvel *=  cp->influence *(1.-0.75*cp->densityWeight);
+                                                               // control particle mod
+                                                               // todo use Omega instead!?
+                                                               ff->forceVel += cp->vel*levVolume*pwvel * velLatticeScale; // levVolume?
+                                                               ff->weightVel += levVolume*pwvel; // levVolume?
+                                                               ff->compAv += cp->avgVel*levVolume*pwvel; // levVolume?
+                                                               ff->compAvWeight += levVolume*pwvel; // levVolume?
+                                                       }
+
+                                                       if(CPODEBUG) errMsg("cppft","i"<<cppi<<" at "<<PRINT_IJK<<" kern:"<<
+                                                                       PRINT_VEC(i-cpi+cpw, j-cpj+cpw, k-cpk+cpw )
+                                                                       //<<" w:"<<ff->weightAtt<<" wa:"
+                                                                       //<<PRINT_VEC( ff->forceAtt[0],ff->forceAtt[1],ff->forceAtt[2] )  
+                                                                       //<<" v:"<<ff->weightVel<<" wv:"
+                                                                       //<<PRINT_VEC( ff->forceVel[0],ff->forceVel[1],ff->forceVel[2] )  
+                                                                       //<<" v:"<<ff->maxDistance<<" wv:"
+                                                                       //<<PRINT_VEC( ff->forceMaxd[0],ff->forceMaxd[1],ff->forceMaxd[2] )  
+                                                                       );
+                                               } // celltype
+
+                                       } // ijk
+                               } // ijk
+                       } // ijk
+               } // cpi, end first cp loop (att,vel)
+               debMsgStd("LbmFsgrSolver::handleCpdata",DM_MSG,"Force cpgrid "<<cpssi<<" generated checks:"<<cpChecks<<" infs:"<<cpInfs ,9);
+       } //cpssi
+       } // lev
+
+       // second loop
+       for(int lev=0; lev<=mMaxRefine; lev++) {
+               LbmFloat levVolume = 1.;
+               LbmFloat levForceScale = 1.;
+               for(int ll=lev; ll<mMaxRefine; ll++) {
+                       if(LBMDIM==3) levVolume *= 8.;
+                       else levVolume *= 4.;
+                       levForceScale *= 2.;
+               }
+       // prepare maxd forces
+       for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
+               ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
+
+               // WARNING copied from above!
+               const LbmFloat velLatticeScale = mLevel[lev].timestep/mLevel[lev].nodeSize;
+               LbmFloat gsx = ((mvGeoEnd[0]-mvGeoStart[0])/(LbmFloat)mLevel[lev].lSizex);
+               LbmFloat gsy = ((mvGeoEnd[1]-mvGeoStart[1])/(LbmFloat)mLevel[lev].lSizey);
+               LbmFloat gsz = ((mvGeoEnd[2]-mvGeoStart[2])/(LbmFloat)mLevel[lev].lSizez);
+#if LBMDIM==2
+               gsz = gsx;
+#endif
+               LbmFloat goffx = mvGeoStart[0];
+               LbmFloat goffy = mvGeoStart[1];
+               LbmFloat goffz = mvGeoStart[2];
+
+               //const LbmFloat cpwIncFac = 2.0;
+               const int mdw = MIN( mLevel[lev].lSizex/2, MAX( (int)( cparts->getRadiusMaxd() /gsx) +1  , 2) ); // wide kernel, md
+               const int mdkarWidth = 2*mdw+1;
+               mpControl->mMdKernel.resize(mdkarWidth* mdkarWidth* mdkarWidth);
+               ControlParticle cpt; cpt.reset();
+               cpt.density = 0.5; cpt.densityWeight = 0.5;
+               cpt.pos = LbmVec( (gsx*(LbmFloat)mdw)+goffx, (gsy*(LbmFloat)mdw)+goffy, (gsz*(LbmFloat)mdw)+goffz );  // optimize?
+#if LBMDIM==3
+               for(int k= 0; k<mdkarWidth; ++k) {
+#else // LBMDIM==3
+               { int k = mdw;
+#endif 
+                       for(int j= 0; j<mdkarWidth; ++j) 
+                               for(int i= 0; i<mdkarWidth; ++i) {
+                                       MDKERN(i,j,k).resetForces();
+                                       LbmVec pos = LbmVec( (gsx*(LbmFloat)i)+goffx, (gsy*(LbmFloat)j)+goffy, (gsz*(LbmFloat)k)+goffz );  // optimize?
+                                       cparts->calculateMaxdForce( &cpt, pos, &MDKERN(i,j,k)  );
+                               }
+               }
+
+               // second cpi loop, maxd forces
+               if(cparts->getInfluenceMaxdist()>0.) {
+                       for(int cppi=0; cppi<cparts->getSize(); cppi++) {
+                               ControlParticle *cp = cparts->getParticle(cppi);
+                               if(cp->influence<=0.) continue;
+                               const int cpi = (int)( (cp->pos[0]-goffx)/gsx );
+                               const int cpj = (int)( (cp->pos[1]-goffy)/gsy );
+                               int       cpk = (int)( (cp->pos[2]-goffz)/gsz );
+
+                               int is,ie,js,je,ks,ke;
+                               ks = BOUNDCHECK(cpk - mdw, getForZMinBnd(), getForZMaxBnd(lev) );
+                               ke = BOUNDCHECK(cpk + mdw, getForZMinBnd(), getForZMaxBnd(lev) );
+                               js = BOUNDCHECK(cpj - mdw, 0, mLevel[lev].lSizey );
+                               je = BOUNDCHECK(cpj + mdw, 0, mLevel[lev].lSizey );
+                               is = BOUNDCHECK(cpi - mdw, 0, mLevel[lev].lSizex );
+                               ie = BOUNDCHECK(cpi + mdw, 0, mLevel[lev].lSizex );
+                               if(LBMDIM==2) { cpk = 0; ks = 0; ke = 1; }
+                               if(CPODEBUG) errMsg("cppft","i"<<cppi<<" mdw"<<mdw<<" gpos"<<PRINT_VEC(cpi,cpj,cpk)<<"   i:"<<is<<","<<ie<<"   j:"<<js<<","<<je<<"   k:"<<ks<<","<<ke<<" "); // DEBUG
+                               cpInfs++;
+
+                               for(int k= ks; k<ke; ++k)
+                                for(int j= js; j<je; ++j) {
+                                       CellFlagType *pflag = &RFLAG(lev,is-1,j,k, mLevel[lev].setCurr);
+                                       for(int i= is; i<ie; ++i) {
+                                               // second cpi loop, maxd forces
+                                               pflag++;
+                                               if( (*pflag) & (CFFluid|CFInter) )  // RFLAG_check
+                                               {
+                                                       cpChecks++;
+                                                       ControlForces *ff = &LBMGET_FORCE(lev,i,j,k); 
+                                                       if(ff->weightAtt == 0.) {
+                                                               ControlForces *kk = &MDKERN( i-cpi+mdw, j-cpj+mdw, k-cpk+mdw);
+                                                               const LbmFloat pmdf = kk->maxDistance;
+                                                               if((ff->maxDistance > pmdf) || (ff->maxDistance<0.))
+                                                                       ff->maxDistance = pmdf;
+                                                               ff->forceMaxd = kk->forceMaxd;
+                                                               // todo use Omega instead!?
+                                                               ff->forceVel = cp->vel* velLatticeScale;
+                                                       }
+                                               } // celltype
+                               } } // ijk
+                       } // cpi, md loop 
+               } // maxd inf>0 */
+
+
+               debMsgStd("ControlData::initControl",DM_MSG,"Maxd cpgrid "<<cpssi<<" generated checks:"<<cpChecks<<" infs:"<<cpInfs ,9);
+       } //cpssi
+
+       // normalize, only done once for the whole array
+       mpControl->mCons[0]->mCparts->finishControl( mpControl->mCpForces[lev], iatt,ivel,imaxd );
+
+       } // lev loop
+
+       myTime_t cpend = getTime(); 
+       debMsgStd("ControlData::handleCpdata",DM_MSG,"Time for cpgrid generation:"<< getTimeString(cpend-cpstart)<<", checks:"<<cpChecks<<" infs:"<<cpInfs<<" " ,8);
+
+       // warning, may return  before
+}
+
+#if LBM_USE_GUI==1
+
+#define USE_GLUTILITIES
+#include "../gui/gui_utilities.h"
+
+void LbmFsgrSolver::cpDebugDisplay(int dispset)
+{
+       for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
+               ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
+               //ControlParticles *cpmotion = mpControl->mCons[cpssi]->mCpmotion;
+               // display cp parts
+               const bool cpCubes = false;
+               const bool cpDots = true;
+               const bool cpCpdist = true;
+               const bool cpHideIna = true;
+               glShadeModel(GL_FLAT);
+               glDisable( GL_LIGHTING ); // dont light lines
+
+               // dot influence
+               if((mpControl->mDebugCpscale>0.) && cpDots) {
+                       glPointSize(mpControl->mDebugCpscale * 8.);
+                       glBegin(GL_POINTS);
+                       for(int i=0; i<cparts->getSize(); i++) {
+                               if((cpHideIna)&&( (cparts->getParticle(i)->influence<=0.) || (cparts->getParticle(i)->size<=0.) )) continue;
+                               ntlVec3Gfx org( vec2G(cparts->getParticle(i)->pos ) );
+                               //LbmFloat halfsize = 0.5; 
+                               LbmFloat scale = cparts->getParticle(i)->densityWeight;
+                               //glColor4f( scale,scale,scale,scale );
+                               glColor4f( 0.,scale,0.,scale );
+                               glVertex3f( org[0],org[1],org[2] );
+                               //errMsg("lbmDebugDisplay","CP "<<i<<" at "<<org); // DEBUG
+                       }
+                       glEnd();
+               }
+
+               // cp positions
+               if((mpControl->mDebugCpscale>0.) && cpDots) {
+                       glPointSize(mpControl->mDebugCpscale * 3.);
+                       glBegin(GL_POINTS); 
+                       glColor3f( 0,1,0 );
+               }
+               for(int i=0; i<cparts->getSize(); i++) {
+                       if((cpHideIna)&&( (cparts->getParticle(i)->influence<=0.) || (cparts->getParticle(i)->size<=0.) )) continue;
+                       ntlVec3Gfx  org( vec2G(cparts->getParticle(i)->pos ) );
+                       LbmFloat halfsize = 0.5; 
+                       LbmFloat scale = cparts->getRadiusAtt() * cparts->getParticle(i)->densityWeight;
+                       if(cpCubes){    glLineWidth( 1 ); 
+                               glColor3f( 1,1,1 );
+                               ntlVec3Gfx s = org-(halfsize * (scale)); 
+                               ntlVec3Gfx e = org+(halfsize * (scale)); 
+                               drawCubeWire( s,e ); }
+                       if((mpControl->mDebugCpscale>0.) && cpDots) {
+                               glVertex3f( org[0],org[1],org[2] );
+                       }
+               }
+               if(cpDots) glEnd();
+
+               if(mpControl->mDebugAvgVelScale>0.) {
+                       const float scale = mpControl->mDebugAvgVelScale;
+
+                       glColor3f( 1.0,1.0,1 );
+                       glBegin(GL_LINES); 
+                       for(int i=0; i<cparts->getSize(); i++) {
+                               if((cpHideIna)&&( (cparts->getParticle(i)->influence<=0.) || (cparts->getParticle(i)->size<=0.) )) continue;
+                               ntlVec3Gfx  org( vec2G(cparts->getParticle(i)->pos ) );
+
+                               //errMsg("CPAVGVEL","i"<<i<<" pos"<<org<<" av"<<cparts->getParticle(i)->avgVel);// DEBUG
+                               float dx = cparts->getParticle(i)->avgVel[0];
+                               float dy = cparts->getParticle(i)->avgVel[1];
+                               float dz = cparts->getParticle(i)->avgVel[2];
+                               dx *= scale; dy *= scale; dz *= scale;
+                               glVertex3f( org[0],org[1],org[2] );
+                               glVertex3f( org[0]+dx,org[1]+dy,org[2]+dz );
+                       }
+                       glEnd();
+               } // */
+
+               if( (LBMDIM==2) && (cpCpdist) ) {
+                       
+                       // debug, for use of e.g. LBMGET_FORCE LbmControlData *mpControl = this;
+#                      define TESTGET_FORCE(lev,i,j,k)   mpControl->mCpForces[lev][ ((k*mLevel[lev].lSizey)+j)*mLevel[lev].lSizex+i ]
+       
+                       glBegin(GL_LINES);
+                       //const int lev=0; 
+                       for(int lev=0; lev<=mMaxRefine; lev++) {
+                       FSGR_FORIJK_BOUNDS(lev) {
+                                       LbmVec pos = LbmVec( 
+                                               ((mvGeoEnd[0]-mvGeoStart[0])/(LbmFloat)mLevel[lev].lSizex) * ((LbmFloat)i+0.5) + mvGeoStart[0], 
+                                               ((mvGeoEnd[1]-mvGeoStart[1])/(LbmFloat)mLevel[lev].lSizey) * ((LbmFloat)j+0.5) + mvGeoStart[1], 
+                                               ((mvGeoEnd[2]-mvGeoStart[2])/(LbmFloat)mLevel[lev].lSizez) * ((LbmFloat)k+0.5) + mvGeoStart[2]  );
+                                       if(LBMDIM==2) pos[2] = ((mvGeoEnd[2]-mvGeoStart[2])*0.5 + mvGeoStart[2]);
+
+                                       if((mpControl->mDebugMaxdScale>0.) && (TESTGET_FORCE(lev,i,j,k).weightAtt<=0.) )
+                                       if(TESTGET_FORCE(lev,i,j,k).maxDistance>=0.) 
+                                       if(TESTGET_FORCE(lev,i,j,k).maxDistance<CPF_MAXDINIT ) {
+                                               const float scale = mpControl->mDebugMaxdScale*10001.;
+                                               float dx = TESTGET_FORCE(lev,i,j,k).forceMaxd[0];
+                                               float dy = TESTGET_FORCE(lev,i,j,k).forceMaxd[1];
+                                               float dz = TESTGET_FORCE(lev,i,j,k).forceMaxd[2];
+                                               dx *= scale; dy *= scale; dz *= scale;
+                                               glColor3f( 0,1,0 );
+                                               glVertex3f( pos[0],pos[1],pos[2] );
+                                               glVertex3f( pos[0]+dx,pos[1]+dy,pos[2]+dz );
+                                       } // */
+                                       if((mpControl->mDebugAttScale>0.) && (TESTGET_FORCE(lev,i,j,k).weightAtt>0.)) {
+                                               const float scale = mpControl->mDebugAttScale*100011.;
+                                               float dx = TESTGET_FORCE(lev,i,j,k).forceAtt[0];
+                                               float dy = TESTGET_FORCE(lev,i,j,k).forceAtt[1];
+                                               float dz = TESTGET_FORCE(lev,i,j,k).forceAtt[2];
+                                               dx *= scale; dy *= scale; dz *= scale;
+                                               glColor3f( 1,0,0 );
+                                               glVertex3f( pos[0],pos[1],pos[2] );
+                                               glVertex3f( pos[0]+dx,pos[1]+dy,pos[2]+dz );
+                                       } // */
+                                       // why check maxDistance?
+                                       if((mpControl->mDebugVelScale>0.) && (TESTGET_FORCE(lev,i,j,k).maxDistance+TESTGET_FORCE(lev,i,j,k).weightVel>0.)) {
+                                               float scale = mpControl->mDebugVelScale*1.;
+                                               float wvscale = TESTGET_FORCE(lev,i,j,k).weightVel;
+                                               float dx = TESTGET_FORCE(lev,i,j,k).forceVel[0];
+                                               float dy = TESTGET_FORCE(lev,i,j,k).forceVel[1];
+                                               float dz = TESTGET_FORCE(lev,i,j,k).forceVel[2];
+                                               scale *= wvscale;
+                                               dx *= scale; dy *= scale; dz *= scale;
+                                               glColor3f( 0.2,0.2,1 );
+                                               glVertex3f( pos[0],pos[1],pos[2] );
+                                               glVertex3f( pos[0]+dx,pos[1]+dy,pos[2]+dz );
+                                       } // */
+                                       if((mpControl->mDebugCompavScale>0.) && (TESTGET_FORCE(lev,i,j,k).compAvWeight>0.)) {
+                                               const float scale = mpControl->mDebugCompavScale*1.;
+                                               float dx = TESTGET_FORCE(lev,i,j,k).compAv[0];
+                                               float dy = TESTGET_FORCE(lev,i,j,k).compAv[1];
+                                               float dz = TESTGET_FORCE(lev,i,j,k).compAv[2];
+                                               dx *= scale; dy *= scale; dz *= scale;
+                                               glColor3f( 0.2,0.2,1 );
+                                               glVertex3f( pos[0],pos[1],pos[2] );
+                                               glVertex3f( pos[0]+dx,pos[1]+dy,pos[2]+dz );
+                                       } // */
+                       } // att,maxd
+                       }
+                       glEnd();
+               }
+       } // cpssi
+
+       //fprintf(stderr,"BLA\n");
+       glEnable( GL_LIGHTING ); // dont light lines
+       glShadeModel(GL_SMOOTH);
+}
+
+#else // LBM_USE_GUI==1
+void LbmFsgrSolver::cpDebugDisplay(int dispset) { }
+#endif // LBM_USE_GUI==1
+
+
diff --git a/intern/elbeem/intern/solver_control.h b/intern/elbeem/intern/solver_control.h
new file mode 100644 (file)
index 0000000..0f45d21
--- /dev/null
@@ -0,0 +1,189 @@
+/******************************************************************************
+ *
+ * El'Beem - the visual lattice boltzmann freesurface simulator
+ * All code distributed as part of El'Beem is covered by the version 2 of the 
+ * GNU General Public License. See the file COPYING for details.
+ * Copyright 2003-2006 Nils Thuerey
+ *
+ * testing extensions
+ *
+ *****************************************************************************/
+
+
+#ifndef LBM_TESTCLASS_H
+#define LBM_TESTCLASS_H
+
+//class IsoSurface;
+class ParticleObject;
+class ControlParticles;
+class ControlForces;
+
+//#define NUMGRIDS 2
+//#define MAXNUMSWS 10
+
+// farfield modes
+#define FARF_3DONLY -1
+#define FARF_BOTH    0
+#define FARF_SWEONLY 1
+// dont reuse 3d vars/init
+#define FARF_SEPSWE  2
+
+// relaxation macros for solver_relax.h
+#if LBM_INCLUDE_CONTROL!=1
+
+// defined in relax.h
+
+#else // LBM_INCLUDE_TESTSOLVERS!=1
+
+// WARNING has to match controlparts.h
+#define CPF_ENTRIES     12
+#define CPF_FORCE       0
+#define CPF_VELWEIGHT   3
+#define CPF_VELOCITY    4
+#define CPF_FORCEWEIGHT 7
+#define CPF_MINCPDIST   8
+#define CPF_MINCPDIR    9
+
+#include "controlparticles.h"
+
+#include "ntl_geometrymodel.h"
+                        
+// get force entry, set=0 is unused anyway
+#define LBMGET_FORCE(lev, i,j,k)  mpControl->mCpForces[lev][ (LBMGI(lev,i,j,k,0)) ]
+
+// debug mods off...
+// same as in src/solver_relax.h!
+#define __PRECOLLIDE_MODS(rho,ux,uy,uz, grav) \
+       ux += (grav)[0]; \
+       uy += (grav)[1]; \
+       uz += (grav)[2]; 
+
+//void testMaxdmod(int i, int j,int k, LbmFloat &ux,LbmFloat &uy,LbmFloat &uz,ControlForces &ff);
+#if LBMDIM==3
+#define MAXDGRAV \
+                       if(myforce->forceMaxd[0]*ux+myforce->forceMaxd[1]*uy<LBM_EPSILON) { \
+                               ux = v2w*myforce->forceVel[0]+ v2wi*ux;  \
+                               uy = v2w*myforce->forceVel[1]+ v2wi*uy; }  \
+                       /* movement inverse to g? */ \
+                       if((uz>LBM_EPSILON)&&(uz>myforce->forceVel[2])) { \
+                               uz = v2w*myforce->forceVel[2]+ v2wi*uz; } 
+#else // LBMDIM==3
+#define MAXDGRAV \
+                       if(myforce->forceMaxd[0]*ux<LBM_EPSILON) { \
+                               ux = v2w*myforce->forceVel[0]+ v2wi*ux; } \
+                       /* movement inverse to g? */ \
+                       if((uy>LBM_EPSILON)&&(uy>myforce->forceVel[1])) { \
+                               uy = v2w*myforce->forceVel[1]+ v2wi*uy; } 
+#endif // LBMDIM==3
+
+// debug modifications of collide vars (testing)
+// requires: lev,i,j,k
+#define PRECOLLIDE_MODS(rho,ux,uy,uz, grav) \
+       LbmFloat attforce = 1.; \
+       if(this->mTForceStrength>0.) { \
+               ControlForces* myforce = &LBMGET_FORCE(lev,i,j,k); \
+               const LbmFloat vf = myforce->weightAtt;\
+               const LbmFloat vw = myforce->weightVel;\
+               if(vf!=0.) { attforce = MAX(0., 1.-vf);  /* TODO FIXME? use ABS(vf) for repulsion force? */ \
+                       ux += myforce->forceAtt[0]; \
+                       uy += myforce->forceAtt[1]; \
+                       uz += myforce->forceAtt[2]; \
+                       \
+               } else if(( myforce->maxDistance>0.) && ( myforce->maxDistance<CPF_MAXDINIT)) {\
+                       const LbmFloat v2w = mpControl->mCons[0]->mCparts->getInfluenceMaxdist() * \
+                               (myforce->maxDistance-mpControl->mCons[0]->mCparts->getRadiusMinMaxd()) / (mpControl->mCons[0]->mCparts->getRadiusMaxd()-mpControl->mCons[0]->mCparts->getRadiusMinMaxd()); \
+                       const LbmFloat v2wi = 1.-v2w; \
+                       if(v2w>0.){ MAXDGRAV; \
+                               /* errMsg("ERRMDTT","at "<<PRINT_IJK<<" maxd="<<myforce->maxDistance<<", newu"<<PRINT_VEC(ux,uy,uz)<<", org"<<PRINT_VEC(oux,ouy,ouz)<<", fv"<<myforce->forceVel<<" " );  */ \
+                       }\
+               } \
+               if(vw>0.) { \
+                       const LbmFloat vwi = 1.-vw;\
+                       const LbmFloat vwd = mpControl->mDiffVelCon;\
+                       ux += vw*(myforce->forceVel[0]-myforce->compAv[0] + vwd*(myforce->compAv[0]-ux) ); \
+                       uy += vw*(myforce->forceVel[1]-myforce->compAv[1] + vwd*(myforce->compAv[1]-uy) ); \
+                       uz += vw*(myforce->forceVel[2]-myforce->compAv[2] + vwd*(myforce->compAv[2]-uz) ); \
+                       /*  TODO test!? modify smooth vel by influence of force for each lbm step, to account for force update only each N steps */ \
+                       myforce->compAv = (myforce->forceVel*vw+ myforce->compAv*vwi); \
+               } \
+       } \
+       ux += (grav)[0]*attforce; \
+       uy += (grav)[1]*attforce; \
+       uz += (grav)[2]*attforce; \
+       /* end PRECOLLIDE_MODS */
+
+#define TEST_IF_CHECK \
+               if((!iffilled)&&(LBMGET_FORCE(lev,i,j,k).weightAtt!=0.)) { \
+                       errMsg("TESTIFFILL"," at "<<PRINT_IJK<<" "<<mass<<" "<<rho); \
+                       iffilled = true; \
+                       if(mass<rho*1.0) mass = rho*1.0; myfrac = 1.0; \
+               }
+
+#endif // LBM_INCLUDE_TESTSOLVERS!=1
+
+
+// a single set of control particles and params
+class LbmControlSet {
+       public:
+               LbmControlSet();
+               ~LbmControlSet();
+               void initCparts();
+
+               // control particles
+               ControlParticles *mCparts; 
+               // control particle overall motion (for easier manual generation)
+               ControlParticles *mCpmotion;
+               // cp data file
+               string mContrPartFile;
+               string mCpmotionFile;
+               // cp debug displau
+               LbmFloat mDebugCpscale, mDebugVelScale, mDebugCompavScale, mDebugAttScale, mDebugMaxdScale, mDebugAvgVelScale;
+
+               // params
+               AnimChannel<float> mcForceAtt;
+               AnimChannel<float> mcForceVel;
+               AnimChannel<float> mcForceMaxd;
+
+               AnimChannel<float> mcRadiusAtt;
+               AnimChannel<float> mcRadiusVel;
+               AnimChannel<float> mcRadiusMind;
+               AnimChannel<float> mcRadiusMaxd;
+
+               AnimChannel<ntlVec3f> mcCpScale;
+               AnimChannel<ntlVec3f> mcCpOffset;
+};
+               
+
+
+// main control data storage
+class LbmControlData 
+{
+       public:
+               LbmControlData();
+               virtual ~LbmControlData();
+
+               // control data
+
+               // contorl params
+               void parseControldataAttrList(AttributeList *attr);
+
+               // control strength, set for solver interface
+               LbmFloat mSetForceStrength;
+               // cp vars
+               std::vector<LbmControlSet*> mCons;
+               // update interval
+               int mCpUpdateInterval;
+               // output
+               string mCpOutfile;
+               // control particle precomputed influence
+               std::vector< std::vector<ControlForces> > mCpForces;
+               std::vector<ControlForces> mCpKernel;
+               std::vector<ControlForces> mMdKernel;
+               // activate differential velcon
+               LbmFloat mDiffVelCon;
+
+               // cp debug displau
+               LbmFloat mDebugCpscale, mDebugVelScale, mDebugCompavScale, mDebugAttScale, mDebugMaxdScale, mDebugAvgVelScale;
+};
+
+#endif // LBM_TESTCLASS_H
index a873f3c69874587ad84d61382ee1d2dee4f3b26a..b1222d48ce54508be9ba42d57ebca51ae0519dd4 100644 (file)
@@ -328,7 +328,10 @@ LbmFsgrSolver::LbmFsgrSolver() :
        mInit2dYZ(false),
        mForceTadapRefine(-1), mCutoff(-1)
 {
-  // not much to do here... 
+#if LBM_INCLUDE_CONTROL==1
+       mpControl = new LbmControlData();
+#endif
+
 #if LBM_INCLUDE_TESTSOLVERS==1
        mpTest = new LbmTestdata();
        mMpNum = mMpIndex = 0;
@@ -438,6 +441,10 @@ LbmFsgrSolver::~LbmFsgrSolver()
        delete mpIso;
        if(mpPreviewSurface) delete mpPreviewSurface;
        // cleanup done during scene deletion...
+       
+#if LBM_INCLUDE_CONTROL==1
+       if(mpControl) delete mpControl;
+#endif
 
        // always output performance estimate
        debMsgStd("LbmFsgrSolver::~LbmFsgrSolver",DM_MSG," Avg. MLSUPS:"<<(mAvgMLSUPS/mAvgMLSUPSCnt), 5);
@@ -488,6 +495,10 @@ void LbmFsgrSolver::parseAttrList()
        mSimulationTime += starttimeskip;
        if(starttimeskip>0.) debMsgStd("LbmFsgrSolver::parseStdAttrList",DM_NOTIFY,"Used starttimeskip="<<starttimeskip<<", t="<<mSimulationTime, 1);
 
+#if LBM_INCLUDE_CONTROL==1
+       mpControl->parseControldataAttrList(mpSifAttrs);
+#endif
+
 #if LBM_INCLUDE_TESTSOLVERS==1
        mUseTestdata = 0;
        mUseTestdata = mpSifAttrs->readBool("use_testdata", mUseTestdata,"LbmFsgrSolver", "mUseTestdata", false);
@@ -689,21 +700,21 @@ bool LbmFsgrSolver::initializeSolverMemory()
 
                // restrict max. chunk of 1 mem block to 1GB for windos
                bool memBlockAllocProblem = false;
-               double maxWinMemChunk = 1100.*1024.*1024.;
-               double maxMacMemChunk = 1200.*1024.*1024.;
                double maxDefaultMemChunk = 2.*1024.*1024.*1024.;
                //std::cerr<<" memEstFine "<< memEstFine <<" maxWin:" <<maxWinMemChunk <<" maxMac:" <<maxMacMemChunk ; // DEBUG
 #ifdef WIN32
+               double maxWinMemChunk = 1100.*1024.*1024.;
                if(sizeof(void *)==4 && memEstFine>maxWinMemChunk) {
                        memBlockAllocProblem = true;
                }
 #endif // WIN32
 #ifdef __APPLE__
+               double maxMacMemChunk = 1200.*1024.*1024.;
                if(memEstFine> maxMacMemChunk) {
                        memBlockAllocProblem = true;
                }
 #endif // Mac
-               if(sizeof(void *)==4 && memEstFine>maxDefaultMemChunk) {
+               if(sizeof(void*)==4 && memEstFine>maxDefaultMemChunk) {
                        // max memory chunk for 32bit systems 2gig
                        memBlockAllocProblem = true;
                }
@@ -1264,8 +1275,11 @@ bool LbmFsgrSolver::initializeSolverPostinit() {
   debMsgStd("LbmFsgrSolver::initialize",DM_MSG,"Init done ... ",10);
        mInitDone = 1;
 
-#if LBM_INCLUDE_TESTSOLVERS==1
+#if LBM_INCLUDE_CONTROL==1
        initCpdata();
+#endif // LBM_INCLUDE_CONTROL==1
+
+#if LBM_INCLUDE_TESTSOLVERS==1
        initTestdata();
 #endif // ELBEEM_PLUGIN!=1
        // not inited? dont use...
index 13ebf91b6961b4483d7fd5dcf66d5e0b609cd532..616ac0e2a237f20af67afd89bcf5096ed90db24e 100644 (file)
 #include "loop_tools.h"
 #include <stdlib.h>
 
-#if (defined (__sun__) || defined (__sun)) || (!defined(linux) && (defined (__sparc) || defined (__sparc__)))
-#include <ieeefp.h>
-#endif
-
-
 /*****************************************************************************/
 /*! perform a single LBM step */
 /*****************************************************************************/
@@ -58,7 +53,7 @@ void LbmFsgrSolver::stepMain() {
 
        // init moving bc's, can change mMaxVlen
        initMovingObstacles(false);
-#if LBM_INCLUDE_TESTSOLVERS==1
+#if LBM_INCLUDE_CONTROL==1
        handleCpdata();
 #endif
 
index 287b73c77b2920146866cc532ea5786fc75cb134..db6e537aff3f1727a9fac30ab5366f02c2e9c3bf 100644 (file)
@@ -15,7 +15,8 @@
 #define CAUSE_PANIC { this->mPanic=1; } /*set flag*/
 #endif // FSGR_STRICT_DEBUG==1
 
-#if LBM_INCLUDE_TESTSOLVERS!=1
+// #if LBM_INCLUDE_TESTSOLVERS!=1
+#if LBM_INCLUDE_CONTROL!=1
 
 #define PRECOLLIDE_MODS(rho,ux,uy,uz, grav) \
        ux += (grav)[0]; \
 
 #define TEST_IF_CHECK 
 
-#else // LBM_INCLUDE_TESTSOLVERS!=1
-// defined in test.h
-
-#define NEWDIRVELMOTEST 0
-#if NEWDIRVELMOTEST==1
-// off for non testing
-#undef PRECOLLIDE_MODS
-#define PRECOLLIDE_MODS(rho,ux,uy,uz, grav) \
-       ux += (grav)[0]; \
-       uy += (grav)[1]; \
-       uz += (grav)[2];  \
-  { \
-               int lev = mMaxRefine, nomb=0; \
-               LbmFloat bcnt = 0.,nux=0.,nuy=0.,nuz=0.; \
-               for(int l=1; l<this->cDfNum; l++) {  \
-                       if(RFLAG_NB(lev, i,j,k,SRCS(lev),l)&CFBnd) { \
-                               if(RFLAG_NB(lev, i,j,k,SRCS(lev),l)&CFBndMoving) { \
-                                       nux += QCELL_NB(lev, i,j,k,SRCS(lev),l, dMass); \
-                                       nuy += QCELL_NB(lev, i,j,k,SRCS(lev),l, dFfrac); \
-                                       bcnt += 1.; \
-                               }       else { \
-                                       nomb++; \
-                               } \
-                       }  \
-               } \
-               if((bcnt>0.)&&(nomb==0)) { \
-                       ux = nux/bcnt; \
-                       uy = nuy/bcnt; \
-                       uz = nuz/bcnt;  \
-               } \
-       } 
-#else // NEWDIRVELMOTEST==1
-// off for non testing
-#endif // NEWDIRVELMOTEST==1
-
-#endif // LBM_INCLUDE_TESTSOLVERS!=1
+#else // LBM_INCLUDE_CONTROL!=1
+// defined in solver_control.h 
+#endif // LBM_INCLUDE_CONTROL!=1
 
        
 /******************************************************************************
index 171a73f72c45fa3d2fc4625e7924659f30e392d4..5a1e266adeb204520c15be8b66850e49d445cf9e 100644 (file)
@@ -430,7 +430,10 @@ DerivedMesh *mesh_create_derived_for_modifier(struct Object *ob, struct Modifier
 
 DerivedMesh *mesh_create_derived_render(struct Object *ob,
                                         CustomDataMask dataMask);
-/* same as above but wont use render settings */
+
+DerivedMesh *mesh_create_derived_index_render(struct Object *ob, CustomDataMask dataMask, int index);
+
+               /* same as above but wont use render settings */
 DerivedMesh *mesh_create_derived_view(struct Object *ob,
                                       CustomDataMask dataMask);
 DerivedMesh *mesh_create_derived_no_deform(struct Object *ob,
diff --git a/source/blender/blenkernel/BKE_fluidsim.h b/source/blender/blenkernel/BKE_fluidsim.h
new file mode 100644 (file)
index 0000000..33c706d
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * BKE_fluidsim.h
+ * 
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "DNA_modifier_types.h"
+#include "DNA_object_fluidsim.h" // N_T
+#include "DNA_object_types.h"
+
+#include "BKE_DerivedMesh.h"
+
+/* old interface */
+FluidsimSettings *fluidsimSettingsNew(Object *srcob);
+
+void initElbeemMesh(Object *ob, int *numVertices, float **vertices, int *numTriangles, int **triangles, int useGlobalCoords, int modifierIndex);
+
+
+/* new fluid-modifier interface */
+void fluidsim_init(FluidsimModifierData *fluidmd);
+void fluidsim_free(FluidsimModifierData *fluidmd);
+
+DerivedMesh *fluidsim_read_cache(Object *ob, DerivedMesh *orgdm, FluidsimModifierData *fluidmd, int framenr, int useRenderParams);
+DerivedMesh *fluidsimModifier_do(FluidsimModifierData *fluidmd, Object *ob, DerivedMesh *dm, int useRenderParams, int isFinalCalc);
+
+// get bounding box of mesh
+void fluid_get_bb(MVert *mvert, int totvert, float obmat[][4],
+                /*RET*/ float start[3], /*RET*/ float size[3] );
+
+
+
index 5bc467465a8e8f4475a6de8336c34ece812ce00c..e4ce502e142af89e1403f860153e2965854a92eb 100644 (file)
@@ -54,7 +54,8 @@
 /* PTCacheID types */
 #define PTCACHE_TYPE_SOFTBODY  0
 #define PTCACHE_TYPE_PARTICLES 1
-#define PTCACHE_TYPE_CLOTH             2
+#define PTCACHE_TYPE_CLOTH     2
+#define PTCACHE_TYPE_FLUIDSIM  3
 
 /* Structs */
 struct Object;
@@ -83,6 +84,7 @@ typedef struct PTCacheID {
 void BKE_ptcache_id_from_softbody(PTCacheID *pid, struct Object *ob, struct SoftBody *sb);
 void BKE_ptcache_id_from_particles(PTCacheID *pid, struct Object *ob, struct ParticleSystem *psys);
 void BKE_ptcache_id_from_cloth(PTCacheID *pid, struct Object *ob, struct ClothModifierData *clmd);
+void BKE_ptcache_id_from_fluidsim(PTCacheID *pid, struct Object *ob, struct FluidsimModifierData *fluidmd);
 
 void BKE_ptcache_ids_from_object(struct ListBase *lb, struct Object *ob);
 
index 328dcada01a94764dcab111c81004cc9b939ab25..569229cb8abeff62dd88c9434934b84aa3c1c801 100644 (file)
@@ -33,8 +33,6 @@
 #include <config.h>
 #endif
 
-#include <zlib.h>
-
 #include "PIL_time.h"
 
 #include "MEM_guardedalloc.h"
@@ -67,6 +65,7 @@
 #include "BKE_deform.h"
 #include "BKE_displist.h"
 #include "BKE_effect.h"
+#include "BKE_fluidsim.h"
 #include "BKE_global.h"
 #include "BKE_key.h"
 #include "BKE_material.h"
 #include "GPU_extensions.h"
 #include "GPU_material.h"
 
-// headers for fluidsim bobj meshes
-#include <stdlib.h>
-#include "LBM_fluidsim.h"
-#include "elbeem.h"
-
 ///////////////////////////////////
 ///////////////////////////////////
 
@@ -420,35 +414,15 @@ void DM_swap_face_data(DerivedMesh *dm, int index, int *corner_indices)
 static DerivedMesh *getMeshDerivedMesh(Mesh *me, Object *ob, float (*vertCos)[3])
 {
        DerivedMesh *dm = CDDM_from_mesh(me, ob);
-       int i, dofluidsim;
-
-       dofluidsim = ((ob->fluidsimFlag & OB_FLUIDSIM_ENABLE) &&
-                     (ob->fluidsimSettings->type & OB_FLUIDSIM_DOMAIN)&&
-                     (ob->fluidsimSettings->meshSurface) &&
-                     (me->totvert == ((Mesh *)(ob->fluidsimSettings->meshSurface))->totvert));
-
-       if (vertCos && !dofluidsim)
+       
+       if(!dm)
+               return NULL;
+       
+       if (vertCos)
                CDDM_apply_vert_coords(dm, vertCos);
 
        CDDM_calc_normals(dm);
 
-       /* apply fluidsim normals */    
-       if (dofluidsim) {
-               // use normals from readBobjgz
-               // TODO? check for modifiers!?
-               MVert *fsvert = ob->fluidsimSettings->meshSurfNormals;
-               short (*normals)[3] = MEM_mallocN(sizeof(short)*3*me->totvert, "fluidsim nor");
-
-               for (i=0; i<me->totvert; i++) {
-                       VECCOPY(normals[i], fsvert[i].no);
-                       //mv->no[0]= 30000; mv->no[1]= mv->no[2]= 0; // DEBUG fixed test normals
-               }
-
-               CDDM_apply_vert_normals(dm, normals);
-
-               MEM_freeN(normals);
-       }
-
        return dm;
 }
 
@@ -2123,7 +2097,7 @@ static void add_orco_dm(Object *ob, EditMesh *em, DerivedMesh *dm, DerivedMesh *
 static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3],
                                 DerivedMesh **deform_r, DerivedMesh **final_r,
                                 int useRenderParams, int useDeform,
-                                int needMapping, CustomDataMask dataMask)
+                                int needMapping, CustomDataMask dataMask, int index)
 {
        Mesh *me = ob->data;
        ModifierData *firstmd, *md;
@@ -2132,7 +2106,6 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3],
        float (*deformedVerts)[3] = NULL;
        DerivedMesh *dm, *orcodm, *finaldm;
        int numVerts = me->totvert;
-       int fluidsimMeshUsed = 0;
        int required_mode;
 
        md = firstmd = modifiers_getVirtualModifierList(ob);
@@ -2148,21 +2121,6 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3],
        if(deform_r) *deform_r = NULL;
        *final_r = NULL;
 
-       /* replace original mesh by fluidsim surface mesh for fluidsim
-        * domain objects
-        */
-       if((G.obedit!=ob) && !needMapping) {
-               if((ob->fluidsimFlag & OB_FLUIDSIM_ENABLE)) {
-                       if(ob->fluidsimSettings->type & OB_FLUIDSIM_DOMAIN) {
-                               loadFluidsimMesh(ob,useRenderParams);
-                               fluidsimMeshUsed = 1;
-                               /* might have changed... */
-                               me = ob->data;
-                               numVerts = me->totvert;
-                       }
-               }
-       }
-
        if(useRenderParams) required_mode = eModifierMode_Render;
        else required_mode = eModifierMode_Realtime;
 
@@ -2171,7 +2129,7 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3],
                        deformedVerts = mesh_getVertexCos(me, &numVerts);
                
                /* Apply all leading deforming modifiers */
-               for(; md; md = md->next, curr = curr->next) {
+               for(;md; md = md->next, curr = curr->next) {
                        ModifierTypeInfo *mti = modifierType_getInfo(md->type);
 
                        if((md->mode & required_mode) != required_mode) continue;
@@ -2185,6 +2143,10 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3],
                        } else {
                                break;
                        }
+                       
+                       /* grab modifiers until index i */
+                       if((index >= 0) && (modifiers_indexInObject(ob, md) >= index))
+                               break;
                }
 
                /* Result of all leading deforming modifiers is cached for
@@ -2210,18 +2172,11 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3],
 #endif
                }
        } else {
-               if(!fluidsimMeshUsed) {
-                       /* default behaviour for meshes */
-                       if(inputVertexCos)
-                               deformedVerts = inputVertexCos;
-                       else
-                               deformedVerts = mesh_getRefKeyCos(me, &numVerts);
-               } else {
-                       /* the fluid sim mesh might have more vertices than the original 
-                        * one, so inputVertexCos shouldnt be used
-                        */
-                       deformedVerts = mesh_getVertexCos(me, &numVerts);
-               }
+               /* default behaviour for meshes */
+               if(inputVertexCos)
+                       deformedVerts = inputVertexCos;
+               else
+                       deformedVerts = mesh_getRefKeyCos(me, &numVerts);
        }
 
 
@@ -2238,7 +2193,7 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3],
        if(me->vnode) dm = derivedmesh_from_versemesh(me->vnode, deformedVerts);
 #endif
 
-       for(; md; md = md->next, curr = curr->next) {
+       for(;md; md = md->next, curr = curr->next) {
                ModifierTypeInfo *mti = modifierType_getInfo(md->type);
 
                if((md->mode & required_mode) != required_mode) continue;
@@ -2344,6 +2299,10 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3],
                                }
                        } 
                }
+               
+               /* grab modifiers until index i */
+               if((index >= 0) && (modifiers_indexInObject(ob, md) >= index))
+                       break;
        }
 
        for(md=firstmd; md; md=md->next)
@@ -2399,9 +2358,6 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3],
                MEM_freeN(deformedVerts);
 
        BLI_linklist_free(datamasks, NULL);
-
-       /* restore mesh in any case */
-       if(fluidsimMeshUsed) ob->data = ob->fluidsimSettings->orgMesh;
 }
 
 static float (*editmesh_getVertexCos(EditMesh *em, int *numVerts_r))[3]
@@ -2753,13 +2709,13 @@ static void mesh_build_data(Object *ob, CustomDataMask dataMask)
 
                        mesh_calc_modifiers(ob, NULL, &ob->derivedDeform,
                                            &ob->derivedFinal, 0, 1,
-                                           needMapping, dataMask);
+                                           needMapping, dataMask, -1);
 
                        CustomData_free_layer_active(&me->fdata, CD_MCOL, me->totface);
                } else {
                        mesh_calc_modifiers(ob, NULL, &ob->derivedDeform,
                                            &ob->derivedFinal, G.rendering, 1,
-                                           needMapping, dataMask);
+                                           needMapping, dataMask, -1);
                }
 
                INIT_MINMAX(min, max);
@@ -2930,7 +2886,21 @@ DerivedMesh *mesh_create_derived_render(Object *ob, CustomDataMask dataMask)
        int orig_lvl= 0;
        
        vert_copy= multires_render_pin(ob, me, &orig_lvl);
-       mesh_calc_modifiers(ob, NULL, NULL, &final, 1, 1, 0, dataMask);
+       mesh_calc_modifiers(ob, NULL, NULL, &final, 1, 1, 0, dataMask, -1);
+       multires_render_final(ob, me, &final, vert_copy, orig_lvl, dataMask);
+
+       return final;
+}
+
+DerivedMesh *mesh_create_derived_index_render(Object *ob, CustomDataMask dataMask, int index)
+{
+       DerivedMesh *final;
+       Mesh *me= get_mesh(ob);
+       float *vert_copy= NULL;
+       int orig_lvl= 0;
+       
+       vert_copy= multires_render_pin(ob, me, &orig_lvl);
+       mesh_calc_modifiers(ob, NULL, NULL, &final, 1, 1, 0, dataMask, index);
        multires_render_final(ob, me, &final, vert_copy, orig_lvl, dataMask);
 
        return final;
@@ -2940,7 +2910,7 @@ DerivedMesh *mesh_create_derived_view(Object *ob, CustomDataMask dataMask)
 {
        DerivedMesh *final;
 
-       mesh_calc_modifiers(ob, NULL, NULL, &final, 0, 1, 0, dataMask);
+       mesh_calc_modifiers(ob, NULL, NULL, &final, 0, 1, 0, dataMask, -1);
 
        return final;
 }
@@ -2950,7 +2920,7 @@ DerivedMesh *mesh_create_derived_no_deform(Object *ob, float (*vertCos)[3],
 {
        DerivedMesh *final;
        
-       mesh_calc_modifiers(ob, vertCos, NULL, &final, 0, 0, 0, dataMask);
+       mesh_calc_modifiers(ob, vertCos, NULL, &final, 0, 0, 0, dataMask, -1);
 
        return final;
 }
@@ -2965,7 +2935,7 @@ DerivedMesh *mesh_create_derived_no_deform_render(Object *ob,
        int orig_lvl= 0;
 
        vert_copy= multires_render_pin(ob, me, &orig_lvl);
-       mesh_calc_modifiers(ob, vertCos, NULL, &final, 1, 0, 0, dataMask);
+       mesh_calc_modifiers(ob, vertCos, NULL, &final, 1, 0, 0, dataMask, -1);
        multires_render_final(ob, me, &final, vert_copy, orig_lvl, dataMask);
 
        return final;
@@ -3325,582 +3295,3 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs,
        }
 }
 
-/* ************************* fluidsim bobj file handling **************************** */
-
-#ifndef DISABLE_ELBEEM
-
-#ifdef WIN32
-#ifndef snprintf
-#define snprintf _snprintf
-#endif
-#endif
-
-/* write .bobj.gz file for a mesh object */
-void writeBobjgz(char *filename, struct Object *ob, int useGlobalCoords, int append, float time) 
-{
-       char debugStrBuffer[256];
-       int wri,i,j,totvert,totface;
-       float wrf;
-       gzFile gzf;
-       DerivedMesh *dm;
-       float vec[3];
-       float rotmat[3][3];
-       MVert *mvert;
-       MFace *mface;
-       //if(append)return; // DEBUG
-
-       if(!ob->data || (ob->type!=OB_MESH)) {
-               snprintf(debugStrBuffer,256,"Writing GZ_BOBJ Invalid object %s ...\n", ob->id.name); 
-               elbeemDebugOut(debugStrBuffer);
-               return;
-       }
-       if((ob->size[0]<0.0) || (ob->size[0]<0.0) || (ob->size[0]<0.0) ) {
-               snprintf(debugStrBuffer,256,"\nfluidSim::writeBobjgz:: Warning object %s has negative scaling - check triangle ordering...?\n\n", ob->id.name); 
-               elbeemDebugOut(debugStrBuffer);
-       }
-
-       snprintf(debugStrBuffer,256,"Writing GZ_BOBJ '%s' ... ",filename); elbeemDebugOut(debugStrBuffer); 
-       if(append) gzf = gzopen(filename, "a+b9");
-       else       gzf = gzopen(filename, "wb9");
-       if (!gzf) {
-               snprintf(debugStrBuffer,256,"writeBobjgz::error - Unable to open file for writing '%s'\n", filename);
-               elbeemDebugOut(debugStrBuffer);
-               return;
-       }
-
-       dm = mesh_create_derived_render(ob, CD_MASK_BAREMESH);
-       //dm = mesh_create_derived_no_deform(ob,NULL);
-
-       mvert = dm->getVertArray(dm);
-       mface = dm->getFaceArray(dm);
-       totvert = dm->getNumVerts(dm);
-       totface = dm->getNumFaces(dm);
-
-       // write time value for appended anim mesh
-       if(append) {
-               gzwrite(gzf, &time, sizeof(time));
-       }
-
-       // continue with verts/norms
-       if(sizeof(wri)!=4) { snprintf(debugStrBuffer,256,"Writing GZ_BOBJ, Invalid int size %d...\n", wri); elbeemDebugOut(debugStrBuffer); return; } // paranoia check
-       wri = dm->getNumVerts(dm);
-       mvert = dm->getVertArray(dm);
-       gzwrite(gzf, &wri, sizeof(wri));
-       for(i=0; i<wri;i++) {
-               VECCOPY(vec, mvert[i].co);
-               if(useGlobalCoords) { Mat4MulVecfl(ob->obmat, vec); }
-               for(j=0; j<3; j++) {
-                       wrf = vec[j]; 
-                       gzwrite(gzf, &wrf, sizeof( wrf )); 
-               }
-       }
-
-       // should be the same as Vertices.size
-       wri = totvert;
-       gzwrite(gzf, &wri, sizeof(wri));
-       EulToMat3(ob->rot, rotmat);
-       for(i=0; i<wri;i++) {
-               VECCOPY(vec, mvert[i].no);
-               Normalize(vec);
-               if(useGlobalCoords) { Mat3MulVecfl(rotmat, vec); }
-               for(j=0; j<3; j++) {
-                       wrf = vec[j];
-                       gzwrite(gzf, &wrf, sizeof( wrf )); 
-               }
-       }
-
-       // append only writes verts&norms 
-       if(!append) {
-               //float side1[3],side2[3],norm1[3],norm2[3];
-               //float inpf;
-       
-               // compute no. of triangles 
-               wri = 0;
-               for(i=0; i<totface; i++) {
-                       wri++;
-                       if(mface[i].v4) { wri++; }
-               }
-               gzwrite(gzf, &wri, sizeof(wri));
-               for(i=0; i<totface; i++) {
-
-                       int face[4];
-                       face[0] = mface[i].v1;
-                       face[1] = mface[i].v2;
-                       face[2] = mface[i].v3;
-                       face[3] = mface[i].v4;
-                       //snprintf(debugStrBuffer,256,"F %s %d = %d,%d,%d,%d \n",ob->id.name, i, face[0],face[1],face[2],face[3] ); elbeemDebugOut(debugStrBuffer);
-                       //VecSubf(side1, mvert[face[1]].co,mvert[face[0]].co);
-                       //VecSubf(side2, mvert[face[2]].co,mvert[face[0]].co);
-                       //Crossf(norm1,side1,side2);
-                       gzwrite(gzf, &(face[0]), sizeof( face[0] )); 
-                       gzwrite(gzf, &(face[1]), sizeof( face[1] )); 
-                       gzwrite(gzf, &(face[2]), sizeof( face[2] )); 
-                       if(face[3]) { 
-                               //VecSubf(side1, mvert[face[2]].co,mvert[face[0]].co);
-                               //VecSubf(side2, mvert[face[3]].co,mvert[face[0]].co);
-                               //Crossf(norm2,side1,side2);
-                               //inpf = Inpf(norm1,norm2);
-                               //if(inpf>0.) {
-                               gzwrite(gzf, &(face[0]), sizeof( face[0] )); 
-                               gzwrite(gzf, &(face[2]), sizeof( face[2] )); 
-                               gzwrite(gzf, &(face[3]), sizeof( face[3] )); 
-                               //} else {
-                                       //gzwrite(gzf, &(face[0]), sizeof( face[0] )); 
-                                       //gzwrite(gzf, &(face[3]), sizeof( face[3] )); 
-                                       //gzwrite(gzf, &(face[2]), sizeof( face[2] )); 
-                               //}
-                       } // quad
-               }
-       }
-
-       snprintf(debugStrBuffer,256,"Done. #Vertices: %d, #Triangles: %d\n", totvert, totface ); 
-       elbeemDebugOut(debugStrBuffer);
-       
-       gzclose( gzf );
-       dm->release(dm);
-}
-
-void initElbeemMesh(struct Object *ob, 
-               int *numVertices, float **vertices, 
-               int *numTriangles, int **triangles,
-               int useGlobalCoords) 
-{
-       DerivedMesh *dm = NULL;
-       MVert *mvert;
-       MFace *mface;
-       int countTris=0, i, totvert, totface;
-       float *verts;
-       int *tris;
-
-       dm = mesh_create_derived_render(ob, CD_MASK_BAREMESH);
-       //dm = mesh_create_derived_no_deform(ob,NULL);
-
-       mvert = dm->getVertArray(dm);
-       mface = dm->getFaceArray(dm);
-       totvert = dm->getNumVerts(dm);
-       totface = dm->getNumFaces(dm);
-
-       *numVertices = totvert;
-       verts = MEM_callocN( totvert*3*sizeof(float), "elbeemmesh_vertices");
-       for(i=0; i<totvert; i++) {
-               VECCOPY( &verts[i*3], mvert[i].co);
-               if(useGlobalCoords) { Mat4MulVecfl(ob->obmat, &verts[i*3]); }
-       }
-       *vertices = verts;
-
-       for(i=0; i<totface; i++) {
-               countTris++;
-               if(mface[i].v4) { countTris++; }
-       }
-       *numTriangles = countTris;
-       tris = MEM_callocN( countTris*3*sizeof(int), "elbeemmesh_triangles");
-       countTris = 0;
-       for(i=0; i<totface; i++) {
-               int face[4];
-               face[0] = mface[i].v1;
-               face[1] = mface[i].v2;
-               face[2] = mface[i].v3;
-               face[3] = mface[i].v4;
-
-               tris[countTris*3+0] = face[0]; 
-               tris[countTris*3+1] = face[1]; 
-               tris[countTris*3+2] = face[2]; 
-               countTris++;
-               if(face[3]) { 
-                       tris[countTris*3+0] = face[0]; 
-                       tris[countTris*3+1] = face[2]; 
-                       tris[countTris*3+2] = face[3]; 
-                       countTris++;
-               }
-       }
-       *triangles = tris;
-
-       dm->release(dm);
-}
-
-/* read .bobj.gz file into a fluidsimDerivedMesh struct */
-Mesh* readBobjgz(char *filename, Mesh *orgmesh, float* bbstart, float *bbsize) //, fluidsimDerivedMesh *fsdm)
-{
-       int wri,i,j;
-       char debugStrBuffer[256];
-       float wrf;
-       Mesh *newmesh; 
-       const int debugBobjRead = 1;
-       // init data from old mesh (materials,flags)
-       MFace *origMFace = &((MFace*) orgmesh->mface)[0];
-       int mat_nr = -1;
-       int flag = -1;
-       MFace *fsface = NULL;
-       int gotBytes;
-       gzFile gzf;
-
-       if(!orgmesh) return NULL;
-       if(!origMFace) return NULL;
-       mat_nr = origMFace->mat_nr;
-       flag = origMFace->flag;
-
-       // similar to copy_mesh
-       newmesh = MEM_dupallocN(orgmesh);
-       newmesh->mat= orgmesh->mat;
-
-       newmesh->mvert= NULL;
-       newmesh->medge= NULL;
-       newmesh->mface= NULL;
-       newmesh->mtface= NULL;
-
-       newmesh->dvert = NULL;
-
-       newmesh->mcol= NULL;
-       newmesh->msticky= NULL;
-       newmesh->texcomesh= NULL;
-       memset(&newmesh->vdata, 0, sizeof(newmesh->vdata));
-       memset(&newmesh->edata, 0, sizeof(newmesh->edata));
-       memset(&newmesh->fdata, 0, sizeof(newmesh->fdata));
-
-       newmesh->key= NULL;
-       newmesh->totface = 0;
-       newmesh->totvert = 0;
-       newmesh->totedge = 0;
-       newmesh->medge = NULL;
-
-
-       snprintf(debugStrBuffer,256,"Reading '%s' GZ_BOBJ... ",filename); elbeemDebugOut(debugStrBuffer); 
-       gzf = gzopen(filename, "rb");
-       // gzf = fopen(filename, "rb");
-       // debug: fread(b,c,1,a) = gzread(a,b,c)
-       if (!gzf) {
-               //snprintf(debugStrBuffer,256,"readBobjgz::error - Unable to open file for reading '%s'\n", filename); // DEBUG
-               MEM_freeN(newmesh);
-               return NULL;
-       }
-
-       //if(sizeof(wri)!=4) { snprintf(debugStrBuffer,256,"Reading GZ_BOBJ, Invalid int size %d...\n", wri); return NULL; } // paranoia check
-       gotBytes = gzread(gzf, &wri, sizeof(wri));
-       newmesh->totvert = wri;
-       newmesh->mvert = CustomData_add_layer(&newmesh->vdata, CD_MVERT, CD_CALLOC, NULL, newmesh->totvert);
-       if(debugBobjRead){ snprintf(debugStrBuffer,256,"#vertices %d ", newmesh->totvert); elbeemDebugOut(debugStrBuffer); } //DEBUG
-       for(i=0; i<newmesh->totvert;i++) {
-               //if(debugBobjRead) snprintf(debugStrBuffer,256,"V %d = ",i);
-               for(j=0; j<3; j++) {
-                       gotBytes = gzread(gzf, &wrf, sizeof( wrf )); 
-                       newmesh->mvert[i].co[j] = wrf;
-                       //if(debugBobjRead) snprintf(debugStrBuffer,256,"%25.20f ", wrf);
-               }
-               //if(debugBobjRead) snprintf(debugStrBuffer,256,"\n");
-       }
-
-       // should be the same as Vertices.size
-       gotBytes = gzread(gzf, &wri, sizeof(wri));
-       if(wri != newmesh->totvert) {
-               // complain #vertices has to be equal to #normals, reset&abort
-               CustomData_free_layer_active(&newmesh->vdata, CD_MVERT, newmesh->totvert);
-               MEM_freeN(newmesh);
-               snprintf(debugStrBuffer,256,"Reading GZ_BOBJ, #normals=%d, #vertices=%d, aborting...\n", wri,newmesh->totvert );
-               return NULL;
-       }
-       for(i=0; i<newmesh->totvert;i++) {
-               for(j=0; j<3; j++) {
-                       gotBytes = gzread(gzf, &wrf, sizeof( wrf )); 
-                       newmesh->mvert[i].no[j] = (short)(wrf*32767.0f);
-                       //newmesh->mvert[i].no[j] = 0.5; // DEBUG tst
-               }
-       //fprintf(stderr,"  DEBDPCN nm%d, %d = %d,%d,%d \n",
-                       //(int)(newmesh->mvert), i, newmesh->mvert[i].no[0], newmesh->mvert[i].no[1], newmesh->mvert[i].no[2]);
-       }
-       //fprintf(stderr,"  DPCN 0 = %d,%d,%d \n", newmesh->mvert[0].no[0], newmesh->mvert[0].no[1], newmesh->mvert[0].no[2]);
-
-       
-       /* compute no. of triangles */
-       gotBytes = gzread(gzf, &wri, sizeof(wri));
-       newmesh->totface = wri;
-       newmesh->mface = CustomData_add_layer(&newmesh->fdata, CD_MFACE, CD_CALLOC, NULL, newmesh->totface);
-       if(debugBobjRead){ snprintf(debugStrBuffer,256,"#faces %d ", newmesh->totface); elbeemDebugOut(debugStrBuffer); } //DEBUG
-       fsface = newmesh->mface;
-       for(i=0; i<newmesh->totface; i++) {
-               int face[4];
-
-               gotBytes = gzread(gzf, &(face[0]), sizeof( face[0] )); 
-               gotBytes = gzread(gzf, &(face[1]), sizeof( face[1] )); 
-               gotBytes = gzread(gzf, &(face[2]), sizeof( face[2] )); 
-               face[3] = 0;
-
-               fsface[i].v1 = face[0];
-               fsface[i].v2 = face[1];
-               fsface[i].v3 = face[2];
-               fsface[i].v4 = face[3];
-       }
-
-       // correct triangles with v3==0 for blender, cycle verts
-       for(i=0; i<newmesh->totface; i++) {
-               if(!fsface[i].v3) {
-                       int temp = fsface[i].v1;
-                       fsface[i].v1 = fsface[i].v2;
-                       fsface[i].v2 = fsface[i].v3;
-                       fsface[i].v3 = temp;
-               }
-       }
-       
-       gzclose( gzf );
-       for(i=0;i<newmesh->totface;i++) { 
-               fsface[i].mat_nr = mat_nr;
-               fsface[i].flag = flag;
-               fsface[i].edcode = ME_V1V2 | ME_V2V3 | ME_V3V1;
-               //snprintf(debugStrBuffer,256,"%d : %d,%d,%d\n", i,fsface[i].mat_nr, fsface[i].flag, fsface[i].edcode );
-       }
-
-       snprintf(debugStrBuffer,256," (%d,%d) done\n", newmesh->totvert,newmesh->totface); elbeemDebugOut(debugStrBuffer); //DEBUG
-       return newmesh;
-}
-
-/* read zipped fluidsim velocities into the co's of the fluidsimsettings normals struct */
-void readVelgz(char *filename, Object *srcob)
-{
-       char debugStrBuffer[256];
-       int wri, i, j;
-       float wrf;
-       gzFile gzf;
-       MVert *vverts = srcob->fluidsimSettings->meshSurfNormals;
-       int len = strlen(filename);
-       Mesh *mesh = srcob->data;
-       // mesh and vverts have to be valid from loading...
-
-       // clean up in any case
-       for(i=0; i<mesh->totvert;i++) { 
-               for(j=0; j<3; j++) {
-                       vverts[i].co[j] = 0.; 
-               } 
-       } 
-       if(srcob->fluidsimSettings->domainNovecgen>0) return;
-
-       if(len<7) { 
-               //printf("readVelgz Eror: invalid filename '%s'\n",filename); // DEBUG
-               return; 
-       }
-
-       // .bobj.gz , correct filename
-       // 87654321
-       filename[len-6] = 'v';
-       filename[len-5] = 'e';
-       filename[len-4] = 'l';
-
-       snprintf(debugStrBuffer,256,"Reading '%s' GZ_VEL... ",filename); elbeemDebugOut(debugStrBuffer); 
-       gzf = gzopen(filename, "rb");
-       if (!gzf) { 
-               //printf("readVelgz Eror: unable to open file '%s'\n",filename); // DEBUG
-               return; 
-       }
-
-       gzread(gzf, &wri, sizeof( wri ));
-       if(wri != mesh->totvert) {
-               //printf("readVelgz Eror: invalid no. of velocities %d vs. %d aborting.\n" ,wri ,mesh->totvert ); // DEBUG
-               return; 
-       }
-
-       for(i=0; i<mesh->totvert;i++) {
-               for(j=0; j<3; j++) {
-                       gzread(gzf, &wrf, sizeof( wrf )); 
-                       vverts[i].co[j] = wrf;
-               }
-               //if(i<20) fprintf(stderr, "GZ_VELload %d = %f,%f,%f  \n",i,vverts[i].co[0],vverts[i].co[1],vverts[i].co[2]); // DEBUG
-       }
-
-       gzclose(gzf);
-}
-
-
-/* ***************************** fluidsim derived mesh ***************************** */
-
-/* check which file to load, and replace old mesh of the object with it */
-/* this replacement is undone at the end of mesh_calc_modifiers */
-void loadFluidsimMesh(Object *srcob, int useRenderParams)
-{
-       Mesh *mesh = NULL;
-       float *bbStart = NULL, *bbSize = NULL;
-       float lastBB[3];
-       int displaymode = 0;
-       int curFrame = G.scene->r.cfra - 1 /*G.scene->r.sfra*/; /* start with 0 at start frame */
-       char targetDir[FILE_MAXFILE+FILE_MAXDIR], targetFile[FILE_MAXFILE+FILE_MAXDIR];
-       char debugStrBuffer[256];
-       //snprintf(debugStrBuffer,256,"loadFluidsimMesh call (obid '%s', rp %d)\n", srcob->id.name, useRenderParams); // debug
-
-       if((!srcob)||(!srcob->fluidsimSettings)) {
-               snprintf(debugStrBuffer,256,"DEBUG - Invalid loadFluidsimMesh call, rp %d, dm %d)\n", useRenderParams, displaymode); // debug
-               elbeemDebugOut(debugStrBuffer); // debug
-               return;
-       }
-       // make sure the original mesh data pointer is stored
-       if(!srcob->fluidsimSettings->orgMesh) {
-               srcob->fluidsimSettings->orgMesh = srcob->data;
-       }
-
-       // free old mesh, if there is one (todo, check if it's still valid?)
-       if(srcob->fluidsimSettings->meshSurface) {
-               Mesh *freeFsMesh = srcob->fluidsimSettings->meshSurface;
-
-               // similar to free_mesh(...) , but no things like unlink...
-               CustomData_free(&freeFsMesh->vdata, freeFsMesh->totvert);
-               CustomData_free(&freeFsMesh->edata, freeFsMesh->totedge);
-               CustomData_free(&freeFsMesh->fdata, freeFsMesh->totface);
-               MEM_freeN(freeFsMesh);
-               
-               if(srcob->data == srcob->fluidsimSettings->meshSurface)
-                srcob->data = srcob->fluidsimSettings->orgMesh;
-               srcob->fluidsimSettings->meshSurface = NULL;
-
-               if(srcob->fluidsimSettings->meshSurfNormals) MEM_freeN(srcob->fluidsimSettings->meshSurfNormals);
-               srcob->fluidsimSettings->meshSurfNormals = NULL;
-       } 
-
-       // init bounding box
-       bbStart = srcob->fluidsimSettings->bbStart; 
-       bbSize = srcob->fluidsimSettings->bbSize;
-       lastBB[0] = bbSize[0];  // TEST
-       lastBB[1] = bbSize[1]; 
-       lastBB[2] = bbSize[2];
-       fluidsimGetAxisAlignedBB(srcob->fluidsimSettings->orgMesh, srcob->obmat, bbStart, bbSize, &srcob->fluidsimSettings->meshBB);
-       // check free fsmesh... TODO
-       
-       if(!useRenderParams) {
-               displaymode = srcob->fluidsimSettings->guiDisplayMode;
-       } else {
-               displaymode = srcob->fluidsimSettings->renderDisplayMode;
-       }
-       
-       snprintf(debugStrBuffer,256,"loadFluidsimMesh call (obid '%s', rp %d, dm %d), curFra=%d, sFra=%d #=%d \n", 
-                       srcob->id.name, useRenderParams, displaymode, G.scene->r.cfra, G.scene->r.sfra, curFrame ); // debug
-       elbeemDebugOut(debugStrBuffer); // debug
-
-       strncpy(targetDir, srcob->fluidsimSettings->surfdataPath, FILE_MAXDIR);
-       // use preview or final mesh?
-       if(displaymode==1) {
-               // just display original object
-               srcob->data = srcob->fluidsimSettings->orgMesh;
-               return;
-       } else if(displaymode==2) {
-               strcat(targetDir,"fluidsurface_preview_####");
-       } else { // 3
-               strcat(targetDir,"fluidsurface_final_####");
-       }
-       BLI_convertstringcode(targetDir, G.sce);
-       BLI_convertstringframe(targetDir, curFrame); // fixed #frame-no 
-       
-       strcpy(targetFile,targetDir);
-       strcat(targetFile, ".bobj.gz");
-
-       snprintf(debugStrBuffer,256,"loadFluidsimMesh call (obid '%s', rp %d, dm %d) '%s' \n", srcob->id.name, useRenderParams, displaymode, targetFile);  // debug
-       elbeemDebugOut(debugStrBuffer); // debug
-
-       if(displaymode!=2) { // dont add bounding box for final
-               mesh = readBobjgz(targetFile, srcob->fluidsimSettings->orgMesh ,NULL,NULL);
-       } else {
-               mesh = readBobjgz(targetFile, srcob->fluidsimSettings->orgMesh, bbSize,bbSize );
-       }
-       if(!mesh) {
-               // switch, abort background rendering when fluidsim mesh is missing
-               const char *strEnvName2 = "BLENDER_ELBEEMBOBJABORT"; // from blendercall.cpp
-               if(G.background==1) {
-                       if(getenv(strEnvName2)) {
-                               int elevel = atoi(getenv(strEnvName2));
-                               if(elevel>0) {
-                                       printf("Env. var %s set, fluid sim mesh '%s' not found, aborting render...\n",strEnvName2, targetFile);
-                                       exit(1);
-                               }
-                       }
-               }
-               
-               // display org. object upon failure
-               srcob->data = srcob->fluidsimSettings->orgMesh;
-               return;
-       }
-
-       if((mesh)&&(mesh->totvert>0)) {
-               make_edges(mesh, 0);    // 0 = make all edges draw
-       }
-       srcob->fluidsimSettings->meshSurface = mesh;
-       srcob->data = mesh;
-       srcob->fluidsimSettings->meshSurfNormals = MEM_dupallocN(mesh->mvert);
-
-       // load vertex velocities, if they exist...
-       // TODO? use generate flag as loading flag as well?
-       // warning, needs original .bobj.gz mesh loading filename
-       if(displaymode==3) {
-               readVelgz(targetFile, srcob);
-       } else {
-               // no data for preview, only clear...
-               int i,j;
-               for(i=0; i<mesh->totvert;i++) { for(j=0; j<3; j++) { srcob->fluidsimSettings->meshSurfNormals[i].co[j] = 0.; }} 
-       }
-
-       //fprintf(stderr,"LOADFLM DEBXHCH fs=%d 3:%d,%d,%d \n", (int)mesh, ((Mesh *)(srcob->fluidsimSettings->meshSurface))->mvert[3].no[0], ((Mesh *)(srcob->fluidsimSettings->meshSurface))->mvert[3].no[1], ((Mesh *)(srcob->fluidsimSettings->meshSurface))->mvert[3].no[2]);
-       return;
-}
-
-/* helper function */
-/* init axis aligned BB for mesh object */
-void fluidsimGetAxisAlignedBB(struct Mesh *mesh, float obmat[][4],
-                /*RET*/ float start[3], /*RET*/ float size[3], /*RET*/ struct Mesh **bbmesh )
-{
-       float bbsx=0.0, bbsy=0.0, bbsz=0.0;
-       float bbex=1.0, bbey=1.0, bbez=1.0;
-       int i;
-       float vec[3];
-
-       VECCOPY(vec, mesh->mvert[0].co); 
-       Mat4MulVecfl(obmat, vec);
-       bbsx = vec[0]; bbsy = vec[1]; bbsz = vec[2];
-       bbex = vec[0]; bbey = vec[1]; bbez = vec[2];
-
-       for(i=1; i<mesh->totvert;i++) {
-               VECCOPY(vec, mesh->mvert[i].co);
-               Mat4MulVecfl(obmat, vec);
-
-               if(vec[0] < bbsx){ bbsx= vec[0]; }
-               if(vec[1] < bbsy){ bbsy= vec[1]; }
-               if(vec[2] < bbsz){ bbsz= vec[2]; }
-               if(vec[0] > bbex){ bbex= vec[0]; }
-               if(vec[1] > bbey){ bbey= vec[1]; }
-               if(vec[2] > bbez){ bbez= vec[2]; }
-       }
-
-       // return values...
-       if(start) {
-               start[0] = bbsx;
-               start[1] = bbsy;
-               start[2] = bbsz;
-       } 
-       if(size) {
-               size[0] = bbex-bbsx;
-               size[1] = bbey-bbsy;
-               size[2] = bbez-bbsz;
-       }
-
-       // init bounding box mesh?
-       if(bbmesh) {
-               int i,j;
-               Mesh *newmesh = NULL;
-               if(!(*bbmesh)) { newmesh = MEM_callocN(sizeof(Mesh), "fluidsimGetAxisAlignedBB_meshbb"); }
-               else {           newmesh = *bbmesh; }
-
-               newmesh->totvert = 8;
-               if(!newmesh->mvert)
-                       newmesh->mvert = CustomData_add_layer(&newmesh->vdata, CD_MVERT, CD_CALLOC, NULL, newmesh->totvert);
-               for(i=0; i<8; i++) {
-                       for(j=0; j<3; j++) newmesh->mvert[i].co[j] = start[j]; 
-               }
-
-               newmesh->totface = 6;
-               if(!newmesh->mface)
-                       newmesh->mface = CustomData_add_layer(&newmesh->fdata, CD_MFACE, CD_CALLOC, NULL, newmesh->totface);
-
-               *bbmesh = newmesh;
-       }
-}
-
-#else // DISABLE_ELBEEM
-
-/* dummy for mesh_calc_modifiers */
-void loadFluidsimMesh(Object *srcob, int useRenderParams) {
-}
-
-#endif // DISABLE_ELBEEM
-
index 9d5dcb8109b12a9e3b0587574147a489f38e5988..c30d86431b13354921e8312830f53413683718d7 100644 (file)
@@ -898,10 +898,7 @@ DerivedMesh *CDDM_from_mesh(Mesh *mesh, Object *ob)
 
        dm->deformedOnly = 1;
 
-       if(ob && ob->fluidsimSettings && ob->fluidsimSettings->meshSurface)
-               alloctype= CD_DUPLICATE;
-       else
-               alloctype= CD_REFERENCE;
+       alloctype= CD_REFERENCE;
 
        CustomData_merge(&mesh->vdata, &dm->vertData, CD_MASK_MESH, alloctype,
                         mesh->totvert);
index 80f450000bbc7356b5a24419ba9f955b87485369..9dc89a4919604ed7584188228eb495659d1a1522 100644 (file)
@@ -1963,12 +1963,6 @@ static void dag_object_time_update_flags(Object *ob)
                                                ob->shapeflag &= ~OB_SHAPE_TEMPLOCK;
                                        }
                                }
-                               if((ob->fluidsimFlag & OB_FLUIDSIM_ENABLE) && (ob->fluidsimSettings)) {
-                                       // fluidsimSettings might not be initialized during load...
-                                       if(ob->fluidsimSettings->type & (OB_FLUIDSIM_DOMAIN|OB_FLUIDSIM_PARTICLE)) {
-                                               ob->recalc |= OB_RECALC_DATA; // NT FSPARTICLE
-                                       }
-                               }
                                if(ob->particlesystem.first)
                                        ob->recalc |= OB_RECALC_DATA;
                                break;
diff --git a/source/blender/blenkernel/intern/fluidsim.c b/source/blender/blenkernel/intern/fluidsim.c
new file mode 100644 (file)
index 0000000..4c00dc1
--- /dev/null
@@ -0,0 +1,637 @@
+/**
+ * fluidsim.c
+ * 
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_force.h" // for pointcache 
+#include "DNA_particle_types.h"
+#include "DNA_scene_types.h" // N_T
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_customdata.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_fluidsim.h"
+#include "BKE_global.h"
+#include "BKE_modifier.h"
+#include "BKE_mesh.h"
+#include "BKE_pointcache.h"
+#include "BKE_utildefines.h"
+
+// headers for fluidsim bobj meshes
+#include <stdlib.h>
+#include "LBM_fluidsim.h"
+#include "elbeem.h"
+#include <zlib.h>
+#include <string.h>
+#include <stdio.h>
+
+/* ************************* fluidsim bobj file handling **************************** */
+
+#ifndef DISABLE_ELBEEM
+
+// -----------------------------------------
+// forward decleration
+// -----------------------------------------
+
+// -----------------------------------------
+
+void fluidsim_init(FluidsimModifierData *fluidmd)
+{
+       if(fluidmd)
+       {
+               FluidsimSettings *fss = MEM_callocN(sizeof(FluidsimSettings), "fluidsimsettings");
+               
+               fluidmd->fss = fss;
+               
+               if(!fss)
+                       return;
+               
+               fss->type = 0;
+               fss->show_advancedoptions = 0;
+
+               fss->resolutionxyz = 50;
+               fss->previewresxyz = 25;
+               fss->realsize = 0.03;
+               fss->guiDisplayMode = 2; // preview
+               fss->renderDisplayMode = 3; // render
+
+               fss->viscosityMode = 2; // default to water
+               fss->viscosityValue = 1.0;
+               fss->viscosityExponent = 6;
+               
+               // dg TODO: change this to []
+               fss->gravx = 0.0;
+               fss->gravy = 0.0;
+               fss->gravz = -9.81;
+               fss->animStart = 0.0; 
+               fss->animEnd = 0.30;
+               fss->gstar = 0.005; // used as normgstar
+               fss->maxRefine = -1;
+               // maxRefine is set according to resolutionxyz during bake
+
+               // fluid/inflow settings
+               // fss->iniVel --> automatically set to 0
+
+               /*  elubie: changed this to default to the same dir as the render output
+               to prevent saving to C:\ on Windows */
+               BLI_strncpy(fss->surfdataPath, btempdir, FILE_MAX);
+
+               // first init of bounding box
+               // no bounding box needed
+               
+               // todo - reuse default init from elbeem!
+               fss->typeFlags = 0;
+               fss->domainNovecgen = 0;
+               fss->volumeInitType = 1; // volume
+               fss->partSlipValue = 0.0;
+
+               fss->generateTracers = 0;
+               fss->generateParticles = 0.0;
+               fss->surfaceSmoothing = 1.0;
+               fss->surfaceSubdivs = 1.0;
+               fss->particleInfSize = 0.0;
+               fss->particleInfAlpha = 0.0;
+       
+               // init fluid control settings
+               fss->attractforceStrength = 0.2;
+               fss->attractforceRadius = 0.75;
+               fss->velocityforceStrength = 0.2;
+               fss->velocityforceRadius = 0.75;
+               fss->cpsTimeStart = fss->animStart;
+               fss->cpsTimeEnd = fss->animEnd;
+               fss->cpsQuality = 10.0; // 1.0 / 10.0 => means 0.1 width
+               
+               /*
+               BAD TODO: this is done in buttons_object.c in the moment 
+               Mesh *mesh = ob->data;
+               // calculate bounding box
+               fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize); 
+               */
+               
+               fss->lastgoodframe = -1;
+               
+               fss->flag = 0;
+
+       }
+       
+       return;
+}
+
+void fluidsim_free(FluidsimModifierData *fluidmd)
+{
+       if(fluidmd)
+       {
+               MEM_freeN(fluidmd->fss);
+       }
+       
+       return;
+}
+
+DerivedMesh *fluidsimModifier_do(FluidsimModifierData *fluidmd, Object *ob, DerivedMesh *dm, int useRenderParams, int isFinalCalc)
+{
+       DerivedMesh *result = NULL;
+       int framenr;
+       FluidsimSettings *fss = NULL;
+
+       framenr= (int)G.scene->r.cfra;
+       
+       // only handle fluidsim domains
+       if(fluidmd && fluidmd->fss && (fluidmd->fss->type != OB_FLUIDSIM_DOMAIN))
+               return dm;
+       
+       // sanity check
+       if(!fluidmd || (fluidmd && !fluidmd->fss))
+               return dm;
+       
+       fss = fluidmd->fss;
+       
+       // timescale not supported yet
+       // clmd->sim_parms->timescale= timescale;
+       
+       // support reversing of baked fluid frames here
+       if((fss->flag & OB_FLUIDSIM_REVERSE) && (fss->lastgoodframe >= 0))
+       {
+               framenr = fss->lastgoodframe - framenr + 1;
+               CLAMP(framenr, 1, fss->lastgoodframe);
+       }
+       
+       /* try to read from cache */
+       if(((fss->lastgoodframe >= framenr) || (fss->lastgoodframe < 0)) && (result = fluidsim_read_cache(ob, dm, fluidmd, framenr, useRenderParams)))
+       {
+               // fss->lastgoodframe = framenr; // set also in src/fluidsim.c
+               return result;
+       }
+       else
+       {       
+               // display last known good frame
+               if(fss->lastgoodframe >= 0)
+               {
+                       if((result = fluidsim_read_cache(ob, dm, fluidmd, fss->lastgoodframe, useRenderParams))) 
+                       {
+                               return result;
+                       }
+                       
+                       // it was supposed to be a valid frame but it isn't!
+                       fss->lastgoodframe = framenr - 1;
+                       
+                       
+                       // this could be likely the case when you load an old fluidsim
+                       if((result = fluidsim_read_cache(ob, dm, fluidmd, fss->lastgoodframe, useRenderParams))) 
+                       {
+                               return result;
+                       }
+               }
+               
+               result = CDDM_copy(dm);
+
+               if(result) 
+               {
+                       return result;
+               }
+       }
+       
+       return dm;
+}
+
+/* read .bobj.gz file into a fluidsimDerivedMesh struct */
+static DerivedMesh *fluidsim_read_obj(char *filename)
+{
+       int wri,i,j;
+       float wrf;
+       int gotBytes;
+       gzFile gzf;
+       int numverts = 0, numfaces = 0;
+       DerivedMesh *dm = NULL;
+       MFace *mface;
+       MVert *mvert;
+       short *normals;
+               
+       // ------------------------------------------------
+       // get numverts + numfaces first
+       // ------------------------------------------------
+       gzf = gzopen(filename, "rb");
+       if (!gzf) 
+       {
+               return NULL;
+       }
+
+       // read numverts
+       gotBytes = gzread(gzf, &wri, sizeof(wri));
+       numverts = wri;
+       
+       // skip verts
+       for(i=0; i<numverts*3; i++) 
+       {       
+               gotBytes = gzread(gzf, &wrf, sizeof( wrf )); 
+       }
+       
+       // read number of normals
+       gotBytes = gzread(gzf, &wri, sizeof(wri));
+       
+       // skip normals
+       for(i=0; i<numverts*3; i++) 
+       {       
+               gotBytes = gzread(gzf, &wrf, sizeof( wrf )); 
+       }
+       
+       /* get no. of triangles */
+       gotBytes = gzread(gzf, &wri, sizeof(wri));
+       numfaces = wri;
+       
+       gzclose( gzf );
+       // ------------------------------------------------
+       
+       if(!numfaces || !numverts)
+               return NULL;
+       
+       gzf = gzopen(filename, "rb");
+       if (!gzf) 
+       {
+               return NULL;
+       }
+       
+       dm = CDDM_new(numverts, 0, numfaces);
+       
+       if(!dm)
+       {
+               gzclose( gzf );
+               return NULL;
+       }
+       
+       // read numverts
+       gotBytes = gzread(gzf, &wri, sizeof(wri));
+
+       // read vertex position from file
+       mvert = CDDM_get_verts(dm);
+       for(i=0; i<numverts; i++) 
+       {
+               MVert *mv = &mvert[i];
+               
+               for(j=0; j<3; j++) 
+               {
+                       gotBytes = gzread(gzf, &wrf, sizeof( wrf )); 
+                       mv->co[j] = wrf;
+               }
+       }
+
+       // should be the same as numverts
+       gotBytes = gzread(gzf, &wri, sizeof(wri));
+       if(wri != numverts) 
+       {
+               if(dm)
+                       dm->release(dm);
+               gzclose( gzf );
+               return NULL;
+       }
+       
+       normals = MEM_callocN(sizeof(short) * numverts * 3, "fluid_tmp_normals" );      
+       if(!normals)
+       {
+               if(dm)
+                       dm->release(dm);
+               gzclose( gzf );
+               return NULL;
+       }       
+       
+       // read normals from file (but don't save them yet)
+       for(i=0; i<numverts*3; i++) 
+       { 
+               gotBytes = gzread(gzf, &wrf, sizeof( wrf )); 
+               normals[i] = (short)(wrf*32767.0f);
+       }
+       
+       /* read no. of triangles */
+       gotBytes = gzread(gzf, &wri, sizeof(wri));
+       
+       if(wri!=numfaces)
+               printf("Fluidsim: error in reading data from file.\n");
+       
+       // read triangles from file
+       mface = CDDM_get_faces(dm);
+       for(i=0; i<numfaces; i++) 
+       {
+               int face[4];
+               MFace *mf = &mface[i];
+
+               gotBytes = gzread(gzf, &(face[0]), sizeof( face[0] )); 
+               gotBytes = gzread(gzf, &(face[1]), sizeof( face[1] )); 
+               gotBytes = gzread(gzf, &(face[2]), sizeof( face[2] )); 
+               face[3] = 0;
+
+               // check if 3rd vertex has index 0 (not allowed in blender)
+               if(face[2])
+               {
+                       mf->v1 = face[0];
+                       mf->v2 = face[1];
+                       mf->v3 = face[2];
+               }
+               else
+               {
+                       mf->v1 = face[1];
+                       mf->v2 = face[2];
+                       mf->v3 = face[0];
+               }
+               mf->v4 = face[3];
+               
+               test_index_face(mf, NULL, 0, 3);
+       }
+       
+       gzclose( gzf );
+       
+       CDDM_calc_edges(dm);
+       
+       CDDM_apply_vert_normals(dm, (short (*)[3])normals);
+       MEM_freeN(normals);
+       
+       // CDDM_calc_normals(result);
+
+       return dm;
+}
+
+DerivedMesh *fluidsim_read_cache(Object *ob, DerivedMesh *orgdm, FluidsimModifierData *fluidmd, int framenr, int useRenderParams)
+{
+       int displaymode = 0;
+       int curFrame = framenr - 1 /*G.scene->r.sfra*/; /* start with 0 at start frame */
+       char targetDir[FILE_MAXFILE+FILE_MAXDIR], targetFile[FILE_MAXFILE+FILE_MAXDIR];
+       FluidsimSettings *fss = fluidmd->fss;
+       DerivedMesh *dm = NULL;
+       MFace *mface;
+       int numfaces;
+       int mat_nr, flag, i;
+       
+       if(!useRenderParams) {
+               displaymode = fss->guiDisplayMode;
+       } else {
+               displaymode = fss->renderDisplayMode;
+       }
+
+       strncpy(targetDir, fss->surfdataPath, FILE_MAXDIR);
+       
+       // use preview or final mesh?
+       if(displaymode==1) 
+       {
+               // just display original object
+               return NULL;
+       } 
+       else if(displaymode==2) 
+       {
+               strcat(targetDir,"fluidsurface_preview_####");
+       } 
+       else 
+       { // 3
+               strcat(targetDir,"fluidsurface_final_####");
+       }
+       
+       BLI_convertstringcode(targetDir, G.sce);
+       BLI_convertstringframe(targetDir, curFrame); // fixed #frame-no 
+       
+       strcpy(targetFile,targetDir);
+       strcat(targetFile, ".bobj.gz");
+
+       dm = fluidsim_read_obj(targetFile);
+       
+       if(!dm) 
+       {       
+               // switch, abort background rendering when fluidsim mesh is missing
+               const char *strEnvName2 = "BLENDER_ELBEEMBOBJABORT"; // from blendercall.cpp
+               
+               if(G.background==1) {
+                       if(getenv(strEnvName2)) {
+                               int elevel = atoi(getenv(strEnvName2));
+                               if(elevel>0) {
+                                       printf("Env. var %s set, fluid sim mesh '%s' not found, aborting render...\n",strEnvName2, targetFile);
+                                       exit(1);
+                               }
+                       }
+               }
+               
+               // display org. object upon failure which is in dm
+               return NULL;
+       }
+       
+       // assign material + flags to new dm
+       mface = orgdm->getFaceArray(orgdm);
+       mat_nr = mface[0].mat_nr;
+       flag = mface[0].flag;
+       
+       mface = dm->getFaceArray(dm);
+       numfaces = dm->getNumFaces(dm);
+       for(i=0; i<numfaces; i++) 
+       {
+               mface[i].mat_nr = mat_nr;
+               mface[i].flag = flag;
+       }
+
+       // load vertex velocities, if they exist...
+       // TODO? use generate flag as loading flag as well?
+       // warning, needs original .bobj.gz mesh loading filename
+       /*
+       if(displaymode==3) 
+       {
+               readVelgz(targetFile, srcob);
+       } 
+       else 
+       {
+               // no data for preview, only clear...
+               int i,j;
+               for(i=0; i<mesh->totvert;i++) { for(j=0; j<3; j++) { srcob->fluidsimSettings->meshSurfNormals[i].co[j] = 0.; }} 
+       }*/
+       
+       return dm;
+}
+
+void fluid_get_bb(MVert *mvert, int totvert, float obmat[][4],
+                /*RET*/ float start[3], /*RET*/ float size[3] )
+{
+       float bbsx=0.0, bbsy=0.0, bbsz=0.0;
+       float bbex=1.0, bbey=1.0, bbez=1.0;
+       int i;
+       float vec[3];
+
+       VECCOPY(vec, mvert[0].co); 
+       Mat4MulVecfl(obmat, vec);
+       bbsx = vec[0]; bbsy = vec[1]; bbsz = vec[2];
+       bbex = vec[0]; bbey = vec[1]; bbez = vec[2];
+
+       for(i = 1; i < totvert; i++) {
+               VECCOPY(vec, mvert[i].co);
+               Mat4MulVecfl(obmat, vec);
+
+               if(vec[0] < bbsx){ bbsx= vec[0]; }
+               if(vec[1] < bbsy){ bbsy= vec[1]; }
+               if(vec[2] < bbsz){ bbsz= vec[2]; }
+               if(vec[0] > bbex){ bbex= vec[0]; }
+               if(vec[1] > bbey){ bbey= vec[1]; }
+               if(vec[2] > bbez){ bbez= vec[2]; }
+       }
+
+       // return values...
+       if(start) {
+               start[0] = bbsx;
+               start[1] = bbsy;
+               start[2] = bbsz;
+       } 
+       if(size) {
+               size[0] = bbex-bbsx;
+               size[1] = bbey-bbsy;
+               size[2] = bbez-bbsz;
+       }
+}
+
+//-------------------------------------------------------------------------------
+// old interface
+//-------------------------------------------------------------------------------
+
+
+
+//-------------------------------------------------------------------------------
+// file handling
+//-------------------------------------------------------------------------------
+
+void initElbeemMesh(struct Object *ob, 
+                   int *numVertices, float **vertices, 
+      int *numTriangles, int **triangles,
+      int useGlobalCoords, int modifierIndex) 
+{
+       DerivedMesh *dm = NULL;
+       MVert *mvert;
+       MFace *mface;
+       int countTris=0, i, totvert, totface;
+       float *verts;
+       int *tris;
+
+       dm = mesh_create_derived_index_render(ob, CD_MASK_BAREMESH, modifierIndex);
+       //dm = mesh_create_derived_no_deform(ob,NULL);
+
+       mvert = dm->getVertArray(dm);
+       mface = dm->getFaceArray(dm);
+       totvert = dm->getNumVerts(dm);
+       totface = dm->getNumFaces(dm);
+
+       *numVertices = totvert;
+       verts = MEM_callocN( totvert*3*sizeof(float), "elbeemmesh_vertices");
+       for(i=0; i<totvert; i++) {
+               VECCOPY( &verts[i*3], mvert[i].co);
+               if(useGlobalCoords) { Mat4MulVecfl(ob->obmat, &verts[i*3]); }
+       }
+       *vertices = verts;
+
+       for(i=0; i<totface; i++) {
+               countTris++;
+               if(mface[i].v4) { countTris++; }
+       }
+       *numTriangles = countTris;
+       tris = MEM_callocN( countTris*3*sizeof(int), "elbeemmesh_triangles");
+       countTris = 0;
+       for(i=0; i<totface; i++) {
+               int face[4];
+               face[0] = mface[i].v1;
+               face[1] = mface[i].v2;
+               face[2] = mface[i].v3;
+               face[3] = mface[i].v4;
+
+               tris[countTris*3+0] = face[0]; 
+               tris[countTris*3+1] = face[1]; 
+               tris[countTris*3+2] = face[2]; 
+               countTris++;
+               if(face[3]) { 
+                       tris[countTris*3+0] = face[0]; 
+                       tris[countTris*3+1] = face[2]; 
+                       tris[countTris*3+2] = face[3]; 
+                       countTris++;
+               }
+       }
+       *triangles = tris;
+
+       dm->release(dm);
+}
+
+/* read zipped fluidsim velocities into the co's of the fluidsimsettings normals struct */
+void readVelgz(char *filename, Object *srcob)
+{
+       int wri, i, j;
+       float wrf;
+       gzFile gzf;
+       MVert *vverts = srcob->fluidsimSettings->meshSurfNormals;
+       int len = strlen(filename);
+       Mesh *mesh = srcob->data;
+       // mesh and vverts have to be valid from loading...
+
+       // clean up in any case
+       for(i=0; i<mesh->totvert;i++) 
+       { 
+               for(j=0; j<3; j++) 
+               {
+                       vverts[i].co[j] = 0.; 
+               } 
+       } 
+       if(srcob->fluidsimSettings->domainNovecgen>0) return;
+
+       if(len<7) 
+       { 
+               return; 
+       }
+
+       // .bobj.gz , correct filename
+       // 87654321
+       filename[len-6] = 'v';
+       filename[len-5] = 'e';
+       filename[len-4] = 'l';
+
+       gzf = gzopen(filename, "rb");
+       if (!gzf)
+               return;
+
+       gzread(gzf, &wri, sizeof( wri ));
+       if(wri != mesh->totvert) 
+       {
+               return; 
+       }
+
+       for(i=0; i<mesh->totvert;i++) 
+       {
+               for(j=0; j<3; j++) 
+               {
+                       gzread(gzf, &wrf, sizeof( wrf )); 
+                       vverts[i].co[j] = wrf;
+               }
+       }
+
+       gzclose(gzf);
+}
+
+
+#endif // DISABLE_ELBEEM
+
index e8a25aaaf807997f4f55e72cad1d1d3b8bd88b56..1448ff8e41723ba37e904a9ba203e7076e074859 100644 (file)
@@ -175,7 +175,9 @@ int fluidsim_ar[FLUIDSIM_TOTIPO]= {
        FLUIDSIM_VISC, FLUIDSIM_TIME,
        FLUIDSIM_GRAV_X , FLUIDSIM_GRAV_Y , FLUIDSIM_GRAV_Z ,
        FLUIDSIM_VEL_X  , FLUIDSIM_VEL_Y  , FLUIDSIM_VEL_Z  ,
-       FLUIDSIM_ACTIVE 
+       FLUIDSIM_ACTIVE,
+       FLUIDSIM_ATTR_FORCE_STR, FLUIDSIM_ATTR_FORCE_RADIUS,
+       FLUIDSIM_VEL_FORCE_STR, FLUIDSIM_VEL_FORCE_RADIUS,
 };
 
 int part_ar[PART_TOTIPO]= {
index 4c74fe1cca2c8f551cc218669bbc7793f4456de3..2b8e97f4cbc5acf96b6fb978aebcbdc3340d01a9 100644 (file)
@@ -55,7 +55,9 @@
 #include "MEM_guardedalloc.h"
 
 #include "DNA_armature_types.h"
+#include "DNA_camera_types.h"
 #include "DNA_cloth_types.h"
+#include "DNA_curve_types.h"
 #include "DNA_effect_types.h"
 #include "DNA_material_types.h"
 #include "DNA_mesh_types.h"
@@ -66,8 +68,6 @@
 #include "DNA_particle_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_texture_types.h"
-#include "DNA_curve_types.h"
-#include "DNA_camera_types.h"
 
 #include "BLI_editVert.h"
 
 #include "BKE_main.h"
 #include "BKE_anim.h"
 #include "BKE_bad_level_calls.h"
+#include "BKE_bmesh.h"
+#include "BKE_booleanops.h"
 #include "BKE_cloth.h"
 #include "BKE_collision.h"
+#include "BKE_cdderivedmesh.h"
 #include "BKE_curve.h"
 #include "BKE_customdata.h"
-#include "BKE_global.h"
-#include "BKE_cdderivedmesh.h"
 #include "BKE_DerivedMesh.h"
-#include "BKE_booleanops.h"
 #include "BKE_displist.h"
-#include "BKE_modifier.h"
+#include "BKE_fluidsim.h"
+#include "BKE_global.h"
 #include "BKE_lattice.h"
 #include "BKE_library.h"
-#include "BKE_subsurf.h"
-#include "BKE_object.h"
-#include "BKE_mesh.h"
-#include "BKE_softbody.h"
-#include "BKE_cloth.h"
 #include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
 #include "BKE_particle.h"
 #include "BKE_pointcache.h"
+#include "BKE_softbody.h"
+#include "BKE_subsurf.h"
 #include "BKE_texture.h"
 #include "BKE_utildefines.h"
+
 #include "depsgraph_private.h"
-#include "BKE_bmesh.h"
 #include "BKE_deform.h"
 #include "BKE_shrinkwrap.h"
 
@@ -6910,6 +6911,93 @@ static DerivedMesh * explodeModifier_applyModifier(
        }
        return derivedData;
 }
+
+/* Fluidsim */
+static void fluidsimModifier_initData(ModifierData *md)
+{
+       FluidsimModifierData *fluidmd= (FluidsimModifierData*) md;
+       
+       fluidsim_init(fluidmd);
+}
+static void fluidsimModifier_freeData(ModifierData *md)
+{
+       FluidsimModifierData *fluidmd= (FluidsimModifierData*) md;
+       
+       fluidsim_free(fluidmd);
+}
+
+static void fluidsimModifier_copyData(ModifierData *md, ModifierData *target)
+{
+       FluidsimModifierData *fluidmd= (FluidsimModifierData*) md;
+       FluidsimModifierData *tfluidmd= (FluidsimModifierData*) target;
+       
+       if(tfluidmd->fss)
+               MEM_freeN(tfluidmd->fss);
+       
+       tfluidmd->fss = MEM_dupallocN(fluidmd->fss);
+}
+
+static DerivedMesh * fluidsimModifier_applyModifier(
+               ModifierData *md, Object *ob, DerivedMesh *derivedData,
+  int useRenderParams, int isFinalCalc)
+{
+       FluidsimModifierData *fluidmd= (FluidsimModifierData*) md;
+       DerivedMesh *result = NULL;
+       
+       /* check for alloc failing */
+       if(!fluidmd->fss)
+       {
+               fluidsimModifier_initData(md);
+               
+               if(!fluidmd->fss)
+                       return derivedData;
+       }
+
+       result = fluidsimModifier_do(fluidmd, ob, derivedData, useRenderParams, isFinalCalc);
+
+       if(result) 
+       { 
+               return result; 
+       }
+       
+       return derivedData;
+}
+
+static void fluidsimModifier_updateDepgraph(
+               ModifierData *md, DagForest *forest,
+      Object *ob, DagNode *obNode)
+{
+       FluidsimModifierData *fluidmd= (FluidsimModifierData*) md;
+       Base *base;
+
+       if(fluidmd && fluidmd->fss)
+       {
+               if(fluidmd->fss->type == OB_FLUIDSIM_DOMAIN)
+               {
+                       for(base = G.scene->base.first; base; base= base->next) 
+                       {
+                               Object *ob1= base->object;
+                               if(ob1 != ob)
+                               {
+                                       FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(ob1, eModifierType_Fluidsim);
+                                       
+                                       // only put dependancies from NON-DOMAIN fluids in here
+                                       if(fluidmdtmp && fluidmdtmp->fss && (fluidmdtmp->fss->type!=OB_FLUIDSIM_DOMAIN))
+                                       {
+                                               DagNode *curNode = dag_get_node(forest, ob1);
+                                               dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Fluidsim Object");
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+static int fluidsimModifier_dependsOnTime(ModifierData *md) 
+{
+       return 1;
+}
+
 /* MeshDeform */
 
 static void meshdeformModifier_initData(ModifierData *md)
@@ -7662,6 +7750,17 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type)
                mti->dependsOnTime = explodeModifier_dependsOnTime;
                mti->requiredDataMask = explodeModifier_requiredDataMask;
                mti->applyModifier = explodeModifier_applyModifier;
+               
+               mti = INIT_TYPE(Fluidsim);
+               mti->type = eModifierTypeType_Nonconstructive
+                               | eModifierTypeFlag_RequiresOriginalData;
+               mti->flags = eModifierTypeFlag_AcceptsMesh;
+               mti->initData = fluidsimModifier_initData;
+               mti->freeData = fluidsimModifier_freeData;
+               mti->copyData = fluidsimModifier_copyData;
+               mti->dependsOnTime = fluidsimModifier_dependsOnTime;
+               mti->applyModifier = fluidsimModifier_applyModifier;
+               mti->updateDepgraph = fluidsimModifier_updateDepgraph;
 
                mti = INIT_TYPE(Shrinkwrap);
                mti->type = eModifierTypeType_OnlyDeform;
index 8879b498ad9a931a6e2a7b36718b4ea0a2f881e1..3e43b7fb803551c9bff1144101b4f63acfbe494e 100644 (file)
@@ -269,7 +269,6 @@ void free_object(Object *ob)
                MEM_freeN(ob->pd);
        }
        if(ob->soft) sbFree(ob->soft);
-       if(ob->fluidsimSettings) fluidsimSettingsFree(ob->fluidsimSettings);
        if(ob->gpulamp.first) GPU_lamp_free(ob);
 }
 
@@ -1211,15 +1210,6 @@ Object *copy_object(Object *ob)
        }
        obn->soft= copy_softbody(ob->soft);
 
-       /* NT copy fluid sim setting memory */
-       if(obn->fluidsimSettings) {
-               obn->fluidsimSettings = fluidsimSettingsCopy(ob->fluidsimSettings);
-               /* copying might fail... */
-               if(obn->fluidsimSettings) {
-                       obn->fluidsimSettings->orgMesh = (Mesh *)obn->data;
-               }
-       }
-
        copy_object_particlesystems(obn, ob);
        
        obn->derivedDeform = NULL;
index 96eb4d5d8e3d14888f8d1a1a96dbda21fcebaa07..e6f736659342aee39156279c320f73cd3fdf404d 100644 (file)
@@ -4399,7 +4399,7 @@ void psys_changed_type(ParticleSystem *psys)
 }
 
 static void particles_fluid_step(Object *ob, ParticleSystem *psys, int cfra)
-{
+{      
        if(psys->particles){
                MEM_freeN(psys->particles);
                psys->particles = 0;
@@ -4408,94 +4408,98 @@ static void particles_fluid_step(Object *ob, ParticleSystem *psys, int cfra)
 
        /* fluid sim particle import handling, actual loading of particles from file */
        #ifndef DISABLE_ELBEEM
-       if( (1) && (ob->fluidsimFlag & OB_FLUIDSIM_ENABLE) &&  // broken, disabled for now!
-               (ob->fluidsimSettings)) { 
-               ParticleSettings *part = psys->part;
-               ParticleData *pa=0;
-               char *suffix  = "fluidsurface_particles_####";
-               char *suffix2 = ".gz";
-               char filename[256];
-               char debugStrBuffer[256];
-               int  curFrame = G.scene->r.cfra -1; // warning - sync with derived mesh fsmesh loading
-               int  p, j, numFileParts, totpart;
-               int readMask, activeParts = 0, fileParts = 0;
-               gzFile gzf;
-
-               if(ob==G.obedit) // off...
-                       return;
-
-               // ok, start loading
-               strcpy(filename, ob->fluidsimSettings->surfdataPath);
-               strcat(filename, suffix);
-               BLI_convertstringcode(filename, G.sce);
-               BLI_convertstringframe(filename, curFrame); // fixed #frame-no 
-               strcat(filename, suffix2);
-
-               gzf = gzopen(filename, "rb");
-               if (!gzf) {
-                       snprintf(debugStrBuffer,256,"readFsPartData::error - Unable to open file for reading '%s' \n", filename); 
-                       //elbeemDebugOut(debugStrBuffer);
-                       return;
-               }
-
-               gzread(gzf, &totpart, sizeof(totpart));
-               numFileParts = totpart;
-               totpart = (G.rendering)?totpart:(part->disp*totpart)/100;
+       {
+               FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
                
-               part->totpart= totpart;
-               part->sta=part->end = 1.0f;
-               part->lifetime = G.scene->r.efra + 1;
-
-               /* initialize particles */
-               realloc_particles(ob, psys, part->totpart);
-               initialize_all_particles(ob, psys, 0);
-
-               // set up reading mask
-               readMask = ob->fluidsimSettings->typeFlags;
-               
-               for(p=0, pa=psys->particles; p<totpart; p++, pa++) {
-                       int ptype=0;
-
-                       gzread(gzf, &ptype, sizeof( ptype )); 
-                       if(ptype&readMask) {
-                               activeParts++;
-
-                               gzread(gzf, &(pa->size), sizeof( float )); 
-
-                               pa->size /= 10.0f;
-
-                               for(j=0; j<3; j++) {
-                                       float wrf;
-                                       gzread(gzf, &wrf, sizeof( wrf )); 
-                                       pa->state.co[j] = wrf;
-                                       //fprintf(stderr,"Rj%d ",j);
-                               }
-                               for(j=0; j<3; j++) {
-                                       float wrf;
-                                       gzread(gzf, &wrf, sizeof( wrf )); 
-                                       pa->state.vel[j] = wrf;
-                               }
-
-                               pa->state.ave[0] = pa->state.ave[1] = pa->state.ave[2] = 0.0f;
-                               pa->state.rot[0] = 1.0;
-                               pa->state.rot[1] = pa->state.rot[2] = pa->state.rot[3] = 0.0;
-
-                               pa->alive = PARS_ALIVE;
-                               //if(a<25) fprintf(stderr,"FSPARTICLE debug set %s , a%d = %f,%f,%f , life=%f \n", filename, a, pa->co[0],pa->co[1],pa->co[2], pa->lifetime );
-                       } else {
-                               // skip...
-                               for(j=0; j<2*3+1; j++) {
-                                       float wrf; gzread(gzf, &wrf, sizeof( wrf )); 
+               if( fluidmd && fluidmd->fss) { 
+                       FluidsimSettings *fss= fluidmd->fss;
+                       ParticleSettings *part = psys->part;
+                       ParticleData *pa=0;
+                       char *suffix  = "fluidsurface_particles_####";
+                       char *suffix2 = ".gz";
+                       char filename[256];
+                       char debugStrBuffer[256];
+                       int  curFrame = G.scene->r.cfra -1; // warning - sync with derived mesh fsmesh loading
+                       int  p, j, numFileParts, totpart;
+                       int readMask, activeParts = 0, fileParts = 0;
+                       gzFile gzf;
+       
+                       if(ob==G.obedit) // off...
+                               return;
+       
+                       // ok, start loading
+                       strcpy(filename, fss->surfdataPath);
+                       strcat(filename, suffix);
+                       BLI_convertstringcode(filename, G.sce);
+                       BLI_convertstringframe(filename, curFrame); // fixed #frame-no 
+                       strcat(filename, suffix2);
+       
+                       gzf = gzopen(filename, "rb");
+                       if (!gzf) {
+                               snprintf(debugStrBuffer,256,"readFsPartData::error - Unable to open file for reading '%s' \n", filename); 
+                               //elbeemDebugOut(debugStrBuffer);
+                               return;
+                       }
+       
+                       gzread(gzf, &totpart, sizeof(totpart));
+                       numFileParts = totpart;
+                       totpart = (G.rendering)?totpart:(part->disp*totpart)/100;
+                       
+                       part->totpart= totpart;
+                       part->sta=part->end = 1.0f;
+                       part->lifetime = G.scene->r.efra + 1;
+       
+                       /* initialize particles */
+                       realloc_particles(ob, psys, part->totpart);
+                       initialize_all_particles(ob, psys, 0);
+       
+                       // set up reading mask
+                       readMask = fss->typeFlags;
+                       
+                       for(p=0, pa=psys->particles; p<totpart; p++, pa++) {
+                               int ptype=0;
+       
+                               gzread(gzf, &ptype, sizeof( ptype )); 
+                               if(ptype&readMask) {
+                                       activeParts++;
+       
+                                       gzread(gzf, &(pa->size), sizeof( float )); 
+       
+                                       pa->size /= 10.0f;
+       
+                                       for(j=0; j<3; j++) {
+                                               float wrf;
+                                               gzread(gzf, &wrf, sizeof( wrf )); 
+                                               pa->state.co[j] = wrf;
+                                               //fprintf(stderr,"Rj%d ",j);
+                                       }
+                                       for(j=0; j<3; j++) {
+                                               float wrf;
+                                               gzread(gzf, &wrf, sizeof( wrf )); 
+                                               pa->state.vel[j] = wrf;
+                                       }
+       
+                                       pa->state.ave[0] = pa->state.ave[1] = pa->state.ave[2] = 0.0f;
+                                       pa->state.rot[0] = 1.0;
+                                       pa->state.rot[1] = pa->state.rot[2] = pa->state.rot[3] = 0.0;
+       
+                                       pa->alive = PARS_ALIVE;
+                                       //if(a<25) fprintf(stderr,"FSPARTICLE debug set %s , a%d = %f,%f,%f , life=%f \n", filename, a, pa->co[0],pa->co[1],pa->co[2], pa->lifetime );
+                               } else {
+                                       // skip...
+                                       for(j=0; j<2*3+1; j++) {
+                                               float wrf; gzread(gzf, &wrf, sizeof( wrf )); 
+                                       }
                                }
+                               fileParts++;
                        }
-                       fileParts++;
-               }
-               gzclose( gzf );
-
-               totpart = psys->totpart = activeParts;
-               snprintf(debugStrBuffer,256,"readFsPartData::done - particles:%d, active:%d, file:%d, mask:%d  \n", psys->totpart,activeParts,fileParts,readMask);
-               elbeemDebugOut(debugStrBuffer);
-       } // fluid sim particles done
+                       gzclose( gzf );
+       
+                       totpart = psys->totpart = activeParts;
+                       snprintf(debugStrBuffer,256,"readFsPartData::done - particles:%d, active:%d, file:%d, mask:%d  \n", psys->totpart,activeParts,fileParts,readMask);
+                       elbeemDebugOut(debugStrBuffer);
+               } // fluid sim particles done
+       }
        #endif // DISABLE_ELBEEM
 }
 
index 43805959e620f2a5d456c79d14dba472c5f514f9..5941966c37b49f5d58124b1c3e1d082749c2edea 100644 (file)
@@ -123,6 +123,17 @@ void BKE_ptcache_id_from_cloth(PTCacheID *pid, Object *ob, ClothModifierData *cl
        pid->cache= clmd->point_cache;
 }
 
+void BKE_ptcache_id_from_fluidsim(PTCacheID *pid, Object *ob, FluidsimModifierData *fluidmd)
+{
+       memset(pid, 0, sizeof(PTCacheID));
+
+       pid->ob= ob;
+       pid->data= fluidmd;
+       pid->type= PTCACHE_TYPE_FLUIDSIM;
+       pid->stack_index= modifiers_indexInObject(ob, (ModifierData *)fluidmd);
+       pid->cache= fluidmd->point_cache;
+}
+
 void BKE_ptcache_ids_from_object(ListBase *lb, Object *ob)
 {
        PTCacheID *pid;
@@ -155,6 +166,13 @@ void BKE_ptcache_ids_from_object(ListBase *lb, Object *ob)
                        BKE_ptcache_id_from_cloth(pid, ob, (ClothModifierData*)md);
                        BLI_addtail(lb, pid);
                }
+               /*
+               else if(md->type == eModifierType_Fluidsim) {
+                       pid= MEM_callocN(sizeof(PTCacheID), "PTCacheID");
+                       BKE_ptcache_id_from_fluidsim(pid, ob, (FluidsimModifierData*)md);
+                       BLI_addtail(lb, pid);
+               }
+               */
        }
 }
 
index a2b1828c39948b7023a6c4844c10d271ff1acb07..b96ca9f3739127b042fb63468520c428dd4e5beb 100644 (file)
@@ -37,6 +37,9 @@
 #ifdef WIN32
 #include "winsock2.h"
 #include "BLI_winstuff.h"
+#ifndef INT_MAX
+#include "limits.h"
+#endif
 #endif
 
 #include <stdio.h> // for printf fopen fwrite fclose sprintf FILE
@@ -3047,9 +3050,12 @@ static void lib_link_object(FileData *fd, Main *main)
                                }
                                act= act->next;
                        }
-
-                       if(ob->fluidsimSettings) {
-                               ob->fluidsimSettings->ipo = newlibadr_us(fd, ob->id.lib, ob->fluidsimSettings->ipo);
+                       
+                       {
+                               FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
+                               
+                               if(fluidmd && fluidmd->fss) 
+                                       fluidmd->fss->ipo = newlibadr_us(fd, ob->id.lib, fluidmd->fss->ipo);
                        }
                        
                        /* texture field */
@@ -3124,6 +3130,11 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
                        }
                        
                }
+               else if (md->type==eModifierType_Fluidsim) {
+                       FluidsimModifierData *fluidmd = (FluidsimModifierData*) md;
+                       
+                       fluidmd->fss= newdataadr(fd, fluidmd->fss);
+               }
                else if (md->type==eModifierType_Collision) {
                        
                        CollisionModifierData *collmd = (CollisionModifierData*) md;
@@ -3310,13 +3321,6 @@ static void direct_link_object(FileData *fd, Object *ob)
                        direct_link_pointcache(fd, sb->pointcache);
        }
        ob->fluidsimSettings= newdataadr(fd, ob->fluidsimSettings); /* NT */
-       if(ob->fluidsimSettings) {
-               // reinit mesh pointers
-               ob->fluidsimSettings->orgMesh = NULL; //ob->data;
-               ob->fluidsimSettings->meshSurface = NULL;
-               ob->fluidsimSettings->meshBB = NULL;
-               ob->fluidsimSettings->meshSurfNormals = NULL;
-       }
 
        link_list(fd, &ob->particlesystem);
        direct_link_particlesystems(fd,&ob->particlesystem);
@@ -7610,8 +7614,12 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                                        }
                                }
 
-                               if(ob->fluidsimSettings && ob->fluidsimSettings->type == OB_FLUIDSIM_PARTICLE)
-                                       part->type = PART_FLUID;
+                               
+                               {
+                                       FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
+                                       if(fluidmd && fluidmd->fss && fluidmd->fss->type == OB_FLUIDSIM_PARTICLE)
+                                               part->type = PART_FLUID;
+                               }
 
                                free_effects(&ob->effect);
 
@@ -7756,6 +7764,29 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                        la->sun_intensity = 1.0;
                }
        }
+       
+       // convert fluids to modifier
+       if(main->versionfile <= 246 && main->subversionfile < 1)
+       {
+               Object *ob;
+               
+               for(ob = main->object.first; ob; ob= ob->id.next) {
+                       if(ob->fluidsimSettings)
+                       {
+                               FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifier_new(eModifierType_Fluidsim);
+                               BLI_addhead(&ob->modifiers, (ModifierData *)fluidmd);
+                               
+                               MEM_freeN(fluidmd->fss);
+                               fluidmd->fss = MEM_dupallocN(ob->fluidsimSettings);
+                               fluidmd->fss->ipo = newlibadr_us(fd, ob->id.lib, ob->fluidsimSettings->ipo);
+                               MEM_freeN(ob->fluidsimSettings);
+                               
+                               fluidmd->fss->lastgoodframe = INT_MAX;
+                               fluidmd->fss->flag = 0;
+                       }
+               }
+       }
+       
 
        if(main->versionfile <= 246 && main->subversionfile < 1){
                Mesh *me;
index 3bc6799d616b292c9c913460264f56d8d3f81ce6..179e548747dede9f3142e0be425d6a2ab0167fe3 100644 (file)
@@ -852,6 +852,11 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
                        writestruct(wd, DATA, "ClothCollSettings", 1, clmd->coll_parms);
                        writestruct(wd, DATA, "PointCache", 1, clmd->point_cache);
                } 
+               else if(md->type==eModifierType_Fluidsim) {
+                       FluidsimModifierData *fluidmd = (FluidsimModifierData*) md;
+                       
+                       writestruct(wd, DATA, "FluidsimSettings", 1, fluidmd->fss);
+               } 
                else if (md->type==eModifierType_Collision) {
                        
                        /*
@@ -917,7 +922,6 @@ static void write_objects(WriteData *wd, ListBase *idbase)
                        writestruct(wd, DATA, "PartDeflect", 1, ob->pd);
                        writestruct(wd, DATA, "SoftBody", 1, ob->soft);
                        if(ob->soft) writestruct(wd, DATA, "PointCache", 1, ob->soft->pointcache);
-                       writestruct(wd, DATA, "FluidsimSettings", 1, ob->fluidsimSettings); // NT
                        
                        write_particlesystems(wd, &ob->particlesystem);
                        write_modifiers(wd, &ob->modifiers);
index e7f46b81935c84d173b337e3639d19804165f4dc..914b22b9ccc615978a45c8fa9013e2774261e06a 100644 (file)
@@ -344,8 +344,8 @@ typedef short IPO_Channel;
 /* ******************** */
 /* fluidsim ipos NT */
 
-#define FLUIDSIM_TOTIPO        9
-#define FLUIDSIM_TOTNAM        9
+#define FLUIDSIM_TOTIPO        13
+#define FLUIDSIM_TOTNAM        13
 
 #define FLUIDSIM_VISC   1
 #define FLUIDSIM_TIME   2
@@ -360,6 +360,11 @@ typedef short IPO_Channel;
 
 #define FLUIDSIM_ACTIVE 9
 
+#define FLUIDSIM_ATTR_FORCE_STR        10
+#define FLUIDSIM_ATTR_FORCE_RADIUS     11
+#define FLUIDSIM_VEL_FORCE_STR                 12
+#define FLUIDSIM_VEL_FORCE_RADIUS      13
+
 /* ******************** */
 /* particle ipos */
 #define PART_TOTIPO            25
index 9599cc1d247f6008bb0c81b8be7ced066269e551..6b2f4ed96b2788d9c9be82ab6af20e1265d8fca1 100644 (file)
@@ -35,6 +35,7 @@ typedef enum ModifierType {
        eModifierType_Cloth,
        eModifierType_Collision,
        eModifierType_Bevel,
+       eModifierType_Fluidsim,
        eModifierType_Shrinkwrap,
        NUM_MODIFIER_TYPES
 } ModifierType;
@@ -492,6 +493,13 @@ typedef struct ExplodeModifierData {
        float protect;
 } ExplodeModifierData;
 
+typedef struct FluidsimModifierData {
+       ModifierData modifier;
+       
+       struct FluidsimSettings *fss; /* definition is is DNA_object_fluidsim.h */
+       struct PointCache *point_cache; /* definition is in DNA_object_force.h */
+} FluidsimModifierData;
+
 typedef struct ShrinkwrapModifierData {
        ModifierData modifier;
 
index 9b1e4fb56cbdb7ada710217581161e7693d89cc5..fde1ce57875827fc8d9d2e25b272c68179eecffd 100644 (file)
@@ -108,7 +108,7 @@ typedef struct FluidsimSettings {
        float surfaceSmoothing;
        /* number of surface subdivisions*/
        int surfaceSubdivs;
-       int unusedDNADummy;
+       int flag; /* GUI flags */
 
        /* particle display - size scaling, and alpha influence */
        float particleInfSize, particleInfAlpha;
@@ -117,6 +117,18 @@ typedef struct FluidsimSettings {
 
        /* save fluidsurface normals in mvert.no, and surface vertex velocities (if available) in mvert.co */
        struct MVert *meshSurfNormals;
+       
+       /* Fluid control settings */
+       float cpsTimeStart;
+       float cpsTimeEnd;
+       float cpsQuality;
+       
+       float attractforceStrength;
+       float attractforceRadius;
+       float velocityforceStrength;
+       float velocityforceRadius;
+
+       int lastgoodframe;
 
 } FluidsimSettings;
 
@@ -128,6 +140,7 @@ typedef struct FluidsimSettings {
 #define OB_FLUIDSIM_INFLOW      16
 #define OB_FLUIDSIM_OUTFLOW     32
 #define OB_FLUIDSIM_PARTICLE    64
+#define OB_FLUIDSIM_CONTROL    128
 
 #define OB_TYPEFLAG_START       0
 #define OB_FSGEO_THIN           (1<<(OB_TYPEFLAG_START+1))
@@ -145,6 +158,9 @@ typedef struct FluidsimSettings {
 #define OB_FSPART_NEWPART (1<<3)
 #define OB_FSPART_FLOAT   (1<<4)
 
+// new fluid bit flags for fss->flags - dg
+#define OB_FLUIDSIM_REVERSE (1 << 0)
+
 #ifdef __cplusplus
 }
 #endif