Blender V2.61 - r43446

volume_precache.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s): Matt Ebb, Ra˙l Fern·ndez Hern·ndez (Farsthary).
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00033 #include <math.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include <float.h>
00037 
00038 #include "MEM_guardedalloc.h"
00039 
00040 #include "BLI_blenlib.h"
00041 #include "BLI_math.h"
00042 #include "BLI_threads.h"
00043 #include "BLI_voxel.h"
00044 #include "BLI_utildefines.h"
00045 
00046 #include "PIL_time.h"
00047 
00048 #include "RE_shader_ext.h"
00049 
00050 #include "DNA_material_types.h"
00051 
00052 #include "rayintersection.h"
00053 #include "rayobject.h"
00054 #include "render_types.h"
00055 #include "rendercore.h"
00056 #include "renderdatabase.h"
00057 #include "volumetric.h"
00058 #include "volume_precache.h"
00059 
00060 #if defined( _MSC_VER ) && !defined( __cplusplus )
00061 # define inline __inline
00062 #endif // defined( _MSC_VER ) && !defined( __cplusplus )
00063 
00064 #include "BKE_global.h"
00065 
00066 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
00067 /* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
00068 /* only to be used here in this file, it's for speed */
00069 extern struct Render R;
00070 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
00071 
00072 /* *** utility code to set up an individual raytree for objectinstance, for checking inside/outside *** */
00073 
00074 /* Recursive test for intersections, from a point inside the mesh, to outside
00075  * Number of intersections (depth) determine if a point is inside or outside the mesh */
00076 static int intersect_outside_volume(RayObject *tree, Isect *isect, float *offset, int limit, int depth)
00077 {
00078     if (limit == 0) return depth;
00079     
00080     if (RE_rayobject_raycast(tree, isect)) {
00081         
00082         isect->start[0] = isect->start[0] + isect->dist*isect->dir[0];
00083         isect->start[1] = isect->start[1] + isect->dist*isect->dir[1];
00084         isect->start[2] = isect->start[2] + isect->dist*isect->dir[2];
00085         
00086         isect->dist = FLT_MAX;
00087         isect->skip = RE_SKIP_VLR_NEIGHBOUR;
00088         isect->orig.face= isect->hit.face;
00089         isect->orig.ob= isect->hit.ob;
00090         
00091         return intersect_outside_volume(tree, isect, offset, limit-1, depth+1);
00092     } else {
00093         return depth;
00094     }
00095 }
00096 
00097 /* Uses ray tracing to check if a point is inside or outside an ObjectInstanceRen */
00098 static int point_inside_obi(RayObject *tree, ObjectInstanceRen *UNUSED(obi), float *co)
00099 {
00100     Isect isect= {{0}};
00101     float dir[3] = {0.0f,0.0f,1.0f};
00102     int final_depth=0, depth=0, limit=20;
00103     
00104     /* set up the isect */
00105     copy_v3_v3(isect.start, co);
00106     copy_v3_v3(isect.dir, dir);
00107     isect.mode= RE_RAY_MIRROR;
00108     isect.last_hit= NULL;
00109     isect.lay= -1;
00110     
00111     isect.dist = FLT_MAX;
00112     isect.orig.face= NULL;
00113     isect.orig.ob = NULL;
00114 
00115     final_depth = intersect_outside_volume(tree, &isect, dir, limit, depth);
00116     
00117     /* even number of intersections: point is outside
00118      * odd number: point is inside */
00119     if (final_depth % 2 == 0) return 0;
00120     else return 1;
00121 }
00122 
00123 /* find the bounding box of an objectinstance in global space */
00124 void global_bounds_obi(Render *re, ObjectInstanceRen *obi, float *bbmin, float *bbmax)
00125 {
00126     ObjectRen *obr = obi->obr;
00127     VolumePrecache *vp = obi->volume_precache;
00128     VertRen *ver= NULL;
00129     float co[3];
00130     int a;
00131     
00132     if (vp->bbmin != NULL && vp->bbmax != NULL) {
00133         copy_v3_v3(bbmin, vp->bbmin);
00134         copy_v3_v3(bbmax, vp->bbmax);
00135         return;
00136     }
00137     
00138     vp->bbmin = MEM_callocN(sizeof(float)*3, "volume precache min boundbox corner");
00139     vp->bbmax = MEM_callocN(sizeof(float)*3, "volume precache max boundbox corner");
00140     
00141     INIT_MINMAX(bbmin, bbmax);
00142     
00143     for(a=0; a<obr->totvert; a++) {
00144         if((a & 255)==0) ver= obr->vertnodes[a>>8].vert;
00145         else ver++;
00146         
00147         copy_v3_v3(co, ver->co);
00148         
00149         /* transformed object instance in camera space */
00150         if(obi->flag & R_TRANSFORMED)
00151             mul_m4_v3(obi->mat, co);
00152         
00153         /* convert to global space */
00154         mul_m4_v3(re->viewinv, co);
00155         
00156         DO_MINMAX(co, vp->bbmin, vp->bbmax);
00157     }
00158     
00159     copy_v3_v3(bbmin, vp->bbmin);
00160     copy_v3_v3(bbmax, vp->bbmax);
00161     
00162 }
00163 
00164 /* *** light cache filtering *** */
00165 
00166 static float get_avg_surrounds(float *cache, int *res, int xx, int yy, int zz)
00167 {
00168     int x, y, z, x_, y_, z_;
00169     int added=0;
00170     float tot=0.0f;
00171     
00172     for (z=-1; z <= 1; z++) {
00173         z_ = zz+z;
00174         if (z_ >= 0 && z_ <= res[2]-1) {
00175         
00176             for (y=-1; y <= 1; y++) {
00177                 y_ = yy+y;
00178                 if (y_ >= 0 && y_ <= res[1]-1) {
00179                 
00180                     for (x=-1; x <= 1; x++) {
00181                         x_ = xx+x;
00182                         if (x_ >= 0 && x_ <= res[0]-1) {
00183                             const int i= V_I(x_, y_, z_, res);
00184                             
00185                             if (cache[i] > 0.0f) {
00186                                 tot += cache[i];
00187                                 added++;
00188                             }
00189                             
00190                         }
00191                     }
00192                 }
00193             }
00194         }
00195     }
00196     
00197     if (added > 0) tot /= added;
00198     
00199     return tot;
00200 }
00201 
00202 /* function to filter the edges of the light cache, where there was no volume originally.
00203  * For each voxel which was originally external to the mesh, it finds the average values of
00204  * the surrounding internal voxels and sets the original external voxel to that average amount.
00205  * Works almost a bit like a 'dilate' filter */
00206 static void lightcache_filter(VolumePrecache *vp)
00207 {
00208     int x, y, z;
00209 
00210     for (z=0; z < vp->res[2]; z++) {
00211         for (y=0; y < vp->res[1]; y++) {
00212             for (x=0; x < vp->res[0]; x++) {
00213                 /* trigger for outside mesh */
00214                 const int i= V_I(x, y, z, vp->res);
00215                 
00216                 if (vp->data_r[i] < -0.f)
00217                     vp->data_r[i] = get_avg_surrounds(vp->data_r, vp->res, x, y, z);
00218                 if (vp->data_g[i] < -0.f)
00219                     vp->data_g[i] = get_avg_surrounds(vp->data_g, vp->res, x, y, z);
00220                 if (vp->data_b[i] < -0.f)
00221                     vp->data_b[i] = get_avg_surrounds(vp->data_b, vp->res, x, y, z);
00222             }
00223         }
00224     }
00225 }
00226 
00227 #if 0
00228 static void lightcache_filter2(VolumePrecache *vp)
00229 {
00230     int x, y, z;
00231     float *new_r, *new_g, *new_b;
00232     int field_size = vp->res[0]*vp->res[1]*vp->res[2]*sizeof(float);
00233     
00234     new_r = MEM_mallocN(field_size, "temp buffer for light cache filter r channel");
00235     new_g = MEM_mallocN(field_size, "temp buffer for light cache filter g channel");
00236     new_b = MEM_mallocN(field_size, "temp buffer for light cache filter b channel");
00237     
00238     memcpy(new_r, vp->data_r, field_size);
00239     memcpy(new_g, vp->data_g, field_size);
00240     memcpy(new_b, vp->data_b, field_size);
00241     
00242     for (z=0; z < vp->res[2]; z++) {
00243         for (y=0; y < vp->res[1]; y++) {
00244             for (x=0; x < vp->res[0]; x++) {
00245                 /* trigger for outside mesh */
00246                 const int i= V_I(x, y, z, vp->res);
00247                 if (vp->data_r[i] < -0.f)
00248                     new_r[i] = get_avg_surrounds(vp->data_r, vp->res, x, y, z);
00249                 if (vp->data_g[i] < -0.f)
00250                     new_g[i] = get_avg_surrounds(vp->data_g, vp->res, x, y, z);
00251                 if (vp->data_b[i] < -0.f)
00252                     new_b[i] = get_avg_surrounds(vp->data_b, vp->res, x, y, z);
00253             }
00254         }
00255     }
00256     
00257     SWAP(float *, vp->data_r, new_r);
00258     SWAP(float *, vp->data_g, new_g);
00259     SWAP(float *, vp->data_b, new_b);
00260     
00261     if (new_r) { MEM_freeN(new_r); new_r=NULL; }
00262     if (new_g) { MEM_freeN(new_g); new_g=NULL; }
00263     if (new_b) { MEM_freeN(new_b); new_b=NULL; }
00264 }
00265 #endif
00266 
00267 static inline int ms_I(int x, int y, int z, int *n) //has a pad of 1 voxel surrounding the core for boundary simulation
00268 { 
00269     /* different ordering to light cache */
00270     return x*(n[1]+2)*(n[2]+2) + y*(n[2]+2) + z;    
00271 }
00272 
00273 static 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
00274 { 
00275     /* same ordering to light cache, with padding */
00276     return z*(n[1]+2)*(n[0]+2) + y*(n[0]+2) + x;    
00277 }
00278 
00279 static inline int lc_to_ms_I(int x, int y, int z, int *n)
00280 { 
00281     /* converting light cache index to multiple scattering index */
00282     return (x-1)*(n[1]*n[2]) + (y-1)*(n[2]) + z-1;
00283 }
00284 
00285 /* *** multiple scattering approximation *** */
00286 
00287 /* get the total amount of light energy in the light cache. used to normalise after multiple scattering */
00288 static float total_ss_energy(Render *re, int do_test_break, VolumePrecache *vp)
00289 {
00290     int x, y, z;
00291     int *res = vp->res;
00292     float energy=0.f;
00293     
00294     for (z=0; z < res[2]; z++) {
00295         for (y=0; y < res[1]; y++) {
00296             for (x=0; x < res[0]; x++) {
00297                 const int i=V_I(x, y, z, res);
00298             
00299                 if (vp->data_r[i] > 0.f) energy += vp->data_r[i];
00300                 if (vp->data_g[i] > 0.f) energy += vp->data_g[i];
00301                 if (vp->data_b[i] > 0.f) energy += vp->data_b[i];
00302             }
00303         }
00304 
00305         if (do_test_break && re->test_break(re->tbh)) break;
00306     }
00307     
00308     return energy;
00309 }
00310 
00311 static float total_ms_energy(Render *re, int do_test_break, float *sr, float *sg, float *sb, int *res)
00312 {
00313     int x, y, z;
00314     float energy=0.f;
00315     
00316     for (z=1;z<=res[2];z++) {
00317         for (y=1;y<=res[1];y++) {
00318             for (x=1;x<=res[0];x++) {
00319                 const int i = ms_I(x,y,z,res);
00320                 
00321                 if (sr[i] > 0.f) energy += sr[i];
00322                 if (sg[i] > 0.f) energy += sg[i];
00323                 if (sb[i] > 0.f) energy += sb[i];
00324             }
00325         }
00326 
00327         if (do_test_break && re->test_break(re->tbh)) break;
00328     }
00329     
00330     return energy;
00331 }
00332 
00333 static void ms_diffuse(Render *re, int do_test_break, float *x0, float *x, float diff, int *n) //n is the unpadded resolution
00334 {
00335     int i, j, k, l;
00336     const float dt = VOL_MS_TIMESTEP;
00337     size_t size = n[0]*n[1]*n[2];
00338     const float a = dt*diff*size;
00339     
00340     for (l=0; l<20; l++)
00341     {
00342         for (k=1; k<=n[2]; k++)
00343         {
00344             for (j=1; j<=n[1]; j++)
00345             {
00346                 for (i=1; i<=n[0]; i++)
00347                 {
00348                    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)]+
00349                                                                         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)]
00350                                                                         ) / (1+6*a);
00351                 }
00352             }
00353 
00354             if (do_test_break && re->test_break(re->tbh)) break;
00355         }
00356 
00357         if (re->test_break(re->tbh)) break;
00358     }
00359 }
00360 
00361 static void multiple_scattering_diffusion(Render *re, VolumePrecache *vp, Material *ma)
00362 {
00363     const float diff = ma->vol.ms_diff * 0.001f;    /* compensate for scaling for a nicer UI range */
00364     const int simframes = (int)(ma->vol.ms_spread * (float)MAX3(vp->res[0], vp->res[1], vp->res[2]));
00365     const int shade_type = ma->vol.shade_type;
00366     float fac = ma->vol.ms_intensity;
00367     
00368     int x, y, z, m;
00369     int *n = vp->res;
00370     const int size = (n[0]+2)*(n[1]+2)*(n[2]+2);
00371     const int do_test_break = (size > 100000);
00372     double time, lasttime= PIL_check_seconds_timer();
00373     float total;
00374     float c=1.0f;
00375     float origf;    /* factor for blending in original light cache */
00376     float energy_ss, energy_ms;
00377 
00378     float *sr0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
00379     float *sr=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
00380     float *sg0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
00381     float *sg=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
00382     float *sb0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
00383     float *sb=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
00384 
00385     total = (float)(n[0]*n[1]*n[2]*simframes);
00386     
00387     energy_ss = total_ss_energy(re, do_test_break, vp);
00388     
00389     /* Scattering as diffusion pass */
00390     for (m=0; m<simframes; m++)
00391     {
00392         /* add sources */
00393         for (z=1; z<=n[2]; z++)
00394         {
00395             for (y=1; y<=n[1]; y++)
00396             {
00397                 for (x=1; x<=n[0]; x++)
00398                 {
00399                     const int i = lc_to_ms_I(x, y ,z, n);   //lc index                  
00400                     const int j = ms_I(x, y, z, n);         //ms index
00401                     
00402                     time= PIL_check_seconds_timer();
00403                     c++;                                        
00404                     if (vp->data_r[i] > 0.0f)
00405                         sr[j] += vp->data_r[i];
00406                     if (vp->data_g[i] > 0.0f)
00407                         sg[j] += vp->data_g[i];
00408                     if (vp->data_b[i] > 0.0f)
00409                         sb[j] += vp->data_b[i];
00410                     
00411                     /* Displays progress every second */
00412                     if(time-lasttime>1.0) {
00413                         char str[64];
00414                         BLI_snprintf(str, sizeof(str), "Simulating multiple scattering: %d%%", (int)(100.0f * (c / total)));
00415                         re->i.infostr= str;
00416                         re->stats_draw(re->sdh, &re->i);
00417                         re->i.infostr= NULL;
00418                         lasttime= time;
00419                     }
00420                 }
00421             }
00422 
00423             if (do_test_break && re->test_break(re->tbh)) break;
00424         }
00425 
00426         if (re->test_break(re->tbh)) break;
00427 
00428         SWAP(float *,sr,sr0);
00429         SWAP(float *,sg,sg0);
00430         SWAP(float *,sb,sb0);
00431 
00432         /* main diffusion simulation */
00433         ms_diffuse(re, do_test_break, sr0, sr, diff, n);
00434         ms_diffuse(re, do_test_break, sg0, sg, diff, n);
00435         ms_diffuse(re, do_test_break, sb0, sb, diff, n);
00436         
00437         if (re->test_break(re->tbh)) break;
00438     }
00439     
00440     /* normalisation factor to conserve energy */
00441     energy_ms = total_ms_energy(re, do_test_break, sr, sg, sb, n);
00442     fac *= (energy_ss / energy_ms);
00443     
00444     /* blend multiple scattering back in the light cache */
00445     if (shade_type == MA_VOL_SHADE_SHADEDPLUSMULTIPLE) {
00446         /* conserve energy - half single, half multiple */
00447         origf = 0.5f;
00448         fac *= 0.5f;
00449     } else {
00450         origf = 0.0f;
00451     }
00452 
00453     for (z=1;z<=n[2];z++)
00454     {
00455         for (y=1;y<=n[1];y++)
00456         {
00457             for (x=1;x<=n[0];x++)
00458             {
00459                 const int i = lc_to_ms_I(x, y ,z, n);   //lc index                  
00460                 const int j = ms_I(x, y, z, n);         //ms index
00461                 
00462                 vp->data_r[i] = origf * vp->data_r[i] + fac * sr[j];
00463                 vp->data_g[i] = origf * vp->data_g[i] + fac * sg[j];
00464                 vp->data_b[i] = origf * vp->data_b[i] + fac * sb[j];
00465             }
00466         }
00467 
00468         if (do_test_break && re->test_break(re->tbh)) break;
00469     }
00470 
00471     MEM_freeN(sr0);
00472     MEM_freeN(sr);
00473     MEM_freeN(sg0);
00474     MEM_freeN(sg);
00475     MEM_freeN(sb0);
00476     MEM_freeN(sb);
00477 }
00478 
00479 
00480 
00481 #if 0 // debug stuff
00482 static void *vol_precache_part_test(void *data)
00483 {
00484     VolPrecachePart *pa = data;
00485 
00486     printf("part number: %d \n", pa->num);
00487     printf("done: %d \n", pa->done);
00488     printf("x min: %d   x max: %d \n", pa->minx, pa->maxx);
00489     printf("y min: %d   y max: %d \n", pa->miny, pa->maxy);
00490     printf("z min: %d   z max: %d \n", pa->minz, pa->maxz);
00491 
00492     return NULL;
00493 }
00494 #endif
00495 
00496 typedef struct VolPrecacheQueue {
00497     ThreadQueue *work;
00498     ThreadQueue *done;
00499 } VolPrecacheQueue;
00500 
00501 /* Iterate over the 3d voxel grid, and fill the voxels with scattering information
00502  *
00503  * It's stored in memory as 3 big float grids next to each other, one for each RGB channel.
00504  * I'm guessing the memory alignment may work out better this way for the purposes
00505  * of doing linear interpolation, but I haven't actually tested this theory! :)
00506  */
00507 static void *vol_precache_part(void *data)
00508 {
00509     VolPrecacheQueue *queue = (VolPrecacheQueue*)data;
00510     VolPrecachePart *pa;
00511 
00512     while ((pa = BLI_thread_queue_pop(queue->work))) {
00513         ObjectInstanceRen *obi = pa->obi;
00514         RayObject *tree = pa->tree;
00515         ShadeInput *shi = pa->shi;
00516         float scatter_col[3] = {0.f, 0.f, 0.f};
00517         float co[3], cco[3], view[3];
00518         int x, y, z, i;
00519         int res[3];
00520 
00521         if (pa->re->test_break && pa->re->test_break(pa->re->tbh))
00522             break;
00523 
00524         res[0]= pa->res[0];
00525         res[1]= pa->res[1];
00526         res[2]= pa->res[2];
00527 
00528         for (z= pa->minz; z < pa->maxz; z++) {
00529             co[2] = pa->bbmin[2] + (pa->voxel[2] * (z + 0.5f));
00530             
00531             for (y= pa->miny; y < pa->maxy; y++) {
00532                 co[1] = pa->bbmin[1] + (pa->voxel[1] * (y + 0.5f));
00533                 
00534                 for (x=pa->minx; x < pa->maxx; x++) {
00535                     co[0] = pa->bbmin[0] + (pa->voxel[0] * (x + 0.5f));
00536                     
00537                     if (pa->re->test_break && pa->re->test_break(pa->re->tbh))
00538                         break;
00539                     
00540                     /* convert from world->camera space for shading */
00541                     mul_v3_m4v3(cco, pa->viewmat, co);
00542                     
00543                     i= V_I(x, y, z, res);
00544                     
00545                     // don't bother if the point is not inside the volume mesh
00546                     if (!point_inside_obi(tree, obi, cco)) {
00547                         obi->volume_precache->data_r[i] = -1.0f;
00548                         obi->volume_precache->data_g[i] = -1.0f;
00549                         obi->volume_precache->data_b[i] = -1.0f;
00550                         continue;
00551                     }
00552                     
00553                     copy_v3_v3(view, cco);
00554                     normalize_v3(view);
00555                     vol_get_scattering(shi, scatter_col, cco, view);
00556                 
00557                     obi->volume_precache->data_r[i] = scatter_col[0];
00558                     obi->volume_precache->data_g[i] = scatter_col[1];
00559                     obi->volume_precache->data_b[i] = scatter_col[2];
00560                     
00561                 }
00562             }
00563         }
00564 
00565         BLI_thread_queue_push(queue->done, pa);
00566     }
00567     
00568     return NULL;
00569 }
00570 
00571 
00572 static void precache_setup_shadeinput(Render *re, ObjectInstanceRen *obi, Material *ma, ShadeInput *shi)
00573 {
00574     memset(shi, 0, sizeof(ShadeInput)); 
00575     shi->depth= 1;
00576     shi->mask= 1;
00577     shi->mat = ma;
00578     shi->vlr = NULL;
00579     memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));    // note, keep this synced with render_types.h
00580     shi->har= shi->mat->har;
00581     shi->obi= obi;
00582     shi->obr= obi->obr;
00583     shi->lay = re->lay;
00584 }
00585 
00586 static void precache_init_parts(Render *re, RayObject *tree, ShadeInput *shi, ObjectInstanceRen *obi, int totthread, int *parts)
00587 {
00588     VolumePrecache *vp = obi->volume_precache;
00589     int i=0, x, y, z;
00590     float voxel[3];
00591     int sizex, sizey, sizez;
00592     float bbmin[3], bbmax[3];
00593     int *res;
00594     int minx, maxx;
00595     int miny, maxy;
00596     int minz, maxz;
00597     
00598     if (!vp) return;
00599 
00600     BLI_freelistN(&re->volume_precache_parts);
00601     
00602     /* currently we just subdivide the box, number of threads per side */
00603     parts[0] = parts[1] = parts[2] = totthread;
00604     res = vp->res;
00605     
00606     /* using boundbox in worldspace */
00607     global_bounds_obi(re, obi, bbmin, bbmax);
00608     sub_v3_v3v3(voxel, bbmax, bbmin);
00609     
00610     voxel[0] /= (float)res[0];
00611     voxel[1] /= (float)res[1];
00612     voxel[2] /= (float)res[2];
00613 
00614     for (x=0; x < parts[0]; x++) {
00615         sizex = ceil(res[0] / (float)parts[0]);
00616         minx = x * sizex;
00617         maxx = minx + sizex;
00618         maxx = (maxx>res[0])?res[0]:maxx;
00619         
00620         for (y=0; y < parts[1]; y++) {
00621             sizey = ceil(res[1] / (float)parts[1]);
00622             miny = y * sizey;
00623             maxy = miny + sizey;
00624             maxy = (maxy>res[1])?res[1]:maxy;
00625             
00626             for (z=0; z < parts[2]; z++) {
00627                 VolPrecachePart *pa= MEM_callocN(sizeof(VolPrecachePart), "new precache part");
00628                 
00629                 sizez = ceil(res[2] / (float)parts[2]);
00630                 minz = z * sizez;
00631                 maxz = minz + sizez;
00632                 maxz = (maxz>res[2])?res[2]:maxz;
00633                 
00634                 pa->re = re;
00635                 pa->num = i;
00636                 pa->tree = tree;
00637                 pa->shi = shi;
00638                 pa->obi = obi;
00639                 copy_m4_m4(pa->viewmat, re->viewmat);
00640                 
00641                 copy_v3_v3(pa->bbmin, bbmin);
00642                 copy_v3_v3(pa->voxel, voxel);
00643                 copy_v3_v3_int(pa->res, res);
00644                 
00645                 pa->minx = minx; pa->maxx = maxx;
00646                 pa->miny = miny; pa->maxy = maxy;
00647                 pa->minz = minz; pa->maxz = maxz;
00648                 
00649                 
00650                 BLI_addtail(&re->volume_precache_parts, pa);
00651                 
00652                 i++;
00653             }
00654         }
00655     }
00656 }
00657 
00658 /* calculate resolution from bounding box in world space */
00659 static int precache_resolution(Render *re, VolumePrecache *vp, ObjectInstanceRen *obi, int res)
00660 {
00661     float dim[3], div;
00662     float bbmin[3], bbmax[3];
00663     
00664     /* bound box in global space */
00665     global_bounds_obi(re, obi, bbmin, bbmax);
00666     sub_v3_v3v3(dim, bbmax, bbmin);
00667     
00668     div = MAX3(dim[0], dim[1], dim[2]);
00669     dim[0] /= div;
00670     dim[1] /= div;
00671     dim[2] /= div;
00672     
00673     vp->res[0] = ceil(dim[0] * res);
00674     vp->res[1] = ceil(dim[1] * res);
00675     vp->res[2] = ceil(dim[2] * res);
00676     
00677     if ((vp->res[0] < 1) || (vp->res[1] < 1) || (vp->res[2] < 1))
00678         return 0;
00679     
00680     return 1;
00681 }
00682 
00683 /* Precache a volume into a 3D voxel grid.
00684  * The voxel grid is stored in the ObjectInstanceRen, 
00685  * in camera space, aligned with the ObjectRen's bounding box.
00686  * Resolution is defined by the user.
00687  */
00688 static void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Material *ma)
00689 {
00690     VolumePrecache *vp;
00691     VolPrecachePart *pa;
00692     RayObject *tree;
00693     ShadeInput shi;
00694     ListBase threads;
00695     VolPrecacheQueue queue;
00696     int parts[3] = {1, 1, 1}, totparts;
00697     
00698     int counter=0;
00699     int totthread = re->r.threads, thread;
00700     
00701     double time, lasttime= PIL_check_seconds_timer();
00702     
00703     R = *re;
00704 
00705     /* create a raytree with just the faces of the instanced ObjectRen, 
00706      * used for checking if the cached point is inside or outside. */
00707     tree = makeraytree_object(&R, obi);
00708     if (!tree) return;
00709 
00710     vp = MEM_callocN(sizeof(VolumePrecache), "volume light cache");
00711     obi->volume_precache = vp;
00712     
00713     if (!precache_resolution(re, vp, obi, ma->vol.precache_resolution)) {
00714         MEM_freeN(vp);
00715         vp = NULL;
00716         return;
00717     }
00718 
00719     vp->data_r = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data red channel");
00720     vp->data_g = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data green channel");
00721     vp->data_b = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data blue channel");
00722     if (vp->data_r==NULL || vp->data_g==NULL || vp->data_b==NULL) {
00723         MEM_freeN(vp);
00724         return;
00725     }
00726 
00727     /* Need a shadeinput to calculate scattering */
00728     precache_setup_shadeinput(re, obi, ma, &shi);
00729     
00730     precache_init_parts(re, tree, &shi, obi, totthread, parts);
00731     totparts = parts[0] * parts[1] * parts[2];
00732 
00733     /* setup work and done queues */
00734     queue.work = BLI_thread_queue_init();
00735     queue.done = BLI_thread_queue_init();
00736     BLI_thread_queue_nowait(queue.work);
00737 
00738     for(pa= re->volume_precache_parts.first; pa; pa= pa->next)
00739         BLI_thread_queue_push(queue.work, pa);
00740     
00741     /* launch threads */
00742     BLI_init_threads(&threads, vol_precache_part, totthread);
00743 
00744     for(thread= 0; thread<totthread; thread++)
00745         BLI_insert_thread(&threads, &queue);
00746     
00747     /* loop waiting for work to be done */
00748     while(counter < totparts) {
00749         if(re->test_break && re->test_break(re->tbh))
00750             break;
00751 
00752         if(BLI_thread_queue_pop_timeout(queue.done, 50))
00753             counter++;
00754 
00755         time= PIL_check_seconds_timer();
00756         if(time-lasttime>1.0) {
00757             char str[64];
00758             BLI_snprintf(str, sizeof(str), "Precaching volume: %d%%", (int)(100.0f * ((float)counter / (float)totparts)));
00759             re->i.infostr= str;
00760             re->stats_draw(re->sdh, &re->i);
00761             re->i.infostr= NULL;
00762             lasttime= time;
00763         }
00764     }
00765     
00766     /* free */
00767     BLI_end_threads(&threads);
00768     BLI_thread_queue_free(queue.work);
00769     BLI_thread_queue_free(queue.done);
00770     BLI_freelistN(&re->volume_precache_parts);
00771     
00772     if(tree) {
00773         //TODO: makeraytree_object creates a tree and saves it on OBI, if we free this tree we should also clear other pointers to it
00774         //RE_rayobject_free(tree);
00775         //tree= NULL;
00776     }
00777     
00778     if (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE))
00779     {
00780         /* this should be before the filtering */
00781         multiple_scattering_diffusion(re, obi->volume_precache, ma);
00782     }
00783         
00784     lightcache_filter(obi->volume_precache);
00785 }
00786 
00787 static int using_lightcache(Material *ma)
00788 {
00789     return (((ma->vol.shadeflag & MA_VOL_PRECACHESHADING) && (ma->vol.shade_type == MA_VOL_SHADE_SHADED))
00790         || (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)));
00791 }
00792 
00793 /* loop through all objects (and their associated materials)
00794  * marked for pre-caching in convertblender.c, and pre-cache them */
00795 void volume_precache(Render *re)
00796 {
00797     ObjectInstanceRen *obi;
00798     VolumeOb *vo;
00799 
00800     re->i.infostr= "Volume preprocessing";
00801     re->stats_draw(re->sdh, &re->i);
00802 
00803     for(vo= re->volumes.first; vo; vo= vo->next) {
00804         if (using_lightcache(vo->ma)) {
00805             for(obi= re->instancetable.first; obi; obi= obi->next) {
00806                 if (obi->obr == vo->obr) {
00807                     vol_precache_objectinstance_threads(re, obi, vo->ma);
00808 
00809                     if(re->test_break && re->test_break(re->tbh))
00810                         break;
00811                 }
00812             }
00813 
00814             if(re->test_break && re->test_break(re->tbh))
00815                 break;
00816         }
00817     }
00818     
00819     re->i.infostr= NULL;
00820     re->stats_draw(re->sdh, &re->i);
00821 }
00822 
00823 void free_volume_precache(Render *re)
00824 {
00825     ObjectInstanceRen *obi;
00826     
00827     for(obi= re->instancetable.first; obi; obi= obi->next) {
00828         if (obi->volume_precache != NULL) {
00829             MEM_freeN(obi->volume_precache->data_r);
00830             MEM_freeN(obi->volume_precache->data_g);
00831             MEM_freeN(obi->volume_precache->data_b);
00832             MEM_freeN(obi->volume_precache->bbmin);
00833             MEM_freeN(obi->volume_precache->bbmax);
00834             MEM_freeN(obi->volume_precache);
00835             obi->volume_precache = NULL;
00836         }
00837     }
00838     
00839     BLI_freelistN(&re->volumes);
00840 }
00841 
00842 int point_inside_volume_objectinstance(Render *re, ObjectInstanceRen *obi, float *co)
00843 {
00844     RayObject *tree;
00845     int inside=0;
00846     
00847     tree = makeraytree_object(re, obi);
00848     if (!tree) return 0;
00849     
00850     inside = point_inside_obi(tree, obi, co);
00851     
00852     //TODO: makeraytree_object creates a tree and saves it on OBI, if we free this tree we should also clear other pointers to it
00853     //RE_rayobject_free(tree);
00854     //tree= NULL;
00855     
00856     return inside;
00857 }
00858