Work around to keep compiler going with zlib.h:
[blender.git] / source / blender / modifiers / intern / MOD_fluidsim_util.c
1 /*
2 * $Id$
3 *
4 * ***** BEGIN GPL LICENSE BLOCK *****
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software  Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 * The Original Code is Copyright (C) 2005 by the Blender Foundation.
21 * All rights reserved.
22 *
23 * Contributor(s): Daniel Dunbar
24 *                 Ton Roosendaal,
25 *                 Ben Batt,
26 *                 Brecht Van Lommel,
27 *                 Campbell Barton
28 *
29 * ***** END GPL LICENSE BLOCK *****
30 *
31 */
32
33 #include <zlib.h>
34
35 #include "stddef.h"
36 #include "string.h"
37 #include "math.h"
38 #include "float.h"
39
40 #include "BLI_blenlib.h"
41 #include "BLI_math.h"
42
43 #include "MEM_guardedalloc.h"
44
45 #include "BKE_cdderivedmesh.h"
46 #include "BKE_DerivedMesh.h"
47 #include "BKE_global.h"
48 #include "BKE_mesh.h"
49 #include "BKE_utildefines.h"
50
51 #include "DNA_mesh_types.h"
52 #include "DNA_meshdata_types.h"
53 #include "DNA_object_types.h"
54 #include "DNA_object_fluidsim.h"
55 #include "DNA_scene_types.h"
56 #include "DNA_space_types.h"
57
58 #include "MOD_modifiertypes.h"
59 #include "MOD_fluidsim_util.h"
60
61 // headers for fluidsim bobj meshes
62 #include <stdlib.h>
63 #include "LBM_fluidsim.h"
64 #include <string.h>
65 #include <stdio.h>
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->fmd = fluidmd;
80                 fss->type = OB_FLUIDSIM_ENABLE;
81                 fss->show_advancedoptions = 0;
82
83                 fss->resolutionxyz = 65;
84                 fss->previewresxyz = 45;
85                 fss->realsize = 0.5;
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 = 4.0;
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 = OB_FSBND_PARTSLIP;
115                 fss->domainNovecgen = 0;
116                 fss->volumeInitType = 1; // volume
117                 fss->partSlipValue = 0.2;
118
119                 fss->generateTracers = 0;
120                 fss->generateParticles = 0.0;
121                 fss->surfaceSmoothing = 1.0;
122                 fss->surfaceSubdivs = 0.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                 // (ab)used to store velocities
143                 fss->meshSurfNormals = NULL;
144                 
145                 fss->lastgoodframe = -1;
146                 
147                 fss->flag |= OB_FLUIDSIM_ACTIVE;
148
149         }
150 #endif
151         return;
152 }
153
154 void fluidsim_free(FluidsimModifierData *fluidmd)
155 {
156 #ifndef DISABLE_ELBEEM
157         if(fluidmd)
158         {
159                 if(fluidmd->fss->meshSurfNormals)
160                 {
161                         MEM_freeN(fluidmd->fss->meshSurfNormals);
162                         fluidmd->fss->meshSurfNormals = NULL;
163                 }
164                 MEM_freeN(fluidmd->fss);
165         }
166 #endif
167         return;
168 }
169
170 #ifndef DISABLE_ELBEEM
171 /* read .bobj.gz file into a fluidsimDerivedMesh struct */
172 DerivedMesh *fluidsim_read_obj(char *filename)
173 {
174         int wri,i,j;
175         float wrf;
176         int gotBytes;
177         gzFile gzf;
178         int numverts = 0, numfaces = 0;
179         DerivedMesh *dm = NULL;
180         MFace *mface;
181         MVert *mvert;
182         short *normals;
183
184         // ------------------------------------------------
185         // get numverts + numfaces first
186         // ------------------------------------------------
187         gzf = gzopen(filename, "rb");
188         if (!gzf)
189         {
190                 return NULL;
191         }
192
193         // read numverts
194         gotBytes = gzread(gzf, &wri, sizeof(wri));
195         numverts = wri;
196
197         // skip verts
198         for(i=0; i<numverts*3; i++)
199         {
200                 gotBytes = gzread(gzf, &wrf, sizeof( wrf ));
201         }
202
203         // read number of normals
204         gotBytes = gzread(gzf, &wri, sizeof(wri));
205
206         // skip normals
207         for(i=0; i<numverts*3; i++)
208         {
209                 gotBytes = gzread(gzf, &wrf, sizeof( wrf ));
210         }
211
212         /* get no. of triangles */
213         gotBytes = gzread(gzf, &wri, sizeof(wri));
214         numfaces = wri;
215
216         gzclose( gzf );
217         // ------------------------------------------------
218
219         if(!numfaces || !numverts)
220                 return NULL;
221
222         gzf = gzopen(filename, "rb");
223         if (!gzf)
224         {
225                 return NULL;
226         }
227
228         dm = CDDM_new(numverts, 0, numfaces);
229
230         if(!dm)
231         {
232                 gzclose( gzf );
233                 return NULL;
234         }
235
236         // read numverts
237         gotBytes = gzread(gzf, &wri, sizeof(wri));
238
239         // read vertex position from file
240         mvert = CDDM_get_verts(dm);
241         for(i=0; i<numverts; i++)
242         {
243                 MVert *mv = &mvert[i];
244
245                 for(j=0; j<3; j++)
246                 {
247                         gotBytes = gzread(gzf, &wrf, sizeof( wrf ));
248                         mv->co[j] = wrf;
249                 }
250         }
251
252         // should be the same as numverts
253         gotBytes = gzread(gzf, &wri, sizeof(wri));
254         if(wri != numverts)
255         {
256                 if(dm)
257                         dm->release(dm);
258                 gzclose( gzf );
259                 return NULL;
260         }
261
262         normals = MEM_callocN(sizeof(short) * numverts * 3, "fluid_tmp_normals" );
263         if(!normals)
264         {
265                 if(dm)
266                         dm->release(dm);
267                 gzclose( gzf );
268                 return NULL;
269         }
270
271         // read normals from file (but don't save them yet)
272         for(i=0; i<numverts*3; i++)
273         {
274                 gotBytes = gzread(gzf, &wrf, sizeof( wrf ));
275                 normals[i] = (short)(wrf*32767.0f);
276         }
277
278         /* read no. of triangles */
279         gotBytes = gzread(gzf, &wri, sizeof(wri));
280
281         if(wri!=numfaces)
282                 printf("Fluidsim: error in reading data from file.\n");
283
284         // read triangles from file
285         mface = CDDM_get_faces(dm);
286         for(i=0; i<numfaces; i++)
287         {
288                 int face[4];
289                 MFace *mf = &mface[i];
290
291                 gotBytes = gzread(gzf, &(face[0]), sizeof( face[0] ));
292                 gotBytes = gzread(gzf, &(face[1]), sizeof( face[1] ));
293                 gotBytes = gzread(gzf, &(face[2]), sizeof( face[2] ));
294                 face[3] = 0;
295
296                 // check if 3rd vertex has index 0 (not allowed in blender)
297                 if(face[2])
298                 {
299                         mf->v1 = face[0];
300                         mf->v2 = face[1];
301                         mf->v3 = face[2];
302                 }
303                 else
304                 {
305                         mf->v1 = face[1];
306                         mf->v2 = face[2];
307                         mf->v3 = face[0];
308                 }
309                 mf->v4 = face[3];
310
311                 test_index_face(mf, NULL, 0, 3);
312         }
313
314         gzclose( gzf );
315
316         CDDM_calc_edges(dm);
317
318         CDDM_apply_vert_normals(dm, (short (*)[3])normals);
319         MEM_freeN(normals);
320
321         // CDDM_calc_normals(result);
322
323         return dm;
324 }
325
326
327 void fluid_get_bb(MVert *mvert, int totvert, float obmat[][4],
328                  /*RET*/ float start[3], /*RET*/ float size[3] )
329 {
330         float bbsx=0.0, bbsy=0.0, bbsz=0.0;
331         float bbex=1.0, bbey=1.0, bbez=1.0;
332         int i;
333         float vec[3];
334
335         if(totvert == 0) {
336                 zero_v3(start);
337                 zero_v3(size);
338                 return;
339         }
340
341         copy_v3_v3(vec, mvert[0].co);
342         mul_m4_v3(obmat, vec);
343         bbsx = vec[0]; bbsy = vec[1]; bbsz = vec[2];
344         bbex = vec[0]; bbey = vec[1]; bbez = vec[2];
345
346         for(i = 1; i < totvert; i++) {
347                 copy_v3_v3(vec, mvert[i].co);
348                 mul_m4_v3(obmat, vec);
349
350                 if(vec[0] < bbsx){ bbsx= vec[0]; }
351                 if(vec[1] < bbsy){ bbsy= vec[1]; }
352                 if(vec[2] < bbsz){ bbsz= vec[2]; }
353                 if(vec[0] > bbex){ bbex= vec[0]; }
354                 if(vec[1] > bbey){ bbey= vec[1]; }
355                 if(vec[2] > bbez){ bbez= vec[2]; }
356         }
357
358         // return values...
359         if(start) {
360                 start[0] = bbsx;
361                 start[1] = bbsy;
362                 start[2] = bbsz;
363         }
364         if(size) {
365                 size[0] = bbex-bbsx;
366                 size[1] = bbey-bbsy;
367                 size[2] = bbez-bbsz;
368         }
369 }
370
371 //-------------------------------------------------------------------------------
372 // old interface
373 //-------------------------------------------------------------------------------
374
375 void fluid_estimate_memory(Object *ob, FluidsimSettings *fss, char *value)
376 {
377         Mesh *mesh;
378
379         value[0]= '\0';
380
381         if(ob->type == OB_MESH) {
382                 /* use mesh bounding box and object scaling */
383                 mesh= ob->data;
384
385                 fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize);
386                 elbeemEstimateMemreq(fss->resolutionxyz, fss->bbSize[0],fss->bbSize[1],fss->bbSize[2], fss->maxRefine, value);
387         }
388 }
389
390
391 /* read zipped fluidsim velocities into the co's of the fluidsimsettings normals struct */
392 void fluidsim_read_vel_cache(FluidsimModifierData *fluidmd, DerivedMesh *dm, char *filename)
393 {
394         int wri, i, j;
395         float wrf;
396         gzFile gzf;
397         FluidsimSettings *fss = fluidmd->fss;
398         int len = strlen(filename);
399         int totvert = dm->getNumVerts(dm);
400         float *velarray = NULL;
401
402         // mesh and vverts have to be valid from loading...
403
404         if(fss->meshSurfNormals)
405                 MEM_freeN(fss->meshSurfNormals);
406
407         if(len<7)
408         {
409                 return;
410         }
411
412         if(fss->domainNovecgen>0) return;
413
414         // abusing pointer to hold an array of 3d-velocities
415         fss->meshSurfNormals = MEM_callocN(sizeof(float)*3*dm->getNumVerts(dm), "Fluidsim_velocities");
416         // abusing pointer to hold an INT
417         fss->meshSurface = SET_INT_IN_POINTER(totvert);
418
419         velarray = (float *)fss->meshSurfNormals;
420
421         // .bobj.gz , correct filename
422         // 87654321
423         filename[len-6] = 'v';
424         filename[len-5] = 'e';
425         filename[len-4] = 'l';
426
427         gzf = gzopen(filename, "rb");
428         if (!gzf)
429         {
430                 MEM_freeN(fss->meshSurfNormals);
431                 fss->meshSurfNormals = NULL;
432                 return;
433         }
434
435         gzread(gzf, &wri, sizeof( wri ));
436         if(wri != totvert)
437         {
438                 MEM_freeN(fss->meshSurfNormals);
439                 fss->meshSurfNormals = NULL;
440                 return;
441         }
442
443         for(i=0; i<totvert;i++)
444         {
445                 for(j=0; j<3; j++)
446                 {
447                         gzread(gzf, &wrf, sizeof( wrf ));
448                         velarray[3*i + j] = wrf;
449                 }
450         }
451
452         gzclose(gzf);
453 }
454
455 DerivedMesh *fluidsim_read_cache(Object *ob, DerivedMesh *orgdm, FluidsimModifierData *fluidmd, int framenr, int useRenderParams)
456 {
457         int displaymode = 0;
458         int curFrame = framenr - 1 /*scene->r.sfra*/; /* start with 0 at start frame */
459         char targetDir[FILE_MAXFILE+FILE_MAXDIR], targetFile[FILE_MAXFILE+FILE_MAXDIR];
460         FluidsimSettings *fss = fluidmd->fss;
461         DerivedMesh *dm = NULL;
462         MFace *mface;
463         int numfaces;
464         int mat_nr, flag, i;
465
466         if(!useRenderParams) {
467                 displaymode = fss->guiDisplayMode;
468         } else {
469                 displaymode = fss->renderDisplayMode;
470         }
471
472         strncpy(targetDir, fss->surfdataPath, FILE_MAXDIR);
473
474         // use preview or final mesh?
475         if(displaymode==1)
476         {
477                 // just display original object
478                 return NULL;
479         }
480         else if(displaymode==2)
481         {
482                 strcat(targetDir,"fluidsurface_preview_####");
483         }
484         else
485         { // 3
486                 strcat(targetDir,"fluidsurface_final_####");
487         }
488
489         BLI_path_abs(targetDir, G.sce);
490         BLI_path_frame(targetDir, curFrame, 0); // fixed #frame-no
491
492         strcpy(targetFile,targetDir);
493         strcat(targetFile, ".bobj.gz");
494
495         dm = fluidsim_read_obj(targetFile);
496
497         if(!dm)
498         {
499                 // switch, abort background rendering when fluidsim mesh is missing
500                 const char *strEnvName2 = "BLENDER_ELBEEMBOBJABORT"; // from blendercall.cpp
501
502                 if(G.background==1) {
503                         if(getenv(strEnvName2)) {
504                                 int elevel = atoi(getenv(strEnvName2));
505                                 if(elevel>0) {
506                                         printf("Env. var %s set, fluid sim mesh '%s' not found, aborting render...\n",strEnvName2, targetFile);
507                                         exit(1);
508                                 }
509                         }
510                 }
511
512                 // display org. object upon failure which is in dm
513                 return NULL;
514         }
515
516         // assign material + flags to new dm
517         mface = orgdm->getFaceArray(orgdm);
518         mat_nr = mface[0].mat_nr;
519         flag = mface[0].flag;
520
521         mface = dm->getFaceArray(dm);
522         numfaces = dm->getNumFaces(dm);
523         for(i=0; i<numfaces; i++)
524         {
525                 mface[i].mat_nr = mat_nr;
526                 mface[i].flag = flag;
527         }
528
529         // load vertex velocities, if they exist...
530         // TODO? use generate flag as loading flag as well?
531         // warning, needs original .bobj.gz mesh loading filename
532         if(displaymode==3)
533         {
534                 fluidsim_read_vel_cache(fluidmd, dm, targetFile);
535         }
536         else
537         {
538                 if(fss->meshSurfNormals)
539                         MEM_freeN(fss->meshSurfNormals);
540
541                 fss->meshSurfNormals = NULL;
542         }
543
544         return dm;
545 }
546
547 #endif // DISABLE_ELBEEM
548
549 DerivedMesh *fluidsimModifier_do(FluidsimModifierData *fluidmd, Scene *scene, Object *ob, DerivedMesh *dm, int useRenderParams, int isFinalCalc)
550 {
551 #ifndef DISABLE_ELBEEM
552         DerivedMesh *result = NULL;
553         int framenr;
554         FluidsimSettings *fss = NULL;
555
556         framenr= (int)scene->r.cfra;
557         
558         // only handle fluidsim domains
559         if(fluidmd && fluidmd->fss && (fluidmd->fss->type != OB_FLUIDSIM_DOMAIN))
560                 return dm;
561         
562         // sanity check
563         if(!fluidmd || (fluidmd && !fluidmd->fss))
564                 return dm;
565         
566         fss = fluidmd->fss;
567         
568         // timescale not supported yet
569         // clmd->sim_parms->timescale= timescale;
570         
571         // support reversing of baked fluid frames here
572         if((fss->flag & OB_FLUIDSIM_REVERSE) && (fss->lastgoodframe >= 0))
573         {
574                 framenr = fss->lastgoodframe - framenr + 1;
575                 CLAMP(framenr, 1, fss->lastgoodframe);
576         }
577         
578         /* try to read from cache */
579         if(((fss->lastgoodframe >= framenr) || (fss->lastgoodframe < 0)) && (result = fluidsim_read_cache(ob, dm, fluidmd, framenr, useRenderParams)))
580         {
581                 // fss->lastgoodframe = framenr; // set also in src/fluidsim.c
582                 return result;
583         }
584         else
585         {       
586                 // display last known good frame
587                 if(fss->lastgoodframe >= 0)
588                 {
589                         if((result = fluidsim_read_cache(ob, dm, fluidmd, fss->lastgoodframe, useRenderParams))) 
590                         {
591                                 return result;
592                         }
593                         
594                         // it was supposed to be a valid frame but it isn't!
595                         fss->lastgoodframe = framenr - 1;
596                         
597                         
598                         // this could be likely the case when you load an old fluidsim
599                         if((result = fluidsim_read_cache(ob, dm, fluidmd, fss->lastgoodframe, useRenderParams))) 
600                         {
601                                 return result;
602                         }
603                 }
604                 
605                 result = CDDM_copy(dm);
606
607                 if(result) 
608                 {
609                         return result;
610                 }
611         }
612         
613         return dm;
614 #else
615         return NULL;
616 #endif
617 }