Changed frame numbering to only alter hashes if they are in the filename (not the...
[blender.git] / source / blender / src / fluidsim.c
1 /**
2  * fluidsim.c
3  * 
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) Blender Foundation
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32
33
34 #include <math.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38
39 #include "MEM_guardedalloc.h"
40
41 /* types */
42 #include "DNA_curve_types.h"
43 #include "DNA_object_types.h"
44 #include "DNA_object_fluidsim.h"        
45 #include "DNA_key_types.h"
46 #include "DNA_mesh_types.h"
47 #include "DNA_meshdata_types.h"
48 #include "DNA_lattice_types.h"
49 #include "DNA_scene_types.h"
50 #include "DNA_camera_types.h"
51 #include "DNA_screen_types.h"
52 #include "DNA_space_types.h"
53 #include "DNA_userdef_types.h"
54 #include "DNA_ipo_types.h"
55 #include "DNA_key_types.h" 
56
57 #include "BLI_blenlib.h"
58 #include "BLI_arithb.h"
59 #include "MTC_matrixops.h"
60
61 #include "BKE_customdata.h"
62 #include "BKE_displist.h"
63 #include "BKE_effect.h"
64 #include "BKE_global.h"
65 #include "BKE_main.h"
66 #include "BKE_key.h"
67 #include "BKE_scene.h"
68 #include "BKE_object.h"
69 #include "BKE_softbody.h"
70 #include "BKE_DerivedMesh.h"
71 #include "BKE_ipo.h"
72 #include "LBM_fluidsim.h"
73 // warning - double elbeem.h in intern/extern...
74 #include "elbeem.h"
75
76 #include "BLI_editVert.h"
77 #include "BIF_editdeform.h"
78 #include "BIF_gl.h"
79 #include "BIF_screen.h"
80 #include "BIF_space.h"
81 #include "BIF_cursors.h"
82 #include "BIF_interface.h"
83 #include "BSE_headerbuttons.h"
84
85 #include "mydevice.h"
86 #include "blendef.h"
87 #include "SDL.h"
88 #include "SDL_thread.h"
89 #include "SDL_mutex.h"
90 #include <sys/stat.h>
91
92 #ifdef WIN32    /* Windos */
93 //#include "BLI_winstuff.h"
94 #ifndef snprintf
95 #define snprintf _snprintf
96 #endif
97 #endif
98 // SDL redefines main for SDL_main, not needed here...
99 #undef main
100
101 #ifdef __APPLE__        /* MacOS X */
102 #undef main
103 #endif
104
105 // from DerivedMesh.c
106 void initElbeemMesh(struct Object *ob, int *numVertices, float **vertices, int *numTriangles, int **triangles, int useGlobalCoords);
107
108 /* from header info.c */
109 extern int start_progress_bar(void);
110 extern void end_progress_bar(void);
111 extern int progress_bar(float done, char *busy_info);
112
113 double fluidsimViscosityPreset[6] = {
114         -1.0,   /* unused */
115         -1.0,   /* manual */
116         1.0e-6, /* water */
117         5.0e-5, /* some (thick) oil */
118         2.0e-3, /* ca. honey */
119         -1.0    /* end */
120 };
121
122 char* fluidsimViscosityPresetString[6] = {
123         "UNUSED",       /* unused */
124         "UNUSED",       /* manual */
125         "  = 1.0 * 10^-6", /* water */
126         "  = 5.0 * 10^-5", /* some (thick) oil */
127         "  = 2.0 * 10^-3", /* ca. honey */
128         "INVALID"       /* end */
129 };
130
131 typedef struct {
132         DerivedMesh dm;
133
134         // similar to MeshDerivedMesh
135         struct Object *ob;      // pointer to parent object
136         float *extverts, *nors; // face normals, colors?
137         Mesh *fsmesh;   // mesh struct to display (either surface, or original one)
138         char meshFree;  // free the mesh afterwards? (boolean)
139 } fluidsimDerivedMesh;
140
141
142
143 /* enable/disable overall compilation */
144 #ifndef DISABLE_ELBEEM
145
146
147 /* ********************** fluid sim settings struct functions ********************** */
148
149 /* allocates and initializes general main data */
150
151 FluidsimSettings *fluidsimSettingsNew(struct Object *srcob)
152 {
153         //char blendDir[FILE_MAXDIR], blendFile[FILE_MAXFILE];
154         FluidsimSettings *fss;
155         
156         /* this call uses derivedMesh methods... */
157         if(srcob->type!=OB_MESH) return NULL;
158         
159         fss= MEM_callocN( sizeof(FluidsimSettings), "fluidsimsettings memory");
160         
161         fss->type = 0;
162         fss->show_advancedoptions = 0;
163
164         fss->resolutionxyz = 50;
165         fss->previewresxyz = 25;
166         fss->realsize = 0.03;
167         fss->guiDisplayMode = 2; // preview
168         fss->renderDisplayMode = 3; // render
169
170         fss->viscosityMode = 2; // default to water
171         fss->viscosityValue = 1.0;
172         fss->viscosityExponent = 6;
173         fss->gravx = 0.0;
174         fss->gravy = 0.0;
175         fss->gravz = -9.81;
176         fss->animStart = 0.0; 
177         fss->animEnd = 0.30;
178         fss->gstar = 0.005; // used as normgstar
179         fss->maxRefine = -1;
180         // maxRefine is set according to resolutionxyz during bake
181
182         // fluid/inflow settings
183         fss->iniVelx = 
184         fss->iniVely = 
185         fss->iniVelz = 0.0;
186
187         /*  elubie: changed this to default to the same dir as the render output
188                 to prevent saving to C:\ on Windows */
189         BLI_strncpy(fss->surfdataPath, btempdir, FILE_MAX); 
190         fss->orgMesh = (Mesh *)srcob->data;
191         fss->meshSurface = NULL;
192         fss->meshBB = NULL;
193         fss->meshSurfNormals = NULL;
194
195         // first init of bounding box
196         fss->bbStart[0] = 0.0;
197         fss->bbStart[1] = 0.0;
198         fss->bbStart[2] = 0.0;
199         fss->bbSize[0] = 1.0;
200         fss->bbSize[1] = 1.0;
201         fss->bbSize[2] = 1.0;
202         fluidsimGetAxisAlignedBB(srcob->data, srcob->obmat, fss->bbStart, fss->bbSize, &fss->meshBB);
203         
204         // todo - reuse default init from elbeem!
205         fss->typeFlags = 0;
206         fss->domainNovecgen = 0;
207         fss->volumeInitType = 1; // volume
208         fss->partSlipValue = 0.0;
209
210         fss->generateTracers = 0;
211         fss->generateParticles = 0.0;
212         fss->surfaceSmoothing = 1.0;
213         fss->surfaceSubdivs = 1.0;
214         fss->particleInfSize = 0.0;
215         fss->particleInfAlpha = 0.0;
216
217         return fss;
218 }
219
220 /* duplicate struct, analogous to free */
221 static Mesh *fluidsimCopyMesh(Mesh *me)
222 {
223         Mesh *dup = MEM_dupallocN(me);
224
225         CustomData_copy(&me->vdata, &dup->vdata, CD_MASK_MESH, CD_DUPLICATE, me->totvert);
226         CustomData_copy(&me->edata, &dup->edata, CD_MASK_MESH, CD_DUPLICATE, me->totedge);
227         CustomData_copy(&me->fdata, &dup->fdata, CD_MASK_MESH, CD_DUPLICATE, me->totface);
228
229         return dup;
230 }
231
232 FluidsimSettings* fluidsimSettingsCopy(FluidsimSettings *fss)
233 {
234         FluidsimSettings *dupfss;
235
236         if(!fss) return NULL;
237         dupfss = MEM_dupallocN(fss);
238
239         if(fss->meshSurface)
240                 dupfss->meshSurface = fluidsimCopyMesh(fss->meshSurface);
241         if(fss->meshBB)
242                 dupfss->meshBB = fluidsimCopyMesh(fss->meshBB);
243
244         if(fss->meshSurfNormals) dupfss->meshSurfNormals = MEM_dupallocN(fss->meshSurfNormals);
245
246         return dupfss;
247 }
248
249 /* free struct */
250 static void fluidsimFreeMesh(Mesh *me)
251 {
252         CustomData_free(&me->vdata, me->totvert);
253         CustomData_free(&me->edata, me->totedge);
254         CustomData_free(&me->fdata, me->totface);
255
256         MEM_freeN(me);
257 }
258
259 void fluidsimSettingsFree(FluidsimSettings *fss)
260 {
261         if(fss->meshSurface) {
262                 fluidsimFreeMesh(fss->meshSurface);
263                 fss->meshSurface = NULL;
264         }
265         if(fss->meshBB) {
266                 fluidsimFreeMesh(fss->meshBB);
267                 fss->meshBB = NULL;
268         }
269
270         if(fss->meshSurfNormals){ MEM_freeN(fss->meshSurfNormals); fss->meshSurfNormals=NULL; } 
271
272         MEM_freeN(fss);
273 }
274
275
276 /* helper function */
277 void fluidsimGetGeometryObjFilename(struct Object *ob, char *dst) { //, char *srcname) {
278         //snprintf(dst,FILE_MAXFILE, "%s_cfgdata_%s.bobj.gz", srcname, ob->id.name);
279         snprintf(dst,FILE_MAXFILE, "fluidcfgdata_%s.bobj.gz", ob->id.name);
280 }
281
282
283
284
285 /* ******************************************************************************** */
286 /* ********************** fluid sim channel helper functions ********************** */
287 /* ******************************************************************************** */
288
289 // no. of entries for the two channel sizes
290 #define CHANNEL_FLOAT 1
291 #define CHANNEL_VEC   3
292
293 #define FS_FREE_ONECHANNEL(c,str) { \
294         if(c){ MEM_freeN(c); c=NULL; } \
295 } // end ONE CHANN, debug: fprintf(stderr,"freeing " str " \n"); 
296
297 #define FS_FREE_CHANNELS { \
298         FS_FREE_ONECHANNEL(timeAtIndex,"timeAtIndex");\
299         FS_FREE_ONECHANNEL(timeAtFrame,"timeAtFrame");\
300         FS_FREE_ONECHANNEL(channelDomainTime,"channelDomainTime"); \
301         FS_FREE_ONECHANNEL(channelDomainGravity,"channelDomainGravity");\
302         FS_FREE_ONECHANNEL(channelDomainViscosity,"channelDomainViscosity");\
303         for(i=0;i<256;i++) { \
304                 FS_FREE_ONECHANNEL(channelObjMove[i][0],"channelObjMove0"); \
305                 FS_FREE_ONECHANNEL(channelObjMove[i][1],"channelObjMove1"); \
306                 FS_FREE_ONECHANNEL(channelObjMove[i][2],"channelObjMove2"); \
307                 FS_FREE_ONECHANNEL(channelObjInivel[i],"channelObjInivel"); \
308                 FS_FREE_ONECHANNEL(channelObjActive[i],"channelObjActive"); \
309         }  \
310 } // end FS FREE CHANNELS
311
312
313 // simplify channels before printing
314 // for API this is done anyway upon init
315 #if 0
316 static void fluidsimPrintChannel(FILE *file, float *channel, int paramsize, char *str, int entries) 
317
318         int i,j; 
319         int channelSize = paramsize; 
320
321         if(entries==3) {
322                 elbeemSimplifyChannelVec3( channel, &channelSize); 
323         } else if(entries==1) {
324                 elbeemSimplifyChannelFloat( channel, &channelSize); 
325         } else {
326                 // invalid, cant happen?
327         }
328
329         fprintf(file, "      CHANNEL %s = \n", str); 
330         for(i=0; i<channelSize;i++) { 
331                 fprintf(file,"        ");  
332                 for(j=0;j<=entries;j++) {  // also print time value
333                         fprintf(file," %f ", channel[i*(entries+1)+j] ); 
334                         if(j==entries-1){ fprintf(file,"  "); }
335                 } 
336                 fprintf(file," \n");  
337         } 
338
339         fprintf(file,  "      ; \n" ); 
340 }
341 #endif
342
343 static void fluidsimInitChannel(float **setchannel, int size, float *time, 
344                 int *icuIds, float *defaults, Ipo* ipo, int entries) {
345         int i,j;
346         IpoCurve* icus[3];
347         char *cstr = NULL;
348         float *channel = NULL;
349         float aniFrlen = G.scene->r.framelen;
350         int current_frame = G.scene->r.cfra;
351         if((entries<1) || (entries>3)) {
352                 printf("fluidsimInitChannel::Error - invalid no. of entries: %d\n",entries);
353                 entries = 1;
354         }
355
356         cstr = "fluidsiminit_channelfloat";
357         if(entries>1) cstr = "fluidsiminit_channelvec";
358         channel = MEM_callocN( size* (entries+1)* sizeof(float), cstr );
359         
360         if(ipo) {
361                 for(j=0; j<entries; j++) icus[j]  = find_ipocurve(ipo, icuIds[j] );
362         } else {
363                 for(j=0; j<entries; j++) icus[j]  = NULL; 
364         }
365         
366         for(j=0; j<entries; j++) {
367                 if(icus[j]) { 
368                         for(i=1; i<=size; i++) {
369                                 /* Bugfix to make python drivers working
370                                 // which uses Blender.get("curframe") 
371                                 */
372                                 G.scene->r.cfra = floor(aniFrlen*((float)i));
373                                 
374                                 calc_icu(icus[j], aniFrlen*((float)i) );
375                                 channel[(i-1)*(entries+1) + j] = icus[j]->curval;
376                         }
377                 }  else {
378                         for(i=1; i<=size; i++) { channel[(i-1)*(entries+1) + j] = defaults[j]; }
379                 }
380                 //printf("fluidsimInitChannel entry:%d , ",j); for(i=1; i<=size; i++) { printf(" val%d:%f ",i, channel[(i-1)*(entries+1) + j] ); } printf(" \n"); // DEBUG
381         }
382         // set time values
383         for(i=1; i<=size; i++) {
384                 channel[(i-1)*(entries+1) + entries] = time[i];
385         }
386         G.scene->r.cfra = current_frame;
387         *setchannel = channel;
388 }
389
390 static void fluidsimInitMeshChannel(float **setchannel, int size, Object *obm, int vertices, float *time) {
391         float *channel = NULL;
392         int mallsize = size* (3*vertices+1);
393         int frame,i;
394         int numVerts=0, numTris=0;
395         int setsize = 3*vertices+1;
396
397         channel = MEM_callocN( mallsize* sizeof(float), "fluidsim_meshchannel" );
398
399         //fprintf(stderr,"\n\nfluidsimInitMeshChannel size%d verts%d mallsize%d \n\n\n",size,vertices,mallsize);
400         for(frame=1; frame<=size; frame++) {
401                 float *verts=NULL;
402                 int *tris=NULL;
403                 G.scene->r.cfra = frame;
404                 scene_update_for_newframe(G.scene, G.scene->lay);
405
406                 initElbeemMesh(obm, &numVerts, &verts, &numTris, &tris, 1);
407                 //fprintf(stderr,"\nfluidsimInitMeshChannel frame%d verts%d/%d \n\n",frame,vertices,numVerts);
408                 for(i=0; i<3*vertices;i++) {
409                         channel[(frame-1)*setsize + i] = verts[i];
410                         //fprintf(stdout," frame%d vert%d=%f \n",frame,i,verts[i]);
411                         //if(i%3==2) fprintf(stdout,"\n");
412                 }
413                 channel[(frame-1)*setsize + setsize-1] = time[frame];
414
415                 MEM_freeN(verts);
416                 MEM_freeN(tris);
417         }
418         *setchannel = channel;
419 }
420
421
422 /* ******************************************************************************** */
423 /* ********************** simulation thread             ************************* */
424 /* ******************************************************************************** */
425
426 SDL_mutex       *globalBakeLock=NULL;
427 int                     globalBakeState = 0; // 0 everything ok, -1 abort simulation, -2 sim error, 1 sim done
428 int                     globalBakeFrame = 0;
429
430 // run simulation in seperate thread
431 static int fluidsimSimulateThread(void *unused) { // *ptr) {
432         //char* fnameCfgPath = (char*)(ptr);
433         int ret=0;
434         
435         ret = elbeemSimulate();
436         SDL_mutexP(globalBakeLock);
437         if(globalBakeState==0) {
438                 if(ret==0) {
439                         // if no error, set to normal exit
440                         globalBakeState = 1;
441                 } else {
442                         // simulation failed, display error
443                         globalBakeState = -2;
444                 }
445         }
446         SDL_mutexV(globalBakeLock);
447         return ret;
448 }
449
450
451 int runSimulationCallback(void *data, int status, int frame) {
452         //elbeemSimulationSettings *settings = (elbeemSimulationSettings*)data;
453         //printf("elbeem blender cb s%d, f%d, domainid:%d \n", status,frame, settings->domainId ); // DEBUG
454         
455         if(!globalBakeLock) return FLUIDSIM_CBRET_ABORT;
456         if(status==FLUIDSIM_CBSTATUS_NEWFRAME) {
457                 SDL_mutexP(globalBakeLock);
458                 globalBakeFrame = frame-1;
459                 SDL_mutexV(globalBakeLock);
460         }
461         
462         //if((frameCounter==3) && (!frameStop)) { frameStop=1; return 1; }
463                 
464         SDL_mutexP(globalBakeLock);
465         if(globalBakeState!=0) {
466                 return FLUIDSIM_CBRET_ABORT;
467         }
468         SDL_mutexV(globalBakeLock);
469         return FLUIDSIM_CBRET_CONTINUE;
470 }
471
472
473 /* ******************************************************************************** */
474 /* ********************** write fluidsim config to file ************************* */
475 /* ******************************************************************************** */
476
477 void fluidsimBake(struct Object *ob)
478 {
479         FILE *fileCfg;
480         int i;
481         struct Object *fsDomain = NULL;
482         FluidsimSettings *domainSettings;
483         struct Object *obit = NULL; /* object iterator */
484         int origFrame = G.scene->r.cfra;
485         char debugStrBuffer[256];
486         int dirExist = 0;
487         int gridlevels = 0;
488         int simAborted = 0; // was the simulation aborted by user?
489         int  doExportOnly = 0;
490         char *exportEnvStr = "BLENDER_ELBEEMEXPORTONLY";
491         const char *strEnvName = "BLENDER_ELBEEMDEBUG"; // from blendercall.cpp
492         //char *channelNames[3] = { "translation","rotation","scale" };
493
494         char *suffixConfig = "fluidsim.cfg";
495         char *suffixSurface = "fluidsurface";
496         char newSurfdataPath[FILE_MAXDIR+FILE_MAXFILE]; // modified output settings
497         char targetDir[FILE_MAXDIR+FILE_MAXFILE];  // store & modify output settings
498         char targetFile[FILE_MAXDIR+FILE_MAXFILE]; // temp. store filename from targetDir for access
499         int  outStringsChanged = 0;             // modified? copy back before baking
500         int  haveSomeFluid = 0;                 // check if any fluid objects are set
501
502         // config vars, inited before either export or run...
503         double calcViscosity = 0.0;
504         int noFrames;
505         double aniFrameTime;
506         float aniFrlen;
507         int   channelObjCount;
508         float *bbStart = NULL;
509         float *bbSize = NULL;
510         float domainMat[4][4];
511         float invDomMat[4][4];
512         // channel data
513         int   allchannelSize; // fixed by no. of frames
514         int   startFrame = 1;  // dont use G.scene->r.sfra here, always start with frame 1
515         // easy frame -> sim time calc
516         float *timeAtFrame=NULL, *timeAtIndex=NULL;
517         // domain
518         float *channelDomainTime = NULL;
519         float *channelDomainViscosity = NULL; 
520         float *channelDomainGravity = NULL;
521         // objects (currently max. 256 objs)
522         float *channelObjMove[256][3]; // object movments , 0=trans, 1=rot, 2=scale
523         float *channelObjInivel[256];    // initial velocities
524         float *channelObjActive[256];    // obj active channel
525
526         if(getenv(strEnvName)) {
527                 int dlevel = atoi(getenv(strEnvName));
528                 elbeemSetDebugLevel(dlevel);
529                 snprintf(debugStrBuffer,256,"fluidsimBake::msg: Debug messages activated due to envvar '%s'\n",strEnvName); 
530                 elbeemDebugOut(debugStrBuffer);
531         }
532         if(getenv(exportEnvStr)) {
533                 doExportOnly = atoi(getenv(exportEnvStr));
534                 snprintf(debugStrBuffer,256,"fluidsimBake::msg: Exporting mode set to '%d' due to envvar '%s'\n",doExportOnly, exportEnvStr); 
535                 elbeemDebugOut(debugStrBuffer);
536         }
537
538         // make sure it corresponds to startFrame setting
539         // old: noFrames = G.scene->r.efra - G.scene->r.sfra +1;
540         noFrames = G.scene->r.efra - 0;
541         if(noFrames<=0) {
542                 pupmenu("Fluidsim Bake Error%t|No frames to export - check your animation range settings. Aborted%x0");
543                 return;
544         }
545
546         /* no object pointer, find in selected ones.. */
547         if(!ob) {
548                 Base *base;
549                 for(base=G.scene->base.first; base; base= base->next) {
550                         if ( ((base)->flag & SELECT) 
551                                         // ignore layer setting for now? && ((base)->lay & G.vd->lay) 
552                                  ) {
553                                 if((!ob)&&(base->object->fluidsimFlag & OB_FLUIDSIM_ENABLE)&&(base->object->type==OB_MESH)) {
554                                         if(base->object->fluidsimSettings->type == OB_FLUIDSIM_DOMAIN) {
555                                                 ob = base->object;
556                                         }
557                                 }
558                         }
559                 }
560                 // no domains found?
561                 if(!ob) return;
562         }
563
564         /* check if there's another domain... */
565         for(obit= G.main->object.first; obit; obit= obit->id.next) {
566                 if((obit->fluidsimFlag & OB_FLUIDSIM_ENABLE)&&(obit->type==OB_MESH)) {
567                         if(obit->fluidsimSettings->type == OB_FLUIDSIM_DOMAIN) {
568                                 if(obit != ob) {
569                                         //snprintf(debugStrBuffer,256,"fluidsimBake::warning - More than one domain!\n"); elbeemDebugOut(debugStrBuffer);
570                                         pupmenu("Fluidsim Bake Error%t|There should be only one domain object! Aborted%x0");
571                                         return;
572                                 }
573                         }
574                 }
575         }
576         /* these both have to be valid, otherwise we wouldnt be here */
577         /* dont use ob here after...*/
578         fsDomain = ob;
579         domainSettings = ob->fluidsimSettings;
580         ob = NULL;
581         /* rough check of settings... */
582         if(domainSettings->previewresxyz > domainSettings->resolutionxyz) {
583                 snprintf(debugStrBuffer,256,"fluidsimBake::warning - Preview (%d) >= Resolution (%d)... setting equal.\n", domainSettings->previewresxyz ,  domainSettings->resolutionxyz); 
584                 elbeemDebugOut(debugStrBuffer);
585                 domainSettings->previewresxyz = domainSettings->resolutionxyz;
586         }
587         // set adaptive coarsening according to resolutionxyz
588         // this should do as an approximation, with in/outflow
589         // doing this more accurate would be overkill
590         // perhaps add manual setting?
591         if(domainSettings->maxRefine <0) {
592                 if(domainSettings->resolutionxyz>128) {
593                         gridlevels = 2;
594                 } else
595                 if(domainSettings->resolutionxyz>64) {
596                         gridlevels = 1;
597                 } else {
598                         gridlevels = 0;
599                 }
600         } else {
601                 gridlevels = domainSettings->maxRefine;
602         }
603         snprintf(debugStrBuffer,256,"fluidsimBake::msg: Baking %s, refine: %d\n", fsDomain->id.name , gridlevels ); 
604         elbeemDebugOut(debugStrBuffer);
605         
606         // check if theres any fluid
607         // abort baking if not...
608         for(obit= G.main->object.first; obit; obit= obit->id.next) {
609                 if( (obit->fluidsimFlag & OB_FLUIDSIM_ENABLE) && 
610                                 (obit->type==OB_MESH) && (
611                           (obit->fluidsimSettings->type == OB_FLUIDSIM_FLUID) ||
612                           (obit->fluidsimSettings->type == OB_FLUIDSIM_INFLOW) )
613                                 ) {
614                         haveSomeFluid = 1;
615                 }
616         }
617         if(!haveSomeFluid) {
618                 pupmenu("Fluidsim Bake Error%t|No fluid objects in scene... Aborted%x0");
619                 return;
620         }
621
622         // prepare names...
623         strncpy(targetDir, domainSettings->surfdataPath, FILE_MAXDIR);
624         strncpy(newSurfdataPath, domainSettings->surfdataPath, FILE_MAXDIR);
625         BLI_convertstringcode(targetDir, G.sce); // fixed #frame-no 
626
627         strcpy(targetFile, targetDir);
628         strcat(targetFile, suffixConfig);
629         if(!doExportOnly) { strcat(targetFile,".tmp"); }  // dont overwrite/delete original file
630         // make sure all directories exist
631         // as the bobjs use the same dir, this only needs to be checked
632         // for the cfg output
633         BLI_make_existing_file(targetFile);
634
635         // check selected directory
636         // simply try to open cfg file for writing to test validity of settings
637         fileCfg = fopen(targetFile, "w");
638         if(fileCfg) { 
639                 dirExist = 1; fclose(fileCfg); 
640                 // remove cfg dummy from  directory test
641                 if(!doExportOnly) { BLI_delete(targetFile, 0,0); }
642         }
643
644         if((strlen(targetDir)<1) || (!dirExist)) {
645                 char blendDir[FILE_MAXDIR+FILE_MAXFILE], blendFile[FILE_MAXDIR+FILE_MAXFILE];
646                 // invalid dir, reset to current/previous
647                 strcpy(blendDir, G.sce);
648                 BLI_splitdirstring(blendDir, blendFile);
649                 if(strlen(blendFile)>6){
650                         int len = strlen(blendFile);
651                         if( (blendFile[len-6]=='.')&& (blendFile[len-5]=='b')&& (blendFile[len-4]=='l')&&
652                                         (blendFile[len-3]=='e')&& (blendFile[len-2]=='n')&& (blendFile[len-1]=='d') ){
653                                 blendFile[len-6] = '\0';
654                         }
655                 }
656                 // todo... strip .blend ?
657                 snprintf(newSurfdataPath,FILE_MAXFILE+FILE_MAXDIR,"//fluidsimdata/%s_%s_", blendFile, fsDomain->id.name);
658
659                 snprintf(debugStrBuffer,256,"fluidsimBake::error - warning resetting output dir to '%s'\n", newSurfdataPath);
660                 elbeemDebugOut(debugStrBuffer);
661                 outStringsChanged=1;
662         }
663
664         // check if modified output dir is ok
665         if(outStringsChanged) {
666                 char dispmsg[FILE_MAXDIR+FILE_MAXFILE+256];
667                 int  selection=0;
668                 strcpy(dispmsg,"Output settings set to: '");
669                 strcat(dispmsg, newSurfdataPath);
670                 strcat(dispmsg, "'%t|Continue with changed settings%x1|Discard and abort%x0");
671
672                 // ask user if thats what he/she wants...
673                 selection = pupmenu(dispmsg);
674                 if(selection<1) return; // 0 from menu, or -1 aborted
675                 strcpy(targetDir, newSurfdataPath);
676                 strncpy(domainSettings->surfdataPath, newSurfdataPath, FILE_MAXDIR);
677                 BLI_convertstringcode(targetDir, G.sce); // fixed #frame-no 
678         }
679         
680         // --------------------------------------------------------------------------------------------
681         // dump data for start frame 
682         // CHECK more reasonable to number frames according to blender?
683         // dump data for frame 0
684   G.scene->r.cfra = startFrame;
685   scene_update_for_newframe(G.scene, G.scene->lay);
686         
687         // init common export vars for both file export and run
688         for(i=0; i<256; i++) {
689                 channelObjMove[i][0] = channelObjMove[i][1] = channelObjMove[i][2] = NULL;
690                 channelObjInivel[i] = NULL;
691                 channelObjActive[i] = NULL;
692         }
693         allchannelSize = G.scene->r.efra; // always use till last frame
694         aniFrameTime = (domainSettings->animEnd - domainSettings->animStart)/(double)noFrames;
695         // blender specific - scale according to map old/new settings in anim panel:
696         aniFrlen = G.scene->r.framelen;
697         if(domainSettings->viscosityMode==1) {
698                 /* manual mode, visc=value/(10^-vexp) */
699                 calcViscosity = (1.0/pow(10.0,domainSettings->viscosityExponent)) * domainSettings->viscosityValue;
700         } else {
701                 calcViscosity = fluidsimViscosityPreset[ domainSettings->viscosityMode ];
702         }
703
704         bbStart = fsDomain->fluidsimSettings->bbStart; 
705         bbSize = fsDomain->fluidsimSettings->bbSize;
706         fluidsimGetAxisAlignedBB(fsDomain->data, fsDomain->obmat, bbStart, bbSize, &domainSettings->meshBB);
707
708         // always init
709         { int timeIcu[1] = { FLUIDSIM_TIME };
710                 float timeDef[1] = { 1. };
711                 int gravIcu[3] = { FLUIDSIM_GRAV_X, FLUIDSIM_GRAV_Y, FLUIDSIM_GRAV_Z };
712                 float gravDef[3];
713                 int viscIcu[1] = { FLUIDSIM_VISC };
714                 float viscDef[1] = { 1. };
715
716                 gravDef[0] = domainSettings->gravx;
717                 gravDef[1] = domainSettings->gravy;
718                 gravDef[2] = domainSettings->gravz;
719
720                 // time channel is a bit special, init by hand...
721                 timeAtIndex = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatindex");
722                 for(i=0; i<=G.scene->r.efra; i++) {
723                         timeAtIndex[i] = (float)(i-startFrame);
724                 }
725                 fluidsimInitChannel( &channelDomainTime, allchannelSize, timeAtIndex, timeIcu,timeDef, domainSettings->ipo, CHANNEL_FLOAT ); // NDEB
726                 // time channel is a multiplicator for aniFrameTime
727                 if(channelDomainTime) {
728                         for(i=0; i<allchannelSize; i++) { 
729                                 channelDomainTime[i*2+0] = aniFrameTime * channelDomainTime[i*2+0]; 
730                                 if(channelDomainTime[i*2+0]<0.) channelDomainTime[i*2+0] = 0.;
731                         }
732                 }
733                 timeAtFrame = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatframe");
734                 timeAtFrame[0] = timeAtFrame[1] = domainSettings->animStart; // start at index 1
735                 if(channelDomainTime) {
736                         for(i=2; i<=allchannelSize; i++) {
737                                 timeAtFrame[i] = timeAtFrame[i-1]+channelDomainTime[(i-1)*2+0];
738                         }
739                 } else {
740                         for(i=2; i<=allchannelSize; i++) { timeAtFrame[i] = timeAtFrame[i-1]+aniFrameTime; }
741                 }
742
743                 fluidsimInitChannel( &channelDomainViscosity, allchannelSize, timeAtFrame, viscIcu,viscDef, domainSettings->ipo, CHANNEL_FLOAT ); // NDEB
744                 if(channelDomainViscosity) {
745                         for(i=0; i<allchannelSize; i++) { channelDomainViscosity[i*2+0] = calcViscosity * channelDomainViscosity[i*2+0]; }
746                 }
747                 fluidsimInitChannel( &channelDomainGravity, allchannelSize, timeAtFrame, gravIcu,gravDef, domainSettings->ipo, CHANNEL_VEC );
748         } // domain channel init
749         
750         // init obj movement channels
751         channelObjCount=0;
752         for(obit= G.main->object.first; obit; obit= obit->id.next) {
753                 //{ snprintf(debugStrBuffer,256,"DEBUG object name=%s, type=%d ...\n", obit->id.name, obit->type); elbeemDebugOut(debugStrBuffer); } // DEBUG
754                 if( (obit->fluidsimFlag & OB_FLUIDSIM_ENABLE) && 
755                                 (obit->type==OB_MESH) &&
756                                 (obit->fluidsimSettings->type != OB_FLUIDSIM_DOMAIN) &&  // if has to match 3 places! // CHECKMATCH
757                                 (obit->fluidsimSettings->type != OB_FLUIDSIM_PARTICLE) ) {
758
759                         //  cant use fluidsimInitChannel for obj channels right now, due
760                         //  to the special DXXX channels, and the rotation specialities
761                         IpoCurve *icuex[3][3];
762                         //IpoCurve *par_icuex[3][3];
763                         int icuIds[3][3] = { 
764                                 {OB_LOC_X,  OB_LOC_Y,  OB_LOC_Z},
765                                 {OB_ROT_X,  OB_ROT_Y,  OB_ROT_Z},
766                                 {OB_SIZE_X, OB_SIZE_Y, OB_SIZE_Z} 
767                         };
768                         // relative ipos
769                         IpoCurve *icudex[3][3];
770                         //IpoCurve *par_icudex[3][3];
771                         int icudIds[3][3] = { 
772                                 {OB_DLOC_X,  OB_DLOC_Y,  OB_DLOC_Z},
773                                 {OB_DROT_X,  OB_DROT_Y,  OB_DROT_Z},
774                                 {OB_DSIZE_X, OB_DSIZE_Y, OB_DSIZE_Z} 
775                         };
776                         int j,k;
777                         float vals[3] = {0.0,0.0,0.0}; 
778                         int o = channelObjCount;
779                         int   inivelIcu[3] =  { FLUIDSIM_VEL_X, FLUIDSIM_VEL_Y, FLUIDSIM_VEL_Z };
780                         float inivelDefs[3];
781                         int   activeIcu[1] =  { FLUIDSIM_ACTIVE };
782                         float activeDefs[1] = { 1 }; // default to on
783
784                         inivelDefs[0] = obit->fluidsimSettings->iniVelx;
785                         inivelDefs[1] = obit->fluidsimSettings->iniVely;
786                         inivelDefs[2] = obit->fluidsimSettings->iniVelz;
787
788                         // check & init loc,rot,size
789                         for(j=0; j<3; j++) {
790                                 for(k=0; k<3; k++) {
791                                         icuex[j][k]  = find_ipocurve(obit->ipo, icuIds[j][k] );
792                                         icudex[j][k] = find_ipocurve(obit->ipo, icudIds[j][k] );
793                                         //if(obit->parent) {
794                                                 //par_icuex[j][k]  = find_ipocurve(obit->parent->ipo, icuIds[j][k] );
795                                                 //par_icudex[j][k] = find_ipocurve(obit->parent->ipo, icudIds[j][k] );
796                                         //}
797                                 }
798                         }
799
800                         for(j=0; j<3; j++) {
801                                 channelObjMove[o][j] = MEM_callocN( allchannelSize*4*sizeof(float), "fluidsiminit_objmovchannel");
802                                 for(i=1; i<=allchannelSize; i++) {
803
804                                         for(k=0; k<3; k++) {
805                                                 if(icuex[j][k]) { 
806                                                         // IPO exists, use it ...
807                                                         calc_icu(icuex[j][k], aniFrlen*((float)i) );
808                                                         vals[k] = icuex[j][k]->curval; 
809                                                         if(obit->parent) {
810                                                                 // add parent transform, multiply scaling, add trafo&rot
811                                                                 //calc_icu(par_icuex[j][k], aniFrlen*((float)i) );
812                                                                 //if(j==2) { vals[k] *= par_icuex[j][k]->curval; }
813                                                                 //else { vals[k] += par_icuex[j][k]->curval; }
814                                                         }
815                                                 } else {
816                                                         // use defaults from static values
817                                                         float setval=0.0;
818                                                         if(j==0) { 
819                                                                 setval = obit->loc[k];
820                                                                 if(obit->parent){ setval += obit->parent->loc[k]; }
821                                                         } else if(j==1) { 
822                                                                 setval = ( 180.0*obit->rot[k] )/( 10.0*M_PI );
823                                                                 if(obit->parent){ setval = ( 180.0*(obit->rot[k]+obit->parent->rot[k]) )/( 10.0*M_PI ); }
824                                                         } else { 
825                                                                 setval = obit->size[k]; 
826                                                                 if(obit->parent){ setval *= obit->parent->size[k]; }
827                                                         }
828                                                         vals[k] = setval;
829                                                 }
830                                                 if(icudex[j][k]) { 
831                                                         calc_icu(icudex[j][k], aniFrlen*((float)i) );
832                                                         //vals[k] += icudex[j][k]->curval; 
833                                                         // add transform, multiply scaling, add trafo&rot
834                                                         if(j==2) { vals[k] *= icudex[j][k]->curval; }
835                                                         else { vals[k] += icudex[j][k]->curval; }
836                                                         if(obit->parent) {
837                                                                 // add parent transform, multiply scaling, add trafo&rot
838                                                                 //calc_icu(par_icuex[j][k], aniFrlen*((float)i) );
839                                                                 //if(j==2) { vals[k] *= par_icudex[j][k]->curval; }
840                                                                 //else { vals[k] += par_icudex[j][k]->curval; }
841                                                         }
842                                                 } 
843                                         } // k
844
845                                         for(k=0; k<3; k++) {
846                                                 float set = vals[k];
847                                                 if(j==1) { // rot is downscaled by 10 for ipo !?
848                                                         set = 360.0 - (10.0*set);
849                                                 }
850                                                 channelObjMove[o][j][(i-1)*4 + k] = set;
851                                         } // k
852                                         channelObjMove[o][j][(i-1)*4 + 3] = timeAtFrame[i];
853                                 }
854                         }
855
856                         fluidsimInitChannel( &channelObjInivel[o], allchannelSize, timeAtFrame, inivelIcu,inivelDefs, obit->fluidsimSettings->ipo, CHANNEL_VEC );
857                         fluidsimInitChannel( &channelObjActive[o], allchannelSize, timeAtFrame, activeIcu,activeDefs, obit->fluidsimSettings->ipo, CHANNEL_FLOAT );
858
859                         channelObjCount++;
860
861                 }
862         }
863
864         // init trafo matrix
865         MTC_Mat4CpyMat4(domainMat, fsDomain->obmat);
866         if(!Mat4Invert(invDomMat, domainMat)) {
867                 snprintf(debugStrBuffer,256,"fluidsimBake::error - Invalid obj matrix?\n"); 
868                 elbeemDebugOut(debugStrBuffer);
869                 // FIXME add fatal msg
870                 FS_FREE_CHANNELS;
871                 return;
872         }
873
874
875         // --------------------------------------------------------------------------------------------
876         // start writing / exporting
877         strcpy(targetFile, targetDir);
878         strcat(targetFile, suffixConfig);
879         if(!doExportOnly) { strcat(targetFile,".tmp"); }  // dont overwrite/delete original file
880         // make sure these directories exist as well
881         if(outStringsChanged) {
882                 BLI_make_existing_file(targetFile);
883         }
884
885         if(!doExportOnly) {
886                 SDL_Thread *simthr = NULL;
887
888                 // perform simulation with El'Beem api and SDL threads
889                 elbeemSimulationSettings fsset;
890                 elbeemResetSettings(&fsset);
891                 fsset.version = 1;
892
893                 // setup global settings
894                 for(i=0 ; i<3; i++) fsset.geoStart[i] = bbStart[i];
895                 for(i=0 ; i<3; i++) fsset.geoSize[i] = bbSize[i];
896                 // simulate with 50^3
897                 fsset.resolutionxyz = (int)domainSettings->resolutionxyz;
898                 fsset.previewresxyz = (int)domainSettings->previewresxyz;
899                 // 10cm water domain
900                 fsset.realsize = domainSettings->realsize;
901                 fsset.viscosity = calcViscosity;
902                 // earth gravity
903                 fsset.gravity[0] = domainSettings->gravx;
904                 fsset.gravity[1] = domainSettings->gravy;
905                 fsset.gravity[2] = domainSettings->gravz;
906                 // simulate 5 frames, each 0.03 seconds, output to ./apitest_XXX.bobj.gz
907                 fsset.animStart = domainSettings->animStart;
908                 fsset.aniFrameTime = aniFrameTime;
909                 fsset.noOfFrames = noFrames - 1; // is otherwise subtracted in parser
910                 strcpy(targetFile, targetDir);
911                 strcat(targetFile, suffixSurface);
912                 // defaults for compressibility and adaptive grids
913                 fsset.gstar = domainSettings->gstar;
914                 fsset.maxRefine = domainSettings->maxRefine; // check <-> gridlevels
915                 fsset.generateParticles = domainSettings->generateParticles; 
916                 fsset.numTracerParticles = domainSettings->generateTracers; 
917                 fsset.surfaceSmoothing = domainSettings->surfaceSmoothing; 
918                 fsset.surfaceSubdivs = domainSettings->surfaceSubdivs; 
919                 fsset.farFieldSize = domainSettings->farFieldSize; 
920                 strcpy( fsset.outputPath, targetFile);
921
922                 // domain channels
923                 fsset.channelSizeFrameTime = 
924                 fsset.channelSizeViscosity = 
925                 fsset.channelSizeGravity =  allchannelSize;
926                 fsset.channelFrameTime = channelDomainTime;
927                 fsset.channelViscosity = channelDomainViscosity;
928                 fsset.channelGravity = channelDomainGravity;
929
930                 fsset.runsimCallback = &runSimulationCallback;
931                 fsset.runsimUserData = &fsset;
932
933                 if(     (domainSettings->typeFlags&OB_FSBND_NOSLIP))   fsset.domainobsType = FLUIDSIM_OBSTACLE_NOSLIP;
934                 else if((domainSettings->typeFlags&OB_FSBND_PARTSLIP)) fsset.domainobsType = FLUIDSIM_OBSTACLE_PARTSLIP;
935                 else if((domainSettings->typeFlags&OB_FSBND_FREESLIP)) fsset.domainobsType = FLUIDSIM_OBSTACLE_FREESLIP;
936                 fsset.domainobsPartslip = domainSettings->partSlipValue;
937                 fsset.generateVertexVectors = (domainSettings->domainNovecgen==0);
938
939                 // init blender trafo matrix
940                 // fprintf(stderr,"elbeemInit - mpTrafo:\n");
941                 { int j; 
942                 for(i=0; i<4; i++) {
943                         for(j=0; j<4; j++) {
944                                 fsset.surfaceTrafo[i*4+j] = invDomMat[j][i];
945                                 // fprintf(stderr,"elbeemInit - mpTrafo %d %d = %f (%d) \n", i,j, fsset.surfaceTrafo[i*4+j] , (i*4+j) );
946                         }
947                 } }
948
949           // init solver with settings
950                 elbeemInit();
951                 elbeemAddDomain(&fsset);
952                 
953                 // init objects
954                 channelObjCount = 0;
955                 for(obit= G.main->object.first; obit; obit= obit->id.next) {
956                         //{ snprintf(debugStrBuffer,256,"DEBUG object name=%s, type=%d ...\n", obit->id.name, obit->type); elbeemDebugOut(debugStrBuffer); } // DEBUG
957                         if( (obit->fluidsimFlag & OB_FLUIDSIM_ENABLE) &&  // if has to match 3 places! // CHECKMATCH
958                                         (obit->type==OB_MESH) &&
959                                         (obit->fluidsimSettings->type != OB_FLUIDSIM_DOMAIN) &&
960                                         (obit->fluidsimSettings->type != OB_FLUIDSIM_PARTICLE)
961                                 ) {
962                                 float *verts=NULL;
963                                 int *tris=NULL;
964                                 int numVerts=0, numTris=0;
965                                 int o = channelObjCount;
966                                 int     deform = (obit->fluidsimSettings->domainNovecgen); // misused value
967                                 // todo - use blenderInitElbeemMesh
968                                 elbeemMesh fsmesh;
969                                 elbeemResetMesh( &fsmesh );
970                                 fsmesh.type = obit->fluidsimSettings->type;;
971                                 // get name of object for debugging solver
972                                 fsmesh.name = obit->id.name; 
973
974                                 initElbeemMesh(obit, &numVerts, &verts, &numTris, &tris, 0);
975                                 fsmesh.numVertices   = numVerts;
976                                 fsmesh.numTriangles  = numTris;
977                                 fsmesh.vertices      = verts;
978                                 fsmesh.triangles     = tris;
979
980                                 fsmesh.channelSizeTranslation  = 
981                                 fsmesh.channelSizeRotation     = 
982                                 fsmesh.channelSizeScale        = 
983                                 fsmesh.channelSizeInitialVel   = 
984                                 fsmesh.channelSizeActive       = allchannelSize;
985
986                                 fsmesh.channelTranslation      = channelObjMove[o][0];
987                                 fsmesh.channelRotation         = channelObjMove[o][1];
988                                 fsmesh.channelScale            = channelObjMove[o][2];
989                                 fsmesh.channelActive           = channelObjActive[o];
990                                 if( (fsmesh.type == OB_FLUIDSIM_FLUID) ||
991                                                 (fsmesh.type == OB_FLUIDSIM_INFLOW) ) {
992                                         fsmesh.channelInitialVel       = channelObjInivel[o];
993                                   fsmesh.localInivelCoords = ((obit->fluidsimSettings->typeFlags&OB_FSINFLOW_LOCALCOORD)?1:0);
994                                 } 
995
996                                 if(     (obit->fluidsimSettings->typeFlags&OB_FSBND_NOSLIP))   fsmesh.obstacleType = FLUIDSIM_OBSTACLE_NOSLIP;
997                                 else if((obit->fluidsimSettings->typeFlags&OB_FSBND_PARTSLIP)) fsmesh.obstacleType = FLUIDSIM_OBSTACLE_PARTSLIP;
998                                 else if((obit->fluidsimSettings->typeFlags&OB_FSBND_FREESLIP)) fsmesh.obstacleType = FLUIDSIM_OBSTACLE_FREESLIP;
999                                 fsmesh.obstaclePartslip = obit->fluidsimSettings->partSlipValue;
1000                                 fsmesh.volumeInitType = obit->fluidsimSettings->volumeInitType;
1001                                 fsmesh.obstacleImpactFactor = obit->fluidsimSettings->surfaceSmoothing; // misused value
1002
1003                                 // animated meshes
1004                                 if(deform) {
1005                                         fsmesh.channelSizeVertices = allchannelSize;
1006                                         fluidsimInitMeshChannel( &fsmesh.channelVertices, allchannelSize, obit, numVerts, timeAtFrame);
1007                                         G.scene->r.cfra = startFrame;
1008                                         scene_update_for_newframe(G.scene, G.scene->lay);
1009                                         // remove channels
1010                                         fsmesh.channelTranslation      = 
1011                                         fsmesh.channelRotation         = 
1012                                         fsmesh.channelScale            = NULL; 
1013                                 } 
1014
1015                                 elbeemAddMesh(&fsmesh);
1016
1017                                 if(verts) MEM_freeN(verts);
1018                                 if(tris) MEM_freeN(tris);
1019                                 if(fsmesh.channelVertices) MEM_freeN(fsmesh.channelVertices);
1020                                 channelObjCount++;
1021                         } // valid mesh
1022                 } // objects
1023                 //domainSettings->type = OB_FLUIDSIM_DOMAIN; // enable for bake display again
1024                 //fsDomain->fluidsimFlag = OB_FLUIDSIM_ENABLE; // disable during bake
1025                 
1026                 globalBakeLock = SDL_CreateMutex();
1027                 // set to neutral, -1 means user abort, -2 means init error
1028                 globalBakeState = 0;
1029                 globalBakeFrame = 0;
1030                 simthr = SDL_CreateThread(fluidsimSimulateThread, targetFile);
1031
1032                 if(!simthr) {
1033                         snprintf(debugStrBuffer,256,"fluidsimBake::error: Unable to create thread... running without one.\n"); 
1034                         elbeemDebugOut(debugStrBuffer);
1035                         set_timecursor(0);
1036                         elbeemSimulate();
1037                 } else {
1038                         int done = 0;
1039                         unsigned short event=0;
1040                         short val;
1041                         float noFramesf = (float)noFrames;
1042                         float percentdone = 0.0;
1043                         int lastRedraw = -1;
1044                         
1045                         start_progress_bar();
1046
1047                         while(done==0) {            
1048                                 char busy_mess[80];
1049                                 
1050                                 waitcursor(1);
1051                                 
1052                                 // lukep we add progress bar as an interim mesure
1053                                 percentdone = globalBakeFrame / noFramesf;
1054                                 sprintf(busy_mess, "baking fluids %d / %d       |||", globalBakeFrame, (int) noFramesf);
1055                                 progress_bar(percentdone, busy_mess );
1056                                 
1057                                 SDL_Delay(2000); // longer delay to prevent frequent redrawing
1058                                 SDL_mutexP(globalBakeLock);
1059                                 if(globalBakeState != 0) done = 1; // 1=ok, <0=error/abort
1060                                 SDL_mutexV(globalBakeLock);
1061
1062                                 while(qtest()) {
1063                                         event = extern_qread(&val);
1064                                         if(event == ESCKEY) {
1065                                                 // abort...
1066                                                 SDL_mutexP(globalBakeLock);
1067                                                 done = -1;
1068                                                 globalBakeFrame = 0;
1069                                                 globalBakeState = -1;
1070                                                 simAborted = 1;
1071                                                 SDL_mutexV(globalBakeLock);
1072                                                 break;
1073                                         }
1074                                 } 
1075
1076                                 // redraw the 3D for showing progress once in a while...
1077                                 if(lastRedraw!=globalBakeFrame) {
1078                                         ScrArea *sa;
1079                                         G.scene->r.cfra = startFrame+globalBakeFrame;
1080                                         lastRedraw = globalBakeFrame;
1081                                         update_for_newframe_muted();
1082                                         sa= G.curscreen->areabase.first;
1083                                         while(sa) {
1084                                                 if(sa->spacetype == SPACE_VIEW3D) { scrarea_do_windraw(sa); }
1085                                                 sa= sa->next;   
1086                                         } 
1087                                         screen_swapbuffers();
1088                                 } // redraw
1089                         }
1090                         SDL_WaitThread(simthr,NULL);
1091                         end_progress_bar();
1092                 }
1093                 SDL_DestroyMutex(globalBakeLock);
1094                 globalBakeLock = NULL;
1095         } // El'Beem API init, thread creation 
1096         // --------------------------------------------------------------------------------------------
1097         else
1098         { // write config file to be run with command line simulator
1099                 pupmenu("Fluidsim Bake Message%t|Config file export not supported.%x0");
1100         } // config file export done!
1101
1102         // --------------------------------------------------------------------------------------------
1103         FS_FREE_CHANNELS;
1104
1105         // go back to "current" blender time
1106         waitcursor(0);
1107   G.scene->r.cfra = origFrame;
1108   scene_update_for_newframe(G.scene, G.scene->lay);
1109         allqueue(REDRAWVIEW3D, 0);
1110         allqueue(REDRAWBUTSOBJECT, 0);
1111
1112         if(!simAborted) {
1113                 char fsmessage[512];
1114                 char elbeemerr[256];
1115                 strcpy(fsmessage,"Fluidsim Bake Error: ");
1116                 // check if some error occurred
1117                 if(globalBakeState==-2) {
1118                         strcat(fsmessage,"Failed to initialize [Msg: ");
1119
1120                         elbeemGetErrorString(elbeemerr);
1121                         strcat(fsmessage,elbeemerr);
1122
1123                         strcat(fsmessage,"] |OK%x0");
1124                         pupmenu(fsmessage);
1125                 } // init error
1126         }
1127 }
1128
1129 void fluidsimFreeBake(struct Object *ob)
1130 {
1131         /* not implemented yet */
1132 }
1133
1134
1135 #else /* DISABLE_ELBEEM */
1136
1137 /* compile dummy functions for disabled fluid sim */
1138
1139 FluidsimSettings *fluidsimSettingsNew(struct Object *srcob) {
1140         return NULL;
1141 }
1142
1143 void fluidsimSettingsFree(FluidsimSettings *fss) {
1144 }
1145
1146 FluidsimSettings* fluidsimSettingsCopy(FluidsimSettings *fss) {
1147         return NULL;
1148 }
1149
1150 /* only compile dummy functions */
1151 void fluidsimBake(struct Object *ob) {
1152 }
1153
1154 void fluidsimFreeBake(struct Object *ob) {
1155 }
1156
1157 #endif /* DISABLE_ELBEEM */
1158