merge from trunk #37722
[blender-staging.git] / intern / elbeem / intern / simulation_object.cpp
1 /** \file elbeem/intern/simulation_object.cpp
2  *  \ingroup elbeem
3  */
4 /******************************************************************************
5  *
6  * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
7  * Copyright 2003-2006 Nils Thuerey
8  *
9  * Basic interface for all simulation modules
10  *
11  *****************************************************************************/
12
13 #include "simulation_object.h"
14 #include "solver_interface.h"
15 #include "ntl_bsptree.h"
16 #include "ntl_ray.h"
17 #include "ntl_world.h"
18 #include "solver_interface.h"
19 #include "particletracer.h"
20 #include "elbeem.h"
21
22 #ifdef _WIN32
23 #else
24 #include <sys/time.h>
25 #endif
26
27
28 //! lbm factory functions
29 LbmSolverInterface* createSolver();
30
31
32 /******************************************************************************
33  * Constructor
34  *****************************************************************************/
35 SimulationObject::SimulationObject() :
36         ntlGeometryShader(),
37         mGeoStart(-100.0), mGeoEnd(100.0),
38         mpGiTree(NULL), mpGiObjects(NULL),
39         mpGlob(NULL),
40         mPanic( false ),
41         mDebugType( 1 /* =FLUIDDISPNothing*/ ),
42         mpLbm(NULL), mpParam( NULL ),
43         mShowSurface(true), mShowParticles(false),
44         mSelectedCid( NULL ),
45         mpElbeemSettings( NULL )
46
47 {
48         mpParam = new Parametrizer();
49         //for(int i=0; i<MAX_DEBDISPSET; i++) { mDebDispSet[i].type  = (i); mDebDispSet[i].on    = false; mDebDispSet[i].scale = 1.0; }
50
51         // reset time
52         mTime                                           = 0.0;
53 }
54
55
56 /******************************************************************************
57  * Destructor
58  *****************************************************************************/
59 SimulationObject::~SimulationObject()
60 {
61         if(mpGiTree)         delete mpGiTree;
62         if(mpElbeemSettings) delete mpElbeemSettings;
63         if(mpLbm)            delete mpLbm;
64         if(mpParam)          delete mpParam;
65         if(mpParts)          delete mpParts;
66         debMsgStd("SimulationObject",DM_MSG,"El'Beem Done!\n",10);
67 }
68
69
70
71 /*****************************************************************************/
72 /*! init tree for certain geometry init */
73 /*****************************************************************************/
74 void SimulationObject::initGeoTree() {
75         // unused!! overriden by solver interface       
76         if(mpGlob == NULL) { 
77                 errFatal("SimulationObject::initGeoTree error","Requires globals!", SIMWORLD_INITERROR); 
78                 return;
79         }
80         ntlScene *scene = mpGlob->getSimScene();
81         mpGiObjects = scene->getObjects();
82
83         if(mpGiTree != NULL) delete mpGiTree;
84         char treeFlag = (1<<(mGeoInitId+4));
85         mpGiTree = new ntlTree( 20, 4, // warning - fixed values for depth & maxtriangles here...
86                                                                                                 scene, treeFlag );
87         // unused!! overriden by solver interface       
88 }
89
90 /*****************************************************************************/
91 /*! destroy tree etc. when geometry init done */
92 /*****************************************************************************/
93 void SimulationObject::freeGeoTree() {
94         if(mpGiTree != NULL) delete mpGiTree;
95 }
96
97
98
99 // copy & remember settings for later use
100 void SimulationObject::copyElbeemSettings(elbeemSimulationSettings *settings) {
101         mpElbeemSettings = new elbeemSimulationSettings;
102         *mpElbeemSettings = *settings;
103
104         mGeoInitId = settings->domainId+1;
105         debMsgStd("SimulationObject",DM_MSG,"mGeoInitId="<<mGeoInitId<<", domainId="<<settings->domainId, 8);
106 }
107
108 /******************************************************************************
109  * simluation interface: initialize simulation using the given configuration file 
110  *****************************************************************************/
111 extern int glob_mpnum;
112 int SimulationObject::initializeLbmSimulation(ntlRenderGlobals *glob)
113 {
114         if(! isSimworldOk() ) return 1;
115         
116         // already inited?
117         if(mpLbm) return 0;
118         
119         mpGlob = glob;
120         if(!getVisible()) {
121                 mpAttrs->setAllUsed();
122                 return 0;
123         }
124
125
126         mGeoInitId = mpAttrs->readInt("geoinitid", mGeoInitId,"LbmSolverInterface", "mGeoInitId", false);
127         //mDimension, mSolverType are deprecated
128         string mSolverType(""); 
129         mSolverType = mpAttrs->readString("solver", mSolverType, "SimulationObject","mSolverType", false ); 
130
131         mpLbm = createSolver(); 
132   /* check lbm pointer */
133         if(mpLbm == NULL) {
134                 errFatal("SimulationObject::initializeLbmSimulation","Unable to init LBM solver! ", SIMWORLD_INITERROR);
135                 return 2;
136         }
137         debMsgStd("SimulationObject::initialized",DM_MSG,"IdStr:"<<mpLbm->getIdString() <<" LBM solver! ", 2);
138
139         mpParts = new ParticleTracer();
140
141         // for non-param simulations
142         mpLbm->setParametrizer( mpParam );
143         mpParam->setAttrList( getAttributeList() );
144         // not needed.. done in solver_init: mpParam->setSize ... in solver_interface
145         mpParam->parseAttrList();
146
147         mpLbm->setAttrList( getAttributeList() );
148         mpLbm->setSwsAttrList( getSwsAttributeList() );
149         mpLbm->parseAttrList();
150         mpParts->parseAttrList( getAttributeList() );
151
152         if(! isSimworldOk() ) return 3;
153         mpParts->setName( getName() + "_part" );
154         mpParts->initialize( glob );
155         if(! isSimworldOk() ) return 4;
156         
157         // init material settings
158         string matMc("default");
159         matMc = mpAttrs->readString("material_surf", matMc, "SimulationObject","matMc", false );
160         mShowSurface   = mpAttrs->readInt("showsurface", mShowSurface, "SimulationObject","mShowSurface", false ); 
161         mShowParticles = mpAttrs->readInt("showparticles", mShowParticles, "SimulationObject","mShowParticles", false ); 
162
163         checkBoundingBox( mGeoStart, mGeoEnd, "SimulationObject::initializeSimulation" );
164         mpLbm->setLbmInitId( mGeoInitId );
165         mpLbm->setGeoStart( mGeoStart );
166         mpLbm->setGeoEnd( mGeoEnd );
167         mpLbm->setRenderGlobals( mpGlob );
168         mpLbm->setName( getName() + "_lbm" );
169         mpLbm->setParticleTracer( mpParts );
170         if(mpElbeemSettings) {
171                 // set further settings from API struct init
172                 if(mpElbeemSettings->outputPath) this->mOutFilename = string(mpElbeemSettings->outputPath);
173                 mpLbm->initDomainTrafo( mpElbeemSettings->surfaceTrafo );
174                 mpLbm->setSmoothing(1.0 * mpElbeemSettings->surfaceSmoothing, 1.0 * mpElbeemSettings->surfaceSmoothing);
175                 mpLbm->setIsoSubdivs(mpElbeemSettings->surfaceSubdivs);
176                 mpLbm->setSizeX(mpElbeemSettings->resolutionxyz);
177                 mpLbm->setSizeY(mpElbeemSettings->resolutionxyz);
178                 mpLbm->setSizeZ(mpElbeemSettings->resolutionxyz);
179                 mpLbm->setPreviewSize(mpElbeemSettings->previewresxyz);
180                 mpLbm->setRefinementDesired(mpElbeemSettings->maxRefine);
181                 mpLbm->setGenerateParticles(mpElbeemSettings->generateParticles);
182                 // set initial particles
183                 mpParts->setNumInitialParticles(mpElbeemSettings->numTracerParticles);
184                 
185                 // surface generation flag
186                 mpLbm->setSurfGenSettings(mpElbeemSettings->mFsSurfGenSetting);
187
188                 string dinitType = string("no");
189                 if     (mpElbeemSettings->domainobsType==FLUIDSIM_OBSTACLE_PARTSLIP) dinitType = string("part"); 
190                 else if(mpElbeemSettings->domainobsType==FLUIDSIM_OBSTACLE_FREESLIP) dinitType = string("free"); 
191                 else /*if(mpElbeemSettings->domainobsType==FLUIDSIM_OBSTACLE_NOSLIP)*/ dinitType = string("no"); 
192                 mpLbm->setDomainBound(dinitType);
193                 mpLbm->setDomainPartSlip(mpElbeemSettings->domainobsPartslip);
194                 mpLbm->setDumpVelocities(mpElbeemSettings->generateVertexVectors);
195                 mpLbm->setFarFieldSize(mpElbeemSettings->farFieldSize);
196                 debMsgStd("SimulationObject::initialize",DM_MSG,"Added domain bound: "<<dinitType<<" ps="<<mpElbeemSettings->domainobsPartslip<<" vv"<<mpElbeemSettings->generateVertexVectors<<","<<mpLbm->getDumpVelocities(), 9 );
197
198                 debMsgStd("SimulationObject::initialize",DM_MSG,"Set ElbeemSettings values "<<mpLbm->getGenerateParticles(),10);
199         }
200
201         if(! mpLbm->initializeSolverMemory()   )         { errMsg("SimulationObject::initialize","initializeSolverMemory failed"); mPanic=true; return 10; }
202         if(checkCallerStatus(FLUIDSIM_CBSTATUS_STEP, 0)) { errMsg("SimulationObject::initialize","initializeSolverMemory status"); mPanic=true; return 11; } 
203         if(! mpLbm->initializeSolverGrids()    )         { errMsg("SimulationObject::initialize","initializeSolverGrids  failed"); mPanic=true; return 12; }
204         if(checkCallerStatus(FLUIDSIM_CBSTATUS_STEP, 0)) { errMsg("SimulationObject::initialize","initializeSolverGrids  status"); mPanic=true; return 13; } 
205         if(! mpLbm->initializeSolverPostinit() )         { errMsg("SimulationObject::initialize","initializeSolverPostin failed"); mPanic=true; return 14; }
206         if(checkCallerStatus(FLUIDSIM_CBSTATUS_STEP, 0)) { errMsg("SimulationObject::initialize","initializeSolverPostin status"); mPanic=true; return 15; } 
207
208         // print cell type stats
209         bool printStats = true;
210         if(glob_mpnum>0) printStats=false; // skip in this case
211         if(printStats) {
212                 const int jmax = sizeof(CellFlagType)*8;
213                 int totalCells = 0;
214                 int flagCount[jmax];
215                 for(int j=0; j<jmax ; j++) flagCount[j] = 0;
216                 int diffInits = 0;
217                 LbmSolverInterface::CellIdentifier cid = mpLbm->getFirstCell();
218                 for(; mpLbm->noEndCell( cid );
219                                         mpLbm->advanceCell( cid ) ) {
220                         int flag = mpLbm->getCellFlag(cid,0);
221                         int flag2 = mpLbm->getCellFlag(cid,1);
222                         if(flag != flag2) {
223                                 diffInits++;
224                         }
225                         for(int j=0; j<jmax ; j++) {
226                                 if( flag&(1<<j) ) flagCount[j]++;
227                         }
228                         totalCells++;
229                 }
230                 mpLbm->deleteCellIterator( &cid );
231
232                 char charNl = '\n';
233                 debugOutNnl("SimulationObject::initializeLbmSimulation celltype stats: " <<charNl, 5);
234                 debugOutNnl("no. of cells = "<<totalCells<<", "<<charNl ,5);
235                 for(int j=0; j<jmax ; j++) {
236                         std::ostringstream out;
237                         if(flagCount[j]>0) {
238                                 out<<"\t" << flagCount[j] <<" x "<< convertCellFlagType2String( (CellFlagType)(1<<j) ) <<", " << charNl;
239                                 debugOutNnl(out.str(), 5);
240                         }
241                 }
242                 // compute dist. of empty/bnd - fluid - if
243                 // cfEmpty   = (1<<0), cfBnd  = (1<< 2), cfFluid   = (1<<10), cfInter   = (1<<11),
244                 if(1){
245                         std::ostringstream out;
246                         out.precision(2); out.width(4);
247                         int totNum = flagCount[1]+flagCount[2]+flagCount[7]+flagCount[8];
248                         double ebFrac = (double)(flagCount[1]+flagCount[2]) / totNum;
249                         double flFrac = (double)(flagCount[7]) / totNum;
250                         double ifFrac = (double)(flagCount[8]) / totNum;
251                         //???
252                         out<<"\tFractions: [empty/bnd - fluid - interface - ext. if]  =  [" << ebFrac<<" - " << flFrac<<" - " << ifFrac<<"] "<< charNl;
253
254                         if(diffInits > 0) {
255                                 debMsgStd("SimulationObject::initializeLbmSimulation",DM_MSG,"celltype Warning: Diffinits="<<diffInits<<"!" , 5);
256                         }
257                         debugOutNnl(out.str(), 5);
258                 }
259         } // cellstats
260
261         // might be modified by mpLbm
262         //mpParts->setStart( mGeoStart );?  mpParts->setEnd( mGeoEnd );?
263         mpParts->setStart( mpLbm->getGeoStart() );
264         mpParts->setEnd(   mpLbm->getGeoEnd()   );
265         mpParts->setCastShadows( false );
266         mpParts->setReceiveShadows( false );
267         mpParts->searchMaterial( glob->getMaterials() );
268
269         // this has to be inited here - before, the values might be unknown
270         IsoSurface *surf = mpLbm->getSurfaceGeoObj();
271         if(surf) {
272                 surf->setName( "final" ); // final surface mesh 
273                 // warning - this might cause overwriting effects for multiple sims and geom dump...
274                 surf->setCastShadows( true );
275                 surf->setReceiveShadows( false );
276                 surf->searchMaterial( glob->getMaterials() );
277                 if(mShowSurface) mObjects.push_back( surf );
278         }
279         
280 #ifdef ELBEEM_PLUGIN
281         mShowParticles=1; // for e.g. dumping
282 #endif // ELBEEM_PLUGIN
283         if((mpLbm->getGenerateParticles()>0.0)||(mpParts->getNumInitialParticles()>0)) {
284                 mShowParticles=1;
285                 mpParts->setDumpParts(true);
286         }
287                 //debMsgStd("SimulationObject::init",DM_NOTIFY,"Using envvar ELBEEM_DUMPPARTICLE to set mShowParticles, DEBUG!",1);
288         //}  // DEBUG ENABLE!!!!!!!!!!
289         if(mShowParticles) {
290                 mObjects.push_back(mpParts);
291         }
292
293         // add objects to display for debugging (e.g. levelset particles)
294         vector<ntlGeometryObject *> debugObjs = mpLbm->getDebugObjects();
295         for(size_t i=0;i<debugObjs.size(); i++) {
296                 debugObjs[i]->setCastShadows( false );
297                 debugObjs[i]->setReceiveShadows( false );
298                 debugObjs[i]->searchMaterial( glob->getMaterials() );
299                 mObjects.push_back( debugObjs[i] );
300                 debMsgStd("SimulationObject::init",DM_NOTIFY,"Added debug obj "<<debugObjs[i]->getName(), 10 );
301         }
302         return 0;
303 }
304
305 /*! set current frame */
306 void SimulationObject::setFrameNum(int num) {
307         // advance parametrizer
308         mpParam->setFrameNum(num);
309 }
310
311 /******************************************************************************
312  * simluation interface: advance simulation another step (whatever delta time that might be) 
313  *****************************************************************************/
314 void SimulationObject::step( void )
315 {
316         if(mpParam->getCurrentAniFrameTime()>0.0) {
317                 // dont advance for stopped time
318                 mpLbm->step();
319                 mTime += mpParam->getTimestep();
320                 //if(mTime>0.001) { errMsg("DEBUG!!!!!!!!","quit mlsu..."); xit(1); } // PROFILE DEBUG TEST!
321         }
322         if(mpLbm->getPanic()) mPanic = true;
323
324         checkCallerStatus(FLUIDSIM_CBSTATUS_STEP, 0);
325         //if((mpElbeemSettings)&&(mpElbeemSettings->runsimCallback)) {
326                 //int ret = (mpElbeemSettings->runsimCallback)(mpElbeemSettings->runsimUserData, FLUIDSIM_CBSTATUS_STEP, 0);
327                 //errMsg("runSimulationCallback cbtest1"," "<<this->getName()<<" ret="<<ret);
328         //}
329   //debMsgStd("SimulationObject::step",DM_MSG," Sim '"<<mName<<"' stepped to "<<mTime<<" (stept="<<(mpParam->getTimestep())<<", framet="<<getFrameTime()<<") ", 10);
330 }
331 /*! prepare visualization of simulation for e.g. raytracing */
332 void SimulationObject::prepareVisualization( void ) {
333         if(mPanic) return;
334         mpLbm->prepareVisualization();
335 }
336
337
338 /******************************************************************************/
339 /* get current start simulation time */
340 double SimulationObject::getStartTime( void ) {
341         //return mpParam->calculateAniStart();
342         return mpParam->getAniStart();
343 }
344 /* get time for a single animation frame */
345 double SimulationObject::getFrameTime( int frame ) {
346         return mpParam->getAniFrameTime(frame);
347 }
348 /* get time for a single time step  */
349 double SimulationObject::getTimestep( void ) {
350         return mpParam->getTimestep();
351 }
352
353
354 /******************************************************************************
355  * return a pointer to the geometry object of this simulation 
356  *****************************************************************************/
357 //ntlGeometryObject *SimulationObject::getGeometry() { return mpMC; }
358 vector<ntlGeometryObject *>::iterator 
359 SimulationObject::getObjectsBegin()
360 {
361         return mObjects.begin();
362 }
363 vector<ntlGeometryObject *>::iterator 
364 SimulationObject::getObjectsEnd()
365 {
366         return mObjects.end();
367 }
368
369
370
371
372
373 /******************************************************************************
374  * GUI - display debug info 
375  *****************************************************************************/
376
377 void SimulationObject::drawDebugDisplay() {
378 #ifndef NOGUI
379         if(!getVisible()) return;
380
381         //if( mDebugType > (MAX_DEBDISPSET-1) ){ errFatal("SimulationObject::drawDebugDisplay","Invalid debug type!", SIMWORLD_GENERICERROR); return; }
382         //mDebDispSet[ mDebugType ].on = true;
383         //errorOut( mDebugType <<"//"<< mDebDispSet[mDebugType].type );
384         mpLbm->debugDisplay( mDebugType );
385
386         //::lbmMarkedCellDisplay<>( mpLbm );
387         mpLbm->lbmMarkedCellDisplay();
388 #endif
389 }
390
391 /* GUI - display interactive info  */
392 void SimulationObject::drawInteractiveDisplay()
393 {
394 #ifndef NOGUI
395         if(!getVisible()) return;
396         if(mSelectedCid) {
397                 // in debugDisplayNode if dispset is on is ignored...
398                 mpLbm->debugDisplayNode( FLUIDDISPGrid, mSelectedCid );
399         }
400 #endif
401 }
402
403
404 /*******************************************************************************/
405 // GUI - handle mouse movement for selection 
406 /*******************************************************************************/
407 void SimulationObject::setMousePos(int x,int y, ntlVec3Gfx org, ntlVec3Gfx dir)
408 {
409         normalize( dir );
410         // assume 2D sim is in XY plane...
411         
412         double zplane = (mGeoEnd[2]-mGeoStart[2])*0.5;
413         double zt = (zplane-org[2]) / dir[2];
414         ntlVec3Gfx pos(
415                         org[0]+ dir[0] * zt,
416                         org[1]+ dir[1] * zt, 0.0);
417
418         mSelectedCid = mpLbm->getCellAt( pos );
419         //errMsg("SMP ", mName<< x<<" "<<y<<" - "<<dir );
420         x = y = 0; // remove warning
421 }
422                         
423
424 void SimulationObject::setMouseClick()
425 {
426         if(mSelectedCid) {
427                 //::debugPrintNodeInfo<>( mpLbm, mSelectedCid, mpLbm->getNodeInfoString() );
428                 mpLbm->debugPrintNodeInfo( mSelectedCid );
429         }
430 }
431
432 /*! notify object that dump is in progress (e.g. for field dump) */
433 void SimulationObject::notifyShaderOfDump(int dumptype, int frameNr,char *frameNrStr,string outfilename) {
434         if(!mpLbm) return;
435
436         mpLbm->notifySolverOfDump(dumptype, frameNr,frameNrStr,outfilename);
437         checkCallerStatus(FLUIDSIM_CBSTATUS_NEWFRAME, frameNr);
438 }
439
440 /*! check status (e.g. stop/abort) from calling program, returns !=0 if sth. happened... */
441 int SimulationObject::checkCallerStatus(int status, int frame) {
442         //return 0; // DEBUG
443         int ret = 0;
444         if((mpElbeemSettings)&&(mpElbeemSettings->runsimCallback)) {
445                 ret = (mpElbeemSettings->runsimCallback)(mpElbeemSettings->runsimUserData, status,frame);
446                 if(ret!=FLUIDSIM_CBRET_CONTINUE) {
447                         if(ret==FLUIDSIM_CBRET_STOP) {
448                                 debMsgStd("SimulationObject::notifySolverOfDump",DM_NOTIFY,"Got stop signal from caller",1);
449                                 setElbeemState( SIMWORLD_STOP );
450                         }
451                         else if(ret==FLUIDSIM_CBRET_ABORT) {
452                                 errFatal("SimulationObject::notifySolverOfDump","Got abort signal from caller, aborting...", SIMWORLD_GENERICERROR );
453                                 mPanic = 1;
454                         }
455                         else {
456                                 errMsg("SimulationObject::notifySolverOfDump","Invalid callback return value: "<<ret<<", ignoring... ");
457                         }
458                 }
459         }
460
461         //debMsgStd("SimulationObject::checkCallerStatus",DM_MSG, "s="<<status<<",f="<<frame<<" "<<this->getName()<<" ret="<<ret);
462         if(isSimworldOk()) return 0;
463         return 1;
464 }
465