svn merge ^/trunk/blender -r41503:41575
[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_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->gstar = 0.005; // used as normgstar
99                 fss->maxRefine = -1;
100                 // maxRefine is set according to resolutionxyz during bake
101
102                 // fluid/inflow settings
103                 // fss->iniVel --> automatically set to 0
104
105                 /*  elubie: changed this to default to the same dir as the render output
106                 to prevent saving to C:\ on Windows */
107                 BLI_strncpy(fss->surfdataPath, BLI_temporary_dir(), FILE_MAX);
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 #ifdef WITH_MOD_FLUID
157         if(fluidmd)
158         {
159                 if(fluidmd->fss->meshVelocities)
160                 {
161                         MEM_freeN(fluidmd->fss->meshVelocities);
162                         fluidmd->fss->meshVelocities = NULL;
163                 }
164                 MEM_freeN(fluidmd->fss);
165         }
166 #else
167         (void)fluidmd; /* unused */
168 #endif
169         
170         return;
171 }
172
173 #ifdef WITH_MOD_FLUID
174 /* read .bobj.gz file into a fluidsimDerivedMesh struct */
175 static DerivedMesh *fluidsim_read_obj(const char *filename)
176 {
177         int wri = 0,i;
178         int gotBytes;
179         gzFile gzf;
180         int numverts = 0, numfaces = 0;
181         DerivedMesh *dm = NULL;
182         MFace *mf;
183         MVert *mv;
184         short *normals, *no_s;
185         float no[3];
186
187         // ------------------------------------------------
188         // get numverts + numfaces first
189         // ------------------------------------------------
190         gzf = gzopen(filename, "rb");
191         if (!gzf)
192         {
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 = gzopen(filename, "rb");
223         if (!gzf)
224         {
225                 return NULL;
226         }
227
228         dm = CDDM_new(numverts, 0, numfaces, 0, 0);
229
230         if(!dm)
231         {
232                 gzclose( gzf );
233                 return NULL;
234         }
235
236         // read numverts
237         gotBytes = gzread(gzf, &wri, sizeof(wri));
238
239         // read vertex position from file
240         mv = CDDM_get_verts(dm);
241
242         for(i=0; i<numverts; i++, mv++)
243                 gotBytes = gzread(gzf, mv->co, sizeof(float) * 3);
244
245         // should be the same as numverts
246         gotBytes = gzread(gzf, &wri, sizeof(wri));
247         if(wri != numverts)
248         {
249                 if(dm)
250                         dm->release(dm);
251                 gzclose( gzf );
252                 return NULL;
253         }
254
255         normals = MEM_callocN(sizeof(short) * numverts * 3, "fluid_tmp_normals" );
256         if(!normals)
257         {
258                 if(dm)
259                         dm->release(dm);
260                 gzclose( gzf );
261                 return NULL;
262         }
263
264         // read normals from file (but don't save them yet)
265         for(i=numverts, no_s= normals; i>0; i--, no_s += 3)
266         {
267                 gotBytes = gzread(gzf, no, sizeof(float) * 3);
268                 normal_float_to_short_v3(no_s, no);
269         }
270
271         /* read no. of triangles */
272         gotBytes = gzread(gzf, &wri, sizeof(wri));
273
274         if(wri!=numfaces) {
275                 printf("Fluidsim: error in reading data from file.\n");
276                 if(dm)
277                         dm->release(dm);
278                 gzclose( gzf );
279                 MEM_freeN(normals);
280                 return NULL;
281         }
282
283         // read triangles from file
284         mf = CDDM_get_tessfaces(dm);
285         for(i=numfaces; i>0; i--, mf++)
286         {
287                 int face[3];
288
289                 gotBytes = gzread(gzf, face, sizeof(int) * 3);
290
291                 // check if 3rd vertex has index 0 (not allowed in blender)
292                 if(face[2])
293                 {
294                         mf->v1 = face[0];
295                         mf->v2 = face[1];
296                         mf->v3 = face[2];
297                 }
298                 else
299                 {
300                         mf->v1 = face[1];
301                         mf->v2 = face[2];
302                         mf->v3 = face[0];
303                 }
304                 mf->v4 = 0;
305
306                 test_index_face(mf, NULL, 0, 3);
307         }
308
309         gzclose( gzf );
310
311         CDDM_calc_edges(dm);
312
313         CDDM_apply_vert_normals(dm, (short (*)[3])normals);
314         MEM_freeN(normals);
315
316         // CDDM_calc_normals(result);
317
318         return dm;
319 }
320
321
322 void fluid_get_bb(MVert *mvert, int totvert, float obmat[][4],
323                  /*RET*/ float start[3], /*RET*/ float size[3] )
324 {
325         float bbsx=0.0, bbsy=0.0, bbsz=0.0;
326         float bbex=1.0, bbey=1.0, bbez=1.0;
327         int i;
328         float vec[3];
329
330         if(totvert == 0) {
331                 zero_v3(start);
332                 zero_v3(size);
333                 return;
334         }
335
336         copy_v3_v3(vec, mvert[0].co);
337         mul_m4_v3(obmat, vec);
338         bbsx = vec[0]; bbsy = vec[1]; bbsz = vec[2];
339         bbex = vec[0]; bbey = vec[1]; bbez = vec[2];
340
341         for(i = 1; i < totvert; i++) {
342                 copy_v3_v3(vec, mvert[i].co);
343                 mul_m4_v3(obmat, vec);
344
345                 if(vec[0] < bbsx){ bbsx= vec[0]; }
346                 if(vec[1] < bbsy){ bbsy= vec[1]; }
347                 if(vec[2] < bbsz){ bbsz= vec[2]; }
348                 if(vec[0] > bbex){ bbex= vec[0]; }
349                 if(vec[1] > bbey){ bbey= vec[1]; }
350                 if(vec[2] > bbez){ bbez= vec[2]; }
351         }
352
353         // return values...
354         if(start) {
355                 start[0] = bbsx;
356                 start[1] = bbsy;
357                 start[2] = bbsz;
358         }
359         if(size) {
360                 size[0] = bbex-bbsx;
361                 size[1] = bbey-bbsy;
362                 size[2] = bbez-bbsz;
363         }
364 }
365
366 //-------------------------------------------------------------------------------
367 // old interface
368 //-------------------------------------------------------------------------------
369
370 void fluid_estimate_memory(Object *ob, FluidsimSettings *fss, char *value)
371 {
372         Mesh *mesh;
373
374         value[0]= '\0';
375
376         if(ob->type == OB_MESH) {
377                 /* use mesh bounding box and object scaling */
378                 mesh= ob->data;
379
380                 fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize);
381                 elbeemEstimateMemreq(fss->resolutionxyz, fss->bbSize[0],fss->bbSize[1],fss->bbSize[2], fss->maxRefine, value);
382         }
383 }
384
385
386 /* read zipped fluidsim velocities into the co's of the fluidsimsettings normals struct */
387 static void fluidsim_read_vel_cache(FluidsimModifierData *fluidmd, DerivedMesh *dm, char *filename)
388 {
389         int wri, i, j;
390         float wrf;
391         gzFile gzf;
392         FluidsimSettings *fss = fluidmd->fss;
393         int len = strlen(filename);
394         int totvert = dm->getNumVerts(dm);
395         FluidVertexVelocity *velarray = NULL;
396
397         // mesh and vverts have to be valid from loading...
398
399         if(fss->meshVelocities)
400                 MEM_freeN(fss->meshVelocities);
401
402         if(len<7)
403         {
404                 return;
405         }
406
407         if(fss->domainNovecgen>0) return;
408
409         fss->meshVelocities = MEM_callocN(sizeof(FluidVertexVelocity)*dm->getNumVerts(dm), "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 = gzopen(filename, "rb");
421         if (!gzf)
422         {
423                 MEM_freeN(fss->meshVelocities);
424                 fss->meshVelocities = NULL;
425                 return;
426         }
427
428         gzread(gzf, &wri, sizeof( wri ));
429         if(wri != totvert)
430         {
431                 MEM_freeN(fss->meshVelocities);
432                 fss->meshVelocities = NULL;
433                 return;
434         }
435
436         for(i=0; i<totvert;i++)
437         {
438                 for(j=0; j<3; j++)
439                 {
440                         gzread(gzf, &wrf, sizeof( wrf ));
441                         velarray[i].vel[j] = wrf;
442                 }
443         }
444
445         gzclose(gzf);
446 }
447
448 static DerivedMesh *fluidsim_read_cache(DerivedMesh *orgdm, FluidsimModifierData *fluidmd, int framenr, int useRenderParams)
449 {
450         int displaymode = 0;
451         int curFrame = framenr - 1 /*scene->r.sfra*/; /* start with 0 at start frame */
452         char targetDir[FILE_MAXFILE+FILE_MAXDIR], targetFile[FILE_MAXFILE+FILE_MAXDIR];
453         FluidsimSettings *fss = fluidmd->fss;
454         DerivedMesh *dm = NULL;
455         MFace *mface;
456         int numfaces;
457         int mat_nr, flag, i;
458
459         if(!useRenderParams) {
460                 displaymode = fss->guiDisplayMode;
461         } else {
462                 displaymode = fss->renderDisplayMode;
463         }
464
465         BLI_strncpy(targetDir, fss->surfdataPath, sizeof(targetDir));
466
467         // use preview or final mesh?
468         if(displaymode==1)
469         {
470                 // just display original object
471                 return NULL;
472         }
473         else if(displaymode==2)
474         {
475                 strcat(targetDir,"fluidsurface_preview_####");
476         }
477         else
478         { // 3
479                 strcat(targetDir,"fluidsurface_final_####");
480         }
481
482         BLI_path_abs(targetDir, G.main->name);
483         BLI_path_frame(targetDir, curFrame, 0); // fixed #frame-no
484
485         BLI_snprintf(targetFile, sizeof(targetFile), "%s.bobj.gz", targetDir);
486
487         dm = fluidsim_read_obj(targetFile);
488
489         if(!dm)
490         {
491                 // switch, abort background rendering when fluidsim mesh is missing
492                 const char *strEnvName2 = "BLENDER_ELBEEMBOBJABORT"; // from blendercall.cpp
493
494                 if(G.background==1) {
495                         if(getenv(strEnvName2)) {
496                                 int elevel = atoi(getenv(strEnvName2));
497                                 if(elevel>0) {
498                                         printf("Env. var %s set, fluid sim mesh '%s' not found, aborting render...\n",strEnvName2, targetFile);
499                                         exit(1);
500                                 }
501                         }
502                 }
503
504                 // display org. object upon failure which is in dm
505                 return NULL;
506         }
507
508         // assign material + flags to new dm
509         mface = orgdm->getTessFaceArray(orgdm);
510         mat_nr = mface[0].mat_nr;
511         flag = mface[0].flag;
512
513         mface = dm->getTessFaceArray(dm);
514         numfaces = dm->getNumTessFaces(dm);
515         for(i=0; i<numfaces; i++)
516         {
517                 mface[i].mat_nr = mat_nr;
518                 mface[i].flag = flag;
519         }
520
521         // load vertex velocities, if they exist...
522         // TODO? use generate flag as loading flag as well?
523         // warning, needs original .bobj.gz mesh loading filename
524         if(displaymode==3)
525         {
526                 fluidsim_read_vel_cache(fluidmd, dm, targetFile);
527         }
528         else
529         {
530                 if(fss->meshVelocities)
531                         MEM_freeN(fss->meshVelocities);
532
533                 fss->meshVelocities = NULL;
534         }
535
536         return dm;
537 }
538 #endif // WITH_MOD_FLUID
539
540 DerivedMesh *fluidsimModifier_do(FluidsimModifierData *fluidmd, Scene *scene,
541                                                 Object *UNUSED(ob),
542                                                 DerivedMesh *dm,
543                                                 int useRenderParams, int UNUSED(isFinalCalc))
544 {
545 #ifdef WITH_MOD_FLUID
546         DerivedMesh *result = NULL;
547         int framenr;
548         FluidsimSettings *fss = NULL;
549
550         framenr= (int)scene->r.cfra;
551         
552         // only handle fluidsim domains
553         if(fluidmd && fluidmd->fss && (fluidmd->fss->type != OB_FLUIDSIM_DOMAIN))
554                 return dm;
555         
556         // sanity check
557         if(!fluidmd || (fluidmd && !fluidmd->fss))
558                 return dm;
559         
560         fss = fluidmd->fss;
561         
562         // timescale not supported yet
563         // clmd->sim_parms->timescale= timescale;
564
565         // support reversing of baked fluid frames here
566         if((fss->flag & OB_FLUIDSIM_REVERSE) && (fss->lastgoodframe >= 0))
567         {
568                 framenr = fss->lastgoodframe - framenr + 1;
569                 CLAMP(framenr, 1, fss->lastgoodframe);
570         }
571         
572         /* try to read from cache */
573         /* if the frame is there, fine, otherwise don't do anything */
574         if((result = fluidsim_read_cache(dm, fluidmd, framenr, useRenderParams)))
575                 return result;
576         
577         return dm;
578 #else
579         /* unused */
580         (void)fluidmd;
581         (void)scene;
582         (void)dm;
583         (void)useRenderParams;
584         return NULL;
585 #endif
586 }