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