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