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