/* * ***** 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 #include #include #include #include /* External modules: */ #include "MEM_guardedalloc.h" #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_jitter.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_meshdata_types.h" #include "DNA_group_types.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_texture.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" /* local include */ #include "rayintersection.h" #include "rayobject.h" #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" #include "PIL_time.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, 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*(R.viewplane.xmax - R.viewplane.xmin); y= (y/(float)R.winy); view[1]= R.viewplane.ymin + y*(R.viewplane.ymax - R.viewplane.ymin); // 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; 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; samplemask & (1 << sample)) addalphaAddfacFloat(rlpp[sample]->rectf + 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; samplerectf + 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; samplerectf + 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; alay & lay)==0); else if (testrect.ymin > har->maxy); else if (testrect.ymax < har->miny); else { minx= floor(har->xs-har->rad); maxx= ceil(har->xs+har->rad); if (testrect.xmin > maxx); else if (testrect.xmax < minx); else { minx= MAX2(minx, testrect.xmin); maxx= MIN2(maxx, testrect.xmax); miny= MAX2(har->miny, testrect.ymin); maxy= MIN2(har->maxy, testrect.ymax); for (y=miny; yrectx + (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; xxs; xsq= xn*xn; dist= xsq+ysq; if (distradsq) { 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; samplerectf + 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; 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; ydisprect.ymax; y++) { for (x=pa->disprect.xmin; xdisprect.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; samplemask & (1 << sample)) { pass= rlpp[sample]->rectf + 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= rl->rectf + 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 (totsamprectf + 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= rl->rectf + 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; samplerectf + 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; /* combined rgb */ add_filt_fmask(curmask, shr->combined, rl->rectf + 4*offset, rectx); for (rpass= rl->passes.first; rpass; rpass= rpass->next) { float *fp, *col= NULL; int pixsize= 3; switch (rpass->passtype) { case SCE_PASS_Z: fp= rpass->rect + offset; *fp= shr->z; break; case SCE_PASS_RGBA: col= shr->col; pixsize= 4; break; case SCE_PASS_EMIT: col= shr->emit; break; case SCE_PASS_DIFFUSE: col= shr->diff; break; case SCE_PASS_SPEC: col= shr->spec; break; case SCE_PASS_SHADOW: col= shr->shad; break; case SCE_PASS_AO: col= shr->ao; break; case SCE_PASS_ENVIRONMENT: col= shr->env; break; case SCE_PASS_INDIRECT: col= shr->indirect; break; case SCE_PASS_REFLECT: col= shr->refl; break; case SCE_PASS_REFRACT: col= shr->refr; break; case SCE_PASS_NORMAL: col= shr->nor; break; case SCE_PASS_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; } break; case SCE_PASS_INDEXOB: /* no filter */ if (shi->vlr) { fp= rpass->rect + offset; if (*fp==0.0f) *fp= (float)shi->obr->ob->index; } break; case SCE_PASS_INDEXMA: /* no filter */ if (shi->vlr) { fp= rpass->rect + offset; if (*fp==0.0f) *fp= (float)shi->mat->index; } break; case SCE_PASS_MIST: /* */ col= &shr->mist; pixsize= 1; break; case SCE_PASS_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]; } } break; case SCE_PASS_RAYHITS: /* */ col= shr->rayhits; pixsize= 4; break; } 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; fp= rl->rectf + 4*offset; copy_v4_v4(fp, shr->combined); for (rpass= rl->passes.first; rpass; rpass= rpass->next) { float *col= NULL, uvcol[3]; int a, pixsize= 3; switch (rpass->passtype) { case SCE_PASS_Z: fp= rpass->rect + offset; *fp= shr->z; break; case SCE_PASS_RGBA: col= shr->col; pixsize= 4; break; case SCE_PASS_EMIT: col= shr->emit; break; case SCE_PASS_DIFFUSE: col= shr->diff; break; case SCE_PASS_SPEC: col= shr->spec; break; case SCE_PASS_SHADOW: col= shr->shad; break; case SCE_PASS_AO: col= shr->ao; break; case SCE_PASS_ENVIRONMENT: col= shr->env; break; case SCE_PASS_INDIRECT: col= shr->indirect; break; case SCE_PASS_REFLECT: col= shr->refl; break; case SCE_PASS_REFRACT: col= shr->refr; break; case SCE_PASS_NORMAL: col= shr->nor; break; case SCE_PASS_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; } break; case SCE_PASS_VECTOR: col= shr->winspeed; pixsize= 4; break; case SCE_PASS_INDEXOB: if (shi->vlr) { fp= rpass->rect + offset; *fp= (float)shi->obr->ob->index; } break; case SCE_PASS_INDEXMA: if (shi->vlr) { fp= rpass->rect + offset; *fp= (float)shi->mat->index; } break; case SCE_PASS_MIST: fp= rpass->rect + offset; *fp= shr->mist; break; case SCE_PASS_RAYHITS: col= shr->rayhits; pixsize= 4; break; } if (col) { fp= rpass->rect + pixsize*offset; for (a=0; afullresult.first) { int sample, nr= BLI_findindex(&pa->result->layers, rl); for (sample=0; samplefullresult, 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; ydisprect.ymax; y++) { for (x=pa->disprect.xmin; xdisprect.xmax; x++, od+=4) { float col[4]; int sample, done = FALSE; for (sample= 0; samplerectf + 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); } else { addAlphaUnderFloat(pass, col); } } } } 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 (zpass->passtype==SCE_PASS_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; ydisprect.ymax; y++) { for (x=pa->disprect.xmin; xdisprect.xmax; x++, od++) { int sample; for (sample=0; samplerectf + 4*od; float rgb[3] = {0}; int 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; ydisprect.ymax-crop; y++, rr->renrect.ymax++) { rd= rectdaps; od= offs; for (x=pa->disprect.xmin+crop; xdisprect.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; samprectx, 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); } lb->first= lb->last= NULL; } 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); } } } static void convert_to_key_alpha(RenderPart *pa, RenderLayer *rl) { RenderLayer *rlpp[RE_MAX_OSA]; int y, sample, totsample; totsample= get_sample_layers(pa, rl, rlpp); for (sample= 0; samplerectf; for (y= pa->rectx*pa->recty; y>0; y--, rectf+=4) { if (rectf[3] >= 1.0f); else if (rectf[3] > 0.0f) { rectf[0] /= rectf[3]; rectf[1] /= rectf[3]; rectf[2] /= rectf[3]; } } } } /* 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; yrecty; y++) for (x=0; xrectx; x++, rz++) (*rz)>>= 4; rz1= rectz; rz2= rz1+pa->rectx; rz3= rz2+pa->rectx; rf= rectf+pa->rectx+1; for (y=0; yrecty-2; y++) { for (x=0; xrectx-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; yrecty; y++) for (x=0; xrectx; 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; samplerectx*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; int *ro= zspan->recto; int *rp= zspan->rectp; int *rz= zspan->rectz; int *rm= zspan->rectmask; int x, y; int mask= 1<recty; y++) { for (x=0; xrectx; 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) { 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->samplesample+=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, rl->rectf, &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, rl->rectf); ztramask= zbuffer_transp_shade(pa, rl, rl->rectf, &psmlist); SWAP(float *, rl->acolrect, rl->rectf); /* 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<rectf; 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= rl->rectf; 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, rl->rectf, edgerect); if (rl->passflag & SCE_PASS_VECTOR) reset_sky_speed(pa, rl); /* de-premul alpha */ if (R.r.alphamode & R_ALPHAKEY) convert_to_key_alpha(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) { 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) { float *fcol= rl->rectf; 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; ydisprect.ymax; y++, rr->renrect.ymax++) { for (x=pa->disprect.xmin; xdisprect.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, rl->rectf); zbuffer_transp_shade(pa, rl, rl->rectf, NULL); SWAP(float *, rl->acolrect, rl->rectf); fcol= rl->rectf; 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, rl->rectf, edgerect); } if (rl->passflag & SCE_PASS_VECTOR) reset_sky_speed(pa, rl); /* de-premul alpha */ if (R.r.alphamode & R_ALPHAKEY) convert_to_key_alpha(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 (xcrop || x>=pa->rectx-pa->crop) return; if (ycrop || 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, 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= len_v3(shi->dxco)*len_v3(shi->dyco); *area= MIN2(*area, 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, display = !(re->r.scemode & R_PREVIEWBUTS); 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= rl->rectf; 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; ydisprect.ymax; y++, rr->renrect.ymax++) { for (x=pa->disprect.xmin; xdisprect.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); else if (rr->rectyrad); maxx= ceil(haloxs+har->rad); if (maxx<0); else if (rr->rectx=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; yradsq) { 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 float hashvectf[]; HaloRen fla; Material *ma; float *rc, 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; bflarec; b++) { fla.r= fabs(rc[0]); fla.g= fabs(rc[1]); fla.b= fabs(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)(fabs(3.9f*rc[6])); fla.rad= ma->subsize*sqrtf(fabs(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, do_draw = FALSE; /* for now, we get the first renderlayer in list with halos set */ for (rl= rr->layers.first; rl; rl= rl->next) if (rl->layflag & SCE_LAY_HALO) break; if (rl==NULL || rl->rectf==NULL) return; mode= R.r.mode; R.r.mode &= ~R_PANORAMA; project_renderdata(&R, projectverto, 0, 0, 0); for (a=0; aflarec) { do_draw = TRUE; renderflare(rr, rl->rectf, har); } } if (do_draw) { /* weak... the display callback wants an active renderlayer pointer... */ rr->renlay= rl; re->display_draw(re->ddh, rr, NULL); } R.r.mode= mode; } /* ************************* bake ************************ */ typedef struct BakeShade { ShadeSample ssamp; ObjectInstanceRen *obi; VlakRen *vlr; ZSpan *zspan; Image *ima; ImBuf *ibuf; int rectx, recty, quad, type, vdone, ready; float dir[3]; Object *actob; unsigned int *rect; float *rect_float; int use_mask; char *rect_mask; /* bake pixel mask */ float dxco[3], dyco[3]; short *do_update; } BakeShade; static void bake_set_shade_input(ObjectInstanceRen *obi, VlakRen *vlr, ShadeInput *shi, int quad, int UNUSED(isect), int x, int y, float u, float v) { 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); /* cache for shadow */ shi->samplenr= R.shadowsamplenr[shi->thread]++; shi->mask= 0xFFFF; /* all samples */ shi->u= -u; shi->v= -v; shi->xs= x; shi->ys= y; shade_input_set_uv(shi); shade_input_set_normals(shi); /* no normal flip */ if (shi->flippednor) shade_input_flip_normals(shi); /* set up view vector to look right at the surface (note that the normal * is negated in the renderer so it does not need to be done here) */ shi->view[0]= shi->vn[0]; shi->view[1]= shi->vn[1]; shi->view[2]= shi->vn[2]; } static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int UNUSED(quad), int x, int y, float UNUSED(u), float UNUSED(v), float *tvn, float *ttang) { BakeShade *bs= handle; ShadeSample *ssamp= &bs->ssamp; ShadeResult shr; VlakRen *vlr= shi->vlr; shade_input_init_material(shi); if (bs->type==RE_BAKE_AO) { ambient_occlusion(shi); if (R.r.bake_flag & R_BAKE_NORMALIZE) { copy_v3_v3(shr.combined, shi->ao); } else { zero_v3(shr.combined); environment_lighting_apply(shi, &shr); } } else { if (bs->type==RE_BAKE_SHADOW) /* Why do shadows set the color anyhow?, ignore material color for baking */ shi->r = shi->g = shi->b = 1.0f; shade_input_set_shade_texco(shi); /* only do AO for a full bake (and obviously AO bakes) * AO for light bakes is a leftover and might not be needed */ if ( ELEM3(bs->type, RE_BAKE_ALL, RE_BAKE_AO, RE_BAKE_LIGHT)) shade_samples_do_AO(ssamp); if (shi->mat->nodetree && shi->mat->use_nodes) { ntreeShaderExecTree(shi->mat->nodetree, shi, &shr); shi->mat= vlr->mat; /* shi->mat is being set in nodetree */ } else shade_material_loop(shi, &shr); if (bs->type==RE_BAKE_NORMALS) { float nor[3]; copy_v3_v3(nor, shi->vn); if (R.r.bake_normal_space == R_BAKE_SPACE_CAMERA); else if (R.r.bake_normal_space == R_BAKE_SPACE_TANGENT) { float mat[3][3], imat[3][3]; /* bitangent */ if (tvn && ttang) { copy_v3_v3(mat[0], ttang); cross_v3_v3v3(mat[1], tvn, ttang); mul_v3_fl(mat[1], ttang[3]); copy_v3_v3(mat[2], tvn); } else { copy_v3_v3(mat[0], shi->nmaptang); cross_v3_v3v3(mat[1], shi->nmapnorm, shi->nmaptang); mul_v3_fl(mat[1], shi->nmaptang[3]); copy_v3_v3(mat[2], shi->nmapnorm); } invert_m3_m3(imat, mat); mul_m3_v3(imat, nor); } else if (R.r.bake_normal_space == R_BAKE_SPACE_OBJECT) mul_mat3_m4_v3(ob->imat_ren, nor); /* ob->imat_ren includes viewinv! */ else if (R.r.bake_normal_space == R_BAKE_SPACE_WORLD) mul_mat3_m4_v3(R.viewinv, nor); normalize_v3(nor); /* in case object has scaling */ // The invert of the red channel is to make // the normal map compliant with the outside world. // It needs to be done because in Blender // the normal used in the renderer points inward. It is generated // this way in calc_vertexnormals(). Should this ever change // this negate must be removed. shr.combined[0]= (-nor[0])/2.0f + 0.5f; shr.combined[1]= nor[1]/2.0f + 0.5f; shr.combined[2]= nor[2]/2.0f + 0.5f; } else if (bs->type==RE_BAKE_TEXTURE) { shr.combined[0]= shi->r; shr.combined[1]= shi->g; shr.combined[2]= shi->b; shr.alpha = shi->alpha; } else if (bs->type==RE_BAKE_SHADOW) { copy_v3_v3(shr.combined, shr.shad); shr.alpha = shi->alpha; } else if (bs->type==RE_BAKE_SPEC_COLOR) { shr.combined[0]= shi->specr; shr.combined[1]= shi->specg; shr.combined[2]= shi->specb; shr.alpha = 1.0f; } else if (bs->type==RE_BAKE_SPEC_INTENSITY) { shr.combined[0]= shr.combined[1]= shr.combined[2]= shi->spec; shr.alpha = 1.0f; } else if (bs->type==RE_BAKE_MIRROR_COLOR) { shr.combined[0]= shi->mirr; shr.combined[1]= shi->mirg; shr.combined[2]= shi->mirb; shr.alpha = 1.0f; } else if (bs->type==RE_BAKE_MIRROR_INTENSITY) { shr.combined[0]= shr.combined[1]= shr.combined[2]= shi->ray_mirror; shr.alpha = 1.0f; } else if (bs->type==RE_BAKE_ALPHA) { shr.combined[0]= shr.combined[1]= shr.combined[2]= shi->alpha; shr.alpha = 1.0f; } else if (bs->type==RE_BAKE_EMIT) { shr.combined[0]= shr.combined[1]= shr.combined[2]= shi->emit; shr.alpha = 1.0f; } } if (bs->rect_float) { float *col= bs->rect_float + 4*(bs->rectx*y + x); copy_v3_v3(col, shr.combined); if (bs->type==RE_BAKE_ALL || bs->type==RE_BAKE_TEXTURE) { col[3]= shr.alpha; } else { col[3]= 1.0; } } else { unsigned char *col= (unsigned char *)(bs->rect + bs->rectx*y + x); if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE) && (R.r.color_mgt_flag & R_COLOR_MANAGEMENT)) { linearrgb_to_srgb_uchar3(col, shr.combined); } else { rgb_float_to_uchar(col, shr.combined); } if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE)) { col[3]= FTOCHAR(shr.alpha); } else { col[3]= 255; } } if (bs->rect_mask) { bs->rect_mask[bs->rectx*y + x] = FILTER_MASK_USED; } } static void bake_displacement(void *handle, ShadeInput *UNUSED(shi), float dist, int x, int y) { BakeShade *bs= handle; float disp; if (R.r.bake_flag & R_BAKE_NORMALIZE && R.r.bake_maxdist) { disp = (dist+R.r.bake_maxdist) / (R.r.bake_maxdist*2); /* alter the range from [-bake_maxdist, bake_maxdist] to [0, 1]*/ } else { disp = 0.5f + dist; /* alter the range from [-0.5,0.5] to [0,1]*/ } if (bs->rect_float) { float *col= bs->rect_float + 4*(bs->rectx*y + x); col[0] = col[1] = col[2] = disp; col[3]= 1.0f; } else { char *col= (char *)(bs->rect + bs->rectx*y + x); col[0] = col[1] = col[2] = FTOCHAR(disp); col[3]= 255; } if (bs->rect_mask) { bs->rect_mask[bs->rectx*y + x] = FILTER_MASK_USED; } } static int bake_intersect_tree(RayObject* raytree, Isect* isect, float *start, float *dir, float sign, float *hitco, float *dist) { float maxdist; int hit; /* might be useful to make a user setting for maxsize*/ if (R.r.bake_maxdist > 0.0f) maxdist= R.r.bake_maxdist; else maxdist= RE_RAYTRACE_MAXDIST + R.r.bake_biasdist; /* 'dir' is always normalized */ madd_v3_v3v3fl(isect->start, start, dir, -R.r.bake_biasdist); mul_v3_v3fl(isect->dir, dir, sign); isect->dist = maxdist; hit = RE_rayobject_raycast(raytree, isect); if (hit) { madd_v3_v3v3fl(hitco, isect->start, isect->dir, isect->dist); *dist= isect->dist; } return hit; } static void bake_set_vlr_dxyco(BakeShade *bs, float *uv1, float *uv2, float *uv3) { VlakRen *vlr= bs->vlr; float A, d1, d2, d3, *v1, *v2, *v3; if (bs->quad) { v1= vlr->v1->co; v2= vlr->v3->co; v3= vlr->v4->co; } else { v1= vlr->v1->co; v2= vlr->v2->co; v3= vlr->v3->co; } /* formula derived from barycentric coordinates: * (uvArea1*v1 + uvArea2*v2 + uvArea3*v3)/uvArea * then taking u and v partial derivatives to get dxco and dyco */ A= (uv2[0] - uv1[0])*(uv3[1] - uv1[1]) - (uv3[0] - uv1[0])*(uv2[1] - uv1[1]); if (fabsf(A) > FLT_EPSILON) { A= 0.5f/A; d1= uv2[1] - uv3[1]; d2= uv3[1] - uv1[1]; d3= uv1[1] - uv2[1]; bs->dxco[0]= (v1[0]*d1 + v2[0]*d2 + v3[0]*d3)*A; bs->dxco[1]= (v1[1]*d1 + v2[1]*d2 + v3[1]*d3)*A; bs->dxco[2]= (v1[2]*d1 + v2[2]*d2 + v3[2]*d3)*A; d1= uv3[0] - uv2[0]; d2= uv1[0] - uv3[0]; d3= uv2[0] - uv1[0]; bs->dyco[0]= (v1[0]*d1 + v2[0]*d2 + v3[0]*d3)*A; bs->dyco[1]= (v1[1]*d1 + v2[1]*d2 + v3[1]*d3)*A; bs->dyco[2]= (v1[2]*d1 + v2[2]*d2 + v3[2]*d3)*A; } else { bs->dxco[0]= bs->dxco[1]= bs->dxco[2]= 0.0f; bs->dyco[0]= bs->dyco[1]= bs->dyco[2]= 0.0f; } if (bs->obi->flag & R_TRANSFORMED) { mul_m3_v3(bs->obi->nmat, bs->dxco); mul_m3_v3(bs->obi->nmat, bs->dyco); } } static void do_bake_shade(void *handle, int x, int y, float u, float v) { BakeShade *bs= handle; VlakRen *vlr= bs->vlr; ObjectInstanceRen *obi= bs->obi; Object *ob= obi->obr->ob; float l, *v1, *v2, *v3, tvn[3], ttang[4]; int quad; ShadeSample *ssamp= &bs->ssamp; ShadeInput *shi= ssamp->shi; /* fast threadsafe break test */ if (R.test_break(R.tbh)) return; /* setup render coordinates */ if (bs->quad) { v1= vlr->v1->co; v2= vlr->v3->co; v3= vlr->v4->co; } else { v1= vlr->v1->co; v2= vlr->v2->co; v3= vlr->v3->co; } /* renderco */ l= 1.0f-u-v; shi->co[0]= l*v3[0]+u*v1[0]+v*v2[0]; shi->co[1]= l*v3[1]+u*v1[1]+v*v2[1]; shi->co[2]= l*v3[2]+u*v1[2]+v*v2[2]; if (obi->flag & R_TRANSFORMED) mul_m4_v3(obi->mat, shi->co); copy_v3_v3(shi->dxco, bs->dxco); copy_v3_v3(shi->dyco, bs->dyco); quad= bs->quad; bake_set_shade_input(obi, vlr, shi, quad, 0, x, y, u, v); if (bs->type==RE_BAKE_NORMALS && R.r.bake_normal_space==R_BAKE_SPACE_TANGENT) { shade_input_set_shade_texco(shi); copy_v3_v3(tvn, shi->nmapnorm); copy_v4_v4(ttang, shi->nmaptang); } /* if we are doing selected to active baking, find point on other face */ if (bs->actob) { Isect isec, minisec; float co[3], minco[3], dist, mindist=0.0f; int hit, sign, dir=1; /* intersect with ray going forward and backward*/ hit= 0; memset(&minisec, 0, sizeof(minisec)); minco[0]= minco[1]= minco[2]= 0.0f; copy_v3_v3(bs->dir, shi->vn); for (sign=-1; sign<=1; sign+=2) { memset(&isec, 0, sizeof(isec)); isec.mode= RE_RAY_MIRROR; isec.orig.ob = obi; isec.orig.face = vlr; isec.userdata= bs->actob; isec.check = RE_CHECK_VLR_BAKE; isec.skip = RE_SKIP_VLR_NEIGHBOUR; if (bake_intersect_tree(R.raytree, &isec, shi->co, shi->vn, sign, co, &dist)) { if (!hit || len_squared_v3v3(shi->co, co) < len_squared_v3v3(shi->co, minco)) { minisec= isec; mindist= dist; copy_v3_v3(minco, co); hit= 1; dir = sign; } } } if (bs->type==RE_BAKE_DISPLACEMENT) { if (hit) bake_displacement(handle, shi, (dir==-1)? mindist:-mindist, x, y); else bake_displacement(handle, shi, 0.0f, x, y); return; } /* if hit, we shade from the new point, otherwise from point one starting face */ if (hit) { obi= (ObjectInstanceRen*)minisec.hit.ob; vlr= (VlakRen*)minisec.hit.face; quad= (minisec.isect == 2); copy_v3_v3(shi->co, minco); u= -minisec.u; v= -minisec.v; bake_set_shade_input(obi, vlr, shi, quad, 1, x, y, u, v); } } if (bs->type==RE_BAKE_NORMALS && R.r.bake_normal_space==R_BAKE_SPACE_TANGENT) bake_shade(handle, ob, shi, quad, x, y, u, v, tvn, ttang); else bake_shade(handle, ob, shi, quad, x, y, u, v, 0, 0); } static int get_next_bake_face(BakeShade *bs) { ObjectRen *obr; VlakRen *vlr; MTFace *tface; static int v= 0, vdone = FALSE; static ObjectInstanceRen *obi= NULL; if (bs==NULL) { vlr= NULL; v= vdone = FALSE; obi= R.instancetable.first; return 0; } BLI_lock_thread(LOCK_CUSTOM1); for (; obi; obi=obi->next, v=0) { obr= obi->obr; for (; vtotvlak; v++) { vlr= RE_findOrAddVlak(obr, v); if ((bs->actob && bs->actob == obr->ob) || (!bs->actob && (obr->ob->flag & SELECT))) { tface= RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0); if (tface && tface->tpage) { Image *ima= tface->tpage; ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL); const float vec_alpha[4]= {0.0f, 0.0f, 0.0f, 0.0f}; const float vec_solid[4]= {0.0f, 0.0f, 0.0f, 1.0f}; if (ibuf==NULL) continue; if (ibuf->rect==NULL && ibuf->rect_float==NULL) continue; if (ibuf->rect_float && !(ibuf->channels==0 || ibuf->channels==4)) continue; if (ima->flag & IMA_USED_FOR_RENDER) { ima->id.flag &= ~LIB_DOIT; continue; } /* find the image for the first time? */ if (ima->id.flag & LIB_DOIT) { ima->id.flag &= ~LIB_DOIT; /* we either fill in float or char, this ensures things go fine */ if (ibuf->rect_float) imb_freerectImBuf(ibuf); /* clear image */ if (R.r.bake_flag & R_BAKE_CLEAR) IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid); /* might be read by UI to set active image for display */ R.bakebuf= ima; } bs->obi= obi; bs->vlr= vlr; bs->vdone++; /* only for error message if nothing was rendered */ v++; BLI_unlock_thread(LOCK_CUSTOM1); return 1; } } } } BLI_unlock_thread(LOCK_CUSTOM1); return 0; } /* already have tested for tface and ima and zspan */ static void shade_tface(BakeShade *bs) { VlakRen *vlr= bs->vlr; ObjectInstanceRen *obi= bs->obi; ObjectRen *obr= obi->obr; MTFace *tface= RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0); Image *ima= tface->tpage; float vec[4][2]; int a, i1, i2, i3; /* check valid zspan */ if (ima!=bs->ima) { bs->ima= ima; bs->ibuf= BKE_image_get_ibuf(ima, NULL); /* note, these calls only free/fill contents of zspan struct, not zspan itself */ zbuf_free_span(bs->zspan); zbuf_alloc_span(bs->zspan, bs->ibuf->x, bs->ibuf->y, R.clipcrop); } bs->rectx= bs->ibuf->x; bs->recty= bs->ibuf->y; bs->rect= bs->ibuf->rect; bs->rect_float= bs->ibuf->rect_float; bs->quad= 0; if (bs->use_mask) { if (bs->ibuf->userdata==NULL) { BLI_lock_thread(LOCK_CUSTOM1); if (bs->ibuf->userdata==NULL) /* since the thread was locked, its possible another thread alloced the value */ bs->ibuf->userdata = (void *)MEM_callocN(sizeof(char)*bs->rectx*bs->recty, "BakeMask"); bs->rect_mask= (char *)bs->ibuf->userdata; BLI_unlock_thread(LOCK_CUSTOM1); } else { bs->rect_mask= (char *)bs->ibuf->userdata; } } /* get pixel level vertex coordinates */ for (a=0; a<4; a++) { /* Note, workaround for pixel aligned UVs which are common and can screw up our intersection tests * where a pixel gets in between 2 faces or the middle of a quad, * camera aligned quads also have this problem but they are less common. * Add a small offset to the UVs, fixes bug #18685 - Campbell */ vec[a][0]= tface->uv[a][0]*(float)bs->rectx - (0.5f + 0.001f); vec[a][1]= tface->uv[a][1]*(float)bs->recty - (0.5f + 0.002f); } /* UV indices have to be corrected for possible quad->tria splits */ i1= 0; i2= 1; i3= 2; vlr_set_uv_indices(vlr, &i1, &i2, &i3); bake_set_vlr_dxyco(bs, vec[i1], vec[i2], vec[i3]); zspan_scanconvert(bs->zspan, bs, vec[i1], vec[i2], vec[i3], do_bake_shade); if (vlr->v4) { bs->quad= 1; bake_set_vlr_dxyco(bs, vec[0], vec[2], vec[3]); zspan_scanconvert(bs->zspan, bs, vec[0], vec[2], vec[3], do_bake_shade); } } static void *do_bake_thread(void *bs_v) { BakeShade *bs= bs_v; while (get_next_bake_face(bs)) { shade_tface(bs); /* fast threadsafe break test */ if (R.test_break(R.tbh)) break; /* access is not threadsafe but since its just true/false probably ok * only used for interactive baking */ if (bs->do_update) *bs->do_update= TRUE; } bs->ready= 1; return NULL; } void RE_bake_ibuf_filter(ImBuf *ibuf, char *mask, const int filter) { /* must check before filtering */ const short is_new_alpha= (ibuf->planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(ibuf); /* Margin */ if (filter) { IMB_filter_extend(ibuf, mask, filter); } /* if the bake results in new alpha then change the image setting */ if (is_new_alpha) { ibuf->planes= R_IMF_PLANES_RGBA; } else { if (filter && ibuf->planes != R_IMF_PLANES_RGBA) { /* clear alpha added by filtering */ IMB_rectfill_alpha(ibuf, 1.0f); } } } /* using object selection tags, the faces with UV maps get baked */ /* render should have been setup */ /* returns 0 if nothing was handled */ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_update, float *progress) { BakeShade *handles; ListBase threads; Image *ima; int a, vdone = FALSE, use_mask = FALSE, result = BAKE_RESULT_OK; /* initialize render global */ R= *re; R.bakebuf= NULL; /* initialize static vars */ get_next_bake_face(NULL); /* do we need a mask? */ if (re->r.bake_filter) use_mask = TRUE; /* baker uses this flag to detect if image was initialized */ for (ima= G.main->image.first; ima; ima= ima->id.next) { ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL); ima->id.flag |= LIB_DOIT; ima->flag&= ~IMA_USED_FOR_RENDER; if (ibuf) { ibuf->userdata = NULL; /* use for masking if needed */ if (ibuf->rect_float) ibuf->profile = IB_PROFILE_LINEAR_RGB; } } BLI_init_threads(&threads, do_bake_thread, re->r.threads); handles= MEM_callocN(sizeof(BakeShade)*re->r.threads, "BakeShade"); /* get the threads running */ for (a=0; ar.threads; a++) { /* set defaults in handles */ handles[a].ssamp.shi[0].lay= re->lay; if (type==RE_BAKE_SHADOW) { handles[a].ssamp.shi[0].passflag= SCE_PASS_SHADOW; } else { handles[a].ssamp.shi[0].passflag= SCE_PASS_COMBINED; } handles[a].ssamp.shi[0].combinedflag= ~(SCE_PASS_SPEC); handles[a].ssamp.shi[0].thread= a; handles[a].ssamp.tot= 1; handles[a].type= type; handles[a].actob= actob; handles[a].zspan= MEM_callocN(sizeof(ZSpan), "zspan for bake"); handles[a].use_mask = use_mask; handles[a].do_update = do_update; /* use to tell the view to update */ BLI_insert_thread(&threads, &handles[a]); } /* wait for everything to be done */ a= 0; while (a!=re->r.threads) { PIL_sleep_ms(50); /* calculate progress */ for (vdone = FALSE, a=0; ar.threads; a++) vdone+= handles[a].vdone; if (progress) *progress = (float)(vdone / (float)re->totvlak); for (a=0; ar.threads; a++) { if (handles[a].ready==0) break; } } /* filter and refresh images */ for (ima= G.main->image.first; ima; ima= ima->id.next) { if ((ima->id.flag & LIB_DOIT)==0) { ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL); if (ima->flag & IMA_USED_FOR_RENDER) result= BAKE_RESULT_FEEDBACK_LOOP; if (!ibuf) continue; RE_bake_ibuf_filter(ibuf, (char *)ibuf->userdata, re->r.bake_filter); ibuf->userflags |= IB_BITMAPDIRTY; if (ibuf->rect_float) IMB_rect_from_float(ibuf); } } /* calculate return value */ for (a=0; ar.threads; a++) { zbuf_free_span(handles[a].zspan); MEM_freeN(handles[a].zspan); } MEM_freeN(handles); BLI_end_threads(&threads); if (vdone==0) result= BAKE_RESULT_NO_OBJECTS; return result; } struct Image *RE_bake_shade_get_image(void) { return R.bakebuf; }