void WorkScheduler::start(CompositorContext &context)
{
#if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
	unsigned int index;
	g_cpuqueue = BLI_thread_queue_init();
	BLI_init_threads(&g_cputhreads, thread_execute_cpu, g_cpudevices.size());
	for (index = 0; index < g_cpudevices.size(); index++) {
		Device *device = g_cpudevices[index];
		BLI_insert_thread(&g_cputhreads, device);
	}
#ifdef COM_OPENCL_ENABLED
	if (context.getHasActiveOpenCLDevices()) {
		g_gpuqueue = BLI_thread_queue_init();
		BLI_init_threads(&g_gputhreads, thread_execute_gpu, g_gpudevices.size());
		for (index = 0; index < g_gpudevices.size(); index++) {
			Device *device = g_gpudevices[index];
			BLI_insert_thread(&g_gputhreads, device);
		}
		g_openclActive = true;
	}
	else {
		g_openclActive = false;
	}
#endif
#endif
}
示例#2
0
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);
}
示例#3
0
static void start_prefetch_threads(MovieClip *clip, int start_frame, int current_frame, int end_frame,
                                   short render_size, short render_flag, short *stop, short *do_update,
                                   float *progress)
{
	ListBase threads;
	PrefetchQueue queue;
	PrefetchThread *handles;
	int tot_thread = BLI_system_thread_count();
	int i;

	/* reserve one thread for the interface */
	if (tot_thread > 1)
		tot_thread--;

	/* initialize queue */
	BLI_spin_init(&queue.spin);

	queue.current_frame = current_frame;
	queue.initial_frame = current_frame;
	queue.start_frame = start_frame;
	queue.end_frame = end_frame;
	queue.render_size = render_size;
	queue.render_flag = render_flag;
	queue.direction = 1;

	queue.stop = stop;
	queue.do_update = do_update;
	queue.progress = progress;

	/* fill in thread handles */
	handles = MEM_callocN(sizeof(PrefetchThread) * tot_thread, "prefetch threaded handles");

	if (tot_thread > 1)
		BLI_init_threads(&threads, do_prefetch_thread, tot_thread);

	for (i = 0; i < tot_thread; i++) {
		PrefetchThread *handle = &handles[i];

		handle->clip = clip;
		handle->queue = &queue;

		if (tot_thread > 1)
			BLI_insert_thread(&threads, handle);
	}

	/* run the threads */
	if (tot_thread > 1)
		BLI_end_threads(&threads);
	else
		do_prefetch_thread(handles);

	MEM_freeN(handles);
}
示例#4
0
文件: wm_jobs.c 项目: 244xiao/blender
/* if different owner starts existing startjob, it suspends itself */
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
{
	if (wm_job->running) {
		/* signal job to end and restart */
		wm_job->stop = TRUE;
		// printf("job started a running job, ending... %s\n", wm_job->name);
	}
	else {
		
		if (wm_job->customdata && wm_job->startjob) {
			
			wm_jobs_test_suspend_stop(wm, wm_job);
			
			if (wm_job->suspended == FALSE) {
				/* copy to ensure proper free in end */
				wm_job->run_customdata = wm_job->customdata;
				wm_job->run_free = wm_job->free;
				wm_job->free = NULL;
				wm_job->customdata = NULL;
				wm_job->running = TRUE;
				
				if (wm_job->initjob)
					wm_job->initjob(wm_job->run_customdata);
				
				wm_job->stop = FALSE;
				wm_job->ready = FALSE;
				wm_job->progress = 0.0;

				// printf("job started: %s\n", wm_job->name);
				
				BLI_init_threads(&wm_job->threads, do_job_thread, 1);
				BLI_insert_thread(&wm_job->threads, wm_job);
			}
			
			/* restarted job has timer already */
			if (wm_job->wt == NULL)
				wm_job->wt = WM_event_add_timer(wm, wm_job->win, TIMERJOBS, wm_job->timestep);

			if (G.debug & G_DEBUG_JOBS)
				wm_job->start_time = PIL_check_seconds_timer();
		}
		else {
			printf("job fails, not initialized\n");
		}
	}
}
示例#5
0
/* if different owner starts existing startjob, it suspends itself */
void WM_jobs_start(wmWindowManager *wm, wmJob *steve)
{
	if(steve->running) {
		/* signal job to end and restart */
		steve->stop= 1;
		// printf("job started a running job, ending... %s\n", steve->name);
	}
	else {
		
		if(steve->customdata && steve->startjob) {
			
			wm_jobs_test_suspend_stop(wm, steve);
			
			if(steve->suspended==0) {
				/* copy to ensure proper free in end */
				steve->run_customdata= steve->customdata;
				steve->run_free= steve->free;
				steve->free= NULL;
				steve->customdata= NULL;
				steve->running= 1;
				
				if(steve->initjob)
					steve->initjob(steve->run_customdata);
				
				steve->stop= 0;
				steve->ready= 0;
				steve->progress= 0.0;

				// printf("job started: %s\n", steve->name);
				
				BLI_init_threads(&steve->threads, do_job_thread, 1);
				BLI_insert_thread(&steve->threads, steve);
			}
			
			/* restarted job has timer already */
			if(steve->wt==NULL)
				steve->wt= WM_event_add_timer(wm, steve->win, TIMERJOBS, steve->timestep);
		}
		else printf("job fails, not initialized\n");
	}
}
示例#6
0
// start thread to cache video frame from file/capture/stream
// this function should be called only when the position in the stream is set for the
// first frame to cache
bool VideoFFmpeg::startCache()
{
	if (!m_cacheStarted && m_isThreaded)
	{
		m_stopThread = false;
		for (int i=0; i<CACHE_FRAME_SIZE; i++)
		{
			CacheFrame *frame = new CacheFrame();
			frame->frame = allocFrameRGB();
			BLI_addtail(&m_frameCacheFree, frame);
		}
		for (int i=0; i<CACHE_PACKET_SIZE; i++) 
		{
			CachePacket *packet = new CachePacket();
			BLI_addtail(&m_packetCacheFree, packet);
		}
		BLI_init_threads(&m_thread, cacheThread, 1);
		BLI_insert_thread(&m_thread, this);
		m_cacheStarted = true;
	}
	return m_cacheStarted;
}
/* 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);
}
示例#8
0
static void do_multires_bake(MultiresBakeRender *bkr, Image *ima, int require_tangent, MPassKnownData passKnownData,
                             MInitBakeData initBakeData, MApplyBakeData applyBakeData, MFreeBakeData freeBakeData)
{
	DerivedMesh *dm = bkr->lores_dm;
	const int lvl = bkr->lvl;
	const int tot_face = dm->getNumTessFaces(dm);

	if (tot_face > 0) {
		MultiresBakeThread *handles;
		MultiresBakeQueue queue;

		ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
		MVert *mvert = dm->getVertArray(dm);
		MFace *mface = dm->getTessFaceArray(dm);
		MTFace *mtface = dm->getTessFaceDataArray(dm, CD_MTFACE);
		float *precomputed_normals = dm->getTessFaceDataArray(dm, CD_NORMAL);
		float *pvtangent = NULL;

		ListBase threads;
		int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count();

		void *bake_data = NULL;

		if (require_tangent) {
			if (CustomData_get_layer_index(&dm->faceData, CD_TANGENT) == -1)
				DM_add_tangent_layer(dm);

			pvtangent = DM_get_tessface_data_layer(dm, CD_TANGENT);
		}

		/* all threads shares the same custom bake data */
		if (initBakeData)
			bake_data = initBakeData(bkr, ima);

		if (tot_thread > 1)
			BLI_init_threads(&threads, do_multires_bake_thread, tot_thread);

		handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles");

		/* faces queue */
		queue.cur_face = 0;
		queue.tot_face = tot_face;
		BLI_spin_init(&queue.spin);

		/* fill in threads handles */
		for (i = 0; i < tot_thread; i++) {
			MultiresBakeThread *handle = &handles[i];

			handle->bkr = bkr;
			handle->image = ima;
			handle->queue = &queue;

			handle->data.mface = mface;
			handle->data.mvert = mvert;
			handle->data.mtface = mtface;
			handle->data.pvtangent = pvtangent;
			handle->data.precomputed_normals = precomputed_normals;  /* don't strictly need this */
			handle->data.w = ibuf->x;
			handle->data.h = ibuf->y;
			handle->data.lores_dm = dm;
			handle->data.hires_dm = bkr->hires_dm;
			handle->data.lvl = lvl;
			handle->data.pass_data = passKnownData;
			handle->data.bake_data = bake_data;
			handle->data.ibuf = ibuf;

			init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel);

			if (tot_thread > 1)
				BLI_insert_thread(&threads, handle);
		}

		/* run threads */
		if (tot_thread > 1)
			BLI_end_threads(&threads);
		else
			do_multires_bake_thread(&handles[0]);

		BLI_spin_end(&queue.spin);

		/* finalize baking */
		if (applyBakeData)
			applyBakeData(bake_data);

		if (freeBakeData)
			freeBakeData(bake_data);

		BKE_image_release_ibuf(ima, ibuf, NULL);
	}
}
示例#9
0
/* 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);
	}
}
示例#10
0
/* 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;
}
示例#11
0
static void do_sequence_proxy(void *pjv, int *build_sizes, int build_count,
                              int *build_undistort_sizes, int build_undistort_count,
                              short *stop, short *do_update, float *progress)
{
	ProxyJob *pj = pjv;
	MovieClip *clip = pj->clip;
	Scene *scene = pj->scene;
	int sfra = SFRA, efra = EFRA;
	ProxyThread *handles;
	ListBase threads;
	int i, tot_thread = BLI_system_thread_count();
	ProxyQueue queue;

	BLI_spin_init(&queue.spin);

	queue.cfra = sfra;
	queue.sfra = sfra;
	queue.efra = efra;
	queue.stop = stop;
	queue.do_update = do_update;
	queue.progress = progress;

	handles = MEM_callocN(sizeof(ProxyThread) * tot_thread, "proxy threaded handles");

	if (tot_thread > 1)
		BLI_init_threads(&threads, do_proxy_thread, tot_thread);

	for (i = 0; i < tot_thread; i++) {
		ProxyThread *handle = &handles[i];

		handle->clip = clip;
		handle->queue = &queue;

		handle->build_count = build_count;
		handle->build_sizes = build_sizes;

		handle->build_undistort_count = build_undistort_count;
		handle->build_undistort_sizes = build_undistort_sizes;

		if (build_undistort_count)
			handle->distortion = BKE_tracking_distortion_new();

		if (tot_thread > 1)
			BLI_insert_thread(&threads, handle);
	}

	if (tot_thread > 1)
		BLI_end_threads(&threads);
	else
		do_proxy_thread(handles);

	MEM_freeN(handles);

	if (build_undistort_count) {
		for (i = 0; i < tot_thread; i++) {
			ProxyThread *handle = &handles[i];

			BKE_tracking_distortion_free(handle->distortion);
		}
	}
}
示例#12
0
static void occ_build_recursive(OcclusionTree *tree, OccNode *node, int begin, int end, int depth)
{
	ListBase threads;
	OcclusionBuildThread othreads[BLENDER_MAX_THREADS];
	OccNode *child, tmpnode;
	/* OccFace *face; */
	int a, b, totthread = 0, offset[TOTCHILD], count[TOTCHILD];

	/* add a new node */
	node->occlusion = 1.0f;

	/* leaf node with only children */
	if (end - begin <= TOTCHILD) {
		for (a = begin, b = 0; a < end; a++, b++) {
			/* face= &tree->face[a]; */
			node->child[b].face = a;
			node->childflag |= (1 << b);
		}
	}
	else {
		/* order faces */
		occ_build_8_split(tree, begin, end, offset, count);

		if (depth == 1 && tree->dothreadedbuild)
			BLI_init_threads(&threads, exec_occ_build, tree->totbuildthread);

		for (b = 0; b < TOTCHILD; b++) {
			if (count[b] == 0) {
				node->child[b].node = NULL;
			}
			else if (count[b] == 1) {
				/* face= &tree->face[offset[b]]; */
				node->child[b].face = offset[b];
				node->childflag |= (1 << b);
			}
			else {
				if (tree->dothreadedbuild)
					BLI_lock_thread(LOCK_CUSTOM1);

				child = BLI_memarena_alloc(tree->arena, sizeof(OccNode));
				node->child[b].node = child;

				/* keep track of maximum depth for stack */
				if (depth >= tree->maxdepth)
					tree->maxdepth = depth + 1;

				if (tree->dothreadedbuild)
					BLI_unlock_thread(LOCK_CUSTOM1);

				if (depth == 1 && tree->dothreadedbuild) {
					othreads[totthread].tree = tree;
					othreads[totthread].node = child;
					othreads[totthread].begin = offset[b];
					othreads[totthread].end = offset[b] + count[b];
					othreads[totthread].depth = depth + 1;
					BLI_insert_thread(&threads, &othreads[totthread]);
					totthread++;
				}
				else
					occ_build_recursive(tree, child, offset[b], offset[b] + count[b], depth + 1);
			}
		}

		if (depth == 1 && tree->dothreadedbuild)
			BLI_end_threads(&threads);
	}

	/* combine area, position and sh */
	for (b = 0; b < TOTCHILD; b++) {
		if (node->childflag & (1 << b)) {
			child = &tmpnode;
			occ_node_from_face(tree->face + node->child[b].face, &tmpnode);
		}
		else {
			child = node->child[b].node;
		}

		if (child) {
			node->area += child->area;
			sh_add(node->sh, node->sh, child->sh);
			madd_v3_v3fl(node->co, child->co, child->area);
		}
	}

	if (node->area != 0.0f)
		mul_v3_fl(node->co, 1.0f / node->area);

	/* compute maximum distance from center */
	node->dco = 0.0f;
	if (node->area > 0.0f)
		occ_build_dco(tree, node, node->co, &node->dco);
}
示例#13
0
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;
}