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