=bmesh= merge from trunk at r36529
[blender.git] / source / blender / modifiers / intern / MOD_fluidsim_util.c
1 /*
2 * $Id$
3 *
4 * ***** BEGIN GPL LICENSE BLOCK *****
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software  Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 * The Original Code is Copyright (C) 2005 by the Blender Foundation.
21 * All rights reserved.
22 *
23 * Contributor(s): Daniel Dunbar
24 *                 Ton Roosendaal,
25 *                 Ben Batt,
26 *                 Brecht Van Lommel,
27 *                 Campbell Barton
28 *
29 * ***** END GPL LICENSE BLOCK *****
30 *
31 */
32
33 /** \file blender/modifiers/intern/MOD_fluidsim_util.c
34  *  \ingroup modifiers
35  */
36
37
38 #include <stddef.h>
39 #include <zlib.h>
40
41 #include "DNA_object_types.h"
42 #include "DNA_scene_types.h"
43 #include "DNA_mesh_types.h"
44 #include "DNA_meshdata_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_object_fluidsim.h"
47
48 #include "BLI_blenlib.h"
49 #include "BLI_math.h"
50 #include "BLI_utildefines.h"
51
52 #include "BKE_main.h"
53 #include "BKE_fluidsim.h" /* ensure definitions here match */
54 #include "BKE_cdderivedmesh.h"
55 #include "BKE_mesh.h"
56 #include "BKE_utildefines.h"
57 #include "BKE_global.h" /* G.main->name only */
58
59 #include "MOD_fluidsim_util.h"
60 #include "MOD_modifiertypes.h"
61
62 #include "MEM_guardedalloc.h"
63
64 // headers for fluidsim bobj meshes
65 #include "LBM_fluidsim.h"
66
67 void fluidsim_init(FluidsimModifierData *fluidmd)
68 {
69 #ifndef DISABLE_ELBEEM
70         if(fluidmd)
71         {
72                 FluidsimSettings *fss = MEM_callocN(sizeof(FluidsimSettings), "fluidsimsettings");
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                 // dg TODO: change this to []
94                 fss->gravx = 0.0;
95                 fss->gravy = 0.0;
96                 fss->gravz = -9.81;
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                 BLI_strncpy(fss->surfdataPath, btempdir, FILE_MAX);
109
110                 // first init of bounding box
111                 // no bounding box needed
112                 
113                 // todo - reuse default init from elbeem!
114                 fss->typeFlags = OB_FSBND_PARTSLIP;
115                 fss->domainNovecgen = 0;
116                 fss->volumeInitType = 1; // volume
117                 fss->partSlipValue = 0.2;
118
119                 fss->generateTracers = 0;
120                 fss->generateParticles = 0.0;
121                 fss->surfaceSmoothing = 1.0;
122                 fss->surfaceSubdivs = 0.0;
123                 fss->particleInfSize = 0.0;
124                 fss->particleInfAlpha = 0.0;
125         
126                 // init fluid control settings
127                 fss->attractforceStrength = 0.2;
128                 fss->attractforceRadius = 0.75;
129                 fss->velocityforceStrength = 0.2;
130                 fss->velocityforceRadius = 0.75;
131                 fss->cpsTimeStart = fss->animStart;
132                 fss->cpsTimeEnd = fss->animEnd;
133                 fss->cpsQuality = 10.0; // 1.0 / 10.0 => means 0.1 width
134                 
135                 /*
136                 BAD TODO: this is done in buttons_object.c in the moment 
137                 Mesh *mesh = ob->data;
138                 // calculate bounding box
139                 fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize); 
140                 */
141                 
142                 // (ab)used to store velocities
143                 fss->meshSurfNormals = NULL;
144                 
145                 fss->lastgoodframe = -1;
146                 
147                 fss->flag |= OB_FLUIDSIM_ACTIVE;
148
149         }
150 #else
151         (void)fluidmd; /* unused */
152 #endif
153         return;
154 }
155
156 void fluidsim_free(FluidsimModifierData *fluidmd)
157 {
158 #ifndef DISABLE_ELBEEM
159         if(fluidmd)
160         {
161                 if(fluidmd->fss->meshSurfNormals)
162                 {
163                         MEM_freeN(fluidmd->fss->meshSurfNormals);
164                         fluidmd->fss->meshSurfNormals = NULL;
165                 }
166                 MEM_freeN(fluidmd->fss);
167         }
168 #else
169         (void)fluidmd; /* unused */
170 #endif
171         
172         return;
173 }
174
175 #ifndef DISABLE_ELBEEM
176 /* read .bobj.gz file into a fluidsimDerivedMesh struct */
177 static DerivedMesh *fluidsim_read_obj(const char *filename)
178 {
179         int wri = 0,i;
180         int gotBytes;
181         gzFile gzf;
182         int numverts = 0, numfaces = 0;
183         DerivedMesh *dm = NULL;
184         MFace *mf;
185         MVert *mv;
186         short *normals, *no_s;
187         float no[3];
188
189         // ------------------------------------------------
190         // get numverts + numfaces first
191         // ------------------------------------------------
192         gzf = gzopen(filename, "rb");
193         if (!gzf)
194         {
195                 return NULL;
196         }
197
198         // read numverts
199         gotBytes = gzread(gzf, &wri, sizeof(wri));
200         numverts = wri;
201
202         // skip verts
203         gotBytes = gzseek(gzf, numverts * 3 * sizeof(float), SEEK_CUR) != -1;
204
205
206         // read number of normals
207         if(gotBytes)
208                 gotBytes = gzread(gzf, &wri, sizeof(wri));
209
210         // skip normals
211         gotBytes = gzseek(gzf, numverts * 3 * sizeof(float), SEEK_CUR) != -1;
212
213         /* get no. of triangles */
214         if(gotBytes)
215                 gotBytes = gzread(gzf, &wri, sizeof(wri));
216         numfaces = wri;
217
218         gzclose( gzf );
219         // ------------------------------------------------
220
221         if(!numfaces || !numverts || !gotBytes)
222                 return NULL;
223
224         gzf = gzopen(filename, "rb");
225         if (!gzf)
226         {
227                 return NULL;
228         }
229
230         dm = CDDM_new(numverts, 0, numfaces, 0, 0);
231
232         if(!dm)
233         {
234                 gzclose( gzf );
235                 return NULL;
236         }
237
238         // read numverts
239         gotBytes = gzread(gzf, &wri, sizeof(wri));
240
241         // read vertex position from file
242         mv = CDDM_get_verts(dm);
243
244         for(i=0; i<numverts; i++, mv++)
245                 gotBytes = gzread(gzf, mv->co, sizeof(float) * 3);
246
247         // should be the same as numverts
248         gotBytes = gzread(gzf, &wri, sizeof(wri));
249         if(wri != numverts)
250         {
251                 if(dm)
252                         dm->release(dm);
253                 gzclose( gzf );
254                 return NULL;
255         }
256
257         normals = MEM_callocN(sizeof(short) * numverts * 3, "fluid_tmp_normals" );
258         if(!normals)
259         {
260                 if(dm)
261                         dm->release(dm);
262                 gzclose( gzf );
263                 return NULL;
264         }
265
266         // read normals from file (but don't save them yet)
267         for(i=numverts, no_s= normals; i>0; i--, no_s += 3)
268         {
269                 gotBytes = gzread(gzf, no, sizeof(float) * 3);
270                 normal_float_to_short_v3(no_s, no);
271         }
272
273         /* read no. of triangles */
274         gotBytes = gzread(gzf, &wri, sizeof(wri));
275
276         if(wri!=numfaces) {
277                 printf("Fluidsim: error in reading data from file.\n");
278                 if(dm)
279                         dm->release(dm);
280                 gzclose( gzf );
281                 MEM_freeN(normals);
282                 return NULL;
283         }
284
285         // read triangles from file
286         mf = CDDM_get_tessfaces(dm);
287         for(i=numfaces; i>0; i--, mf++)
288         {
289                 int face[3];
290
291                 gotBytes = gzread(gzf, face, sizeof(int) * 3);
292
293                 // check if 3rd vertex has index 0 (not allowed in blender)
294                 if(face[2])
295                 {
296                         mf->v1 = face[0];
297                         mf->v2 = face[1];
298                         mf->v3 = face[2];
299                 }
300                 else
301                 {
302                         mf->v1 = face[1];
303                         mf->v2 = face[2];
304                         mf->v3 = face[0];
305                 }
306                 mf->v4 = 0;
307
308                 test_index_face(mf, NULL, 0, 3);
309         }
310
311         gzclose( gzf );
312
313         CDDM_calc_edges(dm);
314
315         CDDM_apply_vert_normals(dm, (short (*)[3])normals);
316         MEM_freeN(normals);
317
318         // CDDM_calc_normals(result);
319
320         return dm;
321 }
322
323
324 void fluid_get_bb(MVert *mvert, int totvert, float obmat[][4],
325                  /*RET*/ float start[3], /*RET*/ float size[3] )
326 {
327         float bbsx=0.0, bbsy=0.0, bbsz=0.0;
328         float bbex=1.0, bbey=1.0, bbez=1.0;
329         int i;
330         float vec[3];
331
332         if(totvert == 0) {
333                 zero_v3(start);
334                 zero_v3(size);
335                 return;
336         }
337
338         copy_v3_v3(vec, mvert[0].co);
339         mul_m4_v3(obmat, vec);
340         bbsx = vec[0]; bbsy = vec[1]; bbsz = vec[2];
341         bbex = vec[0]; bbey = vec[1]; bbez = vec[2];
342
343         for(i = 1; i < totvert; i++) {
344                 copy_v3_v3(vec, mvert[i].co);
345                 mul_m4_v3(obmat, vec);
346
347                 if(vec[0] < bbsx){ bbsx= vec[0]; }
348                 if(vec[1] < bbsy){ bbsy= vec[1]; }
349                 if(vec[2] < bbsz){ bbsz= vec[2]; }
350                 if(vec[0] > bbex){ bbex= vec[0]; }
351                 if(vec[1] > bbey){ bbey= vec[1]; }
352                 if(vec[2] > bbez){ bbez= vec[2]; }
353         }
354
355         // return values...
356         if(start) {
357                 start[0] = bbsx;
358                 start[1] = bbsy;
359                 start[2] = bbsz;
360         }
361         if(size) {
362                 size[0] = bbex-bbsx;
363                 size[1] = bbey-bbsy;
364                 size[2] = bbez-bbsz;
365         }
366 }
367
368 //-------------------------------------------------------------------------------
369 // old interface
370 //-------------------------------------------------------------------------------
371
372 void fluid_estimate_memory(Object *ob, FluidsimSettings *fss, char *value)
373 {
374         Mesh *mesh;
375
376         value[0]= '\0';
377
378         if(ob->type == OB_MESH) {
379                 /* use mesh bounding box and object scaling */
380                 mesh= ob->data;
381
382                 fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize);
383                 elbeemEstimateMemreq(fss->resolutionxyz, fss->bbSize[0],fss->bbSize[1],fss->bbSize[2], fss->maxRefine, value);
384         }
385 }
386
387
388 /* read zipped fluidsim velocities into the co's of the fluidsimsettings normals struct */
389 static void fluidsim_read_vel_cache(FluidsimModifierData *fluidmd, DerivedMesh *dm, char *filename)
390 {
391         int wri, i, j;
392         float wrf;
393         gzFile gzf;
394         FluidsimSettings *fss = fluidmd->fss;
395         int len = strlen(filename);
396         int totvert = dm->getNumVerts(dm);
397         float *velarray = NULL;
398
399         // mesh and vverts have to be valid from loading...
400
401         if(fss->meshSurfNormals)
402                 MEM_freeN(fss->meshSurfNormals);
403
404         if(len<7)
405         {
406                 return;
407         }
408
409         if(fss->domainNovecgen>0) return;
410
411         // abusing pointer to hold an array of 3d-velocities
412         fss->meshSurfNormals = MEM_callocN(sizeof(float)*3*dm->getNumVerts(dm), "Fluidsim_velocities");
413         // abusing pointer to hold an INT
414         fss->meshSurface = SET_INT_IN_POINTER(totvert);
415
416         velarray = (float *)fss->meshSurfNormals;
417
418         // .bobj.gz , correct filename
419         // 87654321
420         filename[len-6] = 'v';
421         filename[len-5] = 'e';
422         filename[len-4] = 'l';
423
424         gzf = gzopen(filename, "rb");
425         if (!gzf)
426         {
427                 MEM_freeN(fss->meshSurfNormals);
428                 fss->meshSurfNormals = NULL;
429                 return;
430         }
431
432         gzread(gzf, &wri, sizeof( wri ));
433         if(wri != totvert)
434         {
435                 MEM_freeN(fss->meshSurfNormals);
436                 fss->meshSurfNormals = NULL;
437                 return;
438         }
439
440         for(i=0; i<totvert;i++)
441         {
442                 for(j=0; j<3; j++)
443                 {
444                         gzread(gzf, &wrf, sizeof( wrf ));
445                         velarray[3*i + j] = wrf;
446                 }
447         }
448
449         gzclose(gzf);
450 }
451
452 static DerivedMesh *fluidsim_read_cache(DerivedMesh *orgdm, FluidsimModifierData *fluidmd, int framenr, int useRenderParams)
453 {
454         int displaymode = 0;
455         int curFrame = framenr - 1 /*scene->r.sfra*/; /* start with 0 at start frame */
456         char targetDir[FILE_MAXFILE+FILE_MAXDIR], targetFile[FILE_MAXFILE+FILE_MAXDIR];
457         FluidsimSettings *fss = fluidmd->fss;
458         DerivedMesh *dm = NULL;
459         MFace *mface;
460         int numfaces;
461         int mat_nr, flag, i;
462
463         if(!useRenderParams) {
464                 displaymode = fss->guiDisplayMode;
465         } else {
466                 displaymode = fss->renderDisplayMode;
467         }
468
469         BLI_strncpy(targetDir, fss->surfdataPath, sizeof(targetDir));
470
471         // use preview or final mesh?
472         if(displaymode==1)
473         {
474                 // just display original object
475                 return NULL;
476         }
477         else if(displaymode==2)
478         {
479                 strcat(targetDir,"fluidsurface_preview_####");
480         }
481         else
482         { // 3
483                 strcat(targetDir,"fluidsurface_final_####");
484         }
485
486         BLI_path_abs(targetDir, G.main->name);
487         BLI_path_frame(targetDir, curFrame, 0); // fixed #frame-no
488
489         BLI_snprintf(targetFile, sizeof(targetFile), "%s.bobj.gz", targetDir);
490
491         dm = fluidsim_read_obj(targetFile);
492
493         if(!dm)
494         {
495                 // switch, abort background rendering when fluidsim mesh is missing
496                 const char *strEnvName2 = "BLENDER_ELBEEMBOBJABORT"; // from blendercall.cpp
497
498                 if(G.background==1) {
499                         if(getenv(strEnvName2)) {
500                                 int elevel = atoi(getenv(strEnvName2));
501                                 if(elevel>0) {
502                                         printf("Env. var %s set, fluid sim mesh '%s' not found, aborting render...\n",strEnvName2, targetFile);
503                                         exit(1);
504                                 }
505                         }
506                 }
507
508                 // display org. object upon failure which is in dm
509                 return NULL;
510         }
511
512         // assign material + flags to new dm
513         mface = orgdm->getTessFaceArray(orgdm);
514         mat_nr = mface[0].mat_nr;
515         flag = mface[0].flag;
516
517         mface = dm->getTessFaceArray(dm);
518         numfaces = dm->getNumTessFaces(dm);
519         for(i=0; i<numfaces; i++)
520         {
521                 mface[i].mat_nr = mat_nr;
522                 mface[i].flag = flag;
523         }
524
525         // load vertex velocities, if they exist...
526         // TODO? use generate flag as loading flag as well?
527         // warning, needs original .bobj.gz mesh loading filename
528         if(displaymode==3)
529         {
530                 fluidsim_read_vel_cache(fluidmd, dm, targetFile);
531         }
532         else
533         {
534                 if(fss->meshSurfNormals)
535                         MEM_freeN(fss->meshSurfNormals);
536
537                 fss->meshSurfNormals = NULL;
538         }
539
540         return dm;
541 }
542 #endif // DISABLE_ELBEEM
543
544 DerivedMesh *fluidsimModifier_do(FluidsimModifierData *fluidmd, Scene *scene,
545                                                 Object *UNUSED(ob),
546                                                 DerivedMesh *dm,
547                                                 int useRenderParams, int UNUSED(isFinalCalc))
548 {
549 #ifndef DISABLE_ELBEEM
550         DerivedMesh *result = NULL;
551         int framenr;
552         FluidsimSettings *fss = NULL;
553
554         framenr= (int)scene->r.cfra;
555         
556         // only handle fluidsim domains
557         if(fluidmd && fluidmd->fss && (fluidmd->fss->type != OB_FLUIDSIM_DOMAIN))
558                 return dm;
559         
560         // sanity check
561         if(!fluidmd || (fluidmd && !fluidmd->fss))
562                 return dm;
563         
564         fss = fluidmd->fss;
565         
566         // timescale not supported yet
567         // clmd->sim_parms->timescale= timescale;
568
569         // support reversing of baked fluid frames here
570         if((fss->flag & OB_FLUIDSIM_REVERSE) && (fss->lastgoodframe >= 0))
571         {
572                 framenr = fss->lastgoodframe - framenr + 1;
573                 CLAMP(framenr, 1, fss->lastgoodframe);
574         }
575         
576         /* try to read from cache */
577         /* if the frame is there, fine, otherwise don't do anything */
578         if((result = fluidsim_read_cache(dm, fluidmd, framenr, useRenderParams)))
579                 return result;
580         
581         return dm;
582 #else
583         /* unused */
584         (void)fluidmd;
585         (void)scene;
586         (void)dm;
587         (void)useRenderParams;
588         return NULL;
589 #endif
590 }