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