Merging r40615 through r40652 from trunk into soc-2011-tomato
[blender.git] / source / blender / editors / physics / physics_fluid.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 /** \file blender/editors/physics/physics_fluid.c
33  *  \ingroup edphys
34  */
35
36
37
38
39 #include <math.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/stat.h>
43
44 #include "MEM_guardedalloc.h"
45
46 /* types */
47 #include "DNA_anim_types.h"
48 #include "DNA_action_types.h"
49 #include "DNA_object_types.h"
50 #include "DNA_object_fluidsim.h"        
51
52 #include "BLI_blenlib.h"
53 #include "BLI_fileops.h"
54 #include "BLI_threads.h"
55 #include "BLI_math.h"
56 #include "BLI_utildefines.h"
57
58 #include "BKE_animsys.h"
59 #include "BKE_armature.h"
60 #include "BKE_blender.h"
61 #include "BKE_context.h"
62 #include "BKE_customdata.h"
63 #include "BKE_DerivedMesh.h"
64 #include "BKE_displist.h"
65 #include "BKE_effect.h"
66 #include "BKE_fluidsim.h"
67 #include "BKE_global.h"
68 #include "BKE_ipo.h"
69 #include "BKE_key.h"
70 #include "BKE_main.h"
71 #include "BKE_modifier.h"
72 #include "BKE_object.h"
73 #include "BKE_report.h"
74 #include "BKE_scene.h"
75 #include "BKE_softbody.h"
76 #include "BKE_unit.h"
77
78
79 #include "LBM_fluidsim.h"
80
81 #include "ED_screen.h"
82 #include "ED_fluidsim.h"
83
84 #include "WM_types.h"
85 #include "WM_api.h"
86
87 #include "physics_intern.h" // own include
88
89 /* enable/disable overall compilation */
90 #ifndef DISABLE_ELBEEM
91
92 #include "WM_api.h"
93
94 #include "DNA_scene_types.h"
95 #include "DNA_ipo_types.h"
96 #include "DNA_mesh_types.h"
97
98 #include "PIL_time.h"
99
100
101 static float get_fluid_viscosity(FluidsimSettings *settings)
102 {
103         switch (settings->viscosityMode) {
104                 case 0:         /* unused */
105                         return -1.0;
106                 case 2:         /* water */
107                         return 1.0e-6;
108                 case 3:         /* some (thick) oil */
109                         return 5.0e-5;
110                 case 4:         /* ca. honey */
111                         return 2.0e-3;
112                 case 1:         /* manual */
113                 default:
114                         return (1.0/pow(10.0, settings->viscosityExponent)) * settings->viscosityValue;
115         }
116 }
117
118 static void get_fluid_gravity(float *gravity, Scene *scene, FluidsimSettings *fss)
119 {
120         if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
121                 copy_v3_v3(gravity, scene->physics_settings.gravity);
122         } else {
123                 copy_v3_v3(gravity, fss->grav);
124         }
125 }
126
127 static float get_fluid_size_m(Scene *scene, Object *domainob, FluidsimSettings *fss)
128 {
129         if (!scene->unit.system) {
130                 return fss->realsize;
131         } else {
132                 float dim[3];
133                 float longest_axis;
134                 
135                 object_get_dimensions(domainob, dim);
136                 longest_axis = MAX3(dim[0], dim[1], dim[2]);
137                 
138                 return longest_axis * scene->unit.scale_length;
139         }
140 }
141
142 static int fluid_is_animated_mesh(FluidsimSettings *fss)
143 {
144         return ((fss->type == OB_FLUIDSIM_CONTROL) || fss->domainNovecgen);
145 }
146
147 /* ********************** fluid sim settings struct functions ********************** */
148
149 #if 0
150 /* helper function */
151 void fluidsimGetGeometryObjFilename(Object *ob, char *dst) { //, char *srcname) {
152         //BLI_snprintf(dst,FILE_MAXFILE, "%s_cfgdata_%s.bobj.gz", srcname, ob->id.name);
153         BLI_snprintf(dst,FILE_MAXFILE, "fluidcfgdata_%s.bobj.gz", ob->id.name);
154 }
155 #endif
156
157
158 /* ********************** fluid sim channel helper functions ********************** */
159
160 typedef struct FluidAnimChannels {
161         int length;
162         
163         double aniFrameTime;
164         
165         float *timeAtFrame;
166         float *DomainTime;
167         float *DomainGravity;
168         float *DomainViscosity;
169 } FluidAnimChannels;
170
171 typedef struct FluidObject {
172         struct FluidObject *next, *prev;
173         
174         struct Object *object;
175         
176         float *Translation;
177         float *Rotation;
178         float *Scale;
179         float *Active;
180         
181         float *InitialVelocity;
182         
183         float *AttractforceStrength;
184         float *AttractforceRadius;
185         float *VelocityforceStrength;
186         float *VelocityforceRadius;
187         
188         float *VertexCache;
189         int numVerts, numTris;
190 } FluidObject;
191
192 // no. of entries for the two channel sizes
193 #define CHANNEL_FLOAT 1
194 #define CHANNEL_VEC   3
195
196 // simplify channels before printing
197 // for API this is done anyway upon init
198 #if 0
199 static void fluidsimPrintChannel(FILE *file, float *channel, int paramsize, char *str, int entries) 
200
201         int i,j; 
202         int channelSize = paramsize; 
203
204         if(entries==3) {
205                 elbeemSimplifyChannelVec3( channel, &channelSize); 
206         } else if(entries==1) {
207                 elbeemSimplifyChannelFloat( channel, &channelSize); 
208         } else {
209                 // invalid, cant happen?
210         }
211
212         fprintf(file, "      CHANNEL %s = \n", str); 
213         for(i=0; i<channelSize;i++) { 
214                 fprintf(file,"        ");  
215                 for(j=0;j<=entries;j++) {  // also print time value
216                         fprintf(file," %f ", channel[i*(entries+1)+j] ); 
217                         if(j==entries-1){ fprintf(file,"  "); }
218                 } 
219                 fprintf(file," \n");  
220         } 
221
222         fprintf(file,  "      ; \n" ); 
223 }
224 #endif
225
226
227 /* Note: fluid anim channel data layout
228  * ------------------------------------
229  * CHANNEL_FLOAT:
230  * frame 1     |frame 2
231  * [dataF][time][dataF][time]
232  *
233  * CHANNEL_VEC:
234  * frame 1                   |frame 2
235  * [dataX][dataY][dataZ][time][dataX][dataY][dataZ][time]
236  *
237  */
238
239 static void init_time(FluidsimSettings *domainSettings, FluidAnimChannels *channels)
240 {
241         int i;
242         
243         channels->timeAtFrame = MEM_callocN( (channels->length+1)*sizeof(float), "timeAtFrame channel");
244         
245         channels->timeAtFrame[0] = channels->timeAtFrame[1] = domainSettings->animStart; // start at index 1
246         
247         for(i=2; i<=channels->length; i++) {
248                 channels->timeAtFrame[i] = channels->timeAtFrame[i-1] + channels->aniFrameTime;
249         }
250 }
251
252 /* if this is slow, can replace with faster, less readable code */
253 static void set_channel(float *channel, float time, float *value, int i, int size)
254 {
255         if (size == CHANNEL_FLOAT) {
256                 channel[(i * 2) + 0] = value[0];
257                 channel[(i * 2) + 1] = time;
258         }
259         else if (size == CHANNEL_VEC) {
260                 channel[(i * 4) + 0] = value[0];
261                 channel[(i * 4) + 1] = value[1];
262                 channel[(i * 4) + 2] = value[2];
263                 channel[(i * 4) + 3] = time;
264         }
265 }
266
267 static void set_vertex_channel(float *channel, float time, struct Scene *scene, struct FluidObject *fobj, int i)
268 {
269         Object *ob = fobj->object;
270         FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
271         float *verts;
272         int *tris=NULL, numVerts=0, numTris=0;
273         int modifierIndex = modifiers_indexInObject(ob, (ModifierData *)fluidmd);
274         int framesize = (3*fobj->numVerts) + 1;
275         int j;
276         
277         if (channel == NULL)
278                 return;
279         
280         initElbeemMesh(scene, ob, &numVerts, &verts, &numTris, &tris, 1, modifierIndex);
281         
282         /* don't allow mesh to change number of verts in anim sequence */
283         if (numVerts != fobj->numVerts) {
284                 MEM_freeN(channel);
285                 channel = NULL;
286                 return;
287         }
288         
289         /* fill frame of channel with vertex locations */
290         for(j=0; j < (3*numVerts); j++) {
291                 channel[i*framesize + j] = verts[j];
292         }
293         channel[i*framesize + framesize-1] = time;
294         
295         MEM_freeN(verts);
296         MEM_freeN(tris);
297 }
298
299 static void free_domain_channels(FluidAnimChannels *channels)
300 {
301         if (!channels->timeAtFrame)
302                 return;
303         MEM_freeN(channels->timeAtFrame);
304         channels->timeAtFrame = NULL;
305         MEM_freeN(channels->DomainGravity);
306         channels->DomainGravity = NULL;
307         MEM_freeN(channels->DomainViscosity);
308         channels->DomainViscosity = NULL;
309 }
310
311 static void free_all_fluidobject_channels(ListBase *fobjects)
312 {
313         FluidObject *fobj;
314         
315         for (fobj=fobjects->first; fobj; fobj=fobj->next) {
316                 if (fobj->Translation) {
317                         MEM_freeN(fobj->Translation);
318                         fobj->Translation = NULL;
319                         MEM_freeN(fobj->Rotation);
320                         fobj->Rotation = NULL;
321                         MEM_freeN(fobj->Scale);
322                         fobj->Scale = NULL;
323                         MEM_freeN(fobj->Active);
324                         fobj->Active = NULL;
325                         MEM_freeN(fobj->InitialVelocity);
326                         fobj->InitialVelocity = NULL;
327                 }
328                 
329                 if (fobj->AttractforceStrength) {
330                         MEM_freeN(fobj->AttractforceStrength);
331                         fobj->AttractforceStrength = NULL;
332                         MEM_freeN(fobj->AttractforceRadius);
333                         fobj->AttractforceRadius = NULL;
334                         MEM_freeN(fobj->VelocityforceStrength);
335                         fobj->VelocityforceStrength = NULL;
336                         MEM_freeN(fobj->VelocityforceRadius);
337                         fobj->VelocityforceRadius = NULL;
338                 }
339                 
340                 if (fobj->VertexCache) {
341                         MEM_freeN(fobj->VertexCache);
342                         fobj->VertexCache = NULL;
343                 }
344         }
345 }
346
347 static void fluid_init_all_channels(bContext *C, Object *UNUSED(fsDomain), FluidsimSettings *domainSettings, FluidAnimChannels *channels, ListBase *fobjects)
348 {
349         Scene *scene = CTX_data_scene(C);
350         Base *base;
351         int i;
352         int length = channels->length;
353         float eval_time;
354         
355         /* XXX: first init time channel - temporary for now */
356         /* init time values (should be done after evaluating animated time curve) */
357         init_time(domainSettings, channels);
358         
359         /* allocate domain animation channels */
360         channels->DomainGravity = MEM_callocN( length * (CHANNEL_VEC+1) * sizeof(float), "channel DomainGravity");
361         channels->DomainViscosity = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "channel DomainViscosity");
362         //channels->DomainTime = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "channel DomainTime");
363         
364         /* allocate fluid objects */
365         for (base=scene->base.first; base; base= base->next) {
366                 Object *ob = base->object;
367                 FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
368                 
369                 if (fluidmd) {
370                         FluidObject *fobj = MEM_callocN(sizeof(FluidObject), "Fluid Object");
371                         fobj->object = ob;
372                         
373                         if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE)) {
374                                 BLI_addtail(fobjects, fobj);
375                                 continue;
376                         }
377                         
378                         fobj->Translation = MEM_callocN( length * (CHANNEL_VEC+1) * sizeof(float), "fluidobject Translation");
379                         fobj->Rotation = MEM_callocN( length * (CHANNEL_VEC+1) * sizeof(float), "fluidobject Rotation");
380                         fobj->Scale = MEM_callocN( length * (CHANNEL_VEC+1) * sizeof(float), "fluidobject Scale");
381                         fobj->Active = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "fluidobject Active");
382                         fobj->InitialVelocity = MEM_callocN( length * (CHANNEL_VEC+1) * sizeof(float), "fluidobject InitialVelocity");
383                         
384                         if (fluidmd->fss->type == OB_FLUIDSIM_CONTROL) {
385                                 fobj->AttractforceStrength = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "fluidobject AttractforceStrength");
386                                 fobj->AttractforceRadius = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "fluidobject AttractforceRadius");
387                                 fobj->VelocityforceStrength = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "fluidobject VelocityforceStrength");
388                                 fobj->VelocityforceRadius = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "fluidobject VelocityforceRadius");
389                         }
390                         
391                         if (fluid_is_animated_mesh(fluidmd->fss)) {
392                                 float *verts=NULL;
393                                 int *tris=NULL, modifierIndex = modifiers_indexInObject(ob, (ModifierData *)fluidmd);
394
395                                 initElbeemMesh(scene, ob, &fobj->numVerts, &verts, &fobj->numTris, &tris, 0, modifierIndex);
396                                 fobj->VertexCache = MEM_callocN( length *((fobj->numVerts*CHANNEL_VEC)+1) * sizeof(float), "fluidobject VertexCache");
397                                 
398                                 MEM_freeN(verts);
399                                 MEM_freeN(tris);
400                         }
401                         
402                         BLI_addtail(fobjects, fobj);
403                 }
404         }
405         
406         /* now we loop over the frames and fill the allocated channels with data */
407         for (i=0; i<channels->length; i++) {
408                 FluidObject *fobj;
409                 float viscosity, gravity[3];
410                 float timeAtFrame;
411                 
412                 eval_time = domainSettings->bakeStart + i;
413                 timeAtFrame = channels->timeAtFrame[i+1];
414                 
415                 /* XXX: This can't be used due to an anim sys optimisation that ignores recalc object animation,
416                  * leaving it for the depgraph (this ignores object animation such as modifier properties though... :/ )
417                  * --> BKE_animsys_evaluate_all_animation(G.main, eval_time);
418                  * This doesn't work with drivers:
419                  * --> BKE_animsys_evaluate_animdata(&fsDomain->id, fsDomain->adt, eval_time, ADT_RECALC_ALL);
420                  */
421                 
422                 /* Modifying the global scene isn't nice, but we can do it in 
423                  * this part of the process before a threaded job is created */
424                 scene->r.cfra = (int)eval_time;
425                 ED_update_for_newframe(CTX_data_main(C), scene, CTX_wm_screen(C), 1);
426                 
427                 /* now scene data should be current according to animation system, so we fill the channels */
428                 
429                 /* Domain properties - gravity/viscosity/time */
430                 get_fluid_gravity(gravity, scene, domainSettings);
431                 set_channel(channels->DomainGravity, timeAtFrame, gravity, i, CHANNEL_VEC);
432                 viscosity = get_fluid_viscosity(domainSettings);
433                 set_channel(channels->DomainViscosity, timeAtFrame, &viscosity, i, CHANNEL_FLOAT);
434                 // XXX : set_channel(channels->DomainTime, timeAtFrame, &time, i, CHANNEL_VEC);
435                 
436                 /* object movement */
437                 for (fobj=fobjects->first; fobj; fobj=fobj->next) {
438                         Object *ob = fobj->object;
439                         FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
440                         float active= (float)(fluidmd->fss->flag & OB_FLUIDSIM_ACTIVE);
441                         float rot_d[3] = {0.f, 0.f, 0.f}, old_rot[3] = {0.f, 0.f, 0.f};
442                         
443                         if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE))
444                                 continue;
445                         
446                         /* init euler rotation values and convert to elbeem format */
447                         /* get the rotation from ob->obmat rather than ob->rot to account for parent animations */
448                         if(i) {
449                                 copy_v3_v3(old_rot, fobj->Rotation + 4*(i-1));
450                                 mul_v3_fl(old_rot, -M_PI/180.f);
451                         }
452
453                         mat4_to_compatible_eulO(rot_d, old_rot, 0, ob->obmat);
454                         mul_v3_fl(rot_d, -180.f/M_PI);
455                         
456                         set_channel(fobj->Translation, timeAtFrame, ob->loc, i, CHANNEL_VEC);
457                         set_channel(fobj->Rotation, timeAtFrame, rot_d, i, CHANNEL_VEC);
458                         set_channel(fobj->Scale, timeAtFrame, ob->size, i, CHANNEL_VEC);
459                         set_channel(fobj->Active, timeAtFrame, &active, i, CHANNEL_FLOAT);
460                         set_channel(fobj->InitialVelocity, timeAtFrame, &fluidmd->fss->iniVelx, i, CHANNEL_VEC);
461                         
462                         if (fluidmd->fss->type == OB_FLUIDSIM_CONTROL) {
463                                 set_channel(fobj->AttractforceStrength, timeAtFrame, &fluidmd->fss->attractforceStrength, i, CHANNEL_FLOAT);
464                                 set_channel(fobj->AttractforceRadius, timeAtFrame, &fluidmd->fss->attractforceRadius, i, CHANNEL_FLOAT);
465                                 set_channel(fobj->VelocityforceStrength, timeAtFrame, &fluidmd->fss->velocityforceStrength, i, CHANNEL_FLOAT);
466                                 set_channel(fobj->VelocityforceRadius, timeAtFrame, &fluidmd->fss->velocityforceRadius, i, CHANNEL_FLOAT);
467                         }
468                         
469                         if (fluid_is_animated_mesh(fluidmd->fss)) {
470                                 set_vertex_channel(fobj->VertexCache, timeAtFrame, scene, fobj, i);
471                         }
472                 }
473         }
474 }
475
476 static void export_fluid_objects(ListBase *fobjects, Scene *scene, int length)
477 {
478         FluidObject *fobj;
479         
480         for (fobj=fobjects->first; fobj; fobj=fobj->next) {
481                 Object *ob = fobj->object;
482                 FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
483                 int modifierIndex = modifiers_indexInObject(ob, (ModifierData *)fluidmd);
484                 
485                 float *verts=NULL;
486                 int *tris=NULL;
487                 int numVerts=0, numTris=0;
488                 int deform = fluid_is_animated_mesh(fluidmd->fss);
489                 
490                 elbeemMesh fsmesh;
491                 
492                 if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE))
493                         continue;
494                 
495                 elbeemResetMesh( &fsmesh );
496                 
497                 fsmesh.type = fluidmd->fss->type;
498                 fsmesh.name = ob->id.name;
499                 
500                 initElbeemMesh(scene, ob, &numVerts, &verts, &numTris, &tris, 0, modifierIndex);
501                 
502                 fsmesh.numVertices   = numVerts;
503                 fsmesh.numTriangles  = numTris;
504                 fsmesh.vertices      = verts;
505                 fsmesh.triangles     = tris;
506                 
507                 fsmesh.channelSizeTranslation  = 
508                 fsmesh.channelSizeRotation     = 
509                 fsmesh.channelSizeScale        = 
510                 fsmesh.channelSizeInitialVel   = 
511                 fsmesh.channelSizeActive       = length;
512                 
513                 fsmesh.channelTranslation      = fobj->Translation;
514                 fsmesh.channelRotation         = fobj->Rotation;
515                 fsmesh.channelScale            = fobj->Scale;
516                 fsmesh.channelActive           = fobj->Active;
517                 
518                 if( ELEM(fsmesh.type, OB_FLUIDSIM_FLUID, OB_FLUIDSIM_INFLOW)) {
519                         fsmesh.channelInitialVel = fobj->InitialVelocity;
520                         fsmesh.localInivelCoords = ((fluidmd->fss->typeFlags & OB_FSINFLOW_LOCALCOORD)?1:0);
521                 } 
522                 
523                 if(fluidmd->fss->typeFlags & OB_FSBND_NOSLIP)
524                         fsmesh.obstacleType = FLUIDSIM_OBSTACLE_NOSLIP;
525                 else if(fluidmd->fss->typeFlags & OB_FSBND_PARTSLIP)
526                         fsmesh.obstacleType = FLUIDSIM_OBSTACLE_PARTSLIP;
527                 else if(fluidmd->fss->typeFlags & OB_FSBND_FREESLIP)
528                         fsmesh.obstacleType = FLUIDSIM_OBSTACLE_FREESLIP;
529                 
530                 fsmesh.obstaclePartslip = fluidmd->fss->partSlipValue;
531                 fsmesh.volumeInitType = fluidmd->fss->volumeInitType;
532                 fsmesh.obstacleImpactFactor = fluidmd->fss->surfaceSmoothing; // misused value
533                 
534                 if (fsmesh.type == OB_FLUIDSIM_CONTROL) {
535                         fsmesh.cpsTimeStart = fluidmd->fss->cpsTimeStart;
536                         fsmesh.cpsTimeEnd = fluidmd->fss->cpsTimeEnd;
537                         fsmesh.cpsQuality = fluidmd->fss->cpsQuality;
538                         fsmesh.obstacleType = (fluidmd->fss->flag & OB_FLUIDSIM_REVERSE);
539                         
540                         fsmesh.channelSizeAttractforceRadius = 
541                         fsmesh.channelSizeVelocityforceStrength = 
542                         fsmesh.channelSizeVelocityforceRadius = 
543                         fsmesh.channelSizeAttractforceStrength = length;
544                         
545                         fsmesh.channelAttractforceStrength = fobj->AttractforceStrength;
546                         fsmesh.channelAttractforceRadius = fobj->AttractforceRadius;
547                         fsmesh.channelVelocityforceStrength = fobj->VelocityforceStrength;
548                         fsmesh.channelVelocityforceRadius = fobj->VelocityforceRadius;
549                 }
550                 else {
551                         fsmesh.channelAttractforceStrength =
552                         fsmesh.channelAttractforceRadius = 
553                         fsmesh.channelVelocityforceStrength = 
554                         fsmesh.channelVelocityforceRadius = NULL; 
555                 }
556                 
557                 /* animated meshes */
558                 if(deform) {
559                         fsmesh.channelSizeVertices = length;
560                         fsmesh.channelVertices = fobj->VertexCache;
561                                 
562                         // remove channels
563                         fsmesh.channelTranslation      = 
564                         fsmesh.channelRotation         = 
565                         fsmesh.channelScale            = NULL; 
566                 }
567                 
568                 elbeemAddMesh(&fsmesh);
569                 
570                 if(verts) MEM_freeN(verts);
571                 if(tris) MEM_freeN(tris);
572         }
573 }
574
575 static int fluid_validate_scene(ReportList *reports, Scene *scene, Object *fsDomain)
576 {
577         Base *base;
578         Object *newdomain = NULL;
579         int channelObjCount = 0;
580         int fluidInputCount = 0;
581
582         for(base=scene->base.first; base; base= base->next)
583         {
584                 Object *ob = base->object;
585                 FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);                    
586
587                 /* only find objects with fluid modifiers */
588                 if (!fluidmdtmp || ob->type != OB_MESH) continue;
589                         
590                 if(fluidmdtmp->fss->type == OB_FLUIDSIM_DOMAIN) {
591                         /* if no initial domain object given, find another potential domain */
592                         if (!fsDomain) {
593                                 newdomain = ob;
594                         }
595                         /* if there's more than one domain, cancel */
596                         else if (fsDomain && ob != fsDomain) {
597                                 BKE_report(reports, RPT_ERROR, "There should be only one domain object");
598                                 return 0;
599                         }
600                 }
601                 
602                 /* count number of objects needed for animation channels */
603                 if ( !ELEM(fluidmdtmp->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE) )
604                         channelObjCount++;
605                 
606                 /* count number of fluid input objects */
607                 if (ELEM(fluidmdtmp->fss->type, OB_FLUIDSIM_FLUID, OB_FLUIDSIM_INFLOW))
608                         fluidInputCount++;
609         }
610
611         if (newdomain)
612                 fsDomain = newdomain;
613         
614         if (!fsDomain) {
615                 BKE_report(reports, RPT_ERROR, "No domain object found");
616                 return 0;
617         }
618         
619         if (channelObjCount>=255) {
620                 BKE_report(reports, RPT_ERROR, "Cannot bake with more then 256 objects");
621                 return 0;
622         }
623         
624         if (fluidInputCount == 0) {
625                 BKE_report(reports, RPT_ERROR, "No fluid input objects in the scene");
626                 return 0;
627         }
628         
629         return 1;
630 }
631
632
633 #define FLUID_SUFFIX_CONFIG             "fluidsim.cfg"
634 #define FLUID_SUFFIX_SURFACE    "fluidsurface"
635
636 static int fluid_init_filepaths(Object *fsDomain, char *targetDir, char *targetFile, char *debugStrBuffer)
637 {
638         FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(fsDomain, eModifierType_Fluidsim);
639         FluidsimSettings *domainSettings= fluidmd->fss; 
640         FILE *fileCfg;
641         int dirExist = 0;
642         char newSurfdataPath[FILE_MAXDIR+FILE_MAXFILE]; // modified output settings
643         const char *suffixConfig = FLUID_SUFFIX_CONFIG;
644         int outStringsChanged = 0;
645         
646         // prepare names...
647         strncpy(targetDir, domainSettings->surfdataPath, FILE_MAXDIR);
648         strncpy(newSurfdataPath, domainSettings->surfdataPath, FILE_MAXDIR);
649         BLI_path_abs(targetDir, G.main->name); // fixed #frame-no 
650
651         // .tmp: dont overwrite/delete original file
652         BLI_snprintf(targetFile, FILE_MAXDIR+FILE_MAXFILE, "%s%s.tmp", targetDir, suffixConfig);
653
654         // make sure all directories exist
655         // as the bobjs use the same dir, this only needs to be checked
656         // for the cfg output
657         BLI_make_existing_file(targetFile);
658         
659         // check selected directory
660         // simply try to open cfg file for writing to test validity of settings
661         fileCfg = fopen(targetFile, "w");
662         if(fileCfg) { 
663                 dirExist = 1; fclose(fileCfg); 
664                 // remove cfg dummy from  directory test
665                 BLI_delete(targetFile, 0,0);
666         }
667         
668         if((strlen(targetDir)<1) || (!dirExist)) {
669                 char blendDir[FILE_MAXDIR+FILE_MAXFILE];
670                 char blendFile[FILE_MAXDIR+FILE_MAXFILE];
671                 
672                 // invalid dir, reset to current/previous
673                 BLI_strncpy(blendDir, G.main->name, FILE_MAXDIR+FILE_MAXFILE);
674                 BLI_splitdirstring(blendDir, blendFile);
675                 BLI_replace_extension(blendFile, FILE_MAXDIR+FILE_MAXFILE, ""); /* strip .blend */
676
677                 BLI_snprintf(newSurfdataPath, FILE_MAXDIR+FILE_MAXFILE ,"//fluidsimdata/%s_%s_", blendFile, fsDomain->id.name);
678                 
679                 BLI_snprintf(debugStrBuffer, 256, "fluidsimBake::error - warning resetting output dir to '%s'\n", newSurfdataPath);
680                 elbeemDebugOut(debugStrBuffer);
681                 outStringsChanged=1;
682         }
683         
684         // check if modified output dir is ok
685 #if 0
686         if(outStringsChanged) {
687                 char dispmsg[FILE_MAXDIR+FILE_MAXFILE+256];
688                 int  selection=0;
689                 BLI_strncpy(dispmsg,"Output settings set to: '", sizeof(dispmsg));
690                 strcat(dispmsg, newSurfdataPath);
691                 strcat(dispmsg, "'%t|Continue with changed settings%x1|Discard and abort%x0");
692                 
693                 // ask user if thats what he/she wants...
694                 selection = pupmenu(dispmsg);
695                 if(selection<1) return 0; // 0 from menu, or -1 aborted
696                 BLI_strncpy(targetDir, newSurfdataPath, sizeof(targetDir));
697                 strncpy(domainSettings->surfdataPath, newSurfdataPath, FILE_MAXDIR);
698                 BLI_path_abs(targetDir, G.main->name); // fixed #frame-no 
699         }
700 #endif  
701         return outStringsChanged;
702 }
703
704 /* ******************************************************************************** */
705 /* ********************** write fluidsim config to file ************************* */
706 /* ******************************************************************************** */
707
708 typedef struct FluidBakeJob {
709         /* from wmJob */
710         void *owner;
711         short *stop, *do_update;
712         float *progress;
713         int current_frame;
714         elbeemSimulationSettings *settings;
715 } FluidBakeJob;
716
717 static void fluidbake_free(void *customdata)
718 {
719         FluidBakeJob *fb= (FluidBakeJob *)customdata;
720         MEM_freeN(fb);
721 }
722
723 /* called by fluidbake, only to check job 'stop' value */
724 static int fluidbake_breakjob(void *customdata)
725 {
726         FluidBakeJob *fb= (FluidBakeJob *)customdata;
727
728         if(fb->stop && *(fb->stop))
729                 return 1;
730         
731         /* this is not nice yet, need to make the jobs list template better 
732          * for identifying/acting upon various different jobs */
733         /* but for now we'll reuse the render break... */
734         return (G.afbreek);
735 }
736
737 /* called by fluidbake, wmJob sends notifier */
738 static void fluidbake_updatejob(void *customdata, float progress)
739 {
740         FluidBakeJob *fb= (FluidBakeJob *)customdata;
741         
742         *(fb->do_update)= 1;
743         *(fb->progress)= progress;
744 }
745
746 static void fluidbake_startjob(void *customdata, short *stop, short *do_update, float *progress)
747 {
748         FluidBakeJob *fb= (FluidBakeJob *)customdata;
749         
750         fb->stop= stop;
751         fb->do_update = do_update;
752         fb->progress = progress;
753         
754         G.afbreek= 0;   /* XXX shared with render - replace with job 'stop' switch */
755         
756         elbeemSimulate();
757         *do_update= 1;
758         *stop = 0;
759 }
760
761 static void fluidbake_endjob(void *customdata)
762 {
763         FluidBakeJob *fb= (FluidBakeJob *)customdata;
764         
765         if (fb->settings) {
766                 MEM_freeN(fb->settings);
767                 fb->settings = NULL;
768         }
769 }
770
771 int runSimulationCallback(void *data, int status, int frame) {
772         FluidBakeJob *fb = (FluidBakeJob *)data;
773         elbeemSimulationSettings *settings = fb->settings;
774         
775         if (status == FLUIDSIM_CBSTATUS_NEWFRAME) {
776                 fluidbake_updatejob(fb, frame / (float)settings->noOfFrames);
777                 //printf("elbeem blender cb s%d, f%d, domainid:%d noOfFrames: %d \n", status,frame, settings->domainId, settings->noOfFrames ); // DEBUG
778         }
779         
780         if (fluidbake_breakjob(fb))  {
781                 return FLUIDSIM_CBRET_ABORT;
782         }
783         
784         return FLUIDSIM_CBRET_CONTINUE;
785 }
786
787 static void fluidbake_free_data(FluidAnimChannels *channels, ListBase *fobjects, elbeemSimulationSettings *fsset, FluidBakeJob *fb)
788 {
789         free_domain_channels(channels);
790         MEM_freeN(channels);
791         channels = NULL;
792
793         free_all_fluidobject_channels(fobjects);
794         BLI_freelistN(fobjects);
795         MEM_freeN(fobjects);
796         fobjects = NULL;
797         
798         if (fsset) {
799                 MEM_freeN(fsset);
800                 fsset = NULL;
801         }
802         
803         if (fb) {
804                 MEM_freeN(fb);
805                 fb = NULL;
806         }
807 }
808
809 /* copied from rna_fluidsim.c: fluidsim_find_lastframe() */
810 static void fluidsim_delete_until_lastframe(FluidsimSettings *fss)
811 {
812         char targetDir[FILE_MAXFILE+FILE_MAXDIR], targetFile[FILE_MAXFILE+FILE_MAXDIR];
813         char targetDirVel[FILE_MAXFILE+FILE_MAXDIR], targetFileVel[FILE_MAXFILE+FILE_MAXDIR];
814         char previewDir[FILE_MAXFILE+FILE_MAXDIR], previewFile[FILE_MAXFILE+FILE_MAXDIR];
815         int curFrame = 1, exists = 0;
816
817         BLI_snprintf(targetDir, sizeof(targetDir), "%sfluidsurface_final_####.bobj.gz", fss->surfdataPath);
818         BLI_snprintf(targetDirVel, sizeof(targetDir), "%sfluidsurface_final_####.bvel.gz", fss->surfdataPath);
819         BLI_snprintf(previewDir, sizeof(targetDir), "%sfluidsurface_preview_####.bobj.gz", fss->surfdataPath);
820
821         BLI_path_abs(targetDir, G.main->name);
822         BLI_path_abs(targetDirVel, G.main->name);
823         BLI_path_abs(previewDir, G.main->name);
824
825         do {
826                 BLI_strncpy(targetFile, targetDir, sizeof(targetFile));
827                 BLI_strncpy(targetFileVel, targetDirVel, sizeof(targetFileVel));
828                 BLI_strncpy(previewFile, previewDir, sizeof(previewFile));
829
830                 BLI_path_frame(targetFile, curFrame, 0);
831                 BLI_path_frame(targetFileVel, curFrame, 0);
832                 BLI_path_frame(previewFile, curFrame, 0);
833
834                 curFrame++;
835
836                 if((exists = BLI_exist(targetFile)))
837                 {
838                         BLI_delete(targetFile, 0, 0);
839                         BLI_delete(targetFileVel, 0, 0);
840                         BLI_delete(previewFile, 0, 0);
841                 }
842         } while(exists);
843
844         return;
845 }
846
847 static int fluidsimBake(bContext *C, ReportList *reports, Object *fsDomain, short do_job)
848 {
849         Scene *scene= CTX_data_scene(C);
850         int i;
851         FluidsimSettings *domainSettings;
852
853         char debugStrBuffer[256];
854         
855         int gridlevels = 0;
856         const char *strEnvName = "BLENDER_ELBEEMDEBUG"; // from blendercall.cpp
857         const char *suffixConfig = FLUID_SUFFIX_CONFIG;
858         const char *suffixSurface = FLUID_SUFFIX_SURFACE;
859
860         char targetDir[FILE_MAXDIR+FILE_MAXFILE];  // store & modify output settings
861         char targetFile[FILE_MAXDIR+FILE_MAXFILE]; // temp. store filename from targetDir for access
862         int  outStringsChanged = 0;             // modified? copy back before baking
863
864         float domainMat[4][4];
865         float invDomMat[4][4];
866
867         int noFrames;
868         int origFrame = scene->r.cfra;
869         
870         FluidAnimChannels *channels = MEM_callocN(sizeof(FluidAnimChannels), "fluid domain animation channels");
871         ListBase *fobjects = MEM_callocN(sizeof(ListBase), "fluid objects");
872         FluidsimModifierData *fluidmd = NULL;
873         Mesh *mesh = NULL;
874
875         FluidBakeJob *fb;
876         elbeemSimulationSettings *fsset= MEM_callocN(sizeof(elbeemSimulationSettings), "Fluid sim settings");
877
878         fb= MEM_callocN(sizeof(FluidBakeJob), "fluid bake job");
879         
880         if(getenv(strEnvName)) {
881                 int dlevel = atoi(getenv(strEnvName));
882                 elbeemSetDebugLevel(dlevel);
883                 BLI_snprintf(debugStrBuffer,256,"fluidsimBake::msg: Debug messages activated due to envvar '%s'\n",strEnvName); 
884                 elbeemDebugOut(debugStrBuffer);
885         }
886         
887         /* make sure it corresponds to startFrame setting (old: noFrames = scene->r.efra - scene->r.sfra +1) */;
888         noFrames = scene->r.efra - 0;
889         if(noFrames<=0) {
890                 BKE_report(reports, RPT_ERROR, "No frames to export - check your animation range settings");
891                 fluidbake_free_data(channels, fobjects, fsset, fb);
892                 return 0;
893         }
894         
895         /* check scene for sane object/modifier settings */
896         if (!fluid_validate_scene(reports, scene, fsDomain)) {
897                 fluidbake_free_data(channels, fobjects, fsset, fb);
898                 return 0;
899         }
900         
901         /* these both have to be valid, otherwise we wouldnt be here */
902         fluidmd = (FluidsimModifierData *)modifiers_findByType(fsDomain, eModifierType_Fluidsim);
903         domainSettings = fluidmd->fss;
904         mesh = fsDomain->data;
905         
906         domainSettings->bakeStart = 1;
907         domainSettings->bakeEnd = scene->r.efra;
908         
909         // calculate bounding box
910         fluid_get_bb(mesh->mvert, mesh->totvert, fsDomain->obmat, domainSettings->bbStart, domainSettings->bbSize);
911         
912         // reset last valid frame
913         domainSettings->lastgoodframe = -1;
914
915         /* delete old baked files */
916         fluidsim_delete_until_lastframe(domainSettings);
917         
918         /* rough check of settings... */
919         if(domainSettings->previewresxyz > domainSettings->resolutionxyz) {
920                 BLI_snprintf(debugStrBuffer,256,"fluidsimBake::warning - Preview (%d) >= Resolution (%d)... setting equal.\n", domainSettings->previewresxyz ,  domainSettings->resolutionxyz); 
921                 elbeemDebugOut(debugStrBuffer);
922                 domainSettings->previewresxyz = domainSettings->resolutionxyz;
923         }
924         // set adaptive coarsening according to resolutionxyz
925         // this should do as an approximation, with in/outflow
926         // doing this more accurate would be overkill
927         // perhaps add manual setting?
928         if(domainSettings->maxRefine <0) {
929                 if(domainSettings->resolutionxyz>128) {
930                         gridlevels = 2;
931                 } else
932                 if(domainSettings->resolutionxyz>64) {
933                         gridlevels = 1;
934                 } else {
935                         gridlevels = 0;
936                 }
937         } else {
938                 gridlevels = domainSettings->maxRefine;
939         }
940         BLI_snprintf(debugStrBuffer,256,"fluidsimBake::msg: Baking %s, refine: %d\n", fsDomain->id.name , gridlevels ); 
941         elbeemDebugOut(debugStrBuffer);
942         
943         
944         
945         /* ******** prepare output file paths ******** */
946         outStringsChanged = fluid_init_filepaths(fsDomain, targetDir, targetFile, debugStrBuffer);
947         channels->length = scene->r.efra;
948         channels->aniFrameTime = (domainSettings->animEnd - domainSettings->animStart)/(double)noFrames;
949         
950         /* ******** initialise and allocate animation channels ******** */
951         fluid_init_all_channels(C, fsDomain, domainSettings, channels, fobjects);
952
953         /* reset to original current frame */
954         scene->r.cfra = origFrame;
955         ED_update_for_newframe(CTX_data_main(C), scene, CTX_wm_screen(C), 1);
956         
957         
958         /* ---- XXX: No Time animation curve for now, leaving this code here for reference 
959          
960         { int timeIcu[1] = { FLUIDSIM_TIME };
961                 float timeDef[1] = { 1. };
962
963                 // time channel is a bit special, init by hand...
964                 timeAtIndex = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatindex");
965                 for(i=0; i<=scene->r.efra; i++) {
966                         timeAtIndex[i] = (float)(i-startFrame);
967                 }
968                 fluidsimInitChannel(scene, &channelDomainTime, allchannelSize, timeAtIndex, timeIcu,timeDef, domainSettings->ipo, CHANNEL_FLOAT ); // NDEB
969                 // time channel is a multiplicator for 
970                 if(channelDomainTime) {
971                         for(i=0; i<allchannelSize; i++) { 
972                                 channelDomainTime[i*2+0] = aniFrameTime * channelDomainTime[i*2+0]; 
973                                 if(channelDomainTime[i*2+0]<0.) channelDomainTime[i*2+0] = 0.;
974                         }
975                 }
976                 timeAtFrame = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatframe");
977                 timeAtFrame[0] = timeAtFrame[1] = domainSettings->animStart; // start at index 1
978                 if(channelDomainTime) {
979                         for(i=2; i<=allchannelSize; i++) {
980                                 timeAtFrame[i] = timeAtFrame[i-1]+channelDomainTime[(i-1)*2+0];
981                         }
982                 fsset->} else {
983                         for(i=2; i<=allchannelSize; i++) { timeAtFrame[i] = timeAtFrame[i-1]+aniFrameTime; }
984                 }
985
986         } // domain channel init
987         */
988                 
989         /* ******** init domain object's matrix ******** */
990         copy_m4_m4(domainMat, fsDomain->obmat);
991         if(!invert_m4_m4(invDomMat, domainMat)) {
992                 BLI_snprintf(debugStrBuffer,256,"fluidsimBake::error - Invalid obj matrix?\n"); 
993                 elbeemDebugOut(debugStrBuffer);
994                 BKE_report(reports, RPT_ERROR, "Invalid object matrix"); 
995
996                 fluidbake_free_data(channels, fobjects, fsset, fb);
997                 return 0;
998         }
999
1000         /* ********  start writing / exporting ******** */
1001         // use .tmp, dont overwrite/delete original file
1002         BLI_snprintf(targetFile, 240, "%s%s.tmp", targetDir, suffixConfig);
1003         
1004         // make sure these directories exist as well
1005         if(outStringsChanged) {
1006                 BLI_make_existing_file(targetFile);
1007         }
1008
1009         /* ******** export domain to elbeem ******** */
1010         elbeemResetSettings(fsset);
1011         fsset->version = 1;
1012
1013         // setup global settings
1014         copy_v3_v3(fsset->geoStart, domainSettings->bbStart);
1015         copy_v3_v3(fsset->geoSize, domainSettings->bbSize);
1016         
1017         // simulate with 50^3
1018         fsset->resolutionxyz = (int)domainSettings->resolutionxyz;
1019         fsset->previewresxyz = (int)domainSettings->previewresxyz;
1020
1021         fsset->realsize = get_fluid_size_m(scene, fsDomain, domainSettings);
1022         fsset->viscosity = get_fluid_viscosity(domainSettings);
1023         get_fluid_gravity(fsset->gravity, scene, domainSettings);
1024
1025         // simulate 5 frames, each 0.03 seconds, output to ./apitest_XXX.bobj.gz
1026         fsset->animStart = domainSettings->animStart;
1027         fsset->aniFrameTime = channels->aniFrameTime;
1028         fsset->noOfFrames = noFrames; // is otherwise subtracted in parser
1029
1030         BLI_snprintf(targetFile, 240, "%s%s", targetDir, suffixSurface);
1031
1032         // defaults for compressibility and adaptive grids
1033         fsset->gstar = domainSettings->gstar;
1034         fsset->maxRefine = domainSettings->maxRefine; // check <-> gridlevels
1035         fsset->generateParticles = domainSettings->generateParticles; 
1036         fsset->numTracerParticles = domainSettings->generateTracers; 
1037         fsset->surfaceSmoothing = domainSettings->surfaceSmoothing; 
1038         fsset->surfaceSubdivs = domainSettings->surfaceSubdivs; 
1039         fsset->farFieldSize = domainSettings->farFieldSize; 
1040         BLI_strncpy(fsset->outputPath, targetFile, 240);
1041
1042         // domain channels
1043         fsset->channelSizeFrameTime = 
1044         fsset->channelSizeViscosity = 
1045         fsset->channelSizeGravity = channels->length;
1046         fsset->channelFrameTime = channels->DomainTime;
1047         fsset->channelViscosity = channels->DomainViscosity;
1048         fsset->channelGravity = channels->DomainGravity;
1049         
1050         fsset->runsimCallback = &runSimulationCallback;
1051         fsset->runsimUserData = fb;
1052
1053         if (domainSettings->typeFlags & OB_FSBND_NOSLIP)                fsset->domainobsType = FLUIDSIM_OBSTACLE_NOSLIP;
1054         else if (domainSettings->typeFlags&OB_FSBND_PARTSLIP)   fsset->domainobsType = FLUIDSIM_OBSTACLE_PARTSLIP;
1055         else if (domainSettings->typeFlags&OB_FSBND_FREESLIP)   fsset->domainobsType = FLUIDSIM_OBSTACLE_FREESLIP;
1056         fsset->domainobsPartslip = domainSettings->partSlipValue;
1057
1058         /* use domainobsType also for surface generation flag (bit: >=64) */
1059         if(domainSettings->typeFlags & OB_FSSG_NOOBS)
1060                 fsset->mFsSurfGenSetting = FLUIDSIM_FSSG_NOOBS;
1061         else
1062                 fsset->mFsSurfGenSetting = 0; // "normal" mode
1063
1064         fsset->generateVertexVectors = (domainSettings->domainNovecgen==0);
1065
1066         // init blender domain transform matrix
1067         { int j; 
1068         for(i=0; i<4; i++) {
1069                 for(j=0; j<4; j++) {
1070                         fsset->surfaceTrafo[i*4+j] = invDomMat[j][i];
1071                 }
1072         } }
1073
1074         /* ******** init solver with settings ******** */
1075         elbeemInit();
1076         elbeemAddDomain(fsset);
1077         
1078         /* ******** export all fluid objects to elbeem ******** */
1079         export_fluid_objects(fobjects, scene, channels->length);
1080         
1081         /* custom data for fluid bake job */
1082         fb->settings = fsset;
1083         
1084         if(do_job) {
1085                 wmJob *steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Fluid Simulation", WM_JOB_PROGRESS);
1086
1087                 /* setup job */
1088                 WM_jobs_customdata(steve, fb, fluidbake_free);
1089                 WM_jobs_timer(steve, 0.1, NC_SCENE|ND_FRAME, NC_SCENE|ND_FRAME);
1090                 WM_jobs_callbacks(steve, fluidbake_startjob, NULL, NULL, fluidbake_endjob);
1091
1092                 WM_jobs_start(CTX_wm_manager(C), steve);
1093         }
1094         else {
1095                 short dummy_stop, dummy_do_update;
1096                 float dummy_progress;
1097
1098                 /* blocking, use with exec() */
1099                 fluidbake_startjob((void *)fb, &dummy_stop, &dummy_do_update, &dummy_progress);
1100                 fluidbake_endjob((void *)fb);
1101                 fluidbake_free((void *)fb);
1102         }
1103
1104         /* ******** free stored animation data ******** */
1105         fluidbake_free_data(channels, fobjects, NULL, NULL);
1106
1107         // elbeemFree();
1108         return 1;
1109 }
1110
1111 void fluidsimFreeBake(Object *UNUSED(ob))
1112 {
1113         /* not implemented yet */
1114 }
1115
1116 #else /* DISABLE_ELBEEM */
1117
1118 /* compile dummy functions for disabled fluid sim */
1119
1120 FluidsimSettings *fluidsimSettingsNew(Object *UNUSED(srcob))
1121 {
1122         return NULL;
1123 }
1124
1125 void fluidsimSettingsFree(FluidsimSettings *UNUSED(fss))
1126 {
1127 }
1128
1129 FluidsimSettings* fluidsimSettingsCopy(FluidsimSettings *UNUSED(fss))
1130 {
1131         return NULL;
1132 }
1133
1134 /* only compile dummy functions */
1135 static int fluidsimBake(bContext *UNUSED(C), ReportList *UNUSED(reports), Object *UNUSED(ob), short UNUSED(do_job))
1136 {
1137         return 0;
1138 }
1139
1140 #endif /* DISABLE_ELBEEM */
1141
1142 /***************************** Operators ******************************/
1143
1144 static int fluid_bake_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1145 {
1146         /* only one bake job at a time */
1147         if(WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
1148                 return OPERATOR_CANCELLED;
1149
1150         if(!fluidsimBake(C, op->reports, CTX_data_active_object(C), TRUE))
1151                 return OPERATOR_CANCELLED;
1152
1153         return OPERATOR_FINISHED;
1154 }
1155
1156 static int fluid_bake_exec(bContext *C, wmOperator *op)
1157 {
1158         if(!fluidsimBake(C, op->reports, CTX_data_active_object(C), FALSE))
1159                 return OPERATOR_CANCELLED;
1160
1161         return OPERATOR_FINISHED;
1162 }
1163
1164 void FLUID_OT_bake(wmOperatorType *ot)
1165 {
1166         /* identifiers */
1167         ot->name= "Fluid Simulation Bake";
1168         ot->description= "Bake fluid simulation";
1169         ot->idname= "FLUID_OT_bake";
1170         
1171         /* api callbacks */
1172         ot->invoke= fluid_bake_invoke;
1173         ot->exec= fluid_bake_exec;
1174         ot->poll= ED_operator_object_active_editable;
1175 }
1176