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