6 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
22 * The Original Code is Copyright (C) Blender Foundation
23 * All rights reserved.
25 * The Original Code is: all of this file.
27 * Contributor(s): none yet.
29 * ***** END GPL LICENSE BLOCK *****
39 #ifdef WIN32 /* Windos */
41 #define snprintf _snprintf
45 #include "MEM_guardedalloc.h"
48 #include "DNA_anim_types.h"
49 #include "DNA_action_types.h"
50 #include "DNA_object_types.h"
51 #include "DNA_object_fluidsim.h"
53 #include "BLI_blenlib.h"
54 #include "BLI_threads.h"
56 #include "BLI_utildefines.h"
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"
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"
79 #include "LBM_fluidsim.h"
82 #include "ED_screen.h"
86 #include "physics_intern.h" // own include
88 /* enable/disable overall compilation */
89 #ifndef DISABLE_ELBEEM
93 #include "DNA_scene_types.h"
94 #include "DNA_ipo_types.h"
95 #include "DNA_mesh_types.h"
100 static float get_fluid_viscosity(FluidsimSettings *settings)
102 switch (settings->viscosityMode) {
107 case 3: /* some (thick) oil */
109 case 4: /* ca. honey */
113 return (1.0/pow(10.0, settings->viscosityExponent)) * settings->viscosityValue;
117 static void get_fluid_gravity(float *gravity, Scene *scene, FluidsimSettings *fss)
119 if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
120 copy_v3_v3(gravity, scene->physics_settings.gravity);
122 copy_v3_v3(gravity, &fss->gravx);
126 static float get_fluid_size_m(Scene *scene, Object *domainob, FluidsimSettings *fss)
128 if (!scene->unit.system) {
129 return fss->realsize;
134 object_get_dimensions(domainob, dim);
135 longest_axis = MAX3(dim[0], dim[1], dim[2]);
137 return longest_axis * scene->unit.scale_length;
141 static int fluid_is_animated_mesh(FluidsimSettings *fss)
143 return ((fss->type == OB_FLUIDSIM_CONTROL) || fss->domainNovecgen);
146 /* ********************** fluid sim settings struct functions ********************** */
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);
157 /* ********************** fluid sim channel helper functions ********************** */
159 typedef struct FluidAnimChannels {
166 float *DomainGravity;
167 float *DomainViscosity;
170 typedef struct FluidObject {
171 struct FluidObject *next, *prev;
173 struct Object *object;
180 float *InitialVelocity;
182 float *AttractforceStrength;
183 float *AttractforceRadius;
184 float *VelocityforceStrength;
185 float *VelocityforceRadius;
188 int numVerts, numTris;
191 // no. of entries for the two channel sizes
192 #define CHANNEL_FLOAT 1
193 #define CHANNEL_VEC 3
195 // simplify channels before printing
196 // for API this is done anyway upon init
198 static void fluidsimPrintChannel(FILE *file, float *channel, int paramsize, char *str, int entries)
201 int channelSize = paramsize;
204 elbeemSimplifyChannelVec3( channel, &channelSize);
205 } else if(entries==1) {
206 elbeemSimplifyChannelFloat( channel, &channelSize);
208 // invalid, cant happen?
211 fprintf(file, " CHANNEL %s = \n", str);
212 for(i=0; i<channelSize;i++) {
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," "); }
221 fprintf(file, " ; \n" );
226 /* Note: fluid anim channel data layout
227 * ------------------------------------
230 * [dataF][time][dataF][time]
234 * [dataX][dataY][dataZ][time][dataX][dataY][dataZ][time]
238 static void init_time(FluidsimSettings *domainSettings, FluidAnimChannels *channels)
242 channels->timeAtFrame = MEM_callocN( (channels->length+1)*sizeof(float), "timeAtFrame channel");
244 channels->timeAtFrame[0] = channels->timeAtFrame[1] = domainSettings->animStart; // start at index 1
246 for(i=2; i<=channels->length; i++) {
247 channels->timeAtFrame[i] = channels->timeAtFrame[i-1] + channels->aniFrameTime;
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)
254 if (size == CHANNEL_FLOAT) {
255 channel[(i * 2) + 0] = value[0];
256 channel[(i * 2) + 1] = time;
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;
266 static void set_vertex_channel(float *channel, float time, struct Scene *scene, struct FluidObject *fobj, int i)
268 Object *ob = fobj->object;
269 FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
271 int *tris=NULL, numVerts=0, numTris=0;
272 int modifierIndex = modifiers_indexInObject(ob, (ModifierData *)fluidmd);
273 int framesize = (3*fobj->numVerts) + 1;
279 initElbeemMesh(scene, ob, &numVerts, &verts, &numTris, &tris, 1, modifierIndex);
281 /* don't allow mesh to change number of verts in anim sequence */
282 if (numVerts != fobj->numVerts) {
288 /* fill frame of channel with vertex locations */
289 for(j=0; j < (3*numVerts); j++) {
290 channel[i*framesize + j] = verts[j];
292 channel[i*framesize + framesize-1] = time;
298 static void free_domain_channels(FluidAnimChannels *channels)
300 if (!channels->timeAtFrame)
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;
310 static void free_all_fluidobject_channels(ListBase *fobjects)
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);
322 MEM_freeN(fobj->Active);
324 MEM_freeN(fobj->InitialVelocity);
325 fobj->InitialVelocity = NULL;
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;
339 if (fobj->VertexCache) {
340 MEM_freeN(fobj->VertexCache);
341 fobj->VertexCache = NULL;
346 static void fluid_init_all_channels(bContext *C, Object *UNUSED(fsDomain), FluidsimSettings *domainSettings, FluidAnimChannels *channels, ListBase *fobjects)
348 Scene *scene = CTX_data_scene(C);
351 int length = channels->length;
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);
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");
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);
369 FluidObject *fobj = MEM_callocN(sizeof(FluidObject), "Fluid Object");
372 if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE)) {
373 BLI_addtail(fobjects, fobj);
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");
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");
390 if (fluid_is_animated_mesh(fluidmd->fss)) {
392 int *tris=NULL, modifierIndex = modifiers_indexInObject(ob, (ModifierData *)fluidmd);
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");
401 BLI_addtail(fobjects, fobj);
405 /* now we loop over the frames and fill the allocated channels with data */
406 for (i=0; i<channels->length; i++) {
408 float viscosity, gravity[3];
411 eval_time = domainSettings->bakeStart + i;
412 timeAtFrame = channels->timeAtFrame[i+1];
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);
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);
426 /* now scene data should be current according to animation system, so we fill the channels */
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);
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};
442 if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE))
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);
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);
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);
463 if (fluid_is_animated_mesh(fluidmd->fss)) {
464 set_vertex_channel(fobj->VertexCache, timeAtFrame, scene, fobj, i);
470 static void export_fluid_objects(ListBase *fobjects, Scene *scene, int length)
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);
481 int numVerts=0, numTris=0;
482 int deform = fluid_is_animated_mesh(fluidmd->fss);
486 if (ELEM(fluidmd->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE))
489 elbeemResetMesh( &fsmesh );
491 fsmesh.type = fluidmd->fss->type;
492 fsmesh.name = ob->id.name;
494 initElbeemMesh(scene, ob, &numVerts, &verts, &numTris, &tris, 0, modifierIndex);
496 fsmesh.numVertices = numVerts;
497 fsmesh.numTriangles = numTris;
498 fsmesh.vertices = verts;
499 fsmesh.triangles = tris;
501 fsmesh.channelSizeTranslation =
502 fsmesh.channelSizeRotation =
503 fsmesh.channelSizeScale =
504 fsmesh.channelSizeInitialVel =
505 fsmesh.channelSizeActive = length;
507 fsmesh.channelTranslation = fobj->Translation;
508 fsmesh.channelRotation = fobj->Rotation;
509 fsmesh.channelScale = fobj->Scale;
510 fsmesh.channelActive = fobj->Active;
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);
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;
524 fsmesh.obstaclePartslip = fluidmd->fss->partSlipValue;
525 fsmesh.volumeInitType = fluidmd->fss->volumeInitType;
526 fsmesh.obstacleImpactFactor = fluidmd->fss->surfaceSmoothing; // misused value
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);
534 fsmesh.channelSizeAttractforceRadius =
535 fsmesh.channelSizeVelocityforceStrength =
536 fsmesh.channelSizeVelocityforceRadius =
537 fsmesh.channelSizeAttractforceStrength = length;
539 fsmesh.channelAttractforceStrength = fobj->AttractforceStrength;
540 fsmesh.channelAttractforceRadius = fobj->AttractforceRadius;
541 fsmesh.channelVelocityforceStrength = fobj->VelocityforceStrength;
542 fsmesh.channelVelocityforceRadius = fobj->VelocityforceRadius;
545 fsmesh.channelAttractforceStrength =
546 fsmesh.channelAttractforceRadius =
547 fsmesh.channelVelocityforceStrength =
548 fsmesh.channelVelocityforceRadius = NULL;
551 /* animated meshes */
553 fsmesh.channelSizeVertices = length;
554 fsmesh.channelVertices = fobj->VertexCache;
557 fsmesh.channelTranslation =
558 fsmesh.channelRotation =
559 fsmesh.channelScale = NULL;
562 elbeemAddMesh(&fsmesh);
564 if(verts) MEM_freeN(verts);
565 if(tris) MEM_freeN(tris);
569 static int fluid_validate_scene(ReportList *reports, Scene *scene, Object *fsDomain)
572 Object *newdomain = NULL;
573 int channelObjCount = 0;
574 int fluidInputCount = 0;
576 for(base=scene->base.first; base; base= base->next)
578 Object *ob = base->object;
579 FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
581 /* only find objects with fluid modifiers */
582 if (!fluidmdtmp || ob->type != OB_MESH) continue;
584 if(fluidmdtmp->fss->type == OB_FLUIDSIM_DOMAIN) {
585 /* if no initial domain object given, find another potential domain */
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.");
596 /* count number of objects needed for animation channels */
597 if ( !ELEM(fluidmdtmp->fss->type, OB_FLUIDSIM_DOMAIN, OB_FLUIDSIM_PARTICLE) )
600 /* count number of fluid input objects */
601 if (ELEM(fluidmdtmp->fss->type, OB_FLUIDSIM_FLUID, OB_FLUIDSIM_INFLOW))
606 fsDomain = newdomain;
609 BKE_report(reports, RPT_ERROR, "No domain object found.");
613 if (channelObjCount>=255) {
614 BKE_report(reports, RPT_ERROR, "Cannot bake with more then 256 objects.");
618 if (fluidInputCount == 0) {
619 BKE_report(reports, RPT_ERROR, "No fluid input objects in the scene.");
627 #define FLUID_SUFFIX_CONFIG "fluidsim.cfg"
628 #define FLUID_SUFFIX_SURFACE "fluidsurface"
630 static int fluid_init_filepaths(Object *fsDomain, char *targetDir, char *targetFile, char *debugStrBuffer)
632 FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(fsDomain, eModifierType_Fluidsim);
633 FluidsimSettings *domainSettings= fluidmd->fss;
636 char newSurfdataPath[FILE_MAXDIR+FILE_MAXFILE]; // modified output settings
637 const char *suffixConfig = FLUID_SUFFIX_CONFIG;
638 int outStringsChanged = 0;
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
645 strcpy(targetFile, targetDir);
646 strcat(targetFile, suffixConfig);
647 strcat(targetFile,".tmp"); // dont overwrite/delete original file
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);
653 // check selected directory
654 // simply try to open cfg file for writing to test validity of settings
655 fileCfg = fopen(targetFile, "w");
657 dirExist = 1; fclose(fileCfg);
658 // remove cfg dummy from directory test
659 BLI_delete(targetFile, 0,0);
662 if((strlen(targetDir)<1) || (!dirExist)) {
663 char blendDir[FILE_MAXDIR+FILE_MAXFILE];
664 char blendFile[FILE_MAXDIR+FILE_MAXFILE];
666 // invalid dir, reset to current/previous
667 strcpy(blendDir, G.main->name);
668 BLI_splitdirstring(blendDir, blendFile);
669 if(BLI_strnlen(blendFile, 7) > 6){
670 int len = strlen(blendFile);
671 if( (blendFile[len-6]=='.')&& (blendFile[len-5]=='b')&& (blendFile[len-4]=='l')&&
672 (blendFile[len-3]=='e')&& (blendFile[len-2]=='n')&& (blendFile[len-1]=='d') ){
673 blendFile[len-6] = '\0';
676 // todo... strip .blend ?
677 snprintf(newSurfdataPath,FILE_MAXFILE+FILE_MAXDIR,"//fluidsimdata/%s_%s_", blendFile, fsDomain->id.name);
679 snprintf(debugStrBuffer,256,"fluidsimBake::error - warning resetting output dir to '%s'\n", newSurfdataPath);
680 elbeemDebugOut(debugStrBuffer);
684 // check if modified output dir is ok
686 if(outStringsChanged) {
687 char dispmsg[FILE_MAXDIR+FILE_MAXFILE+256];
689 strcpy(dispmsg,"Output settings set to: '");
690 strcat(dispmsg, newSurfdataPath);
691 strcat(dispmsg, "'%t|Continue with changed settings%x1|Discard and abort%x0");
693 // ask user if thats what he/she wants...
694 selection = pupmenu(dispmsg);
695 if(selection<1) return 0; // 0 from menu, or -1 aborted
696 strcpy(targetDir, newSurfdataPath);
697 strncpy(domainSettings->surfdataPath, newSurfdataPath, FILE_MAXDIR);
698 BLI_path_abs(targetDir, G.main->name); // fixed #frame-no
701 return outStringsChanged;
704 /* ******************************************************************************** */
705 /* ********************** write fluidsim config to file ************************* */
706 /* ******************************************************************************** */
708 typedef struct FluidBakeJob {
711 short *stop, *do_update;
714 elbeemSimulationSettings *settings;
717 static void fluidbake_free(void *customdata)
719 FluidBakeJob *fb= customdata;
723 /* called by fluidbake, only to check job 'stop' value */
724 static int fluidbake_breakjob(void *UNUSED(customdata))
726 //FluidBakeJob *fb= (FluidBakeJob *)customdata;
727 //return *(fb->stop);
729 /* this is not nice yet, need to make the jobs list template better
730 * for identifying/acting upon various different jobs */
731 /* but for now we'll reuse the render break... */
735 /* called by fluidbake, wmJob sends notifier */
736 static void fluidbake_updatejob(void *customdata, float progress)
738 FluidBakeJob *fb= customdata;
741 *(fb->progress)= progress;
744 static void fluidbake_startjob(void *customdata, short *stop, short *do_update, float *progress)
746 FluidBakeJob *fb= customdata;
749 fb->do_update = do_update;
750 fb->progress = progress;
752 G.afbreek= 0; /* XXX shared with render - replace with job 'stop' switch */
759 static void fluidbake_endjob(void *customdata)
761 FluidBakeJob *fb= customdata;
764 MEM_freeN(fb->settings);
769 int runSimulationCallback(void *data, int status, int frame) {
770 FluidBakeJob *fb = (FluidBakeJob *)data;
771 elbeemSimulationSettings *settings = fb->settings;
773 if (status == FLUIDSIM_CBSTATUS_NEWFRAME) {
774 fluidbake_updatejob(fb, frame / (float)settings->noOfFrames);
775 //printf("elbeem blender cb s%d, f%d, domainid:%d noOfFrames: %d \n", status,frame, settings->domainId, settings->noOfFrames ); // DEBUG
778 if (fluidbake_breakjob(fb)) {
779 return FLUIDSIM_CBRET_ABORT;
782 return FLUIDSIM_CBRET_CONTINUE;
785 static void fluidbake_free_data(FluidAnimChannels *channels, ListBase *fobjects, elbeemSimulationSettings *fsset, FluidBakeJob *fb)
787 free_domain_channels(channels);
791 free_all_fluidobject_channels(fobjects);
792 BLI_freelistN(fobjects);
807 int fluidsimBake(bContext *C, ReportList *reports, Object *fsDomain)
809 Scene *scene= CTX_data_scene(C);
811 FluidsimSettings *domainSettings;
813 char debugStrBuffer[256];
816 const char *strEnvName = "BLENDER_ELBEEMDEBUG"; // from blendercall.cpp
817 const char *suffixConfig = FLUID_SUFFIX_CONFIG;
818 const char *suffixSurface = FLUID_SUFFIX_SURFACE;
820 char targetDir[FILE_MAXDIR+FILE_MAXFILE]; // store & modify output settings
821 char targetFile[FILE_MAXDIR+FILE_MAXFILE]; // temp. store filename from targetDir for access
822 int outStringsChanged = 0; // modified? copy back before baking
824 float domainMat[4][4];
825 float invDomMat[4][4];
828 int origFrame = scene->r.cfra;
830 FluidAnimChannels *channels = MEM_callocN(sizeof(FluidAnimChannels), "fluid domain animation channels");
831 ListBase *fobjects = MEM_callocN(sizeof(ListBase), "fluid objects");
832 FluidsimModifierData *fluidmd = NULL;
837 elbeemSimulationSettings *fsset= MEM_callocN(sizeof(elbeemSimulationSettings), "Fluid sim settings");
839 steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Fluid Simulation", WM_JOB_PROGRESS);
840 fb= MEM_callocN(sizeof(FluidBakeJob), "fluid bake job");
842 if(getenv(strEnvName)) {
843 int dlevel = atoi(getenv(strEnvName));
844 elbeemSetDebugLevel(dlevel);
845 snprintf(debugStrBuffer,256,"fluidsimBake::msg: Debug messages activated due to envvar '%s'\n",strEnvName);
846 elbeemDebugOut(debugStrBuffer);
849 /* make sure it corresponds to startFrame setting (old: noFrames = scene->r.efra - scene->r.sfra +1) */;
850 noFrames = scene->r.efra - 0;
852 BKE_report(reports, RPT_ERROR, "No frames to export - check your animation range settings.");
853 fluidbake_free_data(channels, fobjects, fsset, fb);
857 /* check scene for sane object/modifier settings */
858 if (!fluid_validate_scene(reports, scene, fsDomain)) {
859 fluidbake_free_data(channels, fobjects, fsset, fb);
863 /* these both have to be valid, otherwise we wouldnt be here */
864 fluidmd = (FluidsimModifierData *)modifiers_findByType(fsDomain, eModifierType_Fluidsim);
865 domainSettings = fluidmd->fss;
866 mesh = fsDomain->data;
868 domainSettings->bakeStart = 1;
869 domainSettings->bakeEnd = scene->r.efra;
871 // calculate bounding box
872 fluid_get_bb(mesh->mvert, mesh->totvert, fsDomain->obmat, domainSettings->bbStart, domainSettings->bbSize);
874 // reset last valid frame
875 domainSettings->lastgoodframe = -1;
877 /* rough check of settings... */
878 if(domainSettings->previewresxyz > domainSettings->resolutionxyz) {
879 snprintf(debugStrBuffer,256,"fluidsimBake::warning - Preview (%d) >= Resolution (%d)... setting equal.\n", domainSettings->previewresxyz , domainSettings->resolutionxyz);
880 elbeemDebugOut(debugStrBuffer);
881 domainSettings->previewresxyz = domainSettings->resolutionxyz;
883 // set adaptive coarsening according to resolutionxyz
884 // this should do as an approximation, with in/outflow
885 // doing this more accurate would be overkill
886 // perhaps add manual setting?
887 if(domainSettings->maxRefine <0) {
888 if(domainSettings->resolutionxyz>128) {
891 if(domainSettings->resolutionxyz>64) {
897 gridlevels = domainSettings->maxRefine;
899 snprintf(debugStrBuffer,256,"fluidsimBake::msg: Baking %s, refine: %d\n", fsDomain->id.name , gridlevels );
900 elbeemDebugOut(debugStrBuffer);
904 /* ******** prepare output file paths ******** */
905 outStringsChanged = fluid_init_filepaths(fsDomain, targetDir, targetFile, debugStrBuffer);
906 channels->length = scene->r.efra;
907 channels->aniFrameTime = (domainSettings->animEnd - domainSettings->animStart)/(double)noFrames;
909 /* ******** initialise and allocate animation channels ******** */
910 fluid_init_all_channels(C, fsDomain, domainSettings, channels, fobjects);
912 /* reset to original current frame */
913 scene->r.cfra = origFrame;
914 ED_update_for_newframe(CTX_data_main(C), scene, CTX_wm_screen(C), 1);
917 /* ---- XXX: No Time animation curve for now, leaving this code here for reference
919 { int timeIcu[1] = { FLUIDSIM_TIME };
920 float timeDef[1] = { 1. };
922 // time channel is a bit special, init by hand...
923 timeAtIndex = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatindex");
924 for(i=0; i<=scene->r.efra; i++) {
925 timeAtIndex[i] = (float)(i-startFrame);
927 fluidsimInitChannel(scene, &channelDomainTime, allchannelSize, timeAtIndex, timeIcu,timeDef, domainSettings->ipo, CHANNEL_FLOAT ); // NDEB
928 // time channel is a multiplicator for
929 if(channelDomainTime) {
930 for(i=0; i<allchannelSize; i++) {
931 channelDomainTime[i*2+0] = aniFrameTime * channelDomainTime[i*2+0];
932 if(channelDomainTime[i*2+0]<0.) channelDomainTime[i*2+0] = 0.;
935 timeAtFrame = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatframe");
936 timeAtFrame[0] = timeAtFrame[1] = domainSettings->animStart; // start at index 1
937 if(channelDomainTime) {
938 for(i=2; i<=allchannelSize; i++) {
939 timeAtFrame[i] = timeAtFrame[i-1]+channelDomainTime[(i-1)*2+0];
942 for(i=2; i<=allchannelSize; i++) { timeAtFrame[i] = timeAtFrame[i-1]+aniFrameTime; }
945 } // domain channel init
948 /* ******** init domain object's matrix ******** */
949 copy_m4_m4(domainMat, fsDomain->obmat);
950 if(!invert_m4_m4(invDomMat, domainMat)) {
951 snprintf(debugStrBuffer,256,"fluidsimBake::error - Invalid obj matrix?\n");
952 elbeemDebugOut(debugStrBuffer);
953 BKE_report(reports, RPT_ERROR, "Invalid object matrix.");
955 fluidbake_free_data(channels, fobjects, fsset, fb);
959 /* ******** start writing / exporting ******** */
960 strcpy(targetFile, targetDir);
961 strcat(targetFile, suffixConfig);
962 strcat(targetFile,".tmp"); // dont overwrite/delete original file
964 // make sure these directories exist as well
965 if(outStringsChanged) {
966 BLI_make_existing_file(targetFile);
969 /* ******** export domain to elbeem ******** */
970 elbeemResetSettings(fsset);
973 // setup global settings
974 copy_v3_v3(fsset->geoStart, domainSettings->bbStart);
975 copy_v3_v3(fsset->geoSize, domainSettings->bbSize);
977 // simulate with 50^3
978 fsset->resolutionxyz = (int)domainSettings->resolutionxyz;
979 fsset->previewresxyz = (int)domainSettings->previewresxyz;
981 fsset->realsize = get_fluid_size_m(scene, fsDomain, domainSettings);
982 fsset->viscosity = get_fluid_viscosity(domainSettings);
983 get_fluid_gravity(fsset->gravity, scene, domainSettings);
985 // simulate 5 frames, each 0.03 seconds, output to ./apitest_XXX.bobj.gz
986 fsset->animStart = domainSettings->animStart;
987 fsset->aniFrameTime = channels->aniFrameTime;
988 fsset->noOfFrames = noFrames; // is otherwise subtracted in parser
990 strcpy(targetFile, targetDir);
991 strcat(targetFile, suffixSurface);
992 // defaults for compressibility and adaptive grids
993 fsset->gstar = domainSettings->gstar;
994 fsset->maxRefine = domainSettings->maxRefine; // check <-> gridlevels
995 fsset->generateParticles = domainSettings->generateParticles;
996 fsset->numTracerParticles = domainSettings->generateTracers;
997 fsset->surfaceSmoothing = domainSettings->surfaceSmoothing;
998 fsset->surfaceSubdivs = domainSettings->surfaceSubdivs;
999 fsset->farFieldSize = domainSettings->farFieldSize;
1000 strcpy( fsset->outputPath, targetFile);
1003 fsset->channelSizeFrameTime =
1004 fsset->channelSizeViscosity =
1005 fsset->channelSizeGravity = channels->length;
1006 fsset->channelFrameTime = channels->DomainTime;
1007 fsset->channelViscosity = channels->DomainViscosity;
1008 fsset->channelGravity = channels->DomainGravity;
1010 fsset->runsimCallback = &runSimulationCallback;
1011 fsset->runsimUserData = fb;
1013 if (domainSettings->typeFlags & OB_FSBND_NOSLIP) fsset->domainobsType = FLUIDSIM_OBSTACLE_NOSLIP;
1014 else if (domainSettings->typeFlags&OB_FSBND_PARTSLIP) fsset->domainobsType = FLUIDSIM_OBSTACLE_PARTSLIP;
1015 else if (domainSettings->typeFlags&OB_FSBND_FREESLIP) fsset->domainobsType = FLUIDSIM_OBSTACLE_FREESLIP;
1016 fsset->domainobsPartslip = domainSettings->partSlipValue;
1017 fsset->generateVertexVectors = (domainSettings->domainNovecgen==0);
1019 // init blender domain transform matrix
1021 for(i=0; i<4; i++) {
1022 for(j=0; j<4; j++) {
1023 fsset->surfaceTrafo[i*4+j] = invDomMat[j][i];
1027 /* ******** init solver with settings ******** */
1029 elbeemAddDomain(fsset);
1031 /* ******** export all fluid objects to elbeem ******** */
1032 export_fluid_objects(fobjects, scene, channels->length);
1034 /* custom data for fluid bake job */
1035 fb->settings = fsset;
1038 WM_jobs_customdata(steve, fb, fluidbake_free);
1039 WM_jobs_timer(steve, 0.1, NC_SCENE|ND_FRAME, NC_SCENE|ND_FRAME);
1040 WM_jobs_callbacks(steve, fluidbake_startjob, NULL, NULL, fluidbake_endjob);
1042 WM_jobs_start(CTX_wm_manager(C), steve);
1044 /* ******** free stored animation data ******** */
1045 fluidbake_free_data(channels, fobjects, NULL, NULL);
1051 void fluidsimFreeBake(Object *UNUSED(ob))
1053 /* not implemented yet */
1056 #else /* DISABLE_ELBEEM */
1058 /* compile dummy functions for disabled fluid sim */
1060 FluidsimSettings *fluidsimSettingsNew(Object *UNUSED(srcob))
1065 void fluidsimSettingsFree(FluidsimSettings *UNUSED(fss))
1069 FluidsimSettings* fluidsimSettingsCopy(FluidsimSettings *UNUSED(fss))
1074 /* only compile dummy functions */
1075 int fluidsimBake(bContext *UNUSED(C), ReportList *UNUSED(reports), Object *UNUSED(ob))
1080 void fluidsimFreeBake(Object *UNUSED(ob))
1084 #endif /* DISABLE_ELBEEM */
1086 /***************************** Operators ******************************/
1088 static int fluid_bake_exec(bContext *C, wmOperator *op)
1090 Object *ob= CTX_data_active_object(C);
1092 if(!fluidsimBake(C, op->reports, ob))
1093 return OPERATOR_CANCELLED;
1095 return OPERATOR_FINISHED;
1098 void FLUID_OT_bake(wmOperatorType *ot)
1101 ot->name= "Fluid Simulation Bake";
1102 ot->description= "Bake fluid simulation";
1103 ot->idname= "FLUID_OT_bake";
1106 ot->exec= fluid_bake_exec;
1107 ot->poll= ED_operator_object_active_editable;