quiet various warnings, also disable -Wdouble-promotion with cmake since it gives...
[blender.git] / source / blender / editors / physics / physics_fluid.c
1 /*
2  * fluidsim.c
3  * 
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * The Original Code is Copyright (C) Blender Foundation
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 /** \file blender/editors/physics/physics_fluid.c
33  *  \ingroup edphys
34  */
35
36
37
38
39 #include <math.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/stat.h>
43
44 #ifdef WIN32    /* Windos */
45 #ifndef snprintf
46 #define snprintf _snprintf
47 #endif
48 #endif
49
50 #include "MEM_guardedalloc.h"
51
52 /* types */
53 #include "DNA_anim_types.h"
54 #include "DNA_action_types.h"
55 #include "DNA_object_types.h"
56 #include "DNA_object_fluidsim.h"        
57
58 #include "BLI_blenlib.h"
59 #include "BLI_threads.h"
60 #include "BLI_math.h"
61 #include "BLI_utildefines.h"
62
63 #include "BKE_animsys.h"
64 #include "BKE_armature.h"
65 #include "BKE_blender.h"
66 #include "BKE_context.h"
67 #include "BKE_customdata.h"
68 #include "BKE_DerivedMesh.h"
69 #include "BKE_displist.h"
70 #include "BKE_effect.h"
71 #include "BKE_fluidsim.h"
72 #include "BKE_global.h"
73 #include "BKE_ipo.h"
74 #include "BKE_key.h"
75 #include "BKE_main.h"
76 #include "BKE_modifier.h"
77 #include "BKE_object.h"
78 #include "BKE_report.h"
79 #include "BKE_scene.h"
80 #include "BKE_softbody.h"
81 #include "BKE_unit.h"
82
83
84 #include "LBM_fluidsim.h"
85
86 #include "ED_screen.h"
87 #include "ED_fluidsim.h"
88
89 #include "WM_types.h"
90 #include "WM_api.h"
91
92 #include "physics_intern.h" // own include
93
94 /* enable/disable overall compilation */
95 #ifndef DISABLE_ELBEEM
96
97 #include "WM_api.h"
98
99 #include "DNA_scene_types.h"
100 #include "DNA_ipo_types.h"
101 #include "DNA_mesh_types.h"
102
103 #include "PIL_time.h"
104
105
106 static float get_fluid_viscosity(FluidsimSettings *settings)
107 {
108         switch (settings->viscosityMode) {
109                 case 0:         /* unused */
110                         return -1.0;
111                 case 2:         /* water */
112                         return 1.0e-6;
113                 case 3:         /* some (thick) oil */
114                         return 5.0e-5;
115                 case 4:         /* ca. honey */
116                         return 2.0e-3;
117                 case 1:         /* manual */
118                 default:
119                         return (1.0/pow(10.0, settings->viscosityExponent)) * settings->viscosityValue;
120         }
121 }
122
123 static void get_fluid_gravity(float *gravity, Scene *scene, FluidsimSettings *fss)
124 {
125         if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
126                 copy_v3_v3(gravity, scene->physics_settings.gravity);
127         } else {
128                 copy_v3_v3(gravity, &fss->gravx);
129         }
130 }
131
132 static float get_fluid_size_m(Scene *scene, Object *domainob, FluidsimSettings *fss)
133 {
134         if (!scene->unit.system) {
135                 return fss->realsize;
136         } else {
137                 float dim[3];
138                 float longest_axis;
139                 
140                 object_get_dimensions(domainob, dim);
141                 longest_axis = MAX3(dim[0], dim[1], dim[2]);
142                 
143                 return longest_axis * scene->unit.scale_length;
144         }
145 }
146
147 static int fluid_is_animated_mesh(FluidsimSettings *fss)
148 {
149         return ((fss->type == OB_FLUIDSIM_CONTROL) || fss->domainNovecgen);
150 }
151
152 /* ********************** fluid sim settings struct functions ********************** */
153
154 #if 0
155 /* helper function */
156 void fluidsimGetGeometryObjFilename(Object *ob, char *dst) { //, char *srcname) {
157         //snprintf(dst,FILE_MAXFILE, "%s_cfgdata_%s.bobj.gz", srcname, ob->id.name);
158         snprintf(dst,FILE_MAXFILE, "fluidcfgdata_%s.bobj.gz", ob->id.name);
159 }
160 #endif
161
162
163 /* ********************** fluid sim channel helper functions ********************** */
164
165 typedef struct FluidAnimChannels {
166         int length;
167         
168         double aniFrameTime;
169         
170         float *timeAtFrame;
171         float *DomainTime;
172         float *DomainGravity;
173         float *DomainViscosity;
174 } FluidAnimChannels;
175
176 typedef struct FluidObject {
177         struct FluidObject *next, *prev;
178         
179         struct Object *object;
180         
181         float *Translation;
182         float *Rotation;
183         float *Scale;
184         float *Active;
185         
186         float *InitialVelocity;
187         
188         float *AttractforceStrength;
189         float *AttractforceRadius;
190         float *VelocityforceStrength;
191         float *VelocityforceRadius;
192         
193         float *VertexCache;
194         int numVerts, numTris;
195 } FluidObject;
196
197 // no. of entries for the two channel sizes
198 #define CHANNEL_FLOAT 1
199 #define CHANNEL_VEC   3
200
201 // simplify channels before printing
202 // for API this is done anyway upon init
203 #if 0
204 static void fluidsimPrintChannel(FILE *file, float *channel, int paramsize, char *str, int entries) 
205
206         int i,j; 
207         int channelSize = paramsize; 
208
209         if(entries==3) {
210                 elbeemSimplifyChannelVec3( channel, &channelSize); 
211         } else if(entries==1) {
212                 elbeemSimplifyChannelFloat( channel, &channelSize); 
213         } else {
214                 // invalid, cant happen?
215         }
216
217         fprintf(file, "      CHANNEL %s = \n", str); 
218         for(i=0; i<channelSize;i++) { 
219                 fprintf(file,"        ");  
220                 for(j=0;j<=entries;j++) {  // also print time value
221                         fprintf(file," %f ", channel[i*(entries+1)+j] ); 
222                         if(j==entries-1){ fprintf(file,"  "); }
223                 } 
224                 fprintf(file," \n");  
225         } 
226
227         fprintf(file,  "      ; \n" ); 
228 }
229 #endif
230
231
232 /* Note: fluid anim channel data layout
233  * ------------------------------------
234  * CHANNEL_FLOAT:
235  * frame 1     |frame 2
236  * [dataF][time][dataF][time]
237  *
238  * CHANNEL_VEC:
239  * frame 1                   |frame 2
240  * [dataX][dataY][dataZ][time][dataX][dataY][dataZ][time]
241  *
242  */
243
244 static void init_time(FluidsimSettings *domainSettings, FluidAnimChannels *channels)
245 {
246         int i;
247         
248         channels->timeAtFrame = MEM_callocN( (channels->length+1)*sizeof(float), "timeAtFrame channel");
249         
250         channels->timeAtFrame[0] = channels->timeAtFrame[1] = domainSettings->animStart; // start at index 1
251         
252         for(i=2; i<=channels->length; i++) {
253                 channels->timeAtFrame[i] = channels->timeAtFrame[i-1] + channels->aniFrameTime;
254         }
255 }
256
257 /* if this is slow, can replace with faster, less readable code */
258 static void set_channel(float *channel, float time, float *value, int i, int size)
259 {
260         if (size == CHANNEL_FLOAT) {
261                 channel[(i * 2) + 0] = value[0];
262                 channel[(i * 2) + 1] = time;
263         }
264         else if (size == CHANNEL_VEC) {
265                 channel[(i * 4) + 0] = value[0];
266                 channel[(i * 4) + 1] = value[1];
267                 channel[(i * 4) + 2] = value[2];
268                 channel[(i * 4) + 3] = time;
269         }
270 }
271
272 static void set_vertex_channel(float *channel, float time, struct Scene *scene, struct FluidObject *fobj, int i)
273 {
274         Object *ob = fobj->object;
275         FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
276         float *verts;
277         int *tris=NULL, numVerts=0, numTris=0;
278         int modifierIndex = modifiers_indexInObject(ob, (ModifierData *)fluidmd);
279         int framesize = (3*fobj->numVerts) + 1;
280         int j;
281         
282         if (channel == NULL)
283                 return;
284         
285         initElbeemMesh(scene, ob, &numVerts, &verts, &numTris, &tris, 1, modifierIndex);
286         
287         /* don't allow mesh to change number of verts in anim sequence */
288         if (numVerts != fobj->numVerts) {
289                 MEM_freeN(channel);
290                 channel = NULL;
291                 return;
292         }
293         
294         /* fill frame of channel with vertex locations */
295         for(j=0; j < (3*numVerts); j++) {
296                 channel[i*framesize + j] = verts[j];
297         }
298         channel[i*framesize + framesize-1] = time;
299         
300         MEM_freeN(verts);
301         MEM_freeN(tris);
302 }
303
304 static void free_domain_channels(FluidAnimChannels *channels)
305 {
306         if (!channels->timeAtFrame)
307                 return;
308         MEM_freeN(channels->timeAtFrame);
309         channels->timeAtFrame = NULL;
310         MEM_freeN(channels->DomainGravity);
311         channels->DomainGravity = NULL;
312         MEM_freeN(channels->DomainViscosity);
313         channels->DomainViscosity = NULL;
314 }
315
316 static void free_all_fluidobject_channels(ListBase *fobjects)
317 {
318         FluidObject *fobj;
319         
320         for (fobj=fobjects->first; fobj; fobj=fobj->next) {
321                 if (fobj->Translation) {
322                         MEM_freeN(fobj->Translation);
323                         fobj->Translation = NULL;
324                         MEM_freeN(fobj->Rotation);
325                         fobj->Rotation = NULL;
326                         MEM_freeN(fobj->Scale);
327                         fobj->Scale = NULL;
328                         MEM_freeN(fobj->Active);
329                         fobj->Active = NULL;
330                         MEM_freeN(fobj->InitialVelocity);
331                         fobj->InitialVelocity = NULL;
332                 }
333                 
334                 if (fobj->AttractforceStrength) {
335                         MEM_freeN(fobj->AttractforceStrength);
336                         fobj->AttractforceStrength = NULL;
337                         MEM_freeN(fobj->AttractforceRadius);
338                         fobj->AttractforceRadius = NULL;
339                         MEM_freeN(fobj->VelocityforceStrength);
340                         fobj->VelocityforceStrength = NULL;
341                         MEM_freeN(fobj->VelocityforceRadius);
342                         fobj->VelocityforceRadius = NULL;
343                 }
344                 
345                 if (fobj->VertexCache) {
346                         MEM_freeN(fobj->VertexCache);
347                         fobj->VertexCache = NULL;
348                 }
349         }
350 }
351
352 static void fluid_init_all_channels(bContext *C, Object *UNUSED(fsDomain), FluidsimSettings *domainSettings, FluidAnimChannels *channels, ListBase *fobjects)
353 {
354         Scene *scene = CTX_data_scene(C);
355         Base *base;
356         int i;
357         int length = channels->length;
358         float eval_time;
359         
360         /* XXX: first init time channel - temporary for now */
361         /* init time values (should be done after evaluating animated time curve) */
362         init_time(domainSettings, channels);
363         
364         /* allocate domain animation channels */
365         channels->DomainGravity = MEM_callocN( length * (CHANNEL_VEC+1) * sizeof(float), "channel DomainGravity");
366         channels->DomainViscosity = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "channel DomainViscosity");
367         //channels->DomainTime = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "channel DomainTime");
368         
369         /* allocate fluid objects */
370         for (base=scene->base.first; base; base= base->next) {
371                 Object *ob = base->object;
372                 FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
373                 
374                 if (fluidmd) {
375                         FluidObject *fobj = MEM_callocN(sizeof(FluidObject), "Fluid Object");
376                         fobj->object = ob;
377                         
378                         if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE)) {
379                                 BLI_addtail(fobjects, fobj);
380                                 continue;
381                         }
382                         
383                         fobj->Translation = MEM_callocN( length * (CHANNEL_VEC+1) * sizeof(float), "fluidobject Translation");
384                         fobj->Rotation = MEM_callocN( length * (CHANNEL_VEC+1) * sizeof(float), "fluidobject Rotation");
385                         fobj->Scale = MEM_callocN( length * (CHANNEL_VEC+1) * sizeof(float), "fluidobject Scale");
386                         fobj->Active = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "fluidobject Active");
387                         fobj->InitialVelocity = MEM_callocN( length * (CHANNEL_VEC+1) * sizeof(float), "fluidobject InitialVelocity");
388                         
389                         if (fluidmd->fss->type == OB_FLUIDSIM_CONTROL) {
390                                 fobj->AttractforceStrength = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "fluidobject AttractforceStrength");
391                                 fobj->AttractforceRadius = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "fluidobject AttractforceRadius");
392                                 fobj->VelocityforceStrength = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "fluidobject VelocityforceStrength");
393                                 fobj->VelocityforceRadius = MEM_callocN( length * (CHANNEL_FLOAT+1) * sizeof(float), "fluidobject VelocityforceRadius");
394                         }
395                         
396                         if (fluid_is_animated_mesh(fluidmd->fss)) {
397                                 float *verts=NULL;
398                                 int *tris=NULL, modifierIndex = modifiers_indexInObject(ob, (ModifierData *)fluidmd);
399
400                                 initElbeemMesh(scene, ob, &fobj->numVerts, &verts, &fobj->numTris, &tris, 0, modifierIndex);
401                                 fobj->VertexCache = MEM_callocN( length *((fobj->numVerts*CHANNEL_VEC)+1) * sizeof(float), "fluidobject VertexCache");
402                                 
403                                 MEM_freeN(verts);
404                                 MEM_freeN(tris);
405                         }
406                         
407                         BLI_addtail(fobjects, fobj);
408                 }
409         }
410         
411         /* now we loop over the frames and fill the allocated channels with data */
412         for (i=0; i<channels->length; i++) {
413                 FluidObject *fobj;
414                 float viscosity, gravity[3];
415                 float timeAtFrame;
416                 
417                 eval_time = domainSettings->bakeStart + i;
418                 timeAtFrame = channels->timeAtFrame[i+1];
419                 
420                 /* XXX: This can't be used due to an anim sys optimisation that ignores recalc object animation,
421                  * leaving it for the depgraph (this ignores object animation such as modifier properties though... :/ )
422                  * --> BKE_animsys_evaluate_all_animation(G.main, eval_time);
423                  * This doesn't work with drivers:
424                  * --> BKE_animsys_evaluate_animdata(&fsDomain->id, fsDomain->adt, eval_time, ADT_RECALC_ALL);
425                  */
426                 
427                 /* Modifying the global scene isn't nice, but we can do it in 
428                  * this part of the process before a threaded job is created */
429                 scene->r.cfra = (int)eval_time;
430                 ED_update_for_newframe(CTX_data_main(C), scene, CTX_wm_screen(C), 1);
431                 
432                 /* now scene data should be current according to animation system, so we fill the channels */
433                 
434                 /* Domain properties - gravity/viscosity/time */
435                 get_fluid_gravity(gravity, scene, domainSettings);
436                 set_channel(channels->DomainGravity, timeAtFrame, gravity, i, CHANNEL_VEC);
437                 viscosity = get_fluid_viscosity(domainSettings);
438                 set_channel(channels->DomainViscosity, timeAtFrame, &viscosity, i, CHANNEL_FLOAT);
439                 // XXX : set_channel(channels->DomainTime, timeAtFrame, &time, i, CHANNEL_VEC);
440                 
441                 /* object movement */
442                 for (fobj=fobjects->first; fobj; fobj=fobj->next) {
443                         Object *ob = fobj->object;
444                         FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
445                         float active= (float)(fluidmd->fss->flag & OB_FLUIDSIM_ACTIVE);
446                         float rot_d[3], old_rot[3] = {0.f, 0.f, 0.f};
447                         
448                         if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE))
449                                 continue;
450                         
451                         /* init euler rotation values and convert to elbeem format */
452                         /* get the rotation from ob->obmat rather than ob->rot to account for parent animations */
453                         if(i) {
454                                 copy_v3_v3(old_rot, fobj->Rotation + 4*(i-1));
455                                 mul_v3_fl(old_rot, -M_PI/180.f);
456                         }
457
458                         mat4_to_compatible_eulO(rot_d, old_rot, 0, ob->obmat);
459                         mul_v3_fl(rot_d, -180.f/M_PI);
460                         
461                         set_channel(fobj->Translation, timeAtFrame, ob->loc, i, CHANNEL_VEC);
462                         set_channel(fobj->Rotation, timeAtFrame, rot_d, i, CHANNEL_VEC);
463                         set_channel(fobj->Scale, timeAtFrame, ob->size, i, CHANNEL_VEC);
464                         set_channel(fobj->Active, timeAtFrame, &active, i, CHANNEL_FLOAT);
465                         set_channel(fobj->InitialVelocity, timeAtFrame, &fluidmd->fss->iniVelx, i, CHANNEL_VEC);
466                         
467                         if (fluidmd->fss->type == OB_FLUIDSIM_CONTROL) {
468                                 set_channel(fobj->AttractforceStrength, timeAtFrame, &fluidmd->fss->attractforceStrength, i, CHANNEL_FLOAT);
469                                 set_channel(fobj->AttractforceRadius, timeAtFrame, &fluidmd->fss->attractforceRadius, i, CHANNEL_FLOAT);
470                                 set_channel(fobj->VelocityforceStrength, timeAtFrame, &fluidmd->fss->velocityforceStrength, i, CHANNEL_FLOAT);
471                                 set_channel(fobj->VelocityforceRadius, timeAtFrame, &fluidmd->fss->velocityforceRadius, i, CHANNEL_FLOAT);
472                         }
473                         
474                         if (fluid_is_animated_mesh(fluidmd->fss)) {
475                                 set_vertex_channel(fobj->VertexCache, timeAtFrame, scene, fobj, i);
476                         }
477                 }
478         }
479 }
480
481 static void export_fluid_objects(ListBase *fobjects, Scene *scene, int length)
482 {
483         FluidObject *fobj;
484         
485         for (fobj=fobjects->first; fobj; fobj=fobj->next) {
486                 Object *ob = fobj->object;
487                 FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
488                 int modifierIndex = modifiers_indexInObject(ob, (ModifierData *)fluidmd);
489                 
490                 float *verts=NULL;
491                 int *tris=NULL;
492                 int numVerts=0, numTris=0;
493                 int deform = fluid_is_animated_mesh(fluidmd->fss);
494                 
495                 elbeemMesh fsmesh;
496                 
497                 if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE))
498                         continue;
499                 
500                 elbeemResetMesh( &fsmesh );
501                 
502                 fsmesh.type = fluidmd->fss->type;
503                 fsmesh.name = ob->id.name;
504                 
505                 initElbeemMesh(scene, ob, &numVerts, &verts, &numTris, &tris, 0, modifierIndex);
506                 
507                 fsmesh.numVertices   = numVerts;
508                 fsmesh.numTriangles  = numTris;
509                 fsmesh.vertices      = verts;
510                 fsmesh.triangles     = tris;
511                 
512                 fsmesh.channelSizeTranslation  = 
513                 fsmesh.channelSizeRotation     = 
514                 fsmesh.channelSizeScale        = 
515                 fsmesh.channelSizeInitialVel   = 
516                 fsmesh.channelSizeActive       = length;
517                 
518                 fsmesh.channelTranslation      = fobj->Translation;
519                 fsmesh.channelRotation         = fobj->Rotation;
520                 fsmesh.channelScale            = fobj->Scale;
521                 fsmesh.channelActive           = fobj->Active;
522                 
523                 if( ELEM(fsmesh.type, OB_FLUIDSIM_FLUID, OB_FLUIDSIM_INFLOW)) {
524                         fsmesh.channelInitialVel = fobj->InitialVelocity;
525                         fsmesh.localInivelCoords = ((fluidmd->fss->typeFlags & OB_FSINFLOW_LOCALCOORD)?1:0);
526                 } 
527                 
528                 if(fluidmd->fss->typeFlags & OB_FSBND_NOSLIP)
529                         fsmesh.obstacleType = FLUIDSIM_OBSTACLE_NOSLIP;
530                 else if(fluidmd->fss->typeFlags & OB_FSBND_PARTSLIP)
531                         fsmesh.obstacleType = FLUIDSIM_OBSTACLE_PARTSLIP;
532                 else if(fluidmd->fss->typeFlags & OB_FSBND_FREESLIP)
533                         fsmesh.obstacleType = FLUIDSIM_OBSTACLE_FREESLIP;
534                 
535                 fsmesh.obstaclePartslip = fluidmd->fss->partSlipValue;
536                 fsmesh.volumeInitType = fluidmd->fss->volumeInitType;
537                 fsmesh.obstacleImpactFactor = fluidmd->fss->surfaceSmoothing; // misused value
538                 
539                 if (fsmesh.type == OB_FLUIDSIM_CONTROL) {
540                         fsmesh.cpsTimeStart = fluidmd->fss->cpsTimeStart;
541                         fsmesh.cpsTimeEnd = fluidmd->fss->cpsTimeEnd;
542                         fsmesh.cpsQuality = fluidmd->fss->cpsQuality;
543                         fsmesh.obstacleType = (fluidmd->fss->flag & OB_FLUIDSIM_REVERSE);
544                         
545                         fsmesh.channelSizeAttractforceRadius = 
546                         fsmesh.channelSizeVelocityforceStrength = 
547                         fsmesh.channelSizeVelocityforceRadius = 
548                         fsmesh.channelSizeAttractforceStrength = length;
549                         
550                         fsmesh.channelAttractforceStrength = fobj->AttractforceStrength;
551                         fsmesh.channelAttractforceRadius = fobj->AttractforceRadius;
552                         fsmesh.channelVelocityforceStrength = fobj->VelocityforceStrength;
553                         fsmesh.channelVelocityforceRadius = fobj->VelocityforceRadius;
554                 }
555                 else {
556                         fsmesh.channelAttractforceStrength =
557                         fsmesh.channelAttractforceRadius = 
558                         fsmesh.channelVelocityforceStrength = 
559                         fsmesh.channelVelocityforceRadius = NULL; 
560                 }
561                 
562                 /* animated meshes */
563                 if(deform) {
564                         fsmesh.channelSizeVertices = length;
565                         fsmesh.channelVertices = fobj->VertexCache;
566                                 
567                         // remove channels
568                         fsmesh.channelTranslation      = 
569                         fsmesh.channelRotation         = 
570                         fsmesh.channelScale            = NULL; 
571                 }
572                 
573                 elbeemAddMesh(&fsmesh);
574                 
575                 if(verts) MEM_freeN(verts);
576                 if(tris) MEM_freeN(tris);
577         }
578 }
579
580 static int fluid_validate_scene(ReportList *reports, Scene *scene, Object *fsDomain)
581 {
582         Base *base;
583         Object *newdomain = NULL;
584         int channelObjCount = 0;
585         int fluidInputCount = 0;
586
587         for(base=scene->base.first; base; base= base->next)
588         {
589                 Object *ob = base->object;
590                 FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);                    
591
592                 /* only find objects with fluid modifiers */
593                 if (!fluidmdtmp || ob->type != OB_MESH) continue;
594                         
595                 if(fluidmdtmp->fss->type == OB_FLUIDSIM_DOMAIN) {
596                         /* if no initial domain object given, find another potential domain */
597                         if (!fsDomain) {
598                                 newdomain = ob;
599                         }
600                         /* if there's more than one domain, cancel */
601                         else if (fsDomain && ob != fsDomain) {
602                                 BKE_report(reports, RPT_ERROR, "There should be only one domain object.");
603                                 return 0;
604                         }
605                 }
606                 
607                 /* count number of objects needed for animation channels */
608                 if ( !ELEM(fluidmdtmp->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE) )
609                         channelObjCount++;
610                 
611                 /* count number of fluid input objects */
612                 if (ELEM(fluidmdtmp->fss->type, OB_FLUIDSIM_FLUID, OB_FLUIDSIM_INFLOW))
613                         fluidInputCount++;
614         }
615
616         if (newdomain)
617                 fsDomain = newdomain;
618         
619         if (!fsDomain) {
620                 BKE_report(reports, RPT_ERROR, "No domain object found.");
621                 return 0;
622         }
623         
624         if (channelObjCount>=255) {
625                 BKE_report(reports, RPT_ERROR, "Cannot bake with more then 256 objects.");
626                 return 0;
627         }
628         
629         if (fluidInputCount == 0) {
630                 BKE_report(reports, RPT_ERROR, "No fluid input objects in the scene.");
631                 return 0;
632         }
633         
634         return 1;
635 }
636
637
638 #define FLUID_SUFFIX_CONFIG             "fluidsim.cfg"
639 #define FLUID_SUFFIX_SURFACE    "fluidsurface"
640
641 static int fluid_init_filepaths(Object *fsDomain, char *targetDir, char *targetFile, char *debugStrBuffer)
642 {
643         FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(fsDomain, eModifierType_Fluidsim);
644         FluidsimSettings *domainSettings= fluidmd->fss; 
645         FILE *fileCfg;
646         int dirExist = 0;
647         char newSurfdataPath[FILE_MAXDIR+FILE_MAXFILE]; // modified output settings
648         const char *suffixConfig = FLUID_SUFFIX_CONFIG;
649         int outStringsChanged = 0;
650         
651         // prepare names...
652         strncpy(targetDir, domainSettings->surfdataPath, FILE_MAXDIR);
653         strncpy(newSurfdataPath, domainSettings->surfdataPath, FILE_MAXDIR);
654         BLI_path_abs(targetDir, G.main->name); // fixed #frame-no 
655
656         // .tmp: dont overwrite/delete original file
657         BLI_snprintf(targetFile, FILE_MAXDIR+FILE_MAXFILE, "%s%s.tmp", targetDir, suffixConfig);
658
659         // make sure all directories exist
660         // as the bobjs use the same dir, this only needs to be checked
661         // for the cfg output
662         BLI_make_existing_file(targetFile);
663         
664         // check selected directory
665         // simply try to open cfg file for writing to test validity of settings
666         fileCfg = fopen(targetFile, "w");
667         if(fileCfg) { 
668                 dirExist = 1; fclose(fileCfg); 
669                 // remove cfg dummy from  directory test
670                 BLI_delete(targetFile, 0,0);
671         }
672         
673         if((strlen(targetDir)<1) || (!dirExist)) {
674                 char blendDir[FILE_MAXDIR+FILE_MAXFILE];
675                 char blendFile[FILE_MAXDIR+FILE_MAXFILE];
676                 
677                 // invalid dir, reset to current/previous
678                 BLI_strncpy(blendDir, G.main->name, FILE_MAXDIR+FILE_MAXFILE);
679                 BLI_splitdirstring(blendDir, blendFile);
680                 BLI_replace_extension(blendFile, FILE_MAXDIR+FILE_MAXFILE, ""); /* strip .blend */
681
682                 BLI_snprintf(newSurfdataPath, FILE_MAXDIR+FILE_MAXFILE ,"//fluidsimdata/%s_%s_", blendFile, fsDomain->id.name);
683                 
684                 BLI_snprintf(debugStrBuffer, 256, "fluidsimBake::error - warning resetting output dir to '%s'\n", newSurfdataPath);
685                 elbeemDebugOut(debugStrBuffer);
686                 outStringsChanged=1;
687         }
688         
689         // check if modified output dir is ok
690 #if 0
691         if(outStringsChanged) {
692                 char dispmsg[FILE_MAXDIR+FILE_MAXFILE+256];
693                 int  selection=0;
694                 BLI_strncpy(dispmsg,"Output settings set to: '", sizeof(dispmsg));
695                 strcat(dispmsg, newSurfdataPath);
696                 strcat(dispmsg, "'%t|Continue with changed settings%x1|Discard and abort%x0");
697                 
698                 // ask user if thats what he/she wants...
699                 selection = pupmenu(dispmsg);
700                 if(selection<1) return 0; // 0 from menu, or -1 aborted
701                 BLI_strncpy(targetDir, newSurfdataPath, sizeof(targetDir));
702                 strncpy(domainSettings->surfdataPath, newSurfdataPath, FILE_MAXDIR);
703                 BLI_path_abs(targetDir, G.main->name); // fixed #frame-no 
704         }
705 #endif  
706         return outStringsChanged;
707 }
708
709 /* ******************************************************************************** */
710 /* ********************** write fluidsim config to file ************************* */
711 /* ******************************************************************************** */
712
713 typedef struct FluidBakeJob {
714         /* from wmJob */
715         void *owner;
716         short *stop, *do_update;
717         float *progress;
718         int current_frame;
719         elbeemSimulationSettings *settings;
720 } FluidBakeJob;
721
722 static void fluidbake_free(void *customdata)
723 {
724         FluidBakeJob *fb= customdata;
725         MEM_freeN(fb);
726 }
727
728 /* called by fluidbake, only to check job 'stop' value */
729 static int fluidbake_breakjob(void *UNUSED(customdata))
730 {
731         //FluidBakeJob *fb= (FluidBakeJob *)customdata;
732         //return *(fb->stop);
733         
734         /* this is not nice yet, need to make the jobs list template better 
735          * for identifying/acting upon various different jobs */
736         /* but for now we'll reuse the render break... */
737         return (G.afbreek);
738 }
739
740 /* called by fluidbake, wmJob sends notifier */
741 static void fluidbake_updatejob(void *customdata, float progress)
742 {
743         FluidBakeJob *fb= customdata;
744         
745         *(fb->do_update)= 1;
746         *(fb->progress)= progress;
747 }
748
749 static void fluidbake_startjob(void *customdata, short *stop, short *do_update, float *progress)
750 {
751         FluidBakeJob *fb= customdata;
752         
753         fb->stop= stop;
754         fb->do_update = do_update;
755         fb->progress = progress;
756         
757         G.afbreek= 0;   /* XXX shared with render - replace with job 'stop' switch */
758         
759         elbeemSimulate();
760         *do_update= 1;
761         *stop = 0;
762 }
763
764 static void fluidbake_endjob(void *customdata)
765 {
766         FluidBakeJob *fb= customdata;
767         
768         if (fb->settings) {
769                 MEM_freeN(fb->settings);
770                 fb->settings = NULL;
771         }
772 }
773
774 int runSimulationCallback(void *data, int status, int frame) {
775         FluidBakeJob *fb = (FluidBakeJob *)data;
776         elbeemSimulationSettings *settings = fb->settings;
777         
778         if (status == FLUIDSIM_CBSTATUS_NEWFRAME) {
779                 fluidbake_updatejob(fb, frame / (float)settings->noOfFrames);
780                 //printf("elbeem blender cb s%d, f%d, domainid:%d noOfFrames: %d \n", status,frame, settings->domainId, settings->noOfFrames ); // DEBUG
781         }
782         
783         if (fluidbake_breakjob(fb))  {
784                 return FLUIDSIM_CBRET_ABORT;
785         }
786         
787         return FLUIDSIM_CBRET_CONTINUE;
788 }
789
790 static void fluidbake_free_data(FluidAnimChannels *channels, ListBase *fobjects, elbeemSimulationSettings *fsset, FluidBakeJob *fb)
791 {
792         free_domain_channels(channels);
793         MEM_freeN(channels);
794         channels = NULL;
795
796         free_all_fluidobject_channels(fobjects);
797         BLI_freelistN(fobjects);
798         MEM_freeN(fobjects);
799         fobjects = NULL;
800         
801         if (fsset) {
802                 MEM_freeN(fsset);
803                 fsset = NULL;
804         }
805         
806         if (fb) {
807                 MEM_freeN(fb);
808                 fb = NULL;
809         }
810 }
811
812 static int fluidsimBake(bContext *C, ReportList *reports, Object *fsDomain)
813 {
814         Scene *scene= CTX_data_scene(C);
815         int i;
816         FluidsimSettings *domainSettings;
817
818         char debugStrBuffer[256];
819         
820         int gridlevels = 0;
821         const char *strEnvName = "BLENDER_ELBEEMDEBUG"; // from blendercall.cpp
822         const char *suffixConfig = FLUID_SUFFIX_CONFIG;
823         const char *suffixSurface = FLUID_SUFFIX_SURFACE;
824
825         char targetDir[FILE_MAXDIR+FILE_MAXFILE];  // store & modify output settings
826         char targetFile[FILE_MAXDIR+FILE_MAXFILE]; // temp. store filename from targetDir for access
827         int  outStringsChanged = 0;             // modified? copy back before baking
828
829         float domainMat[4][4];
830         float invDomMat[4][4];
831
832         int noFrames;
833         int origFrame = scene->r.cfra;
834         
835         FluidAnimChannels *channels = MEM_callocN(sizeof(FluidAnimChannels), "fluid domain animation channels");
836         ListBase *fobjects = MEM_callocN(sizeof(ListBase), "fluid objects");
837         FluidsimModifierData *fluidmd = NULL;
838         Mesh *mesh = NULL;
839         
840         wmJob *steve;
841         FluidBakeJob *fb;
842         elbeemSimulationSettings *fsset= MEM_callocN(sizeof(elbeemSimulationSettings), "Fluid sim settings");
843         
844         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Fluid Simulation", WM_JOB_PROGRESS);
845         fb= MEM_callocN(sizeof(FluidBakeJob), "fluid bake job");
846         
847         if(getenv(strEnvName)) {
848                 int dlevel = atoi(getenv(strEnvName));
849                 elbeemSetDebugLevel(dlevel);
850                 snprintf(debugStrBuffer,256,"fluidsimBake::msg: Debug messages activated due to envvar '%s'\n",strEnvName); 
851                 elbeemDebugOut(debugStrBuffer);
852         }
853         
854         /* make sure it corresponds to startFrame setting (old: noFrames = scene->r.efra - scene->r.sfra +1) */;
855         noFrames = scene->r.efra - 0;
856         if(noFrames<=0) {
857                 BKE_report(reports, RPT_ERROR, "No frames to export - check your animation range settings.");
858                 fluidbake_free_data(channels, fobjects, fsset, fb);
859                 return 0;
860         }
861         
862         /* check scene for sane object/modifier settings */
863         if (!fluid_validate_scene(reports, scene, fsDomain)) {
864                 fluidbake_free_data(channels, fobjects, fsset, fb);
865                 return 0;
866         }
867         
868         /* these both have to be valid, otherwise we wouldnt be here */
869         fluidmd = (FluidsimModifierData *)modifiers_findByType(fsDomain, eModifierType_Fluidsim);
870         domainSettings = fluidmd->fss;
871         mesh = fsDomain->data;
872         
873         domainSettings->bakeStart = 1;
874         domainSettings->bakeEnd = scene->r.efra;
875         
876         // calculate bounding box
877         fluid_get_bb(mesh->mvert, mesh->totvert, fsDomain->obmat, domainSettings->bbStart, domainSettings->bbSize);
878         
879         // reset last valid frame
880         domainSettings->lastgoodframe = -1;
881         
882         /* rough check of settings... */
883         if(domainSettings->previewresxyz > domainSettings->resolutionxyz) {
884                 snprintf(debugStrBuffer,256,"fluidsimBake::warning - Preview (%d) >= Resolution (%d)... setting equal.\n", domainSettings->previewresxyz ,  domainSettings->resolutionxyz); 
885                 elbeemDebugOut(debugStrBuffer);
886                 domainSettings->previewresxyz = domainSettings->resolutionxyz;
887         }
888         // set adaptive coarsening according to resolutionxyz
889         // this should do as an approximation, with in/outflow
890         // doing this more accurate would be overkill
891         // perhaps add manual setting?
892         if(domainSettings->maxRefine <0) {
893                 if(domainSettings->resolutionxyz>128) {
894                         gridlevels = 2;
895                 } else
896                 if(domainSettings->resolutionxyz>64) {
897                         gridlevels = 1;
898                 } else {
899                         gridlevels = 0;
900                 }
901         } else {
902                 gridlevels = domainSettings->maxRefine;
903         }
904         snprintf(debugStrBuffer,256,"fluidsimBake::msg: Baking %s, refine: %d\n", fsDomain->id.name , gridlevels ); 
905         elbeemDebugOut(debugStrBuffer);
906         
907         
908         
909         /* ******** prepare output file paths ******** */
910         outStringsChanged = fluid_init_filepaths(fsDomain, targetDir, targetFile, debugStrBuffer);
911         channels->length = scene->r.efra;
912         channels->aniFrameTime = (domainSettings->animEnd - domainSettings->animStart)/(double)noFrames;
913         
914         /* ******** initialise and allocate animation channels ******** */
915         fluid_init_all_channels(C, fsDomain, domainSettings, channels, fobjects);
916
917         /* reset to original current frame */
918         scene->r.cfra = origFrame;
919         ED_update_for_newframe(CTX_data_main(C), scene, CTX_wm_screen(C), 1);
920         
921         
922         /* ---- XXX: No Time animation curve for now, leaving this code here for reference 
923          
924         { int timeIcu[1] = { FLUIDSIM_TIME };
925                 float timeDef[1] = { 1. };
926
927                 // time channel is a bit special, init by hand...
928                 timeAtIndex = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatindex");
929                 for(i=0; i<=scene->r.efra; i++) {
930                         timeAtIndex[i] = (float)(i-startFrame);
931                 }
932                 fluidsimInitChannel(scene, &channelDomainTime, allchannelSize, timeAtIndex, timeIcu,timeDef, domainSettings->ipo, CHANNEL_FLOAT ); // NDEB
933                 // time channel is a multiplicator for 
934                 if(channelDomainTime) {
935                         for(i=0; i<allchannelSize; i++) { 
936                                 channelDomainTime[i*2+0] = aniFrameTime * channelDomainTime[i*2+0]; 
937                                 if(channelDomainTime[i*2+0]<0.) channelDomainTime[i*2+0] = 0.;
938                         }
939                 }
940                 timeAtFrame = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatframe");
941                 timeAtFrame[0] = timeAtFrame[1] = domainSettings->animStart; // start at index 1
942                 if(channelDomainTime) {
943                         for(i=2; i<=allchannelSize; i++) {
944                                 timeAtFrame[i] = timeAtFrame[i-1]+channelDomainTime[(i-1)*2+0];
945                         }
946                 fsset->} else {
947                         for(i=2; i<=allchannelSize; i++) { timeAtFrame[i] = timeAtFrame[i-1]+aniFrameTime; }
948                 }
949
950         } // domain channel init
951         */
952                 
953         /* ******** init domain object's matrix ******** */
954         copy_m4_m4(domainMat, fsDomain->obmat);
955         if(!invert_m4_m4(invDomMat, domainMat)) {
956                 snprintf(debugStrBuffer,256,"fluidsimBake::error - Invalid obj matrix?\n"); 
957                 elbeemDebugOut(debugStrBuffer);
958                 BKE_report(reports, RPT_ERROR, "Invalid object matrix."); 
959
960                 fluidbake_free_data(channels, fobjects, fsset, fb);
961                 return 0;
962         }
963
964         /* ********  start writing / exporting ******** */
965         // use .tmp, dont overwrite/delete original file
966         BLI_snprintf(targetFile, 240, "%s%s.tmp", targetDir, suffixConfig);
967         
968         // make sure these directories exist as well
969         if(outStringsChanged) {
970                 BLI_make_existing_file(targetFile);
971         }
972
973         /* ******** export domain to elbeem ******** */
974         elbeemResetSettings(fsset);
975         fsset->version = 1;
976
977         // setup global settings
978         copy_v3_v3(fsset->geoStart, domainSettings->bbStart);
979         copy_v3_v3(fsset->geoSize, domainSettings->bbSize);
980         
981         // simulate with 50^3
982         fsset->resolutionxyz = (int)domainSettings->resolutionxyz;
983         fsset->previewresxyz = (int)domainSettings->previewresxyz;
984
985         fsset->realsize = get_fluid_size_m(scene, fsDomain, domainSettings);
986         fsset->viscosity = get_fluid_viscosity(domainSettings);
987         get_fluid_gravity(fsset->gravity, scene, domainSettings);
988
989         // simulate 5 frames, each 0.03 seconds, output to ./apitest_XXX.bobj.gz
990         fsset->animStart = domainSettings->animStart;
991         fsset->aniFrameTime = channels->aniFrameTime;
992         fsset->noOfFrames = noFrames; // is otherwise subtracted in parser
993
994         BLI_snprintf(targetFile, 240, "%s%s", targetDir, suffixSurface);
995
996         // defaults for compressibility and adaptive grids
997         fsset->gstar = domainSettings->gstar;
998         fsset->maxRefine = domainSettings->maxRefine; // check <-> gridlevels
999         fsset->generateParticles = domainSettings->generateParticles; 
1000         fsset->numTracerParticles = domainSettings->generateTracers; 
1001         fsset->surfaceSmoothing = domainSettings->surfaceSmoothing; 
1002         fsset->surfaceSubdivs = domainSettings->surfaceSubdivs; 
1003         fsset->farFieldSize = domainSettings->farFieldSize; 
1004         BLI_strncpy(fsset->outputPath, targetFile, 240);
1005
1006         // domain channels
1007         fsset->channelSizeFrameTime = 
1008         fsset->channelSizeViscosity = 
1009         fsset->channelSizeGravity = channels->length;
1010         fsset->channelFrameTime = channels->DomainTime;
1011         fsset->channelViscosity = channels->DomainViscosity;
1012         fsset->channelGravity = channels->DomainGravity;
1013         
1014         fsset->runsimCallback = &runSimulationCallback;
1015         fsset->runsimUserData = fb;
1016
1017         if (domainSettings->typeFlags & OB_FSBND_NOSLIP)                fsset->domainobsType = FLUIDSIM_OBSTACLE_NOSLIP;
1018         else if (domainSettings->typeFlags&OB_FSBND_PARTSLIP)   fsset->domainobsType = FLUIDSIM_OBSTACLE_PARTSLIP;
1019         else if (domainSettings->typeFlags&OB_FSBND_FREESLIP)   fsset->domainobsType = FLUIDSIM_OBSTACLE_FREESLIP;
1020         fsset->domainobsPartslip = domainSettings->partSlipValue;
1021         fsset->generateVertexVectors = (domainSettings->domainNovecgen==0);
1022
1023         // init blender domain transform matrix
1024         { int j; 
1025         for(i=0; i<4; i++) {
1026                 for(j=0; j<4; j++) {
1027                         fsset->surfaceTrafo[i*4+j] = invDomMat[j][i];
1028                 }
1029         } }
1030
1031         /* ******** init solver with settings ******** */
1032         elbeemInit();
1033         elbeemAddDomain(fsset);
1034         
1035         /* ******** export all fluid objects to elbeem ******** */
1036         export_fluid_objects(fobjects, scene, channels->length);
1037         
1038         /* custom data for fluid bake job */
1039         fb->settings = fsset;
1040         
1041         /* setup job */
1042         WM_jobs_customdata(steve, fb, fluidbake_free);
1043         WM_jobs_timer(steve, 0.1, NC_SCENE|ND_FRAME, NC_SCENE|ND_FRAME);
1044         WM_jobs_callbacks(steve, fluidbake_startjob, NULL, NULL, fluidbake_endjob);
1045         
1046         WM_jobs_start(CTX_wm_manager(C), steve);
1047
1048         /* ******** free stored animation data ******** */
1049         fluidbake_free_data(channels, fobjects, NULL, NULL);
1050
1051         // elbeemFree();
1052         return 1;
1053 }
1054
1055 void fluidsimFreeBake(Object *UNUSED(ob))
1056 {
1057         /* not implemented yet */
1058 }
1059
1060 #else /* DISABLE_ELBEEM */
1061
1062 /* compile dummy functions for disabled fluid sim */
1063
1064 FluidsimSettings *fluidsimSettingsNew(Object *UNUSED(srcob))
1065 {
1066         return NULL;
1067 }
1068
1069 void fluidsimSettingsFree(FluidsimSettings *UNUSED(fss))
1070 {
1071 }
1072
1073 FluidsimSettings* fluidsimSettingsCopy(FluidsimSettings *UNUSED(fss))
1074 {
1075         return NULL;
1076 }
1077
1078 /* only compile dummy functions */
1079 static int fluidsimBake(bContext *UNUSED(C), ReportList *UNUSED(reports), Object *UNUSED(ob))
1080 {
1081         return 0;
1082 }
1083
1084 #endif /* DISABLE_ELBEEM */
1085
1086 /***************************** Operators ******************************/
1087
1088 static int fluid_bake_exec(bContext *C, wmOperator *op)
1089 {
1090         /* only one bake job at a time */
1091         if(WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
1092                 return 0;
1093
1094         if(!fluidsimBake(C, op->reports, CTX_data_active_object(C)))
1095                 return OPERATOR_CANCELLED;
1096
1097         return OPERATOR_FINISHED;
1098 }
1099
1100 void FLUID_OT_bake(wmOperatorType *ot)
1101 {
1102         /* identifiers */
1103         ot->name= "Fluid Simulation Bake";
1104         ot->description= "Bake fluid simulation";
1105         ot->idname= "FLUID_OT_bake";
1106         
1107         /* api callbacks */
1108         ot->exec= fluid_bake_exec;
1109         ot->poll= ED_operator_object_active_editable;
1110 }
1111