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