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