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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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_curve_types.h"
49 #include "DNA_object_types.h"
50 #include "DNA_object_fluidsim.h"
51 #include "DNA_key_types.h"
52 #include "DNA_mesh_types.h"
53 #include "DNA_meshdata_types.h"
54 #include "DNA_lattice_types.h"
55 #include "DNA_scene_types.h"
56 #include "DNA_camera_types.h"
57 #include "DNA_screen_types.h"
58 #include "DNA_space_types.h"
59 #include "DNA_userdef_types.h"
60 #include "DNA_ipo_types.h"
61 #include "DNA_key_types.h"
63 #include "BLI_blenlib.h"
64 #include "BLI_threads.h"
65 #include "BLI_arithb.h"
67 #include "BKE_blender.h"
68 #include "BKE_context.h"
69 #include "BKE_customdata.h"
70 #include "BKE_DerivedMesh.h"
71 #include "BKE_displist.h"
72 #include "BKE_effect.h"
73 #include "BKE_fluidsim.h"
74 #include "BKE_global.h"
78 #include "BKE_modifier.h"
79 #include "BKE_object.h"
80 #include "BKE_report.h"
81 #include "BKE_scene.h"
82 #include "BKE_softbody.h"
86 #include "LBM_fluidsim.h"
90 #include "ED_fluidsim.h"
91 #include "ED_screen.h"
96 /* enable/disable overall compilation */
97 #ifndef DISABLE_ELBEEM
100 /* from header info.c */
101 static int start_progress_bar(void) {return 0;};
102 static void end_progress_bar(void) {};
103 static void waitcursor() {};
104 static int progress_bar(float done, char *busy_info) {return 0;}
105 static int pupmenu() {return 0;}
109 double fluidsimViscosityPreset[6] = {
113 5.0e-5, /* some (thick) oil */
114 2.0e-3, /* ca. honey */
118 char* fluidsimViscosityPresetString[6] = {
119 "UNUSED", /* unused */
120 "UNUSED", /* manual */
121 " = 1.0 * 10^-6", /* water */
122 " = 5.0 * 10^-5", /* some (thick) oil */
123 " = 2.0 * 10^-3", /* ca. honey */
127 /* ********************** fluid sim settings struct functions ********************** */
129 /* helper function */
130 void fluidsimGetGeometryObjFilename(Object *ob, char *dst) { //, char *srcname) {
131 //snprintf(dst,FILE_MAXFILE, "%s_cfgdata_%s.bobj.gz", srcname, ob->id.name);
132 snprintf(dst,FILE_MAXFILE, "fluidcfgdata_%s.bobj.gz", ob->id.name);
138 /* ******************************************************************************** */
139 /* ********************** fluid sim channel helper functions ********************** */
140 /* ******************************************************************************** */
142 // no. of entries for the two channel sizes
143 #define CHANNEL_FLOAT 1
144 #define CHANNEL_VEC 3
146 #define FS_FREE_ONECHANNEL(c,str) { \
147 if(c){ MEM_freeN(c); c=NULL; } \
148 } // end ONE CHANN, debug: fprintf(stderr,"freeing " str " \n");
150 #define FS_FREE_CHANNELS { \
151 FS_FREE_ONECHANNEL(timeAtIndex,"timeAtIndex");\
152 FS_FREE_ONECHANNEL(timeAtFrame,"timeAtFrame");\
153 FS_FREE_ONECHANNEL(channelDomainTime,"channelDomainTime"); \
154 FS_FREE_ONECHANNEL(channelDomainGravity,"channelDomainGravity");\
155 FS_FREE_ONECHANNEL(channelDomainViscosity,"channelDomainViscosity");\
156 for(i=0;i<256;i++) { \
157 FS_FREE_ONECHANNEL(channelObjMove[i][0],"channelObjMove0"); \
158 FS_FREE_ONECHANNEL(channelObjMove[i][1],"channelObjMove1"); \
159 FS_FREE_ONECHANNEL(channelObjMove[i][2],"channelObjMove2"); \
160 FS_FREE_ONECHANNEL(channelObjInivel[i],"channelObjInivel"); \
161 FS_FREE_ONECHANNEL(channelObjActive[i],"channelObjActive"); \
162 FS_FREE_ONECHANNEL(channelAttractforceStrength[i],"channelAttractforceStrength"); \
163 FS_FREE_ONECHANNEL(channelAttractforceRadius[i],"channelAttractforceRadius"); \
164 FS_FREE_ONECHANNEL(channelVelocityforceStrength[i],"channelVelocityforceStrength"); \
165 FS_FREE_ONECHANNEL(channelVelocityforceRadius[i],"channelVelocityforceRadius"); \
167 } // end FS FREE CHANNELS
170 // simplify channels before printing
171 // for API this is done anyway upon init
173 static void fluidsimPrintChannel(FILE *file, float *channel, int paramsize, char *str, int entries)
176 int channelSize = paramsize;
179 elbeemSimplifyChannelVec3( channel, &channelSize);
180 } else if(entries==1) {
181 elbeemSimplifyChannelFloat( channel, &channelSize);
183 // invalid, cant happen?
186 fprintf(file, " CHANNEL %s = \n", str);
187 for(i=0; i<channelSize;i++) {
189 for(j=0;j<=entries;j++) { // also print time value
190 fprintf(file," %f ", channel[i*(entries+1)+j] );
191 if(j==entries-1){ fprintf(file," "); }
196 fprintf(file, " ; \n" );
200 static void fluidsimInitChannel(Scene *scene, float **setchannel, int size, float *time,
201 int *icuIds, float *defaults, Ipo* ipo, int entries)
204 /* goes away completely */
208 float *channel = NULL;
209 float aniFrlen = scene->r.framelen;
210 int current_frame = scene->r.cfra;
211 if((entries<1) || (entries>3)) {
212 printf("fluidsimInitChannel::Error - invalid no. of entries: %d\n",entries);
216 cstr = "fluidsiminit_channelfloat";
217 if(entries>1) cstr = "fluidsiminit_channelvec";
218 channel = MEM_callocN( size* (entries+1)* sizeof(float), cstr );
221 for(j=0; j<entries; j++) icus[j] = find_ipocurve(ipo, icuIds[j] );
223 for(j=0; j<entries; j++) icus[j] = NULL;
226 for(j=0; j<entries; j++) {
228 for(i=1; i<=size; i++) {
229 /* Bugfix to make python drivers working
230 // which uses Blender.get("curframe")
232 scene->r.cfra = floor(aniFrlen*((float)i));
234 // XXX calc_icu(icus[j], aniFrlen*((float)i) );
235 channel[(i-1)*(entries+1) + j] = icus[j]->curval;
238 for(i=1; i<=size; i++) { channel[(i-1)*(entries+1) + j] = defaults[j]; }
240 //printf("fluidsimInitChannel entry:%d , ",j); for(i=1; i<=size; i++) { printf(" val%d:%f ",i, channel[(i-1)*(entries+1) + j] ); } printf(" \n"); // DEBUG
243 for(i=1; i<=size; i++) {
244 channel[(i-1)*(entries+1) + entries] = time[i];
246 scene->r.cfra = current_frame;
247 *setchannel = channel;
251 static void fluidsimInitMeshChannel(bContext *C, float **setchannel, int size, Object *obm, int vertices,
252 float *time, int modifierIndex)
254 Scene *scene= CTX_data_scene(C);
255 float *channel = NULL;
256 int mallsize = size* (3*vertices+1);
258 int numVerts=0, numTris=0;
259 int setsize = 3*vertices+1;
261 channel = MEM_callocN( mallsize* sizeof(float), "fluidsim_meshchannel" );
263 //fprintf(stderr,"\n\nfluidsimInitMeshChannel size%d verts%d mallsize%d \n\n\n",size,vertices,mallsize);
264 for(frame=1; frame<=size; frame++) {
267 scene->r.cfra = frame;
268 ED_update_for_newframe(C, 1);
270 initElbeemMesh(scene, obm, &numVerts, &verts, &numTris, &tris, 1, modifierIndex);
271 //fprintf(stderr,"\nfluidsimInitMeshChannel frame%d verts%d/%d \n\n",frame,vertices,numVerts);
272 for(i=0; i<3*vertices;i++) {
273 channel[(frame-1)*setsize + i] = verts[i];
274 //fprintf(stdout," frame%d vert%d=%f \n",frame,i,verts[i]);
275 //if(i%3==2) fprintf(stdout,"\n");
277 channel[(frame-1)*setsize + setsize-1] = time[frame];
282 *setchannel = channel;
286 /* ******************************************************************************** */
287 /* ********************** simulation thread ************************* */
288 /* ******************************************************************************** */
290 static volatile int globalBakeState = 0; // 0 everything ok, -1 abort simulation, -2 sim error, 1 sim done
291 static volatile int globalBakeFrame = 0;
292 static volatile int g_break= 0;
294 // run simulation in seperate thread
295 static void *fluidsimSimulateThread(void *unused) { // *ptr) {
296 //char* fnameCfgPath = (char*)(ptr);
299 ret = elbeemSimulate();
300 BLI_lock_thread(LOCK_CUSTOM1);
301 if(globalBakeState==0) {
303 // if no error, set to normal exit
306 // simulation failed, display error
307 globalBakeState = -2;
310 BLI_unlock_thread(LOCK_CUSTOM1);
315 int runSimulationCallback(void *data, int status, int frame) {
316 //elbeemSimulationSettings *settings = (elbeemSimulationSettings*)data;
317 //printf("elbeem blender cb s%d, f%d, domainid:%d \n", status,frame, settings->domainId ); // DEBUG
319 if(status==FLUIDSIM_CBSTATUS_NEWFRAME) {
320 BLI_lock_thread(LOCK_CUSTOM1);
321 globalBakeFrame = frame-1;
322 BLI_unlock_thread(LOCK_CUSTOM1);
325 //if((frameCounter==3) && (!frameStop)) { frameStop=1; return 1; }
327 BLI_lock_thread(LOCK_CUSTOM1);
328 state = globalBakeState;
329 BLI_unlock_thread(LOCK_CUSTOM1);
332 return FLUIDSIM_CBRET_ABORT;
335 return FLUIDSIM_CBRET_CONTINUE;
339 /* ******************************************************************************** */
340 /* ********************** write fluidsim config to file ************************* */
341 /* ******************************************************************************** */
343 int fluidsimBake(bContext *C, ReportList *reports, Object *ob)
345 Scene *scene= CTX_data_scene(C);
348 Object *fsDomain = NULL;
349 FluidsimSettings *domainSettings;
350 Object *obit = NULL; /* object iterator */
352 int origFrame = scene->r.cfra;
353 char debugStrBuffer[256];
356 int simAborted = 0; // was the simulation aborted by user?
357 int doExportOnly = 0;
358 char *exportEnvStr = "BLENDER_ELBEEMEXPORTONLY";
359 const char *strEnvName = "BLENDER_ELBEEMDEBUG"; // from blendercall.cpp
360 //char *channelNames[3] = { "translation","rotation","scale" };
362 char *suffixConfig = "fluidsim.cfg";
363 char *suffixSurface = "fluidsurface";
364 char newSurfdataPath[FILE_MAXDIR+FILE_MAXFILE]; // modified output settings
365 char targetDir[FILE_MAXDIR+FILE_MAXFILE]; // store & modify output settings
366 char targetFile[FILE_MAXDIR+FILE_MAXFILE]; // temp. store filename from targetDir for access
367 int outStringsChanged = 0; // modified? copy back before baking
368 int haveSomeFluid = 0; // check if any fluid objects are set
370 // config vars, inited before either export or run...
371 double calcViscosity = 0.0;
376 float *bbStart = NULL;
377 float *bbSize = NULL;
378 float domainMat[4][4];
379 float invDomMat[4][4];
381 int allchannelSize; // fixed by no. of frames
382 int startFrame = 1; // dont use scene->r.sfra here, always start with frame 1
383 // easy frame -> sim time calc
384 float *timeAtFrame=NULL, *timeAtIndex=NULL;
386 float *channelDomainTime = NULL;
387 float *channelDomainViscosity = NULL;
388 float *channelDomainGravity = NULL;
389 // objects (currently max. 256 objs)
390 float *channelObjMove[256][3]; // object movments , 0=trans, 1=rot, 2=scale
391 float *channelObjInivel[256]; // initial velocities
392 float *channelObjActive[256]; // obj active channel
394 /* fluid control channels */
395 float *channelAttractforceStrength[256];
396 float *channelAttractforceRadius[256];
397 float *channelVelocityforceStrength[256];
398 float *channelVelocityforceRadius[256];
399 FluidsimModifierData *fluidmd = NULL;
402 if(getenv(strEnvName)) {
403 int dlevel = atoi(getenv(strEnvName));
404 elbeemSetDebugLevel(dlevel);
405 snprintf(debugStrBuffer,256,"fluidsimBake::msg: Debug messages activated due to envvar '%s'\n",strEnvName);
406 elbeemDebugOut(debugStrBuffer);
408 if(getenv(exportEnvStr)) {
409 doExportOnly = atoi(getenv(exportEnvStr));
410 snprintf(debugStrBuffer,256,"fluidsimBake::msg: Exporting mode set to '%d' due to envvar '%s'\n",doExportOnly, exportEnvStr);
411 elbeemDebugOut(debugStrBuffer);
414 // make sure it corresponds to startFrame setting
415 // old: noFrames = scene->r.efra - scene->r.sfra +1;
416 noFrames = scene->r.efra - 0;
418 BKE_report(reports, RPT_ERROR, "No frames to export - check your animation range settings.");
422 /* no object pointer, find in selected ones.. */
424 for(base=scene->base.first; base; base= base->next) {
425 if ((base)->flag & SELECT)
427 FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim);
429 if(fluidmdtmp && (base->object->type==OB_MESH))
431 if(fluidmdtmp->fss->type == OB_FLUIDSIM_DOMAIN)
444 for(base=scene->base.first; base; base= base->next)
446 FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim);
449 (obit->type==OB_MESH) &&
450 (fluidmdtmp->fss->type != OB_FLUIDSIM_DOMAIN) && // if has to match 3 places! // CHECKMATCH
451 (fluidmdtmp->fss->type != OB_FLUIDSIM_PARTICLE) )
457 if (channelObjCount>=255) {
458 BKE_report(reports, RPT_ERROR, "Cannot bake with more then 256 objects.");
462 /* check if there's another domain... */
463 for(base=scene->base.first; base; base= base->next)
465 FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim);
467 if( fluidmdtmp &&(obit->type==OB_MESH))
469 if(fluidmdtmp->fss->type == OB_FLUIDSIM_DOMAIN)
473 BKE_report(reports, RPT_ERROR, "There should be only one domain object.");
480 // check if theres any fluid
481 // abort baking if not...
482 for(base=scene->base.first; base; base= base->next)
484 FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim);
487 (obit->type==OB_MESH) &&
488 ((fluidmdtmp->fss->type == OB_FLUIDSIM_FLUID) ||
489 (fluidmdtmp->fss->type == OB_FLUIDSIM_INFLOW) ))
496 BKE_report(reports, RPT_ERROR, "No fluid objects in scene.");
500 /* these both have to be valid, otherwise we wouldnt be here */
501 /* dont use ob here after...*/
503 fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
504 domainSettings = fluidmd->fss;
506 mesh = fsDomain->data;
508 // calculate bounding box
509 fluid_get_bb(mesh->mvert, mesh->totvert, fsDomain->obmat, domainSettings->bbStart, domainSettings->bbSize);
511 // reset last valid frame
512 domainSettings->lastgoodframe = -1;
514 /* rough check of settings... */
515 if(domainSettings->previewresxyz > domainSettings->resolutionxyz) {
516 snprintf(debugStrBuffer,256,"fluidsimBake::warning - Preview (%d) >= Resolution (%d)... setting equal.\n", domainSettings->previewresxyz , domainSettings->resolutionxyz);
517 elbeemDebugOut(debugStrBuffer);
518 domainSettings->previewresxyz = domainSettings->resolutionxyz;
520 // set adaptive coarsening according to resolutionxyz
521 // this should do as an approximation, with in/outflow
522 // doing this more accurate would be overkill
523 // perhaps add manual setting?
524 if(domainSettings->maxRefine <0) {
525 if(domainSettings->resolutionxyz>128) {
528 if(domainSettings->resolutionxyz>64) {
534 gridlevels = domainSettings->maxRefine;
536 snprintf(debugStrBuffer,256,"fluidsimBake::msg: Baking %s, refine: %d\n", fsDomain->id.name , gridlevels );
537 elbeemDebugOut(debugStrBuffer);
540 strncpy(targetDir, domainSettings->surfdataPath, FILE_MAXDIR);
541 strncpy(newSurfdataPath, domainSettings->surfdataPath, FILE_MAXDIR);
542 BLI_convertstringcode(targetDir, G.sce); // fixed #frame-no
544 strcpy(targetFile, targetDir);
545 strcat(targetFile, suffixConfig);
546 if(!doExportOnly) { strcat(targetFile,".tmp"); } // dont overwrite/delete original file
547 // make sure all directories exist
548 // as the bobjs use the same dir, this only needs to be checked
549 // for the cfg output
550 BLI_make_existing_file(targetFile);
552 // check selected directory
553 // simply try to open cfg file for writing to test validity of settings
554 fileCfg = fopen(targetFile, "w");
556 dirExist = 1; fclose(fileCfg);
557 // remove cfg dummy from directory test
558 if(!doExportOnly) { BLI_delete(targetFile, 0,0); }
561 if((strlen(targetDir)<1) || (!dirExist)) {
562 char blendDir[FILE_MAXDIR+FILE_MAXFILE], blendFile[FILE_MAXDIR+FILE_MAXFILE];
563 // invalid dir, reset to current/previous
564 strcpy(blendDir, G.sce);
565 BLI_splitdirstring(blendDir, blendFile);
566 if(strlen(blendFile)>6){
567 int len = strlen(blendFile);
568 if( (blendFile[len-6]=='.')&& (blendFile[len-5]=='b')&& (blendFile[len-4]=='l')&&
569 (blendFile[len-3]=='e')&& (blendFile[len-2]=='n')&& (blendFile[len-1]=='d') ){
570 blendFile[len-6] = '\0';
573 // todo... strip .blend ?
574 snprintf(newSurfdataPath,FILE_MAXFILE+FILE_MAXDIR,"//fluidsimdata/%s_%s_", blendFile, fsDomain->id.name);
576 snprintf(debugStrBuffer,256,"fluidsimBake::error - warning resetting output dir to '%s'\n", newSurfdataPath);
577 elbeemDebugOut(debugStrBuffer);
581 // check if modified output dir is ok
582 if(outStringsChanged) {
583 char dispmsg[FILE_MAXDIR+FILE_MAXFILE+256];
585 strcpy(dispmsg,"Output settings set to: '");
586 strcat(dispmsg, newSurfdataPath);
587 strcat(dispmsg, "'%t|Continue with changed settings%x1|Discard and abort%x0");
589 // ask user if thats what he/she wants...
590 selection = pupmenu(dispmsg);
591 if(selection<1) return 0; // 0 from menu, or -1 aborted
592 strcpy(targetDir, newSurfdataPath);
593 strncpy(domainSettings->surfdataPath, newSurfdataPath, FILE_MAXDIR);
594 BLI_convertstringcode(targetDir, G.sce); // fixed #frame-no
597 // --------------------------------------------------------------------------------------------
598 // dump data for start frame
599 // CHECK more reasonable to number frames according to blender?
600 // dump data for frame 0
601 scene->r.cfra = startFrame;
602 ED_update_for_newframe(C, 1);
604 // init common export vars for both file export and run
605 for(i=0; i<256; i++) {
606 channelObjMove[i][0] = channelObjMove[i][1] = channelObjMove[i][2] = NULL;
607 channelObjInivel[i] = NULL;
608 channelObjActive[i] = NULL;
609 channelAttractforceStrength[i] = NULL;
610 channelAttractforceRadius[i] = NULL;
611 channelVelocityforceStrength[i] = NULL;
612 channelVelocityforceRadius[i] = NULL;
614 allchannelSize = scene->r.efra; // always use till last frame
615 aniFrameTime = (domainSettings->animEnd - domainSettings->animStart)/(double)noFrames;
616 // blender specific - scale according to map old/new settings in anim panel:
617 aniFrlen = scene->r.framelen;
618 if(domainSettings->viscosityMode==1) {
619 /* manual mode, visc=value/(10^-vexp) */
620 calcViscosity = (1.0/pow(10.0,domainSettings->viscosityExponent)) * domainSettings->viscosityValue;
622 calcViscosity = fluidsimViscosityPreset[ domainSettings->viscosityMode ];
625 bbStart = domainSettings->bbStart;
626 bbSize = domainSettings->bbSize;
629 { int timeIcu[1] = { FLUIDSIM_TIME };
630 float timeDef[1] = { 1. };
631 int gravIcu[3] = { FLUIDSIM_GRAV_X, FLUIDSIM_GRAV_Y, FLUIDSIM_GRAV_Z };
633 int viscIcu[1] = { FLUIDSIM_VISC };
634 float viscDef[1] = { 1. };
636 gravDef[0] = domainSettings->gravx;
637 gravDef[1] = domainSettings->gravy;
638 gravDef[2] = domainSettings->gravz;
640 // time channel is a bit special, init by hand...
641 timeAtIndex = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatindex");
642 for(i=0; i<=scene->r.efra; i++) {
643 timeAtIndex[i] = (float)(i-startFrame);
645 fluidsimInitChannel(scene, &channelDomainTime, allchannelSize, timeAtIndex, timeIcu,timeDef, domainSettings->ipo, CHANNEL_FLOAT ); // NDEB
646 // time channel is a multiplicator for aniFrameTime
647 if(channelDomainTime) {
648 for(i=0; i<allchannelSize; i++) {
649 channelDomainTime[i*2+0] = aniFrameTime * channelDomainTime[i*2+0];
650 if(channelDomainTime[i*2+0]<0.) channelDomainTime[i*2+0] = 0.;
653 timeAtFrame = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatframe");
654 timeAtFrame[0] = timeAtFrame[1] = domainSettings->animStart; // start at index 1
655 if(channelDomainTime) {
656 for(i=2; i<=allchannelSize; i++) {
657 timeAtFrame[i] = timeAtFrame[i-1]+channelDomainTime[(i-1)*2+0];
660 for(i=2; i<=allchannelSize; i++) { timeAtFrame[i] = timeAtFrame[i-1]+aniFrameTime; }
663 fluidsimInitChannel(scene, &channelDomainViscosity, allchannelSize, timeAtFrame, viscIcu,viscDef, domainSettings->ipo, CHANNEL_FLOAT ); // NDEB
664 if(channelDomainViscosity) {
665 for(i=0; i<allchannelSize; i++) { channelDomainViscosity[i*2+0] = calcViscosity * channelDomainViscosity[i*2+0]; }
667 fluidsimInitChannel(scene, &channelDomainGravity, allchannelSize, timeAtFrame, gravIcu,gravDef, domainSettings->ipo, CHANNEL_VEC );
668 } // domain channel init
670 // init obj movement channels
672 for(base=scene->base.first; base; base= base->next)
674 FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim);
678 (obit->type==OB_MESH) &&
679 (fluidmdtmp->fss->type != OB_FLUIDSIM_DOMAIN) && // if has to match 3 places! // CHECKMATCH
680 (fluidmdtmp->fss->type != OB_FLUIDSIM_PARTICLE) ) {
682 // cant use fluidsimInitChannel for obj channels right now, due
683 // to the special DXXX channels, and the rotation specialities
684 IpoCurve *icuex[3][3];
685 //IpoCurve *par_icuex[3][3];
688 {OB_LOC_X, OB_LOC_Y, OB_LOC_Z},
689 {OB_ROT_X, OB_ROT_Y, OB_ROT_Z},
690 {OB_SIZE_X, OB_SIZE_Y, OB_SIZE_Z}
692 int icudIds[3][3] = {
693 {OB_DLOC_X, OB_DLOC_Y, OB_DLOC_Z},
694 {OB_DROT_X, OB_DROT_Y, OB_DROT_Z},
695 {OB_DSIZE_X, OB_DSIZE_Y, OB_DSIZE_Z}
699 IpoCurve *icudex[3][3];
700 //IpoCurve *par_icudex[3][3];
702 float vals[3] = {0.0,0.0,0.0};
703 int o = channelObjCount;
704 int inivelIcu[3] = { FLUIDSIM_VEL_X, FLUIDSIM_VEL_Y, FLUIDSIM_VEL_Z };
706 int activeIcu[1] = { FLUIDSIM_ACTIVE };
707 float activeDefs[1] = { 1 }; // default to on
709 inivelDefs[0] = fluidmdtmp->fss->iniVelx;
710 inivelDefs[1] = fluidmdtmp->fss->iniVely;
711 inivelDefs[2] = fluidmdtmp->fss->iniVelz;
713 // check & init loc,rot,size
716 // XXX prevent invalid memory access until this works
720 // XXX icuex[j][k] = find_ipocurve(obit->ipo, icuIds[j][k] );
721 // XXX icudex[j][k] = find_ipocurve(obit->ipo, icudIds[j][k] );
722 // XXX lines below were already disabled!
724 //par_icuex[j][k] = find_ipocurve(obit->parent->ipo, icuIds[j][k] );
725 //par_icudex[j][k] = find_ipocurve(obit->parent->ipo, icudIds[j][k] );
731 channelObjMove[o][j] = MEM_callocN( allchannelSize*4*sizeof(float), "fluidsiminit_objmovchannel");
732 for(i=1; i<=allchannelSize; i++) {
736 // IPO exists, use it ...
737 // XXX calc_icu(icuex[j][k], aniFrlen*((float)i) );
738 vals[k] = icuex[j][k]->curval;
740 // add parent transform, multiply scaling, add trafo&rot
741 //calc_icu(par_icuex[j][k], aniFrlen*((float)i) );
742 //if(j==2) { vals[k] *= par_icuex[j][k]->curval; }
743 //else { vals[k] += par_icuex[j][k]->curval; }
746 // use defaults from static values
749 setval = obit->loc[k];
750 if(obit->parent){ setval += obit->parent->loc[k]; }
752 setval = ( 180.0*obit->rot[k] )/( 10.0*M_PI );
753 if(obit->parent){ setval = ( 180.0*(obit->rot[k]+obit->parent->rot[k]) )/( 10.0*M_PI ); }
755 setval = obit->size[k];
756 if(obit->parent){ setval *= obit->parent->size[k]; }
761 // XXX calc_icu(icudex[j][k], aniFrlen*((float)i) );
762 //vals[k] += icudex[j][k]->curval;
763 // add transform, multiply scaling, add trafo&rot
764 if(j==2) { vals[k] *= icudex[j][k]->curval; }
765 else { vals[k] += icudex[j][k]->curval; }
767 // add parent transform, multiply scaling, add trafo&rot
768 //calc_icu(par_icuex[j][k], aniFrlen*((float)i) );
769 //if(j==2) { vals[k] *= par_icudex[j][k]->curval; }
770 //else { vals[k] += par_icudex[j][k]->curval; }
777 if(j==1) { // rot is downscaled by 10 for ipo !?
778 set = 360.0 - (10.0*set);
780 channelObjMove[o][j][(i-1)*4 + k] = set;
782 channelObjMove[o][j][(i-1)*4 + 3] = timeAtFrame[i];
787 int attrFSIcu[1] = { FLUIDSIM_ATTR_FORCE_STR };
788 int attrFRIcu[1] = { FLUIDSIM_ATTR_FORCE_RADIUS };
789 int velFSIcu[1] = { FLUIDSIM_VEL_FORCE_STR };
790 int velFRIcu[1] = { FLUIDSIM_VEL_FORCE_RADIUS };
797 attrFSDefs[0] = fluidmdtmp->fss->attractforceStrength;
798 attrFRDefs[0] = fluidmdtmp->fss->attractforceRadius;
799 velFSDefs[0] = fluidmdtmp->fss->velocityforceStrength;
800 velFRDefs[0] = fluidmdtmp->fss->velocityforceRadius;
802 fluidsimInitChannel(scene, &channelAttractforceStrength[o], allchannelSize, timeAtFrame, attrFSIcu,attrFSDefs, fluidmdtmp->fss->ipo, CHANNEL_FLOAT );
803 fluidsimInitChannel(scene, &channelAttractforceRadius[o], allchannelSize, timeAtFrame, attrFRIcu,attrFRDefs, fluidmdtmp->fss->ipo, CHANNEL_FLOAT );
804 fluidsimInitChannel(scene, &channelVelocityforceStrength[o], allchannelSize, timeAtFrame, velFSIcu,velFSDefs, fluidmdtmp->fss->ipo, CHANNEL_FLOAT );
805 fluidsimInitChannel(scene, &channelVelocityforceRadius[o], allchannelSize, timeAtFrame, velFRIcu,velFRDefs, fluidmdtmp->fss->ipo, CHANNEL_FLOAT );
808 fluidsimInitChannel(scene, &channelObjInivel[o], allchannelSize, timeAtFrame, inivelIcu,inivelDefs, fluidmdtmp->fss->ipo, CHANNEL_VEC );
809 fluidsimInitChannel(scene, &channelObjActive[o], allchannelSize, timeAtFrame, activeIcu,activeDefs, fluidmdtmp->fss->ipo, CHANNEL_FLOAT );
818 Mat4CpyMat4(domainMat, fsDomain->obmat);
819 if(!Mat4Invert(invDomMat, domainMat)) {
820 snprintf(debugStrBuffer,256,"fluidsimBake::error - Invalid obj matrix?\n");
821 elbeemDebugOut(debugStrBuffer);
822 BKE_report(reports, RPT_ERROR, "Invalid object matrix.");
823 // FIXME add fatal msg
828 // --------------------------------------------------------------------------------------------
829 // start writing / exporting
830 strcpy(targetFile, targetDir);
831 strcat(targetFile, suffixConfig);
832 if(!doExportOnly) { strcat(targetFile,".tmp"); } // dont overwrite/delete original file
833 // make sure these directories exist as well
834 if(outStringsChanged) {
835 BLI_make_existing_file(targetFile);
841 // perform simulation with El'Beem api and threads
842 elbeemSimulationSettings fsset;
843 elbeemResetSettings(&fsset);
846 // setup global settings
847 for(i=0 ; i<3; i++) fsset.geoStart[i] = bbStart[i];
848 for(i=0 ; i<3; i++) fsset.geoSize[i] = bbSize[i];
850 // simulate with 50^3
851 fsset.resolutionxyz = (int)domainSettings->resolutionxyz;
852 fsset.previewresxyz = (int)domainSettings->previewresxyz;
854 fsset.realsize = domainSettings->realsize;
855 fsset.viscosity = calcViscosity;
857 fsset.gravity[0] = domainSettings->gravx;
858 fsset.gravity[1] = domainSettings->gravy;
859 fsset.gravity[2] = domainSettings->gravz;
860 // simulate 5 frames, each 0.03 seconds, output to ./apitest_XXX.bobj.gz
861 fsset.animStart = domainSettings->animStart;
862 fsset.aniFrameTime = aniFrameTime;
863 fsset.noOfFrames = noFrames; // is otherwise subtracted in parser
864 strcpy(targetFile, targetDir);
865 strcat(targetFile, suffixSurface);
866 // defaults for compressibility and adaptive grids
867 fsset.gstar = domainSettings->gstar;
868 fsset.maxRefine = domainSettings->maxRefine; // check <-> gridlevels
869 fsset.generateParticles = domainSettings->generateParticles;
870 fsset.numTracerParticles = domainSettings->generateTracers;
871 fsset.surfaceSmoothing = domainSettings->surfaceSmoothing;
872 fsset.surfaceSubdivs = domainSettings->surfaceSubdivs;
873 fsset.farFieldSize = domainSettings->farFieldSize;
874 strcpy( fsset.outputPath, targetFile);
877 fsset.channelSizeFrameTime =
878 fsset.channelSizeViscosity =
879 fsset.channelSizeGravity = allchannelSize;
880 fsset.channelFrameTime = channelDomainTime;
881 fsset.channelViscosity = channelDomainViscosity;
882 fsset.channelGravity = channelDomainGravity;
884 fsset.runsimCallback = &runSimulationCallback;
885 fsset.runsimUserData = &fsset;
887 if( (domainSettings->typeFlags&OB_FSBND_NOSLIP)) fsset.domainobsType = FLUIDSIM_OBSTACLE_NOSLIP;
888 else if((domainSettings->typeFlags&OB_FSBND_PARTSLIP)) fsset.domainobsType = FLUIDSIM_OBSTACLE_PARTSLIP;
889 else if((domainSettings->typeFlags&OB_FSBND_FREESLIP)) fsset.domainobsType = FLUIDSIM_OBSTACLE_FREESLIP;
890 fsset.domainobsPartslip = domainSettings->partSlipValue;
891 fsset.generateVertexVectors = (domainSettings->domainNovecgen==0);
893 // init blender trafo matrix
894 // fprintf(stderr,"elbeemInit - mpTrafo:\n");
898 fsset.surfaceTrafo[i*4+j] = invDomMat[j][i];
899 // fprintf(stderr,"elbeemInit - mpTrafo %d %d = %f (%d) \n", i,j, fsset.surfaceTrafo[i*4+j] , (i*4+j) );
903 // init solver with settings
905 elbeemAddDomain(&fsset);
909 for(base=scene->base.first; base; base= base->next) {
910 FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim);
912 //{ snprintf(debugStrBuffer,256,"DEBUG object name=%s, type=%d ...\n", obit->id.name, obit->type); elbeemDebugOut(debugStrBuffer); } // DEBUG
913 if( fluidmdtmp && // if has to match 3 places! // CHECKMATCH
914 (obit->type==OB_MESH) &&
915 (fluidmdtmp->fss->type != OB_FLUIDSIM_DOMAIN) &&
916 (fluidmdtmp->fss->type != OB_FLUIDSIM_PARTICLE))
920 int numVerts=0, numTris=0;
921 int o = channelObjCount;
922 int deform = (fluidmdtmp->fss->domainNovecgen); // misused value
923 // todo - use blenderInitElbeemMesh
924 int modifierIndex = modifiers_indexInObject(obit, (ModifierData *)fluidmdtmp);
927 elbeemResetMesh( &fsmesh );
928 fsmesh.type = fluidmdtmp->fss->type;
929 // get name of object for debugging solver
930 fsmesh.name = obit->id.name;
932 initElbeemMesh(scene, obit, &numVerts, &verts, &numTris, &tris, 0, modifierIndex);
933 fsmesh.numVertices = numVerts;
934 fsmesh.numTriangles = numTris;
935 fsmesh.vertices = verts;
936 fsmesh.triangles = tris;
938 fsmesh.channelSizeTranslation =
939 fsmesh.channelSizeRotation =
940 fsmesh.channelSizeScale =
941 fsmesh.channelSizeInitialVel =
942 fsmesh.channelSizeActive = allchannelSize;
944 fsmesh.channelTranslation = channelObjMove[o][0];
945 fsmesh.channelRotation = channelObjMove[o][1];
946 fsmesh.channelScale = channelObjMove[o][2];
947 fsmesh.channelActive = channelObjActive[o];
948 if( (fsmesh.type == OB_FLUIDSIM_FLUID) ||
949 (fsmesh.type == OB_FLUIDSIM_INFLOW)) {
950 fsmesh.channelInitialVel = channelObjInivel[o];
951 fsmesh.localInivelCoords = ((fluidmdtmp->fss->typeFlags&OB_FSINFLOW_LOCALCOORD)?1:0);
954 if( (fluidmdtmp->fss->typeFlags&OB_FSBND_NOSLIP)) fsmesh.obstacleType = FLUIDSIM_OBSTACLE_NOSLIP;
955 else if((fluidmdtmp->fss->typeFlags&OB_FSBND_PARTSLIP)) fsmesh.obstacleType = FLUIDSIM_OBSTACLE_PARTSLIP;
956 else if((fluidmdtmp->fss->typeFlags&OB_FSBND_FREESLIP)) fsmesh.obstacleType = FLUIDSIM_OBSTACLE_FREESLIP;
957 fsmesh.obstaclePartslip = fluidmdtmp->fss->partSlipValue;
958 fsmesh.volumeInitType = fluidmdtmp->fss->volumeInitType;
959 fsmesh.obstacleImpactFactor = fluidmdtmp->fss->surfaceSmoothing; // misused value
961 if(fsmesh.type == OB_FLUIDSIM_CONTROL)
963 // control fluids will get exported as whole
966 fsmesh.cpsTimeStart = fluidmdtmp->fss->cpsTimeStart;
967 fsmesh.cpsTimeEnd = fluidmdtmp->fss->cpsTimeEnd;
968 fsmesh.cpsQuality = fluidmdtmp->fss->cpsQuality;
969 fsmesh.obstacleType = (fluidmdtmp->fss->flag & OB_FLUIDSIM_REVERSE);
971 fsmesh.channelSizeAttractforceRadius =
972 fsmesh.channelSizeVelocityforceStrength =
973 fsmesh.channelSizeVelocityforceRadius =
974 fsmesh.channelSizeAttractforceStrength = allchannelSize;
976 fsmesh.channelAttractforceStrength = channelAttractforceStrength[o];
977 fsmesh.channelAttractforceRadius = channelAttractforceRadius[o];
978 fsmesh.channelVelocityforceStrength = channelVelocityforceStrength[o];
979 fsmesh.channelVelocityforceRadius = channelVelocityforceRadius[o];
984 fsmesh.channelAttractforceStrength =
985 fsmesh.channelAttractforceRadius =
986 fsmesh.channelVelocityforceStrength =
987 fsmesh.channelVelocityforceRadius = NULL;
992 fsmesh.channelSizeVertices = allchannelSize;
993 fluidsimInitMeshChannel(C, &fsmesh.channelVertices, allchannelSize, obit, numVerts, timeAtFrame, modifierIndex);
994 scene->r.cfra = startFrame;
995 ED_update_for_newframe(C, 1);
997 fsmesh.channelTranslation =
998 fsmesh.channelRotation =
999 fsmesh.channelScale = NULL;
1002 elbeemAddMesh(&fsmesh);
1004 if(verts) MEM_freeN(verts);
1005 if(tris) MEM_freeN(tris);
1006 if(fsmesh.channelVertices) MEM_freeN(fsmesh.channelVertices);
1010 //domainSettings->type = OB_FLUIDSIM_DOMAIN; // enable for bake display again
1012 // set to neutral, -1 means user abort, -2 means init error
1013 globalBakeState = 0;
1014 globalBakeFrame = 0;
1015 BLI_init_threads(&threads, fluidsimSimulateThread, 1);
1016 BLI_insert_thread(&threads, targetFile);
1020 float noFramesf = (float)noFrames;
1021 float percentdone = 0.0;
1022 int lastRedraw = -1;
1025 G.afbreek= 0; /* blender_test_break uses this global */
1027 start_progress_bar();
1034 // lukep we add progress bar as an interim mesure
1035 percentdone = globalBakeFrame / noFramesf;
1036 sprintf(busy_mess, "baking fluids %d / %d |||", globalBakeFrame, (int) noFramesf);
1037 progress_bar(percentdone, busy_mess );
1039 // longer delay to prevent frequent redrawing
1042 BLI_lock_thread(LOCK_CUSTOM1);
1043 if(globalBakeState != 0) done = 1; // 1=ok, <0=error/abort
1044 BLI_unlock_thread(LOCK_CUSTOM1);
1046 if (!G.background) {
1047 g_break= blender_test_break();
1052 BLI_lock_thread(LOCK_CUSTOM1);
1055 domainSettings->lastgoodframe = startFrame+globalBakeFrame;
1058 globalBakeFrame = 0;
1059 globalBakeState = -1;
1061 BLI_unlock_thread(LOCK_CUSTOM1);
1066 // redraw the 3D for showing progress once in a while...
1067 if(lastRedraw!=globalBakeFrame) {
1070 scene->r.cfra = startFrame+globalBakeFrame;
1071 lastRedraw = globalBakeFrame;
1072 ED_update_for_newframe(C, 1);
1073 sa= G.curscreen->areabase.first;
1075 if(sa->spacetype == SPACE_VIEW3D) { scrarea_do_windraw(sa); }
1078 screen_swapbuffers();
1084 BLI_end_threads(&threads);
1085 } // El'Beem API init, thread creation
1086 // --------------------------------------------------------------------------------------------
1088 { // write config file to be run with command line simulator
1089 BKE_report(reports, RPT_WARNING, "Config file export not supported.");
1090 } // config file export done!
1092 // --------------------------------------------------------------------------------------------
1095 // go back to "current" blender time
1098 if(globalBakeState >= 0)
1101 domainSettings->lastgoodframe = startFrame+globalBakeFrame;
1104 scene->r.cfra = origFrame;
1105 ED_update_for_newframe(C, 1);
1108 char elbeemerr[256];
1110 // check if some error occurred
1111 if(globalBakeState==-2) {
1112 elbeemGetErrorString(elbeemerr);
1113 BKE_reportf(reports, RPT_ERROR, "Failed to initialize [Msg: %s]", elbeemerr);
1122 void fluidsimFreeBake(Object *ob)
1124 /* not implemented yet */
1127 #else /* DISABLE_ELBEEM */
1129 /* compile dummy functions for disabled fluid sim */
1131 FluidsimSettings *fluidsimSettingsNew(Object *srcob)
1136 void fluidsimSettingsFree(FluidsimSettings *fss)
1140 FluidsimSettings* fluidsimSettingsCopy(FluidsimSettings *fss)
1145 /* only compile dummy functions */
1146 int fluidsimBake(bContext *C, ReportList *reports, Object *ob)
1151 void fluidsimFreeBake(Object *ob)
1155 #endif /* DISABLE_ELBEEM */
1157 /***************************** Operators ******************************/
1159 static int fluid_bake_exec(bContext *C, wmOperator *op)
1161 Object *ob= CTX_data_active_object(C);
1163 // XXX TODO redraw, escape, non-blocking, ..
1164 if(!fluidsimBake(C, op->reports, ob))
1165 return OPERATOR_CANCELLED;
1167 return OPERATOR_FINISHED;
1170 void FLUID_OT_bake(wmOperatorType *ot)
1173 ot->name= "Fluid Simulation Bake";
1174 ot->idname= "FLUID_OT_bake";
1177 ot->exec= fluid_bake_exec;
1178 ot->poll= ED_operator_object_active;
1181 void ED_operatortypes_fluid(void)
1183 WM_operatortype_append(FLUID_OT_bake);