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