/* * ***** 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, RPW * 2004-2006 Blender Foundation, full recode * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/render/intern/source/zbuf.c * \ingroup render */ /*---------------------------------------------------------------------------*/ /* Common includes */ /*---------------------------------------------------------------------------*/ #include #include #include #include #include #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_jitter.h" #include "BLI_threads.h" #include "BLI_utildefines.h" #include "MEM_guardedalloc.h" #include "DNA_lamp_types.h" #include "DNA_node_types.h" #include "DNA_meshdata_types.h" #include "DNA_material_types.h" #include "BKE_global.h" #include "BKE_material.h" #include "RE_render_ext.h" /* local includes */ #include "pixelblending.h" #include "render_result.h" #include "render_types.h" #include "renderpipeline.h" #include "renderdatabase.h" #include "rendercore.h" #include "shadbuf.h" #include "shading.h" #include "sss.h" #include "strand.h" /* own includes */ #include "zbuf.h" /* could enable at some point but for now there are far too many conversions */ #ifdef __GNUC__ # pragma GCC diagnostic ignored "-Wdouble-promotion" #endif /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* 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; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* ****************** Spans ******************************* */ /* each zbuffer has coordinates transformed to local rect coordinates, so we can simply clip */ void zbuf_alloc_span(ZSpan *zspan, int rectx, int recty, float clipcrop) { memset(zspan, 0, sizeof(ZSpan)); zspan->rectx= rectx; zspan->recty= recty; zspan->span1= MEM_mallocN(recty*sizeof(float), "zspan"); zspan->span2= MEM_mallocN(recty*sizeof(float), "zspan"); zspan->clipcrop= clipcrop; } void zbuf_free_span(ZSpan *zspan) { if (zspan) { if (zspan->span1) MEM_freeN(zspan->span1); if (zspan->span2) MEM_freeN(zspan->span2); zspan->span1= zspan->span2= NULL; } } /* reset range for clipping */ static void zbuf_init_span(ZSpan *zspan) { zspan->miny1= zspan->miny2= zspan->recty+1; zspan->maxy1= zspan->maxy2= -1; zspan->minp1= zspan->maxp1= zspan->minp2= zspan->maxp2= NULL; } static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2]) { const float *minv, *maxv; float *span; float xx1, dx0, xs0; int y, my0, my2; if (v1[1]= zspan->recty) return; /* clip top */ if (my2>=zspan->recty) my2= zspan->recty-1; /* clip bottom */ if (my0<0) my0= 0; if (my0>my2) return; /* if (my0>my2) should still fill in, that way we get spans that skip nicely */ xx1= maxv[1]-minv[1]; if (xx1>FLT_EPSILON) { dx0= (minv[0]-maxv[0])/xx1; xs0= dx0*(minv[1]-my2) + minv[0]; } else { dx0 = 0.0f; xs0 = min_ff(minv[0], maxv[0]); } /* empty span */ if (zspan->maxp1 == NULL) { span= zspan->span1; } else { /* does it complete left span? */ if ( maxv == zspan->minp1 || minv==zspan->maxp1) { span= zspan->span1; } else { span= zspan->span2; } } if (span==zspan->span1) { // printf("left span my0 %d my2 %d\n", my0, my2); if (zspan->minp1==NULL || zspan->minp1[1] > minv[1] ) { zspan->minp1= minv; } if (zspan->maxp1==NULL || zspan->maxp1[1] < maxv[1] ) { zspan->maxp1= maxv; } if (my0miny1) zspan->miny1= my0; if (my2>zspan->maxy1) zspan->maxy1= my2; } else { // printf("right span my0 %d my2 %d\n", my0, my2); if (zspan->minp2==NULL || zspan->minp2[1] > minv[1] ) { zspan->minp2= minv; } if (zspan->maxp2==NULL || zspan->maxp2[1] < maxv[1] ) { zspan->maxp2= maxv; } if (my0miny2) zspan->miny2= my0; if (my2>zspan->maxy2) zspan->maxy2= my2; } for (y=my2; y>=my0; y--, xs0+= dx0) { /* xs0 is the xcoord! */ span[y]= xs0; } } /*-----------------------------------------------------------*/ /* Functions */ /*-----------------------------------------------------------*/ void fillrect(int *rect, int x, int y, int val) { int len, *drect; len= x*y; drect= rect; while (len>0) { len--; *drect= val; drect++; } } /* based on Liang&Barsky, for clipping of pyramidical volume */ static short cliptestf(float a, float b, float c, float d, float *u1, float *u2) { float p= a + b, q= c + d, r; if (p<0.0f) { if (q*u2) return 0; else if (r>*u1) *u1=r; } } else { if (p>0.0f) { if (q<0.0f) return 0; else if (q abs4) c+=2; if ( v[1] > abs4) c+=4; else if ( v[1] < -abs4) c+=8; if (v[2] < -abs4) c+=16; /* this used to be " if (v[2]<0) ", see clippz() */ else if (v[2]> abs4) c+= 32; return c; } /* ************* ACCUMULATION ZBUF ************ */ static APixstr *addpsmainA(ListBase *lb) { APixstrMain *psm; psm= MEM_mallocN(sizeof(APixstrMain), "addpsmainA"); BLI_addtail(lb, psm); psm->ps= MEM_callocN(4096*sizeof(APixstr), "pixstr"); return psm->ps; } void freepsA(ListBase *lb) { APixstrMain *psm, *psmnext; for (psm= lb->first; psm; psm= psmnext) { psmnext= psm->next; if (psm->ps) MEM_freeN(psm->ps); MEM_freeN(psm); } } static APixstr *addpsA(ZSpan *zspan) { /* make new PS */ if (zspan->apsmcounter==0) { zspan->curpstr= addpsmainA(zspan->apsmbase); zspan->apsmcounter= 4095; } else { zspan->curpstr++; zspan->apsmcounter--; } return zspan->curpstr; } static void zbuffillAc4(ZSpan *zspan, int obi, int zvlnr, const float *v1, const float *v2, const float *v3, const float *v4) { APixstr *ap, *apofs, *apn; double zxd, zyd, zy0, zverg; float x0, y0, z0; float x1, y1, z1, x2, y2, z2, xx1; const float *span1, *span2; int *rz, *rm, x, y; int sn1, sn2, rectx, *rectzofs, *rectmaskofs, my0, my2, mask; /* init */ zbuf_init_span(zspan); /* set spans */ zbuf_add_to_span(zspan, v1, v2); zbuf_add_to_span(zspan, v2, v3); if (v4) { zbuf_add_to_span(zspan, v3, v4); zbuf_add_to_span(zspan, v4, v1); } else zbuf_add_to_span(zspan, v3, v1); /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; if (my2rectx; rectzofs= (int *)(zspan->arectz+rectx*(my2)); rectmaskofs= (int *)(zspan->rectmask+rectx*(my2)); apofs= (zspan->apixbuf+ rectx*(my2)); mask= zspan->mask; /* correct span */ sn1= (my0 + my2)/2; if (zspan->span1[sn1] < zspan->span2[sn1]) { span1= zspan->span1+my2; span2= zspan->span2+my2; } else { span1= zspan->span2+my2; span2= zspan->span1+my2; } for (y=my2; y>=my0; y--, span1--, span2--) { sn1= floor(*span1); sn2= floor(*span2); sn1++; if (sn2>=rectx) sn2= rectx-1; if (sn1<0) sn1= 0; if (sn2>=sn1) { int intzverg; zverg= (double)sn1*zxd + zy0; rz= rectzofs+sn1; rm= rectmaskofs+sn1; ap= apofs+sn1; x= sn2-sn1; zverg-= zspan->polygon_offset; while (x>=0) { intzverg= (int)CLAMPIS(zverg, INT_MIN, INT_MAX); if ( intzverg < *rz) { if (!zspan->rectmask || intzverg > *rm) { apn= ap; while (apn) { if (apn->p[0]==0) {apn->obi[0]= obi; apn->p[0]= zvlnr; apn->z[0]= intzverg; apn->mask[0]= mask; break; } if (apn->p[0]==zvlnr && apn->obi[0]==obi) {apn->mask[0]|= mask; break; } if (apn->p[1]==0) {apn->obi[1]= obi; apn->p[1]= zvlnr; apn->z[1]= intzverg; apn->mask[1]= mask; break; } if (apn->p[1]==zvlnr && apn->obi[1]==obi) {apn->mask[1]|= mask; break; } if (apn->p[2]==0) {apn->obi[2]= obi; apn->p[2]= zvlnr; apn->z[2]= intzverg; apn->mask[2]= mask; break; } if (apn->p[2]==zvlnr && apn->obi[2]==obi) {apn->mask[2]|= mask; break; } if (apn->p[3]==0) {apn->obi[3]= obi; apn->p[3]= zvlnr; apn->z[3]= intzverg; apn->mask[3]= mask; break; } if (apn->p[3]==zvlnr && apn->obi[3]==obi) {apn->mask[3]|= mask; break; } if (apn->next==NULL) apn->next= addpsA(zspan); apn= apn->next; } } } zverg+= zxd; rz++; rm++; ap++; x--; } } zy0-=zyd; rectzofs-= rectx; rectmaskofs-= rectx; apofs-= rectx; } } static void zbuflineAc(ZSpan *zspan, int obi, int zvlnr, const float vec1[3], const float vec2[3]) { APixstr *ap, *apn; const int *rectz, *rectmask; int start, end, x, y, oldx, oldy, ofs; int dz, vergz, mask, maxtest=0; float dx, dy; float v1[3], v2[3]; dx= vec2[0]-vec1[0]; dy= vec2[1]-vec1[1]; mask= zspan->mask; if (fabsf(dx) > fabsf(dy)) { /* all lines from left to right */ if (vec1[0]=zspan->rectx) end= zspan->rectx-1; oldy= floor(v1[1]); dy/= dx; vergz= v1[2]; vergz-= zspan->polygon_offset; dz= (v2[2]-v1[2])/dx; if (vergz>0x50000000 && dz>0) maxtest= 1; /* prevent overflow */ rectz= (int *)(zspan->arectz+zspan->rectx*(oldy) +start); rectmask= (int *)(zspan->rectmask+zspan->rectx*(oldy) +start); ap= (zspan->apixbuf+ zspan->rectx*(oldy) +start); if (dy<0) ofs= -zspan->rectx; else ofs= zspan->rectx; for (x= start; x<=end; x++, rectz++, rectmask++, ap++) { y= floor(v1[1]); if (y!=oldy) { oldy= y; rectz+= ofs; rectmask+= ofs; ap+= ofs; } if (x>=0 && y>=0 && yrecty) { if (vergz<*rectz) { if (!zspan->rectmask || vergz>*rectmask) { apn= ap; while (apn) { /* loop unrolled */ if (apn->p[0]==0) {apn->obi[0]= obi; apn->p[0]= zvlnr; apn->z[0]= vergz; apn->mask[0]= mask; break; } if (apn->p[0]==zvlnr && apn->obi[0]==obi) {apn->mask[0]|= mask; break; } if (apn->p[1]==0) {apn->obi[1]= obi; apn->p[1]= zvlnr; apn->z[1]= vergz; apn->mask[1]= mask; break; } if (apn->p[1]==zvlnr && apn->obi[1]==obi) {apn->mask[1]|= mask; break; } if (apn->p[2]==0) {apn->obi[2]= obi; apn->p[2]= zvlnr; apn->z[2]= vergz; apn->mask[2]= mask; break; } if (apn->p[2]==zvlnr && apn->obi[2]==obi) {apn->mask[2]|= mask; break; } if (apn->p[3]==0) {apn->obi[3]= obi; apn->p[3]= zvlnr; apn->z[3]= vergz; apn->mask[3]= mask; break; } if (apn->p[3]==zvlnr && apn->obi[3]==obi) {apn->mask[3]|= mask; break; } if (apn->next == NULL) apn->next = addpsA(zspan); apn= apn->next; } } } } v1[1]+= dy; if (maxtest && (vergz > 0x7FFFFFF0 - dz)) vergz= 0x7FFFFFF0; else vergz+= dz; } } else { /* all lines from top to bottom */ if (vec1[1]=zspan->recty || end<0) return; if (end>=zspan->recty) end= zspan->recty-1; oldx= floor(v1[0]); dx/= dy; vergz= v1[2]; vergz-= zspan->polygon_offset; dz= (v2[2]-v1[2])/dy; if (vergz>0x50000000 && dz>0) maxtest= 1; /* prevent overflow */ rectz= (int *)( zspan->arectz+ (start)*zspan->rectx+ oldx ); rectmask= (int *)( zspan->rectmask+ (start)*zspan->rectx+ oldx ); ap= (zspan->apixbuf+ zspan->rectx*(start) +oldx); if (dx<0) ofs= -1; else ofs= 1; for (y= start; y<=end; y++, rectz+=zspan->rectx, rectmask+=zspan->rectx, ap+=zspan->rectx) { x= floor(v1[0]); if (x!=oldx) { oldx= x; rectz+= ofs; rectmask+= ofs; ap+= ofs; } if (x>=0 && y>=0 && xrectx) { if (vergz<*rectz) { if (!zspan->rectmask || vergz>*rectmask) { apn= ap; while (apn) { /* loop unrolled */ if (apn->p[0]==0) {apn->obi[0]= obi; apn->p[0]= zvlnr; apn->z[0]= vergz; apn->mask[0]= mask; break; } if (apn->p[0]==zvlnr) {apn->mask[0]|= mask; break; } if (apn->p[1]==0) {apn->obi[1]= obi; apn->p[1]= zvlnr; apn->z[1]= vergz; apn->mask[1]= mask; break; } if (apn->p[1]==zvlnr) {apn->mask[1]|= mask; break; } if (apn->p[2]==0) {apn->obi[2]= obi; apn->p[2]= zvlnr; apn->z[2]= vergz; apn->mask[2]= mask; break; } if (apn->p[2]==zvlnr) {apn->mask[2]|= mask; break; } if (apn->p[3]==0) {apn->obi[3]= obi; apn->p[3]= zvlnr; apn->z[3]= vergz; apn->mask[3]= mask; break; } if (apn->p[3]==zvlnr) {apn->mask[3]|= mask; break; } if (apn->next == NULL) apn->next = addpsA(zspan); apn= apn->next; } } } } v1[0]+= dx; if (maxtest && (vergz > 0x7FFFFFF0 - dz)) vergz= 0x7FFFFFF0; else vergz+= dz; } } } /* ************* NORMAL ZBUFFER ************ */ static void zbufline(ZSpan *zspan, int obi, int zvlnr, const float vec1[3], const float vec2[3]) { int *rectz, *rectp, *recto, *rectmask; int start, end, x, y, oldx, oldy, ofs; int dz, vergz, maxtest= 0; float dx, dy; float v1[3], v2[3]; dx= vec2[0]-vec1[0]; dy= vec2[1]-vec1[1]; if (fabsf(dx) > fabsf(dy)) { /* all lines from left to right */ if (vec1[0]=zspan->rectx) end= zspan->rectx-1; oldy= floor(v1[1]); dy/= dx; vergz= floor(v1[2]); dz= floor((v2[2]-v1[2])/dx); if (vergz>0x50000000 && dz>0) maxtest= 1; /* prevent overflow */ rectz= zspan->rectz + oldy*zspan->rectx+ start; rectp= zspan->rectp + oldy*zspan->rectx+ start; recto= zspan->recto + oldy*zspan->rectx+ start; rectmask= zspan->rectmask + oldy*zspan->rectx+ start; if (dy<0) ofs= -zspan->rectx; else ofs= zspan->rectx; for (x= start; x<=end; x++, rectz++, rectp++, recto++, rectmask++) { y= floor(v1[1]); if (y!=oldy) { oldy= y; rectz+= ofs; rectp+= ofs; recto+= ofs; rectmask+= ofs; } if (x>=0 && y>=0 && yrecty) { if (vergz<*rectz) { if (!zspan->rectmask || vergz>*rectmask) { *recto= obi; *rectz= vergz; *rectp= zvlnr; } } } v1[1]+= dy; if (maxtest && (vergz > 0x7FFFFFF0 - dz)) vergz= 0x7FFFFFF0; else vergz+= dz; } } else { /* all lines from top to bottom */ if (vec1[1]=zspan->recty) end= zspan->recty-1; oldx= floor(v1[0]); dx/= dy; vergz= floor(v1[2]); dz= floor((v2[2]-v1[2])/dy); if (vergz>0x50000000 && dz>0) maxtest= 1; /* prevent overflow */ rectz= zspan->rectz + start*zspan->rectx+ oldx; rectp= zspan->rectp + start*zspan->rectx+ oldx; recto= zspan->recto + start*zspan->rectx+ oldx; rectmask= zspan->rectmask + start*zspan->rectx+ oldx; if (dx<0) ofs= -1; else ofs= 1; for (y= start; y<=end; y++, rectz+=zspan->rectx, rectp+=zspan->rectx, recto+=zspan->rectx, rectmask+=zspan->rectx) { x= floor(v1[0]); if (x!=oldx) { oldx= x; rectz+= ofs; rectp+= ofs; recto+= ofs; rectmask+= ofs; } if (x>=0 && y>=0 && xrectx) { if (vergz<*rectz) { if (!zspan->rectmask || vergz>*rectmask) { *rectz= vergz; *rectp= zvlnr; *recto= obi; } } } v1[0]+= dx; if (maxtest && (vergz > 0x7FFFFFF0 - dz)) vergz= 0x7FFFFFF0; else vergz+= dz; } } } static void zbufline_onlyZ(ZSpan *zspan, int UNUSED(obi), int UNUSED(zvlnr), const float vec1[3], const float vec2[3]) { int *rectz, *rectz1= NULL; int start, end, x, y, oldx, oldy, ofs; int dz, vergz, maxtest= 0; float dx, dy; float v1[3], v2[3]; dx= vec2[0]-vec1[0]; dy= vec2[1]-vec1[1]; if (fabsf(dx) > fabsf(dy)) { /* all lines from left to right */ if (vec1[0]=zspan->rectx) end= zspan->rectx-1; oldy= floor(v1[1]); dy/= dx; vergz= floor(v1[2]); dz= floor((v2[2]-v1[2])/dx); if (vergz>0x50000000 && dz>0) maxtest= 1; /* prevent overflow */ rectz= zspan->rectz + oldy*zspan->rectx+ start; if (zspan->rectz1) rectz1= zspan->rectz1 + oldy*zspan->rectx+ start; if (dy<0) ofs= -zspan->rectx; else ofs= zspan->rectx; for (x= start; x<=end; x++, rectz++) { y= floor(v1[1]); if (y!=oldy) { oldy= y; rectz+= ofs; if (rectz1) rectz1+= ofs; } if (x>=0 && y>=0 && yrecty) { if (vergz < *rectz) { if (rectz1) *rectz1= *rectz; *rectz= vergz; } else if (rectz1 && vergz < *rectz1) *rectz1= vergz; } v1[1]+= dy; if (maxtest && (vergz > 0x7FFFFFF0 - dz)) vergz= 0x7FFFFFF0; else vergz+= dz; if (rectz1) rectz1++; } } else { /* all lines from top to bottom */ if (vec1[1]=zspan->recty) end= zspan->recty-1; oldx= floor(v1[0]); dx/= dy; vergz= floor(v1[2]); dz= floor((v2[2]-v1[2])/dy); if (vergz>0x50000000 && dz>0) maxtest= 1; /* prevent overflow */ rectz= zspan->rectz + start*zspan->rectx+ oldx; if (zspan->rectz1) rectz1= zspan->rectz1 + start*zspan->rectx+ oldx; if (dx<0) ofs= -1; else ofs= 1; for (y= start; y<=end; y++, rectz+=zspan->rectx) { x= floor(v1[0]); if (x!=oldx) { oldx= x; rectz+= ofs; if (rectz1) rectz1+= ofs; } if (x>=0 && y>=0 && xrectx) { if (vergz < *rectz) { if (rectz1) *rectz1= *rectz; *rectz= vergz; } else if (rectz1 && vergz < *rectz1) *rectz1= vergz; } v1[0]+= dx; if (maxtest && (vergz > 0x7FFFFFF0 - dz)) vergz= 0x7FFFFFF0; else vergz+= dz; if (rectz1) rectz1+=zspan->rectx; } } } static int clipline(float v1[4], float v2[4]) /* return 0: do not draw */ { float dz, dw, u1=0.0, u2=1.0; float dx, dy, v13; dz= v2[2]-v1[2]; dw= v2[3]-v1[3]; /* this 1.01 is for clipping x and y just a tinsy larger. that way it is * filled in with zbufwire correctly when rendering in parts. otherwise * you see line endings at edges... */ if (cliptestf(-dz, -dw, v1[3], v1[2], &u1, &u2)) { if (cliptestf(dz, -dw, v1[3], -v1[2], &u1, &u2)) { dx= v2[0]-v1[0]; dz= 1.01f*(v2[3]-v1[3]); v13= 1.01f*v1[3]; if (cliptestf(-dx, -dz, v1[0], v13, &u1, &u2)) { if (cliptestf(dx, -dz, v13, -v1[0], &u1, &u2)) { dy= v2[1]-v1[1]; if (cliptestf(-dy, -dz, v1[1], v13, &u1, &u2)) { if (cliptestf(dy, -dz, v13, -v1[1], &u1, &u2)) { if (u2<1.0f) { v2[0]= v1[0]+u2*dx; v2[1]= v1[1]+u2*dy; v2[2]= v1[2]+u2*dz; v2[3]= v1[3]+u2*dw; } if (u1>0.0f) { v1[0]= v1[0]+u1*dx; v1[1]= v1[1]+u1*dy; v1[2]= v1[2]+u1*dz; v1[3]= v1[3]+u1*dw; } return 1; } } } } } } return 0; } void hoco_to_zco(ZSpan *zspan, float zco[3], const float hoco[4]) { float div; div= 1.0f/hoco[3]; zco[0]= zspan->zmulx*(1.0f+hoco[0]*div) + zspan->zofsx; zco[1]= zspan->zmuly*(1.0f+hoco[1]*div) + zspan->zofsy; zco[2]= 0x7FFFFFFF *(hoco[2]*div); } void zbufclipwire(ZSpan *zspan, int obi, int zvlnr, int ec, const float ho1[4], const float ho2[4], const float ho3[4], const float ho4[4], const int c1, const int c2, const int c3, const int c4) { float vez[20]; int and, or; /* edgecode: 1= draw */ if (ec==0) return; if (ho4) { and= (c1 & c2 & c3 & c4); or= (c1 | c2 | c3 | c4); } else { and= (c1 & c2 & c3); or= (c1 | c2 | c3); } if (or) { /* not in the middle */ if (and) { /* out completely */ return; } else { /* clipping */ if (ec & ME_V1V2) { copy_v4_v4(vez, ho1); copy_v4_v4(vez+4, ho2); if ( clipline(vez, vez+4)) { hoco_to_zco(zspan, vez, vez); hoco_to_zco(zspan, vez+4, vez+4); zspan->zbuflinefunc(zspan, obi, zvlnr, vez, vez+4); } } if (ec & ME_V2V3) { copy_v4_v4(vez, ho2); copy_v4_v4(vez+4, ho3); if ( clipline(vez, vez+4)) { hoco_to_zco(zspan, vez, vez); hoco_to_zco(zspan, vez+4, vez+4); zspan->zbuflinefunc(zspan, obi, zvlnr, vez, vez+4); } } if (ho4) { if (ec & ME_V3V4) { copy_v4_v4(vez, ho3); copy_v4_v4(vez+4, ho4); if ( clipline(vez, vez+4)) { hoco_to_zco(zspan, vez, vez); hoco_to_zco(zspan, vez+4, vez+4); zspan->zbuflinefunc(zspan, obi, zvlnr, vez, vez+4); } } if (ec & ME_V4V1) { copy_v4_v4(vez, ho4); copy_v4_v4(vez+4, ho1); if ( clipline(vez, vez+4)) { hoco_to_zco(zspan, vez, vez); hoco_to_zco(zspan, vez+4, vez+4); zspan->zbuflinefunc(zspan, obi, zvlnr, vez, vez+4); } } } else { if (ec & ME_V3V1) { copy_v4_v4(vez, ho3); copy_v4_v4(vez+4, ho1); if ( clipline(vez, vez+4)) { hoco_to_zco(zspan, vez, vez); hoco_to_zco(zspan, vez+4, vez+4); zspan->zbuflinefunc(zspan, obi, zvlnr, vez, vez+4); } } } return; } } hoco_to_zco(zspan, vez, ho1); hoco_to_zco(zspan, vez+4, ho2); hoco_to_zco(zspan, vez+8, ho3); if (ho4) { hoco_to_zco(zspan, vez+12, ho4); if (ec & ME_V3V4) zspan->zbuflinefunc(zspan, obi, zvlnr, vez+8, vez+12); if (ec & ME_V4V1) zspan->zbuflinefunc(zspan, obi, zvlnr, vez+12, vez); } else { if (ec & ME_V3V1) zspan->zbuflinefunc(zspan, obi, zvlnr, vez+8, vez); } if (ec & ME_V1V2) zspan->zbuflinefunc(zspan, obi, zvlnr, vez, vez+4); if (ec & ME_V2V3) zspan->zbuflinefunc(zspan, obi, zvlnr, vez+4, vez+8); } void zbufsinglewire(ZSpan *zspan, int obi, int zvlnr, const float ho1[4], const float ho2[4]) { float f1[4], f2[4]; int c1, c2; c1= testclip(ho1); c2= testclip(ho2); if (c1 | c2) { /* not in the middle */ if (!(c1 & c2)) { /* not out completely */ copy_v4_v4(f1, ho1); copy_v4_v4(f2, ho2); if (clipline(f1, f2)) { hoco_to_zco(zspan, f1, f1); hoco_to_zco(zspan, f2, f2); zspan->zbuflinefunc(zspan, obi, zvlnr, f1, f2); } } } else { hoco_to_zco(zspan, f1, ho1); hoco_to_zco(zspan, f2, ho2); zspan->zbuflinefunc(zspan, obi, zvlnr, f1, f2); } } /** * Fill the z buffer, but invert z order, and add the face index to * the corresponding face buffer. * * This is one of the z buffer fill functions called in zbufclip() and * zbufwireclip(). * * \param v1 [4 floats, world coordinates] first vertex * \param v2 [4 floats, world coordinates] second vertex * \param v3 [4 floats, world coordinates] third vertex */ /* WATCH IT: zbuffillGLinv4 and zbuffillGL4 are identical except for a 2 lines, * commented below */ static void zbuffillGLinv4(ZSpan *zspan, int obi, int zvlnr, const float *v1, const float *v2, const float *v3, const float *v4) { double zxd, zyd, zy0, zverg; float x0, y0, z0; float x1, y1, z1, x2, y2, z2, xx1; const float *span1, *span2; int *rectoofs, *ro; int *rectpofs, *rp; const int *rectmaskofs, *rm; int *rz, x, y; int sn1, sn2, rectx, *rectzofs, my0, my2; /* init */ zbuf_init_span(zspan); /* set spans */ zbuf_add_to_span(zspan, v1, v2); zbuf_add_to_span(zspan, v2, v3); if (v4) { zbuf_add_to_span(zspan, v3, v4); zbuf_add_to_span(zspan, v4, v1); } else zbuf_add_to_span(zspan, v3, v1); /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; // printf("my %d %d\n", my0, my2); if (my2rectx; rectzofs= (zspan->rectz+rectx*my2); rectpofs= (zspan->rectp+rectx*my2); rectoofs= (zspan->recto+rectx*my2); rectmaskofs= (zspan->rectmask+rectx*my2); /* correct span */ sn1= (my0 + my2)/2; if (zspan->span1[sn1] < zspan->span2[sn1]) { span1= zspan->span1+my2; span2= zspan->span2+my2; } else { span1= zspan->span2+my2; span2= zspan->span1+my2; } for (y=my2; y>=my0; y--, span1--, span2--) { sn1= floor(*span1); sn2= floor(*span2); sn1++; if (sn2>=rectx) sn2= rectx-1; if (sn1<0) sn1= 0; if (sn2>=sn1) { int intzverg; zverg= (double)sn1*zxd + zy0; rz= rectzofs+sn1; rp= rectpofs+sn1; ro= rectoofs+sn1; rm= rectmaskofs+sn1; x= sn2-sn1; while (x>=0) { intzverg= (int)CLAMPIS(zverg, INT_MIN, INT_MAX); if ( intzverg > *rz || *rz==0x7FFFFFFF) { /* UNIQUE LINE: see comment above */ if (!zspan->rectmask || intzverg > *rm) { *ro= obi; /* UNIQUE LINE: see comment above (order differs) */ *rz= intzverg; *rp= zvlnr; } } zverg+= zxd; rz++; rp++; ro++; rm++; x--; } } zy0-=zyd; rectzofs-= rectx; rectpofs-= rectx; rectoofs-= rectx; rectmaskofs-= rectx; } } /* uses spanbuffers */ /* WATCH IT: zbuffillGLinv4 and zbuffillGL4 are identical except for a 2 lines, * commented below */ static void zbuffillGL4(ZSpan *zspan, int obi, int zvlnr, const float *v1, const float *v2, const float *v3, const float *v4) { double zxd, zyd, zy0, zverg; float x0, y0, z0; float x1, y1, z1, x2, y2, z2, xx1; const float *span1, *span2; int *rectoofs, *ro; int *rectpofs, *rp; const int *rectmaskofs, *rm; int *rz, x, y; int sn1, sn2, rectx, *rectzofs, my0, my2; /* init */ zbuf_init_span(zspan); /* set spans */ zbuf_add_to_span(zspan, v1, v2); zbuf_add_to_span(zspan, v2, v3); if (v4) { zbuf_add_to_span(zspan, v3, v4); zbuf_add_to_span(zspan, v4, v1); } else zbuf_add_to_span(zspan, v3, v1); /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; // printf("my %d %d\n", my0, my2); if (my2rectx; rectzofs= (zspan->rectz+rectx*my2); rectpofs= (zspan->rectp+rectx*my2); rectoofs= (zspan->recto+rectx*my2); rectmaskofs= (zspan->rectmask+rectx*my2); /* correct span */ sn1= (my0 + my2)/2; if (zspan->span1[sn1] < zspan->span2[sn1]) { span1= zspan->span1+my2; span2= zspan->span2+my2; } else { span1= zspan->span2+my2; span2= zspan->span1+my2; } for (y=my2; y>=my0; y--, span1--, span2--) { sn1= floor(*span1); sn2= floor(*span2); sn1++; if (sn2>=rectx) sn2= rectx-1; if (sn1<0) sn1= 0; if (sn2>=sn1) { int intzverg; zverg= (double)sn1*zxd + zy0; rz= rectzofs+sn1; rp= rectpofs+sn1; ro= rectoofs+sn1; rm= rectmaskofs+sn1; x= sn2-sn1; while (x>=0) { intzverg= (int)CLAMPIS(zverg, INT_MIN, INT_MAX); if (intzverg < *rz) { /* ONLY UNIQUE LINE: see comment above */ if (!zspan->rectmask || intzverg > *rm) { *rz= intzverg; *rp= zvlnr; *ro= obi; /* UNIQUE LINE: see comment above (order differs) */ } } zverg+= zxd; rz++; rp++; ro++; rm++; x--; } } zy0-=zyd; rectzofs-= rectx; rectpofs-= rectx; rectoofs-= rectx; rectmaskofs-= rectx; } } /** * Fill the z buffer. The face buffer is not operated on! * * This is one of the z buffer fill functions called in zbufclip() and * zbufwireclip(). * * \param v1 [4 floats, world coordinates] first vertex * \param v2 [4 floats, world coordinates] second vertex * \param v3 [4 floats, world coordinates] third vertex */ /* now: filling two Z values, the closest and 2nd closest */ static void zbuffillGL_onlyZ(ZSpan *zspan, int UNUSED(obi), int UNUSED(zvlnr), const float *v1, const float *v2, const float *v3, const float *v4) { double zxd, zyd, zy0, zverg; float x0, y0, z0; float x1, y1, z1, x2, y2, z2, xx1; const float *span1, *span2; int *rz, *rz1, x, y; int sn1, sn2, rectx, *rectzofs, *rectzofs1= NULL, my0, my2; /* init */ zbuf_init_span(zspan); /* set spans */ zbuf_add_to_span(zspan, v1, v2); zbuf_add_to_span(zspan, v2, v3); if (v4) { zbuf_add_to_span(zspan, v3, v4); zbuf_add_to_span(zspan, v4, v1); } else zbuf_add_to_span(zspan, v3, v1); /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; // printf("my %d %d\n", my0, my2); if (my2rectx; rectzofs= (zspan->rectz+rectx*my2); if (zspan->rectz1) rectzofs1= (zspan->rectz1+rectx*my2); /* correct span */ sn1= (my0 + my2)/2; if (zspan->span1[sn1] < zspan->span2[sn1]) { span1= zspan->span1+my2; span2= zspan->span2+my2; } else { span1= zspan->span2+my2; span2= zspan->span1+my2; } for (y=my2; y>=my0; y--, span1--, span2--) { sn1= floor(*span1); sn2= floor(*span2); sn1++; if (sn2>=rectx) sn2= rectx-1; if (sn1<0) sn1= 0; if (sn2>=sn1) { zverg= (double)sn1*zxd + zy0; rz= rectzofs+sn1; rz1= rectzofs1+sn1; x= sn2-sn1; while (x>=0) { int zvergi= (int)CLAMPIS(zverg, INT_MIN, INT_MAX); /* option: maintain two depth values, closest and 2nd closest */ if (zvergi < *rz) { if (rectzofs1) *rz1= *rz; *rz= zvergi; } else if (rectzofs1 && zvergi < *rz1) *rz1= zvergi; zverg+= zxd; rz++; rz1++; x--; } } zy0-=zyd; rectzofs-= rectx; if (rectzofs1) rectzofs1-= rectx; } } /* 2d scanconvert for tria, calls func for each x, y coordinate and gives UV barycentrics */ void zspan_scanconvert_strand(ZSpan *zspan, void *handle, float *v1, float *v2, float *v3, void (*func)(void *, int, int, float, float, float) ) { float x0, y0, x1, y1, x2, y2, z0, z1, z2, z; float u, v, uxd, uyd, vxd, vyd, uy0, vy0, zxd, zyd, zy0, xx1; const float *span1, *span2; int x, y, sn1, sn2, rectx= zspan->rectx, my0, my2; /* init */ zbuf_init_span(zspan); /* set spans */ zbuf_add_to_span(zspan, v1, v2); zbuf_add_to_span(zspan, v2, v3); zbuf_add_to_span(zspan, v3, v1); /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; // printf("my %d %d\n", my0, my2); if (my2span1[sn1] < zspan->span2[sn1]) { span1= zspan->span1+my2; span2= zspan->span2+my2; } else { span1= zspan->span2+my2; span2= zspan->span1+my2; } for (y=my2; y>=my0; y--, span1--, span2--) { sn1= floor(*span1); sn2= floor(*span2); sn1++; if (sn2>=rectx) sn2= rectx-1; if (sn1<0) sn1= 0; u= (double)sn1*uxd + uy0; v= (double)sn1*vxd + vy0; z= (double)sn1*zxd + zy0; for (x= sn1; x<=sn2; x++, u+=uxd, v+=vxd, z+=zxd) func(handle, x, y, u, v, z); uy0 -= uyd; vy0 -= vyd; zy0 -= zyd; } } /* scanconvert for strand triangles, calls func for each x, y coordinate and gives UV barycentrics and z */ void zspan_scanconvert(ZSpan *zspan, void *handle, float *v1, float *v2, float *v3, void (*func)(void *, int, int, float, float) ) { float x0, y0, x1, y1, x2, y2, z0, z1, z2; float u, v, uxd, uyd, vxd, vyd, uy0, vy0, xx1; const float *span1, *span2; int x, y, sn1, sn2, rectx= zspan->rectx, my0, my2; /* init */ zbuf_init_span(zspan); /* set spans */ zbuf_add_to_span(zspan, v1, v2); zbuf_add_to_span(zspan, v2, v3); zbuf_add_to_span(zspan, v3, v1); /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; // printf("my %d %d\n", my0, my2); if (my2span1[sn1] < zspan->span2[sn1]) { span1= zspan->span1+my2; span2= zspan->span2+my2; } else { span1= zspan->span2+my2; span2= zspan->span1+my2; } for (y=my2; y>=my0; y--, span1--, span2--) { sn1= floor(*span1); sn2= floor(*span2); sn1++; if (sn2>=rectx) sn2= rectx-1; if (sn1<0) sn1= 0; u= (double)sn1*uxd + uy0; v= (double)sn1*vxd + vy0; for (x= sn1; x<=sn2; x++, u+=uxd, v+=vxd) func(handle, x, y, u, v); uy0 -= uyd; vy0 -= vyd; } } /** * (clip pyramid) * Sets lambda: flag, and parametrize the clipping of vertices in * viewspace coordinates. lambda = -1 means no clipping, lambda in [0, 1] means a clipping. * Note: uses globals. * \param v1 start coordinate s * \param v2 target coordinate t * \param b1 * \param b2 * \param b3 * \param a index for coordinate (x, y, or z) */ static void clippyra(float *lambda, float *v1, float *v2, int *b2, int *b3, int a, float clipcrop) { float da, dw, u1=0.0, u2=1.0; float v13; lambda[0]= -1.0; lambda[1]= -1.0; da= v2[a]-v1[a]; /* prob; we clip slightly larger, osa renders add 2 pixels on edges, should become variable? */ /* or better; increase r.winx/y size, but thats quite a complex one. do it later */ if (a==2) { dw= (v2[3]-v1[3]); v13= v1[3]; } else { dw= clipcrop*(v2[3]-v1[3]); v13= clipcrop*v1[3]; } /* according the original article by Liang&Barsky, for clipping of * homogeneous coordinates with viewplane, the value of "0" is used instead of "-w" . * This differs from the other clipping cases (like left or top) and I considered * it to be not so 'homogenic'. But later it has proven to be an error, * who would have thought that of L&B! */ if (cliptestf(-da, -dw, v13, v1[a], &u1, &u2)) { if (cliptestf(da, -dw, v13, -v1[a], &u1, &u2)) { *b3=1; if (u2<1.0f) { lambda[1]= u2; *b2=1; } else lambda[1]=1.0; /* u2 */ if (u1>0.0f) { lambda[0] = u1; *b2 = 1; } else { lambda[0] = 0.0; } } } } /** * (make vertex pyramide clip) * Checks lambda and uses this to make decision about clipping the line * segment from v1 to v2. lambda is the factor by which the vector is * cut. ( calculate s + l * ( t - s )). The result is appended to the * vertex list of this face. * * * \param v1 start coordinate s * \param v2 target coordinate t * \param b1 * \param b2 * \param clve vertex vector. */ static void makevertpyra(float *vez, float *lambda, float **trias, float *v1, float *v2, int *b1, int *clve) { float l1, l2, *adr; l1= lambda[0]; l2= lambda[1]; if (l1!= -1.0f) { if (l1!= 0.0f) { adr= vez+4*(*clve); trias[*b1]=adr; (*clve)++; adr[0]= v1[0]+l1*(v2[0]-v1[0]); adr[1]= v1[1]+l1*(v2[1]-v1[1]); adr[2]= v1[2]+l1*(v2[2]-v1[2]); adr[3]= v1[3]+l1*(v2[3]-v1[3]); } else trias[*b1]= v1; (*b1)++; } if (l2!= -1.0f) { if (l2!= 1.0f) { adr= vez+4*(*clve); trias[*b1]=adr; (*clve)++; adr[0]= v1[0]+l2*(v2[0]-v1[0]); adr[1]= v1[1]+l2*(v2[1]-v1[1]); adr[2]= v1[2]+l2*(v2[2]-v1[2]); adr[3]= v1[3]+l2*(v2[3]-v1[3]); (*b1)++; } } } /* ------------------------------------------------------------------------- */ void projectverto(const float v1[3], float winmat[4][4], float adr[4]) { /* calcs homogenic coord of vertex v1 */ float x, y, z; x = v1[0]; y = v1[1]; z = v1[2]; adr[0] = x * winmat[0][0] + z * winmat[2][0] + winmat[3][0]; adr[1] = y * winmat[1][1] + z * winmat[2][1] + winmat[3][1]; adr[2] = z * winmat[2][2] + winmat[3][2]; adr[3] = z * winmat[2][3] + winmat[3][3]; //printf("hoco %f %f %f %f\n", adr[0], adr[1], adr[2], adr[3]); } /* ------------------------------------------------------------------------- */ void projectvert(const float v1[3], float winmat[4][4], float adr[4]) { /* calcs homogenic coord of vertex v1 */ float x, y, z; x = v1[0]; y = v1[1]; z = v1[2]; adr[0] = x * winmat[0][0] + y * winmat[1][0] + z * winmat[2][0] + winmat[3][0]; adr[1] = x * winmat[0][1] + y * winmat[1][1] + z * winmat[2][1] + winmat[3][1]; adr[2] = x * winmat[0][2] + y * winmat[1][2] + z * winmat[2][2] + winmat[3][2]; adr[3] = x * winmat[0][3] + y * winmat[1][3] + z * winmat[2][3] + winmat[3][3]; } /* ------------------------------------------------------------------------- */ #define ZBUF_PROJECT_CACHE_SIZE 256 typedef struct ZbufProjectCache { int index, clip; float ho[4]; } ZbufProjectCache; static void zbuf_project_cache_clear(ZbufProjectCache *cache, int size) { int i; if (size > ZBUF_PROJECT_CACHE_SIZE) size= ZBUF_PROJECT_CACHE_SIZE; memset(cache, 0, sizeof(ZbufProjectCache)*size); for (i=0; idisprect.xmin - winx-1)/(float)winx; bounds[1]= (2*pa->disprect.xmax - winx+1)/(float)winx; bounds[2]= (2*pa->disprect.ymin - winy-1)/(float)winy; bounds[3]= (2*pa->disprect.ymax - winy+1)/(float)winy; } static int zbuf_part_project(ZbufProjectCache *cache, int index, float winmat[4][4], float *bounds, float *co, float *ho) { float vec[3]; int cindex= index & 255; if (cache[cindex].index == index) { copy_v4_v4(ho, cache[cindex].ho); return cache[cindex].clip; } else { float wco; int clipflag= 0; copy_v3_v3(vec, co); projectvert(co, winmat, ho); wco= ho[3]; if (ho[0] < bounds[0]*wco) clipflag |= 1; else if (ho[0] > bounds[1]*wco) clipflag |= 2; if (ho[1] > bounds[3]*wco) clipflag |= 4; else if (ho[1] < bounds[2]*wco) clipflag |= 8; copy_v4_v4(cache[cindex].ho, ho); cache[cindex].clip= clipflag; cache[cindex].index= index; return clipflag; } } void zbuf_render_project(float winmat[4][4], const float co[3], float ho[4]) { float vec[3]; copy_v3_v3(vec, co); projectvert(vec, winmat, ho); } void zbuf_make_winmat(Render *re, float winmat[4][4]) { if (re->r.mode & R_PANORAMA) { float panomat[4][4]; unit_m4(panomat); panomat[0][0]= re->panoco; panomat[0][2]= re->panosi; panomat[2][0]= -re->panosi; panomat[2][2]= re->panoco; mul_m4_m4m4(winmat, re->winmat, panomat); } else copy_m4_m4(winmat, re->winmat); } /* do zbuffering and clip, f1 f2 f3 are hocos, c1 c2 c3 are clipping flags */ void zbufclip(ZSpan *zspan, int obi, int zvlnr, const float f1[4], const float f2[4], const float f3[4], const int c1, const int c2, const int c3) { float *vlzp[32][3], lambda[3][2]; float vez[400], *trias[40]; if (c1 | c2 | c3) { /* not in middle */ if (c1 & c2 & c3) { /* completely out */ return; } else { /* clipping */ int arg, v, b, clipflag[3], b1, b2, b3, c4, clve=3, clvlo, clvl=1; float *fp; vez[0]= f1[0]; vez[1]= f1[1]; vez[2]= f1[2]; vez[3]= f1[3]; vez[4]= f2[0]; vez[5]= f2[1]; vez[6]= f2[2]; vez[7]= f2[3]; vez[8]= f3[0]; vez[9]= f3[1]; vez[10]= f3[2];vez[11]= f3[3]; vlzp[0][0]= vez; vlzp[0][1]= vez+4; vlzp[0][2]= vez+8; clipflag[0]= ( (c1 & 48) | (c2 & 48) | (c3 & 48) ); if (clipflag[0]==0) { /* othwerwise it needs to be calculated again, after the first (z) clip */ clipflag[1]= ( (c1 & 3) | (c2 & 3) | (c3 & 3) ); clipflag[2]= ( (c1 & 12) | (c2 & 12) | (c3 & 12) ); } else clipflag[1]=clipflag[2]= 0; for (b=0;b<3;b++) { if (clipflag[b]) { clvlo= clvl; for (v=0; vclipcrop); clippyra(lambda[1], vlzp[v][1], vlzp[v][2], &b2, &b3, arg, zspan->clipcrop); clippyra(lambda[2], vlzp[v][2], vlzp[v][0], &b2, &b3, arg, zspan->clipcrop); if (b2==0 && b3==1) { /* completely 'in', but we copy because of last for () loop in this section */; vlzp[clvl][0]= vlzp[v][0]; vlzp[clvl][1]= vlzp[v][1]; vlzp[clvl][2]= vlzp[v][2]; vlzp[v][0]= NULL; clvl++; } else if (b3==0) { vlzp[v][0]= NULL; /* completely 'out' */; } else { b1=0; makevertpyra(vez, lambda[0], trias, vlzp[v][0], vlzp[v][1], &b1, &clve); makevertpyra(vez, lambda[1], trias, vlzp[v][1], vlzp[v][2], &b1, &clve); makevertpyra(vez, lambda[2], trias, vlzp[v][2], vlzp[v][0], &b1, &clve); /* after front clip done: now set clip flags */ if (b==0) { clipflag[1]= clipflag[2]= 0; f1= vez; for (b3=0; b32) { for (b3=3; b3<=b1; b3++) { vlzp[clvl][0]= trias[0]; vlzp[clvl][1]= trias[b3-2]; vlzp[clvl][2]= trias[b3-1]; clvl++; } } } } } } } /* warning, clip overflow, this should never happen! */ BLI_assert(!(clve > 38 || clvl > 31)); /* perspective division */ fp = vez; for (b = 0; b < clve; b++) { hoco_to_zco(zspan, fp, fp); fp += 4; } for (b = 1; b < clvl; b++) { if (vlzp[b][0]) { zspan->zbuffunc(zspan, obi, zvlnr, vlzp[b][0], vlzp[b][1], vlzp[b][2], NULL); } } return; } } /* perspective division: HCS to ZCS */ hoco_to_zco(zspan, vez, f1); hoco_to_zco(zspan, vez+4, f2); hoco_to_zco(zspan, vez+8, f3); zspan->zbuffunc(zspan, obi, zvlnr, vez, vez+4, vez+8, NULL); } void zbufclip4(ZSpan *zspan, int obi, int zvlnr, const float f1[4], const float f2[4], const float f3[4], const float f4[4], const int c1, const int c2, const int c3, const int c4) { float vez[16]; if (c1 | c2 | c3 | c4) { /* not in middle */ if (c1 & c2 & c3 & c4) { /* completely out */ return; } else { /* clipping */ zbufclip(zspan, obi, zvlnr, f1, f2, f3, c1, c2, c3); zbufclip(zspan, obi, zvlnr, f1, f3, f4, c1, c3, c4); } return; } /* perspective division: HCS to ZCS */ hoco_to_zco(zspan, vez, f1); hoco_to_zco(zspan, vez+4, f2); hoco_to_zco(zspan, vez+8, f3); hoco_to_zco(zspan, vez+12, f4); zspan->zbuffunc(zspan, obi, zvlnr, vez, vez+4, vez+8, vez+12); } /* ************** ZMASK ******************************** */ #define EXTEND_PIXEL(a) if (temprectp[a]) { z += rectz[a]; tot++; } (void)0 /* changes the zbuffer to be ready for z-masking: applies an extend-filter, and then clears */ static void zmask_rect(int *rectz, int *rectp, int xs, int ys, int neg) { int len=0, x, y; int *temprectp; int row1, row2, row3, *curp, *curz; temprectp= MEM_dupallocN(rectp); /* extend: if pixel is not filled in, we check surrounding pixels and average z value */ for (y=1; y<=ys; y++) { /* setup row indices */ row1= (y-2)*xs; row2= row1 + xs; row3= row2 + xs; if (y==1) row1= row2; else if (y==ys) row3= row2; curp= rectp + (y-1)*xs; curz= rectz + (y-1)*xs; for (x=0; x=0; len--) { if (rectp[len]==0) { rectz[len] = -0x7FFFFFFF; rectp[len]= -1; /* env code */ } } } } /* ***************** ZBUFFER MAIN ROUTINES **************** */ void zbuffer_solid(RenderPart *pa, RenderLayer *rl, void(*fillfunc)(RenderPart *, ZSpan *, int, void *), void *data) { ZbufProjectCache cache[ZBUF_PROJECT_CACHE_SIZE]; ZSpan zspans[16], *zspan; /* 16 = RE_MAX_OSA */ VlakRen *vlr= NULL; VertRen *v1, *v2, *v3, *v4; Material *ma = NULL; ObjectInstanceRen *obi; ObjectRen *obr; float obwinmat[4][4], winmat[4][4], bounds[4]; float ho1[4], ho2[4], ho3[4], ho4[4]={0}; unsigned int lay= rl->lay, lay_zmask= rl->lay_zmask; int i, v, zvlnr, zsample, samples, c1, c2, c3, c4=0; short nofill=0, env=0, wire=0, zmaskpass=0; short all_z= (rl->layflag & SCE_LAY_ALL_Z) && !(rl->layflag & SCE_LAY_ZMASK); short neg_zmask= (rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK); zbuf_make_winmat(&R, winmat); samples= (R.osa? R.osa: 1); samples= MIN2(4, samples-pa->sample); for (zsample=0; zsamplerectx, pa->recty, R.clipcrop); /* needed for transform from hoco to zbuffer co */ zspan->zmulx= ((float)R.winx)/2.0f; zspan->zmuly= ((float)R.winy)/2.0f; if (R.osa) { zspan->zofsx= -pa->disprect.xmin - R.jit[pa->sample+zsample][0]; zspan->zofsy= -pa->disprect.ymin - R.jit[pa->sample+zsample][1]; } else if (R.i.curblur) { zspan->zofsx= -pa->disprect.xmin - R.mblur_jit[R.i.curblur-1][0]; zspan->zofsy= -pa->disprect.ymin - R.mblur_jit[R.i.curblur-1][1]; } else { zspan->zofsx= -pa->disprect.xmin; zspan->zofsy= -pa->disprect.ymin; } /* to center the sample position */ zspan->zofsx -= 0.5f; zspan->zofsy -= 0.5f; /* the buffers */ if (zsample == samples-1) { zspan->rectp= pa->rectp; zspan->recto= pa->recto; if (neg_zmask) zspan->rectz= pa->rectmask; else zspan->rectz= pa->rectz; } else { zspan->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto"); zspan->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp"); zspan->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); } fillrect(zspan->rectz, pa->rectx, pa->recty, 0x7FFFFFFF); fillrect(zspan->rectp, pa->rectx, pa->recty, 0); fillrect(zspan->recto, pa->rectx, pa->recty, 0); } /* in case zmask we fill Z for objects in lay_zmask first, then clear Z, and then do normal zbuffering */ if (rl->layflag & SCE_LAY_ZMASK) zmaskpass= 1; for (; zmaskpass >=0; zmaskpass--) { ma= NULL; /* filling methods */ for (zsample=0; zsamplezbuffunc= zbuffillGLinv4; else zspan->zbuffunc= zbuffillGL4; zspan->zbuflinefunc= zbufline; } /* regular zbuffering loop, does all sample buffers */ for (i=0, obi=R.instancetable.first; obi; i++, obi=obi->next) { obr= obi->obr; /* continue happens in 2 different ways... zmaskpass only does lay_zmask stuff */ if (zmaskpass) { if ((obi->lay & lay_zmask)==0) continue; } else if (!all_z && !(obi->lay & (lay|lay_zmask))) continue; if (obi->flag & R_TRANSFORMED) mul_m4_m4m4(obwinmat, winmat, obi->mat); else copy_m4_m4(obwinmat, winmat); if (clip_render_object(obi->obr->boundbox, bounds, obwinmat)) continue; zbuf_project_cache_clear(cache, obr->totvert); for (v=0; vtotvlak; v++) { if ((v & 255)==0) vlr= obr->vlaknodes[v>>8].vlak; else vlr++; /* the cases: visible for render, only z values, zmask, nothing */ if (obi->lay & lay) { if (vlr->mat!=ma) { ma= vlr->mat; nofill= (ma->mode & MA_ONLYCAST) || ((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP)); env= (ma->mode & MA_ENV); wire= (ma->material_type == MA_TYPE_WIRE); for (zsample=0; zsamplemode & MA_ZINV || (zmaskpass && neg_zmask)) zspans[zsample].zbuffunc= zbuffillGLinv4; else zspans[zsample].zbuffunc= zbuffillGL4; } } } else if (all_z || (obi->lay & lay_zmask)) { env= 1; nofill= 0; ma= NULL; } else { nofill= 1; ma= NULL; /* otherwise nofill can hang */ } if (!(vlr->flag & R_HIDDEN) && nofill==0) { unsigned short partclip; v1= vlr->v1; v2= vlr->v2; v3= vlr->v3; v4= vlr->v4; c1= zbuf_part_project(cache, v1->index, obwinmat, bounds, v1->co, ho1); c2= zbuf_part_project(cache, v2->index, obwinmat, bounds, v2->co, ho2); c3= zbuf_part_project(cache, v3->index, obwinmat, bounds, v3->co, ho3); /* partclipping doesn't need viewplane clipping */ partclip= c1 & c2 & c3; if (v4) { c4= zbuf_part_project(cache, v4->index, obwinmat, bounds, v4->co, ho4); partclip &= c4; } if (partclip==0) { if (env) zvlnr= -1; else zvlnr= v+1; c1= testclip(ho1); c2= testclip(ho2); c3= testclip(ho3); if (v4) c4= testclip(ho4); for (zsample=0; zsampleec, ho1, ho2, ho3, ho4, c1, c2, c3, c4); else zbufclipwire(zspan, i, zvlnr, vlr->ec, ho1, ho2, ho3, NULL, c1, c2, c3, 0); } else { /* strands allow to be filled in as quad */ if (v4 && (vlr->flag & R_STRAND)) { zbufclip4(zspan, i, zvlnr, ho1, ho2, ho3, ho4, c1, c2, c3, c4); } else { zbufclip(zspan, i, zvlnr, ho1, ho2, ho3, c1, c2, c3); if (v4) zbufclip(zspan, i, (env)? zvlnr: zvlnr+RE_QUAD_OFFS, ho1, ho3, ho4, c1, c3, c4); } } } } } } } /* clear all z to close value, so it works as mask for next passes (ztra+strand) */ if (zmaskpass) { for (zsample=0; zsamplerectmask= zspan->rectz; if (zsample == samples-1) zspan->rectz= pa->rectz; else zspan->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); fillrect(zspan->rectz, pa->rectx, pa->recty, 0x7FFFFFFF); zmask_rect(zspan->rectmask, zspan->rectp, pa->rectx, pa->recty, 1); } else zmask_rect(zspan->rectz, zspan->rectp, pa->rectx, pa->recty, 0); } } } for (zsample=0; zsamplesample+zsample, data); if (zsample != samples-1) { MEM_freeN(zspan->rectz); MEM_freeN(zspan->rectp); MEM_freeN(zspan->recto); if (zspan->rectmask) MEM_freeN(zspan->rectmask); } zbuf_free_span(zspan); } } void zbuffer_shadow(Render *re, float winmat[4][4], LampRen *lar, int *rectz, int size, float jitx, float jity) { ZbufProjectCache cache[ZBUF_PROJECT_CACHE_SIZE]; ZSpan zspan; ObjectInstanceRen *obi; ObjectRen *obr; VlakRen *vlr= NULL; Material *ma= NULL; StrandSegment sseg; StrandRen *strand= NULL; StrandVert *svert; StrandBound *sbound; float obwinmat[4][4], ho1[4], ho2[4], ho3[4], ho4[4]; int a, b, c, i, c1, c2, c3, c4, ok=1, lay= -1; if (lar->mode & (LA_LAYER|LA_LAYER_SHADOW)) lay= lar->lay; /* 1.0f for clipping in clippyra()... bad stuff actually */ zbuf_alloc_span(&zspan, size, size, 1.0f); zspan.zmulx= ((float)size)/2.0f; zspan.zmuly= ((float)size)/2.0f; /* -0.5f to center the sample position */ zspan.zofsx= jitx - 0.5f; zspan.zofsy= jity - 0.5f; /* the buffers */ zspan.rectz= rectz; fillrect(rectz, size, size, 0x7FFFFFFE); if (lar->buftype==LA_SHADBUF_HALFWAY) { zspan.rectz1= MEM_mallocN(size*size*sizeof(int), "seconday z buffer"); fillrect(zspan.rectz1, size, size, 0x7FFFFFFE); } /* filling methods */ zspan.zbuflinefunc= zbufline_onlyZ; zspan.zbuffunc= zbuffillGL_onlyZ; for (i=0, obi=re->instancetable.first; obi; i++, obi=obi->next) { obr= obi->obr; if (obr->ob==re->excludeob) continue; else if (!(obi->lay & lay)) continue; if (obi->flag & R_TRANSFORMED) mul_m4_m4m4(obwinmat, winmat, obi->mat); else copy_m4_m4(obwinmat, winmat); if (clip_render_object(obi->obr->boundbox, NULL, obwinmat)) continue; zbuf_project_cache_clear(cache, obr->totvert); /* faces */ for (a=0; atotvlak; a++) { if ((a & 255)==0) vlr= obr->vlaknodes[a>>8].vlak; else vlr++; /* note, these conditions are copied in shadowbuf_autoclip() */ if (vlr->mat!= ma) { ma= vlr->mat; ok= 1; if ((ma->mode2 & MA_CASTSHADOW)==0 || (ma->mode & MA_SHADBUF)==0) ok= 0; } if (ok && (obi->lay & lay) && !(vlr->flag & R_HIDDEN)) { c1= zbuf_shadow_project(cache, vlr->v1->index, obwinmat, vlr->v1->co, ho1); c2= zbuf_shadow_project(cache, vlr->v2->index, obwinmat, vlr->v2->co, ho2); c3= zbuf_shadow_project(cache, vlr->v3->index, obwinmat, vlr->v3->co, ho3); if ((ma->material_type == MA_TYPE_WIRE) || (vlr->flag & R_STRAND)) { if (vlr->v4) { c4= zbuf_shadow_project(cache, vlr->v4->index, obwinmat, vlr->v4->co, ho4); zbufclipwire(&zspan, 0, a+1, vlr->ec, ho1, ho2, ho3, ho4, c1, c2, c3, c4); } else zbufclipwire(&zspan, 0, a+1, vlr->ec, ho1, ho2, ho3, NULL, c1, c2, c3, 0); } else { if (vlr->v4) { c4= zbuf_shadow_project(cache, vlr->v4->index, obwinmat, vlr->v4->co, ho4); zbufclip4(&zspan, 0, 0, ho1, ho2, ho3, ho4, c1, c2, c3, c4); } else zbufclip(&zspan, 0, 0, ho1, ho2, ho3, c1, c2, c3); } } if ((a & 255)==255 && re->test_break(re->tbh)) break; } /* strands */ if (obr->strandbuf) { /* for each bounding box containing a number of strands */ sbound= obr->strandbuf->bound; for (c=0; cstrandbuf->totbound; c++, sbound++) { if (clip_render_object(sbound->boundbox, NULL, obwinmat)) continue; /* for each strand in this bounding box */ for (a=sbound->start; aend; a++) { strand= RE_findOrAddStrand(obr, a); sseg.obi= obi; sseg.buffer= strand->buffer; sseg.sqadaptcos= sseg.buffer->adaptcos; sseg.sqadaptcos *= sseg.sqadaptcos; sseg.strand= strand; svert= strand->vert; /* note, these conditions are copied in shadowbuf_autoclip() */ if (sseg.buffer->ma!= ma) { ma= sseg.buffer->ma; ok= 1; if ((ma->mode2 & MA_CASTSHADOW)==0 || (ma->mode & MA_SHADBUF)==0) ok= 0; } if (ok && (sseg.buffer->lay & lay)) { zbuf_project_cache_clear(cache, strand->totvert); for (b=0; btotvert-1; b++, svert++) { sseg.v[0]= (b > 0)? (svert-1): svert; sseg.v[1]= svert; sseg.v[2]= svert+1; sseg.v[3]= (b < strand->totvert-2)? svert+2: svert+1; c1= zbuf_shadow_project(cache, sseg.v[0]-strand->vert, obwinmat, sseg.v[0]->co, ho1); c2= zbuf_shadow_project(cache, sseg.v[1]-strand->vert, obwinmat, sseg.v[1]->co, ho2); c3= zbuf_shadow_project(cache, sseg.v[2]-strand->vert, obwinmat, sseg.v[2]->co, ho3); c4= zbuf_shadow_project(cache, sseg.v[3]-strand->vert, obwinmat, sseg.v[3]->co, ho4); if (!(c1 & c2 & c3 & c4)) render_strand_segment(re, winmat, NULL, &zspan, 1, &sseg); } } if ((a & 255)==255 && re->test_break(re->tbh)) break; } } } if (re->test_break(re->tbh)) break; } /* merge buffers */ if (lar->buftype==LA_SHADBUF_HALFWAY) { for (a=size*size -1; a>=0; a--) rectz[a]= (rectz[a]>>1) + (zspan.rectz1[a]>>1); MEM_freeN(zspan.rectz1); } zbuf_free_span(&zspan); } static void zbuffill_sss(ZSpan *zspan, int obi, int zvlnr, const float *v1, const float *v2, const float *v3, const float *v4) { double zxd, zyd, zy0, z; float x0, y0, x1, y1, x2, y2, z0, z1, z2, xx1, *span1, *span2; int x, y, sn1, sn2, rectx= zspan->rectx, my0, my2; /* init */ zbuf_init_span(zspan); /* set spans */ zbuf_add_to_span(zspan, v1, v2); zbuf_add_to_span(zspan, v2, v3); if (v4) { zbuf_add_to_span(zspan, v3, v4); zbuf_add_to_span(zspan, v4, v1); } else zbuf_add_to_span(zspan, v3, v1); /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; if (my2span1[sn1] < zspan->span2[sn1]) { span1= zspan->span1+my2; span2= zspan->span2+my2; } else { span1= zspan->span2+my2; span2= zspan->span1+my2; } for (y=my2; y>=my0; y--, span1--, span2--) { sn1= floor(*span1); sn2= floor(*span2); sn1++; if (sn2>=rectx) sn2= rectx-1; if (sn1<0) sn1= 0; z= (double)sn1*zxd + zy0; for (x= sn1; x<=sn2; x++, z+=zxd) zspan->sss_func(zspan->sss_handle, obi, zvlnr, x, y, z); zy0 -= zyd; } } void zbuffer_sss(RenderPart *pa, unsigned int lay, void *handle, void (*func)(void *, int, int, int, int, int)) { ZbufProjectCache cache[ZBUF_PROJECT_CACHE_SIZE]; ZSpan zspan; ObjectInstanceRen *obi; ObjectRen *obr; VlakRen *vlr= NULL; VertRen *v1, *v2, *v3, *v4; Material *ma = NULL, *sss_ma = R.sss_mat; float obwinmat[4][4], winmat[4][4], bounds[4]; float ho1[4], ho2[4], ho3[4], ho4[4]={0}; int i, v, zvlnr, c1, c2, c3, c4=0; short nofill=0, env=0, wire=0; zbuf_make_winmat(&R, winmat); zbuffer_part_bounds(R.winx, R.winy, pa, bounds); zbuf_alloc_span(&zspan, pa->rectx, pa->recty, R.clipcrop); zspan.sss_handle= handle; zspan.sss_func= func; /* needed for transform from hoco to zbuffer co */ zspan.zmulx= ((float)R.winx)/2.0f; zspan.zmuly= ((float)R.winy)/2.0f; /* -0.5f to center the sample position */ zspan.zofsx= -pa->disprect.xmin - 0.5f; zspan.zofsy= -pa->disprect.ymin - 0.5f; /* filling methods */ zspan.zbuffunc= zbuffill_sss; /* fill front and back zbuffer */ if (pa->rectz) { fillrect(pa->recto, pa->rectx, pa->recty, 0); fillrect(pa->rectp, pa->rectx, pa->recty, 0); fillrect(pa->rectz, pa->rectx, pa->recty, 0x7FFFFFFF); } if (pa->rectbackz) { fillrect(pa->rectbacko, pa->rectx, pa->recty, 0); fillrect(pa->rectbackp, pa->rectx, pa->recty, 0); fillrect(pa->rectbackz, pa->rectx, pa->recty, -0x7FFFFFFF); } for (i=0, obi=R.instancetable.first; obi; i++, obi=obi->next) { obr= obi->obr; if (!(obi->lay & lay)) continue; if (obi->flag & R_TRANSFORMED) mul_m4_m4m4(obwinmat, winmat, obi->mat); else copy_m4_m4(obwinmat, winmat); if (clip_render_object(obi->obr->boundbox, bounds, obwinmat)) continue; zbuf_project_cache_clear(cache, obr->totvert); for (v=0; vtotvlak; v++) { if ((v & 255)==0) vlr= obr->vlaknodes[v>>8].vlak; else vlr++; if (material_in_material(vlr->mat, sss_ma)) { /* three cases, visible for render, only z values and nothing */ if (obi->lay & lay) { if (vlr->mat!=ma) { ma= vlr->mat; nofill= ma->mode & MA_ONLYCAST; env= (ma->mode & MA_ENV); wire= (ma->material_type == MA_TYPE_WIRE); } } else { nofill= 1; ma= NULL; /* otherwise nofill can hang */ } if (nofill==0 && wire==0 && env==0) { unsigned short partclip; v1= vlr->v1; v2= vlr->v2; v3= vlr->v3; v4= vlr->v4; c1= zbuf_part_project(cache, v1->index, obwinmat, bounds, v1->co, ho1); c2= zbuf_part_project(cache, v2->index, obwinmat, bounds, v2->co, ho2); c3= zbuf_part_project(cache, v3->index, obwinmat, bounds, v3->co, ho3); /* partclipping doesn't need viewplane clipping */ partclip= c1 & c2 & c3; if (v4) { c4= zbuf_part_project(cache, v4->index, obwinmat, bounds, v4->co, ho4); partclip &= c4; } if (partclip==0) { c1= testclip(ho1); c2= testclip(ho2); c3= testclip(ho3); zvlnr= v+1; zbufclip(&zspan, i, zvlnr, ho1, ho2, ho3, c1, c2, c3); if (v4) { c4= testclip(ho4); zbufclip(&zspan, i, zvlnr+RE_QUAD_OFFS, ho1, ho3, ho4, c1, c3, c4); } } } } } } zbuf_free_span(&zspan); } /* ******************** VECBLUR ACCUM BUF ************************* */ typedef struct DrawBufPixel { const float *colpoin; float alpha; } DrawBufPixel; static void zbuf_fill_in_rgba(ZSpan *zspan, DrawBufPixel *col, float *v1, float *v2, float *v3, float *v4) { DrawBufPixel *rectpofs, *rp; double zxd, zyd, zy0, zverg; float x0, y0, z0; float x1, y1, z1, x2, y2, z2, xx1; const float *span1, *span2; float *rectzofs, *rz; int x, y; int sn1, sn2, rectx, my0, my2; /* init */ zbuf_init_span(zspan); /* set spans */ zbuf_add_to_span(zspan, v1, v2); zbuf_add_to_span(zspan, v2, v3); zbuf_add_to_span(zspan, v3, v4); zbuf_add_to_span(zspan, v4, v1); /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; // printf("my %d %d\n", my0, my2); if (my2rectx; rectzofs= (float *)(zspan->rectz + rectx*my2); rectpofs= ((DrawBufPixel *)zspan->rectp) + rectx*my2; /* correct span */ sn1= (my0 + my2)/2; if (zspan->span1[sn1] < zspan->span2[sn1]) { span1= zspan->span1+my2; span2= zspan->span2+my2; } else { span1= zspan->span2+my2; span2= zspan->span1+my2; } for (y=my2; y>=my0; y--, span1--, span2--) { sn1= floor(*span1); sn2= floor(*span2); sn1++; if (sn2>=rectx) sn2= rectx-1; if (sn1<0) sn1= 0; if (sn2>=sn1) { zverg= (double)sn1*zxd + zy0; rz= rectzofs+sn1; rp= rectpofs+sn1; x= sn2-sn1; while (x>=0) { if (zverg < (double)*rz) { *rz= zverg; *rp= *col; } zverg+= zxd; rz++; rp++; x--; } } zy0-=zyd; rectzofs-= rectx; rectpofs-= rectx; } } /* char value==255 is filled in, rest should be zero */ /* returns alpha values, but sets alpha to 1 for zero alpha pixels that have an alpha value as neighbor */ void antialias_tagbuf(int xsize, int ysize, char *rectmove) { char *row1, *row2, *row3; char prev, next; int a, x, y, step; /* 1: tag pixels to be candidate for AA */ for (y=2; y>8; } } } } } /* 3: evaluate vertical scanlines and calculate alphas */ /* use for reading a copy of the original tagged buffer */ for (x=0; x>8; } } } } } /* last: pixels with 0 we fill in zbuffer, with 1 we skip for mask */ for (y=2; y1 || row2[2]>1 || row1[1]>1 || row3[1]>1) row2[1]= 1; } } } } /* in: two vectors, first vector points from origin back in time, 2nd vector points to future */ /* we make this into 3 points, center point is (0, 0) */ /* and offset the center point just enough to make curve go through midpoint */ static void quad_bezier_2d(float *result, float *v1, float *v2, float *ipodata) { float p1[2], p2[2], p3[2]; p3[0]= -v2[0]; p3[1]= -v2[1]; p1[0]= v1[0]; p1[1]= v1[1]; /* official formula 2*p2 - 0.5*p1 - 0.5*p3 */ p2[0]= -0.5f*p1[0] - 0.5f*p3[0]; p2[1]= -0.5f*p1[1] - 0.5f*p3[1]; result[0]= ipodata[0]*p1[0] + ipodata[1]*p2[0] + ipodata[2]*p3[0]; result[1]= ipodata[0]*p1[1] + ipodata[1]*p2[1] + ipodata[2]*p3[1]; } static void set_quad_bezier_ipo(float fac, float *data) { float mfac= (1.0f-fac); data[0]= mfac*mfac; data[1]= 2.0f*mfac*fac; data[2]= fac*fac; } void RE_zbuf_accumulate_vecblur(NodeBlurData *nbd, int xsize, int ysize, float *newrect, float *imgrect, float *vecbufrect, float *zbufrect) { ZSpan zspan; DrawBufPixel *rectdraw, *dr; static float jit[256][2]; float v1[3], v2[3], v3[3], v4[3], fx, fy; float *rectvz, *dvz, *dimg, *dvec1, *dvec2, *dz, *dz1, *dz2, *rectz; float *minvecbufrect= NULL, *rectweight, *rw, *rectmax, *rm, *ro; float maxspeedsq= (float)nbd->maxspeed*nbd->maxspeed; int y, x, step, maxspeed=nbd->maxspeed, samples= nbd->samples; int tsktsk= 0; static int firsttime= 1; char *rectmove, *dm; zbuf_alloc_span(&zspan, xsize, ysize, 1.0f); zspan.zmulx= ((float)xsize)/2.0f; zspan.zmuly= ((float)ysize)/2.0f; zspan.zofsx= 0.0f; zspan.zofsy= 0.0f; /* the buffers */ rectz= MEM_mapallocN(sizeof(float)*xsize*ysize, "zbuf accum"); zspan.rectz= (int *)rectz; rectmove= MEM_mapallocN(xsize*ysize, "rectmove"); rectdraw= MEM_mapallocN(sizeof(DrawBufPixel)*xsize*ysize, "rect draw"); zspan.rectp= (int *)rectdraw; rectweight= MEM_mapallocN(sizeof(float)*xsize*ysize, "rect weight"); rectmax= MEM_mapallocN(sizeof(float)*xsize*ysize, "rect max"); /* debug... check if PASS_VECTOR_MAX still is in buffers */ dvec1= vecbufrect; for (x= 4*xsize*ysize; x>0; x--, dvec1++) { if (dvec1[0]==PASS_VECTOR_MAX) { dvec1[0]= 0.0f; tsktsk= 1; } } if (tsktsk) printf("Found uninitialized speed in vector buffer... fixed.\n"); /* min speed? then copy speedbuffer to recalculate speed vectors */ if (nbd->minspeed) { float minspeed= (float)nbd->minspeed; float minspeedsq= minspeed*minspeed; minvecbufrect= MEM_mapallocN(4*sizeof(float)*xsize*ysize, "minspeed buf"); dvec1= vecbufrect; dvec2= minvecbufrect; for (x= 2*xsize*ysize; x>0; x--, dvec1+=2, dvec2+=2) { if (dvec1[0]==0.0f && dvec1[1]==0.0f) { dvec2[0]= dvec1[0]; dvec2[1]= dvec1[1]; } else { float speedsq= dvec1[0]*dvec1[0] + dvec1[1]*dvec1[1]; if (speedsq <= minspeedsq) { dvec2[0]= 0.0f; dvec2[1]= 0.0f; } else { speedsq = 1.0f - minspeed / sqrtf(speedsq); dvec2[0]= speedsq*dvec1[0]; dvec2[1]= speedsq*dvec1[1]; } } } SWAP(float *, minvecbufrect, vecbufrect); } /* make vertex buffer with averaged speed and zvalues */ rectvz= MEM_mapallocN(4*sizeof(float)*(xsize+1)*(ysize+1), "vertices"); dvz= rectvz; for (y=0; y<=ysize; y++) { if (y==0) dvec1= vecbufrect + 4*y*xsize; else dvec1= vecbufrect + 4*(y-1)*xsize; if (y==ysize) dvec2= vecbufrect + 4*(y-1)*xsize; else dvec2= vecbufrect + 4*y*xsize; for (x=0; x<=xsize; x++) { /* two vectors, so a step loop */ for (step=0; step<2; step++, dvec1+=2, dvec2+=2, dvz+=2) { /* average on minimal speed */ int div= 0; if (x!=0) { if (dvec1[-4]!=0.0f || dvec1[-3]!=0.0f) { dvz[0]= dvec1[-4]; dvz[1]= dvec1[-3]; div++; } if (dvec2[-4]!=0.0f || dvec2[-3]!=0.0f) { if (div==0) { dvz[0]= dvec2[-4]; dvz[1]= dvec2[-3]; div++; } else if ( (ABS(dvec2[-4]) + ABS(dvec2[-3]))< (ABS(dvz[0]) + ABS(dvz[1])) ) { dvz[0]= dvec2[-4]; dvz[1]= dvec2[-3]; } } } if (x!=xsize) { if (dvec1[0]!=0.0f || dvec1[1]!=0.0f) { if (div==0) { dvz[0]= dvec1[0]; dvz[1]= dvec1[1]; div++; } else if ( (ABS(dvec1[0]) + ABS(dvec1[1]))< (ABS(dvz[0]) + ABS(dvz[1])) ) { dvz[0]= dvec1[0]; dvz[1]= dvec1[1]; } } if (dvec2[0]!=0.0f || dvec2[1]!=0.0f) { if (div==0) { dvz[0]= dvec2[0]; dvz[1]= dvec2[1]; } else if ( (ABS(dvec2[0]) + ABS(dvec2[1]))< (ABS(dvz[0]) + ABS(dvz[1])) ) { dvz[0]= dvec2[0]; dvz[1]= dvec2[1]; } } } if (maxspeed) { float speedsq= dvz[0]*dvz[0] + dvz[1]*dvz[1]; if (speedsq > maxspeedsq) { speedsq = (float)maxspeed / sqrtf(speedsq); dvz[0]*= speedsq; dvz[1]*= speedsq; } } } } } /* set border speeds to keep border speeds on border */ dz1= rectvz; dz2= rectvz+4*(ysize)*(xsize+1); for (x=0; x<=xsize; x++, dz1+=4, dz2+=4) { dz1[1]= 0.0f; dz2[1]= 0.0f; dz1[3]= 0.0f; dz2[3]= 0.0f; } dz1= rectvz; dz2= rectvz+4*(xsize); for (y=0; y<=ysize; y++, dz1+=4*(xsize+1), dz2+=4*(xsize+1)) { dz1[0]= 0.0f; dz2[0]= 0.0f; dz1[2]= 0.0f; dz2[2]= 0.0f; } /* tag moving pixels, only these faces we draw */ dm= rectmove; dvec1= vecbufrect; for (x=xsize*ysize; x>0; x--, dm++, dvec1+=4) { if ((dvec1[0]!=0.0f || dvec1[1]!=0.0f || dvec1[2]!=0.0f || dvec1[3]!=0.0f)) *dm= 255; } antialias_tagbuf(xsize, ysize, rectmove); /* has to become static, the init-jit calls a random-seed, screwing up texture noise node */ if (firsttime) { firsttime= 0; BLI_jitter_init(jit, 256); } memset(newrect, 0, sizeof(float)*xsize*ysize*4); /* accumulate */ samples/= 2; for (step= 1; step<=samples; step++) { float speedfac= 0.5f*nbd->fac*(float)step/(float)(samples+1); int side; for (side=0; side<2; side++) { float blendfac, ipodata[4]; /* clear zbuf, if we draw future we fill in not moving pixels */ if (0) for (x= xsize*ysize-1; x>=0; x--) rectz[x]= 10e16; else for (x= xsize*ysize-1; x>=0; x--) { if (rectmove[x]==0) rectz[x]= zbufrect[x]; else rectz[x]= 10e16; } /* clear drawing buffer */ for (x= xsize*ysize-1; x>=0; x--) rectdraw[x].colpoin= NULL; dimg= imgrect; dm= rectmove; dz= zbufrect; dz1= rectvz; dz2= rectvz + 4*(xsize + 1); if (side) { if (nbd->curved==0) { dz1+= 2; dz2+= 2; } speedfac= -speedfac; } set_quad_bezier_ipo(0.5f + 0.5f*speedfac, ipodata); for (fy= -0.5f+jit[step & 255][0], y=0; y1) { float jfx = fx + 0.5f; float jfy = fy + 0.5f; DrawBufPixel col; /* make vertices */ if (nbd->curved) { /* curved */ quad_bezier_2d(v1, dz1, dz1+2, ipodata); v1[0]+= jfx; v1[1]+= jfy; v1[2]= *dz; quad_bezier_2d(v2, dz1+4, dz1+4+2, ipodata); v2[0]+= jfx+1.0f; v2[1]+= jfy; v2[2]= *dz; quad_bezier_2d(v3, dz2+4, dz2+4+2, ipodata); v3[0]+= jfx+1.0f; v3[1]+= jfy+1.0f; v3[2]= *dz; quad_bezier_2d(v4, dz2, dz2+2, ipodata); v4[0]+= jfx; v4[1]+= jfy+1.0f; v4[2]= *dz; } else { v1[0]= speedfac*dz1[0]+jfx; v1[1]= speedfac*dz1[1]+jfy; v1[2]= *dz; v2[0]= speedfac*dz1[4]+jfx+1.0f; v2[1]= speedfac*dz1[5]+jfy; v2[2]= *dz; v3[0]= speedfac*dz2[4]+jfx+1.0f; v3[1]= speedfac*dz2[5]+jfy+1.0f; v3[2]= *dz; v4[0]= speedfac*dz2[0]+jfx; v4[1]= speedfac*dz2[1]+jfy+1.0f; v4[2]= *dz; } if (*dm==255) col.alpha= 1.0f; else if (*dm<2) col.alpha= 0.0f; else col.alpha= ((float)*dm)/255.0f; col.colpoin= dimg; zbuf_fill_in_rgba(&zspan, &col, v1, v2, v3, v4); } } dz1+=4; dz2+=4; } /* blend with a falloff. this fixes the ugly effect you get with * a fast moving object. then it looks like a solid object overlayed * over a very transparent moving version of itself. in reality, the * whole object should become transparent if it is moving fast, be * we don't know what is behind it so we don't do that. this hack * overestimates the contribution of foreground pixels but looks a * bit better without a sudden cutoff. */ blendfac= ((samples - step)/(float)samples); /* smoothstep to make it look a bit nicer as well */ blendfac= 3.0f*pow(blendfac, 2.0f) - 2.0f*pow(blendfac, 3.0f); /* accum */ rw= rectweight; rm= rectmax; for (dr= rectdraw, dz2=newrect, x= xsize*ysize-1; x>=0; x--, dr++, dz2+=4, rw++, rm++) { if (dr->colpoin) { float bfac= dr->alpha*blendfac; dz2[0] += bfac*dr->colpoin[0]; dz2[1] += bfac*dr->colpoin[1]; dz2[2] += bfac*dr->colpoin[2]; dz2[3] += bfac*dr->colpoin[3]; *rw += bfac; *rm= MAX2(*rm, bfac); } } } } /* blend between original images and accumulated image */ rw= rectweight; rm= rectmax; ro= imgrect; dm= rectmove; for (dz2=newrect, x= xsize*ysize-1; x>=0; x--, dz2+=4, ro+=4, rw++, rm++, dm++) { float mfac = *rm; float fac = (*rw == 0.0f)? 0.0f: mfac/(*rw); float nfac = 1.0f - mfac; dz2[0]= fac*dz2[0] + nfac*ro[0]; dz2[1]= fac*dz2[1] + nfac*ro[1]; dz2[2]= fac*dz2[2] + nfac*ro[2]; dz2[3]= fac*dz2[3] + nfac*ro[3]; } MEM_freeN(rectz); MEM_freeN(rectmove); MEM_freeN(rectdraw); MEM_freeN(rectvz); MEM_freeN(rectweight); MEM_freeN(rectmax); if (minvecbufrect) MEM_freeN(vecbufrect); /* rects were swapped! */ zbuf_free_span(&zspan); } /* ******************** ABUF ************************* */ /** * Copy results from the solid face z buffering to the transparent * buffer. */ static void copyto_abufz(RenderPart *pa, int *arectz, int *rectmask, int sample) { PixStr *ps; int x, y, *rza, *rma; intptr_t *rd; if (R.osa==0) { if (!pa->rectz) fillrect(arectz, pa->rectx, pa->recty, 0x7FFFFFFE); else memcpy(arectz, pa->rectz, sizeof(int)*pa->rectx*pa->recty); if (rectmask && pa->rectmask) memcpy(rectmask, pa->rectmask, sizeof(int)*pa->rectx*pa->recty); return; } else if (!pa->rectdaps) { fillrect(arectz, pa->rectx, pa->recty, 0x7FFFFFFE); return; } rza= arectz; rma= rectmask; rd= pa->rectdaps; sample= (1<recty; y++) { for (x=0; xrectx; x++) { *rza= 0x7FFFFFFF; if (rectmask) *rma= 0x7FFFFFFF; if (*rd) { /* when there's a sky pixstruct, fill in sky-Z, otherwise solid Z */ for (ps= (PixStr *)(*rd); ps; ps= ps->next) { if (sample & ps->mask) { *rza= ps->z; if (rectmask) *rma= ps->maskz; break; } } } rd++; rza++, rma++; } } } /* ------------------------------------------------------------------------ */ /** * Do accumulation z buffering. */ static int zbuffer_abuf(Render *re, RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, unsigned int lay, int negzmask, float winmat[4][4], int winx, int winy, int samples, float (*jit)[2], float UNUSED(clipcrop), int shadow) { ZbufProjectCache cache[ZBUF_PROJECT_CACHE_SIZE]; ZSpan zspans[16], *zspan; /* MAX_OSA */ Material *ma=NULL; ObjectInstanceRen *obi; ObjectRen *obr; VlakRen *vlr=NULL; VertRen *v1, *v2, *v3, *v4; float vec[3], hoco[4], mul, zval, fval; float obwinmat[4][4], bounds[4], ho1[4], ho2[4], ho3[4], ho4[4]={0}; int i, v, zvlnr, c1, c2, c3, c4=0, dofill= 0; int zsample, polygon_offset; zbuffer_part_bounds(winx, winy, pa, bounds); for (zsample=0; zsamplerectx, pa->recty, re->clipcrop); /* needed for transform from hoco to zbuffer co */ zspan->zmulx= ((float)winx)/2.0f; zspan->zmuly= ((float)winy)/2.0f; /* the buffers */ zspan->arectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "Arectz"); zspan->apixbuf= APixbuf; zspan->apsmbase= apsmbase; if (negzmask) zspan->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "Arectmask"); /* filling methods */ zspan->zbuffunc= zbuffillAc4; zspan->zbuflinefunc= zbuflineAc; copyto_abufz(pa, zspan->arectz, zspan->rectmask, zsample); /* init zbuffer */ zspan->mask= 1<zofsx= -pa->disprect.xmin - jit[zsample][0]; zspan->zofsy= -pa->disprect.ymin - jit[zsample][1]; } else { zspan->zofsx= -pa->disprect.xmin; zspan->zofsy= -pa->disprect.ymin; } /* to center the sample position */ zspan->zofsx -= 0.5f; zspan->zofsy -= 0.5f; } /* we use this to test if nothing was filled in */ zvlnr= 0; for (i=0, obi=re->instancetable.first; obi; i++, obi=obi->next) { obr= obi->obr; if (!(obi->lay & lay)) continue; if (obi->flag & R_TRANSFORMED) mul_m4_m4m4(obwinmat, winmat, obi->mat); else copy_m4_m4(obwinmat, winmat); if (clip_render_object(obi->obr->boundbox, bounds, obwinmat)) continue; zbuf_project_cache_clear(cache, obr->totvert); for (v=0; vtotvlak; v++) { if ((v & 255)==0) vlr= obr->vlaknodes[v>>8].vlak; else vlr++; if (vlr->mat!=ma) { ma= vlr->mat; if (shadow) dofill= (ma->mode2 & MA_CASTSHADOW) && (ma->mode & MA_SHADBUF); else dofill= (((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP)) && !(ma->mode & MA_ONLYCAST)); } if (dofill) { if (!(vlr->flag & R_HIDDEN) && (obi->lay & lay)) { unsigned short partclip; v1= vlr->v1; v2= vlr->v2; v3= vlr->v3; v4= vlr->v4; c1= zbuf_part_project(cache, v1->index, obwinmat, bounds, v1->co, ho1); c2= zbuf_part_project(cache, v2->index, obwinmat, bounds, v2->co, ho2); c3= zbuf_part_project(cache, v3->index, obwinmat, bounds, v3->co, ho3); /* partclipping doesn't need viewplane clipping */ partclip= c1 & c2 & c3; if (v4) { c4= zbuf_part_project(cache, v4->index, obwinmat, bounds, v4->co, ho4); partclip &= c4; } if (partclip==0) { /* a little advantage for transp rendering (a z offset) */ if (!shadow && ma->zoffs != 0.0f) { mul= 0x7FFFFFFF; zval= mul*(1.0f+ho1[2]/ho1[3]); copy_v3_v3(vec, v1->co); /* z is negative, otherwise its being clipped */ vec[2]-= ma->zoffs; projectverto(vec, obwinmat, hoco); fval= mul*(1.0f+hoco[2]/hoco[3]); polygon_offset= (int)fabsf(zval - fval); } else polygon_offset= 0; zvlnr= v+1; c1= testclip(ho1); c2= testclip(ho2); c3= testclip(ho3); if (v4) c4= testclip(ho4); for (zsample=0; zsamplepolygon_offset= polygon_offset; if (ma->material_type == MA_TYPE_WIRE) { if (v4) zbufclipwire(zspan, i, zvlnr, vlr->ec, ho1, ho2, ho3, ho4, c1, c2, c3, c4); else zbufclipwire(zspan, i, zvlnr, vlr->ec, ho1, ho2, ho3, NULL, c1, c2, c3, 0); } else { if (v4 && (vlr->flag & R_STRAND)) { zbufclip4(zspan, i, zvlnr, ho1, ho2, ho3, ho4, c1, c2, c3, c4); } else { zbufclip(zspan, i, zvlnr, ho1, ho2, ho3, c1, c2, c3); if (v4) zbufclip(zspan, i, zvlnr+RE_QUAD_OFFS, ho1, ho3, ho4, c1, c3, c4); } } } } if ((v & 255)==255) if (re->test_break(re->tbh)) break; } } } if (re->test_break(re->tbh)) break; } for (zsample=0; zsamplearectz); if (zspan->rectmask) MEM_freeN(zspan->rectmask); zbuf_free_span(zspan); } return zvlnr; } static int zbuffer_abuf_render(RenderPart *pa, APixstr *APixbuf, APixstrand *APixbufstrand, ListBase *apsmbase, RenderLayer *rl, StrandShadeCache *sscache) { float winmat[4][4], (*jit)[2]; int samples, negzmask, doztra= 0; samples= (R.osa)? R.osa: 1; negzmask= ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)); if (R.osa) jit= R.jit; else if (R.i.curblur) jit= &R.mblur_jit[R.i.curblur-1]; else jit= NULL; zbuf_make_winmat(&R, winmat); if (rl->layflag & SCE_LAY_ZTRA) doztra+= zbuffer_abuf(&R, pa, APixbuf, apsmbase, rl->lay, negzmask, winmat, R.winx, R.winy, samples, jit, R.clipcrop, 0); if ((rl->layflag & SCE_LAY_STRAND) && APixbufstrand) doztra+= zbuffer_strands_abuf(&R, pa, APixbufstrand, apsmbase, rl->lay, negzmask, winmat, R.winx, R.winy, samples, jit, R.clipcrop, 0, sscache); return doztra; } void zbuffer_abuf_shadow(Render *re, LampRen *lar, float winmat[4][4], APixstr *APixbuf, APixstrand *APixbufstrand, ListBase *apsmbase, int size, int samples, float (*jit)[2]) { RenderPart pa; int lay= -1; if (lar->mode & LA_LAYER) lay= lar->lay; memset(&pa, 0, sizeof(RenderPart)); pa.rectx= size; pa.recty= size; pa.disprect.xmin = 0; pa.disprect.ymin = 0; pa.disprect.xmax = size; pa.disprect.ymax = size; zbuffer_abuf(re, &pa, APixbuf, apsmbase, lay, 0, winmat, size, size, samples, jit, 1.0f, 1); if (APixbufstrand) zbuffer_strands_abuf(re, &pa, APixbufstrand, apsmbase, lay, 0, winmat, size, size, samples, jit, 1.0f, 1, NULL); } /* different rules for speed in transparent pass... */ /* speed pointer NULL = sky, we clear */ /* else if either alpha is full or no solid was filled in: copy speed */ /* else fill in minimum speed */ static void add_transp_speed(RenderLayer *rl, int offset, float speed[4], float alpha, intptr_t *rdrect) { RenderPass *rpass; for (rpass= rl->passes.first; rpass; rpass= rpass->next) { if (rpass->passtype==SCE_PASS_VECTOR) { float *fp= rpass->rect + 4*offset; if (speed==NULL) { /* clear */ if (fp[0]==PASS_VECTOR_MAX) fp[0]= 0.0f; if (fp[1]==PASS_VECTOR_MAX) fp[1]= 0.0f; if (fp[2]==PASS_VECTOR_MAX) fp[2]= 0.0f; if (fp[3]==PASS_VECTOR_MAX) fp[3]= 0.0f; } else if (rdrect==NULL || rdrect[offset]==0 || alpha>0.95f) { copy_v4_v4(fp, speed); } else { /* add minimum speed in pixel */ if ( (ABS(speed[0]) + ABS(speed[1]))< (ABS(fp[0]) + ABS(fp[1])) ) { fp[0]= speed[0]; fp[1]= speed[1]; } if ( (ABS(speed[2]) + ABS(speed[3]))< (ABS(fp[2]) + ABS(fp[3])) ) { fp[2]= speed[2]; fp[3]= speed[3]; } } break; } } } static void add_transp_obindex(RenderLayer *rl, int offset, Object *ob) { RenderPass *rpass; for (rpass= rl->passes.first; rpass; rpass= rpass->next) { if (rpass->passtype == SCE_PASS_INDEXOB) { float *fp= rpass->rect + offset; *fp= (float)ob->index; break; } } } static void add_transp_material_index(RenderLayer *rl, int offset, Material *mat) { RenderPass *rpass; for (rpass= rl->passes.first; rpass; rpass= rpass->next) { if (rpass->passtype == SCE_PASS_INDEXMA) { float *fp= rpass->rect + offset; *fp= (float)mat->index; break; } } } /* ONLY OSA! merge all shaderesult samples to one */ /* target should have been cleared */ static void merge_transp_passes(RenderLayer *rl, ShadeResult *shr) { RenderPass *rpass; float weight= 1.0f/((float)R.osa); int delta= sizeof(ShadeResult)/4; for (rpass= rl->passes.first; rpass; rpass= rpass->next) { float *col= NULL; int pixsize= 3; switch (rpass->passtype) { 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_MIST: col= &shr->mist; pixsize= 1; break; case SCE_PASS_Z: col= &shr->z; pixsize= 1; break; case SCE_PASS_VECTOR: { ShadeResult *shr_t= shr+1; float *fp= shr->winspeed; /* was initialized */ int samp; /* add minimum speed in pixel */ for (samp= 1; sampcombined[3] > 0.0f) { const float *speed= shr_t->winspeed; if ( (ABS(speed[0]) + ABS(speed[1]))< (ABS(fp[0]) + ABS(fp[1])) ) { fp[0]= speed[0]; fp[1]= speed[1]; } if ( (ABS(speed[2]) + ABS(speed[3]))< (ABS(fp[2]) + ABS(fp[3])) ) { fp[2]= speed[2]; fp[3]= speed[3]; } } } } break; } if (col) { const float *fp= col+delta; int samp; for (samp= 1; samp1) { col[1]+= fp[1]; col[2]+= fp[2]; if (pixsize==4) col[3]+= fp[3]; } } col[0]*= weight; if (pixsize>1) { col[1]*= weight; col[2]*= weight; if (pixsize==4) col[3]*= weight; } } } } static void add_transp_passes(RenderLayer *rl, int offset, ShadeResult *shr, float alpha) { RenderPass *rpass; 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; if (shr->z < *fp) *fp= shr->z; break; case SCE_PASS_RGBA: fp= rpass->rect + 4*offset; addAlphaOverFloat(fp, shr->col); 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_MIST: col= &shr->mist; pixsize= 1; break; } if (col) { fp= rpass->rect + pixsize*offset; fp[0]= col[0] + (1.0f-alpha)*fp[0]; if (pixsize==3) { fp[1]= col[1] + (1.0f-alpha)*fp[1]; fp[2]= col[2] + (1.0f-alpha)*fp[2]; } } } } typedef struct ZTranspRow { int obi; int z; int p; int mask; int segment; float u, v; } ZTranspRow; static int vergzvlak(const void *a1, const void *a2) { const ZTranspRow *r1 = a1, *r2 = a2; if (r1->z < r2->z) return 1; else if (r1->z > r2->z) return -1; return 0; } static void shade_strand_samples(StrandShadeCache *cache, ShadeSample *ssamp, int UNUSED(x), int UNUSED(y), ZTranspRow *row, int addpassflag) { StrandSegment sseg; StrandVert *svert; ObjectInstanceRen *obi; ObjectRen *obr; obi= R.objectinstance + row->obi; obr= obi->obr; sseg.obi= obi; sseg.strand= RE_findOrAddStrand(obr, row->p-1); sseg.buffer= sseg.strand->buffer; svert= sseg.strand->vert + row->segment; sseg.v[0]= (row->segment > 0)? (svert-1): svert; sseg.v[1]= svert; sseg.v[2]= svert+1; sseg.v[3]= (row->segment < sseg.strand->totvert-2)? svert+2: svert+1; ssamp->tot= 1; strand_shade_segment(&R, cache, &sseg, ssamp, row->v, row->u, addpassflag); ssamp->shi[0].mask= row->mask; } static void unref_strand_samples(StrandShadeCache *cache, ZTranspRow *row, int totface) { StrandVert *svert; ObjectInstanceRen *obi; ObjectRen *obr; StrandRen *strand; /* remove references to samples that are not being rendered, but we still * need to remove them so that the reference count of strand vertex shade * samples correctly drops to zero */ while (totface > 0) { totface--; if (row[totface].segment != -1) { obi= R.objectinstance + row[totface].obi; obr= obi->obr; strand= RE_findOrAddStrand(obr, row[totface].p-1); svert= strand->vert + row[totface].segment; strand_shade_unref(cache, obi, svert); strand_shade_unref(cache, obi, svert+1); } } } static void shade_tra_samples_fill(ShadeSample *ssamp, int x, int y, int z, int obi, int facenr, int curmask) { ShadeInput *shi= ssamp->shi; float xs, ys; ssamp->tot= 0; shade_input_set_triangle(shi, obi, facenr, 1); /* officially should always be true... we have no sky info */ if (shi->vlr) { /* full osa is only set for OSA renders */ if (shi->vlr->flag & R_FULL_OSA) { short shi_inc= 0, samp; for (samp=0; sampmask= (1<samplenr= R.shadowsamplenr[shi->thread]++; shade_input_set_viewco(shi, x, y, xs, ys, (float)z); shade_input_set_uv(shi); if (shi_inc==0) shade_input_set_normals(shi); else /* XXX shi->flippednor messes up otherwise */ shade_input_set_vertex_normals(shi); shi_inc= 1; } } } else { if (R.osa) { short b= R.samples->centmask[curmask]; xs= (float)x + R.samples->centLut[b & 15] + 0.5f; ys= (float)y + R.samples->centLut[b>>4] + 0.5f; } else if (R.i.curblur) { xs= (float)x + R.mblur_jit[R.i.curblur-1][0] + 0.5f; ys= (float)y + R.mblur_jit[R.i.curblur-1][1] + 0.5f; } else { xs= (float)x + 0.5f; ys= (float)y + 0.5f; } shi->mask= curmask; shi->samplenr= R.shadowsamplenr[shi->thread]++; shade_input_set_viewco(shi, x, y, xs, ys, (float)z); shade_input_set_uv(shi); shade_input_set_normals(shi); } /* total sample amount, shi->sample is static set in initialize */ ssamp->tot= shi->sample+1; } } static int shade_tra_samples(ShadeSample *ssamp, StrandShadeCache *cache, int x, int y, ZTranspRow *row, int addpassflag) { if (row->segment != -1) { shade_strand_samples(cache, ssamp, x, y, row, addpassflag); return 1; } shade_tra_samples_fill(ssamp, x, y, row->z, row->obi, row->p, row->mask); if (ssamp->tot) { ShadeInput *shi= ssamp->shi; ShadeResult *shr= ssamp->shr; int samp; /* if AO? */ shade_samples_do_AO(ssamp); /* if shade (all shadepinputs have same passflag) */ if (shi->passflag & ~(SCE_PASS_Z|SCE_PASS_INDEXOB|SCE_PASS_INDEXMA)) { for (samp=0; samptot; samp++, shi++, shr++) { shade_input_set_shade_texco(shi); shade_input_do_shade(shi, shr); /* include lamphalos for ztra, since halo layer was added already */ if (R.flag & R_LAMPHALO) if (shi->layflag & SCE_LAY_HALO) renderspothalo(shi, shr->combined, shr->combined[3]); } } else if (shi->passflag & SCE_PASS_Z) { for (samp=0; samptot; samp++, shi++, shr++) shr->z= -shi->co[2]; } return 1; } return 0; } static int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassflag) { int a, sample, osa = (R.osa? R.osa: 1), retval = osa; for (a=0; a < osa; a++, samp_shr++) { ShadeInput *shi= ssamp->shi; ShadeResult *shr= ssamp->shr; for (sample=0; sampletot; sample++, shi++, shr++) { if (shi->mask & (1<combined[3])*shr->combined[3]; addAlphaUnderFloat(samp_shr->combined, shr->combined); samp_shr->z = min_ff(samp_shr->z, shr->z); if (addpassflag & SCE_PASS_VECTOR) { copy_v4_v4(samp_shr->winspeed, shr->winspeed); } /* optim... */ if (addpassflag & ~(SCE_PASS_VECTOR)) { if (addpassflag & SCE_PASS_RGBA) addAlphaUnderFloat(samp_shr->col, shr->col); if (addpassflag & SCE_PASS_NORMAL) madd_v3_v3fl(samp_shr->nor, shr->nor, fac); if (addpassflag & SCE_PASS_EMIT) madd_v3_v3fl(samp_shr->emit, shr->emit, fac); if (addpassflag & SCE_PASS_DIFFUSE) madd_v3_v3fl(samp_shr->diff, shr->diff, fac); if (addpassflag & SCE_PASS_SPEC) madd_v3_v3fl(samp_shr->spec, shr->spec, fac); if (addpassflag & SCE_PASS_SHADOW) madd_v3_v3fl(samp_shr->shad, shr->shad, fac); if (addpassflag & SCE_PASS_AO) madd_v3_v3fl(samp_shr->ao, shr->ao, fac); if (addpassflag & SCE_PASS_ENVIRONMENT) madd_v3_v3fl(samp_shr->env, shr->env, fac); if (addpassflag & SCE_PASS_INDIRECT) madd_v3_v3fl(samp_shr->indirect, shr->indirect, fac); if (addpassflag & SCE_PASS_REFLECT) madd_v3_v3fl(samp_shr->refl, shr->refl, fac); if (addpassflag & SCE_PASS_REFRACT) madd_v3_v3fl(samp_shr->refr, shr->refr, fac); if (addpassflag & SCE_PASS_MIST) samp_shr->mist= samp_shr->mist+fac*shr->mist; } } } if (samp_shr->combined[3]>0.999f) retval--; } return retval; } static void reset_sky_speedvectors(RenderPart *pa, RenderLayer *rl, float *rectf) { /* speed vector exception... if solid render was done, sky pixels are set to zero already */ /* for all pixels with alpha zero, we re-initialize speed again then */ float *fp, *col; int a; fp= RE_RenderLayerGetPass(rl, SCE_PASS_VECTOR); if (fp==NULL) return; col= rectf+3; for (a= 4*pa->rectx*pa->recty -4; a>=0; a-=4) { if (col[a]==0.0f) { fp[a]= PASS_VECTOR_MAX; fp[a+1]= PASS_VECTOR_MAX; fp[a+2]= PASS_VECTOR_MAX; fp[a+3]= PASS_VECTOR_MAX; } } } #define MAX_ZROW 2000 /* main render call to do the z-transparent layer */ /* returns a mask, only if a) transp rendered and b) solid was rendered */ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pass, ListBase *UNUSED(psmlist)) { RenderResult *rr= pa->result; ShadeSample ssamp; APixstr *APixbuf; /* Zbuffer: linked list of face samples */ APixstrand *APixbufstrand = NULL; APixstr *ap, *aprect, *apn; APixstrand *apstrand, *aprectstrand, *apnstrand; ListBase apsmbase={NULL, NULL}; ShadeResult samp_shr[16]; /* MAX_OSA */ ZTranspRow zrow[MAX_ZROW]; StrandShadeCache *sscache= NULL; RenderLayer *rlpp[RE_MAX_OSA]; float sampalpha, alpha, *passrect= pass; intptr_t *rdrect; int x, y, crop=0, a, b, totface, totfullsample, totsample, doztra; int addpassflag, offs= 0, od, osa = (R.osa? R.osa: 1); unsigned short *ztramask= NULL, filled; /* looks nicer for calling code */ if (R.test_break(R.tbh)) return NULL; if (R.osa > 16) { /* MAX_OSA */ printf("zbuffer_transp_shade: osa too large\n"); G.is_break = true; return NULL; } APixbuf= MEM_callocN(pa->rectx*pa->recty*sizeof(APixstr), "APixbuf"); if (R.totstrand && (rl->layflag & SCE_LAY_STRAND)) { APixbufstrand= MEM_callocN(pa->rectx*pa->recty*sizeof(APixstrand), "APixbufstrand"); sscache= strand_shade_cache_create(); } /* general shader info, passes */ shade_sample_initialize(&ssamp, pa, rl); addpassflag= rl->passflag & ~(SCE_PASS_COMBINED); if (R.osa) sampalpha= 1.0f/(float)R.osa; else sampalpha= 1.0f; /* fill the Apixbuf */ doztra= zbuffer_abuf_render(pa, APixbuf, APixbufstrand, &apsmbase, rl, sscache); if (doztra == 0) { /* nothing filled in */ MEM_freeN(APixbuf); if (APixbufstrand) MEM_freeN(APixbufstrand); if (sscache) strand_shade_cache_free(sscache); freepsA(&apsmbase); return NULL; } aprect= APixbuf; aprectstrand= APixbufstrand; rdrect= pa->rectdaps; /* needed for correct zbuf/index pass */ totfullsample= get_sample_layers(pa, rl, rlpp); /* irregular shadowb buffer creation */ if (R.r.mode & R_SHADOW) ISB_create(pa, APixbuf); /* masks, to have correct alpha combine */ if (R.osa && (rl->layflag & SCE_LAY_SOLID) && pa->fullresult.first==NULL) ztramask= MEM_callocN(pa->rectx*pa->recty*sizeof(short), "ztramask"); /* zero alpha pixels get speed vector max again */ if (addpassflag & SCE_PASS_VECTOR) if (rl->layflag & SCE_LAY_SOLID) reset_sky_speedvectors(pa, rl, rl->acolrect?rl->acolrect:rl->rectf); /* if acolrect is set we use it */ /* filtered render, for now we assume only 1 filter size */ if (pa->crop) { crop= 1; offs= pa->rectx + 1; passrect+= 4*offs; aprect+= offs; aprectstrand+= offs; } /* init scanline updates */ rr->renrect.ymin = 0; rr->renrect.ymax = -pa->crop; rr->renlay= rl; /* render the tile */ for (y=pa->disprect.ymin+crop; ydisprect.ymax-crop; y++, rr->renrect.ymax++) { pass= passrect; ap= aprect; apstrand= aprectstrand; od= offs; if (R.test_break(R.tbh)) break; for (x=pa->disprect.xmin+crop; xdisprect.xmax-crop; x++, ap++, apstrand++, pass+=4, od++) { if (ap->p[0]==0 && (!APixbufstrand || apstrand->p[0]==0)) { if (addpassflag & SCE_PASS_VECTOR) add_transp_speed(rl, od, NULL, 0.0f, rdrect); } else { /* sort in z */ totface= 0; apn= ap; while (apn) { for (a=0; a<4; a++) { if (apn->p[a]) { zrow[totface].obi= apn->obi[a]; zrow[totface].z= apn->z[a]; zrow[totface].p= apn->p[a]; zrow[totface].mask= apn->mask[a]; zrow[totface].segment= -1; totface++; if (totface>=MAX_ZROW) totface= MAX_ZROW-1; } else break; } apn= apn->next; } apnstrand= (APixbufstrand)? apstrand: NULL; while (apnstrand) { for (a=0; a<4; a++) { if (apnstrand->p[a]) { zrow[totface].obi= apnstrand->obi[a]; zrow[totface].z= apnstrand->z[a]; zrow[totface].p= apnstrand->p[a]; zrow[totface].mask= apnstrand->mask[a]; zrow[totface].segment= apnstrand->seg[a]; if (R.osa) { totsample= 0; for (b=0; bu[a]/totsample; zrow[totface].v= apnstrand->v[a]/totsample; totface++; if (totface>=MAX_ZROW) totface= MAX_ZROW-1; } } apnstrand= apnstrand->next; } if (totface==2) { if (zrow[0].z < zrow[1].z) { SWAP(ZTranspRow, zrow[0], zrow[1]); } } else if (totface>2) { qsort(zrow, totface, sizeof(ZTranspRow), vergzvlak); } /* front face does index pass for transparent, no AA or filters, but yes FSA */ if (addpassflag & SCE_PASS_INDEXOB) { ObjectRen *obr= R.objectinstance[zrow[totface-1].obi].obr; if (obr->ob) { for (a= 0; aob); } } if (addpassflag & SCE_PASS_INDEXMA) { ObjectRen *obr = R.objectinstance[zrow[totface-1].obi].obr; int p = zrow[totface-1].p; Material *mat = NULL; if (zrow[totface-1].segment == -1) { int facenr = (p - 1) & RE_QUAD_MASK; VlakRen *vlr = NULL; if (facenr >= 0 && facenr < obr->totvlak) vlr = RE_findOrAddVlak(obr, facenr); if (vlr) mat = vlr->mat; } else { StrandRen *strand = RE_findOrAddStrand(obr, p - 1); if (strand) mat = strand->buffer->ma; } if (mat) { for (a= 0; a0) { totface--; if (shade_tra_samples(&ssamp, sscache, x, y, &zrow[totface], addpassflag)) { filled= addtosamp_shr(samp_shr, &ssamp, addpassflag); addAlphaUnderFloat(pass, ssamp.shr[0].combined); if (filled == 0) { if (sscache) unref_strand_samples(sscache, zrow, totface); break; } } } alpha= samp_shr->combined[3]; if (alpha!=0.0f) { add_transp_passes(rl, od, samp_shr, alpha); if (addpassflag & SCE_PASS_VECTOR) add_transp_speed(rl, od, samp_shr->winspeed, alpha, rdrect); } } else { short *sp= (short *)(ztramask+od); while (totface>0) { totface--; if (shade_tra_samples(&ssamp, sscache, x, y, &zrow[totface], addpassflag)) { filled= addtosamp_shr(samp_shr, &ssamp, addpassflag); if (ztramask) *sp |= zrow[totface].mask; if (filled==0) { if (sscache) unref_strand_samples(sscache, zrow, totface); break; } } } /* multisample buffers or filtered mask filling? */ if (pa->fullresult.first) { for (a=0; arectf + 4*od, samp_shr[a].combined); add_transp_passes(rl, od, &samp_shr[a], alpha); if (addpassflag & SCE_PASS_VECTOR) add_transp_speed(rl, od, samp_shr[a].winspeed, alpha, rdrect); } } } else { alpha= 0.0f; /* note; cannot use pass[3] for alpha due to filtermask */ for (a=0; arectx); alpha+= samp_shr[a].combined[3]; } if (addpassflag) { alpha*= sampalpha; /* merge all in one, and then add */ merge_transp_passes(rl, samp_shr); add_transp_passes(rl, od, samp_shr, alpha); if (addpassflag & SCE_PASS_VECTOR) add_transp_speed(rl, od, samp_shr[0].winspeed, alpha, rdrect); } } } } } aprect+= pa->rectx; aprectstrand+= pa->rectx; passrect+= 4*pa->rectx; offs+= pa->rectx; } /* disable scanline updating */ rr->renlay= NULL; MEM_freeN(APixbuf); if (APixbufstrand) MEM_freeN(APixbufstrand); if (sscache) strand_shade_cache_free(sscache); freepsA(&apsmbase); if (R.r.mode & R_SHADOW) ISB_free(pa); return ztramask; } /* end of zbuf.c */