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