Merge branch 'master' into blender2.8
[blender.git] / source / blender / modifiers / intern / MOD_fluidsim_util.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) 2005 by the Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Daniel Dunbar
22  *                 Ton Roosendaal,
23  *                 Ben Batt,
24  *                 Brecht Van Lommel,
25  *                 Campbell Barton
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  *
29  */
30
31 /** \file blender/modifiers/intern/MOD_fluidsim_util.c
32  *  \ingroup modifiers
33  */
34
35
36 #include <stddef.h>
37 #include <zlib.h>
38
39 #include "DNA_object_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_mesh_types.h"
42 #include "DNA_meshdata_types.h"
43 #include "DNA_object_fluidsim_types.h"
44
45 #include "BLI_blenlib.h"
46 #include "BLI_math.h"
47 #include "BLI_utildefines.h"
48
49 #include "BKE_fluidsim.h" /* ensure definitions here match */
50 #include "BKE_cdderivedmesh.h"
51 #include "BKE_main.h"
52 #ifdef WITH_MOD_FLUID
53 #  include "BKE_global.h"
54 #endif
55
56 #include "DEG_depsgraph.h"
57 #include "DEG_depsgraph_query.h"
58
59 #include "MOD_fluidsim_util.h"
60 #include "MOD_modifiertypes.h"
61
62 #include "MEM_guardedalloc.h"
63
64 // headers for fluidsim bobj meshes
65 #include "LBM_fluidsim.h"
66
67
68 void fluidsim_init(FluidsimModifierData *fluidmd)
69 {
70 #ifdef WITH_MOD_FLUID
71         if (fluidmd) {
72                 FluidsimSettings *fss = MEM_callocN(sizeof(FluidsimSettings), "fluidsimsettings");
73
74                 fluidmd->fss = fss;
75                 
76                 if (!fss)
77                         return;
78                 
79                 fss->fmd = fluidmd;
80                 fss->type = OB_FLUIDSIM_ENABLE;
81                 fss->threads = 0;
82                 fss->show_advancedoptions = 0;
83
84                 fss->resolutionxyz = 65;
85                 fss->previewresxyz = 45;
86                 fss->realsize = 0.5;
87                 fss->guiDisplayMode = OB_FSDOM_PREVIEW;
88                 fss->renderDisplayMode = OB_FSDOM_FINAL;
89
90                 fss->viscosityValue = 1.0;
91                 fss->viscosityExponent = 6;
92                 
93                 fss->grav[0] = 0.0;
94                 fss->grav[1] = 0.0;
95                 fss->grav[2] = -9.81;
96
97                 fss->animStart = 0.0; 
98                 fss->animEnd = 4.0;
99                 fss->animRate = 1.0;
100                 fss->gstar = 0.005; // used as normgstar
101                 fss->maxRefine = -1;
102                 /* maxRefine is set according to resolutionxyz during bake */
103
104                 /* fluid/inflow settings
105                  * fss->iniVel --> automatically set to 0 */
106
107                 modifier_path_init(fss->surfdataPath, sizeof(fss->surfdataPath), OB_FLUIDSIM_SURF_DIR_DEFAULT);
108
109                 /* first init of bounding box */
110                 /* no bounding box needed */
111
112                 /* todo - reuse default init from elbeem! */
113                 fss->typeFlags = OB_FSBND_PARTSLIP | OB_FSSG_NOOBS;
114                 fss->domainNovecgen = 0;
115                 fss->volumeInitType = 1; /* volume */
116                 fss->partSlipValue = 0.2;
117
118                 fss->generateTracers = 0;
119                 fss->generateParticles = 0.0;
120                 fss->surfaceSmoothing = 1.0;
121                 fss->surfaceSubdivs = 0.0;
122                 fss->particleInfSize = 0.0;
123                 fss->particleInfAlpha = 0.0;
124
125                 /* init fluid control settings */
126                 fss->attractforceStrength = 0.2;
127                 fss->attractforceRadius = 0.75;
128                 fss->velocityforceStrength = 0.2;
129                 fss->velocityforceRadius = 0.75;
130                 fss->cpsTimeStart = fss->animStart;
131                 fss->cpsTimeEnd = fss->animEnd;
132                 fss->cpsQuality = 10.0; // 1.0 / 10.0 => means 0.1 width
133                 
134                 /*
135                  * BAD TODO: this is done in buttons_object.c in the moment
136                  * Mesh *mesh = ob->data;
137                  * // calculate bounding box
138                  * fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize);
139                  */
140                 
141                 fss->meshVelocities = NULL;
142                 
143                 fss->lastgoodframe = -1;
144                 
145                 fss->flag |= OB_FLUIDSIM_ACTIVE;
146
147         }
148 #else
149         (void)fluidmd; /* unused */
150 #endif
151         return;
152 }
153
154 void fluidsim_free(FluidsimModifierData *fluidmd)
155 {
156         if (fluidmd && fluidmd->fss) {
157                 if (fluidmd->fss->meshVelocities) {
158                         MEM_freeN(fluidmd->fss->meshVelocities);
159                 }
160                 MEM_SAFE_FREE(fluidmd->fss);
161         }
162
163         /* Seems to never be used, but for sqke of consistency... */
164         BLI_assert(fluidmd->point_cache == NULL);
165         fluidmd->point_cache = NULL;
166         
167         return;
168 }
169
170 #ifdef WITH_MOD_FLUID
171 /* read .bobj.gz file into a fluidsimDerivedMesh struct */
172 static DerivedMesh *fluidsim_read_obj(const char *filename, const MPoly *mp_example)
173 {
174         int wri = 0, i;
175         int gotBytes;
176         gzFile gzf;
177         int numverts = 0, numfaces = 0;
178         DerivedMesh *dm = NULL;
179         MPoly *mp;
180         MLoop *ml;
181         MVert *mv;
182         short *normals, *no_s;
183         float no[3];
184
185         const short mp_mat_nr = mp_example->mat_nr;
186         const char mp_flag =   mp_example->flag;
187
188         /* ------------------------------------------------
189          * get numverts + numfaces first
190          * ------------------------------------------------ */
191         gzf = BLI_gzopen(filename, "rb");
192         if (!gzf) {
193                 return NULL;
194         }
195
196         /* read numverts */
197         gotBytes = gzread(gzf, &wri, sizeof(wri));
198         numverts = wri;
199
200         /* skip verts */
201         gotBytes = gzseek(gzf, numverts * 3 * sizeof(float), SEEK_CUR) != -1;
202
203
204         /* read number of normals */
205         if (gotBytes)
206                 gotBytes = gzread(gzf, &wri, sizeof(wri));
207
208         /* skip normals */
209         gotBytes = gzseek(gzf, numverts * 3 * sizeof(float), SEEK_CUR) != -1;
210
211         /* get no. of triangles */
212         if (gotBytes)
213                 gotBytes = gzread(gzf, &wri, sizeof(wri));
214         numfaces = wri;
215
216         gzclose(gzf);
217         /* ------------------------------------------------ */
218
219         if (!numfaces || !numverts || !gotBytes)
220                 return NULL;
221
222         gzf = BLI_gzopen(filename, "rb");
223         if (!gzf) {
224                 return NULL;
225         }
226
227         dm = CDDM_new(numverts, 0, 0, numfaces * 3, numfaces);
228
229         if (!dm) {
230                 gzclose(gzf);
231                 return NULL;
232         }
233
234         /* read numverts */
235         gotBytes = gzread(gzf, &wri, sizeof(wri));
236
237         /* read vertex position from file */
238         mv = CDDM_get_verts(dm);
239
240         for (i = 0; i < numverts; i++, mv++)
241                 gotBytes = gzread(gzf, mv->co, sizeof(float) * 3);
242
243         /* should be the same as numverts */
244         gotBytes = gzread(gzf, &wri, sizeof(wri));
245         if (wri != numverts) {
246                 if (dm)
247                         dm->release(dm);
248                 gzclose(gzf);
249                 return NULL;
250         }
251
252         normals = MEM_calloc_arrayN(numverts, 3 * sizeof(short), "fluid_tmp_normals");
253         if (!normals) {
254                 if (dm)
255                         dm->release(dm);
256                 gzclose(gzf);
257                 return NULL;
258         }
259
260         /* read normals from file (but don't save them yet) */
261         for (i = numverts, no_s = normals; i > 0; i--, no_s += 3) {
262                 gotBytes = gzread(gzf, no, sizeof(float) * 3);
263                 normal_float_to_short_v3(no_s, no);
264         }
265
266         /* read no. of triangles */
267         gotBytes = gzread(gzf, &wri, sizeof(wri));
268
269         if (wri != numfaces) {
270                 printf("Fluidsim: error in reading data from file.\n");
271                 if (dm)
272                         dm->release(dm);
273                 gzclose(gzf);
274                 MEM_freeN(normals);
275                 return NULL;
276         }
277
278         /* read triangles from file */
279         mp = CDDM_get_polys(dm);
280         ml = CDDM_get_loops(dm);
281         for (i = 0; i < numfaces; i++, mp++, ml += 3) {
282                 int face[3];
283
284                 gotBytes = gzread(gzf, face, sizeof(int) * 3);
285
286                 /* initialize from existing face */
287                 mp->mat_nr = mp_mat_nr;
288                 mp->flag =   mp_flag;
289
290                 mp->loopstart = i * 3;
291                 mp->totloop = 3;
292
293                 ml[0].v = face[0];
294                 ml[1].v = face[1];
295                 ml[2].v = face[2];
296
297         }
298
299         gzclose(gzf);
300
301         CDDM_calc_edges(dm);
302
303         CDDM_apply_vert_normals(dm, (short (*)[3])normals);
304         MEM_freeN(normals);
305
306         // CDDM_calc_normals(result);
307         return dm;
308 }
309
310
311 void fluid_get_bb(
312         MVert *mvert, int totvert, float obmat[4][4],
313         /*RET*/ float start[3], /*RET*/ float size[3])
314 {
315         float bbsx = 0.0, bbsy = 0.0, bbsz = 0.0;
316         float bbex = 1.0, bbey = 1.0, bbez = 1.0;
317         int i;
318         float vec[3];
319
320         if (totvert == 0) {
321                 zero_v3(start);
322                 zero_v3(size);
323                 return;
324         }
325
326         copy_v3_v3(vec, mvert[0].co);
327         mul_m4_v3(obmat, vec);
328         bbsx = vec[0]; bbsy = vec[1]; bbsz = vec[2];
329         bbex = vec[0]; bbey = vec[1]; bbez = vec[2];
330
331         for (i = 1; i < totvert; i++) {
332                 copy_v3_v3(vec, mvert[i].co);
333                 mul_m4_v3(obmat, vec);
334
335                 if (vec[0] < bbsx) { bbsx = vec[0]; }
336                 if (vec[1] < bbsy) { bbsy = vec[1]; }
337                 if (vec[2] < bbsz) { bbsz = vec[2]; }
338                 if (vec[0] > bbex) { bbex = vec[0]; }
339                 if (vec[1] > bbey) { bbey = vec[1]; }
340                 if (vec[2] > bbez) { bbez = vec[2]; }
341         }
342
343         /* return values... */
344         if (start) {
345                 start[0] = bbsx;
346                 start[1] = bbsy;
347                 start[2] = bbsz;
348         }
349         if (size) {
350                 size[0] = bbex - bbsx;
351                 size[1] = bbey - bbsy;
352                 size[2] = bbez - bbsz;
353         }
354 }
355
356 //-------------------------------------------------------------------------------
357 // old interface
358 //-------------------------------------------------------------------------------
359
360 void fluid_estimate_memory(Object *ob, FluidsimSettings *fss, char *value)
361 {
362         Mesh *mesh;
363
364         value[0] = '\0';
365
366         if (ob->type == OB_MESH) {
367                 /* use mesh bounding box and object scaling */
368                 mesh = ob->data;
369
370                 fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize);
371                 elbeemEstimateMemreq(fss->resolutionxyz, fss->bbSize[0], fss->bbSize[1], fss->bbSize[2], fss->maxRefine, value);
372         }
373 }
374
375
376 /* read zipped fluidsim velocities into the co's of the fluidsimsettings normals struct */
377 static void fluidsim_read_vel_cache(FluidsimModifierData *fluidmd, DerivedMesh *dm, char *filename)
378 {
379         int wri, i, j;
380         float wrf;
381         gzFile gzf;
382         FluidsimSettings *fss = fluidmd->fss;
383         int len = strlen(filename);
384         int totvert = dm->getNumVerts(dm);
385         FluidVertexVelocity *velarray = NULL;
386
387         /* mesh and vverts have to be valid from loading... */
388
389         if (fss->meshVelocities)
390                 MEM_freeN(fss->meshVelocities);
391
392         if (len < 7) {
393                 return;
394         }
395
396         if (fss->domainNovecgen > 0) return;
397
398         fss->meshVelocities = MEM_calloc_arrayN(dm->getNumVerts(dm), sizeof(FluidVertexVelocity), "Fluidsim_velocities");
399         fss->totvert = totvert;
400
401         velarray = fss->meshVelocities;
402
403         /* .bobj.gz, correct filename
404          * 87654321 */
405         filename[len - 6] = 'v';
406         filename[len - 5] = 'e';
407         filename[len - 4] = 'l';
408
409         gzf = BLI_gzopen(filename, "rb");
410         if (!gzf) {
411                 MEM_freeN(fss->meshVelocities);
412                 fss->meshVelocities = NULL;
413                 return;
414         }
415
416         gzread(gzf, &wri, sizeof(wri));
417         if (wri != totvert) {
418                 MEM_freeN(fss->meshVelocities);
419                 fss->meshVelocities = NULL;
420                 return;
421         }
422
423         for (i = 0; i < totvert; i++) {
424                 for (j = 0; j < 3; j++) {
425                         gzread(gzf, &wrf, sizeof(wrf));
426                         velarray[i].vel[j] = wrf;
427                 }
428         }
429
430         gzclose(gzf);
431 }
432
433 static DerivedMesh *fluidsim_read_cache(
434         Object *ob, DerivedMesh *orgdm,
435         FluidsimModifierData *fluidmd, int framenr, int useRenderParams)
436 {
437         int curFrame = framenr /* - 1 */ /*scene->r.sfra*/; /* start with 0 at start frame */ 
438         /*  why start with 0 as start frame?? Animations + time are frozen for frame 0 anyway. (See physics_fluid.c for that. - DG */
439         /* If we start with frame 0, we need to remap all animation channels, too, because they will all be 1 frame late if using frame-1! - DG */
440
441         char targetFile[FILE_MAX];
442         FluidsimSettings *fss = fluidmd->fss;
443         DerivedMesh *dm = NULL;
444         MPoly *mpoly;
445         MPoly mp_example = {0};
446
447         const int displaymode = useRenderParams ? fss->renderDisplayMode : fss->guiDisplayMode;
448
449         switch (displaymode) {
450                 case OB_FSDOM_GEOM:
451                         /* just display original object */
452                         return NULL;
453                 case OB_FSDOM_PREVIEW:
454                         /* use preview mesh */
455                         BLI_join_dirfile(targetFile, sizeof(targetFile), fss->surfdataPath, OB_FLUIDSIM_SURF_PREVIEW_OBJ_FNAME);
456                         break;
457                 case OB_FSDOM_FINAL:
458                         /* use final mesh */
459                         BLI_join_dirfile(targetFile, sizeof(targetFile), fss->surfdataPath, OB_FLUIDSIM_SURF_FINAL_OBJ_FNAME);
460                         break;
461                 default:
462                         BLI_assert(!"Wrong fluidsim display type");
463                         return NULL;
464         }
465
466         /* offset baked frame */
467         curFrame += fss->frameOffset;
468
469         BLI_path_abs(targetFile, modifier_path_relbase_from_global(ob));
470         BLI_path_frame(targetFile, curFrame, 0); // fixed #frame-no
471
472         /* assign material + flags to new dm
473          * if there's no faces in original dm, keep materials and flags unchanged */
474         mpoly = orgdm->getPolyArray(orgdm);
475         if (mpoly) {
476                 mp_example = *mpoly;
477         }
478         /* else leave NULL'd */
479
480         dm = fluidsim_read_obj(targetFile, &mp_example);
481
482         if (!dm) {
483                 /* switch, abort background rendering when fluidsim mesh is missing */
484                 const char *strEnvName2 = "BLENDER_ELBEEMBOBJABORT"; // from blendercall.cpp
485
486                 if (G.background == 1) {
487                         if (getenv(strEnvName2)) {
488                                 int elevel = atoi(getenv(strEnvName2));
489                                 if (elevel > 0) {
490                                         printf("Env. var %s set, fluid sim mesh '%s' not found, aborting render...\n",
491                                                strEnvName2, targetFile);
492                                         exit(1);
493                                 }
494                         }
495                 }
496
497                 /* display org. object upon failure which is in dm */
498                 return NULL;
499         }
500
501         /* load vertex velocities, if they exist...
502          * TODO? use generate flag as loading flag as well?
503          * warning, needs original .bobj.gz mesh loading filename */
504         if (displaymode == OB_FSDOM_FINAL) {
505                 fluidsim_read_vel_cache(fluidmd, dm, targetFile);
506         }
507         else {
508                 if (fss->meshVelocities)
509                         MEM_freeN(fss->meshVelocities);
510
511                 fss->meshVelocities = NULL;
512         }
513
514         return dm;
515 }
516 #endif // WITH_MOD_FLUID
517
518 DerivedMesh *fluidsimModifier_do(
519         FluidsimModifierData *fluidmd,
520         const ModifierEvalContext *ctx,
521         DerivedMesh *dm)
522 {
523 #ifdef WITH_MOD_FLUID
524         Object *ob = ctx->object;
525         Depsgraph *depsgraph = ctx->depsgraph;
526         const bool useRenderParams = (ctx->flag & MOD_APPLY_RENDER) != 0;
527 //      const bool isFinalCalc = (ctx->flag & MOD_APPLY_USECACHE) != 0;
528         DerivedMesh *result = NULL;
529         int framenr;
530         FluidsimSettings *fss = NULL;
531
532         framenr = (int)DEG_get_ctime(depsgraph);
533         
534         /* only handle fluidsim domains */
535         if (fluidmd && fluidmd->fss && (fluidmd->fss->type != OB_FLUIDSIM_DOMAIN))
536                 return dm;
537
538         /* sanity check */
539         if (!fluidmd || !fluidmd->fss)
540                 return dm;
541
542         fss = fluidmd->fss;
543
544         /* timescale not supported yet
545          * clmd->sim_parms->timescale = timescale; */
546
547         /* support reversing of baked fluid frames here */
548         if ((fss->flag & OB_FLUIDSIM_REVERSE) && (fss->lastgoodframe >= 0)) {
549                 framenr = fss->lastgoodframe - framenr + 1;
550                 CLAMP(framenr, 1, fss->lastgoodframe);
551         }
552         
553         /* try to read from cache */
554         /* if the frame is there, fine, otherwise don't do anything */
555         if ((result = fluidsim_read_cache(ob, dm, fluidmd, framenr, useRenderParams)))
556                 return result;
557         
558         return dm;
559 #else
560         /* unused */
561         (void)fluidmd;
562         (void)ctx;
563         (void)dm;
564         return NULL;
565 #endif
566 }