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