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