|
Blender
V2.59
|
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