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