diff options
author | Campbell Barton <ideasman42@gmail.com> | 2018-06-08 09:10:35 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2018-06-08 09:10:35 +0300 |
commit | 908b6960c01ffb1665af56ff6f03aaa3eac5366a (patch) | |
tree | db609864fbe601073212b4c37541a7965ea96ea5 /source/blender/render/intern/source/rendercore.c | |
parent | d352a0adc5dadd0bfdc3b1b1ac76d92be689966b (diff) | |
parent | a25c11fd8d602236f36c34c342453149bdc1d909 (diff) |
Merge branch 'master' into blender2.8
Diffstat (limited to 'source/blender/render/intern/source/rendercore.c')
-rw-r--r-- | source/blender/render/intern/source/rendercore.c | 2030 |
1 files changed, 2030 insertions, 0 deletions
diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c new file mode 100644 index 00000000000..99d2436d4bc --- /dev/null +++ b/source/blender/render/intern/source/rendercore.c @@ -0,0 +1,2030 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributors: Hos, Robert Wenzlaff. + * Contributors: 2004/2005/2006 Blender Foundation, full recode + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/render/intern/source/rendercore.c + * \ingroup render + */ + + +/* system includes */ +#include <stdio.h> +#include <math.h> +#include <float.h> +#include <string.h> +#include <assert.h> + +/* External modules: */ +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_blenlib.h" +#include "BLI_rand.h" +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "DNA_image_types.h" +#include "DNA_lamp_types.h" +#include "DNA_material_types.h" +#include "DNA_group_types.h" + +/* local include */ +#include "renderpipeline.h" +#include "render_result.h" +#include "render_types.h" +#include "renderdatabase.h" +#include "occlusion.h" +#include "pixelblending.h" +#include "pixelshading.h" +#include "shadbuf.h" +#include "shading.h" +#include "sss.h" +#include "zbuf.h" + +/* own include */ +#include "rendercore.h" + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */ +/* only to be used here in this file, it's for speed */ +extern struct Render R; +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* x and y are current pixels in rect to be rendered */ +/* do not normalize! */ +void calc_view_vector(float view[3], float x, float y) +{ + + view[2]= -ABS(R.clipsta); + + if (R.r.mode & R_ORTHO) { + view[0]= view[1]= 0.0f; + } + else { + + if (R.r.mode & R_PANORAMA) { + x-= R.panodxp; + } + + /* move x and y to real viewplane coords */ + x = (x / (float)R.winx); + view[0] = R.viewplane.xmin + x * BLI_rctf_size_x(&R.viewplane); + + y = (y / (float)R.winy); + view[1] = R.viewplane.ymin + y * BLI_rctf_size_y(&R.viewplane); + +// if (R.flag & R_SEC_FIELD) { +// if (R.r.mode & R_ODDFIELD) view[1]= (y+R.ystart)*R.ycor; +// else view[1]= (y+R.ystart+1.0)*R.ycor; +// } +// else view[1]= (y+R.ystart+R.bluroffsy+0.5)*R.ycor; + + if (R.r.mode & R_PANORAMA) { + float u= view[0] + R.panodxv; float v= view[2]; + view[0]= R.panoco*u + R.panosi*v; + view[2]= -R.panosi*u + R.panoco*v; + } + } +} + +void calc_renderco_ortho(float co[3], float x, float y, int z) +{ + /* x and y 3d coordinate can be derived from pixel coord and winmat */ + float fx= 2.0f/(R.winx*R.winmat[0][0]); + float fy= 2.0f/(R.winy*R.winmat[1][1]); + float zco; + + co[0]= (x - 0.5f*R.winx)*fx - R.winmat[3][0]/R.winmat[0][0]; + co[1]= (y - 0.5f*R.winy)*fy - R.winmat[3][1]/R.winmat[1][1]; + + zco= ((float)z)/2147483647.0f; + co[2]= R.winmat[3][2]/( R.winmat[2][3]*zco - R.winmat[2][2] ); +} + +void calc_renderco_zbuf(float co[3], const float view[3], int z) +{ + float fac, zco; + + /* inverse of zbuf calc: zbuf = MAXZ*hoco_z/hoco_w */ + zco= ((float)z)/2147483647.0f; + co[2]= R.winmat[3][2]/( R.winmat[2][3]*zco - R.winmat[2][2] ); + + fac= co[2]/view[2]; + co[0]= fac*view[0]; + co[1]= fac*view[1]; +} + +/* also used in zbuf.c and shadbuf.c */ +int count_mask(unsigned short mask) +{ + if (R.samples) + return (R.samples->cmask[mask & 255]+R.samples->cmask[mask>>8]); + return 0; +} + +static int calchalo_z(HaloRen *har, int zz) +{ + + if (har->type & HA_ONLYSKY) { + if (zz < 0x7FFFFFF0) zz= - 0x7FFFFF; /* edge render messes zvalues */ + } + else { + zz= (zz>>8); + } + return zz; +} + + + +static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, int od, float dist, float xn, float yn, PixStr *ps) +{ + float col[4], accol[4], fac; + int amount, amountm, zz, flarec, sample, fullsample, mask=0; + + fullsample= (totsample > 1); + amount= 0; + accol[0] = accol[1] = accol[2] = accol[3]= 0.0f; + col[0] = col[1] = col[2] = col[3]= 0.0f; + flarec= har->flarec; + + while (ps) { + amountm= count_mask(ps->mask); + amount+= amountm; + + zz= calchalo_z(har, ps->z); + if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) { + if (shadeHaloFloat(har, col, zz, dist, xn, yn, flarec)) { + flarec= 0; + + if (fullsample) { + for (sample=0; sample<totsample; sample++) { + if (ps->mask & (1 << sample)) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } + } + } + else { + fac= ((float)amountm)/(float)R.osa; + accol[0]+= fac*col[0]; + accol[1]+= fac*col[1]; + accol[2]+= fac*col[2]; + accol[3]+= fac*col[3]; + } + } + } + + mask |= ps->mask; + ps= ps->next; + } + + /* now do the sky sub-pixels */ + amount= R.osa-amount; + if (amount) { + if (shadeHaloFloat(har, col, 0x7FFFFF, dist, xn, yn, flarec)) { + if (!fullsample) { + fac= ((float)amount)/(float)R.osa; + accol[0]+= fac*col[0]; + accol[1]+= fac*col[1]; + accol[2]+= fac*col[2]; + accol[3]+= fac*col[3]; + } + } + } + + if (fullsample) { + for (sample=0; sample<totsample; sample++) { + if (!(mask & (1 << sample))) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } + } + } + else { + col[0]= accol[0]; + col[1]= accol[1]; + col[2]= accol[2]; + col[3]= accol[3]; + + for (sample=0; sample<totsample; sample++) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } + } +} + +static void halo_tile(RenderPart *pa, RenderLayer *rl) +{ + RenderLayer *rlpp[RE_MAX_OSA]; + HaloRen *har; + rcti disprect= pa->disprect, testrect= pa->disprect; + float dist, xsq, ysq, xn, yn; + float col[4]; + intptr_t *rd= NULL; + int a, *rz, zz, y, sample, totsample, od; + short minx, maxx, miny, maxy, x; + unsigned int lay= rl->lay; + + /* we don't render halos in the cropped area, gives errors in flare counter */ + if (pa->crop) { + testrect.xmin+= pa->crop; + testrect.xmax-= pa->crop; + testrect.ymin+= pa->crop; + testrect.ymax-= pa->crop; + } + + totsample= get_sample_layers(pa, rl, rlpp); + + for (a=0; a<R.tothalo; a++) { + har= R.sortedhalos[a]; + + /* layer test, clip halo with y */ + if ((har->lay & lay) == 0) { + /* pass */ + } + else if (testrect.ymin > har->maxy) { + /* pass */ + } + else if (testrect.ymax < har->miny) { + /* pass */ + } + else { + + minx= floor(har->xs-har->rad); + maxx= ceil(har->xs+har->rad); + + if (testrect.xmin > maxx) { + /* pass */ + } + else if (testrect.xmax < minx) { + /* pass */ + } + else { + + minx = max_ii(minx, testrect.xmin); + maxx = min_ii(maxx, testrect.xmax); + + miny = max_ii(har->miny, testrect.ymin); + maxy = min_ii(har->maxy, testrect.ymax); + + for (y=miny; y<maxy; y++) { + int rectofs= (y-disprect.ymin)*pa->rectx + (minx - disprect.xmin); + rz= pa->rectz + rectofs; + od= rectofs; + + if (pa->rectdaps) + rd= pa->rectdaps + rectofs; + + yn= (y-har->ys)*R.ycor; + ysq= yn*yn; + + for (x=minx; x<maxx; x++, rz++, od++) { + xn= x- har->xs; + xsq= xn*xn; + dist= xsq+ysq; + if (dist<har->radsq) { + if (rd && *rd) { + halo_pixelstruct(har, rlpp, totsample, od, dist, xn, yn, (PixStr *)*rd); + } + else { + zz= calchalo_z(har, *rz); + if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) { + if (shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec)) { + for (sample=0; sample<totsample; sample++) { + float * rect= RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname); + addalphaAddfacFloat(rect + od*4, col, har->add); + } + } + } + } + } + if (rd) rd++; + } + } + } + } + if (R.test_break(R.tbh) ) break; + } +} + +static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) +{ + RenderLayer *rlpp[RE_MAX_OSA]; + ShadeInput shi; + float *pass; + float fac, col[4]; + intptr_t *rd= pa->rectdaps; + const int *rz= pa->rectz; + int x, y, sample, totsample, fullsample, od; + + totsample= get_sample_layers(pa, rl, rlpp); + fullsample= (totsample > 1); + + shade_input_initialize(&shi, pa, rl, 0); /* this zero's ShadeInput for us */ + + for (od=0, y=pa->disprect.ymin; y<pa->disprect.ymax; y++) { + for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, rz++, od++) { + + calc_view_vector(shi.view, x, y); + + if (rd && *rd) { + PixStr *ps= (PixStr *)*rd; + int count, totsamp= 0, mask= 0; + + while (ps) { + if (R.r.mode & R_ORTHO) + calc_renderco_ortho(shi.co, (float)x, (float)y, ps->z); + else + calc_renderco_zbuf(shi.co, shi.view, ps->z); + + totsamp+= count= count_mask(ps->mask); + mask |= ps->mask; + + col[0]= col[1]= col[2]= col[3]= 0.0f; + renderspothalo(&shi, col, 1.0f); + + if (fullsample) { + for (sample=0; sample<totsample; sample++) { + if (ps->mask & (1 << sample)) { + pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname); + pass += od * 4; + pass[0]+= col[0]; + pass[1]+= col[1]; + pass[2]+= col[2]; + pass[3]+= col[3]; + if (pass[3]>1.0f) pass[3]= 1.0f; + } + } + } + else { + fac= ((float)count)/(float)R.osa; + pass = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname); + pass += od * 4; + pass[0]+= fac*col[0]; + pass[1]+= fac*col[1]; + pass[2]+= fac*col[2]; + pass[3]+= fac*col[3]; + if (pass[3]>1.0f) pass[3]= 1.0f; + } + + ps= ps->next; + } + + if (totsamp<R.osa) { + shi.co[2]= 0.0f; + + col[0]= col[1]= col[2]= col[3]= 0.0f; + renderspothalo(&shi, col, 1.0f); + + if (fullsample) { + for (sample=0; sample<totsample; sample++) { + if (!(mask & (1 << sample))) { + + pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname); + pass += od * 4; + pass[0]+= col[0]; + pass[1]+= col[1]; + pass[2]+= col[2]; + pass[3]+= col[3]; + if (pass[3]>1.0f) pass[3]= 1.0f; + } + } + } + else { + fac= ((float)R.osa-totsamp)/(float)R.osa; + pass = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname); + pass += od * 4; + pass[0]+= fac*col[0]; + pass[1]+= fac*col[1]; + pass[2]+= fac*col[2]; + pass[3]+= fac*col[3]; + if (pass[3]>1.0f) pass[3]= 1.0f; + } + } + } + else { + if (R.r.mode & R_ORTHO) + calc_renderco_ortho(shi.co, (float)x, (float)y, *rz); + else + calc_renderco_zbuf(shi.co, shi.view, *rz); + + col[0]= col[1]= col[2]= col[3]= 0.0f; + renderspothalo(&shi, col, 1.0f); + + for (sample=0; sample<totsample; sample++) { + pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname); + pass += od * 4; + pass[0]+= col[0]; + pass[1]+= col[1]; + pass[2]+= col[2]; + pass[3]+= col[3]; + if (pass[3]>1.0f) pass[3]= 1.0f; + } + } + + if (rd) rd++; + } + if (y&1) + if (R.test_break(R.tbh)) break; + } +} + + +/* ********************* MAINLOOPS ******************** */ + +/* osa version */ +static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset, ShadeInput *shi, ShadeResult *shr) +{ + RenderPass *rpass; + + for (rpass= rl->passes.first; rpass; rpass= rpass->next) { + float *fp, *col= NULL; + int pixsize= 3; + + if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) { + add_filt_fmask(curmask, shr->combined, rpass->rect + 4*offset, rectx); + } + else if (STREQ(rpass->name, RE_PASSNAME_Z)) { + fp = rpass->rect + offset; + *fp = shr->z; + } + else if (STREQ(rpass->name, RE_PASSNAME_RGBA)) { + col = shr->col; + pixsize = 4; + } + else if (STREQ(rpass->name, RE_PASSNAME_EMIT)) { + col = shr->emit; + } + else if (STREQ(rpass->name, RE_PASSNAME_DIFFUSE)) { + col = shr->diff; + } + else if (STREQ(rpass->name, RE_PASSNAME_SPEC)) { + col = shr->spec; + } + else if (STREQ(rpass->name, RE_PASSNAME_SHADOW)) { + col = shr->shad; + } + else if (STREQ(rpass->name, RE_PASSNAME_AO)) { + col = shr->ao; + } + else if (STREQ(rpass->name, RE_PASSNAME_ENVIRONMENT)) { + col = shr->env; + } + else if (STREQ(rpass->name, RE_PASSNAME_INDIRECT)) { + col = shr->indirect; + } + else if (STREQ(rpass->name, RE_PASSNAME_REFLECT)) { + col = shr->refl; + } + else if (STREQ(rpass->name, RE_PASSNAME_REFRACT)) { + col = shr->refr; + } + else if (STREQ(rpass->name, RE_PASSNAME_NORMAL)) { + col = shr->nor; + } + else if (STREQ(rpass->name, RE_PASSNAME_UV)) { + /* box filter only, gauss will screwup UV too much */ + if (shi->totuv) { + float mult = (float)count_mask(curmask)/(float)R.osa; + fp = rpass->rect + 3*offset; + fp[0]+= mult*(0.5f + 0.5f*shi->uv[shi->actuv].uv[0]); + fp[1]+= mult*(0.5f + 0.5f*shi->uv[shi->actuv].uv[1]); + fp[2]+= mult; + } + } + else if (STREQ(rpass->name, RE_PASSNAME_INDEXOB)) { + /* no filter */ + if (shi->vlr) { + fp = rpass->rect + offset; + if (*fp==0.0f) + *fp = (float)shi->obr->ob->index; + } + } + else if (STREQ(rpass->name, RE_PASSNAME_INDEXMA)) { + /* no filter */ + if (shi->vlr) { + fp = rpass->rect + offset; + if (*fp==0.0f) + *fp = (float)shi->mat->index; + } + } + else if (STREQ(rpass->name, RE_PASSNAME_MIST)) { + /* */ + col = &shr->mist; + pixsize = 1; + } + else if (STREQ(rpass->name, RE_PASSNAME_VECTOR)) { + /* add minimum speed in pixel, no filter */ + fp = rpass->rect + 4*offset; + if ( (ABS(shr->winspeed[0]) + ABS(shr->winspeed[1]))< (ABS(fp[0]) + ABS(fp[1])) ) { + fp[0] = shr->winspeed[0]; + fp[1] = shr->winspeed[1]; + } + if ( (ABS(shr->winspeed[2]) + ABS(shr->winspeed[3]))< (ABS(fp[2]) + ABS(fp[3])) ) { + fp[2] = shr->winspeed[2]; + fp[3] = shr->winspeed[3]; + } + } + else if (STREQ(rpass->name, RE_PASSNAME_RAYHITS)) { + /* */ + col = shr->rayhits; + pixsize= 4; + } + + if (col) { + fp= rpass->rect + pixsize*offset; + add_filt_fmask_pixsize(curmask, col, fp, rectx, pixsize); + } + } +} + +/* non-osa version */ +static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult *shr) +{ + RenderPass *rpass; + float *fp; + + for (rpass= rl->passes.first; rpass; rpass= rpass->next) { + float *col= NULL, uvcol[3]; + int a, pixsize= 3; + + if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) { + /* copy combined to use for preview */ + copy_v4_v4(rpass->rect + 4*offset, shr->combined); + } + else if (STREQ(rpass->name, RE_PASSNAME_Z)) { + fp = rpass->rect + offset; + *fp = shr->z; + } + else if (STREQ(rpass->name, RE_PASSNAME_RGBA)) { + col = shr->col; + pixsize = 4; + } + else if (STREQ(rpass->name, RE_PASSNAME_EMIT)) { + col = shr->emit; + } + else if (STREQ(rpass->name, RE_PASSNAME_DIFFUSE)) { + col = shr->diff; + } + else if (STREQ(rpass->name, RE_PASSNAME_SPEC)) { + col = shr->spec; + } + else if (STREQ(rpass->name, RE_PASSNAME_SHADOW)) { + col = shr->shad; + } + else if (STREQ(rpass->name, RE_PASSNAME_AO)) { + col = shr->ao; + } + else if (STREQ(rpass->name, RE_PASSNAME_ENVIRONMENT)) { + col = shr->env; + } + else if (STREQ(rpass->name, RE_PASSNAME_INDIRECT)) { + col = shr->indirect; + } + else if (STREQ(rpass->name, RE_PASSNAME_REFLECT)) { + col = shr->refl; + } + else if (STREQ(rpass->name, RE_PASSNAME_REFRACT)) { + col = shr->refr; + } + else if (STREQ(rpass->name, RE_PASSNAME_NORMAL)) { + col = shr->nor; + } + else if (STREQ(rpass->name, RE_PASSNAME_UV)) { + if (shi->totuv) { + uvcol[0] = 0.5f + 0.5f*shi->uv[shi->actuv].uv[0]; + uvcol[1] = 0.5f + 0.5f*shi->uv[shi->actuv].uv[1]; + uvcol[2] = 1.0f; + col = uvcol; + } + } + else if (STREQ(rpass->name, RE_PASSNAME_VECTOR)) { + col = shr->winspeed; + pixsize = 4; + } + else if (STREQ(rpass->name, RE_PASSNAME_INDEXOB)) { + if (shi->vlr) { + fp = rpass->rect + offset; + *fp = (float)shi->obr->ob->index; + } + } + else if (STREQ(rpass->name, RE_PASSNAME_INDEXMA)) { + if (shi->vlr) { + fp = rpass->rect + offset; + *fp = (float)shi->mat->index; + } + } + else if (STREQ(rpass->name, RE_PASSNAME_MIST)) { + fp = rpass->rect + offset; + *fp = shr->mist; + } + else if (STREQ(rpass->name, RE_PASSNAME_RAYHITS)) { + col = shr->rayhits; + pixsize = 4; + } + + if (col) { + fp = rpass->rect + pixsize*offset; + for (a=0; a<pixsize; a++) + fp[a] = col[a]; + } + } +} + +int get_sample_layers(RenderPart *pa, RenderLayer *rl, RenderLayer **rlpp) +{ + + if (pa->fullresult.first) { + int sample, nr= BLI_findindex(&pa->result->layers, rl); + + for (sample=0; sample<R.osa; sample++) { + RenderResult *rr= BLI_findlink(&pa->fullresult, sample); + + rlpp[sample]= BLI_findlink(&rr->layers, nr); + } + return R.osa; + } + else { + rlpp[0]= rl; + return 1; + } +} + + +/* only do sky, is default in the solid layer (shade_tile) btw */ +static void sky_tile(RenderPart *pa, RenderLayer *rl) +{ + RenderLayer *rlpp[RE_MAX_OSA]; + int x, y, od=0, totsample; + + if (R.r.alphamode!=R_ADDSKY) + return; + + totsample= get_sample_layers(pa, rl, rlpp); + + for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++) { + for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, od+=4) { + float col[4]; + int sample; + bool done = false; + + for (sample= 0; sample<totsample; sample++) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname); + pass += od; + + if (pass[3]<1.0f) { + + if (done==0) { + shadeSkyPixel(col, x, y, pa->thread); + done = true; + } + + if (pass[3]==0.0f) { + copy_v4_v4(pass, col); + pass[3] = 1.0f; + } + else { + addAlphaUnderFloat(pass, col); + pass[3] = 1.0f; + } + } + } + } + + if (y&1) + if (R.test_break(R.tbh)) break; + } +} + +static void atm_tile(RenderPart *pa, RenderLayer *rl) +{ + RenderPass *zpass; + GroupObject *go; + LampRen *lar; + RenderLayer *rlpp[RE_MAX_OSA]; + int totsample; + int x, y, od= 0; + + totsample= get_sample_layers(pa, rl, rlpp); + + /* check that z pass is enabled */ + if (pa->rectz==NULL) return; + for (zpass= rl->passes.first; zpass; zpass= zpass->next) + if (STREQ(zpass->name, RE_PASSNAME_Z)) + break; + + if (zpass==NULL) return; + + /* check for at least one sun lamp that its atmosphere flag is enabled */ + for (go=R.lights.first; go; go= go->next) { + lar= go->lampren; + if (lar->type==LA_SUN && lar->sunsky && (lar->sunsky->effect_type & LA_SUN_EFFECT_AP)) + break; + } + /* do nothign and return if there is no sun lamp */ + if (go==NULL) + return; + + /* for each x,y and each sample, and each sun lamp*/ + for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++) { + for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, od++) { + int sample; + + for (sample=0; sample<totsample; sample++) { + const float *zrect = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_Z, R.viewname) + od; + float *rgbrect = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname) + 4*od; + float rgb[3] = {0}; + bool done = false; + + for (go=R.lights.first; go; go= go->next) { + + + lar= go->lampren; + if (lar->type==LA_SUN && lar->sunsky) { + + /* if it's sky continue and don't apply atmosphere effect on it */ + if (*zrect >= 9.9e10f || rgbrect[3]==0.0f) { + continue; + } + + if ((lar->sunsky->effect_type & LA_SUN_EFFECT_AP)) { + float tmp_rgb[3]; + + /* skip if worldspace lamp vector is below horizon */ + if (go->ob->obmat[2][2] < 0.f) { + continue; + } + + copy_v3_v3(tmp_rgb, rgbrect); + if (rgbrect[3]!=1.0f) { /* de-premul */ + mul_v3_fl(tmp_rgb, 1.0f/rgbrect[3]); + } + shadeAtmPixel(lar->sunsky, tmp_rgb, x, y, *zrect); + if (rgbrect[3]!=1.0f) { /* premul */ + mul_v3_fl(tmp_rgb, rgbrect[3]); + } + + if (done==0) { + copy_v3_v3(rgb, tmp_rgb); + done = true; + } + else { + rgb[0] = 0.5f*rgb[0] + 0.5f*tmp_rgb[0]; + rgb[1] = 0.5f*rgb[1] + 0.5f*tmp_rgb[1]; + rgb[2] = 0.5f*rgb[2] + 0.5f*tmp_rgb[2]; + } + } + } + } + + /* if at least for one sun lamp aerial perspective was applied*/ + if (done) { + copy_v3_v3(rgbrect, rgb); + } + } + } + } +} + +static void shadeDA_tile(RenderPart *pa, RenderLayer *rl) +{ + RenderResult *rr= pa->result; + ShadeSample ssamp; + intptr_t *rd, *rectdaps= pa->rectdaps; + int samp; + int x, y, seed, crop=0, offs=0, od; + + if (R.test_break(R.tbh)) return; + + /* irregular shadowb buffer creation */ + if (R.r.mode & R_SHADOW) + ISB_create(pa, NULL); + + /* we set per pixel a fixed seed, for random AO and shadow samples */ + seed= pa->rectx*pa->disprect.ymin; + + /* general shader info, passes */ + shade_sample_initialize(&ssamp, pa, rl); + + /* occlusion caching */ + if (R.occlusiontree) + cache_occ_samples(&R, pa, &ssamp); + + /* filtered render, for now we assume only 1 filter size */ + if (pa->crop) { + crop= 1; + rectdaps+= pa->rectx + 1; + offs= pa->rectx + 1; + } + + /* scanline updates have to be 2 lines behind */ + rr->renrect.ymin = 0; + rr->renrect.ymax = -2*crop; + rr->renlay= rl; + + for (y=pa->disprect.ymin+crop; y<pa->disprect.ymax-crop; y++, rr->renrect.ymax++) { + rd= rectdaps; + od= offs; + + for (x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, rd++, od++) { + BLI_thread_srandom(pa->thread, seed++); + + if (*rd) { + if (shade_samples(&ssamp, (PixStr *)(*rd), x, y)) { + + /* multisample buffers or filtered mask filling? */ + if (pa->fullresult.first) { + int a; + for (samp=0; samp<ssamp.tot; samp++) { + int smask= ssamp.shi[samp].mask; + for (a=0; a<R.osa; a++) { + int mask= 1<<a; + if (smask & mask) + add_passes(ssamp.rlpp[a], od, &ssamp.shi[samp], &ssamp.shr[samp]); + } + } + } + else { + for (samp=0; samp<ssamp.tot; samp++) + add_filt_passes(rl, ssamp.shi[samp].mask, pa->rectx, od, &ssamp.shi[samp], &ssamp.shr[samp]); + } + } + } + } + + rectdaps+= pa->rectx; + offs+= pa->rectx; + + if (y&1) if (R.test_break(R.tbh)) break; + } + + /* disable scanline updating */ + rr->renlay= NULL; + + if (R.r.mode & R_SHADOW) + ISB_free(pa); + + if (R.occlusiontree) + free_occ_samples(&R, pa); +} + +/* ************* pixel struct ******** */ + + +static PixStrMain *addpsmain(ListBase *lb) +{ + PixStrMain *psm; + + psm= (PixStrMain *)MEM_mallocN(sizeof(PixStrMain), "pixstrMain"); + BLI_addtail(lb, psm); + + psm->ps= (PixStr *)MEM_mallocN(4096*sizeof(PixStr), "pixstr"); + psm->counter= 0; + + return psm; +} + +static void freeps(ListBase *lb) +{ + PixStrMain *psm, *psmnext; + + for (psm= lb->first; psm; psm= psmnext) { + psmnext= psm->next; + if (psm->ps) + MEM_freeN(psm->ps); + MEM_freeN(psm); + } + BLI_listbase_clear(lb); +} + +static void addps(ListBase *lb, intptr_t *rd, int obi, int facenr, int z, int maskz, unsigned short mask) +{ + PixStrMain *psm; + PixStr *ps, *last= NULL; + + if (*rd) { + ps= (PixStr *)(*rd); + + while (ps) { + if ( ps->obi == obi && ps->facenr == facenr ) { + ps->mask |= mask; + return; + } + last= ps; + ps= ps->next; + } + } + + /* make new PS (pixel struct) */ + psm= lb->last; + + if (psm->counter==4095) + psm= addpsmain(lb); + + ps= psm->ps + psm->counter++; + + if (last) last->next= ps; + else *rd= (intptr_t)ps; + + ps->next= NULL; + ps->obi= obi; + ps->facenr= facenr; + ps->z= z; + ps->maskz= maskz; + ps->mask = mask; + ps->shadfac= 0; +} + +static void edge_enhance_add(RenderPart *pa, float *rectf, float *arect) +{ + float addcol[4]; + int pix; + + if (arect==NULL) + return; + + for (pix= pa->rectx*pa->recty; pix>0; pix--, arect++, rectf+=4) { + if (*arect != 0.0f) { + addcol[0]= *arect * R.r.edgeR; + addcol[1]= *arect * R.r.edgeG; + addcol[2]= *arect * R.r.edgeB; + addcol[3]= *arect; + addAlphaOverFloat(rectf, addcol); + } + } +} + +/* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */ +static void clamp_alpha_rgb_range(RenderPart *pa, RenderLayer *rl) +{ + RenderLayer *rlpp[RE_MAX_OSA]; + int y, sample, totsample; + + totsample= get_sample_layers(pa, rl, rlpp); + + /* not for full sample, there we clamp after compositing */ + if (totsample > 1) + return; + + for (sample= 0; sample<totsample; sample++) { + float *rectf = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname); + + for (y= pa->rectx*pa->recty; y>0; y--, rectf+=4) { + rectf[0] = MAX2(rectf[0], 0.0f); + rectf[1] = MAX2(rectf[1], 0.0f); + rectf[2] = MAX2(rectf[2], 0.0f); + CLAMP(rectf[3], 0.0f, 1.0f); + } + } +} + +/* adds only alpha values */ +static void edge_enhance_tile(RenderPart *pa, float *rectf, int *rectz) +{ + /* use zbuffer to define edges, add it to the image */ + int y, x, col, *rz, *rz1, *rz2, *rz3; + int zval1, zval2, zval3; + float *rf; + + /* shift values in zbuffer 4 to the right (anti overflows), for filter we need multiplying with 12 max */ + rz= rectz; + if (rz==NULL) return; + + for (y=0; y<pa->recty; y++) + for (x=0; x<pa->rectx; x++, rz++) (*rz)>>= 4; + + rz1= rectz; + rz2= rz1+pa->rectx; + rz3= rz2+pa->rectx; + + rf= rectf+pa->rectx+1; + + for (y=0; y<pa->recty-2; y++) { + for (x=0; x<pa->rectx-2; x++, rz1++, rz2++, rz3++, rf++) { + + /* prevent overflow with sky z values */ + zval1= rz1[0] + 2*rz1[1] + rz1[2]; + zval2= 2*rz2[0] + 2*rz2[2]; + zval3= rz3[0] + 2*rz3[1] + rz3[2]; + + col= ( 4*rz2[1] - (zval1 + zval2 + zval3)/3 ); + if (col<0) col= -col; + + col >>= 5; + if (col > (1<<16)) col= (1<<16); + else col= (R.r.edgeint*col)>>8; + + if (col>0) { + float fcol; + + if (col>255) fcol= 1.0f; + else fcol= (float)col/255.0f; + + if (R.osa) + *rf+= fcol/(float)R.osa; + else + *rf= fcol; + } + } + rz1+= 2; + rz2+= 2; + rz3+= 2; + rf+= 2; + } + + /* shift back zbuf values, we might need it still */ + rz= rectz; + for (y=0; y<pa->recty; y++) + for (x=0; x<pa->rectx; x++, rz++) (*rz)<<= 4; + +} + +static void reset_sky_speed(RenderPart *pa, RenderLayer *rl) +{ + /* for all pixels with max speed, set to zero */ + RenderLayer *rlpp[RE_MAX_OSA]; + float *fp; + int a, sample, totsample; + + totsample= get_sample_layers(pa, rl, rlpp); + + for (sample= 0; sample<totsample; sample++) { + fp= RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_VECTOR, R.viewname); + if (fp==NULL) break; + + for (a= 4*pa->rectx*pa->recty - 1; a>=0; a--) + if (fp[a] == PASS_VECTOR_MAX) fp[a]= 0.0f; + } +} + +static unsigned short *make_solid_mask(RenderPart *pa) +{ + intptr_t *rd= pa->rectdaps; + unsigned short *solidmask, *sp; + int x; + + if (rd==NULL) return NULL; + + sp=solidmask= MEM_mallocN(sizeof(short)*pa->rectx*pa->recty, "solidmask"); + + for (x=pa->rectx*pa->recty; x>0; x--, rd++, sp++) { + if (*rd) { + PixStr *ps= (PixStr *)*rd; + + *sp= ps->mask; + for (ps= ps->next; ps; ps= ps->next) + *sp |= ps->mask; + } + else + *sp= 0; + } + + return solidmask; +} + +static void addAlphaOverFloatMask(float *dest, float *source, unsigned short dmask, unsigned short smask) +{ + unsigned short shared= dmask & smask; + float mul= 1.0f - source[3]; + + if (shared) { /* overlapping masks */ + + /* masks differ, we make a mixture of 'add' and 'over' */ + if (shared!=dmask) { + float shared_bits= (float)count_mask(shared); /* alpha over */ + float tot_bits= (float)count_mask(smask|dmask); /* alpha add */ + + float add= (tot_bits - shared_bits)/tot_bits; /* add level */ + mul= add + (1.0f-add)*mul; + } + } + else if (dmask && smask) { + /* works for premul only, of course */ + dest[0]+= source[0]; + dest[1]+= source[1]; + dest[2]+= source[2]; + dest[3]+= source[3]; + + return; + } + + dest[0]= (mul*dest[0]) + source[0]; + dest[1]= (mul*dest[1]) + source[1]; + dest[2]= (mul*dest[2]) + source[2]; + dest[3]= (mul*dest[3]) + source[3]; +} + +typedef struct ZbufSolidData { + RenderLayer *rl; + ListBase *psmlist; + float *edgerect; +} ZbufSolidData; + +static void make_pixelstructs(RenderPart *pa, ZSpan *zspan, int sample, void *data) +{ + ZbufSolidData *sdata = (ZbufSolidData *)data; + ListBase *lb= sdata->psmlist; + intptr_t *rd= pa->rectdaps; + const int *ro= zspan->recto; + const int *rp= zspan->rectp; + const int *rz= zspan->rectz; + const int *rm= zspan->rectmask; + int x, y; + int mask= 1<<sample; + + for (y=0; y<pa->recty; y++) { + for (x=0; x<pa->rectx; x++, rd++, rp++, ro++, rz++, rm++) { + if (*rp) { + addps(lb, rd, *ro, *rp, *rz, (zspan->rectmask)? *rm: 0, mask); + } + } + } + + if (sdata->rl->layflag & SCE_LAY_EDGE) + if (R.r.mode & R_EDGE) + edge_enhance_tile(pa, sdata->edgerect, zspan->rectz); +} + +/* main call for shading Delta Accum, for OSA */ +/* supposed to be fully threadable! */ +void zbufshadeDA_tile(RenderPart *pa) +{ + RenderResult *rr= pa->result; + RenderLayer *rl; + ListBase psmlist= {NULL, NULL}; + float *edgerect= NULL; + + /* allocate the necessary buffers */ + /* zbuffer inits these rects */ + pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto"); + pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp"); + pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); + for (rl= rr->layers.first; rl; rl= rl->next) { + float *rect = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname); + + if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) + pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask"); + + /* initialize pixelstructs and edge buffer */ + addpsmain(&psmlist); + pa->rectdaps= MEM_callocN(sizeof(intptr_t)*pa->rectx*pa->recty+4, "zbufDArectd"); + + if (rl->layflag & SCE_LAY_EDGE) + if (R.r.mode & R_EDGE) + edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge"); + + /* always fill visibility */ + for (pa->sample=0; pa->sample<R.osa; pa->sample+=4) { + ZbufSolidData sdata; + + sdata.rl= rl; + sdata.psmlist= &psmlist; + sdata.edgerect= edgerect; + zbuffer_solid(pa, rl, make_pixelstructs, &sdata); + if (R.test_break(R.tbh)) break; + } + + /* shades solid */ + if (rl->layflag & SCE_LAY_SOLID) + shadeDA_tile(pa, rl); + + /* lamphalo after solid, before ztra, looks nicest because ztra does own halo */ + if (R.flag & R_LAMPHALO) + if (rl->layflag & SCE_LAY_HALO) + lamphalo_tile(pa, rl); + + /* halo before ztra, because ztra fills in zbuffer now */ + if (R.flag & R_HALO) + if (rl->layflag & SCE_LAY_HALO) + halo_tile(pa, rl); + + /* transp layer */ + if (R.flag & R_ZTRA || R.totstrand) { + if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) { + if (pa->fullresult.first) { + zbuffer_transp_shade(pa, rl, rect, &psmlist); + } + else { + unsigned short *ztramask, *solidmask= NULL; /* 16 bits, MAX_OSA */ + + /* allocate, but not free here, for asynchronous display of this rect in main thread */ + rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer"); + + /* swap for live updates, and it is used in zbuf.c!!! */ + SWAP(float *, rl->acolrect, rect); + ztramask = zbuffer_transp_shade(pa, rl, rect, &psmlist); + SWAP(float *, rl->acolrect, rect); + + /* zbuffer transp only returns ztramask if there's solid rendered */ + if (ztramask) + solidmask= make_solid_mask(pa); + + if (ztramask && solidmask) { + unsigned short *sps= solidmask, *spz= ztramask; + unsigned short fullmask= (1<<R.osa)-1; + float *fcol= rect; + float *acol= rl->acolrect; + int x; + + for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4, sps++, spz++) { + if (*sps == fullmask) + addAlphaOverFloat(fcol, acol); + else + addAlphaOverFloatMask(fcol, acol, *sps, *spz); + } + } + else { + float *fcol= rect; + float *acol= rl->acolrect; + int x; + for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) { + addAlphaOverFloat(fcol, acol); + } + } + if (solidmask) MEM_freeN(solidmask); + if (ztramask) MEM_freeN(ztramask); + } + } + } + + /* sun/sky */ + if (rl->layflag & SCE_LAY_SKY) + atm_tile(pa, rl); + + /* sky before edge */ + if (rl->layflag & SCE_LAY_SKY) + sky_tile(pa, rl); + + /* extra layers */ + if (rl->layflag & SCE_LAY_EDGE) + if (R.r.mode & R_EDGE) + edge_enhance_add(pa, rect, edgerect); + + if (rl->passflag & SCE_PASS_VECTOR) + reset_sky_speed(pa, rl); + + /* clamp alpha to 0..1 range, can go outside due to filter */ + clamp_alpha_rgb_range(pa, rl); + + /* free stuff within loop! */ + MEM_freeN(pa->rectdaps); pa->rectdaps= NULL; + freeps(&psmlist); + + if (edgerect) MEM_freeN(edgerect); + edgerect= NULL; + + if (pa->rectmask) { + MEM_freeN(pa->rectmask); + pa->rectmask= NULL; + } + } + + /* free all */ + MEM_freeN(pa->recto); pa->recto= NULL; + MEM_freeN(pa->rectp); pa->rectp= NULL; + MEM_freeN(pa->rectz); pa->rectz= NULL; + + /* display active layer */ + rr->renrect.ymin=rr->renrect.ymax = 0; + rr->renlay= render_get_active_layer(&R, rr); +} + + +/* ------------------------------------------------------------------------ */ + +/* non OSA case, full tile render */ +/* supposed to be fully threadable! */ +void zbufshade_tile(RenderPart *pa) +{ + ShadeSample ssamp; + RenderResult *rr= pa->result; + RenderLayer *rl; + PixStr ps; + float *edgerect= NULL; + + /* fake pixel struct, to comply to osa render */ + ps.next= NULL; + ps.mask= 0xFFFF; + + /* zbuffer code clears/inits rects */ + pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto"); + pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp"); + pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); + + for (rl= rr->layers.first; rl; rl= rl->next) { + float *rect= RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname); + if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) + pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask"); + + /* general shader info, passes */ + shade_sample_initialize(&ssamp, pa, rl); + + zbuffer_solid(pa, rl, NULL, NULL); + + if (!R.test_break(R.tbh)) { /* NOTE: this if () is not consistent */ + + /* edges only for solid part, ztransp doesn't support it yet anti-aliased */ + if (rl->layflag & SCE_LAY_EDGE) { + if (R.r.mode & R_EDGE) { + edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge"); + edge_enhance_tile(pa, edgerect, pa->rectz); + } + } + + /* initialize scanline updates for main thread */ + rr->renrect.ymin = 0; + rr->renlay= rl; + + if (rl->layflag & SCE_LAY_SOLID) { + const float *fcol = rect; + const int *ro= pa->recto, *rp= pa->rectp, *rz= pa->rectz; + int x, y, offs=0, seed; + + /* we set per pixel a fixed seed, for random AO and shadow samples */ + seed= pa->rectx*pa->disprect.ymin; + + /* irregular shadowb buffer creation */ + if (R.r.mode & R_SHADOW) + ISB_create(pa, NULL); + + if (R.occlusiontree) + cache_occ_samples(&R, pa, &ssamp); + + for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++, rr->renrect.ymax++) { + for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, ro++, rz++, rp++, fcol+=4, offs++) { + /* per pixel fixed seed */ + BLI_thread_srandom(pa->thread, seed++); + + if (*rp) { + ps.obi= *ro; + ps.facenr= *rp; + ps.z= *rz; + if (shade_samples(&ssamp, &ps, x, y)) { + /* combined and passes */ + add_passes(rl, offs, ssamp.shi, ssamp.shr); + } + } + } + if (y&1) + if (R.test_break(R.tbh)) break; + } + + if (R.occlusiontree) + free_occ_samples(&R, pa); + + if (R.r.mode & R_SHADOW) + ISB_free(pa); + } + + /* disable scanline updating */ + rr->renlay= NULL; + } + + /* lamphalo after solid, before ztra, looks nicest because ztra does own halo */ + if (R.flag & R_LAMPHALO) + if (rl->layflag & SCE_LAY_HALO) + lamphalo_tile(pa, rl); + + /* halo before ztra, because ztra fills in zbuffer now */ + if (R.flag & R_HALO) + if (rl->layflag & SCE_LAY_HALO) + halo_tile(pa, rl); + + if (R.flag & R_ZTRA || R.totstrand) { + if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) { + float *fcol, *acol; + int x; + + /* allocate, but not free here, for asynchronous display of this rect in main thread */ + rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer"); + + /* swap for live updates */ + SWAP(float *, rl->acolrect, rect); + zbuffer_transp_shade(pa, rl, rect, NULL); + SWAP(float *, rl->acolrect, rect); + + fcol= rect; acol= rl->acolrect; + for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) { + addAlphaOverFloat(fcol, acol); + } + } + } + + /* sun/sky */ + if (rl->layflag & SCE_LAY_SKY) + atm_tile(pa, rl); + + /* sky before edge */ + if (rl->layflag & SCE_LAY_SKY) + sky_tile(pa, rl); + + if (!R.test_break(R.tbh)) { + if (rl->layflag & SCE_LAY_EDGE) + if (R.r.mode & R_EDGE) + edge_enhance_add(pa, rect, edgerect); + } + + if (rl->passflag & SCE_PASS_VECTOR) + reset_sky_speed(pa, rl); + + if (edgerect) MEM_freeN(edgerect); + edgerect= NULL; + + if (pa->rectmask) { + MEM_freeN(pa->rectmask); + pa->rectmask= NULL; + } + } + + /* display active layer */ + rr->renrect.ymin=rr->renrect.ymax = 0; + rr->renlay= render_get_active_layer(&R, rr); + + MEM_freeN(pa->recto); pa->recto= NULL; + MEM_freeN(pa->rectp); pa->rectp= NULL; + MEM_freeN(pa->rectz); pa->rectz= NULL; +} + +/* SSS preprocess tile render, fully threadable */ +typedef struct ZBufSSSHandle { + RenderPart *pa; + ListBase psmlist; + int totps; +} ZBufSSSHandle; + +static void addps_sss(void *cb_handle, int obi, int facenr, int x, int y, int z) +{ + ZBufSSSHandle *handle = cb_handle; + RenderPart *pa= handle->pa; + + /* extra border for filter gives double samples on part edges, + * don't use those */ + if (x<pa->crop || x>=pa->rectx-pa->crop) + return; + if (y<pa->crop || y>=pa->recty-pa->crop) + return; + + if (pa->rectall) { + intptr_t *rs= pa->rectall + pa->rectx*y + x; + + addps(&handle->psmlist, rs, obi, facenr, z, 0, 0); + handle->totps++; + } + if (pa->rectz) { + int *rz= pa->rectz + pa->rectx*y + x; + int *rp= pa->rectp + pa->rectx*y + x; + int *ro= pa->recto + pa->rectx*y + x; + + if (z < *rz) { + if (*rp == 0) + handle->totps++; + *rz= z; + *rp= facenr; + *ro= obi; + } + } + if (pa->rectbackz) { + int *rz= pa->rectbackz + pa->rectx*y + x; + int *rp= pa->rectbackp + pa->rectx*y + x; + int *ro= pa->rectbacko + pa->rectx*y + x; + + if (z >= *rz) { + if (*rp == 0) + handle->totps++; + *rz= z; + *rp= facenr; + *ro= obi; + } + } +} + +static void shade_sample_sss(ShadeSample *ssamp, Material *mat, ObjectInstanceRen *obi, VlakRen *vlr, int quad, float x, float y, float z, float *co, float color[3], float *area) +{ + ShadeInput *shi= ssamp->shi; + ShadeResult shr; + float /* texfac,*/ /* UNUSED */ orthoarea, nor[3], alpha, sx, sy; + + /* cache for shadow */ + shi->samplenr= R.shadowsamplenr[shi->thread]++; + + if (quad) + shade_input_set_triangle_i(shi, obi, vlr, 0, 2, 3); + else + shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2); + + /* center pixel */ + sx = x + 0.5f; + sy = y + 0.5f; + + /* we estimate the area here using shi->dxco and shi->dyco. we need to + * enabled shi->osatex these are filled. we compute two areas, one with + * the normal pointed at the camera and one with the original normal, and + * then clamp to avoid a too large contribution from a single pixel */ + shi->osatex= 1; + + copy_v3_v3(nor, shi->facenor); + calc_view_vector(shi->facenor, sx, sy); + normalize_v3(shi->facenor); + shade_input_set_viewco(shi, x, y, sx, sy, z); + orthoarea= len_v3(shi->dxco)*len_v3(shi->dyco); + + copy_v3_v3(shi->facenor, nor); + shade_input_set_viewco(shi, x, y, sx, sy, z); + *area = min_ff(len_v3(shi->dxco) * len_v3(shi->dyco), 2.0f * orthoarea); + + shade_input_set_uv(shi); + shade_input_set_normals(shi); + + /* we don't want flipped normals, they screw up back scattering */ + if (shi->flippednor) + shade_input_flip_normals(shi); + + /* not a pretty solution, but fixes common cases */ + if (shi->obr->ob && shi->obr->ob->transflag & OB_NEG_SCALE) { + negate_v3(shi->vn); + negate_v3(shi->vno); + negate_v3(shi->nmapnorm); + } + + /* if nodetree, use the material that we are currently preprocessing + * instead of the node material */ + if (shi->mat->nodetree && shi->mat->use_nodes) + shi->mat= mat; + + /* init material vars */ + shade_input_init_material(shi); + + /* render */ + shade_input_set_shade_texco(shi); + + shade_samples_do_AO(ssamp); + shade_material_loop(shi, &shr); + + copy_v3_v3(co, shi->co); + copy_v3_v3(color, shr.combined); + + /* texture blending */ + /* texfac= shi->mat->sss_texfac; */ /* UNUSED */ + + alpha= shr.combined[3]; + *area *= alpha; +} + +static void zbufshade_sss_free(RenderPart *pa) +{ +#if 0 + MEM_freeN(pa->rectall); pa->rectall= NULL; + freeps(&handle.psmlist); +#else + MEM_freeN(pa->rectz); pa->rectz= NULL; + MEM_freeN(pa->rectp); pa->rectp= NULL; + MEM_freeN(pa->recto); pa->recto= NULL; + MEM_freeN(pa->rectbackz); pa->rectbackz= NULL; + MEM_freeN(pa->rectbackp); pa->rectbackp= NULL; + MEM_freeN(pa->rectbacko); pa->rectbacko= NULL; +#endif +} + +void zbufshade_sss_tile(RenderPart *pa) +{ + Render *re= &R; + ShadeSample ssamp; + ZBufSSSHandle handle; + RenderResult *rr= pa->result; + RenderLayer *rl; + VlakRen *vlr; + Material *mat= re->sss_mat; + float (*co)[3], (*color)[3], *area, *fcol; + int x, y, seed, quad, totpoint; + const bool display = (re->r.scemode & (R_BUTS_PREVIEW | R_VIEWPORT_PREVIEW)) == 0; + int *ro, *rz, *rp, *rbo, *rbz, *rbp, lay; +#if 0 + PixStr *ps; + intptr_t *rs; + int z; +#endif + + /* setup pixelstr list and buffer for zbuffering */ + handle.pa= pa; + handle.totps= 0; + +#if 0 + handle.psmlist.first= handle.psmlist.last= NULL; + addpsmain(&handle.psmlist); + + pa->rectall= MEM_callocN(sizeof(intptr_t)*pa->rectx*pa->recty+4, "rectall"); +#else + pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto"); + pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp"); + pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); + pa->rectbacko= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbacko"); + pa->rectbackp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackp"); + pa->rectbackz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackz"); +#endif + + /* setup shade sample with correct passes */ + memset(&ssamp, 0, sizeof(ssamp)); + shade_sample_initialize(&ssamp, pa, rr->layers.first); + ssamp.tot= 1; + + for (rl=rr->layers.first; rl; rl=rl->next) { + ssamp.shi[0].lay |= rl->lay; + ssamp.shi[0].layflag |= rl->layflag; + ssamp.shi[0].passflag |= rl->passflag; + ssamp.shi[0].combinedflag |= ~rl->pass_xor; + } + + rl= rr->layers.first; + ssamp.shi[0].passflag |= SCE_PASS_RGBA|SCE_PASS_COMBINED; + ssamp.shi[0].combinedflag &= ~(SCE_PASS_SPEC); + ssamp.shi[0].mat_override= NULL; + ssamp.shi[0].light_override= NULL; + lay= ssamp.shi[0].lay; + + /* create the pixelstrs to be used later */ + zbuffer_sss(pa, lay, &handle, addps_sss); + + if (handle.totps==0) { + zbufshade_sss_free(pa); + return; + } + + fcol= RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname); + + co= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSCo"); + color= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSColor"); + area= MEM_mallocN(sizeof(float)*handle.totps, "SSSArea"); + +#if 0 + /* create ISB (does not work currently!) */ + if (re->r.mode & R_SHADOW) + ISB_create(pa, NULL); +#endif + + if (display) { + /* initialize scanline updates for main thread */ + rr->renrect.ymin = 0; + rr->renlay= rl; + } + + seed= pa->rectx*pa->disprect.ymin; +#if 0 + rs= pa->rectall; +#else + rz= pa->rectz; + rp= pa->rectp; + ro= pa->recto; + rbz= pa->rectbackz; + rbp= pa->rectbackp; + rbo= pa->rectbacko; +#endif + totpoint= 0; + + for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++, rr->renrect.ymax++) { + for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, fcol+=4) { + /* per pixel fixed seed */ + BLI_thread_srandom(pa->thread, seed++); + +#if 0 + if (rs) { + /* for each sample in this pixel, shade it */ + for (ps = (PixStr *)(*rs); ps; ps=ps->next) { + ObjectInstanceRen *obi= &re->objectinstance[ps->obi]; + ObjectRen *obr= obi->obr; + vlr= RE_findOrAddVlak(obr, (ps->facenr-1) & RE_QUAD_MASK); + quad= (ps->facenr & RE_QUAD_OFFS); + z= ps->z; + + shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, z, + co[totpoint], color[totpoint], &area[totpoint]); + + totpoint++; + + add_v3_v3(fcol, color); + fcol[3]= 1.0f; + } + + rs++; + } +#else + if (rp) { + if (*rp != 0) { + ObjectInstanceRen *obi= &re->objectinstance[*ro]; + ObjectRen *obr= obi->obr; + + /* shade front */ + vlr= RE_findOrAddVlak(obr, (*rp-1) & RE_QUAD_MASK); + quad= ((*rp) & RE_QUAD_OFFS); + + shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, *rz, + co[totpoint], color[totpoint], &area[totpoint]); + + add_v3_v3(fcol, color[totpoint]); + fcol[3]= 1.0f; + totpoint++; + } + + rp++; rz++; ro++; + } + + if (rbp) { + if (*rbp != 0 && !(*rbp == *(rp-1) && *rbo == *(ro-1))) { + ObjectInstanceRen *obi= &re->objectinstance[*rbo]; + ObjectRen *obr= obi->obr; + + /* shade back */ + vlr= RE_findOrAddVlak(obr, (*rbp-1) & RE_QUAD_MASK); + quad= ((*rbp) & RE_QUAD_OFFS); + + shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, *rbz, + co[totpoint], color[totpoint], &area[totpoint]); + + /* to indicate this is a back sample */ + area[totpoint]= -area[totpoint]; + + add_v3_v3(fcol, color[totpoint]); + fcol[3]= 1.0f; + totpoint++; + } + + rbz++; rbp++; rbo++; + } +#endif + } + + if (y&1) + if (re->test_break(re->tbh)) break; + } + + /* note: after adding we do not free these arrays, sss keeps them */ + if (totpoint > 0) { + sss_add_points(re, co, color, area, totpoint); + } + else { + MEM_freeN(co); + MEM_freeN(color); + MEM_freeN(area); + } + +#if 0 + if (re->r.mode & R_SHADOW) + ISB_free(pa); +#endif + + if (display) { + /* display active layer */ + rr->renrect.ymin=rr->renrect.ymax = 0; + rr->renlay= render_get_active_layer(&R, rr); + } + + zbufshade_sss_free(pa); +} + +/* ------------------------------------------------------------------------ */ + +static void renderhalo_post(RenderResult *rr, float *rectf, HaloRen *har) /* postprocess version */ +{ + float dist, xsq, ysq, xn, yn, colf[4], *rectft, *rtf; + float haloxs, haloys; + int minx, maxx, miny, maxy, x, y; + + /* calculate the disprect mapped coordinate for halo. note: rectx is disprect corrected */ + haloxs= har->xs - R.disprect.xmin; + haloys= har->ys - R.disprect.ymin; + + har->miny= miny= haloys - har->rad/R.ycor; + har->maxy= maxy= haloys + har->rad/R.ycor; + + if (maxy < 0) { + /* pass */ + } + else if (rr->recty < miny) { + /* pass */ + } + else { + minx = floor(haloxs - har->rad); + maxx = ceil(haloxs + har->rad); + + if (maxx < 0) { + /* pass */ + } + else if (rr->rectx < minx) { + /* pass */ + } + else { + if (minx<0) minx= 0; + if (maxx>=rr->rectx) maxx= rr->rectx-1; + if (miny<0) miny= 0; + if (maxy>rr->recty) maxy= rr->recty; + + rectft= rectf+ 4*rr->rectx*miny; + + for (y=miny; y<maxy; y++) { + + rtf= rectft+4*minx; + + yn= (y - haloys)*R.ycor; + ysq= yn*yn; + + for (x=minx; x<=maxx; x++) { + xn= x - haloxs; + xsq= xn*xn; + dist= xsq+ysq; + if (dist<har->radsq) { + + if (shadeHaloFloat(har, colf, 0x7FFFFF, dist, xn, yn, har->flarec)) + addalphaAddfacFloat(rtf, colf, har->add); + } + rtf+=4; + } + + rectft+= 4*rr->rectx; + + if (R.test_break(R.tbh)) break; + } + } + } +} +/* ------------------------------------------------------------------------ */ + +static void renderflare(RenderResult *rr, float *rectf, HaloRen *har) +{ + extern const float hashvectf[]; + HaloRen fla; + Material *ma; + const float *rc; + float rad, alfa, visifac, vec[3]; + int b, type; + + fla= *har; + fla.linec= fla.ringc= fla.flarec= 0; + + rad= har->rad; + alfa= har->alfa; + + visifac= R.ycor*(har->pixels); + /* all radials added / r^3 == 1.0f! */ + visifac /= (har->rad*har->rad*har->rad); + visifac*= visifac; + + ma= har->mat; + + /* first halo: just do */ + + har->rad= rad*ma->flaresize*visifac; + har->radsq= har->rad*har->rad; + har->zs= fla.zs= 0; + + har->alfa= alfa*visifac; + + renderhalo_post(rr, rectf, har); + + /* next halo's: the flares */ + rc= hashvectf + ma->seed2; + + for (b=1; b<har->flarec; b++) { + + fla.r = fabsf(rc[0]); + fla.g = fabsf(rc[1]); + fla.b = fabsf(rc[2]); + fla.alfa= ma->flareboost*fabsf(alfa*visifac*rc[3]); + fla.hard= 20.0f + fabsf(70.0f*rc[7]); + fla.tex= 0; + + type= (int)(fabsf(3.9f*rc[6])); + + fla.rad = ma->subsize * sqrtf(fabsf(2.0f * har->rad * rc[4])); + + if (type==3) { + fla.rad*= 3.0f; + fla.rad+= R.rectx/10; + } + + fla.radsq= fla.rad*fla.rad; + + vec[0]= 1.4f*rc[5]*(har->xs-R.winx/2); + vec[1]= 1.4f*rc[5]*(har->ys-R.winy/2); + vec[2]= 32.0f*sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + 1.0f); + + fla.xs= R.winx/2 + vec[0] + (1.2f+rc[8])*R.rectx*vec[0]/vec[2]; + fla.ys= R.winy/2 + vec[1] + (1.2f+rc[8])*R.rectx*vec[1]/vec[2]; + + if (R.flag & R_SEC_FIELD) { + if (R.r.mode & R_ODDFIELD) fla.ys += 0.5f; + else fla.ys -= 0.5f; + } + if (type & 1) fla.type= HA_FLARECIRC; + else fla.type= 0; + renderhalo_post(rr, rectf, &fla); + + fla.alfa*= 0.5f; + if (type & 2) fla.type= HA_FLARECIRC; + else fla.type= 0; + renderhalo_post(rr, rectf, &fla); + + rc+= 7; + } +} + +/* needs recode... integrate this better! */ +void add_halo_flare(Render *re) +{ + RenderResult *rr= re->result; + RenderLayer *rl; + HaloRen *har; + int a, mode; + float *rect; + + /* for now, we get the first renderlayer in list with halos set */ + for (rl= rr->layers.first; rl; rl= rl->next) { + bool do_draw = false; + + if ((rl->layflag & SCE_LAY_HALO) == 0) + continue; + + rect = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, re->viewname); + + if (rect==NULL) + continue; + + mode= R.r.mode; + R.r.mode &= ~R_PANORAMA; + + project_renderdata(&R, projectverto, 0, 0, 0); + + for (a=0; a<R.tothalo; a++) { + har= R.sortedhalos[a]; + + if (har->flarec && (har->lay & rl->lay)) { + do_draw = true; + renderflare(rr, rect, har); + } + } + + if (do_draw) { + /* weak... the display callback wants an active renderlayer pointer... */ + rr->renlay= rl; + re->display_update(re->duh, rr, NULL); + } + + R.r.mode= mode; + } +} + +void render_internal_update_passes(RenderEngine *engine, Scene *scene, SceneRenderLayer *srl) +{ + int type; + + RE_engine_register_pass(engine, scene, srl, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA); + +#define CHECK_PASS(name, channels, chanid) \ + if (srl->passflag & (SCE_PASS_ ## name)) { \ + if (channels == 4) type = SOCK_RGBA; \ + else if (channels == 3) type = SOCK_VECTOR; \ + else type = SOCK_FLOAT; \ + RE_engine_register_pass(engine, scene, srl, RE_PASSNAME_ ## name, channels, chanid, type); \ + } + + CHECK_PASS(Z, 1, "Z"); + CHECK_PASS(VECTOR, 4, "XYZW"); + CHECK_PASS(NORMAL, 3, "XYZ"); + CHECK_PASS(UV, 3, "UVA"); + CHECK_PASS(RGBA, 4, "RGBA"); + CHECK_PASS(EMIT, 3, "RGB"); + CHECK_PASS(DIFFUSE, 3, "RGB"); + CHECK_PASS(SPEC, 3, "RGB"); + CHECK_PASS(AO, 3, "RGB"); + CHECK_PASS(ENVIRONMENT, 3, "RGB"); + CHECK_PASS(INDIRECT, 3, "RGB"); + CHECK_PASS(SHADOW, 3, "RGB"); + CHECK_PASS(REFLECT, 3, "RGB"); + CHECK_PASS(REFRACT, 3, "RGB"); + CHECK_PASS(INDEXOB, 1, "X"); + CHECK_PASS(INDEXMA, 1, "X"); + CHECK_PASS(MIST, 1, "Z"); + +#undef CHECK_PASS +} |