Blender V2.61 - r43446
|
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