1 /******************************************************************************
3 * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
4 * All code distributed as part of El'Beem is covered by the version 2 of the
5 * GNU General Public License. See the file COPYING for details.
6 * Copyright 2003-2006 Nils Thuerey
8 * Main program functions
12 #include "ntl_blenderdumper.h"
13 extern "C" void elbeemCheckDebugEnv(void);
15 #include "ntl_world.h"
16 #include "ntl_geometrymodel.h"
18 /*****************************************************************************/
19 // region of interest global vars
20 // currently used by e.g. fsgr solver
21 double guiRoiSX = 0.0;
22 double guiRoiSY = 0.0;
23 double guiRoiSZ = 0.0;
24 double guiRoiEX = 1.0;
25 double guiRoiEY = 1.0;
26 double guiRoiEZ = 1.0;
27 int guiRoiMaxLev=6, guiRoiMinLev=0;
29 //! global raytracer pointer (=world)
30 ntlWorld *gpWorld = NULL;
36 // reset elbeemSimulationSettings struct with defaults
38 void elbeemResetSettings(elbeemSimulationSettings *set) {
42 for(int i=0 ; i<3; i++) set->geoStart[i] = 0.0;
43 for(int i=0 ; i<3; i++) set->geoSize[i] = 1.0;
44 set->resolutionxyz = 64;
45 set->previewresxyz = 24;
47 set->viscosity = 0.000001;
49 for(int i=0 ; i<2; i++) set->gravity[i] = 0.0;
50 set->gravity[2] = -9.81;
53 set->aniFrameTime = 0.01;
57 set->generateParticles = 0.0;
58 set->numTracerParticles = 0;
59 strcpy(set->outputPath,"./elbeemdata_");
61 set->channelSizeFrameTime=0;
62 set->channelFrameTime=NULL;
63 set->channelSizeViscosity=0;
64 set->channelViscosity=NULL;
65 set->channelSizeGravity=0;
66 set->channelGravity=NULL;
68 set->domainobsType= FLUIDSIM_OBSTACLE_NOSLIP;
69 set->domainobsPartslip= 0.;
70 set->generateVertexVectors = 0;
71 set->surfaceSmoothing = 1.;
72 set->surfaceSubdivs = 1;
74 set->farFieldSize = 0.;
75 set->runsimCallback = NULL;
76 set->runsimUserData = NULL;
79 for(int i=0; i<16; i++) set->surfaceTrafo[i] = 0.0;
80 for(int i=0; i<4; i++) set->surfaceTrafo[i*4+i] = 1.0;
83 // start fluidsim init
86 setElbeemState( SIMWORLD_INITIALIZING );
87 setElbeemErrorString("[none]");
88 resetGlobalColorSetting();
90 elbeemCheckDebugEnv();
91 debMsgStd("performElbeemSimulation",DM_NOTIFY,"El'Beem Simulation Init Start as Plugin, debugLevel:"<<gDebugLevel<<" ...\n", 2);
93 // create world object with initial settings
94 ntlBlenderDumper *elbeem = new ntlBlenderDumper();
106 // start fluidsim init
108 int elbeemAddDomain(elbeemSimulationSettings *settings) {
109 // has to be inited...
110 if((getElbeemState() == SIMWORLD_INVALID) && (!gpWorld)) { elbeemInit(); }
111 if(getElbeemState() != SIMWORLD_INITIALIZING) { errFatal("elbeemAddDomain","Unable to init simulation world",SIMWORLD_INITERROR); }
112 // create domain with given settings
113 gpWorld->addDomain(settings);
117 // error message access
119 void elbeemGetErrorString(char *buffer) {
121 strncpy(buffer,getElbeemErrorString(),256);
124 // reset elbeemMesh struct with zeroes
126 void elbeemResetMesh(elbeemMesh *mesh) {
128 // init typedef struct elbeemMesh
131 mesh->parentDomainId = 0;
134 mesh->numVertices = 0;
135 mesh->vertices = NULL;
137 mesh->channelSizeVertices = 0;
138 mesh->channelVertices = NULL;
141 mesh->numTriangles = 0;
142 mesh->triangles = NULL;
144 /* animation channels */
145 mesh->channelSizeTranslation = 0;
146 mesh->channelTranslation = NULL;
147 mesh->channelSizeRotation = 0;
148 mesh->channelRotation = NULL;
149 mesh->channelSizeScale = 0;
150 mesh->channelScale = NULL;
153 mesh->channelSizeActive = 0;
154 mesh->channelActive = NULL;
156 mesh->channelSizeInitialVel = 0;
157 mesh->channelInitialVel = NULL;
159 mesh->localInivelCoords = 0;
161 mesh->obstacleType= FLUIDSIM_OBSTACLE_NOSLIP;
162 mesh->obstaclePartslip= 0.;
163 mesh->obstacleImpactFactor= 1.;
165 mesh->volumeInitType= OB_VOLUMEINIT_VOLUME;
167 /* name of the mesh, mostly for debugging */
168 mesh->name = "[unnamed]";
170 /* fluid control settings */
171 mesh->cpsTimeStart = 0;
172 mesh->cpsTimeEnd = 0;
173 mesh->cpsQuality = 0;
175 mesh->channelSizeAttractforceStrength = 0;
176 mesh->channelAttractforceStrength = NULL;
177 mesh->channelSizeAttractforceRadius = 0;
178 mesh->channelAttractforceRadius = NULL;
179 mesh->channelSizeVelocityforceStrength = 0;
180 mesh->channelVelocityforceStrength = NULL;
181 mesh->channelSizeVelocityforceRadius = 0;
182 mesh->channelVelocityforceRadius = NULL;
185 int globalMeshCounter = 1;
186 // add mesh as fluidsim object
188 int elbeemAddMesh(elbeemMesh *mesh) {
190 if(getElbeemState() != SIMWORLD_INITIALIZING) { errFatal("elbeemAddMesh","World and domain not initialized, call elbeemInit and elbeemAddDomain before...", SIMWORLD_INITERROR); }
193 case OB_FLUIDSIM_OBSTACLE:
194 if (mesh->obstacleType==FLUIDSIM_OBSTACLE_PARTSLIP) initType = FGI_BNDPART;
195 else if(mesh->obstacleType==FLUIDSIM_OBSTACLE_FREESLIP) initType = FGI_BNDFREE;
196 else /*if(mesh->obstacleType==FLUIDSIM_OBSTACLE_NOSLIP)*/ initType = FGI_BNDNO;
198 case OB_FLUIDSIM_FLUID: initType = FGI_FLUID; break;
199 case OB_FLUIDSIM_INFLOW: initType = FGI_MBNDINFLOW; break;
200 case OB_FLUIDSIM_OUTFLOW: initType = FGI_MBNDOUTFLOW; break;
201 case OB_FLUIDSIM_CONTROL: initType = FGI_CONTROL; break;
202 default: return 1; // invalid type
205 ntlGeometryObjModel *obj = new ntlGeometryObjModel( );
206 gpWorld->getRenderGlobals()->getSimScene()->addGeoClass( obj );
208 mesh->numVertices, mesh->vertices, mesh->numTriangles, mesh->triangles,
209 mesh->channelSizeVertices, mesh->channelVertices );
210 if(mesh->name) obj->setName(string(mesh->name));
213 snprintf(meshname,100,"mesh%04d",globalMeshCounter);
214 obj->setName(string(meshname));
217 obj->setGeoInitId( mesh->parentDomainId+1 );
218 obj->setGeoInitIntersect(true);
219 obj->setGeoInitType(initType);
221 // abuse partslip value for control fluid: reverse control keys or not
222 if(initType == FGI_CONTROL)
223 obj->setGeoPartSlipValue(mesh->obstacleType);
225 obj->setGeoPartSlipValue(mesh->obstaclePartslip);
227 obj->setGeoImpactFactor(mesh->obstacleImpactFactor);
229 /* fluid control features */
230 obj->setCpsTimeStart(mesh->cpsTimeStart);
231 obj->setCpsTimeEnd(mesh->cpsTimeEnd);
232 obj->setCpsQuality(mesh->cpsQuality);
234 if((mesh->volumeInitType<VOLUMEINIT_VOLUME)||(mesh->volumeInitType>VOLUMEINIT_BOTH)) mesh->volumeInitType = VOLUMEINIT_VOLUME;
235 obj->setVolumeInit(mesh->volumeInitType);
236 // use channel instead, obj->setInitialVelocity( ntlVec3Gfx(mesh->iniVelocity[0], mesh->iniVelocity[1], mesh->iniVelocity[2]) );
239 mesh->channelSizeTranslation, mesh->channelTranslation,
240 mesh->channelSizeRotation, mesh->channelRotation,
241 mesh->channelSizeScale, mesh->channelScale,
242 mesh->channelSizeActive, mesh->channelActive,
243 mesh->channelSizeInitialVel, mesh->channelInitialVel,
244 mesh->channelSizeAttractforceStrength, mesh->channelAttractforceStrength,
245 mesh->channelSizeAttractforceRadius, mesh->channelAttractforceRadius,
246 mesh->channelSizeVelocityforceStrength, mesh->channelVelocityforceStrength,
247 mesh->channelSizeVelocityforceRadius, mesh->channelVelocityforceRadius
249 obj->setLocalCoordInivel( mesh->localInivelCoords );
251 debMsgStd("elbeemAddMesh",DM_MSG,"Added elbeem mesh: "<<obj->getName()<<" type="<<initType<<" "<<obj->getIsAnimated(), 9 );
255 // do the actual simulation
257 int elbeemSimulate(void) {
258 if(!gpWorld) return 1;
260 gpWorld->finishWorldInit();
261 if( isSimworldOk() ) {
262 myTime_t timestart = getTime();
263 gpWorld->renderAnimation();
264 myTime_t timeend = getTime();
266 if(getElbeemState() != SIMWORLD_STOP) {
271 debMsgStd("elbeemSimulate",DM_NOTIFY, "El'Beem simulation done, time: "<<getTimeString(timeend-timestart)<<".\n", 2 );
273 debMsgStd("elbeemSimulate",DM_NOTIFY, "El'Beem simulation stopped, time so far: "<<getTimeString(timeend-timestart)<<".", 2 );
283 // continue a previously stopped simulation
285 int elbeemContinueSimulation(void) {
287 if(getElbeemState() != SIMWORLD_STOP) {
288 errMsg("elbeemContinueSimulation","No running simulation found! Aborting...");
289 if(gpWorld) delete gpWorld;
293 myTime_t timestart = getTime();
294 gpWorld->renderAnimation();
295 myTime_t timeend = getTime();
297 if(getElbeemState() != SIMWORLD_STOP) {
301 debMsgStd("elbeemContinueSimulation",DM_NOTIFY, "El'Beem simulation done, time: "<<getTimeString(timeend-timestart)<<".\n", 2 );
303 debMsgStd("elbeemContinueSimulation",DM_NOTIFY, "El'Beem simulation stopped, time so far: "<<getTimeString(timeend-timestart)<<".", 2 );
309 // global vector to flag values to remove
310 vector<int> gKeepVal;
312 #define SIMPLIFY_FLOAT_EPSILON (1e-6f)
313 #define SIMPLIFY_DOUBLE_EPSILON (1e-12f)
314 #define SFLOATEQ(x,y) (ABS((x)-(y)) < SIMPLIFY_FLOAT_EPSILON)
315 #define SDOUBLEEQ(x,y) (ABS((x)-(y)) < SIMPLIFY_DOUBLE_EPSILON)
316 #define SVECFLOATEQ(x,y) ( \
317 (ABS((x)[0]-(y)[0]) < SIMPLIFY_FLOAT_EPSILON) && \
318 (ABS((x)[1]-(y)[1]) < SIMPLIFY_FLOAT_EPSILON) && \
319 (ABS((x)[2]-(y)[2]) < SIMPLIFY_FLOAT_EPSILON) )
321 // helper function - simplify animation channels
323 int elbeemSimplifyChannelFloat(float *channel, int* size) {
324 bool changed = false;
327 if(orgsize<1) return false;
328 gKeepVal.resize( orgsize );
329 for(int i=0; i<orgsize; i++) { gKeepVal[i] = true; }
330 const bool debugSF = false;
332 float last = channel[0 + 0];
333 for(int i=1; i<orgsize; i++) {
334 float curr = channel[2*i + 0];
336 if(SFLOATEQ(curr,last)) remove = true;
337 // dont remove if next value is different
338 if((remove)&&(i<orgsize-1)) {
339 float next = channel[2*(i+1)+0];
340 if(!SFLOATEQ(next,curr)) remove = false;
347 if(debugSF) errMsg("elbeemSimplifyChannelFloat","i"<<i<<"/"<<orgsize<<" v"<<channel[ (i*2) + 0 ]<<" t"<<channel[ (i*2) + 1 ]<<" nsize="<<nsize<<" r"<<remove );
353 for(int i=1; i<orgsize; i++) {
355 channel[ (nsize*2) + 0 ] = channel[ (i*2) + 0 ];
356 channel[ (nsize*2) + 1 ] = channel[ (i*2) + 1 ];
363 if(debugSF) for(int i=1; i<nsize; i++) {
364 errMsg("elbeemSimplifyChannelFloat","n i"<<i<<"/"<<nsize<<" v"<<channel[ (i*2) + 0 ]<<" t"<<channel[ (i*2) + 1 ] );
371 int elbeemSimplifyChannelVec3(float *channel, int* size) {
372 bool changed = false;
375 if(orgsize<1) return false;
376 gKeepVal.resize( orgsize );
377 for(int i=0; i<orgsize; i++) { gKeepVal[i] = true; }
378 const bool debugVF = false;
380 ntlVec3f last( channel[0 + 0], channel[0 + 1], channel[0 + 2] );
381 for(int i=1; i<orgsize; i++) {
382 ntlVec3f curr( channel[4*i + 0], channel[4*i + 1], channel[4*i + 2]);
384 if(SVECFLOATEQ(curr,last)) remove = true;
385 // dont remove if next value is different
386 if((remove)&&(i<orgsize-1)) {
387 ntlVec3f next( channel[4*(i+1)+0], channel[4*(i+1)+1], channel[4*(i+1)+2]);
388 if(!SVECFLOATEQ(next,curr)) remove = false;
395 if(debugVF) errMsg("elbeemSimplifyChannelVec3","i"<<i<<"/"<<orgsize<<" v"<<
396 channel[ (i*4) + 0 ]<<","<< channel[ (i*4) + 1 ]<<","<< channel[ (i*4) + 2 ]<<
397 " t"<<channel[ (i*4) + 3 ]<<" nsize="<<nsize<<" r"<<remove );
403 for(int i=1; i<orgsize; i++) {
405 for(int j=0; j<4; j++){ channel[ (nsize*4) + j ] = channel[ (i*4) + j ]; }
412 if(debugVF) for(int i=1; i<nsize; i++) {
413 errMsg("elbeemSimplifyChannelVec3","n i"<<i<<"/"<<nsize<<" v"<<
414 channel[ (i*4) + 0 ]<<","<< channel[ (i*4) + 1 ]<<","<< channel[ (i*4) + 2 ]<<
415 " t"<<channel[ (i*4) + 3 ] );
422 #undef SIMPLIFY_FLOAT_EPSILON
423 #undef SIMPLIFY_DOUBLE_EPSILON