static void thumbnails_startjob(void *tjv, short *stop, short *do_update, float *UNUSED(progress)) { ThumbnailJob *tj= tjv; FileImage* limg = tj->loadimages.first; tj->stop= stop; tj->do_update= do_update; while ( (*stop==0) && (limg) ) { if ( limg->flags & IMAGEFILE ) { limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_IMAGE); } else if ( limg->flags & BLENDERFILE ) { limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_BLEND); } else if ( limg->flags & MOVIEFILE ) { limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_MOVIE); if (!limg->img) { /* remember that file can't be loaded via IMB_open_anim */ limg->flags &= ~MOVIEFILE; limg->flags |= MOVIEFILE_ICON; } } *do_update = 1; PIL_sleep_ms(10); limg = limg->next; } }
static void thumbnails_startjob(void *tjv, short *stop, short *do_update, float *UNUSED(progress)) { ThumbnailJob *tj = tjv; FileImage *limg = tj->loadimages.first; tj->stop = stop; tj->do_update = do_update; while ((*stop == 0) && (limg)) { ThumbSource source = 0; BLI_assert(limg->flags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)); if (limg->flags & FILE_TYPE_IMAGE) { source = THB_SOURCE_IMAGE; } else if (limg->flags & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { source = THB_SOURCE_BLEND; } else if (limg->flags & FILE_TYPE_MOVIE) { source = THB_SOURCE_MOVIE; } else if (limg->flags & FILE_TYPE_FTFONT) { source = THB_SOURCE_FONT; } limg->img = IMB_thumb_manage(limg->path, THB_LARGE, source); *do_update = true; PIL_sleep_ms(10); limg = limg->next; } }
void BLI_insert_work(ThreadedWorker *worker, void *param) { WorkParam *p = MEM_callocN(sizeof(WorkParam), "workparam"); int index; if (BLI_available_threads(&worker->threadbase) == 0) { index = worker->total; while(index == worker->total) { PIL_sleep_ms(worker->sleep_time); for (index = 0; index < worker->total; index++) { if (worker->busy[index] == 0) { BLI_remove_thread_index(&worker->threadbase, index); break; } } } } else { index = BLI_available_thread_index(&worker->threadbase); } worker->busy[index] = 1; p->param = param; p->index = index; p->worker = worker; BLI_insert_thread(&worker->threadbase, p); }
void wm_window_process_events(const bContext *C) { int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */ if (hasevent) GHOST_DispatchEvents(g_system); hasevent |= wm_window_timer(C); /* no event, we sleep 5 milliseconds */ if (hasevent == 0) PIL_sleep_ms(5); }
/* optimized tree execute test for compositing */ static void ntreeCompositExecTreeOld(bNodeTree *ntree, RenderData *rd, int do_preview) { bNodeExec *nodeexec; bNode *node; ListBase threads; ThreadData thdata; int totnode, curnode, rendering = TRUE, n; bNodeTreeExec *exec = ntree->execdata; if (do_preview) ntreeInitPreview(ntree, 0, 0); if (!ntree->execdata) { /* XXX this is the top-level tree, so we use the ntree->execdata pointer. */ exec = ntreeCompositBeginExecTree(ntree, 1); } ntree_composite_texnode(ntree, 1); /* prevent unlucky accidents */ if (G.background) rd->scemode &= ~R_COMP_CROP; /* setup callerdata for thread callback */ thdata.rd= rd; thdata.stack= exec->stack; /* fixed seed, for example noise texture */ BLI_srandom(rd->cfra); /* sets need_exec tags in nodes */ curnode = totnode= setExecutableNodes(exec, &thdata); BLI_init_threads(&threads, exec_composite_node, rd->threads); while (rendering) { if (BLI_available_threads(&threads)) { nodeexec= getExecutableNode(exec); if (nodeexec) { node = nodeexec->node; if (ntree->progress && totnode) ntree->progress(ntree->prh, (1.0f - curnode/(float)totnode)); if (ntree->stats_draw) { char str[128]; BLI_snprintf(str, sizeof(str), "Compositing %d %s", curnode, node->name); ntree->stats_draw(ntree->sdh, str); } curnode--; node->threaddata = &thdata; node->exec= NODE_PROCESSING; BLI_insert_thread(&threads, nodeexec); } else PIL_sleep_ms(50); } else PIL_sleep_ms(50); rendering= 0; /* test for ESC */ if (ntree->test_break && ntree->test_break(ntree->tbh)) { for (node= ntree->nodes.first; node; node= node->next) node->exec |= NODE_READY; } /* check for ready ones, and if we need to continue */ for (n=0, nodeexec=exec->nodeexec; n < exec->totnodes; ++n, ++nodeexec) { node = nodeexec->node; if (node->exec & NODE_READY) { if ((node->exec & NODE_FINISHED)==0) { BLI_remove_thread(&threads, nodeexec); /* this waits for running thread to finish btw */ node->exec |= NODE_FINISHED; /* freeing unused buffers */ if (rd->scemode & R_COMP_FREE) freeExecutableNode(exec); } } else rendering= 1; } } BLI_end_threads(&threads); /* XXX top-level tree uses the ntree->execdata pointer */ ntreeCompositEndExecTree(exec, 1); }
/* return path for restart */ static char *wm_main_playanim_intern(int argc, const char **argv) { struct ImBuf *ibuf = NULL; static char filepath[FILE_MAX]; /* abused to return dropped file path */ GHOST_TUns32 maxwinx, maxwiny; int i; /* This was done to disambiguate the name for use under c++. */ struct anim *anim = NULL; int start_x = 0, start_y = 0; int sfra = -1; int efra = -1; int totblock; PlayState ps = {0}; /* ps.doubleb = TRUE;*/ /* UNUSED */ ps.go = TRUE; ps.direction = TRUE; ps.next_frame = 1; ps.once = FALSE; ps.turbo = FALSE; ps.pingpong = FALSE; ps.noskip = FALSE; ps.sstep = FALSE; ps.wait2 = FALSE; ps.stopped = FALSE; ps.picture = NULL; ps.dropped_file[0] = 0; ps.zoom = 1.0f; /* resetmap = FALSE */ ps.fstep = 1; ps.fontid = -1; while (argc > 1) { if (argv[1][0] == '-') { switch (argv[1][1]) { case 'm': fromdisk = TRUE; break; case 'p': if (argc > 3) { start_x = atoi(argv[2]); start_y = atoi(argv[3]); argc -= 2; argv += 2; } else { printf("too few arguments for -p (need 2): skipping\n"); } break; case 'f': if (argc > 3) { double fps = atof(argv[2]); double fps_base = atof(argv[3]); if (fps == 0.0) { fps = 1; printf("invalid fps," "forcing 1\n"); } swaptime = fps_base / fps; argc -= 2; argv += 2; } else { printf("too few arguments for -f (need 2): skipping\n"); } break; case 's': sfra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) )); argc--; argv++; break; case 'e': efra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) )); argc--; argv++; break; case 'j': ps.fstep = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]))); swaptime *= ps.fstep; argc--; argv++; break; default: printf("unknown option '%c': skipping\n", argv[1][1]); break; } argc--; argv++; } else { break; } } if (argc > 1) { BLI_strncpy(filepath, argv[1], sizeof(filepath)); } else { BLI_current_working_dir(filepath, sizeof(filepath)); BLI_add_slash(filepath); } if (IMB_isanim(filepath)) { /* OCIO_TODO: support different input color spaces */ anim = IMB_open_anim(filepath, IB_rect, 0, NULL); if (anim) { ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); IMB_close_anim(anim); anim = NULL; } } else if (!IMB_ispic(filepath)) { printf("%s: '%s' not an image file\n", __func__, filepath); exit(1); } if (ibuf == NULL) { /* OCIO_TODO: support different input color space */ ibuf = IMB_loadiffname(filepath, IB_rect, NULL); } if (ibuf == NULL) { printf("%s: '%s' couldn't open\n", __func__, filepath); exit(1); } #if 0 //XXX25 #if !defined(WIN32) && !defined(__APPLE__) if (fork()) exit(0); #endif #endif //XXX25 { GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, &ps); g_WS.ghost_system = GHOST_CreateSystem(); GHOST_AddEventConsumer(g_WS.ghost_system, consumer); playanim_window_open("Blender:Anim", start_x, start_y, ibuf->x, ibuf->y); /* unified matrix, note it affects offset for drawing */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); } GHOST_GetMainDisplayDimensions(g_WS.ghost_system, &maxwinx, &maxwiny); //GHOST_ActivateWindowDrawingContext(g_WS.ghost_window); /* initialize the font */ BLF_init(11, 72); ps.fontid = BLF_load_mem("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size); BLF_size(ps.fontid, 11, 72); ps.ibufx = ibuf->x; ps.ibufy = ibuf->y; ps.win_x = ps.ibufx; ps.win_y = ps.ibufy; if (maxwinx % ibuf->x) maxwinx = ibuf->x * (1 + (maxwinx / ibuf->x)); if (maxwiny % ibuf->y) maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y)); glClearColor(0.1, 0.1, 0.1, 0.0); glClear(GL_COLOR_BUFFER_BIT); GHOST_SwapWindowBuffers(g_WS.ghost_window); if (sfra == -1 || efra == -1) { /* one of the frames was invalid, just use all images */ sfra = 1; efra = MAXFRAME; } build_pict_list(&ps, filepath, (efra - sfra) + 1, ps.fstep, ps.fontid); for (i = 2; i < argc; i++) { BLI_strncpy(filepath, argv[i], sizeof(filepath)); build_pict_list(&ps, filepath, (efra - sfra) + 1, ps.fstep, ps.fontid); } IMB_freeImBuf(ibuf); ibuf = NULL; pupdate_time(); ptottime = 0; /* newly added in 2.6x, without this images never get freed */ #define USE_IMB_CACHE while (ps.go) { if (ps.pingpong) ps.direction = -ps.direction; if (ps.direction == 1) { ps.picture = picsbase.first; } else { ps.picture = picsbase.last; } if (ps.picture == NULL) { printf("couldn't find pictures\n"); ps.go = FALSE; } if (ps.pingpong) { if (ps.direction == 1) { ps.picture = ps.picture->next; } else { ps.picture = ps.picture->prev; } } if (ptottime > 0.0) ptottime = 0.0; while (ps.picture) { int hasevent; #ifndef USE_IMB_CACHE if (ibuf != NULL && ibuf->ftype == 0) IMB_freeImBuf(ibuf); #endif if (ps.picture->ibuf) { ibuf = ps.picture->ibuf; } else if (ps.picture->anim) { ibuf = IMB_anim_absolute(ps.picture->anim, ps.picture->frame, IMB_TC_NONE, IMB_PROXY_NONE); } else if (ps.picture->mem) { /* use correct colorspace here */ ibuf = IMB_ibImageFromMemory((unsigned char *) ps.picture->mem, ps.picture->size, ps.picture->IB_flags, NULL, ps.picture->name); } else { /* use correct colorspace here */ ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags, NULL); } if (ibuf) { #ifdef USE_IMB_CACHE ps.picture->ibuf = ibuf; #endif BLI_strncpy(ibuf->name, ps.picture->name, sizeof(ibuf->name)); /* why only windows? (from 2.4x) - campbell */ #ifdef _WIN32 GHOST_SetTitle(g_WS.ghost_window, ps.picture->name); #endif while (pupdate_time()) PIL_sleep_ms(1); ptottime -= swaptime; playanim_toscreen(&ps, ps.picture, ibuf, ps.fontid, ps.fstep); } /* else delete */ else { printf("error: can't play this image type\n"); exit(0); } if (ps.once) { if (ps.picture->next == NULL) { ps.wait2 = TRUE; } else if (ps.picture->prev == NULL) { ps.wait2 = TRUE; } } ps.next_frame = ps.direction; while ( (hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0)) || ps.wait2 != 0) { if (hasevent) { GHOST_DispatchEvents(g_WS.ghost_system); } /* Note, this still draws for mousemoves on pause */ if (ps.wait2) { if (hasevent) { if (ibuf) { while (pupdate_time()) PIL_sleep_ms(1); ptottime -= swaptime; playanim_toscreen(&ps, ps.picture, ibuf, ps.fontid, ps.fstep); } } } if (!ps.go) { break; } } ps.wait2 = ps.sstep; if (ps.wait2 == 0 && ps.stopped == 0) { ps.stopped = TRUE; } pupdate_time(); if (ps.picture && ps.next_frame) { /* always at least set one step */ while (ps.picture) { ps.picture = playanim_step(ps.picture, ps.next_frame); if (ps.once && ps.picture != NULL) { if (ps.picture->next == NULL) { ps.wait2 = TRUE; } else if (ps.picture->prev == NULL) { ps.wait2 = TRUE; } } if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) break; ptottime -= swaptime; } if (ps.picture == NULL && ps.sstep) { ps.picture = playanim_step(ps.picture, ps.next_frame); } } if (ps.go == FALSE) { break; } } } ps.picture = picsbase.first; anim = NULL; while (ps.picture) { if (ps.picture && ps.picture->anim && (anim != ps.picture->anim)) { // to prevent divx crashes anim = ps.picture->anim; IMB_close_anim(anim); } if (ps.picture->ibuf) { IMB_freeImBuf(ps.picture->ibuf); } if (ps.picture->mem) { MEM_freeN(ps.picture->mem); } ps.picture = ps.picture->next; } /* cleanup */ #ifndef USE_IMB_CACHE if (ibuf) IMB_freeImBuf(ibuf); #endif BLI_freelistN(&picsbase); #if 0 // XXX25 free_blender(); #else /* we still miss freeing a lot!, * but many areas could skip initialization too for anim play */ BLF_exit(); #endif GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window); /* early exit, IMB and BKE should be exited only in end */ if (ps.dropped_file[0]) { BLI_strncpy(filepath, ps.dropped_file, sizeof(filepath)); return filepath; } IMB_exit(); BKE_images_exit(); totblock = MEM_get_memory_blocks_in_use(); if (totblock != 0) { /* prints many bAKey, bArgument's which are tricky to fix */ #if 0 printf("Error Totblock: %d\n", totblock); MEM_printmemlist(); #endif } return NULL; }
/* Precache a volume into a 3D voxel grid. * The voxel grid is stored in the ObjectInstanceRen, * in camera space, aligned with the ObjectRen's bounding box. * Resolution is defined by the user. */ void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Material *ma) { VolumePrecache *vp; VolPrecachePart *nextpa, *pa; RayObject *tree; ShadeInput shi; ListBase threads; float *bbmin=obi->obr->boundbox[0], *bbmax=obi->obr->boundbox[1]; int parts[3] = {1, 1, 1}, totparts; int caching=1, counter=0; int totthread = re->r.threads; double time, lasttime= PIL_check_seconds_timer(); R = *re; /* create a raytree with just the faces of the instanced ObjectRen, * used for checking if the cached point is inside or outside. */ //tree = create_raytree_obi(obi, bbmin, bbmax); tree = makeraytree_object(&R, obi); if (!tree) return; INIT_MINMAX(bbmin, bbmax); RE_rayobject_merge_bb( tree, bbmin, bbmax); vp = MEM_callocN(sizeof(VolumePrecache), "volume light cache"); if (!precache_resolution(vp, bbmin, bbmax, ma->vol.precache_resolution)) { MEM_freeN(vp); vp = NULL; return; } vp->data_r = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data red channel"); vp->data_g = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data green channel"); vp->data_b = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data blue channel"); obi->volume_precache = vp; /* Need a shadeinput to calculate scattering */ precache_setup_shadeinput(re, obi, ma, &shi); precache_init_parts(re, tree, &shi, obi, totthread, parts); totparts = parts[0] * parts[1] * parts[2]; BLI_init_threads(&threads, vol_precache_part, totthread); while(caching) { if(BLI_available_threads(&threads) && !(re->test_break(re->tbh))) { nextpa = precache_get_new_part(re); if (nextpa) { nextpa->working = 1; BLI_insert_thread(&threads, nextpa); } } else PIL_sleep_ms(50); caching=0; counter=0; for(pa= re->volume_precache_parts.first; pa; pa= pa->next) { if(pa->done) { counter++; BLI_remove_thread(&threads, pa); } else caching = 1; } if (re->test_break(re->tbh) && BLI_available_threads(&threads)==totthread) caching=0; time= PIL_check_seconds_timer(); if(time-lasttime>1.0f) { char str[64]; sprintf(str, "Precaching volume: %d%%", (int)(100.0f * ((float)counter / (float)totparts))); re->i.infostr= str; re->stats_draw(re->sdh, &re->i); re->i.infostr= NULL; lasttime= time; } } BLI_end_threads(&threads); BLI_freelistN(&re->volume_precache_parts); if(tree) { //TODO: makeraytree_object creates a tree and saves it on OBI, if we free this tree we should also clear other pointers to it //RE_rayobject_free(tree); //tree= NULL; } lightcache_filter(obi->volume_precache); if (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)) { multiple_scattering_diffusion(re, vp, ma); } }
/* * This thread is used to load video frame asynchronously. * It provides a frame caching service. * The main thread is responsible for positioning the frame pointer in the * file correctly before calling startCache() which starts this thread. * The cache is organized in two layers: 1) a cache of 20-30 undecoded packets to keep * memory and CPU low 2) a cache of 5 decoded frames. * If the main thread does not find the frame in the cache (because the video has restarted * or because the GE is lagging), it stops the cache with StopCache() (this is a synchronous * function: it sends a signal to stop the cache thread and wait for confirmation), then * change the position in the stream and restarts the cache thread. */ void *VideoFFmpeg::cacheThread(void *data) { VideoFFmpeg* video = (VideoFFmpeg*)data; // holds the frame that is being decoded CacheFrame *currentFrame = NULL; CachePacket *cachePacket; bool endOfFile = false; int frameFinished = 0; double timeBase = av_q2d(video->m_formatCtx->streams[video->m_videoStream]->time_base); int64_t startTs = video->m_formatCtx->streams[video->m_videoStream]->start_time; if (startTs == AV_NOPTS_VALUE) startTs = 0; while (!video->m_stopThread) { // packet cache is used solely by this thread, no need to lock // In case the stream/file contains other stream than the one we are looking for, // allow a bit of cycling to get rid quickly of those frames frameFinished = 0; while ( !endOfFile && (cachePacket = (CachePacket *)video->m_packetCacheFree.first) != NULL && frameFinished < 25) { // free packet => packet cache is not full yet, just read more if (av_read_frame(video->m_formatCtx, &cachePacket->packet)>=0) { if (cachePacket->packet.stream_index == video->m_videoStream) { // make sure fresh memory is allocated for the packet and move it to queue av_dup_packet(&cachePacket->packet); BLI_remlink(&video->m_packetCacheFree, cachePacket); BLI_addtail(&video->m_packetCacheBase, cachePacket); break; } else { // this is not a good packet for us, just leave it on free queue // Note: here we could handle sound packet av_free_packet(&cachePacket->packet); frameFinished++; } } else { if (video->m_isFile) // this mark the end of the file endOfFile = true; // if we cannot read a packet, no need to continue break; } } // frame cache is also used by main thread, lock if (currentFrame == NULL) { // no current frame being decoded, take free one pthread_mutex_lock(&video->m_cacheMutex); if ((currentFrame = (CacheFrame *)video->m_frameCacheFree.first) != NULL) BLI_remlink(&video->m_frameCacheFree, currentFrame); pthread_mutex_unlock(&video->m_cacheMutex); } if (currentFrame != NULL) { // this frame is out of free and busy queue, we can manipulate it without locking frameFinished = 0; while (!frameFinished && (cachePacket = (CachePacket *)video->m_packetCacheBase.first) != NULL) { BLI_remlink(&video->m_packetCacheBase, cachePacket); // use m_frame because when caching, it is not used in main thread // we can't use currentFrame directly because we need to convert to RGB first avcodec_decode_video2(video->m_codecCtx, video->m_frame, &frameFinished, &cachePacket->packet); if (frameFinished) { AVFrame * input = video->m_frame; /* This means the data wasnt read properly, this check stops crashing */ if ( input->data[0]!=0 || input->data[1]!=0 || input->data[2]!=0 || input->data[3]!=0) { if (video->m_deinterlace) { if (avpicture_deinterlace( (AVPicture*) video->m_frameDeinterlaced, (const AVPicture*) video->m_frame, video->m_codecCtx->pix_fmt, video->m_codecCtx->width, video->m_codecCtx->height) >= 0) { input = video->m_frameDeinterlaced; } } // convert to RGB24 sws_scale(video->m_imgConvertCtx, input->data, input->linesize, 0, video->m_codecCtx->height, currentFrame->frame->data, currentFrame->frame->linesize); // move frame to queue, this frame is necessarily the next one video->m_curPosition = (long)((cachePacket->packet.dts-startTs) * (video->m_baseFrameRate*timeBase) + 0.5); currentFrame->framePosition = video->m_curPosition; pthread_mutex_lock(&video->m_cacheMutex); BLI_addtail(&video->m_frameCacheBase, currentFrame); pthread_mutex_unlock(&video->m_cacheMutex); currentFrame = NULL; } } av_free_packet(&cachePacket->packet); BLI_addtail(&video->m_packetCacheFree, cachePacket); } if (currentFrame && endOfFile) { // no more packet and end of file => put a special frame that indicates that currentFrame->framePosition = -1; pthread_mutex_lock(&video->m_cacheMutex); BLI_addtail(&video->m_frameCacheBase, currentFrame); pthread_mutex_unlock(&video->m_cacheMutex); currentFrame = NULL; // no need to stay any longer in this thread break; } } // small sleep to avoid unnecessary looping PIL_sleep_ms(10); } // before quitting, put back the current frame to queue to allow freeing if (currentFrame) { pthread_mutex_lock(&video->m_cacheMutex); BLI_addtail(&video->m_frameCacheFree, currentFrame); pthread_mutex_unlock(&video->m_cacheMutex); } return 0; }
/* only this runs inside thread */ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float *UNUSED(progress)) { ScreenshotJob *sj = sjv; RenderData rd = sj->scene->r; bMovieHandle *mh = BKE_movie_handle_get(sj->scene->r.im_format.imtype); /* we need this as local variables for renderdata */ rd.frs_sec = U.scrcastfps; rd.frs_sec_base = 1.0f; if (BKE_imtype_is_movie(rd.im_format.imtype)) { if (!mh->start_movie(sj->scene, &rd, sj->dumpsx, sj->dumpsy, &sj->reports)) { printf("screencast job stopped\n"); return; } } else mh = NULL; sj->stop = stop; sj->do_update = do_update; *do_update = true; /* wait for opengl rect */ while (*stop == 0) { if (sj->dumprect) { if (mh) { if (mh->append_movie(&rd, rd.sfra, rd.cfra, (int *)sj->dumprect, sj->dumpsx, sj->dumpsy, &sj->reports)) { BKE_reportf(&sj->reports, RPT_INFO, "Appended frame: %d", rd.cfra); printf("Appended frame %d\n", rd.cfra); } else { break; } } else { ImBuf *ibuf = IMB_allocImBuf(sj->dumpsx, sj->dumpsy, rd.im_format.planes, 0); char name[FILE_MAX]; int ok; BKE_makepicstring(name, rd.pic, sj->bmain->name, rd.cfra, &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true); ibuf->rect = sj->dumprect; ok = BKE_imbuf_write(ibuf, name, &rd.im_format); if (ok == 0) { printf("Write error: cannot save %s\n", name); BKE_reportf(&sj->reports, RPT_INFO, "Write error: cannot save %s", name); break; } else { printf("Saved file: %s\n", name); BKE_reportf(&sj->reports, RPT_INFO, "Saved file: %s", name); } /* imbuf knows which rects are not part of ibuf */ IMB_freeImBuf(ibuf); } MEM_freeN(sj->dumprect); sj->dumprect = NULL; *do_update = true; rd.cfra++; } else PIL_sleep_ms(U.scrcastwait); } if (mh) mh->end_movie(); BKE_report(&sj->reports, RPT_INFO, "Screencast job stopped"); }
/* returns 0 if nothing was handled */ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_update, float *progress) { BakeShade *handles; ListBase threads; Image *ima; int a, vdone = false, result = BAKE_RESULT_OK; bool use_mask = false; bool use_displacement_buffer = false; re->scene_color_manage = BKE_scene_check_color_management_enabled(re->scene); /* initialize render global */ R = *re; R.bakebuf = NULL; /* initialize static vars */ get_next_bake_face(NULL); /* do we need a mask? */ if (re->r.bake_filter) use_mask = true; /* do we need buffer to store displacements */ if (ELEM(type, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE)) { if (((R.r.bake_flag & R_BAKE_NORMALIZE) && R.r.bake_maxdist == 0.0f) || (type == RE_BAKE_DERIVATIVE)) { use_displacement_buffer = true; use_mask = true; } } /* baker uses this flag to detect if image was initialized */ if ((R.r.bake_flag & R_BAKE_VCOL) == 0) { for (ima = G.main->image.first; ima; ima = ima->id.next) { ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL, IMA_IBUF_IMA); ima->id.flag |= LIB_DOIT; ima->flag &= ~IMA_USED_FOR_RENDER; if (ibuf) { ibuf->userdata = NULL; /* use for masking if needed */ } BKE_image_release_ibuf(ima, ibuf, NULL); } } if (R.r.bake_flag & R_BAKE_VCOL) { /* untag all meshes */ tag_main_lb(&G.main->mesh, false); } BLI_init_threads(&threads, do_bake_thread, re->r.threads); handles = MEM_callocN(sizeof(BakeShade) * re->r.threads, "BakeShade"); /* get the threads running */ for (a = 0; a < re->r.threads; a++) { /* set defaults in handles */ handles[a].ssamp.shi[0].lay = re->lay; if (type == RE_BAKE_SHADOW) { handles[a].ssamp.shi[0].passflag = SCE_PASS_SHADOW; } else { handles[a].ssamp.shi[0].passflag = SCE_PASS_COMBINED; } handles[a].ssamp.shi[0].combinedflag = ~(SCE_PASS_SPEC); handles[a].ssamp.shi[0].thread = a; handles[a].ssamp.tot = 1; handles[a].type = type; handles[a].actob = actob; if (R.r.bake_flag & R_BAKE_VCOL) handles[a].zspan = NULL; else handles[a].zspan = MEM_callocN(sizeof(ZSpan), "zspan for bake"); handles[a].use_mask = use_mask; handles[a].use_displacement_buffer = use_displacement_buffer; handles[a].do_update = do_update; /* use to tell the view to update */ handles[a].displacement_min = FLT_MAX; handles[a].displacement_max = -FLT_MAX; BLI_insert_thread(&threads, &handles[a]); } /* wait for everything to be done */ a = 0; while (a != re->r.threads) { PIL_sleep_ms(50); /* calculate progress */ for (vdone = false, a = 0; a < re->r.threads; a++) vdone += handles[a].vdone; if (progress) *progress = (float)(vdone / (float)re->totvlak); for (a = 0; a < re->r.threads; a++) { if (handles[a].ready == false) { break; } } } /* filter and refresh images */ if ((R.r.bake_flag & R_BAKE_VCOL) == 0) { float displacement_min = FLT_MAX, displacement_max = -FLT_MAX; if (use_displacement_buffer) { for (a = 0; a < re->r.threads; a++) { displacement_min = min_ff(displacement_min, handles[a].displacement_min); displacement_max = max_ff(displacement_max, handles[a].displacement_max); } } for (ima = G.main->image.first; ima; ima = ima->id.next) { if ((ima->id.flag & LIB_DOIT) == 0) { ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL, IMA_IBUF_IMA); BakeImBufuserData *userdata; if (ima->flag & IMA_USED_FOR_RENDER) result = BAKE_RESULT_FEEDBACK_LOOP; if (!ibuf) continue; userdata = (BakeImBufuserData *)ibuf->userdata; if (userdata) { if (use_displacement_buffer) { if (type == RE_BAKE_DERIVATIVE) { float user_scale = (R.r.bake_flag & R_BAKE_USERSCALE) ? R.r.bake_user_scale : -1.0f; RE_bake_make_derivative(ibuf, userdata->displacement_buffer, userdata->mask_buffer, displacement_min, displacement_max, user_scale); } else { RE_bake_ibuf_normalize_displacement(ibuf, userdata->displacement_buffer, userdata->mask_buffer, displacement_min, displacement_max); } } RE_bake_ibuf_filter(ibuf, userdata->mask_buffer, re->r.bake_filter); } ibuf->userflags |= IB_BITMAPDIRTY; BKE_image_release_ibuf(ima, ibuf, NULL); } } /* calculate return value */ for (a = 0; a < re->r.threads; a++) { zbuf_free_span(handles[a].zspan); MEM_freeN(handles[a].zspan); } } MEM_freeN(handles); BLI_end_threads(&threads); if (vdone == 0) { result = BAKE_RESULT_NO_OBJECTS; } return result; }
int fluidsimBake(bContext *C, ReportList *reports, Object *ob) { Scene *scene= CTX_data_scene(C); FILE *fileCfg; int i; Object *fsDomain = NULL; FluidsimSettings *domainSettings; Object *obit = NULL; /* object iterator */ Base *base; int origFrame = scene->r.cfra; char debugStrBuffer[256]; int dirExist = 0; int gridlevels = 0; int simAborted = 0; // was the simulation aborted by user? int doExportOnly = 0; char *exportEnvStr = "BLENDER_ELBEEMEXPORTONLY"; const char *strEnvName = "BLENDER_ELBEEMDEBUG"; // from blendercall.cpp //char *channelNames[3] = { "translation","rotation","scale" }; char *suffixConfig = "fluidsim.cfg"; char *suffixSurface = "fluidsurface"; char newSurfdataPath[FILE_MAXDIR+FILE_MAXFILE]; // modified output settings char targetDir[FILE_MAXDIR+FILE_MAXFILE]; // store & modify output settings char targetFile[FILE_MAXDIR+FILE_MAXFILE]; // temp. store filename from targetDir for access int outStringsChanged = 0; // modified? copy back before baking int haveSomeFluid = 0; // check if any fluid objects are set // config vars, inited before either export or run... double calcViscosity = 0.0; int noFrames; double aniFrameTime; float aniFrlen; int channelObjCount; float *bbStart = NULL; float *bbSize = NULL; float domainMat[4][4]; float invDomMat[4][4]; // channel data int allchannelSize; // fixed by no. of frames int startFrame = 1; // dont use scene->r.sfra here, always start with frame 1 // easy frame -> sim time calc float *timeAtFrame=NULL, *timeAtIndex=NULL; // domain float *channelDomainTime = NULL; float *channelDomainViscosity = NULL; float *channelDomainGravity = NULL; // objects (currently max. 256 objs) float *channelObjMove[256][3]; // object movments , 0=trans, 1=rot, 2=scale float *channelObjInivel[256]; // initial velocities float *channelObjActive[256]; // obj active channel /* fluid control channels */ float *channelAttractforceStrength[256]; float *channelAttractforceRadius[256]; float *channelVelocityforceStrength[256]; float *channelVelocityforceRadius[256]; FluidsimModifierData *fluidmd = NULL; Mesh *mesh = NULL; if(getenv(strEnvName)) { int dlevel = atoi(getenv(strEnvName)); elbeemSetDebugLevel(dlevel); snprintf(debugStrBuffer,256,"fluidsimBake::msg: Debug messages activated due to envvar '%s'\n",strEnvName); elbeemDebugOut(debugStrBuffer); } if(getenv(exportEnvStr)) { doExportOnly = atoi(getenv(exportEnvStr)); snprintf(debugStrBuffer,256,"fluidsimBake::msg: Exporting mode set to '%d' due to envvar '%s'\n",doExportOnly, exportEnvStr); elbeemDebugOut(debugStrBuffer); } // make sure it corresponds to startFrame setting // old: noFrames = scene->r.efra - scene->r.sfra +1; noFrames = scene->r.efra - 0; if(noFrames<=0) { BKE_report(reports, RPT_ERROR, "No frames to export - check your animation range settings."); return 0; } /* no object pointer, find in selected ones.. */ if(!ob) { for(base=scene->base.first; base; base= base->next) { if ((base)->flag & SELECT) { FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim); if(fluidmdtmp && (base->object->type==OB_MESH)) { if(fluidmdtmp->fss->type == OB_FLUIDSIM_DOMAIN) { ob = base->object; break; } } } } // no domains found? if(!ob) return 0; } channelObjCount = 0; for(base=scene->base.first; base; base= base->next) { FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim); obit = base->object; if( fluidmdtmp && (obit->type==OB_MESH) && (fluidmdtmp->fss->type != OB_FLUIDSIM_DOMAIN) && // if has to match 3 places! // CHECKMATCH (fluidmdtmp->fss->type != OB_FLUIDSIM_PARTICLE) ) { channelObjCount++; } } if (channelObjCount>=255) { BKE_report(reports, RPT_ERROR, "Cannot bake with more then 256 objects."); return 0; } /* check if there's another domain... */ for(base=scene->base.first; base; base= base->next) { FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim); obit = base->object; if( fluidmdtmp &&(obit->type==OB_MESH)) { if(fluidmdtmp->fss->type == OB_FLUIDSIM_DOMAIN) { if(obit != ob) { BKE_report(reports, RPT_ERROR, "There should be only one domain object."); return 0; } } } } // check if theres any fluid // abort baking if not... for(base=scene->base.first; base; base= base->next) { FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim); obit = base->object; if( fluidmdtmp && (obit->type==OB_MESH) && ((fluidmdtmp->fss->type == OB_FLUIDSIM_FLUID) || (fluidmdtmp->fss->type == OB_FLUIDSIM_INFLOW) )) { haveSomeFluid = 1; break; } } if(!haveSomeFluid) { BKE_report(reports, RPT_ERROR, "No fluid objects in scene."); return 0; } /* these both have to be valid, otherwise we wouldnt be here */ /* dont use ob here after...*/ fsDomain = ob; fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim); domainSettings = fluidmd->fss; ob = NULL; mesh = fsDomain->data; // calculate bounding box fluid_get_bb(mesh->mvert, mesh->totvert, fsDomain->obmat, domainSettings->bbStart, domainSettings->bbSize); // reset last valid frame domainSettings->lastgoodframe = -1; /* rough check of settings... */ if(domainSettings->previewresxyz > domainSettings->resolutionxyz) { snprintf(debugStrBuffer,256,"fluidsimBake::warning - Preview (%d) >= Resolution (%d)... setting equal.\n", domainSettings->previewresxyz , domainSettings->resolutionxyz); elbeemDebugOut(debugStrBuffer); domainSettings->previewresxyz = domainSettings->resolutionxyz; } // set adaptive coarsening according to resolutionxyz // this should do as an approximation, with in/outflow // doing this more accurate would be overkill // perhaps add manual setting? if(domainSettings->maxRefine <0) { if(domainSettings->resolutionxyz>128) { gridlevels = 2; } else if(domainSettings->resolutionxyz>64) { gridlevels = 1; } else { gridlevels = 0; } } else { gridlevels = domainSettings->maxRefine; } snprintf(debugStrBuffer,256,"fluidsimBake::msg: Baking %s, refine: %d\n", fsDomain->id.name , gridlevels ); elbeemDebugOut(debugStrBuffer); // prepare names... strncpy(targetDir, domainSettings->surfdataPath, FILE_MAXDIR); strncpy(newSurfdataPath, domainSettings->surfdataPath, FILE_MAXDIR); BLI_convertstringcode(targetDir, G.sce); // fixed #frame-no strcpy(targetFile, targetDir); strcat(targetFile, suffixConfig); if(!doExportOnly) { strcat(targetFile,".tmp"); } // dont overwrite/delete original file // make sure all directories exist // as the bobjs use the same dir, this only needs to be checked // for the cfg output BLI_make_existing_file(targetFile); // check selected directory // simply try to open cfg file for writing to test validity of settings fileCfg = fopen(targetFile, "w"); if(fileCfg) { dirExist = 1; fclose(fileCfg); // remove cfg dummy from directory test if(!doExportOnly) { BLI_delete(targetFile, 0,0); } } if((strlen(targetDir)<1) || (!dirExist)) { char blendDir[FILE_MAXDIR+FILE_MAXFILE], blendFile[FILE_MAXDIR+FILE_MAXFILE]; // invalid dir, reset to current/previous strcpy(blendDir, G.sce); BLI_splitdirstring(blendDir, blendFile); if(strlen(blendFile)>6){ int len = strlen(blendFile); if( (blendFile[len-6]=='.')&& (blendFile[len-5]=='b')&& (blendFile[len-4]=='l')&& (blendFile[len-3]=='e')&& (blendFile[len-2]=='n')&& (blendFile[len-1]=='d') ){ blendFile[len-6] = '\0'; } } // todo... strip .blend ? snprintf(newSurfdataPath,FILE_MAXFILE+FILE_MAXDIR,"//fluidsimdata/%s_%s_", blendFile, fsDomain->id.name); snprintf(debugStrBuffer,256,"fluidsimBake::error - warning resetting output dir to '%s'\n", newSurfdataPath); elbeemDebugOut(debugStrBuffer); outStringsChanged=1; } // check if modified output dir is ok if(outStringsChanged) { char dispmsg[FILE_MAXDIR+FILE_MAXFILE+256]; int selection=0; strcpy(dispmsg,"Output settings set to: '"); strcat(dispmsg, newSurfdataPath); strcat(dispmsg, "'%t|Continue with changed settings%x1|Discard and abort%x0"); // ask user if thats what he/she wants... selection = pupmenu(dispmsg); if(selection<1) return 0; // 0 from menu, or -1 aborted strcpy(targetDir, newSurfdataPath); strncpy(domainSettings->surfdataPath, newSurfdataPath, FILE_MAXDIR); BLI_convertstringcode(targetDir, G.sce); // fixed #frame-no } // -------------------------------------------------------------------------------------------- // dump data for start frame // CHECK more reasonable to number frames according to blender? // dump data for frame 0 scene->r.cfra = startFrame; ED_update_for_newframe(C, 1); // init common export vars for both file export and run for(i=0; i<256; i++) { channelObjMove[i][0] = channelObjMove[i][1] = channelObjMove[i][2] = NULL; channelObjInivel[i] = NULL; channelObjActive[i] = NULL; channelAttractforceStrength[i] = NULL; channelAttractforceRadius[i] = NULL; channelVelocityforceStrength[i] = NULL; channelVelocityforceRadius[i] = NULL; } allchannelSize = scene->r.efra; // always use till last frame aniFrameTime = (domainSettings->animEnd - domainSettings->animStart)/(double)noFrames; // blender specific - scale according to map old/new settings in anim panel: aniFrlen = scene->r.framelen; if(domainSettings->viscosityMode==1) { /* manual mode, visc=value/(10^-vexp) */ calcViscosity = (1.0/pow(10.0,domainSettings->viscosityExponent)) * domainSettings->viscosityValue; } else { calcViscosity = fluidsimViscosityPreset[ domainSettings->viscosityMode ]; } bbStart = domainSettings->bbStart; bbSize = domainSettings->bbSize; // always init { int timeIcu[1] = { FLUIDSIM_TIME }; float timeDef[1] = { 1. }; int gravIcu[3] = { FLUIDSIM_GRAV_X, FLUIDSIM_GRAV_Y, FLUIDSIM_GRAV_Z }; float gravDef[3]; int viscIcu[1] = { FLUIDSIM_VISC }; float viscDef[1] = { 1. }; gravDef[0] = domainSettings->gravx; gravDef[1] = domainSettings->gravy; gravDef[2] = domainSettings->gravz; // time channel is a bit special, init by hand... timeAtIndex = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatindex"); for(i=0; i<=scene->r.efra; i++) { timeAtIndex[i] = (float)(i-startFrame); } fluidsimInitChannel(scene, &channelDomainTime, allchannelSize, timeAtIndex, timeIcu,timeDef, domainSettings->ipo, CHANNEL_FLOAT ); // NDEB // time channel is a multiplicator for aniFrameTime if(channelDomainTime) { for(i=0; i<allchannelSize; i++) { channelDomainTime[i*2+0] = aniFrameTime * channelDomainTime[i*2+0]; if(channelDomainTime[i*2+0]<0.) channelDomainTime[i*2+0] = 0.; } } timeAtFrame = MEM_callocN( (allchannelSize+1)*1*sizeof(float), "fluidsiminit_timeatframe"); timeAtFrame[0] = timeAtFrame[1] = domainSettings->animStart; // start at index 1 if(channelDomainTime) { for(i=2; i<=allchannelSize; i++) { timeAtFrame[i] = timeAtFrame[i-1]+channelDomainTime[(i-1)*2+0]; } } else { for(i=2; i<=allchannelSize; i++) { timeAtFrame[i] = timeAtFrame[i-1]+aniFrameTime; } } fluidsimInitChannel(scene, &channelDomainViscosity, allchannelSize, timeAtFrame, viscIcu,viscDef, domainSettings->ipo, CHANNEL_FLOAT ); // NDEB if(channelDomainViscosity) { for(i=0; i<allchannelSize; i++) { channelDomainViscosity[i*2+0] = calcViscosity * channelDomainViscosity[i*2+0]; } } fluidsimInitChannel(scene, &channelDomainGravity, allchannelSize, timeAtFrame, gravIcu,gravDef, domainSettings->ipo, CHANNEL_VEC ); } // domain channel init // init obj movement channels channelObjCount=0; for(base=scene->base.first; base; base= base->next) { FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim); obit = base->object; if( fluidmdtmp && (obit->type==OB_MESH) && (fluidmdtmp->fss->type != OB_FLUIDSIM_DOMAIN) && // if has to match 3 places! // CHECKMATCH (fluidmdtmp->fss->type != OB_FLUIDSIM_PARTICLE) ) { // cant use fluidsimInitChannel for obj channels right now, due // to the special DXXX channels, and the rotation specialities IpoCurve *icuex[3][3]; //IpoCurve *par_icuex[3][3]; #if 0 int icuIds[3][3] = { {OB_LOC_X, OB_LOC_Y, OB_LOC_Z}, {OB_ROT_X, OB_ROT_Y, OB_ROT_Z}, {OB_SIZE_X, OB_SIZE_Y, OB_SIZE_Z} }; int icudIds[3][3] = { {OB_DLOC_X, OB_DLOC_Y, OB_DLOC_Z}, {OB_DROT_X, OB_DROT_Y, OB_DROT_Z}, {OB_DSIZE_X, OB_DSIZE_Y, OB_DSIZE_Z} }; #endif // relative ipos IpoCurve *icudex[3][3]; //IpoCurve *par_icudex[3][3]; int j,k; float vals[3] = {0.0,0.0,0.0}; int o = channelObjCount; int inivelIcu[3] = { FLUIDSIM_VEL_X, FLUIDSIM_VEL_Y, FLUIDSIM_VEL_Z }; float inivelDefs[3]; int activeIcu[1] = { FLUIDSIM_ACTIVE }; float activeDefs[1] = { 1 }; // default to on inivelDefs[0] = fluidmdtmp->fss->iniVelx; inivelDefs[1] = fluidmdtmp->fss->iniVely; inivelDefs[2] = fluidmdtmp->fss->iniVelz; // check & init loc,rot,size for(j=0; j<3; j++) { for(k=0; k<3; k++) { // XXX prevent invalid memory access until this works icuex[j][k]= NULL; icudex[j][k]= NULL; // XXX icuex[j][k] = find_ipocurve(obit->ipo, icuIds[j][k] ); // XXX icudex[j][k] = find_ipocurve(obit->ipo, icudIds[j][k] ); // XXX lines below were already disabled! //if(obit->parent) { //par_icuex[j][k] = find_ipocurve(obit->parent->ipo, icuIds[j][k] ); //par_icudex[j][k] = find_ipocurve(obit->parent->ipo, icudIds[j][k] ); //} } } for(j=0; j<3; j++) { channelObjMove[o][j] = MEM_callocN( allchannelSize*4*sizeof(float), "fluidsiminit_objmovchannel"); for(i=1; i<=allchannelSize; i++) { for(k=0; k<3; k++) { if(icuex[j][k]) { // IPO exists, use it ... // XXX calc_icu(icuex[j][k], aniFrlen*((float)i) ); vals[k] = icuex[j][k]->curval; if(obit->parent) { // add parent transform, multiply scaling, add trafo&rot //calc_icu(par_icuex[j][k], aniFrlen*((float)i) ); //if(j==2) { vals[k] *= par_icuex[j][k]->curval; } //else { vals[k] += par_icuex[j][k]->curval; } } } else { // use defaults from static values float setval=0.0; if(j==0) { setval = obit->loc[k]; if(obit->parent){ setval += obit->parent->loc[k]; } } else if(j==1) { setval = ( 180.0*obit->rot[k] )/( 10.0*M_PI ); if(obit->parent){ setval = ( 180.0*(obit->rot[k]+obit->parent->rot[k]) )/( 10.0*M_PI ); } } else { setval = obit->size[k]; if(obit->parent){ setval *= obit->parent->size[k]; } } vals[k] = setval; } if(icudex[j][k]) { // XXX calc_icu(icudex[j][k], aniFrlen*((float)i) ); //vals[k] += icudex[j][k]->curval; // add transform, multiply scaling, add trafo&rot if(j==2) { vals[k] *= icudex[j][k]->curval; } else { vals[k] += icudex[j][k]->curval; } if(obit->parent) { // add parent transform, multiply scaling, add trafo&rot //calc_icu(par_icuex[j][k], aniFrlen*((float)i) ); //if(j==2) { vals[k] *= par_icudex[j][k]->curval; } //else { vals[k] += par_icudex[j][k]->curval; } } } } // k for(k=0; k<3; k++) { float set = vals[k]; if(j==1) { // rot is downscaled by 10 for ipo !? set = 360.0 - (10.0*set); } channelObjMove[o][j][(i-1)*4 + k] = set; } // k channelObjMove[o][j][(i-1)*4 + 3] = timeAtFrame[i]; } } { int attrFSIcu[1] = { FLUIDSIM_ATTR_FORCE_STR }; int attrFRIcu[1] = { FLUIDSIM_ATTR_FORCE_RADIUS }; int velFSIcu[1] = { FLUIDSIM_VEL_FORCE_STR }; int velFRIcu[1] = { FLUIDSIM_VEL_FORCE_RADIUS }; float attrFSDefs[1]; float attrFRDefs[1]; float velFSDefs[1]; float velFRDefs[1]; attrFSDefs[0] = fluidmdtmp->fss->attractforceStrength; attrFRDefs[0] = fluidmdtmp->fss->attractforceRadius; velFSDefs[0] = fluidmdtmp->fss->velocityforceStrength; velFRDefs[0] = fluidmdtmp->fss->velocityforceRadius; fluidsimInitChannel(scene, &channelAttractforceStrength[o], allchannelSize, timeAtFrame, attrFSIcu,attrFSDefs, fluidmdtmp->fss->ipo, CHANNEL_FLOAT ); fluidsimInitChannel(scene, &channelAttractforceRadius[o], allchannelSize, timeAtFrame, attrFRIcu,attrFRDefs, fluidmdtmp->fss->ipo, CHANNEL_FLOAT ); fluidsimInitChannel(scene, &channelVelocityforceStrength[o], allchannelSize, timeAtFrame, velFSIcu,velFSDefs, fluidmdtmp->fss->ipo, CHANNEL_FLOAT ); fluidsimInitChannel(scene, &channelVelocityforceRadius[o], allchannelSize, timeAtFrame, velFRIcu,velFRDefs, fluidmdtmp->fss->ipo, CHANNEL_FLOAT ); } fluidsimInitChannel(scene, &channelObjInivel[o], allchannelSize, timeAtFrame, inivelIcu,inivelDefs, fluidmdtmp->fss->ipo, CHANNEL_VEC ); fluidsimInitChannel(scene, &channelObjActive[o], allchannelSize, timeAtFrame, activeIcu,activeDefs, fluidmdtmp->fss->ipo, CHANNEL_FLOAT ); channelObjCount++; } } // init trafo matrix Mat4CpyMat4(domainMat, fsDomain->obmat); if(!Mat4Invert(invDomMat, domainMat)) { snprintf(debugStrBuffer,256,"fluidsimBake::error - Invalid obj matrix?\n"); elbeemDebugOut(debugStrBuffer); BKE_report(reports, RPT_ERROR, "Invalid object matrix."); // FIXME add fatal msg FS_FREE_CHANNELS; return 0; } // -------------------------------------------------------------------------------------------- // start writing / exporting strcpy(targetFile, targetDir); strcat(targetFile, suffixConfig); if(!doExportOnly) { strcat(targetFile,".tmp"); } // dont overwrite/delete original file // make sure these directories exist as well if(outStringsChanged) { BLI_make_existing_file(targetFile); } if(!doExportOnly) { ListBase threads; // perform simulation with El'Beem api and threads elbeemSimulationSettings fsset; elbeemResetSettings(&fsset); fsset.version = 1; // setup global settings for(i=0 ; i<3; i++) fsset.geoStart[i] = bbStart[i]; for(i=0 ; i<3; i++) fsset.geoSize[i] = bbSize[i]; // simulate with 50^3 fsset.resolutionxyz = (int)domainSettings->resolutionxyz; fsset.previewresxyz = (int)domainSettings->previewresxyz; // 10cm water domain fsset.realsize = domainSettings->realsize; fsset.viscosity = calcViscosity; // earth gravity fsset.gravity[0] = domainSettings->gravx; fsset.gravity[1] = domainSettings->gravy; fsset.gravity[2] = domainSettings->gravz; // simulate 5 frames, each 0.03 seconds, output to ./apitest_XXX.bobj.gz fsset.animStart = domainSettings->animStart; fsset.aniFrameTime = aniFrameTime; fsset.noOfFrames = noFrames; // is otherwise subtracted in parser strcpy(targetFile, targetDir); strcat(targetFile, suffixSurface); // defaults for compressibility and adaptive grids fsset.gstar = domainSettings->gstar; fsset.maxRefine = domainSettings->maxRefine; // check <-> gridlevels fsset.generateParticles = domainSettings->generateParticles; fsset.numTracerParticles = domainSettings->generateTracers; fsset.surfaceSmoothing = domainSettings->surfaceSmoothing; fsset.surfaceSubdivs = domainSettings->surfaceSubdivs; fsset.farFieldSize = domainSettings->farFieldSize; strcpy( fsset.outputPath, targetFile); // domain channels fsset.channelSizeFrameTime = fsset.channelSizeViscosity = fsset.channelSizeGravity = allchannelSize; fsset.channelFrameTime = channelDomainTime; fsset.channelViscosity = channelDomainViscosity; fsset.channelGravity = channelDomainGravity; fsset.runsimCallback = &runSimulationCallback; fsset.runsimUserData = &fsset; if( (domainSettings->typeFlags&OB_FSBND_NOSLIP)) fsset.domainobsType = FLUIDSIM_OBSTACLE_NOSLIP; else if((domainSettings->typeFlags&OB_FSBND_PARTSLIP)) fsset.domainobsType = FLUIDSIM_OBSTACLE_PARTSLIP; else if((domainSettings->typeFlags&OB_FSBND_FREESLIP)) fsset.domainobsType = FLUIDSIM_OBSTACLE_FREESLIP; fsset.domainobsPartslip = domainSettings->partSlipValue; fsset.generateVertexVectors = (domainSettings->domainNovecgen==0); // init blender trafo matrix // fprintf(stderr,"elbeemInit - mpTrafo:\n"); { int j; for(i=0; i<4; i++) { for(j=0; j<4; j++) { fsset.surfaceTrafo[i*4+j] = invDomMat[j][i]; // fprintf(stderr,"elbeemInit - mpTrafo %d %d = %f (%d) \n", i,j, fsset.surfaceTrafo[i*4+j] , (i*4+j) ); } } } // init solver with settings elbeemInit(); elbeemAddDomain(&fsset); // init objects channelObjCount = 0; for(base=scene->base.first; base; base= base->next) { FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(base->object, eModifierType_Fluidsim); obit = base->object; //{ snprintf(debugStrBuffer,256,"DEBUG object name=%s, type=%d ...\n", obit->id.name, obit->type); elbeemDebugOut(debugStrBuffer); } // DEBUG if( fluidmdtmp && // if has to match 3 places! // CHECKMATCH (obit->type==OB_MESH) && (fluidmdtmp->fss->type != OB_FLUIDSIM_DOMAIN) && (fluidmdtmp->fss->type != OB_FLUIDSIM_PARTICLE)) { float *verts=NULL; int *tris=NULL; int numVerts=0, numTris=0; int o = channelObjCount; int deform = (fluidmdtmp->fss->domainNovecgen); // misused value // todo - use blenderInitElbeemMesh int modifierIndex = modifiers_indexInObject(obit, (ModifierData *)fluidmdtmp); elbeemMesh fsmesh; elbeemResetMesh( &fsmesh ); fsmesh.type = fluidmdtmp->fss->type; // get name of object for debugging solver fsmesh.name = obit->id.name; initElbeemMesh(scene, obit, &numVerts, &verts, &numTris, &tris, 0, modifierIndex); fsmesh.numVertices = numVerts; fsmesh.numTriangles = numTris; fsmesh.vertices = verts; fsmesh.triangles = tris; fsmesh.channelSizeTranslation = fsmesh.channelSizeRotation = fsmesh.channelSizeScale = fsmesh.channelSizeInitialVel = fsmesh.channelSizeActive = allchannelSize; fsmesh.channelTranslation = channelObjMove[o][0]; fsmesh.channelRotation = channelObjMove[o][1]; fsmesh.channelScale = channelObjMove[o][2]; fsmesh.channelActive = channelObjActive[o]; if( (fsmesh.type == OB_FLUIDSIM_FLUID) || (fsmesh.type == OB_FLUIDSIM_INFLOW)) { fsmesh.channelInitialVel = channelObjInivel[o]; fsmesh.localInivelCoords = ((fluidmdtmp->fss->typeFlags&OB_FSINFLOW_LOCALCOORD)?1:0); } if( (fluidmdtmp->fss->typeFlags&OB_FSBND_NOSLIP)) fsmesh.obstacleType = FLUIDSIM_OBSTACLE_NOSLIP; else if((fluidmdtmp->fss->typeFlags&OB_FSBND_PARTSLIP)) fsmesh.obstacleType = FLUIDSIM_OBSTACLE_PARTSLIP; else if((fluidmdtmp->fss->typeFlags&OB_FSBND_FREESLIP)) fsmesh.obstacleType = FLUIDSIM_OBSTACLE_FREESLIP; fsmesh.obstaclePartslip = fluidmdtmp->fss->partSlipValue; fsmesh.volumeInitType = fluidmdtmp->fss->volumeInitType; fsmesh.obstacleImpactFactor = fluidmdtmp->fss->surfaceSmoothing; // misused value if(fsmesh.type == OB_FLUIDSIM_CONTROL) { // control fluids will get exported as whole deform = 1; fsmesh.cpsTimeStart = fluidmdtmp->fss->cpsTimeStart; fsmesh.cpsTimeEnd = fluidmdtmp->fss->cpsTimeEnd; fsmesh.cpsQuality = fluidmdtmp->fss->cpsQuality; fsmesh.obstacleType = (fluidmdtmp->fss->flag & OB_FLUIDSIM_REVERSE); fsmesh.channelSizeAttractforceRadius = fsmesh.channelSizeVelocityforceStrength = fsmesh.channelSizeVelocityforceRadius = fsmesh.channelSizeAttractforceStrength = allchannelSize; fsmesh.channelAttractforceStrength = channelAttractforceStrength[o]; fsmesh.channelAttractforceRadius = channelAttractforceRadius[o]; fsmesh.channelVelocityforceStrength = channelVelocityforceStrength[o]; fsmesh.channelVelocityforceRadius = channelVelocityforceRadius[o]; } else { // set channels to 0 fsmesh.channelAttractforceStrength = fsmesh.channelAttractforceRadius = fsmesh.channelVelocityforceStrength = fsmesh.channelVelocityforceRadius = NULL; } // animated meshes if(deform) { fsmesh.channelSizeVertices = allchannelSize; fluidsimInitMeshChannel(C, &fsmesh.channelVertices, allchannelSize, obit, numVerts, timeAtFrame, modifierIndex); scene->r.cfra = startFrame; ED_update_for_newframe(C, 1); // remove channels fsmesh.channelTranslation = fsmesh.channelRotation = fsmesh.channelScale = NULL; } elbeemAddMesh(&fsmesh); if(verts) MEM_freeN(verts); if(tris) MEM_freeN(tris); if(fsmesh.channelVertices) MEM_freeN(fsmesh.channelVertices); channelObjCount++; } // valid mesh } // objects //domainSettings->type = OB_FLUIDSIM_DOMAIN; // enable for bake display again // set to neutral, -1 means user abort, -2 means init error globalBakeState = 0; globalBakeFrame = 0; BLI_init_threads(&threads, fluidsimSimulateThread, 1); BLI_insert_thread(&threads, targetFile); { int done = 0; float noFramesf = (float)noFrames; float percentdone = 0.0; int lastRedraw = -1; g_break= 0; G.afbreek= 0; /* blender_test_break uses this global */ start_progress_bar(); while(done==0) { char busy_mess[80]; waitcursor(1); // lukep we add progress bar as an interim mesure percentdone = globalBakeFrame / noFramesf; sprintf(busy_mess, "baking fluids %d / %d |||", globalBakeFrame, (int) noFramesf); progress_bar(percentdone, busy_mess ); // longer delay to prevent frequent redrawing PIL_sleep_ms(2000); BLI_lock_thread(LOCK_CUSTOM1); if(globalBakeState != 0) done = 1; // 1=ok, <0=error/abort BLI_unlock_thread(LOCK_CUSTOM1); if (!G.background) { g_break= blender_test_break(); if(g_break) { // abort... BLI_lock_thread(LOCK_CUSTOM1); if(domainSettings) domainSettings->lastgoodframe = startFrame+globalBakeFrame; done = -1; globalBakeFrame = 0; globalBakeState = -1; simAborted = 1; BLI_unlock_thread(LOCK_CUSTOM1); break; } } // redraw the 3D for showing progress once in a while... if(lastRedraw!=globalBakeFrame) { #if 0 ScrArea *sa; scene->r.cfra = startFrame+globalBakeFrame; lastRedraw = globalBakeFrame; ED_update_for_newframe(C, 1); sa= G.curscreen->areabase.first; while(sa) { if(sa->spacetype == SPACE_VIEW3D) { scrarea_do_windraw(sa); } sa= sa->next; } screen_swapbuffers(); #endif } // redraw } end_progress_bar(); } BLI_end_threads(&threads); } // El'Beem API init, thread creation // -------------------------------------------------------------------------------------------- else { // write config file to be run with command line simulator BKE_report(reports, RPT_WARNING, "Config file export not supported."); } // config file export done! // -------------------------------------------------------------------------------------------- FS_FREE_CHANNELS; // go back to "current" blender time waitcursor(0); if(globalBakeState >= 0) { if(domainSettings) domainSettings->lastgoodframe = startFrame+globalBakeFrame; } scene->r.cfra = origFrame; ED_update_for_newframe(C, 1); if(!simAborted) { char elbeemerr[256]; // check if some error occurred if(globalBakeState==-2) { elbeemGetErrorString(elbeemerr); BKE_reportf(reports, RPT_ERROR, "Failed to initialize [Msg: %s]", elbeemerr); return 0; } // init error } // elbeemFree(); return 1; }