2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): Matt Ebb, Ra˙l Fern·ndez Hern·ndez (Farsthary).
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/render/intern/source/volume_precache.c
38 #include "MEM_guardedalloc.h"
40 #include "BLI_blenlib.h"
42 #include "BLI_threads.h"
43 #include "BLI_voxel.h"
44 #include "BLI_utildefines.h"
48 #include "RE_shader_ext.h"
50 #include "DNA_material_types.h"
52 #include "rayintersection.h"
53 #include "rayobject.h"
54 #include "render_types.h"
55 #include "rendercore.h"
56 #include "renderdatabase.h"
57 #include "volumetric.h"
58 #include "volume_precache.h"
60 #include "BKE_global.h"
62 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
63 /* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
64 /* only to be used here in this file, it's for speed */
65 extern struct Render R;
66 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
68 /* *** utility code to set up an individual raytree for objectinstance, for checking inside/outside *** */
70 /* Recursive test for intersections, from a point inside the mesh, to outside
71 * Number of intersections (depth) determine if a point is inside or outside the mesh */
72 static int intersect_outside_volume(RayObject *tree, Isect *isect, float *offset, int limit, int depth)
74 if (limit == 0) return depth;
76 if (RE_rayobject_raycast(tree, isect)) {
78 isect->start[0] = isect->start[0] + isect->dist*isect->dir[0];
79 isect->start[1] = isect->start[1] + isect->dist*isect->dir[1];
80 isect->start[2] = isect->start[2] + isect->dist*isect->dir[2];
82 isect->dist = FLT_MAX;
83 isect->skip = RE_SKIP_VLR_NEIGHBOUR;
84 isect->orig.face= isect->hit.face;
85 isect->orig.ob= isect->hit.ob;
87 return intersect_outside_volume(tree, isect, offset, limit-1, depth+1);
94 /* Uses ray tracing to check if a point is inside or outside an ObjectInstanceRen */
95 static int point_inside_obi(RayObject *tree, ObjectInstanceRen *UNUSED(obi), float *co)
98 float dir[3] = {0.0f,0.0f,1.0f};
99 int final_depth=0, depth=0, limit=20;
101 /* set up the isect */
102 copy_v3_v3(isect.start, co);
103 copy_v3_v3(isect.dir, dir);
104 isect.mode= RE_RAY_MIRROR;
105 isect.last_hit= NULL;
108 isect.dist = FLT_MAX;
109 isect.orig.face= NULL;
110 isect.orig.ob = NULL;
112 final_depth = intersect_outside_volume(tree, &isect, dir, limit, depth);
114 /* even number of intersections: point is outside
115 * odd number: point is inside */
116 if (final_depth % 2 == 0) return 0;
120 /* find the bounding box of an objectinstance in global space */
121 void global_bounds_obi(Render *re, ObjectInstanceRen *obi, float *bbmin, float *bbmax)
123 ObjectRen *obr = obi->obr;
124 VolumePrecache *vp = obi->volume_precache;
129 if (vp->bbmin != NULL && vp->bbmax != NULL) {
130 copy_v3_v3(bbmin, vp->bbmin);
131 copy_v3_v3(bbmax, vp->bbmax);
135 vp->bbmin = MEM_callocN(sizeof(float)*3, "volume precache min boundbox corner");
136 vp->bbmax = MEM_callocN(sizeof(float)*3, "volume precache max boundbox corner");
138 INIT_MINMAX(bbmin, bbmax);
140 for (a=0; a<obr->totvert; a++) {
141 if ((a & 255)==0) ver= obr->vertnodes[a>>8].vert;
144 copy_v3_v3(co, ver->co);
146 /* transformed object instance in camera space */
147 if (obi->flag & R_TRANSFORMED)
148 mul_m4_v3(obi->mat, co);
150 /* convert to global space */
151 mul_m4_v3(re->viewinv, co);
153 DO_MINMAX(co, vp->bbmin, vp->bbmax);
156 copy_v3_v3(bbmin, vp->bbmin);
157 copy_v3_v3(bbmax, vp->bbmax);
161 /* *** light cache filtering *** */
163 static float get_avg_surrounds(float *cache, int *res, int xx, int yy, int zz)
165 int x, y, z, x_, y_, z_;
169 for (z=-1; z <= 1; z++) {
171 if (z_ >= 0 && z_ <= res[2]-1) {
173 for (y=-1; y <= 1; y++) {
175 if (y_ >= 0 && y_ <= res[1]-1) {
177 for (x=-1; x <= 1; x++) {
179 if (x_ >= 0 && x_ <= res[0]-1) {
180 const int i= V_I(x_, y_, z_, res);
182 if (cache[i] > 0.0f) {
194 if (added > 0) tot /= added;
199 /* function to filter the edges of the light cache, where there was no volume originally.
200 * For each voxel which was originally external to the mesh, it finds the average values of
201 * the surrounding internal voxels and sets the original external voxel to that average amount.
202 * Works almost a bit like a 'dilate' filter */
203 static void lightcache_filter(VolumePrecache *vp)
207 for (z=0; z < vp->res[2]; z++) {
208 for (y=0; y < vp->res[1]; y++) {
209 for (x=0; x < vp->res[0]; x++) {
210 /* trigger for outside mesh */
211 const int i= V_I(x, y, z, vp->res);
213 if (vp->data_r[i] < -0.f)
214 vp->data_r[i] = get_avg_surrounds(vp->data_r, vp->res, x, y, z);
215 if (vp->data_g[i] < -0.f)
216 vp->data_g[i] = get_avg_surrounds(vp->data_g, vp->res, x, y, z);
217 if (vp->data_b[i] < -0.f)
218 vp->data_b[i] = get_avg_surrounds(vp->data_b, vp->res, x, y, z);
225 static void lightcache_filter2(VolumePrecache *vp)
228 float *new_r, *new_g, *new_b;
229 int field_size = vp->res[0]*vp->res[1]*vp->res[2]*sizeof(float);
231 new_r = MEM_mallocN(field_size, "temp buffer for light cache filter r channel");
232 new_g = MEM_mallocN(field_size, "temp buffer for light cache filter g channel");
233 new_b = MEM_mallocN(field_size, "temp buffer for light cache filter b channel");
235 memcpy(new_r, vp->data_r, field_size);
236 memcpy(new_g, vp->data_g, field_size);
237 memcpy(new_b, vp->data_b, field_size);
239 for (z=0; z < vp->res[2]; z++) {
240 for (y=0; y < vp->res[1]; y++) {
241 for (x=0; x < vp->res[0]; x++) {
242 /* trigger for outside mesh */
243 const int i= V_I(x, y, z, vp->res);
244 if (vp->data_r[i] < -0.f)
245 new_r[i] = get_avg_surrounds(vp->data_r, vp->res, x, y, z);
246 if (vp->data_g[i] < -0.f)
247 new_g[i] = get_avg_surrounds(vp->data_g, vp->res, x, y, z);
248 if (vp->data_b[i] < -0.f)
249 new_b[i] = get_avg_surrounds(vp->data_b, vp->res, x, y, z);
254 SWAP(float *, vp->data_r, new_r);
255 SWAP(float *, vp->data_g, new_g);
256 SWAP(float *, vp->data_b, new_b);
258 if (new_r) { MEM_freeN(new_r); new_r=NULL; }
259 if (new_g) { MEM_freeN(new_g); new_g=NULL; }
260 if (new_b) { MEM_freeN(new_b); new_b=NULL; }
264 BLI_INLINE int ms_I(int x, int y, int z, int *n) //has a pad of 1 voxel surrounding the core for boundary simulation
266 /* different ordering to light cache */
267 return x*(n[1]+2)*(n[2]+2) + y*(n[2]+2) + z;
270 BLI_INLINE int v_I_pad(int x, int y, int z, int *n) //has a pad of 1 voxel surrounding the core for boundary simulation
272 /* same ordering to light cache, with padding */
273 return z*(n[1]+2)*(n[0]+2) + y*(n[0]+2) + x;
276 BLI_INLINE int lc_to_ms_I(int x, int y, int z, int *n)
278 /* converting light cache index to multiple scattering index */
279 return (x-1)*(n[1]*n[2]) + (y-1)*(n[2]) + z-1;
282 /* *** multiple scattering approximation *** */
284 /* get the total amount of light energy in the light cache. used to normalize after multiple scattering */
285 static float total_ss_energy(Render *re, int do_test_break, VolumePrecache *vp)
291 for (z=0; z < res[2]; z++) {
292 for (y=0; y < res[1]; y++) {
293 for (x=0; x < res[0]; x++) {
294 const int i=V_I(x, y, z, res);
296 if (vp->data_r[i] > 0.f) energy += vp->data_r[i];
297 if (vp->data_g[i] > 0.f) energy += vp->data_g[i];
298 if (vp->data_b[i] > 0.f) energy += vp->data_b[i];
302 if (do_test_break && re->test_break(re->tbh)) break;
308 static float total_ms_energy(Render *re, int do_test_break, float *sr, float *sg, float *sb, int *res)
313 for (z=1;z<=res[2];z++) {
314 for (y=1;y<=res[1];y++) {
315 for (x=1;x<=res[0];x++) {
316 const int i = ms_I(x,y,z,res);
318 if (sr[i] > 0.f) energy += sr[i];
319 if (sg[i] > 0.f) energy += sg[i];
320 if (sb[i] > 0.f) energy += sb[i];
324 if (do_test_break && re->test_break(re->tbh)) break;
330 static void ms_diffuse(Render *re, int do_test_break, float *x0, float *x, float diff, int *n) //n is the unpadded resolution
333 const float dt = VOL_MS_TIMESTEP;
334 size_t size = n[0]*n[1]*n[2];
335 const float a = dt*diff*size;
337 for (l=0; l<20; l++) {
338 for (k=1; k<=n[2]; k++) {
339 for (j=1; j<=n[1]; j++) {
340 for (i=1; i<=n[0]; i++) {
341 x[v_I_pad(i,j,k,n)] = (x0[v_I_pad(i,j,k,n)]) + a*( x0[v_I_pad(i-1,j,k,n)]+ x0[v_I_pad(i+1,j,k,n)]+ x0[v_I_pad(i,j-1,k,n)]+
342 x0[v_I_pad(i,j+1,k,n)]+ x0[v_I_pad(i,j,k-1,n)]+x0[v_I_pad(i,j,k+1,n)]
347 if (do_test_break && re->test_break(re->tbh)) break;
350 if (re->test_break(re->tbh)) break;
354 static void multiple_scattering_diffusion(Render *re, VolumePrecache *vp, Material *ma)
356 const float diff = ma->vol.ms_diff * 0.001f; /* compensate for scaling for a nicer UI range */
357 const int simframes = (int)(ma->vol.ms_spread * (float)MAX3(vp->res[0], vp->res[1], vp->res[2]));
358 const int shade_type = ma->vol.shade_type;
359 float fac = ma->vol.ms_intensity;
363 const int size = (n[0]+2)*(n[1]+2)*(n[2]+2);
364 const int do_test_break = (size > 100000);
365 double time, lasttime= PIL_check_seconds_timer();
368 float origf; /* factor for blending in original light cache */
369 float energy_ss, energy_ms;
371 float *sr0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
372 float *sr=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
373 float *sg0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
374 float *sg=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
375 float *sb0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
376 float *sb=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
378 total = (float)(n[0]*n[1]*n[2]*simframes);
380 energy_ss = total_ss_energy(re, do_test_break, vp);
382 /* Scattering as diffusion pass */
383 for (m=0; m<simframes; m++) {
385 for (z=1; z<=n[2]; z++) {
386 for (y=1; y<=n[1]; y++) {
387 for (x=1; x<=n[0]; x++) {
388 const int i = lc_to_ms_I(x, y ,z, n); //lc index
389 const int j = ms_I(x, y, z, n); //ms index
391 time= PIL_check_seconds_timer();
393 if (vp->data_r[i] > 0.0f)
394 sr[j] += vp->data_r[i];
395 if (vp->data_g[i] > 0.0f)
396 sg[j] += vp->data_g[i];
397 if (vp->data_b[i] > 0.0f)
398 sb[j] += vp->data_b[i];
400 /* Displays progress every second */
401 if (time-lasttime>1.0) {
403 BLI_snprintf(str, sizeof(str), "Simulating multiple scattering: %d%%", (int)(100.0f * (c / total)));
405 re->stats_draw(re->sdh, &re->i);
412 if (do_test_break && re->test_break(re->tbh)) break;
415 if (re->test_break(re->tbh)) break;
417 SWAP(float *,sr,sr0);
418 SWAP(float *,sg,sg0);
419 SWAP(float *,sb,sb0);
421 /* main diffusion simulation */
422 ms_diffuse(re, do_test_break, sr0, sr, diff, n);
423 ms_diffuse(re, do_test_break, sg0, sg, diff, n);
424 ms_diffuse(re, do_test_break, sb0, sb, diff, n);
426 if (re->test_break(re->tbh)) break;
429 /* normalization factor to conserve energy */
430 energy_ms = total_ms_energy(re, do_test_break, sr, sg, sb, n);
431 fac *= (energy_ss / energy_ms);
433 /* blend multiple scattering back in the light cache */
434 if (shade_type == MA_VOL_SHADE_SHADEDPLUSMULTIPLE) {
435 /* conserve energy - half single, half multiple */
443 for (z=1;z<=n[2];z++) {
444 for (y=1;y<=n[1];y++) {
445 for (x=1;x<=n[0];x++) {
446 const int i = lc_to_ms_I(x, y ,z, n); //lc index
447 const int j = ms_I(x, y, z, n); //ms index
449 vp->data_r[i] = origf * vp->data_r[i] + fac * sr[j];
450 vp->data_g[i] = origf * vp->data_g[i] + fac * sg[j];
451 vp->data_b[i] = origf * vp->data_b[i] + fac * sb[j];
455 if (do_test_break && re->test_break(re->tbh)) break;
469 static void *vol_precache_part_test(void *data)
471 VolPrecachePart *pa = data;
473 printf("part number: %d\n", pa->num);
474 printf("done: %d\n", pa->done);
475 printf("x min: %d x max: %d\n", pa->minx, pa->maxx);
476 printf("y min: %d y max: %d\n", pa->miny, pa->maxy);
477 printf("z min: %d z max: %d\n", pa->minz, pa->maxz);
483 typedef struct VolPrecacheQueue {
488 /* Iterate over the 3d voxel grid, and fill the voxels with scattering information
490 * It's stored in memory as 3 big float grids next to each other, one for each RGB channel.
491 * I'm guessing the memory alignment may work out better this way for the purposes
492 * of doing linear interpolation, but I haven't actually tested this theory! :)
494 static void *vol_precache_part(void *data)
496 VolPrecacheQueue *queue = (VolPrecacheQueue*)data;
499 while ((pa = BLI_thread_queue_pop(queue->work))) {
500 ObjectInstanceRen *obi = pa->obi;
501 RayObject *tree = pa->tree;
502 ShadeInput *shi = pa->shi;
503 float scatter_col[3] = {0.f, 0.f, 0.f};
504 float co[3], cco[3], view[3];
508 if (pa->re->test_break && pa->re->test_break(pa->re->tbh))
515 for (z= pa->minz; z < pa->maxz; z++) {
516 co[2] = pa->bbmin[2] + (pa->voxel[2] * (z + 0.5f));
518 for (y= pa->miny; y < pa->maxy; y++) {
519 co[1] = pa->bbmin[1] + (pa->voxel[1] * (y + 0.5f));
521 for (x=pa->minx; x < pa->maxx; x++) {
522 co[0] = pa->bbmin[0] + (pa->voxel[0] * (x + 0.5f));
524 if (pa->re->test_break && pa->re->test_break(pa->re->tbh))
527 /* convert from world->camera space for shading */
528 mul_v3_m4v3(cco, pa->viewmat, co);
530 i= V_I(x, y, z, res);
532 // don't bother if the point is not inside the volume mesh
533 if (!point_inside_obi(tree, obi, cco)) {
534 obi->volume_precache->data_r[i] = -1.0f;
535 obi->volume_precache->data_g[i] = -1.0f;
536 obi->volume_precache->data_b[i] = -1.0f;
540 copy_v3_v3(view, cco);
542 vol_get_scattering(shi, scatter_col, cco, view);
544 obi->volume_precache->data_r[i] = scatter_col[0];
545 obi->volume_precache->data_g[i] = scatter_col[1];
546 obi->volume_precache->data_b[i] = scatter_col[2];
552 BLI_thread_queue_push(queue->done, pa);
559 static void precache_setup_shadeinput(Render *re, ObjectInstanceRen *obi, Material *ma, ShadeInput *shi)
561 memset(shi, 0, sizeof(ShadeInput));
566 memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); // note, keep this synced with render_types.h
567 shi->har= shi->mat->har;
573 static void precache_init_parts(Render *re, RayObject *tree, ShadeInput *shi, ObjectInstanceRen *obi, int totthread, int *parts)
575 VolumePrecache *vp = obi->volume_precache;
578 int sizex, sizey, sizez;
579 float bbmin[3], bbmax[3];
587 BLI_freelistN(&re->volume_precache_parts);
589 /* currently we just subdivide the box, number of threads per side */
590 parts[0] = parts[1] = parts[2] = totthread;
593 /* using boundbox in worldspace */
594 global_bounds_obi(re, obi, bbmin, bbmax);
595 sub_v3_v3v3(voxel, bbmax, bbmin);
597 voxel[0] /= (float)res[0];
598 voxel[1] /= (float)res[1];
599 voxel[2] /= (float)res[2];
601 for (x=0; x < parts[0]; x++) {
602 sizex = ceil(res[0] / (float)parts[0]);
605 maxx = (maxx>res[0])?res[0]:maxx;
607 for (y=0; y < parts[1]; y++) {
608 sizey = ceil(res[1] / (float)parts[1]);
611 maxy = (maxy>res[1])?res[1]:maxy;
613 for (z=0; z < parts[2]; z++) {
614 VolPrecachePart *pa= MEM_callocN(sizeof(VolPrecachePart), "new precache part");
616 sizez = ceil(res[2] / (float)parts[2]);
619 maxz = (maxz>res[2])?res[2]:maxz;
626 copy_m4_m4(pa->viewmat, re->viewmat);
628 copy_v3_v3(pa->bbmin, bbmin);
629 copy_v3_v3(pa->voxel, voxel);
630 copy_v3_v3_int(pa->res, res);
632 pa->minx = minx; pa->maxx = maxx;
633 pa->miny = miny; pa->maxy = maxy;
634 pa->minz = minz; pa->maxz = maxz;
637 BLI_addtail(&re->volume_precache_parts, pa);
645 /* calculate resolution from bounding box in world space */
646 static int precache_resolution(Render *re, VolumePrecache *vp, ObjectInstanceRen *obi, int res)
649 float bbmin[3], bbmax[3];
651 /* bound box in global space */
652 global_bounds_obi(re, obi, bbmin, bbmax);
653 sub_v3_v3v3(dim, bbmax, bbmin);
655 div = MAX3(dim[0], dim[1], dim[2]);
660 vp->res[0] = ceil(dim[0] * res);
661 vp->res[1] = ceil(dim[1] * res);
662 vp->res[2] = ceil(dim[2] * res);
664 if ((vp->res[0] < 1) || (vp->res[1] < 1) || (vp->res[2] < 1))
670 /* Precache a volume into a 3D voxel grid.
671 * The voxel grid is stored in the ObjectInstanceRen,
672 * in camera space, aligned with the ObjectRen's bounding box.
673 * Resolution is defined by the user.
675 static void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Material *ma)
682 VolPrecacheQueue queue;
683 int parts[3] = {1, 1, 1}, totparts;
686 int totthread = re->r.threads, thread;
688 double time, lasttime= PIL_check_seconds_timer();
692 /* create a raytree with just the faces of the instanced ObjectRen,
693 * used for checking if the cached point is inside or outside. */
694 tree = makeraytree_object(&R, obi);
697 vp = MEM_callocN(sizeof(VolumePrecache), "volume light cache");
698 obi->volume_precache = vp;
700 if (!precache_resolution(re, vp, obi, ma->vol.precache_resolution)) {
706 vp->data_r = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data red channel");
707 vp->data_g = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data green channel");
708 vp->data_b = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data blue channel");
709 if (vp->data_r==NULL || vp->data_g==NULL || vp->data_b==NULL) {
714 /* Need a shadeinput to calculate scattering */
715 precache_setup_shadeinput(re, obi, ma, &shi);
717 precache_init_parts(re, tree, &shi, obi, totthread, parts);
718 totparts = parts[0] * parts[1] * parts[2];
720 /* setup work and done queues */
721 queue.work = BLI_thread_queue_init();
722 queue.done = BLI_thread_queue_init();
723 BLI_thread_queue_nowait(queue.work);
725 for (pa= re->volume_precache_parts.first; pa; pa= pa->next)
726 BLI_thread_queue_push(queue.work, pa);
729 BLI_init_threads(&threads, vol_precache_part, totthread);
731 for (thread= 0; thread<totthread; thread++)
732 BLI_insert_thread(&threads, &queue);
734 /* loop waiting for work to be done */
735 while (counter < totparts) {
736 if (re->test_break && re->test_break(re->tbh))
739 if (BLI_thread_queue_pop_timeout(queue.done, 50))
742 time= PIL_check_seconds_timer();
743 if (time-lasttime>1.0) {
745 BLI_snprintf(str, sizeof(str), "Precaching volume: %d%%", (int)(100.0f * ((float)counter / (float)totparts)));
747 re->stats_draw(re->sdh, &re->i);
754 BLI_end_threads(&threads);
755 BLI_thread_queue_free(queue.work);
756 BLI_thread_queue_free(queue.done);
757 BLI_freelistN(&re->volume_precache_parts);
760 //TODO: makeraytree_object creates a tree and saves it on OBI, if we free this tree we should also clear other pointers to it
761 //RE_rayobject_free(tree);
765 if (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)) {
766 /* this should be before the filtering */
767 multiple_scattering_diffusion(re, obi->volume_precache, ma);
770 lightcache_filter(obi->volume_precache);
773 static int using_lightcache(Material *ma)
775 return (((ma->vol.shadeflag & MA_VOL_PRECACHESHADING) && (ma->vol.shade_type == MA_VOL_SHADE_SHADED)) ||
776 (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)));
779 /* loop through all objects (and their associated materials)
780 * marked for pre-caching in convertblender.c, and pre-cache them */
781 void volume_precache(Render *re)
783 ObjectInstanceRen *obi;
786 re->i.infostr= "Volume preprocessing";
787 re->stats_draw(re->sdh, &re->i);
789 for (vo= re->volumes.first; vo; vo= vo->next) {
790 if (using_lightcache(vo->ma)) {
791 for (obi= re->instancetable.first; obi; obi= obi->next) {
792 if (obi->obr == vo->obr) {
793 vol_precache_objectinstance_threads(re, obi, vo->ma);
795 if (re->test_break && re->test_break(re->tbh))
800 if (re->test_break && re->test_break(re->tbh))
806 re->stats_draw(re->sdh, &re->i);
809 void free_volume_precache(Render *re)
811 ObjectInstanceRen *obi;
813 for (obi= re->instancetable.first; obi; obi= obi->next) {
814 if (obi->volume_precache != NULL) {
815 MEM_freeN(obi->volume_precache->data_r);
816 MEM_freeN(obi->volume_precache->data_g);
817 MEM_freeN(obi->volume_precache->data_b);
818 MEM_freeN(obi->volume_precache->bbmin);
819 MEM_freeN(obi->volume_precache->bbmax);
820 MEM_freeN(obi->volume_precache);
821 obi->volume_precache = NULL;
825 BLI_freelistN(&re->volumes);
828 int point_inside_volume_objectinstance(Render *re, ObjectInstanceRen *obi, float *co)
833 tree = makeraytree_object(re, obi);
836 inside = point_inside_obi(tree, obi, co);
838 //TODO: makeraytree_object creates a tree and saves it on OBI, if we free this tree we should also clear other pointers to it
839 //RE_rayobject_free(tree);