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 #include "MEM_guardedalloc.h"
42 #include "DNA_curve_types.h"
43 #include "DNA_object_types.h"
44 #include "DNA_object_fluidsim.h"
45 #include "DNA_key_types.h"
46 #include "DNA_mesh_types.h"
47 #include "DNA_meshdata_types.h"
48 #include "DNA_lattice_types.h"
49 #include "DNA_scene_types.h"
50 #include "DNA_camera_types.h"
51 #include "DNA_screen_types.h"
52 #include "DNA_space_types.h"
53 #include "DNA_userdef_types.h"
54 #include "DNA_ipo_types.h"
55 #include "DNA_key_types.h"
57 #include "BLI_blenlib.h"
58 #include "BLI_arithb.h"
59 #include "MTC_matrixops.h"
61 #include "BKE_customdata.h"
62 #include "BKE_displist.h"
63 #include "BKE_effect.h"
64 #include "BKE_global.h"
67 #include "BKE_scene.h"
68 #include "BKE_object.h"
69 #include "BKE_softbody.h"
70 #include "BKE_DerivedMesh.h"
72 #include "LBM_fluidsim.h"
73 // warning - double elbeem.h in intern/extern...
76 #include "BLI_editVert.h"
77 #include "BIF_editdeform.h"
79 #include "BIF_screen.h"
80 #include "BIF_space.h"
81 #include "BIF_cursors.h"
82 #include "BIF_interface.h"
83 #include "BSE_headerbuttons.h"
88 #include "SDL_thread.h"
89 #include "SDL_mutex.h"
92 #ifdef WIN32 /* Windos */
93 //#include "BLI_winstuff.h"
95 #define snprintf _snprintf
98 // SDL redefines main for SDL_main, not needed here...
101 #ifdef __APPLE__ /* MacOS X */
105 // from DerivedMesh.c
106 void initElbeemMesh(struct Object *ob, int *numVertices, float **vertices, int *numTriangles, int **triangles, int useGlobalCoords);
108 /* from header info.c */
109 extern int start_progress_bar(void);
110 extern void end_progress_bar(void);
111 extern int progress_bar(float done, char *busy_info);
113 double fluidsimViscosityPreset[6] = {
117 5.0e-5, /* some (thick) oil */
118 2.0e-3, /* ca. honey */
122 char* fluidsimViscosityPresetString[6] = {
123 "UNUSED", /* unused */
124 "UNUSED", /* manual */
125 " = 1.0 * 10^-6", /* water */
126 " = 5.0 * 10^-5", /* some (thick) oil */
127 " = 2.0 * 10^-3", /* ca. honey */
134 // similar to MeshDerivedMesh
135 struct Object *ob; // pointer to parent object
136 float *extverts, *nors; // face normals, colors?
137 Mesh *fsmesh; // mesh struct to display (either surface, or original one)
138 char meshFree; // free the mesh afterwards? (boolean)
139 } fluidsimDerivedMesh;
143 /* enable/disable overall compilation */
144 #ifndef DISABLE_ELBEEM
147 /* ********************** fluid sim settings struct functions ********************** */
149 /* allocates and initializes general main data */
151 FluidsimSettings *fluidsimSettingsNew(struct Object *srcob)
153 //char blendDir[FILE_MAXDIR], blendFile[FILE_MAXFILE];
154 FluidsimSettings *fss;
156 /* this call uses derivedMesh methods... */
157 if(srcob->type!=OB_MESH) return NULL;
159 fss= MEM_callocN( sizeof(FluidsimSettings), "fluidsimsettings memory");
162 fss->show_advancedoptions = 0;
164 fss->resolutionxyz = 50;
165 fss->previewresxyz = 25;
166 fss->realsize = 0.03;
167 fss->guiDisplayMode = 2; // preview
168 fss->renderDisplayMode = 3; // render
170 fss->viscosityMode = 2; // default to water
171 fss->viscosityValue = 1.0;
172 fss->viscosityExponent = 6;
176 fss->animStart = 0.0;
178 fss->gstar = 0.005; // used as normgstar
180 // maxRefine is set according to resolutionxyz during bake
182 // fluid/inflow settings
187 /* elubie: changed this to default to the same dir as the render output
188 to prevent saving to C:\ on Windows */
189 BLI_strncpy(fss->surfdataPath, btempdir, FILE_MAX);
190 fss->orgMesh = (Mesh *)srcob->data;
191 fss->meshSurface = NULL;
193 fss->meshSurfNormals = NULL;
195 // first init of bounding box
196 fss->bbStart[0] = 0.0;
197 fss->bbStart[1] = 0.0;
198 fss->bbStart[2] = 0.0;
199 fss->bbSize[0] = 1.0;
200 fss->bbSize[1] = 1.0;
201 fss->bbSize[2] = 1.0;
202 fluidsimGetAxisAlignedBB(srcob->data, srcob->obmat, fss->bbStart, fss->bbSize, &fss->meshBB);
204 // todo - reuse default init from elbeem!
206 fss->domainNovecgen = 0;
207 fss->volumeInitType = 1; // volume
208 fss->partSlipValue = 0.0;
210 fss->generateTracers = 0;
211 fss->generateParticles = 0.0;
212 fss->surfaceSmoothing = 1.0;
213 fss->surfaceSubdivs = 1.0;
214 fss->particleInfSize = 0.0;
215 fss->particleInfAlpha = 0.0;
217 // init fluid control settings
218 fss->attractforceStrength = 0.2;
219 fss->attractforceRadius = 0.75;
220 fss->velocityforceStrength = 0.2;
221 fss->velocityforceRadius = 0.75;
222 fss->cpsTimeStart = fss->animStart;
223 fss->cpsTimeEnd = fss->animEnd;
228 /* duplicate struct, analogous to free */
229 static Mesh *fluidsimCopyMesh(Mesh *me)
231 Mesh *dup = MEM_dupallocN(me);
233 CustomData_copy(&me->vdata, &dup->vdata, CD_MASK_MESH, CD_DUPLICATE, me->totvert);
234 CustomData_copy(&me->edata, &dup->edata, CD_MASK_MESH, CD_DUPLICATE, me->totedge);
235 CustomData_copy(&me->fdata, &dup->fdata, CD_MASK_MESH, CD_DUPLICATE, me->totface);
240 FluidsimSettings* fluidsimSettingsCopy(FluidsimSettings *fss)
242 FluidsimSettings *dupfss;
244 if(!fss) return NULL;
245 dupfss = MEM_dupallocN(fss);
248 dupfss->meshSurface = fluidsimCopyMesh(fss->meshSurface);
250 dupfss->meshBB = fluidsimCopyMesh(fss->meshBB);
252 if(fss->meshSurfNormals) dupfss->meshSurfNormals = MEM_dupallocN(fss->meshSurfNormals);
258 static void fluidsimFreeMesh(Mesh *me)
260 CustomData_free(&me->vdata, me->totvert);
261 CustomData_free(&me->edata, me->totedge);
262 CustomData_free(&me->fdata, me->totface);
267 void fluidsimSettingsFree(FluidsimSettings *fss)
269 if(fss->meshSurface) {
270 fluidsimFreeMesh(fss->meshSurface);
271 fss->meshSurface = NULL;
274 fluidsimFreeMesh(fss->meshBB);
278 if(fss->meshSurfNormals){ MEM_freeN(fss->meshSurfNormals); fss->meshSurfNormals=NULL; }
284 /* helper function */
285 void fluidsimGetGeometryObjFilename(struct Object *ob, char *dst) { //, char *srcname) {
286 //snprintf(dst,FILE_MAXFILE, "%s_cfgdata_%s.bobj.gz", srcname, ob->id.name);
287 snprintf(dst,FILE_MAXFILE, "fluidcfgdata_%s.bobj.gz", ob->id.name);
293 /* ******************************************************************************** */
294 /* ********************** fluid sim channel helper functions ********************** */
295 /* ******************************************************************************** */
297 // no. of entries for the two channel sizes
298 #define CHANNEL_FLOAT 1
299 #define CHANNEL_VEC 3
301 #define FS_FREE_ONECHANNEL(c,str) { \
302 if(c){ MEM_freeN(c); c=NULL; } \
303 } // end ONE CHANN, debug: fprintf(stderr,"freeing " str " \n");
305 #define FS_FREE_CHANNELS { \
306 FS_FREE_ONECHANNEL(timeAtIndex,"timeAtIndex");\
307 FS_FREE_ONECHANNEL(timeAtFrame,"timeAtFrame");\
308 FS_FREE_ONECHANNEL(channelDomainTime,"channelDomainTime"); \
309 FS_FREE_ONECHANNEL(channelDomainGravity,"channelDomainGravity");\
310 FS_FREE_ONECHANNEL(channelDomainViscosity,"channelDomainViscosity");\
311 for(i=0;i<256;i++) { \
312 FS_FREE_ONECHANNEL(channelObjMove[i][0],"channelObjMove0"); \
313 FS_FREE_ONECHANNEL(channelObjMove[i][1],"channelObjMove1"); \
314 FS_FREE_ONECHANNEL(channelObjMove[i][2],"channelObjMove2"); \
315 FS_FREE_ONECHANNEL(channelObjInivel[i],"channelObjInivel"); \
316 FS_FREE_ONECHANNEL(channelObjActive[i],"channelObjActive"); \
318 } // end FS FREE CHANNELS
321 // simplify channels before printing
322 // for API this is done anyway upon init
324 static void fluidsimPrintChannel(FILE *file, float *channel, int paramsize, char *str, int entries)
327 int channelSize = paramsize;
330 elbeemSimplifyChannelVec3( channel, &channelSize);
331 } else if(entries==1) {
332 elbeemSimplifyChannelFloat( channel, &channelSize);
334 // invalid, cant happen?
337 fprintf(file, " CHANNEL %s = \n", str);
338 for(i=0; i<channelSize;i++) {
340 for(j=0;j<=entries;j++) { // also print time value
341 fprintf(file," %f ", channel[i*(entries+1)+j] );
342 if(j==entries-1){ fprintf(file," "); }
347 fprintf(file, " ; \n" );
351 static void fluidsimInitChannel(float **setchannel, int size, float *time,
352 int *icuIds, float *defaults, Ipo* ipo, int entries) {
356 float *channel = NULL;
357 float aniFrlen = G.scene->r.framelen;
358 int current_frame = G.scene->r.cfra;
359 if((entries<1) || (entries>3)) {
360 printf("fluidsimInitChannel::Error - invalid no. of entries: %d\n",entries);
364 cstr = "fluidsiminit_channelfloat";
365 if(entries>1) cstr = "fluidsiminit_channelvec";
366 channel = MEM_callocN( size* (entries+1)* sizeof(float), cstr );
369 for(j=0; j<entries; j++) icus[j] = find_ipocurve(ipo, icuIds[j] );
371 for(j=0; j<entries; j++) icus[j] = NULL;
374 for(j=0; j<entries; j++) {
376 for(i=1; i<=size; i++) {
377 /* Bugfix to make python drivers working
378 // which uses Blender.get("curframe")
380 G.scene->r.cfra = floor(aniFrlen*((float)i));
382 calc_icu(icus[j], aniFrlen*((float)i) );
383 channel[(i-1)*(entries+1) + j] = icus[j]->curval;
386 for(i=1; i<=size; i++) { channel[(i-1)*(entries+1) + j] = defaults[j]; }
388 //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
391 for(i=1; i<=size; i++) {
392 channel[(i-1)*(entries+1) + entries] = time[i];
394 G.scene->r.cfra = current_frame;
395 *setchannel = channel;
398 static void fluidsimInitMeshChannel(float **setchannel, int size, Object *obm, int vertices, float *time) {
399 float *channel = NULL;
400 int mallsize = size* (3*vertices+1);
402 int numVerts=0, numTris=0;
403 int setsize = 3*vertices+1;
405 channel = MEM_callocN( mallsize* sizeof(float), "fluidsim_meshchannel" );
407 //fprintf(stderr,"\n\nfluidsimInitMeshChannel size%d verts%d mallsize%d \n\n\n",size,vertices,mallsize);
408 for(frame=1; frame<=size; frame++) {
411 G.scene->r.cfra = frame;
412 scene_update_for_newframe(G.scene, G.scene->lay);
414 initElbeemMesh(obm, &numVerts, &verts, &numTris, &tris, 1);
415 //fprintf(stderr,"\nfluidsimInitMeshChannel frame%d verts%d/%d \n\n",frame,vertices,numVerts);
416 for(i=0; i<3*vertices;i++) {
417 channel[(frame-1)*setsize + i] = verts[i];
418 //fprintf(stdout," frame%d vert%d=%f \n",frame,i,verts[i]);
419 //if(i%3==2) fprintf(stdout,"\n");
421 channel[(frame-1)*setsize + setsize-1] = time[frame];
426 *setchannel = channel;
430 /* ******************************************************************************** */
431 /* ********************** simulation thread ************************* */
432 /* ******************************************************************************** */
434 SDL_mutex *globalBakeLock=NULL;
435 int globalBakeState = 0; // 0 everything ok, -1 abort simulation, -2 sim error, 1 sim done
436 int globalBakeFrame = 0;
438 // run simulation in seperate thread
439 static int fluidsimSimulateThread(void *unused) { // *ptr) {
440 //char* fnameCfgPath = (char*)(ptr);
443 ret = elbeemSimulate();
444 SDL_mutexP(globalBakeLock);
445 if(globalBakeState==0) {
447 // if no error, set to normal exit
450 // simulation failed, display error
451 globalBakeState = -2;
454 SDL_mutexV(globalBakeLock);
459 int runSimulationCallback(void *data, int status, int frame) {
460 //elbeemSimulationSettings *settings = (elbeemSimulationSettings*)data;
461 //printf("elbeem blender cb s%d, f%d, domainid:%d \n", status,frame, settings->domainId ); // DEBUG
463 if(!globalBakeLock) return FLUIDSIM_CBRET_ABORT;
464 if(status==FLUIDSIM_CBSTATUS_NEWFRAME) {
465 SDL_mutexP(globalBakeLock);
466 globalBakeFrame = frame-1;
467 SDL_mutexV(globalBakeLock);
470 //if((frameCounter==3) && (!frameStop)) { frameStop=1; return 1; }
472 SDL_mutexP(globalBakeLock);
473 if(globalBakeState!=0) {
474 return FLUIDSIM_CBRET_ABORT;
476 SDL_mutexV(globalBakeLock);
477 return FLUIDSIM_CBRET_CONTINUE;
481 /* ******************************************************************************** */
482 /* ********************** write fluidsim config to file ************************* */
483 /* ******************************************************************************** */
485 void fluidsimBake(struct Object *ob)
489 struct Object *fsDomain = NULL;
490 FluidsimSettings *domainSettings;
491 struct Object *obit = NULL; /* object iterator */
493 int origFrame = G.scene->r.cfra;
494 char debugStrBuffer[256];
497 int simAborted = 0; // was the simulation aborted by user?
498 int doExportOnly = 0;
499 char *exportEnvStr = "BLENDER_ELBEEMEXPORTONLY";
500 const char *strEnvName = "BLENDER_ELBEEMDEBUG"; // from blendercall.cpp
501 //char *channelNames[3] = { "translation","rotation","scale" };
503 char *suffixConfig = "fluidsim.cfg";
504 char *suffixSurface = "fluidsurface";
505 char newSurfdataPath[FILE_MAXDIR+FILE_MAXFILE]; // modified output settings
506 char targetDir[FILE_MAXDIR+FILE_MAXFILE]; // store & modify output settings
507 char targetFile[FILE_MAXDIR+FILE_MAXFILE]; // temp. store filename from targetDir for access
508 int outStringsChanged = 0; // modified? copy back before baking
509 int haveSomeFluid = 0; // check if any fluid objects are set
511 // config vars, inited before either export or run...
512 double calcViscosity = 0.0;
517 float *bbStart = NULL;
518 float *bbSize = NULL;
519 float domainMat[4][4];
520 float invDomMat[4][4];
522 int allchannelSize; // fixed by no. of frames
523 int startFrame = 1; // dont use G.scene->r.sfra here, always start with frame 1
524 // easy frame -> sim time calc
525 float *timeAtFrame=NULL, *timeAtIndex=NULL;
527 float *channelDomainTime = NULL;
528 float *channelDomainViscosity = NULL;
529 float *channelDomainGravity = NULL;
530 // objects (currently max. 256 objs)
531 float *channelObjMove[256][3]; // object movments , 0=trans, 1=rot, 2=scale
532 float *channelObjInivel[256]; // initial velocities
533 float *channelObjActive[256]; // obj active channel
535 if(getenv(strEnvName)) {
536 int dlevel = atoi(getenv(strEnvName));
537 elbeemSetDebugLevel(dlevel);
538 snprintf(debugStrBuffer,256,"fluidsimBake::msg: Debug messages activated due to envvar '%s'\n",strEnvName);
539 elbeemDebugOut(debugStrBuffer);
541 if(getenv(exportEnvStr)) {
542 doExportOnly = atoi(getenv(exportEnvStr));
543 snprintf(debugStrBuffer,256,"fluidsimBake::msg: Exporting mode set to '%d' due to envvar '%s'\n",doExportOnly, exportEnvStr);
544 elbeemDebugOut(debugStrBuffer);
547 // make sure it corresponds to startFrame setting
548 // old: noFrames = G.scene->r.efra - G.scene->r.sfra +1;
549 noFrames = G.scene->r.efra - 0;
551 pupmenu("Fluidsim Bake Error%t|No frames to export - check your animation range settings. Aborted%x0");
555 /* no object pointer, find in selected ones.. */
557 for(base=G.scene->base.first; base; base= base->next) {
558 if ( ((base)->flag & SELECT)
559 // ignore layer setting for now? && ((base)->lay & G.vd->lay)
561 if((!ob)&&(base->object->fluidsimFlag & OB_FLUIDSIM_ENABLE)&&(base->object->type==OB_MESH)) {
562 if(base->object->fluidsimSettings->type == OB_FLUIDSIM_DOMAIN) {
573 for(base=G.scene->base.first; base; base= base->next) {
575 //{ snprintf(debugStrBuffer,256,"DEBUG object name=%s, type=%d ...\n", obit->id.name, obit->type); elbeemDebugOut(debugStrBuffer); } // DEBUG
576 if( (obit->fluidsimFlag & OB_FLUIDSIM_ENABLE) &&
577 (obit->type==OB_MESH) &&
578 (obit->fluidsimSettings->type != OB_FLUIDSIM_DOMAIN) && // if has to match 3 places! // CHECKMATCH
579 (obit->fluidsimSettings->type != OB_FLUIDSIM_PARTICLE) ) {
584 if (channelObjCount>=255) {
585 pupmenu("Fluidsim Bake Error%t|Cannot bake with more then 256 objects");
589 /* check if there's another domain... */
590 for(base=G.scene->base.first; base; base= base->next) {
592 if((obit->fluidsimFlag & OB_FLUIDSIM_ENABLE)&&(obit->type==OB_MESH)) {
593 if(obit->fluidsimSettings->type == OB_FLUIDSIM_DOMAIN) {
595 //snprintf(debugStrBuffer,256,"fluidsimBake::warning - More than one domain!\n"); elbeemDebugOut(debugStrBuffer);
596 pupmenu("Fluidsim Bake Error%t|There should be only one domain object! Aborted%x0");
602 /* these both have to be valid, otherwise we wouldnt be here */
603 /* dont use ob here after...*/
605 domainSettings = ob->fluidsimSettings;
607 /* rough check of settings... */
608 if(domainSettings->previewresxyz > domainSettings->resolutionxyz) {
609 snprintf(debugStrBuffer,256,"fluidsimBake::warning - Preview (%d) >= Resolution (%d)... setting equal.\n", domainSettings->previewresxyz , domainSettings->resolutionxyz);
610 elbeemDebugOut(debugStrBuffer);
611 domainSettings->previewresxyz = domainSettings->resolutionxyz;
613 // set adaptive coarsening according to resolutionxyz
614 // this should do as an approximation, with in/outflow
615 // doing this more accurate would be overkill
616 // perhaps add manual setting?
617 if(domainSettings->maxRefine <0) {
618 if(domainSettings->resolutionxyz>128) {
621 if(domainSettings->resolutionxyz>64) {
627 gridlevels = domainSettings->maxRefine;
629 snprintf(debugStrBuffer,256,"fluidsimBake::msg: Baking %s, refine: %d\n", fsDomain->id.name , gridlevels );
630 elbeemDebugOut(debugStrBuffer);
632 // check if theres any fluid
633 // abort baking if not...
634 for(base=G.scene->base.first; base; base= base->next) {
636 if( (obit->fluidsimFlag & OB_FLUIDSIM_ENABLE) &&
637 (obit->type==OB_MESH) && (
638 (obit->fluidsimSettings->type == OB_FLUIDSIM_FLUID) ||
639 (obit->fluidsimSettings->type == OB_FLUIDSIM_INFLOW) )
645 pupmenu("Fluidsim Bake Error%t|No fluid objects in scene... Aborted%x0");
650 strncpy(targetDir, domainSettings->surfdataPath, FILE_MAXDIR);
651 strncpy(newSurfdataPath, domainSettings->surfdataPath, FILE_MAXDIR);
652 BLI_convertstringcode(targetDir, G.sce); // fixed #frame-no
654 strcpy(targetFile, targetDir);
655 strcat(targetFile, suffixConfig);
656 if(!doExportOnly) { strcat(targetFile,".tmp"); } // dont overwrite/delete original file
657 // make sure all directories exist
658 // as the bobjs use the same dir, this only needs to be checked
659 // for the cfg output
660 BLI_make_existing_file(targetFile);
662 // check selected directory
663 // simply try to open cfg file for writing to test validity of settings
664 fileCfg = fopen(targetFile, "w");
666 dirExist = 1; fclose(fileCfg);
667 // remove cfg dummy from directory test
668 if(!doExportOnly) { BLI_delete(targetFile, 0,0); }
671 if((strlen(targetDir)<1) || (!dirExist)) {
672 char blendDir[FILE_MAXDIR+FILE_MAXFILE], blendFile[FILE_MAXDIR+FILE_MAXFILE];
673 // invalid dir, reset to current/previous
674 strcpy(blendDir, G.sce);
675 BLI_splitdirstring(blendDir, blendFile);
676 if(strlen(blendFile)>6){
677 int len = strlen(blendFile);
678 if( (blendFile[len-6]=='.')&& (blendFile[len-5]=='b')&& (blendFile[len-4]=='l')&&
679 (blendFile[len-3]=='e')&& (blendFile[len-2]=='n')&& (blendFile[len-1]=='d') ){
680 blendFile[len-6] = '\0';
683 // todo... strip .blend ?
684 snprintf(newSurfdataPath,FILE_MAXFILE+FILE_MAXDIR,"//fluidsimdata/%s_%s_", blendFile, fsDomain->id.name);
686 snprintf(debugStrBuffer,256,"fluidsimBake::error - warning resetting output dir to '%s'\n", newSurfdataPath);
687 elbeemDebugOut(debugStrBuffer);
691 // check if modified output dir is ok
692 if(outStringsChanged) {
693 char dispmsg[FILE_MAXDIR+FILE_MAXFILE+256];
695 strcpy(dispmsg,"Output settings set to: '");
696 strcat(dispmsg, newSurfdataPath);
697 strcat(dispmsg, "'%t|Continue with changed settings%x1|Discard and abort%x0");
699 // ask user if thats what he/she wants...
700 selection = pupmenu(dispmsg);
701 if(selection<1) return; // 0 from menu, or -1 aborted
702 strcpy(targetDir, newSurfdataPath);
703 strncpy(domainSettings->surfdataPath, newSurfdataPath, FILE_MAXDIR);
704 BLI_convertstringcode(targetDir, G.sce); // fixed #frame-no
707 // --------------------------------------------------------------------------------------------
708 // dump data for start frame
709 // CHECK more reasonable to number frames according to blender?
710 // dump data for frame 0
711 G.scene->r.cfra = startFrame;
712 scene_update_for_newframe(G.scene, G.scene->lay);
714 // init common export vars for both file export and run
715 for(i=0; i<256; i++) {
716 channelObjMove[i][0] = channelObjMove[i][1] = channelObjMove[i][2] = NULL;
717 channelObjInivel[i] = NULL;
718 channelObjActive[i] = NULL;
720 allchannelSize = G.scene->r.efra; // always use till last frame
721 aniFrameTime = (domainSettings->animEnd - domainSettings->animStart)/(double)noFrames;
722 // blender specific - scale according to map old/new settings in anim panel:
723 aniFrlen = G.scene->r.framelen;
724 if(domainSettings->viscosityMode==1) {
725 /* manual mode, visc=value/(10^-vexp) */
726 calcViscosity = (1.0/pow(10.0,domainSettings->viscosityExponent)) * domainSettings->viscosityValue;
728 calcViscosity = fluidsimViscosityPreset[ domainSettings->viscosityMode ];
731 bbStart = fsDomain->fluidsimSettings->bbStart;
732 bbSize = fsDomain->fluidsimSettings->bbSize;
733 fluidsimGetAxisAlignedBB(fsDomain->data, fsDomain->obmat, bbStart, bbSize, &domainSettings->meshBB);
736 { int timeIcu[1] = { FLUIDSIM_TIME };
737 float timeDef[1] = { 1. };
738 int gravIcu[3] = { FLUIDSIM_GRAV_X, FLUIDSIM_GRAV_Y, FLUIDSIM_GRAV_Z };
740 int viscIcu[1] = { FLUIDSIM_VISC };
741 float viscDef[1] = { 1. };
743 gravDef[0] = domainSettings->gravx;
744 gravDef[1] = domainSettings->gravy;
745 gravDef[2] = domainSettings->gravz;
747 // time channel is a bit special, init by hand...
748 timeAtIndex = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatindex");
749 for(i=0; i<=G.scene->r.efra; i++) {
750 timeAtIndex[i] = (float)(i-startFrame);
752 fluidsimInitChannel( &channelDomainTime, allchannelSize, timeAtIndex, timeIcu,timeDef, domainSettings->ipo, CHANNEL_FLOAT ); // NDEB
753 // time channel is a multiplicator for aniFrameTime
754 if(channelDomainTime) {
755 for(i=0; i<allchannelSize; i++) {
756 channelDomainTime[i*2+0] = aniFrameTime * channelDomainTime[i*2+0];
757 if(channelDomainTime[i*2+0]<0.) channelDomainTime[i*2+0] = 0.;
760 timeAtFrame = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatframe");
761 timeAtFrame[0] = timeAtFrame[1] = domainSettings->animStart; // start at index 1
762 if(channelDomainTime) {
763 for(i=2; i<=allchannelSize; i++) {
764 timeAtFrame[i] = timeAtFrame[i-1]+channelDomainTime[(i-1)*2+0];
767 for(i=2; i<=allchannelSize; i++) { timeAtFrame[i] = timeAtFrame[i-1]+aniFrameTime; }
770 fluidsimInitChannel( &channelDomainViscosity, allchannelSize, timeAtFrame, viscIcu,viscDef, domainSettings->ipo, CHANNEL_FLOAT ); // NDEB
771 if(channelDomainViscosity) {
772 for(i=0; i<allchannelSize; i++) { channelDomainViscosity[i*2+0] = calcViscosity * channelDomainViscosity[i*2+0]; }
774 fluidsimInitChannel( &channelDomainGravity, allchannelSize, timeAtFrame, gravIcu,gravDef, domainSettings->ipo, CHANNEL_VEC );
775 } // domain channel init
777 // init obj movement channels
779 for(base=G.scene->base.first; base; base= base->next) {
781 //{ snprintf(debugStrBuffer,256,"DEBUG object name=%s, type=%d ...\n", obit->id.name, obit->type); elbeemDebugOut(debugStrBuffer); } // DEBUG
782 if( (obit->fluidsimFlag & OB_FLUIDSIM_ENABLE) &&
783 (obit->type==OB_MESH) &&
784 (obit->fluidsimSettings->type != OB_FLUIDSIM_DOMAIN) && // if has to match 3 places! // CHECKMATCH
785 (obit->fluidsimSettings->type != OB_FLUIDSIM_PARTICLE) ) {
787 // cant use fluidsimInitChannel for obj channels right now, due
788 // to the special DXXX channels, and the rotation specialities
789 IpoCurve *icuex[3][3];
790 //IpoCurve *par_icuex[3][3];
792 {OB_LOC_X, OB_LOC_Y, OB_LOC_Z},
793 {OB_ROT_X, OB_ROT_Y, OB_ROT_Z},
794 {OB_SIZE_X, OB_SIZE_Y, OB_SIZE_Z}
797 IpoCurve *icudex[3][3];
798 //IpoCurve *par_icudex[3][3];
799 int icudIds[3][3] = {
800 {OB_DLOC_X, OB_DLOC_Y, OB_DLOC_Z},
801 {OB_DROT_X, OB_DROT_Y, OB_DROT_Z},
802 {OB_DSIZE_X, OB_DSIZE_Y, OB_DSIZE_Z}
805 float vals[3] = {0.0,0.0,0.0};
806 int o = channelObjCount;
807 int inivelIcu[3] = { FLUIDSIM_VEL_X, FLUIDSIM_VEL_Y, FLUIDSIM_VEL_Z };
809 int activeIcu[1] = { FLUIDSIM_ACTIVE };
810 float activeDefs[1] = { 1 }; // default to on
812 inivelDefs[0] = obit->fluidsimSettings->iniVelx;
813 inivelDefs[1] = obit->fluidsimSettings->iniVely;
814 inivelDefs[2] = obit->fluidsimSettings->iniVelz;
816 // check & init loc,rot,size
819 icuex[j][k] = find_ipocurve(obit->ipo, icuIds[j][k] );
820 icudex[j][k] = find_ipocurve(obit->ipo, icudIds[j][k] );
822 //par_icuex[j][k] = find_ipocurve(obit->parent->ipo, icuIds[j][k] );
823 //par_icudex[j][k] = find_ipocurve(obit->parent->ipo, icudIds[j][k] );
829 channelObjMove[o][j] = MEM_callocN( allchannelSize*4*sizeof(float), "fluidsiminit_objmovchannel");
830 for(i=1; i<=allchannelSize; i++) {
834 // IPO exists, use it ...
835 calc_icu(icuex[j][k], aniFrlen*((float)i) );
836 vals[k] = icuex[j][k]->curval;
838 // add parent transform, multiply scaling, add trafo&rot
839 //calc_icu(par_icuex[j][k], aniFrlen*((float)i) );
840 //if(j==2) { vals[k] *= par_icuex[j][k]->curval; }
841 //else { vals[k] += par_icuex[j][k]->curval; }
844 // use defaults from static values
847 setval = obit->loc[k];
848 if(obit->parent){ setval += obit->parent->loc[k]; }
850 setval = ( 180.0*obit->rot[k] )/( 10.0*M_PI );
851 if(obit->parent){ setval = ( 180.0*(obit->rot[k]+obit->parent->rot[k]) )/( 10.0*M_PI ); }
853 setval = obit->size[k];
854 if(obit->parent){ setval *= obit->parent->size[k]; }
859 calc_icu(icudex[j][k], aniFrlen*((float)i) );
860 //vals[k] += icudex[j][k]->curval;
861 // add transform, multiply scaling, add trafo&rot
862 if(j==2) { vals[k] *= icudex[j][k]->curval; }
863 else { vals[k] += icudex[j][k]->curval; }
865 // add parent transform, multiply scaling, add trafo&rot
866 //calc_icu(par_icuex[j][k], aniFrlen*((float)i) );
867 //if(j==2) { vals[k] *= par_icudex[j][k]->curval; }
868 //else { vals[k] += par_icudex[j][k]->curval; }
875 if(j==1) { // rot is downscaled by 10 for ipo !?
876 set = 360.0 - (10.0*set);
878 channelObjMove[o][j][(i-1)*4 + k] = set;
880 channelObjMove[o][j][(i-1)*4 + 3] = timeAtFrame[i];
884 fluidsimInitChannel( &channelObjInivel[o], allchannelSize, timeAtFrame, inivelIcu,inivelDefs, obit->fluidsimSettings->ipo, CHANNEL_VEC );
885 fluidsimInitChannel( &channelObjActive[o], allchannelSize, timeAtFrame, activeIcu,activeDefs, obit->fluidsimSettings->ipo, CHANNEL_FLOAT );
893 MTC_Mat4CpyMat4(domainMat, fsDomain->obmat);
894 if(!Mat4Invert(invDomMat, domainMat)) {
895 snprintf(debugStrBuffer,256,"fluidsimBake::error - Invalid obj matrix?\n");
896 elbeemDebugOut(debugStrBuffer);
897 // FIXME add fatal msg
903 // --------------------------------------------------------------------------------------------
904 // start writing / exporting
905 strcpy(targetFile, targetDir);
906 strcat(targetFile, suffixConfig);
907 if(!doExportOnly) { strcat(targetFile,".tmp"); } // dont overwrite/delete original file
908 // make sure these directories exist as well
909 if(outStringsChanged) {
910 BLI_make_existing_file(targetFile);
914 SDL_Thread *simthr = NULL;
916 // perform simulation with El'Beem api and SDL threads
917 elbeemSimulationSettings fsset;
918 elbeemResetSettings(&fsset);
921 // setup global settings
922 for(i=0 ; i<3; i++) fsset.geoStart[i] = bbStart[i];
923 for(i=0 ; i<3; i++) fsset.geoSize[i] = bbSize[i];
924 // simulate with 50^3
925 fsset.resolutionxyz = (int)domainSettings->resolutionxyz;
926 fsset.previewresxyz = (int)domainSettings->previewresxyz;
928 fsset.realsize = domainSettings->realsize;
929 fsset.viscosity = calcViscosity;
931 fsset.gravity[0] = domainSettings->gravx;
932 fsset.gravity[1] = domainSettings->gravy;
933 fsset.gravity[2] = domainSettings->gravz;
934 // simulate 5 frames, each 0.03 seconds, output to ./apitest_XXX.bobj.gz
935 fsset.animStart = domainSettings->animStart;
936 fsset.aniFrameTime = aniFrameTime;
937 fsset.noOfFrames = noFrames - 1; // is otherwise subtracted in parser
938 strcpy(targetFile, targetDir);
939 strcat(targetFile, suffixSurface);
940 // defaults for compressibility and adaptive grids
941 fsset.gstar = domainSettings->gstar;
942 fsset.maxRefine = domainSettings->maxRefine; // check <-> gridlevels
943 fsset.generateParticles = domainSettings->generateParticles;
944 fsset.numTracerParticles = domainSettings->generateTracers;
945 fsset.surfaceSmoothing = domainSettings->surfaceSmoothing;
946 fsset.surfaceSubdivs = domainSettings->surfaceSubdivs;
947 fsset.farFieldSize = domainSettings->farFieldSize;
948 strcpy( fsset.outputPath, targetFile);
951 fsset.channelSizeFrameTime =
952 fsset.channelSizeViscosity =
953 fsset.channelSizeGravity = allchannelSize;
954 fsset.channelFrameTime = channelDomainTime;
955 fsset.channelViscosity = channelDomainViscosity;
956 fsset.channelGravity = channelDomainGravity;
958 fsset.runsimCallback = &runSimulationCallback;
959 fsset.runsimUserData = &fsset;
961 if( (domainSettings->typeFlags&OB_FSBND_NOSLIP)) fsset.domainobsType = FLUIDSIM_OBSTACLE_NOSLIP;
962 else if((domainSettings->typeFlags&OB_FSBND_PARTSLIP)) fsset.domainobsType = FLUIDSIM_OBSTACLE_PARTSLIP;
963 else if((domainSettings->typeFlags&OB_FSBND_FREESLIP)) fsset.domainobsType = FLUIDSIM_OBSTACLE_FREESLIP;
964 fsset.domainobsPartslip = domainSettings->partSlipValue;
965 fsset.generateVertexVectors = (domainSettings->domainNovecgen==0);
967 // init blender trafo matrix
968 // fprintf(stderr,"elbeemInit - mpTrafo:\n");
972 fsset.surfaceTrafo[i*4+j] = invDomMat[j][i];
973 // fprintf(stderr,"elbeemInit - mpTrafo %d %d = %f (%d) \n", i,j, fsset.surfaceTrafo[i*4+j] , (i*4+j) );
977 // init solver with settings
979 elbeemAddDomain(&fsset);
983 for(base=G.scene->base.first; base; base= base->next) {
985 //{ snprintf(debugStrBuffer,256,"DEBUG object name=%s, type=%d ...\n", obit->id.name, obit->type); elbeemDebugOut(debugStrBuffer); } // DEBUG
986 if( (obit->fluidsimFlag & OB_FLUIDSIM_ENABLE) && // if has to match 3 places! // CHECKMATCH
987 (obit->type==OB_MESH) &&
988 (obit->fluidsimSettings->type != OB_FLUIDSIM_DOMAIN) &&
989 (obit->fluidsimSettings->type != OB_FLUIDSIM_PARTICLE)
993 int numVerts=0, numTris=0;
994 int o = channelObjCount;
995 int deform = (obit->fluidsimSettings->domainNovecgen); // misused value
996 // todo - use blenderInitElbeemMesh
998 elbeemResetMesh( &fsmesh );
999 fsmesh.type = obit->fluidsimSettings->type;
1000 // get name of object for debugging solver
1001 fsmesh.name = obit->id.name;
1003 initElbeemMesh(obit, &numVerts, &verts, &numTris, &tris, 0);
1004 fsmesh.numVertices = numVerts;
1005 fsmesh.numTriangles = numTris;
1006 fsmesh.vertices = verts;
1007 fsmesh.triangles = tris;
1009 fsmesh.channelSizeTranslation =
1010 fsmesh.channelSizeRotation =
1011 fsmesh.channelSizeScale =
1012 fsmesh.channelSizeInitialVel =
1013 fsmesh.channelSizeActive = allchannelSize;
1015 fsmesh.channelTranslation = channelObjMove[o][0];
1016 fsmesh.channelRotation = channelObjMove[o][1];
1017 fsmesh.channelScale = channelObjMove[o][2];
1018 fsmesh.channelActive = channelObjActive[o];
1019 if( (fsmesh.type == OB_FLUIDSIM_FLUID) ||
1020 (fsmesh.type == OB_FLUIDSIM_INFLOW)) {
1021 fsmesh.channelInitialVel = channelObjInivel[o];
1022 fsmesh.localInivelCoords = ((obit->fluidsimSettings->typeFlags&OB_FSINFLOW_LOCALCOORD)?1:0);
1025 if( (obit->fluidsimSettings->typeFlags&OB_FSBND_NOSLIP)) fsmesh.obstacleType = FLUIDSIM_OBSTACLE_NOSLIP;
1026 else if((obit->fluidsimSettings->typeFlags&OB_FSBND_PARTSLIP)) fsmesh.obstacleType = FLUIDSIM_OBSTACLE_PARTSLIP;
1027 else if((obit->fluidsimSettings->typeFlags&OB_FSBND_FREESLIP)) fsmesh.obstacleType = FLUIDSIM_OBSTACLE_FREESLIP;
1028 fsmesh.obstaclePartslip = obit->fluidsimSettings->partSlipValue;
1029 fsmesh.volumeInitType = obit->fluidsimSettings->volumeInitType;
1030 fsmesh.obstacleImpactFactor = obit->fluidsimSettings->surfaceSmoothing; // misused value
1032 if(fsmesh.type == OB_FLUIDSIM_CONTROL)
1034 // control fluids will get exported as whole
1037 fsmesh.cpsTimeStart = obit->fluidsimSettings->cpsTimeStart;
1038 fsmesh.cpsTimeEnd = obit->fluidsimSettings->cpsTimeEnd;
1040 fsmesh.attractforceStrength = obit->fluidsimSettings->attractforceStrength;
1041 fsmesh.attractforceRadius = obit->fluidsimSettings->attractforceRadius;
1042 fsmesh.velocityforceStrength = obit->fluidsimSettings->velocityforceStrength;
1043 fsmesh.velocityforceRadius = obit->fluidsimSettings->velocityforceRadius;
1048 fsmesh.channelSizeVertices = allchannelSize;
1049 fluidsimInitMeshChannel( &fsmesh.channelVertices, allchannelSize, obit, numVerts, timeAtFrame);
1050 G.scene->r.cfra = startFrame;
1051 scene_update_for_newframe(G.scene, G.scene->lay);
1053 fsmesh.channelTranslation =
1054 fsmesh.channelRotation =
1055 fsmesh.channelScale = NULL;
1058 elbeemAddMesh(&fsmesh);
1060 if(verts) MEM_freeN(verts);
1061 if(tris) MEM_freeN(tris);
1062 if(fsmesh.channelVertices) MEM_freeN(fsmesh.channelVertices);
1066 //domainSettings->type = OB_FLUIDSIM_DOMAIN; // enable for bake display again
1067 //fsDomain->fluidsimFlag = OB_FLUIDSIM_ENABLE; // disable during bake
1069 globalBakeLock = SDL_CreateMutex();
1070 // set to neutral, -1 means user abort, -2 means init error
1071 globalBakeState = 0;
1072 globalBakeFrame = 0;
1073 simthr = SDL_CreateThread(fluidsimSimulateThread, targetFile);
1076 snprintf(debugStrBuffer,256,"fluidsimBake::error: Unable to create thread... running without one.\n");
1077 elbeemDebugOut(debugStrBuffer);
1082 unsigned short event=0;
1084 float noFramesf = (float)noFrames;
1085 float percentdone = 0.0;
1086 int lastRedraw = -1;
1088 start_progress_bar();
1095 // lukep we add progress bar as an interim mesure
1096 percentdone = globalBakeFrame / noFramesf;
1097 sprintf(busy_mess, "baking fluids %d / %d |||", globalBakeFrame, (int) noFramesf);
1098 progress_bar(percentdone, busy_mess );
1100 SDL_Delay(2000); // longer delay to prevent frequent redrawing
1101 SDL_mutexP(globalBakeLock);
1102 if(globalBakeState != 0) done = 1; // 1=ok, <0=error/abort
1103 SDL_mutexV(globalBakeLock);
1106 event = extern_qread(&val);
1107 if(event == ESCKEY) {
1109 SDL_mutexP(globalBakeLock);
1111 globalBakeFrame = 0;
1112 globalBakeState = -1;
1114 SDL_mutexV(globalBakeLock);
1119 // redraw the 3D for showing progress once in a while...
1120 if(lastRedraw!=globalBakeFrame) {
1122 G.scene->r.cfra = startFrame+globalBakeFrame;
1123 lastRedraw = globalBakeFrame;
1124 update_for_newframe_muted();
1125 sa= G.curscreen->areabase.first;
1127 if(sa->spacetype == SPACE_VIEW3D) { scrarea_do_windraw(sa); }
1130 screen_swapbuffers();
1133 SDL_WaitThread(simthr,NULL);
1136 SDL_DestroyMutex(globalBakeLock);
1137 globalBakeLock = NULL;
1138 } // El'Beem API init, thread creation
1139 // --------------------------------------------------------------------------------------------
1141 { // write config file to be run with command line simulator
1142 pupmenu("Fluidsim Bake Message%t|Config file export not supported.%x0");
1143 } // config file export done!
1145 // --------------------------------------------------------------------------------------------
1148 // go back to "current" blender time
1150 G.scene->r.cfra = origFrame;
1151 scene_update_for_newframe(G.scene, G.scene->lay);
1152 allqueue(REDRAWVIEW3D, 0);
1153 allqueue(REDRAWBUTSOBJECT, 0);
1156 char fsmessage[512];
1157 char elbeemerr[256];
1158 strcpy(fsmessage,"Fluidsim Bake Error: ");
1159 // check if some error occurred
1160 if(globalBakeState==-2) {
1161 strcat(fsmessage,"Failed to initialize [Msg: ");
1163 elbeemGetErrorString(elbeemerr);
1164 strcat(fsmessage,elbeemerr);
1166 strcat(fsmessage,"] |OK%x0");
1172 void fluidsimFreeBake(struct Object *ob)
1174 /* not implemented yet */
1178 #else /* DISABLE_ELBEEM */
1180 /* compile dummy functions for disabled fluid sim */
1182 FluidsimSettings *fluidsimSettingsNew(struct Object *srcob) {
1186 void fluidsimSettingsFree(FluidsimSettings *fss) {
1189 FluidsimSettings* fluidsimSettingsCopy(FluidsimSettings *fss) {
1193 /* only compile dummy functions */
1194 void fluidsimBake(struct Object *ob) {
1197 void fluidsimFreeBake(struct Object *ob) {
1200 #endif /* DISABLE_ELBEEM */