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