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