diff options
-rw-r--r-- | source/blender/blenkernel/intern/material.c | 1 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 2 | ||||
-rw-r--r-- | source/blender/render/extern/include/RE_shader_ext.h | 1 | ||||
-rw-r--r-- | source/blender/render/intern/source/shadeinput.c | 1 | ||||
-rw-r--r-- | source/blender/render/intern/source/volumetric.c | 299 | ||||
-rw-r--r-- | source/blender/src/buttons_shading.c | 47 |
6 files changed, 274 insertions, 77 deletions
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 3fcc76bf058..b47e4d2ebaa 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -173,6 +173,7 @@ void init_material(Material *ma) ma->vol_absorption = 1.0f; ma->vol_scattering = 1.0f; ma->vol_absorption_col[0] = ma->vol_absorption_col[1] = ma->vol_absorption_col[2] = 0.0f; + ma->vol_precache_resolution = 50; ma->mode= MA_TRACEBLE|MA_SHADBUF|MA_SHADOW|MA_RADIO|MA_RAYBIAS|MA_TANGENT_STR; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index a3e6a21dd0b..4af54832221 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -7895,6 +7895,8 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } if (ma->vol_density_scale < 0.0001f) ma->vol_density_scale = 1.0f; + if (ma->vol_precache_resolution == 0) + ma->vol_precache_resolution = 50; } for(tex=main->tex.first; tex; tex= tex->id.next) { diff --git a/source/blender/render/extern/include/RE_shader_ext.h b/source/blender/render/extern/include/RE_shader_ext.h index 112a1e9929d..5a320da522f 100644 --- a/source/blender/render/extern/include/RE_shader_ext.h +++ b/source/blender/render/extern/include/RE_shader_ext.h @@ -100,6 +100,7 @@ typedef struct ShadeInput struct StrandRen *strand; struct ObjectInstanceRen *obi; struct ObjectRen *obr; + struct Render *re; /* link back to the Render */ int facenr; float facenor[3]; /* copy from face */ short flippednor; /* is facenor flipped? */ diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c index aa11e3d75dd..6989fe66b67 100644 --- a/source/blender/render/intern/source/shadeinput.c +++ b/source/blender/render/intern/source/shadeinput.c @@ -1270,6 +1270,7 @@ void shade_input_initialize(ShadeInput *shi, RenderPart *pa, RenderLayer *rl, in shi->combinedflag= ~rl->pass_xor; shi->mat_override= rl->mat_override; shi->light_override= rl->light_override; + shi->re = &R; // shi->rl= rl; /* note shi.depth==0 means first hit, not raytracing */ diff --git a/source/blender/render/intern/source/volumetric.c b/source/blender/render/intern/source/volumetric.c index fc2e4a630b1..0f6e7591ece 100644 --- a/source/blender/render/intern/source/volumetric.c +++ b/source/blender/render/intern/source/volumetric.c @@ -51,6 +51,7 @@ #include "BKE_main.h" #include "render_types.h" +#include "renderdatabase.h" #include "pixelshading.h" #include "shading.h" #include "texture.h" @@ -61,8 +62,6 @@ extern struct Render R; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -#define PRECACHE_RES 5 - static int vol_backface_intersect_check(Isect *is, int ob, RayFace *face) { VlakRen *vlr = (VlakRen *)face; @@ -96,7 +95,7 @@ static int vol_always_intersect_check(Isect *is, int ob, RayFace *face) /* TODO: Box or sphere intersection types could speed things up */ static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco, Isect *isect, int intersect_type, int checkfunc) { - float maxsize = RE_ray_tree_max_size(R.raytree); + float maxsize = RE_ray_tree_max_size(shi->re->raytree); int intersected=0; /* TODO: use object's bounding box to calculate max size */ @@ -115,9 +114,9 @@ static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco, else if (intersect_type == VOL_BOUNDS_SS) isect->faceorig= NULL; if (checkfunc==VOL_IS_BACKFACE) - intersected = RE_ray_tree_intersect_check(R.raytree, isect, vol_backface_intersect_check); + intersected = RE_ray_tree_intersect_check(shi->re->raytree, isect, vol_backface_intersect_check); else - intersected = RE_ray_tree_intersect(R.raytree, isect); + intersected = RE_ray_tree_intersect(shi->re->raytree, isect); if(intersected) { @@ -240,6 +239,82 @@ void vol_get_absorption(ShadeInput *shi, float *absorb_col, float *co) absorb_col[2] = (1.0f - absorb_col[2]) * absorption; } + +static float D(ShadeInput *shi, int rgb, int x, int y, int z) +{ + const int res = shi->mat->vol_precache_resolution; + CLAMP(x, 0, res-1); + CLAMP(y, 0, res-1); + CLAMP(y, 0, res-1); + return shi->obi->volume_precache[rgb*res*res*res + x*res*res + y*res + z]; +} + +inline float lerp(float t, float v1, float v2) { + return (1.f - t) * v1 + t * v2; +} + +/* trilinear interpolation */ +static void vol_get_precached_scattering(ShadeInput *shi, float *scatter_col, float *co) +{ + const int res = shi->mat->vol_precache_resolution; + float voxx, voxy, voxz; + int vx, vy, vz; + float dx, dy, dz; + float d00, d10, d01, d11, d0, d1, d_final; + float bbmin[3], bbmax[3], dim[3]; + int rgb; + + if (!shi->obi->volume_precache) return; + + VECCOPY(bbmin, shi->obi->obr->boundbox[0]); + VECCOPY(bbmax, shi->obi->obr->boundbox[1]); + VecSubf(dim, bbmax, bbmin); + + voxx = ((co[0] - bbmin[0]) / dim[0]) * res - 0.5f; + voxy = ((co[1] - bbmin[1]) / dim[1]) * res - 0.5f; + voxz = ((co[2] - bbmin[2]) / dim[2]) * res - 0.5f; + + vx = (int)voxx; vy = (int)voxy; vz = (int)voxz; + + dx = voxx - vx; dy = voxy - vy; dz = voxz - vz; + + for (rgb=0; rgb < 3; rgb++) { + d00 = lerp(dx, D(shi, rgb, vx, vy, vz), D(shi, rgb, vx+1, vy, vz)); + d10 = lerp(dx, D(shi, rgb, vx, vy+1, vz), D(shi, rgb, vx+1, vy+1, vz)); + d01 = lerp(dx, D(shi, rgb, vx, vy, vz+1), D(shi, rgb, vx+1, vy, vz+1)); + d11 = lerp(dx, D(shi, rgb, vx, vy+1, vz+1), D(shi, rgb, vx+1, vy+1, vz+1)); + d0 = lerp(dy, d00, d10); + d1 = lerp(dy, d01, d11); + d_final = lerp(dz, d0, d1); + + scatter_col[rgb] = d_final; + } +} + +#if 0 +/* no interpolation, not used */ +static void vol_get_precached_scattering_nearest(ShadeInput *shi, float *scatter_col, float *co) +{ + const int res = shi->mat->vol_precache_resolution; + int x,y,z; + float bbmin[3], bbmax[3], dim[3]; + + if (!shi->obi->volume_precache) return; + + VECCOPY(bbmin, shi->obi->obr->boundbox[0]); + VECCOPY(bbmax, shi->obi->obr->boundbox[1]); + VecSubf(dim, bbmax, bbmin); + + x = (int)(((co[0] - bbmin[0]) / dim[0]) * res); + y = (int)(((co[1] - bbmin[1]) / dim[1]) * res); + z = (int)(((co[2] - bbmin[2]) / dim[2]) * res); + + scatter_col[0] = shi->obi->volume_precache[0*res*res*res + x*res*res + y*res + z]; + scatter_col[1] = shi->obi->volume_precache[1*res*res*res + x*res*res + y*res + z]; + scatter_col[2] = shi->obi->volume_precache[2*res*res*res + x*res*res + y*res + z]; +} +#endif + /* Compute attenuation, otherwise known as 'optical thickness', extinction, or tau. * Used in the relationship Transmittance = e^(-attenuation) */ @@ -276,7 +351,6 @@ void vol_get_attenuation(ShadeInput *shi, float *tau, float *co, float *endco, f VecAddf(step_end, step_sta, step_vec); for (s = 0; s < nsteps; s++) { - if (s > 0) density = vol_get_density(shi, step_sta); @@ -317,9 +391,9 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float * shi->osatex= 0; do_lamp_tex(lar, lv, shi, lacol, LA_TEXTURE); } - + VecMulf(lacol, visifac*lar->energy); - + if (ELEM(lar->type, LA_SUN, LA_HEMI)) VECCOPY(lv, lar->vec); VecMulf(lv, -1.0f); @@ -364,27 +438,27 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float * vol_get_scattering_fac(shi, &scatter_fac, co, density); VecMulf(lacol, scatter_fac); - } /* single scattering only for now */ -void vol_get_scattering(Render *re, ShadeInput *shi, float *scatter, float *co, float stepsize, float density) +void vol_get_scattering(ShadeInput *shi, float *scatter, float *co, float stepsize, float density) { GroupObject *go; ListBase *lights; LampRen *lar; float col[3] = {0.f, 0.f, 0.f}; - - lights= get_lights(shi); - for(go=lights->first; go; go= go->next) + int i=0; + + for(go=shi->re->lights.first; go; go= go->next) { float lacol[3] = {0.f, 0.f, 0.f}; + i++; + lar= go->lampren; if (lar) { - vol_shade_one_lamp(shi, co, lar, lacol, stepsize, density); - + VecMulf(lacol, density); VecAddf(col, col, lacol); @@ -415,7 +489,6 @@ static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float VecMulVecf(radiance, tr, col); tr[0] = tr[1] = tr[2] = 1.0f; - /* ray marching */ nsteps = (int)ceil(VecLenf(co, endco) / stepsize); @@ -447,23 +520,12 @@ static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float /* incoming light via emission or scattering (additive) */ vol_get_emission(shi, emit_col, step_mid, density); - if ((shi->mat->vol_shadeflag & MA_VOL_PRECACHESHADING) && shi->obi->volume_precache) { - const int res = shi->mat->vol_precache_resolution; - int x,y,z; - float bbmin[3], bbmax[3], dim[3]; - - VECCOPY(bbmin, shi->obi->obr->boundbox[0]); - VECCOPY(bbmax, shi->obi->obr->boundbox[1]); - VecSubf(dim, bbmax, bbmin); - - x = (int)(((step_mid[0] - bbmin[0]) / dim[0]) * res); - y = (int)(((step_mid[1] - bbmin[1]) / dim[1]) * res); - z = (int)(((step_mid[2] - bbmin[2]) / dim[2]) * res); - - scatter_col[0] = scatter_col[1] = scatter_col[2] = shi->obi->volume_precache[x*res*res + y*res + z]; - } - else - vol_get_scattering(&R, shi, scatter_col, step_mid, stepsize, density); + + if ((shi->mat->vol_shadeflag & MA_VOL_PRECACHESHADING) && + (shi->mat->vol_shadeflag & MA_VOL_ATTENUATED)) { + vol_get_precached_scattering(shi, scatter_col, step_mid); + } else + vol_get_scattering(shi, scatter_col, step_mid, stepsize, density); VecAddf(d_radiance, emit_col, scatter_col); @@ -544,7 +606,7 @@ static void shade_intersection(ShadeInput *shi, float *col, Isect *is) static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, float *co, float *col) { Isect isect; - float maxsize = RE_ray_tree_max_size(R.raytree); + float maxsize = RE_ray_tree_max_size(shi->re->raytree); VECCOPY(isect.start, co); isect.end[0] = isect.start[0] + shi->view[0] * maxsize; @@ -560,7 +622,7 @@ static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, float *co, float *co isect.lay= -1; /* check to see if there's anything behind the volume, otherwise shade the sky */ - if(RE_ray_tree_intersect(R.raytree, &isect)) { + if(RE_ray_tree_intersect(shi->re->raytree, &isect)) { shade_intersection(shi, col, &isect); } else { shadeSkyView(col, co, shi->view, NULL); @@ -568,6 +630,7 @@ static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, float *co, float *co } } +/* the main entry point for volume shading */ void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr) { float hitco[3], col[4] = {0.f,0.f,0.f,0.f}; @@ -629,6 +692,8 @@ void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr) } } +/* Traces a shadow through the object, + * pretty much gets the transmission over a ray path */ void volume_trace_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct Isect *last_is) { float hitco[3]; @@ -678,26 +743,125 @@ void volume_trace_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct shr->combined[2] = 0.0f; shr->combined[3] = shr->alpha = 0.0f; } +} + + +/* Recursive test for intersections, from a point inside the mesh, to outside + * Number of intersections (depth) determine if a point is inside or outside the mesh */ +int intersect_outside_volume(RayTree *tree, Isect *isect, float *offset, int limit, int depth) +{ + if (limit == 0) return depth; + + if (RE_ray_tree_intersect(tree, isect)) { + float hitco[3]; + + hitco[0] = isect->start[0] + isect->labda*isect->vec[0]; + hitco[1] = isect->start[1] + isect->labda*isect->vec[1]; + hitco[2] = isect->start[2] + isect->labda*isect->vec[2]; + VecAddf(isect->start, hitco, offset); + + return intersect_outside_volume(tree, isect, offset, limit-1, depth+1); + } else { + return depth; + } +} +/* Uses ray tracing to check if a point is inside or outside an ObjectInstanceRen */ +int point_inside_obi(RayTree *tree, ObjectInstanceRen *obi, float *co) +{ + float maxsize = RE_ray_tree_max_size(tree); + int intersected; + Isect isect; + float vec[3] = {0.0f,0.0f,1.0f}; + int final_depth=0, depth=0, limit=20; + + /* set up the isect */ + memset(&isect, 0, sizeof(isect)); + VECCOPY(isect.start, co); + isect.end[0] = co[0] + vec[0] * maxsize; + isect.end[1] = co[1] + vec[1] * maxsize; + isect.end[2] = co[2] + vec[2] * maxsize; + + /* and give it a little offset to prevent self-intersections */ + VecMulf(vec, 1e-5); + VecAddf(isect.start, isect.start, vec); + + isect.mode= RE_RAY_MIRROR; + isect.face_last= NULL; + isect.lay= -1; + + final_depth = intersect_outside_volume(tree, &isect, vec, limit, depth); + + /* even number of intersections: point is outside + * odd number: point is inside */ + if (final_depth % 2 == 0) return 0; + else return 1; +} + +static int inside_check_func(Isect *is, int ob, RayFace *face) +{ + return 1; +} +static void vlr_face_coords(RayFace *face, float **v1, float **v2, float **v3, float **v4) +{ + VlakRen *vlr= (VlakRen*)face; + + *v1 = (vlr->v1)? vlr->v1->co: NULL; + *v2 = (vlr->v2)? vlr->v2->co: NULL; + *v3 = (vlr->v3)? vlr->v3->co: NULL; + *v4 = (vlr->v4)? vlr->v4->co: NULL; +} + +RayTree *create_raytree_obi(ObjectInstanceRen *obi, float *bbmin, float *bbmax) +{ + int v; + VlakRen *vlr= NULL; + + /* create empty raytree */ + RayTree *tree = RE_ray_tree_create(64, obi->obr->totvlak, bbmin, bbmax, + vlr_face_coords, inside_check_func, NULL, NULL); + + /* fill it with faces */ + for(v=0; v<obi->obr->totvlak; v++) { + if((v & 255)==0) + vlr= obi->obr->vlaknodes[v>>8].vlak; + else + vlr++; + + RE_ray_tree_add_face(tree, 0, vlr); + } + + RE_ray_tree_done(tree); } +/* Precache a volume into a 3D voxel grid. + * The voxel grid is stored in the ObjectInstanceRen, + * in camera space, aligned with the ObjectRen's bounding box. + * Resolution is defined by the user. + */ void vol_precache_objectinstance(Render *re, ObjectInstanceRen *obi, Material *ma, float *bbmin, float *bbmax) { int x, y, z; float co[3], voxel[3], scatter_col[3]; ShadeInput shi; + float view[3] = {0.0,0.0,1.0}; float density; float stepsize; - float resf; - int res_2; - float res_3; + float resf, res_3f; + int res_2, res_3; float i = 1.0f; - double lasttime= PIL_check_seconds_timer(); + double time, lasttime= PIL_check_seconds_timer(); const int res = ma->vol_precache_resolution; + /* create a raytree with just the faces of the instanced ObjectRen, + * used for checking if the cached point is inside or outside. */ + RayTree *tree = create_raytree_obi(obi, bbmin, bbmax); + if (!tree) return; + + /* Need a shadeinput to calculate scattering */ memset(&shi, 0, sizeof(ShadeInput)); shi.depth= 1; shi.mask= 1; @@ -707,18 +871,28 @@ void vol_precache_objectinstance(Render *re, ObjectInstanceRen *obi, Material *m shi.har= shi.mat->har; shi.obi= obi; shi.obr= obi->obr; + shi.lay = re->scene->lay; + shi.re = re; + VECCOPY(shi.view, view); stepsize = vol_get_stepsize(&shi, STEPSIZE_VIEW); - resf = (float) res; + resf = (float)res; res_2 = res*res; res_3 = res*res*res; + res_3f = (float)res_3; VecSubf(voxel, bbmax, bbmin); VecMulf(voxel, 1.0f/res); - obi->volume_precache = MEM_mallocN(sizeof(float)*res*res*res, "volume light cache"); + obi->volume_precache = MEM_callocN(sizeof(float)*res_3*3, "volume light cache"); + /* Iterate over the 3d voxel grid, and fill the voxels with scattering information + * + * It's stored in memory as 3 big float grids next to each other, one for each RGB channel. + * I'm guessing the memory alignment may work out better this way for the purposes + * of doing linear interpolation, but I haven't actually tested this theory! :) + */ for (x=0; x < res; x++) { co[0] = bbmin[0] + (voxel[0] * x); @@ -726,47 +900,54 @@ void vol_precache_objectinstance(Render *re, ObjectInstanceRen *obi, Material *m co[1] = bbmin[1] + (voxel[1] * y); for (z=0; z < res; z++) { - double time= PIL_check_seconds_timer(); - co[2] = bbmin[2] + (voxel[2] * z); - - density = vol_get_density(&shi, co); - vol_get_scattering(re, &shi, scatter_col, co, stepsize, density); - obi->volume_precache[x*res_2 + y*res + z] = (scatter_col[0] + scatter_col[1] + scatter_col[2]) / 3.0f; - + time= PIL_check_seconds_timer(); + i++; + /* display progress every second */ if(re->test_break()) return; if(time-lasttime>1.0f) { char str[64]; - sprintf(str, "Precaching volume: %d%%", (int)(100.0f * (i / res_3))); + sprintf(str, "Precaching volume: %d%%", (int)(100.0f * (i / res_3f))); re->i.infostr= str; re->stats_draw(&re->i); re->i.infostr= NULL; lasttime= time; } - i++; + /* don't bother if the point is not inside the volume mesh */ + if (!point_inside_obi(tree, obi, co)) + continue; + + density = vol_get_density(&shi, co); + vol_get_scattering(&shi, scatter_col, co, stepsize, density); + + obi->volume_precache[0*res_3 + x*res_2 + y*res + z] = scatter_col[0]; + obi->volume_precache[1*res_3 + x*res_2 + y*res + z] = scatter_col[1]; + obi->volume_precache[2*res_3 + x*res_2 + y*res + z] = scatter_col[2]; } } } + + if(tree) { + RE_ray_tree_free(tree); + tree= NULL; + } } +/* loop through all objects (and their associated materials) + * marked for pre-caching in convertblender.c, and pre-cache them */ void volume_precache(Render *re) { ObjectInstanceRen *obi; VolPrecache *vp; - int i=1; - - printf("Precaching %d volumes... \n", BLI_countlist(&re->vol_precache_obs)); - + for(vp= re->vol_precache_obs.first; vp; vp= vp->next) { for(obi= re->instancetable.first; obi; obi= obi->next) { - if (obi->obr == vp->obr) { - printf("Precaching Object: %s with Material: %s \n", vp->obr->ob->id.name+2, vp->ma->id.name+2); + if (obi->obr == vp->obr) vol_precache_objectinstance(re, obi, vp->ma, obi->obr->boundbox[0], obi->obr->boundbox[1]); - } } } @@ -779,10 +960,8 @@ void free_volume_precache(Render *re) ObjectInstanceRen *obi; for(obi= re->instancetable.first; obi; obi= obi->next) { - if (obi->volume_precache) { + if (obi->volume_precache) MEM_freeN(obi->volume_precache); - printf("freed volume precache of object: %s \n", obi->obr->ob->id.name+2); - } } BLI_freelistN(&re->vol_precache_obs); diff --git a/source/blender/src/buttons_shading.c b/source/blender/src/buttons_shading.c index 8ad4078b90e..72a396d63be 100644 --- a/source/blender/src/buttons_shading.c +++ b/source/blender/src/buttons_shading.c @@ -4357,7 +4357,7 @@ static void material_panel_material_volume(Material *ma) short yco=PANEL_YMAX; block= uiNewBlock(&curarea->uiblocks, "material_panel_material_volume", UI_EMBOSS, UI_HELV, curarea->win); - if(uiNewPanel(curarea, block, "Volume", "Material", PANELX, PANELY, PANELW, PANELH)==0) return; + if(uiNewPanel(curarea, block, "Volume", "Material", PANELX, PANELY, PANELW, PANELH+40)==0) return; uiSetButLock(ma->id.lib!=NULL, ERROR_LIBDATA_MESSAGE); @@ -4365,32 +4365,28 @@ static void material_panel_material_volume(Material *ma) uiDefButF(block, NUM, B_MATPRV, "Step Size: ", X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_stepsize), 0.001, 100.0, 10, 2, "Ray marching step size"); uiDefButS(block, MENU, B_TEXREDR_PRV, "Step Size Calculation %t|Randomized %x0|Constant %x1", - X2CLM1, yco-=BUTH, BUTW2, BUTH, &ma->vol_stepsize_type, 0.0, 0.0, 0, 0, "Step size calculation, replace banding with jittering"); + X2CLM1, yco-=BUTH, BUTW2, BUTH, &ma->vol_stepsize_type, 0.0, 0.0, 0, 0, "Step size calculation, randomized replaces banding with jittering"); uiBlockEndAlign(block); yco -= YSPACE; - + uiBlockBeginAlign(block); uiDefButBitS(block, TOG, MA_VOL_ATTENUATED, B_MATPRV, "Self Shading", X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_shadeflag), 0, 0, 0, 0, "Uses absorption for light attenuation"); uiDefButF(block, NUM, B_MATPRV, "Step Size: ", X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_shade_stepsize), 0.001, 100.0, 10, 2, "Step"); - uiDefButBitS(block, TOG, MA_VOL_PRECACHESHADING, B_MATPRV, "Precache", - X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_shadeflag), 0, 0, 0, 0, "precache"); - uiDefButS(block, NUM, B_MATPRV, "Resolution: ", - X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_precache_resolution), 0.0, 1024.0, 10, 2, "precache voxel resolution"); uiBlockEndAlign(block); yco -= YSPACE; - uiBlockBeginAlign(block); - uiDefButS(block, MENU, B_TEXREDR_PRV, "Scattering Direction %t|Isotropic %x0|Mie Hazy %x1|Mie Murky %x2|Rayleigh %x3|Henyey-Greenstein %x4|Schlick %x5", - X2CLM1, yco-=BUTH, BUTW2, BUTH, &ma->vol_phasefunc_type, 0.0, 0.0, 0, 0, "Scattering Direction (Phase Function)"); - if (ELEM(ma->vol_phasefunc_type, MA_VOL_PH_HG, MA_VOL_PH_SCHLICK)) { - uiDefButF(block, NUM, B_MATPRV, "Asymmetry: ", - X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_phasefunc_g), -1.0, 1.0, 0, 0, "> 0 is forward scattering, < 0 is back scattering"); + if (ma->vol_shadeflag & MA_VOL_ATTENUATED) { + uiBlockBeginAlign(block); + uiDefButBitS(block, TOG, MA_VOL_PRECACHESHADING, B_MATPRV, "Light Cache", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_shadeflag), 0, 0, 0, 0, "Pre-cache the shading information into a voxel grid"); + uiDefButS(block, NUM, B_MATPRV, "Resolution: ", + X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_precache_resolution), 0.0, 1024.0, 10, 2, "Resolution of the voxel grid, low resolutions are faster, high resolutions use more memory (res ^3)"); + uiBlockEndAlign(block); } - uiBlockEndAlign(block); /*uiDefButBitS(block, TOG, MA_VOL_RECVSHADOW, B_MATPRV, "Receive Shadows", X2CLM1, yco-=BUTH, BUTW2, BUTH, &(ma->vol_shadeflag), 0, 0, 0, 0, "Receive shadows from external objects"); @@ -4409,7 +4405,7 @@ static void material_panel_material_volume(Material *ma) uiBlockBeginAlign(block); uiDefButF(block, NUM, B_MATPRV, "Absorption: ", - X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->vol_absorption), 0.0, 100.0, 10, 0, "Multiplier for absorption"); + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->vol_absorption), 0.0, 100.0, 10, 0, "Amount of light absorbed by the volume"); uiDefButF(block, COL, B_MATPRV, "", X2CLM2, yco-=BUTH, BUTW2, BUTH, ma->vol_absorption_col, 0, 0, 0, B_MATCOL, ""); uiBlockEndAlign(block); @@ -4418,7 +4414,7 @@ static void material_panel_material_volume(Material *ma) uiBlockBeginAlign(block); uiDefButF(block, NUMSLI, B_MATPRV, "Emit: ", - X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->emit), 0.0, 2.0, 0, 0, "Emission component"); + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->emit), 0.0, 2.0, 0, 0, "Amount of light emitted from the volume"); uiDefButF(block, COL, B_MATPRV, "", X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->r), 0, 0, 0, B_MATCOL, ""); uiBlockEndAlign(block); @@ -4426,7 +4422,24 @@ static void material_panel_material_volume(Material *ma) yco -= YSPACE; uiDefButF(block, NUM, B_MATPRV, "Scattering: ", - X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->vol_scattering), 0.0, 100.0, 10, 0, "Multiplier for scattering"); + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->vol_scattering), 0.0, 100.0, 10, 0, "Amount of light scattered through the volume from lamps"); + + yco -= YSPACE; + + uiBlockBeginAlign(block); + uiDefButS(block, MENU, B_TEXREDR_PRV, "Scattering Direction %t|Isotropic %x0|Mie Hazy %x1|Mie Murky %x2|Rayleigh %x3|Henyey-Greenstein %x4|Schlick %x5", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &ma->vol_phasefunc_type, 0.0, 0.0, 0, 0, "Scattering Direction (Phase Function)"); + if (ELEM(ma->vol_phasefunc_type, MA_VOL_PH_HG, MA_VOL_PH_SCHLICK)) { + uiDefButF(block, NUM, B_MATPRV, "Asymmetry: ", + X2CLM2, yco-=BUTH, BUTW2, BUTH, &(ma->vol_phasefunc_g), -1.0, 1.0, 0, 0, "> 0 is forward scattering, < 0 is back scattering"); + uiBlockEndAlign(block); + } else { + uiBlockEndAlign(block); + /* spacer */ + uiDefBut(block, LABEL, B_NOP, "", + X2CLM2, yco-=BUTH, BUTW2, BUTH, 0, 0, 0, 0, 0, ""); + } + } static void material_panel_nodes(Material *ma) |