Blender V2.61 - r43446

sss.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) 2007 Blender Foundation.
00020  * All rights reserved.
00021  *
00022  * The Original Code is: all of this file.
00023  *
00024  * Contributor(s): none yet.
00025  *
00026  * ***** END GPL LICENSE BLOCK *****
00027  */
00028 
00034 /* Possible Improvements:
00035    - add fresnel terms
00036    - adapt Rd table to scale, now with small scale there are a lot of misses?
00037    - possible interesting method: perform sss on all samples in the tree,
00038      and then use those values interpolated somehow later. can also do this
00039      filtering on demand for speed. since we are doing things in screen
00040      space now there is an exact correspondence
00041    - avoid duplicate shading (filtering points in advance, irradiance cache
00042      like lookup?)
00043    - lower resolution samples
00044 */
00045 
00046 #include <math.h>
00047 #include <string.h>
00048 #include <stdio.h>
00049 #include <string.h>
00050 
00051 /* external modules: */
00052 #include "MEM_guardedalloc.h"
00053 
00054 #include "BLI_math.h"
00055 #include "BLI_blenlib.h"
00056 #include "BLI_utildefines.h"
00057 #include "BLI_ghash.h"
00058 #include "BLI_memarena.h"
00059 
00060 #include "PIL_time.h"
00061 
00062 #include "DNA_material_types.h"
00063 
00064 #include "BKE_colortools.h"
00065 #include "BKE_global.h"
00066 #include "BKE_main.h"
00067 #include "BKE_material.h"
00068 #include "BKE_node.h"
00069 #include "BKE_scene.h"
00070 
00071 
00072 /* this module */
00073 #include "render_types.h"
00074 #include "rendercore.h"
00075 #include "renderdatabase.h" 
00076 #include "shading.h"
00077 #include "sss.h"
00078 #include "zbuf.h"
00079 
00080 extern Render R; // meh
00081 
00082 /* Generic Multiple Scattering API */
00083 
00084 /* Relevant papers:
00085     [1] A Practical Model for Subsurface Light Transport
00086     [2] A Rapid Hierarchical Rendering Technique for Translucent Materials
00087     [3] Efficient Rendering of Local Subsurface Scattering
00088     [4] Implementing a skin BSSRDF (or several...)
00089 */
00090 
00091 /* Defines */
00092 
00093 #define RD_TABLE_RANGE      100.0f
00094 #define RD_TABLE_RANGE_2    10000.0f
00095 #define RD_TABLE_SIZE       10000
00096 
00097 #define MAX_OCTREE_NODE_POINTS  8
00098 #define MAX_OCTREE_DEPTH        15
00099 
00100 /* Struct Definitions */
00101 
00102 struct ScatterSettings {
00103     float eta;      /* index of refraction */
00104     float sigma_a;  /* absorption coefficient */
00105     float sigma_s_; /* reduced scattering coefficient */
00106     float sigma_t_; /* reduced extinction coefficient */
00107     float sigma;    /* effective extinction coefficient */
00108     float Fdr;      /* diffuse fresnel reflectance */
00109     float D;        /* diffusion constant */
00110     float A;
00111     float alpha_;   /* reduced albedo */
00112     float zr;       /* distance of virtual lightsource above surface */
00113     float zv;       /* distance of virtual lightsource below surface */
00114     float ld;       /* mean free path */
00115     float ro;       /* diffuse reflectance */
00116     float color;
00117     float invsigma_t_;
00118     float frontweight;
00119     float backweight;
00120 
00121     float *tableRd;  /* lookup table to avoid computing Rd */
00122     float *tableRd2; /* lookup table to avoid computing Rd for bigger values */
00123 };
00124 
00125 typedef struct ScatterPoint {
00126     float co[3];
00127     float rad[3];
00128     float area;
00129     int back;
00130 } ScatterPoint;
00131 
00132 typedef struct ScatterNode {
00133     float co[3];
00134     float rad[3];
00135     float backrad[3];
00136     float area, backarea;
00137 
00138     int totpoint;
00139     ScatterPoint *points;
00140 
00141     float split[3];
00142     struct ScatterNode *child[8];
00143 } ScatterNode;
00144 
00145 struct ScatterTree {
00146     MemArena *arena;
00147 
00148     ScatterSettings *ss[3];
00149     float error, scale;
00150 
00151     ScatterNode *root;
00152     ScatterPoint *points;
00153     ScatterPoint **refpoints;
00154     ScatterPoint **tmppoints;
00155     int totpoint;
00156     float min[3], max[3];
00157 };
00158 
00159 typedef struct ScatterResult {
00160     float rad[3];
00161     float backrad[3];
00162     float rdsum[3];
00163     float backrdsum[3];
00164 } ScatterResult;
00165 
00166 /* Functions for BSSRDF reparametrization in to more intuitive parameters,
00167    see [2] section 4 for more info. */
00168 
00169 static float f_Rd(float alpha_, float A, float ro)
00170 {
00171     float sq;
00172 
00173     sq= sqrt(3.0f*(1.0f - alpha_));
00174     return (alpha_/2.0f)*(1.0f + expf((-4.0f/3.0f)*A*sq))*expf(-sq) - ro;
00175 }
00176 
00177 static float compute_reduced_albedo(ScatterSettings *ss)
00178 {
00179     const float tolerance= 1e-8;
00180     const int max_iteration_count= 20;
00181     float d, fsub, xn_1= 0.0f , xn= 1.0f, fxn, fxn_1;
00182     int i;
00183 
00184     /* use secant method to compute reduced albedo using Rd function inverse
00185        with a given reflectance */
00186     fxn= f_Rd(xn, ss->A, ss->ro);
00187     fxn_1= f_Rd(xn_1, ss->A, ss->ro);
00188 
00189     for(i= 0; i < max_iteration_count; i++) {
00190         fsub= (fxn - fxn_1);
00191         if(fabsf(fsub) < tolerance)
00192             break;
00193         d= ((xn - xn_1)/fsub)*fxn;
00194         if(fabsf(d) < tolerance)
00195             break;
00196 
00197         xn_1= xn;
00198         fxn_1= fxn;
00199         xn= xn - d;
00200 
00201         if(xn > 1.0f) xn= 1.0f;
00202         if(xn_1 > 1.0f) xn_1= 1.0f;
00203         
00204         fxn= f_Rd(xn, ss->A, ss->ro);
00205     }
00206 
00207     /* avoid division by zero later */
00208     if(xn <= 0.0f)
00209         xn= 0.00001f;
00210 
00211     return xn;
00212 }
00213 
00214 /* Exponential falloff functions */
00215 
00216 static float Rd_rsquare(ScatterSettings *ss, float rr)
00217 {
00218     float sr, sv, Rdr, Rdv;
00219 
00220     sr= sqrt(rr + ss->zr*ss->zr);
00221     sv= sqrt(rr + ss->zv*ss->zv);
00222 
00223     Rdr= ss->zr*(1.0f + ss->sigma*sr)*expf(-ss->sigma*sr)/(sr*sr*sr);
00224     Rdv= ss->zv*(1.0f + ss->sigma*sv)*expf(-ss->sigma*sv)/(sv*sv*sv);
00225 
00226     return /*ss->alpha_*/(1.0f/(4.0f*(float)M_PI))*(Rdr + Rdv);
00227 }
00228 
00229 static float Rd(ScatterSettings *ss, float r)
00230 {
00231     return Rd_rsquare(ss, r*r);
00232 }
00233 
00234 /* table lookups for Rd. this avoids expensive exp calls. we use two
00235    separate tables as well for lower and higher numbers to improve
00236    precision, since the number are poorly distributed because we do
00237    a lookup with the squared distance for smaller distances, saving
00238    another sqrt. */
00239 
00240 static void approximate_Rd_rgb(ScatterSettings **ss, float rr, float *rd)
00241 {
00242     float indexf, t, idxf;
00243     int index;
00244 
00245     if(rr > (RD_TABLE_RANGE_2*RD_TABLE_RANGE_2));
00246     else if(rr > RD_TABLE_RANGE) {
00247         rr= sqrt(rr);
00248         indexf= rr*(RD_TABLE_SIZE/RD_TABLE_RANGE_2);
00249         index= (int)indexf;
00250         idxf= (float)index;
00251         t= indexf - idxf;
00252 
00253         if(index >= 0 && index < RD_TABLE_SIZE) {
00254             rd[0]= (ss[0]->tableRd2[index]*(1-t) + ss[0]->tableRd2[index+1]*t);
00255             rd[1]= (ss[1]->tableRd2[index]*(1-t) + ss[1]->tableRd2[index+1]*t);
00256             rd[2]= (ss[2]->tableRd2[index]*(1-t) + ss[2]->tableRd2[index+1]*t);
00257             return;
00258         }
00259     }
00260     else {
00261         indexf= rr*(RD_TABLE_SIZE/RD_TABLE_RANGE);
00262         index= (int)indexf;
00263         idxf= (float)index;
00264         t= indexf - idxf;
00265 
00266         if(index >= 0 && index < RD_TABLE_SIZE) {
00267             rd[0]= (ss[0]->tableRd[index]*(1-t) + ss[0]->tableRd[index+1]*t);
00268             rd[1]= (ss[1]->tableRd[index]*(1-t) + ss[1]->tableRd[index+1]*t);
00269             rd[2]= (ss[2]->tableRd[index]*(1-t) + ss[2]->tableRd[index+1]*t);
00270             return;
00271         }
00272     }
00273 
00274     /* fallback to slow Rd computation */
00275     rd[0]= Rd_rsquare(ss[0], rr);
00276     rd[1]= Rd_rsquare(ss[1], rr);
00277     rd[2]= Rd_rsquare(ss[2], rr);
00278 }
00279 
00280 static void build_Rd_table(ScatterSettings *ss)
00281 {
00282     float r;
00283     int i, size = RD_TABLE_SIZE+1;
00284 
00285     ss->tableRd= MEM_mallocN(sizeof(float)*size, "scatterTableRd");
00286     ss->tableRd2= MEM_mallocN(sizeof(float)*size, "scatterTableRd");
00287 
00288     for(i= 0; i < size; i++) {
00289         r= i*(RD_TABLE_RANGE/RD_TABLE_SIZE);
00290         /*if(r < ss->invsigma_t_*ss->invsigma_t_)
00291             r= ss->invsigma_t_*ss->invsigma_t_;*/
00292         ss->tableRd[i]= Rd(ss, sqrt(r));
00293 
00294         r= i*(RD_TABLE_RANGE_2/RD_TABLE_SIZE);
00295         /*if(r < ss->invsigma_t_)
00296             r= ss->invsigma_t_;*/
00297         ss->tableRd2[i]= Rd(ss, r);
00298     }
00299 }
00300 
00301 ScatterSettings *scatter_settings_new(float refl, float radius, float ior, float reflfac, float frontweight, float backweight)
00302 {
00303     ScatterSettings *ss;
00304     
00305     ss= MEM_callocN(sizeof(ScatterSettings), "ScatterSettings");
00306 
00307     /* see [1] and [3] for these formulas */
00308     ss->eta= ior;
00309     ss->Fdr= -1.440f/ior*ior + 0.710f/ior + 0.668f + 0.0636f*ior;
00310     ss->A= (1.0f + ss->Fdr)/(1.0f - ss->Fdr);
00311     ss->ld= radius;
00312     ss->ro= MIN2(refl, 0.999f);
00313     ss->color= ss->ro*reflfac + (1.0f-reflfac);
00314 
00315     ss->alpha_= compute_reduced_albedo(ss);
00316 
00317     ss->sigma= 1.0f/ss->ld;
00318     ss->sigma_t_= ss->sigma/sqrtf(3.0f*(1.0f - ss->alpha_));
00319     ss->sigma_s_= ss->alpha_*ss->sigma_t_;
00320     ss->sigma_a= ss->sigma_t_ - ss->sigma_s_;
00321 
00322     ss->D= 1.0f/(3.0f*ss->sigma_t_);
00323 
00324     ss->zr= 1.0f/ss->sigma_t_;
00325     ss->zv= ss->zr + 4.0f*ss->A*ss->D;
00326 
00327     ss->invsigma_t_= 1.0f/ss->sigma_t_;
00328 
00329     ss->frontweight= frontweight;
00330     ss->backweight= backweight;
00331 
00332     /* precompute a table of Rd values for quick lookup */
00333     build_Rd_table(ss);
00334 
00335     return ss;
00336 }
00337 
00338 void scatter_settings_free(ScatterSettings *ss)
00339 {
00340     MEM_freeN(ss->tableRd);
00341     MEM_freeN(ss->tableRd2);
00342     MEM_freeN(ss);
00343 }
00344 
00345 /* Hierarchical method as in [2]. */
00346 
00347 /* traversal */
00348 
00349 #define SUBNODE_INDEX(co, split) \
00350     ((co[0]>=split[0]) + (co[1]>=split[1])*2 + (co[2]>=split[2])*4)
00351     
00352 static void add_radiance(ScatterTree *tree, float *frontrad, float *backrad, float area, float backarea, float rr, ScatterResult *result)
00353 {
00354     float rd[3], frontrd[3], backrd[3];
00355 
00356     approximate_Rd_rgb(tree->ss, rr, rd);
00357 
00358     if(frontrad && area) {
00359         frontrd[0] = rd[0]*area;
00360         frontrd[1] = rd[1]*area;
00361         frontrd[2] = rd[2]*area;
00362 
00363         result->rad[0] += frontrad[0]*frontrd[0];
00364         result->rad[1] += frontrad[1]*frontrd[1];
00365         result->rad[2] += frontrad[2]*frontrd[2];
00366 
00367         result->rdsum[0] += frontrd[0];
00368         result->rdsum[1] += frontrd[1];
00369         result->rdsum[2] += frontrd[2];
00370     }
00371     if(backrad && backarea) {
00372         backrd[0] = rd[0]*backarea;
00373         backrd[1] = rd[1]*backarea;
00374         backrd[2] = rd[2]*backarea;
00375 
00376         result->backrad[0] += backrad[0]*backrd[0];
00377         result->backrad[1] += backrad[1]*backrd[1];
00378         result->backrad[2] += backrad[2]*backrd[2];
00379 
00380         result->backrdsum[0] += backrd[0];
00381         result->backrdsum[1] += backrd[1];
00382         result->backrdsum[2] += backrd[2];
00383     }
00384 }
00385 
00386 static void traverse_octree(ScatterTree *tree, ScatterNode *node, float *co, int self, ScatterResult *result)
00387 {
00388     float sub[3], dist;
00389     int i, index = 0;
00390 
00391     if(node->totpoint > 0) {
00392         /* leaf - add radiance from all samples */
00393         for(i=0; i<node->totpoint; i++) {
00394             ScatterPoint *p= &node->points[i];
00395 
00396             sub_v3_v3v3(sub, co, p->co);
00397             dist= dot_v3v3(sub, sub);
00398 
00399             if(p->back)
00400                 add_radiance(tree, NULL, p->rad, 0.0f, p->area, dist, result);
00401             else
00402                 add_radiance(tree, p->rad, NULL, p->area, 0.0f, dist, result);
00403         }
00404     }
00405     else {
00406         /* branch */
00407         if (self)
00408             index = SUBNODE_INDEX(co, node->split);
00409 
00410         for(i=0; i<8; i++) {
00411             if(node->child[i]) {
00412                 ScatterNode *subnode= node->child[i];
00413 
00414                 if(self && index == i) {
00415                     /* always traverse node containing the point */
00416                     traverse_octree(tree, subnode, co, 1, result);
00417                 }
00418                 else {
00419                     /* decide subnode traversal based on maximum solid angle */
00420                     sub_v3_v3v3(sub, co, subnode->co);
00421                     dist= dot_v3v3(sub, sub);
00422 
00423                     /* actually area/dist > error, but this avoids division */
00424                     if(subnode->area+subnode->backarea>tree->error*dist) {
00425                         traverse_octree(tree, subnode, co, 0, result);
00426                     }
00427                     else {
00428                         add_radiance(tree, subnode->rad, subnode->backrad,
00429                             subnode->area, subnode->backarea, dist, result);
00430                     }
00431                 }
00432             }
00433         }
00434     }
00435 }
00436 
00437 static void compute_radiance(ScatterTree *tree, float *co, float *rad)
00438 {
00439     ScatterResult result;
00440     float rdsum[3], backrad[3], backrdsum[3];
00441 
00442     memset(&result, 0, sizeof(result));
00443 
00444     traverse_octree(tree, tree->root, co, 1, &result);
00445 
00446     /* the original paper doesn't do this, but we normalize over the
00447        sampled area and multiply with the reflectance. this is because
00448        our point samples are incomplete, there are no samples on parts
00449        of the mesh not visible from the camera. this can not only make
00450        it darker, but also lead to ugly color shifts */
00451 
00452     mul_v3_fl(result.rad, tree->ss[0]->frontweight);
00453     mul_v3_fl(result.backrad, tree->ss[0]->backweight);
00454 
00455     copy_v3_v3(rad, result.rad);
00456     add_v3_v3v3(backrad, result.rad, result.backrad);
00457 
00458     copy_v3_v3(rdsum, result.rdsum);
00459     add_v3_v3v3(backrdsum, result.rdsum, result.backrdsum);
00460 
00461     if(rdsum[0] > 1e-16f) rad[0]= tree->ss[0]->color*rad[0]/rdsum[0];
00462     if(rdsum[1] > 1e-16f) rad[1]= tree->ss[1]->color*rad[1]/rdsum[1];
00463     if(rdsum[2] > 1e-16f) rad[2]= tree->ss[2]->color*rad[2]/rdsum[2];
00464 
00465     if(backrdsum[0] > 1e-16f) backrad[0]= tree->ss[0]->color*backrad[0]/backrdsum[0];
00466     if(backrdsum[1] > 1e-16f) backrad[1]= tree->ss[1]->color*backrad[1]/backrdsum[1];
00467     if(backrdsum[2] > 1e-16f) backrad[2]= tree->ss[2]->color*backrad[2]/backrdsum[2];
00468 
00469     rad[0]= MAX2(rad[0], backrad[0]);
00470     rad[1]= MAX2(rad[1], backrad[1]);
00471     rad[2]= MAX2(rad[2], backrad[2]);
00472 }
00473 
00474 /* building */
00475 
00476 static void sum_leaf_radiance(ScatterTree *UNUSED(tree), ScatterNode *node)
00477 {
00478     ScatterPoint *p;
00479     float rad, totrad= 0.0f, inv;
00480     int i;
00481 
00482     node->co[0]= node->co[1]= node->co[2]= 0.0;
00483     node->rad[0]= node->rad[1]= node->rad[2]= 0.0;
00484     node->backrad[0]= node->backrad[1]= node->backrad[2]= 0.0;
00485 
00486     /* compute total rad, rad weighted average position,
00487        and total area */
00488     for(i=0; i<node->totpoint; i++) {
00489         p= &node->points[i];
00490 
00491         rad= p->area*fabsf(p->rad[0] + p->rad[1] + p->rad[2]);
00492         totrad += rad;
00493 
00494         node->co[0] += rad*p->co[0];
00495         node->co[1] += rad*p->co[1];
00496         node->co[2] += rad*p->co[2];
00497 
00498         if(p->back) {
00499             node->backrad[0] += p->rad[0]*p->area;
00500             node->backrad[1] += p->rad[1]*p->area;
00501             node->backrad[2] += p->rad[2]*p->area;
00502 
00503             node->backarea += p->area;
00504         }
00505         else {
00506             node->rad[0] += p->rad[0]*p->area;
00507             node->rad[1] += p->rad[1]*p->area;
00508             node->rad[2] += p->rad[2]*p->area;
00509 
00510             node->area += p->area;
00511         }
00512     }
00513 
00514     if(node->area > 1e-16f) {
00515         inv= 1.0f/node->area;
00516         node->rad[0] *= inv;
00517         node->rad[1] *= inv;
00518         node->rad[2] *= inv;
00519     }
00520     if(node->backarea > 1e-16f) {
00521         inv= 1.0f/node->backarea;
00522         node->backrad[0] *= inv;
00523         node->backrad[1] *= inv;
00524         node->backrad[2] *= inv;
00525     }
00526 
00527     if(totrad > 1e-16f) {
00528         inv= 1.0f/totrad;
00529         node->co[0] *= inv;
00530         node->co[1] *= inv;
00531         node->co[2] *= inv;
00532     }
00533     else {
00534         /* make sure that if radiance is 0.0f, we still have these points in
00535            the tree at a good position, they count for rdsum too */
00536         for(i=0; i<node->totpoint; i++) {
00537             p= &node->points[i];
00538 
00539             node->co[0] += p->co[0];
00540             node->co[1] += p->co[1];
00541             node->co[2] += p->co[2];
00542         }
00543 
00544         node->co[0] /= node->totpoint;
00545         node->co[1] /= node->totpoint;
00546         node->co[2] /= node->totpoint;
00547     }
00548 }
00549 
00550 static void sum_branch_radiance(ScatterTree *UNUSED(tree), ScatterNode *node)
00551 {
00552     ScatterNode *subnode;
00553     float rad, totrad= 0.0f, inv;
00554     int i, totnode;
00555 
00556     node->co[0]= node->co[1]= node->co[2]= 0.0;
00557     node->rad[0]= node->rad[1]= node->rad[2]= 0.0;
00558     node->backrad[0]= node->backrad[1]= node->backrad[2]= 0.0;
00559 
00560     /* compute total rad, rad weighted average position,
00561        and total area */
00562     for(i=0; i<8; i++) {
00563         if(node->child[i] == NULL)
00564             continue;
00565 
00566         subnode= node->child[i];
00567 
00568         rad= subnode->area*fabsf(subnode->rad[0] + subnode->rad[1] + subnode->rad[2]);
00569         rad += subnode->backarea*fabsf(subnode->backrad[0] + subnode->backrad[1] + subnode->backrad[2]);
00570         totrad += rad;
00571 
00572         node->co[0] += rad*subnode->co[0];
00573         node->co[1] += rad*subnode->co[1];
00574         node->co[2] += rad*subnode->co[2];
00575 
00576         node->rad[0] += subnode->rad[0]*subnode->area;
00577         node->rad[1] += subnode->rad[1]*subnode->area;
00578         node->rad[2] += subnode->rad[2]*subnode->area;
00579 
00580         node->backrad[0] += subnode->backrad[0]*subnode->backarea;
00581         node->backrad[1] += subnode->backrad[1]*subnode->backarea;
00582         node->backrad[2] += subnode->backrad[2]*subnode->backarea;
00583 
00584         node->area += subnode->area;
00585         node->backarea += subnode->backarea;
00586     }
00587 
00588     if(node->area > 1e-16f) {
00589         inv= 1.0f/node->area;
00590         node->rad[0] *= inv;
00591         node->rad[1] *= inv;
00592         node->rad[2] *= inv;
00593     }
00594     if(node->backarea > 1e-16f) {
00595         inv= 1.0f/node->backarea;
00596         node->backrad[0] *= inv;
00597         node->backrad[1] *= inv;
00598         node->backrad[2] *= inv;
00599     }
00600 
00601     if(totrad > 1e-16f) {
00602         inv= 1.0f/totrad;
00603         node->co[0] *= inv;
00604         node->co[1] *= inv;
00605         node->co[2] *= inv;
00606     }
00607     else {
00608         /* make sure that if radiance is 0.0f, we still have these points in
00609            the tree at a good position, they count for rdsum too */
00610         totnode= 0;
00611 
00612         for(i=0; i<8; i++) {
00613             if(node->child[i]) {
00614                 subnode= node->child[i];
00615 
00616                 node->co[0] += subnode->co[0];
00617                 node->co[1] += subnode->co[1];
00618                 node->co[2] += subnode->co[2];
00619 
00620                 totnode++;
00621             }
00622         }
00623 
00624         node->co[0] /= totnode;
00625         node->co[1] /= totnode;
00626         node->co[2] /= totnode;
00627     }
00628 }
00629 
00630 static void sum_radiance(ScatterTree *tree, ScatterNode *node)
00631 {
00632     if(node->totpoint > 0) {
00633         sum_leaf_radiance(tree, node);
00634     }
00635     else {
00636         int i;
00637 
00638         for(i=0; i<8; i++)
00639             if(node->child[i])
00640                 sum_radiance(tree, node->child[i]);
00641 
00642         sum_branch_radiance(tree, node);
00643     }
00644 }
00645 
00646 static void subnode_middle(int i, float *mid, float *subsize, float *submid)
00647 {
00648     int x= i & 1, y= i & 2, z= i & 4;
00649 
00650     submid[0]= mid[0] + ((x)? subsize[0]: -subsize[0]);
00651     submid[1]= mid[1] + ((y)? subsize[1]: -subsize[1]);
00652     submid[2]= mid[2] + ((z)? subsize[2]: -subsize[2]);
00653 }
00654 
00655 static void create_octree_node(ScatterTree *tree, ScatterNode *node, float *mid, float *size, ScatterPoint **refpoints, int depth)
00656 {
00657     ScatterNode *subnode;
00658     ScatterPoint **subrefpoints, **tmppoints= tree->tmppoints;
00659     int index, nsize[8], noffset[8], i, subco, usednodes, usedi;
00660     float submid[3], subsize[3];
00661 
00662     /* stopping condition */
00663     if(node->totpoint <= MAX_OCTREE_NODE_POINTS || depth == MAX_OCTREE_DEPTH) {
00664         for(i=0; i<node->totpoint; i++)
00665             node->points[i]= *(refpoints[i]);
00666 
00667         return;
00668     }
00669 
00670     subsize[0]= size[0]*0.5f;
00671     subsize[1]= size[1]*0.5f;
00672     subsize[2]= size[2]*0.5f;
00673 
00674     node->split[0]= mid[0];
00675     node->split[1]= mid[1];
00676     node->split[2]= mid[2];
00677 
00678     memset(nsize, 0, sizeof(nsize));
00679     memset(noffset, 0, sizeof(noffset));
00680 
00681     /* count points in subnodes */
00682     for(i=0; i<node->totpoint; i++) {
00683         index= SUBNODE_INDEX(refpoints[i]->co, node->split);
00684         tmppoints[i]= refpoints[i];
00685         nsize[index]++;
00686     }
00687 
00688     /* here we check if only one subnode is used. if this is the case, we don't
00689        create a new node, but rather call this function again, with different 
00690        size and middle position for the same node. */
00691     for(usedi=0, usednodes=0, i=0; i<8; i++) {
00692         if(nsize[i]) {
00693             usednodes++;
00694             usedi = i;
00695         }
00696         if(i != 0)
00697             noffset[i]= noffset[i-1]+nsize[i-1];
00698     }
00699     
00700     if(usednodes<=1) {
00701         subnode_middle(usedi, mid, subsize, submid);
00702         create_octree_node(tree, node, submid, subsize, refpoints, depth+1);
00703         return;
00704     }
00705 
00706     /* reorder refpoints by subnode */
00707     for(i=0; i<node->totpoint; i++) {
00708         index= SUBNODE_INDEX(tmppoints[i]->co, node->split);
00709         refpoints[noffset[index]]= tmppoints[i];
00710         noffset[index]++;
00711     }
00712 
00713     /* create subnodes */
00714     for(subco=0, i=0; i<8; subco+=nsize[i], i++) {
00715         if(nsize[i] > 0) {
00716             subnode= BLI_memarena_alloc(tree->arena, sizeof(ScatterNode));
00717             node->child[i]= subnode;
00718             subnode->points= node->points + subco;
00719             subnode->totpoint= nsize[i];
00720             subrefpoints= refpoints + subco;
00721 
00722             subnode_middle(i, mid, subsize, submid);
00723 
00724             create_octree_node(tree, subnode, submid, subsize, subrefpoints,
00725                 depth+1);
00726         }
00727         else
00728             node->child[i]= NULL;
00729     }
00730 
00731     node->points= NULL;
00732     node->totpoint= 0;
00733 }
00734 
00735 /* public functions */
00736 
00737 ScatterTree *scatter_tree_new(ScatterSettings *ss[3], float scale, float error,
00738     float (*co)[3], float (*color)[3], float *area, int totpoint)
00739 {
00740     ScatterTree *tree;
00741     ScatterPoint *points, **refpoints;
00742     int i;
00743 
00744     /* allocate tree */
00745     tree= MEM_callocN(sizeof(ScatterTree), "ScatterTree");
00746     tree->scale= scale;
00747     tree->error= error;
00748     tree->totpoint= totpoint;
00749 
00750     tree->ss[0]= ss[0];
00751     tree->ss[1]= ss[1];
00752     tree->ss[2]= ss[2];
00753 
00754     points= MEM_callocN(sizeof(ScatterPoint)*totpoint, "ScatterPoints");
00755     refpoints= MEM_callocN(sizeof(ScatterPoint*)*totpoint, "ScatterRefPoints");
00756 
00757     tree->points= points;
00758     tree->refpoints= refpoints;
00759 
00760     /* build points */
00761     INIT_MINMAX(tree->min, tree->max);
00762 
00763     for(i=0; i<totpoint; i++) {
00764         copy_v3_v3(points[i].co, co[i]);
00765         copy_v3_v3(points[i].rad, color[i]);
00766         points[i].area= fabsf(area[i])/(tree->scale*tree->scale);
00767         points[i].back= (area[i] < 0.0f);
00768 
00769         mul_v3_fl(points[i].co, 1.0f/tree->scale);
00770         DO_MINMAX(points[i].co, tree->min, tree->max);
00771 
00772         refpoints[i]= points + i;
00773     }
00774 
00775     return tree;
00776 }
00777 
00778 void scatter_tree_build(ScatterTree *tree)
00779 {
00780     ScatterPoint *newpoints, **tmppoints;
00781     float mid[3], size[3];
00782     int totpoint= tree->totpoint;
00783 
00784     newpoints= MEM_callocN(sizeof(ScatterPoint)*totpoint, "ScatterPoints");
00785     tmppoints= MEM_callocN(sizeof(ScatterPoint*)*totpoint, "ScatterTmpPoints");
00786     tree->tmppoints= tmppoints;
00787 
00788     tree->arena= BLI_memarena_new(0x8000 * sizeof(ScatterNode), "sss tree arena");
00789     BLI_memarena_use_calloc(tree->arena);
00790 
00791     /* build tree */
00792     tree->root= BLI_memarena_alloc(tree->arena, sizeof(ScatterNode));
00793     tree->root->points= newpoints;
00794     tree->root->totpoint= totpoint;
00795 
00796     mid[0]= (tree->min[0]+tree->max[0])*0.5f;
00797     mid[1]= (tree->min[1]+tree->max[1])*0.5f;
00798     mid[2]= (tree->min[2]+tree->max[2])*0.5f;
00799 
00800     size[0]= (tree->max[0]-tree->min[0])*0.5f;
00801     size[1]= (tree->max[1]-tree->min[1])*0.5f;
00802     size[2]= (tree->max[2]-tree->min[2])*0.5f;
00803 
00804     create_octree_node(tree, tree->root, mid, size, tree->refpoints, 0);
00805 
00806     MEM_freeN(tree->points);
00807     MEM_freeN(tree->refpoints);
00808     MEM_freeN(tree->tmppoints);
00809     tree->refpoints= NULL;
00810     tree->tmppoints= NULL;
00811     tree->points= newpoints;
00812     
00813     /* sum radiance at nodes */
00814     sum_radiance(tree, tree->root);
00815 }
00816 
00817 void scatter_tree_sample(ScatterTree *tree, float *co, float *color)
00818 {
00819     float sco[3];
00820 
00821     copy_v3_v3(sco, co);
00822     mul_v3_fl(sco, 1.0f/tree->scale);
00823 
00824     compute_radiance(tree, sco, color);
00825 }
00826 
00827 void scatter_tree_free(ScatterTree *tree)
00828 {
00829     if (tree->arena) BLI_memarena_free(tree->arena);
00830     if (tree->points) MEM_freeN(tree->points);
00831     if (tree->refpoints) MEM_freeN(tree->refpoints);
00832         
00833     MEM_freeN(tree);
00834 }
00835 
00836 /* Internal Renderer API */
00837 
00838 /* sss tree building */
00839 
00840 typedef struct SSSData {
00841     ScatterTree *tree;
00842     ScatterSettings *ss[3];
00843 } SSSData;
00844 
00845 typedef struct SSSPoints {
00846     struct SSSPoints *next, *prev;
00847 
00848     float (*co)[3];
00849     float (*color)[3];
00850     float *area;
00851     int totpoint;
00852 } SSSPoints;
00853 
00854 static void sss_create_tree_mat(Render *re, Material *mat)
00855 {
00856     SSSPoints *p;
00857     RenderResult *rr;
00858     ListBase points;
00859     float (*co)[3] = NULL, (*color)[3] = NULL, *area = NULL;
00860     int totpoint = 0, osa, osaflag, partsdone;
00861 
00862     if(re->test_break(re->tbh))
00863         return;
00864     
00865     points.first= points.last= NULL;
00866 
00867     /* TODO: this is getting a bit ugly, copying all those variables and
00868        setting them back, maybe we need to create our own Render? */
00869 
00870     /* do SSS preprocessing render */
00871     BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
00872     rr= re->result;
00873     osa= re->osa;
00874     osaflag= re->r.mode & R_OSA;
00875     partsdone= re->i.partsdone;
00876 
00877     re->osa= 0;
00878     re->r.mode &= ~R_OSA;
00879     re->sss_points= &points;
00880     re->sss_mat= mat;
00881     re->i.partsdone= 0;
00882 
00883     if(!(re->r.scemode & R_PREVIEWBUTS))
00884         re->result= NULL;
00885     BLI_rw_mutex_unlock(&re->resultmutex);
00886 
00887     RE_TileProcessor(re);
00888     
00889     BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
00890     if(!(re->r.scemode & R_PREVIEWBUTS)) {
00891         RE_FreeRenderResult(re->result);
00892         re->result= rr;
00893     }
00894     BLI_rw_mutex_unlock(&re->resultmutex);
00895 
00896     re->i.partsdone= partsdone;
00897     re->sss_mat= NULL;
00898     re->sss_points= NULL;
00899     re->osa= osa;
00900     if (osaflag) re->r.mode |= R_OSA;
00901 
00902     /* no points? no tree */
00903     if(!points.first)
00904         return;
00905 
00906     /* merge points together into a single buffer */
00907     if(!re->test_break(re->tbh)) {
00908         for(totpoint=0, p=points.first; p; p=p->next)
00909             totpoint += p->totpoint;
00910         
00911         co= MEM_mallocN(sizeof(*co)*totpoint, "SSSCo");
00912         color= MEM_mallocN(sizeof(*color)*totpoint, "SSSColor");
00913         area= MEM_mallocN(sizeof(*area)*totpoint, "SSSArea");
00914 
00915         for(totpoint=0, p=points.first; p; p=p->next) {
00916             memcpy(co+totpoint, p->co, sizeof(*co)*p->totpoint);
00917             memcpy(color+totpoint, p->color, sizeof(*color)*p->totpoint);
00918             memcpy(area+totpoint, p->area, sizeof(*area)*p->totpoint);
00919             totpoint += p->totpoint;
00920         }
00921     }
00922 
00923     /* free points */
00924     for(p=points.first; p; p=p->next) {
00925         MEM_freeN(p->co);
00926         MEM_freeN(p->color);
00927         MEM_freeN(p->area);
00928     }
00929     BLI_freelistN(&points);
00930 
00931     /* build tree */
00932     if(!re->test_break(re->tbh)) {
00933         SSSData *sss= MEM_callocN(sizeof(*sss), "SSSData");
00934         float ior= mat->sss_ior, cfac= mat->sss_colfac;
00935         float *radius= mat->sss_radius;
00936         float fw= mat->sss_front, bw= mat->sss_back;
00937         float error = mat->sss_error;
00938 
00939         error= get_render_aosss_error(&re->r, error);
00940         if((re->r.scemode & R_PREVIEWBUTS) && error < 0.5f)
00941             error= 0.5f;
00942         
00943         sss->ss[0]= scatter_settings_new(mat->sss_col[0], radius[0], ior, cfac, fw, bw);
00944         sss->ss[1]= scatter_settings_new(mat->sss_col[1], radius[1], ior, cfac, fw, bw);
00945         sss->ss[2]= scatter_settings_new(mat->sss_col[2], radius[2], ior, cfac, fw, bw);
00946         sss->tree= scatter_tree_new(sss->ss, mat->sss_scale, error,
00947             co, color, area, totpoint);
00948 
00949         MEM_freeN(co);
00950         MEM_freeN(color);
00951         MEM_freeN(area);
00952 
00953         scatter_tree_build(sss->tree);
00954 
00955         BLI_ghash_insert(re->sss_hash, mat, sss);
00956     }
00957     else {
00958         if (co) MEM_freeN(co);
00959         if (color) MEM_freeN(color);
00960         if (area) MEM_freeN(area);
00961     }
00962 }
00963 
00964 void sss_add_points(Render *re, float (*co)[3], float (*color)[3], float *area, int totpoint)
00965 {
00966     SSSPoints *p;
00967     
00968     if(totpoint > 0) {
00969         p= MEM_callocN(sizeof(SSSPoints), "SSSPoints");
00970 
00971         p->co= co;
00972         p->color= color;
00973         p->area= area;
00974         p->totpoint= totpoint;
00975 
00976         BLI_lock_thread(LOCK_CUSTOM1);
00977         BLI_addtail(re->sss_points, p);
00978         BLI_unlock_thread(LOCK_CUSTOM1);
00979     }
00980 }
00981 
00982 static void sss_free_tree(SSSData *sss)
00983 {
00984     scatter_tree_free(sss->tree);
00985     scatter_settings_free(sss->ss[0]);
00986     scatter_settings_free(sss->ss[1]);
00987     scatter_settings_free(sss->ss[2]);
00988     MEM_freeN(sss);
00989 }
00990 
00991 /* public functions */
00992 
00993 void make_sss_tree(Render *re)
00994 {
00995     Material *mat;
00996     
00997     re->sss_hash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "make_sss_tree gh");
00998 
00999     re->i.infostr= "SSS preprocessing";
01000     re->stats_draw(re->sdh, &re->i);
01001     
01002     for(mat= re->main->mat.first; mat; mat= mat->id.next)
01003         if(mat->id.us && (mat->flag & MA_IS_USED) && (mat->sss_flag & MA_DIFF_SSS))
01004             sss_create_tree_mat(re, mat);
01005     
01006     /* XXX preview exception */
01007     /* localizing preview render data is not fun for node trees :( */
01008     if(re->main!=G.main) {
01009         for(mat= G.main->mat.first; mat; mat= mat->id.next)
01010             if(mat->id.us && (mat->flag & MA_IS_USED) && (mat->sss_flag & MA_DIFF_SSS))
01011                 sss_create_tree_mat(re, mat);
01012     }
01013     
01014 }
01015 
01016 void free_sss(Render *re)
01017 {
01018     if(re->sss_hash) {
01019         GHashIterator *it= BLI_ghashIterator_new(re->sss_hash);
01020 
01021         while(!BLI_ghashIterator_isDone(it)) {
01022             sss_free_tree(BLI_ghashIterator_getValue(it));
01023             BLI_ghashIterator_step(it);
01024         }
01025 
01026         BLI_ghashIterator_free(it);
01027         BLI_ghash_free(re->sss_hash, NULL, NULL);
01028         re->sss_hash= NULL;
01029     }
01030 }
01031 
01032 int sample_sss(Render *re, Material *mat, float *co, float *color)
01033 {
01034     if(re->sss_hash) {
01035         SSSData *sss= BLI_ghash_lookup(re->sss_hash, mat);
01036 
01037         if(sss) {
01038             scatter_tree_sample(sss->tree, co, color);
01039             return 1;
01040         }
01041         else {
01042             color[0]= 0.0f;
01043             color[1]= 0.0f;
01044             color[2]= 0.0f;
01045         }
01046     }
01047 
01048     return 0;
01049 }
01050 
01051 int sss_pass_done(struct Render *re, struct Material *mat)
01052 {
01053     return ((re->flag & R_BAKING) || !(re->r.mode & R_SSS) || (re->sss_hash && BLI_ghash_lookup(re->sss_hash, mat)));
01054 }
01055