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