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