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