Bugfix for a) Fluid ipos gone after save-load b) changing viscosity causes fluidsim...
[blender.git] / source / blender / blenkernel / intern / fluidsim.c
1 /**
2  * fluidsim.c
3  * 
4  *
5  * ***** BEGIN GPL LICENSE BLOCK *****
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * The Original Code is Copyright (C) Blender Foundation
22  * All rights reserved.
23  *
24  * The Original Code is: all of this file.
25  *
26  * Contributor(s): none yet.
27  *
28  * ***** END GPL LICENSE BLOCK *****
29  */
30
31 #include "MEM_guardedalloc.h"
32
33 #include "DNA_mesh_types.h"
34 #include "DNA_meshdata_types.h"
35 #include "DNA_object_force.h" // for pointcache 
36 #include "DNA_particle_types.h"
37 #include "DNA_scene_types.h" // N_T
38
39 #include "BLI_arithb.h"
40 #include "BLI_blenlib.h"
41
42 #include "BKE_cdderivedmesh.h"
43 #include "BKE_customdata.h"
44 #include "BKE_DerivedMesh.h"
45 #include "BKE_fluidsim.h"
46 #include "BKE_global.h"
47 #include "BKE_modifier.h"
48 #include "BKE_mesh.h"
49 #include "BKE_pointcache.h"
50 #include "BKE_utildefines.h"
51
52 // headers for fluidsim bobj meshes
53 #include <stdlib.h>
54 #include "LBM_fluidsim.h"
55 #include "elbeem.h"
56 #include <zlib.h>
57 #include <string.h>
58 #include <stdio.h>
59
60 /* ************************* fluidsim bobj file handling **************************** */
61
62 #ifndef DISABLE_ELBEEM
63
64 // -----------------------------------------
65 // forward decleration
66 // -----------------------------------------
67
68 // -----------------------------------------
69
70 void fluidsim_init(FluidsimModifierData *fluidmd)
71 {
72         if(fluidmd)
73         {
74                 FluidsimSettings *fss = MEM_callocN(sizeof(FluidsimSettings), "fluidsimsettings");
75                 
76                 fluidmd->fss = fss;
77                 
78                 if(!fss)
79                         return;
80                 
81                 fss->type = 0;
82                 fss->show_advancedoptions = 0;
83
84                 fss->resolutionxyz = 50;
85                 fss->previewresxyz = 25;
86                 fss->realsize = 0.03;
87                 fss->guiDisplayMode = 2; // preview
88                 fss->renderDisplayMode = 3; // render
89
90                 fss->viscosityMode = 2; // default to water
91                 fss->viscosityValue = 1.0;
92                 fss->viscosityExponent = 6;
93                 
94                 // dg TODO: change this to []
95                 fss->gravx = 0.0;
96                 fss->gravy = 0.0;
97                 fss->gravz = -9.81;
98                 fss->animStart = 0.0; 
99                 fss->animEnd = 0.30;
100                 fss->gstar = 0.005; // used as normgstar
101                 fss->maxRefine = -1;
102                 // maxRefine is set according to resolutionxyz during bake
103
104                 // fluid/inflow settings
105                 // fss->iniVel --> automatically set to 0
106
107                 /*  elubie: changed this to default to the same dir as the render output
108                 to prevent saving to C:\ on Windows */
109                 BLI_strncpy(fss->surfdataPath, btempdir, FILE_MAX);
110
111                 // first init of bounding box
112                 // no bounding box needed
113                 
114                 // todo - reuse default init from elbeem!
115                 fss->typeFlags = 0;
116                 fss->domainNovecgen = 0;
117                 fss->volumeInitType = 1; // volume
118                 fss->partSlipValue = 0.0;
119
120                 fss->generateTracers = 0;
121                 fss->generateParticles = 0.0;
122                 fss->surfaceSmoothing = 1.0;
123                 fss->surfaceSubdivs = 1.0;
124                 fss->particleInfSize = 0.0;
125                 fss->particleInfAlpha = 0.0;
126         
127                 // init fluid control settings
128                 fss->attractforceStrength = 0.2;
129                 fss->attractforceRadius = 0.75;
130                 fss->velocityforceStrength = 0.2;
131                 fss->velocityforceRadius = 0.75;
132                 fss->cpsTimeStart = fss->animStart;
133                 fss->cpsTimeEnd = fss->animEnd;
134                 fss->cpsQuality = 10.0; // 1.0 / 10.0 => means 0.1 width
135                 
136                 /*
137                 BAD TODO: this is done in buttons_object.c in the moment 
138                 Mesh *mesh = ob->data;
139                 // calculate bounding box
140                 fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize); 
141                 */
142                 
143                 fss->lastgoodframe = -1;
144                 
145                 fss->flag = 0;
146
147         }
148         
149         return;
150 }
151
152 void fluidsim_free(FluidsimModifierData *fluidmd)
153 {
154         if(fluidmd)
155         {
156                 MEM_freeN(fluidmd->fss);
157         }
158         
159         return;
160 }
161
162 DerivedMesh *fluidsimModifier_do(FluidsimModifierData *fluidmd, Object *ob, DerivedMesh *dm, int useRenderParams, int isFinalCalc)
163 {
164         DerivedMesh *result = NULL;
165         int framenr;
166         FluidsimSettings *fss = NULL;
167
168         framenr= (int)G.scene->r.cfra;
169         
170         // only handle fluidsim domains
171         if(fluidmd && fluidmd->fss && (fluidmd->fss->type != OB_FLUIDSIM_DOMAIN))
172                 return dm;
173         
174         // sanity check
175         if(!fluidmd || (fluidmd && !fluidmd->fss))
176                 return dm;
177         
178         fss = fluidmd->fss;
179         
180         // timescale not supported yet
181         // clmd->sim_parms->timescale= timescale;
182         
183         // support reversing of baked fluid frames here
184         if((fss->flag & OB_FLUIDSIM_REVERSE) && (fss->lastgoodframe >= 0))
185         {
186                 framenr = fss->lastgoodframe - framenr + 1;
187                 CLAMP(framenr, 1, fss->lastgoodframe);
188         }
189         
190         /* try to read from cache */
191         if(((fss->lastgoodframe >= framenr) || (fss->lastgoodframe < 0)) && (result = fluidsim_read_cache(ob, dm, fluidmd, framenr, useRenderParams)))
192         {
193                 // fss->lastgoodframe = framenr; // set also in src/fluidsim.c
194                 return result;
195         }
196         else
197         {       
198                 // display last known good frame
199                 if(fss->lastgoodframe >= 0)
200                 {
201                         if((result = fluidsim_read_cache(ob, dm, fluidmd, fss->lastgoodframe, useRenderParams))) 
202                         {
203                                 return result;
204                         }
205                         
206                         // it was supposed to be a valid frame but it isn't!
207                         fss->lastgoodframe = framenr - 1;
208                         
209                         
210                         // this could be likely the case when you load an old fluidsim
211                         if((result = fluidsim_read_cache(ob, dm, fluidmd, fss->lastgoodframe, useRenderParams))) 
212                         {
213                                 return result;
214                         }
215                 }
216                 
217                 result = CDDM_copy(dm);
218
219                 if(result) 
220                 {
221                         return result;
222                 }
223         }
224         
225         return dm;
226 }
227
228 /* read .bobj.gz file into a fluidsimDerivedMesh struct */
229 static DerivedMesh *fluidsim_read_obj(char *filename)
230 {
231         int wri,i,j;
232         float wrf;
233         int gotBytes;
234         gzFile gzf;
235         int numverts = 0, numfaces = 0;
236         DerivedMesh *dm = NULL;
237         MFace *mface;
238         MVert *mvert;
239         short *normals;
240                 
241         // ------------------------------------------------
242         // get numverts + numfaces first
243         // ------------------------------------------------
244         gzf = gzopen(filename, "rb");
245         if (!gzf) 
246         {
247                 return NULL;
248         }
249
250         // read numverts
251         gotBytes = gzread(gzf, &wri, sizeof(wri));
252         numverts = wri;
253         
254         // skip verts
255         for(i=0; i<numverts*3; i++) 
256         {       
257                 gotBytes = gzread(gzf, &wrf, sizeof( wrf )); 
258         }
259         
260         // read number of normals
261         gotBytes = gzread(gzf, &wri, sizeof(wri));
262         
263         // skip normals
264         for(i=0; i<numverts*3; i++) 
265         {       
266                 gotBytes = gzread(gzf, &wrf, sizeof( wrf )); 
267         }
268         
269         /* get no. of triangles */
270         gotBytes = gzread(gzf, &wri, sizeof(wri));
271         numfaces = wri;
272         
273         gzclose( gzf );
274         // ------------------------------------------------
275         
276         if(!numfaces || !numverts)
277                 return NULL;
278         
279         gzf = gzopen(filename, "rb");
280         if (!gzf) 
281         {
282                 return NULL;
283         }
284         
285         dm = CDDM_new(numverts, 0, numfaces);
286         
287         if(!dm)
288         {
289                 gzclose( gzf );
290                 return NULL;
291         }
292         
293         // read numverts
294         gotBytes = gzread(gzf, &wri, sizeof(wri));
295
296         // read vertex position from file
297         mvert = CDDM_get_verts(dm);
298         for(i=0; i<numverts; i++) 
299         {
300                 MVert *mv = &mvert[i];
301                 
302                 for(j=0; j<3; j++) 
303                 {
304                         gotBytes = gzread(gzf, &wrf, sizeof( wrf )); 
305                         mv->co[j] = wrf;
306                 }
307         }
308
309         // should be the same as numverts
310         gotBytes = gzread(gzf, &wri, sizeof(wri));
311         if(wri != numverts) 
312         {
313                 if(dm)
314                         dm->release(dm);
315                 gzclose( gzf );
316                 return NULL;
317         }
318         
319         normals = MEM_callocN(sizeof(short) * numverts * 3, "fluid_tmp_normals" );      
320         if(!normals)
321         {
322                 if(dm)
323                         dm->release(dm);
324                 gzclose( gzf );
325                 return NULL;
326         }       
327         
328         // read normals from file (but don't save them yet)
329         for(i=0; i<numverts*3; i++) 
330         { 
331                 gotBytes = gzread(gzf, &wrf, sizeof( wrf )); 
332                 normals[i] = (short)(wrf*32767.0f);
333         }
334         
335         /* read no. of triangles */
336         gotBytes = gzread(gzf, &wri, sizeof(wri));
337         
338         if(wri!=numfaces)
339                 printf("Fluidsim: error in reading data from file.\n");
340         
341         // read triangles from file
342         mface = CDDM_get_faces(dm);
343         for(i=0; i<numfaces; i++) 
344         {
345                 int face[4];
346                 MFace *mf = &mface[i];
347
348                 gotBytes = gzread(gzf, &(face[0]), sizeof( face[0] )); 
349                 gotBytes = gzread(gzf, &(face[1]), sizeof( face[1] )); 
350                 gotBytes = gzread(gzf, &(face[2]), sizeof( face[2] )); 
351                 face[3] = 0;
352
353                 // check if 3rd vertex has index 0 (not allowed in blender)
354                 if(face[2])
355                 {
356                         mf->v1 = face[0];
357                         mf->v2 = face[1];
358                         mf->v3 = face[2];
359                 }
360                 else
361                 {
362                         mf->v1 = face[1];
363                         mf->v2 = face[2];
364                         mf->v3 = face[0];
365                 }
366                 mf->v4 = face[3];
367                 
368                 test_index_face(mf, NULL, 0, 3);
369         }
370         
371         gzclose( gzf );
372         
373         CDDM_calc_edges(dm);
374         
375         CDDM_apply_vert_normals(dm, (short (*)[3])normals);
376         MEM_freeN(normals);
377         
378         // CDDM_calc_normals(result);
379
380         return dm;
381 }
382
383 DerivedMesh *fluidsim_read_cache(Object *ob, DerivedMesh *orgdm, FluidsimModifierData *fluidmd, int framenr, int useRenderParams)
384 {
385         int displaymode = 0;
386         int curFrame = framenr - 1 /*G.scene->r.sfra*/; /* start with 0 at start frame */
387         char targetDir[FILE_MAXFILE+FILE_MAXDIR], targetFile[FILE_MAXFILE+FILE_MAXDIR];
388         FluidsimSettings *fss = fluidmd->fss;
389         DerivedMesh *dm = NULL;
390         MFace *mface;
391         int numfaces;
392         int mat_nr, flag, i;
393         
394         if(!useRenderParams) {
395                 displaymode = fss->guiDisplayMode;
396         } else {
397                 displaymode = fss->renderDisplayMode;
398         }
399
400         strncpy(targetDir, fss->surfdataPath, FILE_MAXDIR);
401         
402         // use preview or final mesh?
403         if(displaymode==1) 
404         {
405                 // just display original object
406                 return NULL;
407         } 
408         else if(displaymode==2) 
409         {
410                 strcat(targetDir,"fluidsurface_preview_####");
411         } 
412         else 
413         { // 3
414                 strcat(targetDir,"fluidsurface_final_####");
415         }
416         
417         BLI_convertstringcode(targetDir, G.sce);
418         BLI_convertstringframe(targetDir, curFrame); // fixed #frame-no 
419         
420         strcpy(targetFile,targetDir);
421         strcat(targetFile, ".bobj.gz");
422
423         dm = fluidsim_read_obj(targetFile);
424         
425         if(!dm) 
426         {       
427                 // switch, abort background rendering when fluidsim mesh is missing
428                 const char *strEnvName2 = "BLENDER_ELBEEMBOBJABORT"; // from blendercall.cpp
429                 
430                 if(G.background==1) {
431                         if(getenv(strEnvName2)) {
432                                 int elevel = atoi(getenv(strEnvName2));
433                                 if(elevel>0) {
434                                         printf("Env. var %s set, fluid sim mesh '%s' not found, aborting render...\n",strEnvName2, targetFile);
435                                         exit(1);
436                                 }
437                         }
438                 }
439                 
440                 // display org. object upon failure which is in dm
441                 return NULL;
442         }
443         
444         // assign material + flags to new dm
445         mface = orgdm->getFaceArray(orgdm);
446         mat_nr = mface[0].mat_nr;
447         flag = mface[0].flag;
448         
449         mface = dm->getFaceArray(dm);
450         numfaces = dm->getNumFaces(dm);
451         for(i=0; i<numfaces; i++) 
452         {
453                 mface[i].mat_nr = mat_nr;
454                 mface[i].flag = flag;
455         }
456
457         // load vertex velocities, if they exist...
458         // TODO? use generate flag as loading flag as well?
459         // warning, needs original .bobj.gz mesh loading filename
460         /*
461         if(displaymode==3) 
462         {
463                 readVelgz(targetFile, srcob);
464         } 
465         else 
466         {
467                 // no data for preview, only clear...
468                 int i,j;
469                 for(i=0; i<mesh->totvert;i++) { for(j=0; j<3; j++) { srcob->fluidsimSettings->meshSurfNormals[i].co[j] = 0.; }} 
470         }*/
471         
472         return dm;
473 }
474
475 void fluid_get_bb(MVert *mvert, int totvert, float obmat[][4],
476                  /*RET*/ float start[3], /*RET*/ float size[3] )
477 {
478         float bbsx=0.0, bbsy=0.0, bbsz=0.0;
479         float bbex=1.0, bbey=1.0, bbez=1.0;
480         int i;
481         float vec[3];
482
483         VECCOPY(vec, mvert[0].co); 
484         Mat4MulVecfl(obmat, vec);
485         bbsx = vec[0]; bbsy = vec[1]; bbsz = vec[2];
486         bbex = vec[0]; bbey = vec[1]; bbez = vec[2];
487
488         for(i = 1; i < totvert; i++) {
489                 VECCOPY(vec, mvert[i].co);
490                 Mat4MulVecfl(obmat, vec);
491
492                 if(vec[0] < bbsx){ bbsx= vec[0]; }
493                 if(vec[1] < bbsy){ bbsy= vec[1]; }
494                 if(vec[2] < bbsz){ bbsz= vec[2]; }
495                 if(vec[0] > bbex){ bbex= vec[0]; }
496                 if(vec[1] > bbey){ bbey= vec[1]; }
497                 if(vec[2] > bbez){ bbez= vec[2]; }
498         }
499
500         // return values...
501         if(start) {
502                 start[0] = bbsx;
503                 start[1] = bbsy;
504                 start[2] = bbsz;
505         } 
506         if(size) {
507                 size[0] = bbex-bbsx;
508                 size[1] = bbey-bbsy;
509                 size[2] = bbez-bbsz;
510         }
511 }
512
513 //-------------------------------------------------------------------------------
514 // old interface
515 //-------------------------------------------------------------------------------
516
517
518
519 //-------------------------------------------------------------------------------
520 // file handling
521 //-------------------------------------------------------------------------------
522
523 void initElbeemMesh(struct Object *ob, 
524                     int *numVertices, float **vertices, 
525       int *numTriangles, int **triangles,
526       int useGlobalCoords, int modifierIndex) 
527 {
528         DerivedMesh *dm = NULL;
529         MVert *mvert;
530         MFace *mface;
531         int countTris=0, i, totvert, totface;
532         float *verts;
533         int *tris;
534
535         dm = mesh_create_derived_index_render(ob, CD_MASK_BAREMESH, modifierIndex);
536         //dm = mesh_create_derived_no_deform(ob,NULL);
537
538         mvert = dm->getVertArray(dm);
539         mface = dm->getFaceArray(dm);
540         totvert = dm->getNumVerts(dm);
541         totface = dm->getNumFaces(dm);
542
543         *numVertices = totvert;
544         verts = MEM_callocN( totvert*3*sizeof(float), "elbeemmesh_vertices");
545         for(i=0; i<totvert; i++) {
546                 VECCOPY( &verts[i*3], mvert[i].co);
547                 if(useGlobalCoords) { Mat4MulVecfl(ob->obmat, &verts[i*3]); }
548         }
549         *vertices = verts;
550
551         for(i=0; i<totface; i++) {
552                 countTris++;
553                 if(mface[i].v4) { countTris++; }
554         }
555         *numTriangles = countTris;
556         tris = MEM_callocN( countTris*3*sizeof(int), "elbeemmesh_triangles");
557         countTris = 0;
558         for(i=0; i<totface; i++) {
559                 int face[4];
560                 face[0] = mface[i].v1;
561                 face[1] = mface[i].v2;
562                 face[2] = mface[i].v3;
563                 face[3] = mface[i].v4;
564
565                 tris[countTris*3+0] = face[0]; 
566                 tris[countTris*3+1] = face[1]; 
567                 tris[countTris*3+2] = face[2]; 
568                 countTris++;
569                 if(face[3]) { 
570                         tris[countTris*3+0] = face[0]; 
571                         tris[countTris*3+1] = face[2]; 
572                         tris[countTris*3+2] = face[3]; 
573                         countTris++;
574                 }
575         }
576         *triangles = tris;
577
578         dm->release(dm);
579 }
580
581 /* read zipped fluidsim velocities into the co's of the fluidsimsettings normals struct */
582 void readVelgz(char *filename, Object *srcob)
583 {
584         int wri, i, j;
585         float wrf;
586         gzFile gzf;
587         MVert *vverts = srcob->fluidsimSettings->meshSurfNormals;
588         int len = strlen(filename);
589         Mesh *mesh = srcob->data;
590         // mesh and vverts have to be valid from loading...
591
592         // clean up in any case
593         for(i=0; i<mesh->totvert;i++) 
594         { 
595                 for(j=0; j<3; j++) 
596                 {
597                         vverts[i].co[j] = 0.; 
598                 } 
599         } 
600         if(srcob->fluidsimSettings->domainNovecgen>0) return;
601
602         if(len<7) 
603         { 
604                 return; 
605         }
606
607         // .bobj.gz , correct filename
608         // 87654321
609         filename[len-6] = 'v';
610         filename[len-5] = 'e';
611         filename[len-4] = 'l';
612
613         gzf = gzopen(filename, "rb");
614         if (!gzf)
615                 return;
616
617         gzread(gzf, &wri, sizeof( wri ));
618         if(wri != mesh->totvert) 
619         {
620                 return; 
621         }
622
623         for(i=0; i<mesh->totvert;i++) 
624         {
625                 for(j=0; j<3; j++) 
626                 {
627                         gzread(gzf, &wrf, sizeof( wrf )); 
628                         vverts[i].co[j] = wrf;
629                 }
630         }
631
632         gzclose(gzf);
633 }
634
635
636 #endif // DISABLE_ELBEEM
637