diff options
Diffstat (limited to 'source/blender/render/intern/source')
20 files changed, 7125 insertions, 2512 deletions
diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index d8ad518da14..a9bc2131d77 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -54,6 +54,7 @@ #include "DNA_meshdata_types.h" #include "DNA_meta_types.h" #include "DNA_modifier_types.h" +#include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" #include "DNA_object_fluidsim.h" @@ -99,6 +100,7 @@ #include "envmap.h" //XXX #include "multires.h" +#include "occlusion.h" #include "render_types.h" #include "rendercore.h" #include "renderdatabase.h" @@ -106,9 +108,12 @@ #include "radio.h" #include "shadbuf.h" #include "shading.h" +#include "strand.h" #include "texture.h" #include "sss.h" +#include "strand.h" #include "zbuf.h" +#include "sunsky.h" #ifndef DISABLE_YAFRAY /* disable yafray */ @@ -198,6 +203,7 @@ void RE_make_stars(Render *re, void (*initfunc)(void), else stargrid *= 1.0; /* then it draws fewer */ if(re) MTC_Mat4Invert(mat, re->viewmat); + else MTC_Mat4One(mat); /* BOUNDING BOX CALCULATION * bbox goes from z = loc_near_var | loc_far_var, @@ -228,7 +234,7 @@ void RE_make_stars(Render *re, void (*initfunc)(void), } if(re) /* add render object for stars */ - obr= RE_addRenderObject(re, NULL, NULL, 0, 0); + obr= RE_addRenderObject(re, NULL, NULL, 0, 0, 0); for (x = sx, fx = sx * stargrid; x <= ex; x++, fx += stargrid) { for (y = sy, fy = sy * stargrid; y <= ey ; y++, fy += stargrid) { @@ -447,34 +453,8 @@ static void calc_edge_stress(Render *re, ObjectRen *obr, Mesh *me) MEM_freeN(accum); } -void tangent_from_uv(float *uv1, float *uv2, float *uv3, float *co1, float *co2, float *co3, float *n, float *tang) -{ - float tangv[3], ct[3], e1[3], e2[3], s1, t1, s2, t2, det; - - s1= uv2[0] - uv1[0]; - s2= uv3[0] - uv1[0]; - t1= uv2[1] - uv1[1]; - t2= uv3[1] - uv1[1]; - det= 1.0f / (s1 * t2 - s2 * t1); - - /* normals in render are inversed... */ - VecSubf(e1, co1, co2); - VecSubf(e2, co1, co3); - tang[0] = (t2*e1[0] - t1*e2[0])*det; - tang[1] = (t2*e1[1] - t1*e2[1])*det; - tang[2] = (t2*e1[2] - t1*e2[2])*det; - tangv[0] = (s1*e2[0] - s2*e1[0])*det; - tangv[1] = (s1*e2[1] - s2*e1[1])*det; - tangv[2] = (s1*e2[2] - s2*e1[2])*det; - Crossf(ct, tang, tangv); - - /* check flip */ - if ((ct[0]*n[0] + ct[1]*n[1] + ct[2]*n[2]) < 0.0f) - VecMulf(tang, -1.0f); -} - /* gets tangent from tface or orco */ -static void calc_tangent_vector(ObjectRen *obr, VlakRen *vlr) +static void calc_tangent_vector(ObjectRen *obr, VertexTangent **vtangents, MemArena *arena, VlakRen *vlr, int do_nmap_tangent, int do_tangent) { MTFace *tface= RE_vlakren_get_tface(obr, vlr, obr->actmtface, NULL, 0); VertRen *v1=vlr->v1, *v2=vlr->v2, *v3=vlr->v3, *v4=vlr->v4; @@ -500,30 +480,55 @@ static void calc_tangent_vector(ObjectRen *obr, VlakRen *vlr) tangent_from_uv(uv1, uv2, uv3, v1->co, v2->co, v3->co, vlr->n, tang); - tav= RE_vertren_get_tangent(obr, v1, 1); - VECADD(tav, tav, tang); - tav= RE_vertren_get_tangent(obr, v2, 1); - VECADD(tav, tav, tang); - tav= RE_vertren_get_tangent(obr, v3, 1); - VECADD(tav, tav, tang); - - if(v4) { - tangent_from_uv(uv1, uv3, uv4, v1->co, v3->co, v4->co, vlr->n, tang); - + if(do_tangent) { tav= RE_vertren_get_tangent(obr, v1, 1); VECADD(tav, tav, tang); - tav= RE_vertren_get_tangent(obr, v3, 1); + tav= RE_vertren_get_tangent(obr, v2, 1); VECADD(tav, tav, tang); - tav= RE_vertren_get_tangent(obr, v4, 1); + tav= RE_vertren_get_tangent(obr, v3, 1); VECADD(tav, tav, tang); } + + if(do_nmap_tangent) { + sum_or_add_vertex_tangent(arena, &vtangents[v1->index], tang, uv1); + sum_or_add_vertex_tangent(arena, &vtangents[v2->index], tang, uv2); + sum_or_add_vertex_tangent(arena, &vtangents[v3->index], tang, uv3); + } + + if(v4) { + tangent_from_uv(uv1, uv3, uv4, v1->co, v3->co, v4->co, vlr->n, tang); + + if(do_tangent) { + tav= RE_vertren_get_tangent(obr, v1, 1); + VECADD(tav, tav, tang); + tav= RE_vertren_get_tangent(obr, v3, 1); + VECADD(tav, tav, tang); + tav= RE_vertren_get_tangent(obr, v4, 1); + VECADD(tav, tav, tang); + } + + if(do_nmap_tangent) { + sum_or_add_vertex_tangent(arena, &vtangents[v1->index], tang, uv1); + sum_or_add_vertex_tangent(arena, &vtangents[v3->index], tang, uv3); + sum_or_add_vertex_tangent(arena, &vtangents[v4->index], tang, uv4); + } + } } -static void calc_vertexnormals(Render *re, ObjectRen *obr, int do_tangent) +static void calc_vertexnormals(Render *re, ObjectRen *obr, int do_tangent, int do_nmap_tangent) { + MemArena *arena= NULL; + VertexTangent **vtangents= NULL; int a; + if(do_nmap_tangent) { + arena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); + BLI_memarena_use_calloc(arena); + + vtangents= MEM_callocN(sizeof(VertexTangent*)*obr->totvert, "VertexTangent"); + } + /* clear all vertex normals */ for(a=0; a<obr->totvert; a++) { VertRen *ver= RE_findOrAddVert(obr, a); @@ -596,10 +601,10 @@ static void calc_vertexnormals(Render *re, ObjectRen *obr, int do_tangent) v3->n[2] +=fac3*vlr->n[2]; } - if(do_tangent) { + if(do_nmap_tangent || do_tangent) { /* tangents still need to be calculated for flat faces too */ /* weighting removed, they are not vertexnormals */ - calc_tangent_vector(obr, vlr); + calc_tangent_vector(obr, vtangents, arena, vlr, do_nmap_tangent, do_tangent); } } @@ -618,6 +623,30 @@ static void calc_vertexnormals(Render *re, ObjectRen *obr, int do_tangent) if(f1[0]==0.0 && f1[1]==0.0 && f1[2]==0.0) VECCOPY(f1, vlr->n); } } + + if(do_nmap_tangent) { + VertRen *v1=vlr->v1, *v2=vlr->v2, *v3=vlr->v3, *v4=vlr->v4; + MTFace *tface= RE_vlakren_get_tface(obr, vlr, obr->actmtface, NULL, 0); + + if(tface) { + float *vtang, *ftang= RE_vlakren_get_nmap_tangent(obr, vlr, 1); + + vtang= find_vertex_tangent(vtangents[v1->index], tface->uv[0]); + VECCOPY(ftang, vtang); + Normalize(ftang); + vtang= find_vertex_tangent(vtangents[v2->index], tface->uv[1]); + VECCOPY(ftang+3, vtang); + Normalize(ftang+3); + vtang= find_vertex_tangent(vtangents[v3->index], tface->uv[2]); + VECCOPY(ftang+6, vtang); + Normalize(ftang+6); + if(v4) { + vtang= find_vertex_tangent(vtangents[v4->index], tface->uv[3]); + VECCOPY(ftang+9, vtang); + Normalize(ftang+9); + } + } + } } /* normalize vertex normals */ @@ -636,94 +665,12 @@ static void calc_vertexnormals(Render *re, ObjectRen *obr, int do_tangent) } } } -} -// NT same as calc_vertexnormals, but dont modify the existing vertex normals -// only recalculate other render data. If this is at some point used for other things than fluidsim, -// this could be made on option for the normal calc_vertexnormals -static void calc_fluidsimnormals(Render *re, ObjectRen *obr, int do_tangent) -{ - int a; - - /* dont clear vertex normals here */ - // OFF for(a=0; a<obr->totvert; a++) { VertRen *ver= RE_findOrAddVert(obr, a); ver->n[0]=ver->n[1]=ver->n[2]= 0.0; } - /* calculate cos of angles and point-masses, use as weight factor to add face normal to vertex */ - for(a=0; a<obr->totvlak; a++) { - VlakRen *vlr= RE_findOrAddVlak(obr, a); - if(vlr->flag & ME_SMOOTH) { - VertRen *v1= vlr->v1; - VertRen *v2= vlr->v2; - VertRen *v3= vlr->v3; - VertRen *v4= vlr->v4; - float n1[3], n2[3], n3[3], n4[3]; - float fac1, fac2, fac3, fac4=0.0f; - - if(re->flag & R_GLOB_NOPUNOFLIP) - vlr->flag |= R_NOPUNOFLIP; - - VecSubf(n1, v2->co, v1->co); - Normalize(n1); - VecSubf(n2, v3->co, v2->co); - Normalize(n2); - if(v4==NULL) { - VecSubf(n3, v1->co, v3->co); - Normalize(n3); - fac1= saacos(-n1[0]*n3[0]-n1[1]*n3[1]-n1[2]*n3[2]); - fac2= saacos(-n1[0]*n2[0]-n1[1]*n2[1]-n1[2]*n2[2]); - fac3= saacos(-n2[0]*n3[0]-n2[1]*n3[1]-n2[2]*n3[2]); - } - else { - VecSubf(n3, v4->co, v3->co); - Normalize(n3); - VecSubf(n4, v1->co, v4->co); - Normalize(n4); - fac1= saacos(-n4[0]*n1[0]-n4[1]*n1[1]-n4[2]*n1[2]); - fac2= saacos(-n1[0]*n2[0]-n1[1]*n2[1]-n1[2]*n2[2]); - fac3= saacos(-n2[0]*n3[0]-n2[1]*n3[1]-n2[2]*n3[2]); - fac4= saacos(-n3[0]*n4[0]-n3[1]*n4[1]-n3[2]*n4[2]); - - if(!(vlr->flag & R_NOPUNOFLIP)) { - if( check_vnormal(vlr->n, v4->n) ) fac4= -fac4; - } - } - - //if(do_tangent) - // calc_tangent_vector(obr, vlr, fac1, fac2, fac3, fac4); - } - if(do_tangent) { - /* tangents still need to be calculated for flat faces too */ - /* weighting removed, they are not vertexnormals */ - calc_tangent_vector(obr, vlr); - } - } - - /* do solid faces */ - for(a=0; a<obr->totvlak; a++) { - VlakRen *vlr= RE_findOrAddVlak(obr, a); - if((vlr->flag & ME_SMOOTH)==0) { - float *f1= vlr->v1->n; - if(f1[0]==0.0 && f1[1]==0.0 && f1[2]==0.0) VECCOPY(f1, vlr->n); - f1= vlr->v2->n; - if(f1[0]==0.0 && f1[1]==0.0 && f1[2]==0.0) VECCOPY(f1, vlr->n); - f1= vlr->v3->n; - if(f1[0]==0.0 && f1[1]==0.0 && f1[2]==0.0) VECCOPY(f1, vlr->n); - if(vlr->v4) { - f1= vlr->v4->n; - if(f1[0]==0.0 && f1[1]==0.0 && f1[2]==0.0) VECCOPY(f1, vlr->n); - } - } - } - - /* normalize vertex normals */ - for(a=0; a<obr->totvert; a++) { - VertRen *ver= RE_findOrAddVert(obr, a); - Normalize(ver->n); - if(do_tangent) { - float *tav= RE_vertren_get_tangent(obr, ver, 0); - if(tav) Normalize(tav); - } - } + if(arena) + BLI_memarena_free(arena); + if(vtangents) + MEM_freeN(vtangents); } /* ------------------------------------------------------------------------- */ @@ -926,6 +873,8 @@ static float *get_object_orco(Render *re, Object *ob) orco = make_orco_curve(ob); } else if (ob->type==OB_SURF) { orco = make_orco_surf(ob); + } else if (ob->type==OB_MBALL) { + orco = make_orco_mball(ob); } if (orco) @@ -951,6 +900,26 @@ static void free_mesh_orco_hash(Render *re) } } +static void flag_render_node_material(Render *re, bNodeTree *ntree) +{ + bNode *node; + + for(node=ntree->nodes.first; node; node= node->next) { + if(node->id) { + if(GS(node->id->name)==ID_MA) { + Material *ma= (Material *)node->id; + + if(ma->mode & MA_ZTRA) + re->flag |= R_ZTRA; + + ma->flag |= MA_IS_USED; + } + else if(node->type==NODE_GROUP) + flag_render_node_material(re, (bNodeTree *)node->id); + } + } +} + static Material *give_render_material(Render *re, Object *ob, int nr) { extern Material defmaterial; /* material.c */ @@ -959,14 +928,17 @@ static Material *give_render_material(Render *re, Object *ob, int nr) ma= give_current_material(ob, nr); if(ma==NULL) ma= &defmaterial; - else - if(ma->mode & MA_ZTRA) - re->flag |= R_ZTRA; if(re->r.mode & R_SPEED) ma->texco |= NEED_UV; + if(ma->mode & MA_ZTRA) + re->flag |= R_ZTRA; + /* for light groups */ ma->flag |= MA_IS_USED; + + if(ma->nodetree && ma->use_nodes) + flag_render_node_material(re, ma->nodetree); return ma; } @@ -977,7 +949,7 @@ static Material *give_render_material(Render *re, Object *ob, int nr) /* future thread problem... */ static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, float *orco, float *surfnor, - float *uvco, int totuv, float *vec, float *vec1, float ctime, + float *uvco, int totuv, MCol *mcol, int totcol, float *vec, float *vec1, float ctime, int first, int line, int adapt, float adapt_angle, float adapt_pix, int override_uv) { static VertRen *v1= NULL, *v2= NULL; @@ -1040,7 +1012,6 @@ static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, flo if(line) { vlr= RE_findOrAddVlak(obr, obr->totvlak++); vlr->flag= flag; - vlr->obr= obr; vlr->v1= RE_findOrAddVert(obr, obr->totvert++); vlr->v2= RE_findOrAddVert(obr, obr->totvert++); vlr->v3= RE_findOrAddVert(obr, obr->totvert++); @@ -1074,7 +1045,6 @@ static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, flo vlr->mat= ma; vlr->ec= ME_V2V3; - vlr->lay= obr->ob->lay; if(surfnor) { float *snor= RE_vlakren_get_surfnor(obr, vlr, 1); @@ -1101,6 +1071,14 @@ static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, flo mtf->uv[2][1]=mtf->uv[3][1]=1.0f; } } + if(mcol){ + for(i=0; i<totcol; i++){ + MCol *mc; + mc=RE_vlakren_get_mcol(obr,vlr,i,NULL,1); + mc[0]=mc[1]=mc[2]=mc[3]=mcol[i]; + mc[0]=mc[1]=mc[2]=mc[3]=mcol[i]; + } + } } /* first two vertices of a strand */ else if(first) { @@ -1130,7 +1108,6 @@ static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, flo if(adapt==0 || second){ vlr= RE_findOrAddVlak(obr, obr->totvlak++); vlr->flag= flag; - vlr->obr= obr; vlr->v1= v1; vlr->v2= v2; vlr->v3= RE_findOrAddVert(obr, obr->totvert++); @@ -1160,7 +1137,6 @@ static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, flo if(Inpf(anor,nor)<adapt_angle && w>adapt_pix){ vlr= RE_findOrAddVlak(obr, obr->totvlak++); vlr->flag= flag; - vlr->obr= obr; vlr->v1= v1; vlr->v2= v2; vlr->v3= RE_findOrAddVert(obr, obr->totvert++); @@ -1193,7 +1169,6 @@ static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, flo vlr->mat= ma; vlr->ec= ME_V2V3; - vlr->lay= obr->ob->lay; if(surfnor) { float *snor= RE_vlakren_get_surfnor(obr, vlr, 1); @@ -1220,6 +1195,14 @@ static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, flo mtf->uv[2][1]=mtf->uv[3][1]=(vlr->v3->accum+1.0f)/2.0f; } } + if(mcol){ + for(i=0; i<totcol; i++){ + MCol *mc; + mc=RE_vlakren_get_mcol(obr,vlr,i,NULL,1); + mc[0]=mc[1]=mc[2]=mc[3]=mcol[i]; + mc[0]=mc[1]=mc[2]=mc[3]=mcol[i]; + } + } } } @@ -1230,7 +1213,6 @@ static void static_particle_wire(ObjectRen *obr, Material *ma, float *vec, float if(line) { vlr= RE_findOrAddVlak(obr, obr->totvlak++); - vlr->obr= obr; vlr->v1= RE_findOrAddVert(obr, obr->totvert++); vlr->v2= RE_findOrAddVert(obr, obr->totvert++); vlr->v3= vlr->v2; @@ -1246,7 +1228,6 @@ static void static_particle_wire(ObjectRen *obr, Material *ma, float *vec, float vlr->mat= ma; vlr->ec= ME_V1V2; - vlr->lay= obr->ob->lay; } else if(first) { @@ -1255,7 +1236,6 @@ static void static_particle_wire(ObjectRen *obr, Material *ma, float *vec, float } else { vlr= RE_findOrAddVlak(obr, obr->totvlak++); - vlr->obr= obr; vlr->v1= v1; vlr->v2= RE_findOrAddVert(obr, obr->totvert++); vlr->v3= vlr->v2; @@ -1270,7 +1250,6 @@ static void static_particle_wire(ObjectRen *obr, Material *ma, float *vec, float vlr->mat= ma; vlr->ec= ME_V1V2; - vlr->lay= obr->ob->lay; } } @@ -1287,7 +1266,6 @@ static void particle_billboard(Render *re, ObjectRen *obr, Material *ma, Object onevec[align]=1.0f; vlr= RE_findOrAddVlak(obr, obr->totvlak++); - vlr->obr= obr; vlr->v1= RE_findOrAddVert(obr, obr->totvert++); vlr->v2= RE_findOrAddVert(obr, obr->totvert++); vlr->v3= RE_findOrAddVert(obr, obr->totvert++); @@ -1370,7 +1348,6 @@ static void particle_billboard(Render *re, ObjectRen *obr, Material *ma, Object vlr->mat= ma; vlr->ec= ME_V2V3; - vlr->lay= obr->ob->lay; if(uv_split>1){ uvdx=uvdy=1.0f/(float)uv_split; @@ -1456,7 +1433,8 @@ static void particle_billboard(Render *re, ObjectRen *obr, Material *ma, Object } static void render_new_particle(Render *re, ObjectRen *obr, DerivedMesh *dm, Material *ma, int path, int first, int line, float time, float *loc, float *loc1, float *orco, float *surfnor, int totuv, float *uvco, - float size, int seed, int override_uv, int adapt, float adapt_angle, float adapt_pix) + int totcol, MCol *mcol, float size, int seed, int override_uv, + int adapt, float adapt_angle, float adapt_pix) { HaloRen *har=0; if(path){ @@ -1467,19 +1445,18 @@ static void render_new_particle(Render *re, ObjectRen *obr, DerivedMesh *dm, Mat if(har) har->lay= obr->ob->lay; } else - static_particle_strand(re, obr, ma, orco, surfnor, uvco, totuv, loc, loc1, time, first, line, adapt, adapt_angle, adapt_pix, override_uv); + static_particle_strand(re, obr, ma, orco, surfnor, uvco, totuv, mcol, totcol, loc, loc1, time, first, line, adapt, adapt_angle, adapt_pix, override_uv); } else{ har= RE_inithalo_particle(re, obr, dm, ma, loc, NULL, orco, uvco, size, 0.0, seed); if(har) har->lay= obr->ob->lay; } } -static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem *psys) +static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem *psys, int timeoffset) { Object *ob= obr->ob; Object *tob=0, *bb_ob=re->scene->camera; Material *ma=0; - CustomDataLayer *layer; MTFace *mtface; ParticleSystemModifierData *psmd; ParticleSystem *tpsys=0; @@ -1490,21 +1467,25 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem ParticleCacheKey *cache=0; StrandBuffer *strandbuf=0; StrandVert *svert=0; + StrandBound *sbound= 0; StrandRen *strand=0; RNG *rng= 0; + MCol *mcol= 0; float loc[3],loc1[3],loc0[3],vel[3],mat[4][4],nmat[3][3],co[3],nor[3],time; float *orco=0,*surfnor=0,*uvco=0, strandlen=0.0f, curlen=0.0f; float hasize, pa_size, pa_time, r_tilt, cfra=bsystem_time(ob,(float)CFRA,0.0); - float loc_tex[3], size_tex[3], adapt_angle=0.0, adapt_pix=0.0, random; - float simplify[2]; - int i, a, k, max_k=0, totpart, totuv=0, override_uv=-1, dosimplify = 0; - int path_possible=0, keys_possible=0, baked_keys=0, totchild=psys->totchild; - int seed, path_nbr=0, path=0, orco1=0, adapt=0, uv[3]={0,0,0}; + float adapt_angle=0.0, adapt_pix=0.0, random, simplify[2]; + int i, a, k, max_k=0, totpart, totuv=0, totcol=0, override_uv=-1, dosimplify = 0, dosurfacecache = 0; + int path_possible=0, keys_possible=0, baked_keys=0, totchild=0; + int seed, path_nbr=0, path=0, orco1=0, adapt=0, uv[3]={0,0,0}, num; + int totface, *origindex = 0; char **uv_name=0; /* 1. check that everything is ok & updated */ if(psys==NULL) return 0; + + totchild=psys->totchild; part=psys->part; pars=psys->particles; @@ -1547,6 +1528,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem RE_set_customdata_names(obr, &psmd->dm->faceData); totuv=CustomData_number_of_layers(&psmd->dm->faceData,CD_MTFACE); + totcol=CustomData_number_of_layers(&psmd->dm->faceData,CD_MCOL); if(ma->texco & TEXCO_UV && totuv) { uvco = MEM_callocN(totuv*2*sizeof(float),"particle_uvs"); @@ -1557,6 +1539,9 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem } } + if(totcol) + mcol = MEM_callocN(totcol*sizeof(MCol),"particle_mcols"); + if(part->draw_as==PART_DRAW_BB){ int first_uv=CustomData_get_layer_index(&psmd->dm->faceData,CD_MTFACE); @@ -1607,8 +1592,6 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem totpart=psys->totpart; - mesh_get_texspace(ob->data, loc_tex, NULL, size_tex); - if(psys->pathcache){ path_possible=1; keys_possible=1; @@ -1645,12 +1628,37 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem strandbuf->overrideuv= override_uv; strandbuf->minwidth= ma->strand_min; + if(ma->strand_widthfade == 0.0f) + strandbuf->widthfade= 0.0f; + else if(ma->strand_widthfade >= 1.0f) + strandbuf->widthfade= 2.0f - ma->strand_widthfade; + else + strandbuf->widthfade= 1.0f/MAX2(ma->strand_widthfade, 1e-5f); + if(part->flag & PART_HAIR_BSPLINE) strandbuf->flag |= R_STRAND_BSPLINE; if(ma->mode & MA_STR_B_UNITS) strandbuf->flag |= R_STRAND_B_UNITS; svert= strandbuf->vert; + + if(re->r.mode & R_SPEED) + dosurfacecache= 1; + else if((re->wrld.mode & WO_AMB_OCC) && (re->wrld.ao_gather_method == WO_AOGATHER_APPROX)) + if(ma->amb != 0.0f) + dosurfacecache= 1; + + totface= psmd->dm->getNumFaces(psmd->dm); + origindex= psmd->dm->getFaceDataArray(psmd->dm, CD_ORIGINDEX); + if(origindex) { + for(a=0; a<totface; a++) + strandbuf->totbound= MAX2(strandbuf->totbound, origindex[a]); + strandbuf->totbound++; + } + strandbuf->totbound++; + strandbuf->bound= MEM_callocN(sizeof(StrandBound)*strandbuf->totbound, "StrandBound"); + sbound= strandbuf->bound; + sbound->start= sbound->end= 0; } } } @@ -1669,7 +1677,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem psys->lattice=psys_get_lattice(ob,psys); /* 3. start creating renderable things */ - for(a=0,pa=pars; a<totpart+totchild; a++, pa++) { + for(a=0,pa=pars; a<totpart+totchild; a++, pa++, seed++) { random = rng_getFloat(rng); if(a<totpart){ @@ -1694,20 +1702,43 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem /* get orco */ if(tpsys && (part->from==PART_FROM_PARTICLE || part->phystype==PART_PHYS_NO)){ tpa=tpsys->particles+pa->num; - psys_particle_on_emitter(ob, psmd,tpart->from,tpa->num, -1,tpa->fuv,tpa->foffset,co,nor,0,0,orco,0); + psys_particle_on_emitter(psmd,tpart->from,tpa->num,pa->num_dmcache,tpa->fuv,tpa->foffset,co,nor,0,0,orco,0); } else - psys_particle_on_emitter(ob, psmd,part->from,pa->num,-1,pa->fuv,pa->foffset,co,nor,0,0,orco,0); + psys_particle_on_emitter(psmd,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0,orco,0); + + num= pa->num_dmcache; + + if(num == DMCACHE_NOTFOUND) + if(pa->num < psmd->dm->getNumFaces(psmd->dm)) + num= pa->num; if(uvco && ELEM(part->from,PART_FROM_FACE,PART_FROM_VOLUME)){ - layer=psmd->dm->faceData.layers + CustomData_get_layer_index(&psmd->dm->faceData,CD_MFACE); for(i=0; i<totuv; i++){ - MFace *mface=psmd->dm->getFaceData(psmd->dm,pa->num,CD_MFACE); - - mtface=(MTFace*)CustomData_get_layer_n(&psmd->dm->faceData,CD_MTFACE,i); - mtface+=pa->num; - - psys_interpolate_uvs(mtface,mface->v4,pa->fuv,uvco+2*i); + if(num != DMCACHE_NOTFOUND) { + MFace *mface=psmd->dm->getFaceData(psmd->dm,num,CD_MFACE); + mtface=(MTFace*)CustomData_get_layer_n(&psmd->dm->faceData,CD_MTFACE,i); + mtface+=num; + + psys_interpolate_uvs(mtface,mface->v4,pa->fuv,uvco+2*i); + } + else { + uvco[2*i]= 0.0f; + uvco[2*i + 1]= 0.0f; + } + } + } + if(mcol && ELEM(part->from,PART_FROM_FACE,PART_FROM_VOLUME)){ + for(i=0; i<totcol; i++){ + if(num != DMCACHE_NOTFOUND) { + MFace *mface=psmd->dm->getFaceData(psmd->dm,num,CD_MFACE); + MCol *mc=(MCol*)CustomData_get_layer_n(&psmd->dm->faceData,CD_MCOL,i); + mc+=num*4; + + psys_interpolate_mcol(mc,mface->v4,pa->fuv,mcol+i); + } + else + memset(&mcol[i], 0, sizeof(MCol)); } } @@ -1744,14 +1775,22 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem r_tilt=2.0f*cpa->rand[2]; + num= cpa->num; + /* get orco */ - psys_particle_on_emitter(ob, psmd, - (part->childtype == PART_CHILD_FACES)? PART_FROM_FACE: PART_FROM_PARTICLE, - cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co,nor,0,0,orco,0); + if(part->childtype == PART_CHILD_FACES) { + psys_particle_on_emitter(psmd, + PART_FROM_FACE, cpa->num,DMCACHE_ISCHILD, + cpa->fuv,cpa->foffset,co,nor,0,0,orco,0); + } + else { + ParticleData *par = psys->particles + cpa->parent; + psys_particle_on_emitter(psmd, part->from, + par->num,DMCACHE_ISCHILD,par->fuv, + par->foffset,co,nor,0,0,orco,0); + } if(uvco){ - layer=psmd->dm->faceData.layers + CustomData_get_layer_index(&psmd->dm->faceData,CD_MFACE); - if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ for(i=0; i<totuv; i++){ if(part->childtype==PART_CHILD_FACES){ @@ -1768,30 +1807,77 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem } } else if(ELEM(part->from,PART_FROM_FACE,PART_FROM_VOLUME)){ - for(i=0; i<totuv; i++){ - ParticleData *parent = psys->particles+cpa->parent; - MFace *mface=psmd->dm->getFaceData(psmd->dm,parent->num,CD_MFACE); + ParticleData *parent = psys->particles + cpa->parent; + num= parent->num_dmcache; - mtface=(MTFace*)CustomData_get_layer_n(&psmd->dm->faceData,CD_MTFACE,i); - mtface+=parent->num; - - psys_interpolate_uvs(mtface,mface->v4,parent->fuv,uvco+2*i); + if(num == DMCACHE_NOTFOUND) + if(parent->num < psmd->dm->getNumFaces(psmd->dm)) + num= parent->num; + + for(i=0; i<totuv; i++) { + if(num != DMCACHE_NOTFOUND) { + MFace *mface=psmd->dm->getFaceData(psmd->dm,num,CD_MFACE); + mtface=(MTFace*)CustomData_get_layer_n(&psmd->dm->faceData,CD_MTFACE,i); + mtface+=num; + psys_interpolate_uvs(mtface,mface->v4,parent->fuv,uvco+2*i); + } + else { + uvco[2*i]= 0.0f; + uvco[2*i + 1]= 0.0f; + } + } + } + } + + if(mcol){ + if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ + for(i=0; i<totcol; i++){ + if(part->childtype==PART_CHILD_FACES){ + MFace *mface=psmd->dm->getFaceData(psmd->dm,cpa->num,CD_MFACE); + MCol *mc=(MCol*)CustomData_get_layer_n(&psmd->dm->faceData,CD_MCOL,i); + mc+=cpa->num*4; + + psys_interpolate_mcol(mc,mface->v4,cpa->fuv,mcol+i); + } + else + memset(&mcol[i], 0, sizeof(MCol)); + } + } + else if(ELEM(part->from,PART_FROM_FACE,PART_FROM_VOLUME)){ + ParticleData *parent = psys->particles + cpa->parent; + num= parent->num_dmcache; + + if(num == DMCACHE_NOTFOUND) + if(parent->num < psmd->dm->getNumFaces(psmd->dm)) + num= parent->num; + + for(i=0; i<totcol; i++){ + if(num != DMCACHE_NOTFOUND) { + MFace *mface=psmd->dm->getFaceData(psmd->dm,num,CD_MFACE); + MCol *mc=(MCol*)CustomData_get_layer_n(&psmd->dm->faceData,CD_MCOL,i); + mc+=num*4; + + psys_interpolate_mcol(mc,mface->v4,parent->fuv,mcol+i); + } + else + memset(&mcol[i], 0, sizeof(MCol)); } } } dosimplify= psys_render_simplify_params(psys, cpa, simplify); - if(path_nbr) { + if(path_nbr && psys->childcache) { cache = psys->childcache[a-totpart]; max_k = (int)cache->steps; } - } - if(orco) { - orco[0] = (orco[0]-loc_tex[0])/size_tex[0]; - orco[1] = (orco[1]-loc_tex[1])/size_tex[1]; - orco[2] = (orco[2]-loc_tex[2])/size_tex[2]; + if(strandbuf) { + if(origindex[cpa->num]+1 > sbound - strandbuf->bound) { + sbound= strandbuf->bound + origindex[cpa->num]+1; + sbound->start= sbound->end= obr->totstrand; + } + } } /* surface normal shading setup */ @@ -1820,8 +1906,13 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem VECCOPY(snor, surfnor); } - if(uvco){ - for(i=0; i<totuv; i++){ + if(dosurfacecache && num >= 0) { + int *facenum= RE_strandren_get_face(obr, strand, 1); + *facenum= num; + } + + if(uvco) { + for(i=0; i<totuv; i++) { if(i != override_uv) { float *uv= RE_strandren_get_uv(obr, strand, i, NULL, 1); @@ -1830,6 +1921,14 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem } } } + if(mcol) { + for(i=0; i<totcol; i++) { + MCol *mc= RE_strandren_get_mcol(obr, strand, i, NULL, 1); + *mc = mcol[i]; + } + } + + sbound->end++; } /* strandco computation setup */ @@ -1879,7 +1978,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem VECADDFAC(loc1,loc,vel,part->draw_line[1]); render_new_particle(re,obr,psmd->dm,ma,1,0,1,0.0f,loc0,loc1, - orco,surfnor,totuv,uvco,hasize,seed,override_uv,0,0,0); + orco,surfnor,totuv,uvco,totcol,mcol,hasize,seed,override_uv,0,0,0); } else if(part->draw_as==PART_DRAW_BB) { VECCOPY(vel,state.vel); @@ -1899,13 +1998,13 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem VECSUB(loc0,loc1,loc); VECADD(loc0,loc1,loc0); render_new_particle(re,obr,psmd->dm,ma,path,1,0,0.0f,loc1,loc0, - orco,surfnor,totuv,uvco,hasize,seed,override_uv, + orco,surfnor,totuv,uvco,totcol,mcol,hasize,seed,override_uv, adapt,adapt_angle,adapt_pix); } if(path_nbr==0 || k) render_new_particle(re,obr,psmd->dm,ma,path,0,0,time,loc,loc1, - orco,surfnor,totuv,uvco,hasize,seed,override_uv, + orco,surfnor,totuv,uvco,totcol,mcol,hasize,seed,override_uv, adapt,adapt_angle,adapt_pix); VECCOPY(loc1,loc); @@ -1919,6 +2018,9 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem break; } + if(dosurfacecache) + strandbuf->surface= cache_strand_surface(re, obr, psmd->dm, mat, timeoffset); + /* 4. clean up */ if(ma) do_mat_ipo(ma); @@ -1927,6 +2029,9 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem if(uvco) MEM_freeN(uvco); + + if(mcol) + MEM_freeN(mcol); if(uv_name) MEM_freeN(uv_name); @@ -1944,7 +2049,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem } if(path && (ma->mode_l & MA_TANGENT_STR)==0) - calc_vertexnormals(re, obr, 0); + calc_vertexnormals(re, obr, 0, 0); return 1; } @@ -2005,7 +2110,8 @@ static void make_render_halos(Render *re, ObjectRen *obr, Mesh *me, int totvert, static int verghalo(const void *a1, const void *a2) { - const HaloRen *har1= a1, *har2= a2; + const HaloRen *har1= *(const HaloRen**)a1; + const HaloRen *har2= *(const HaloRen**)a2; if(har1->zs < har2->zs) return 1; else if(har1->zs > har2->zs) return -1; @@ -2053,11 +2159,11 @@ static short test_for_displace(Render *re, Object *ob) return 0; } -static void displace_render_vert(Render *re, ObjectRen *obr, ShadeInput *shi, VertRen *vr, int vindex, float *scale) +static void displace_render_vert(Render *re, ObjectRen *obr, ShadeInput *shi, VertRen *vr, int vindex, float *scale, float mat[][4], float imat[][3]) { MTFace *tface; short texco= shi->mat->texco; - float sample=0; + float sample=0, displace[3]; char *name; int i; @@ -2066,6 +2172,15 @@ static void displace_render_vert(Render *re, ObjectRen *obr, ShadeInput *shi, Ve /* vertex normal is used for textures type 'col' and 'var' */ VECCOPY(shi->vn, vr->n); + if(mat) + Mat4MulVecfl(mat, shi->co); + + if(imat) { + shi->vn[0]= imat[0][0]*vr->n[0]+imat[0][1]*vr->n[1]+imat[0][2]*vr->n[2]; + shi->vn[1]= imat[1][0]*vr->n[0]+imat[1][1]*vr->n[1]+imat[1][2]*vr->n[2]; + shi->vn[2]= imat[2][0]*vr->n[0]+imat[2][1]*vr->n[1]+imat[2][2]*vr->n[2]; + } + if (texco & TEXCO_UV) { shi->totuv= 0; shi->actuv= obr->actmtface; @@ -2111,11 +2226,18 @@ static void displace_render_vert(Render *re, ObjectRen *obr, ShadeInput *shi, Ve //printf("no=%f, %f, %f\nbefore co=%f, %f, %f\n", vr->n[0], vr->n[1], vr->n[2], //vr->co[0], vr->co[1], vr->co[2]); + + displace[0]= shi->displace[0] * scale[0]; + displace[1]= shi->displace[1] * scale[1]; + displace[2]= shi->displace[2] * scale[2]; + if(mat) + Mat3MulVecfl(imat, displace); + /* 0.5 could become button once? */ - vr->co[0] += shi->displace[0] * scale[0] ; - vr->co[1] += shi->displace[1] * scale[1] ; - vr->co[2] += shi->displace[2] * scale[2] ; + vr->co[0] += displace[0]; + vr->co[1] += displace[1]; + vr->co[2] += displace[2]; //printf("after co=%f, %f, %f\n", vr->co[0], vr->co[1], vr->co[2]); @@ -2133,28 +2255,36 @@ static void displace_render_vert(Render *re, ObjectRen *obr, ShadeInput *shi, Ve return; } -static void displace_render_face(Render *re, VlakRen *vlr, float *scale) +static void displace_render_face(Render *re, ObjectRen *obr, VlakRen *vlr, float *scale, float mat[][4], float imat[][3]) { ShadeInput shi; + /* Warning, This is not that nice, and possibly a bit slow, + however some variables were not initialized properly in, unless using shade_input_initialize(...), we need to do a memset */ + memset(&shi, 0, sizeof(ShadeInput)); + /* end warning! - Campbell */ + /* set up shadeinput struct for multitex() */ - shi.osatex= 0; /* signal not to use dx[] and dy[] texture AA vectors */ + + /* memset above means we dont need this */ + /*shi.osatex= 0;*/ /* signal not to use dx[] and dy[] texture AA vectors */ + shi.vlr= vlr; /* current render face */ shi.mat= vlr->mat; /* current input material */ /* Displace the verts, flag is set when done */ if (!vlr->v1->flag) - displace_render_vert(re, vlr->obr, &shi, vlr->v1,0, scale); + displace_render_vert(re, obr, &shi, vlr->v1,0, scale, mat, imat); if (!vlr->v2->flag) - displace_render_vert(re, vlr->obr, &shi, vlr->v2, 1, scale); + displace_render_vert(re, obr, &shi, vlr->v2, 1, scale, mat, imat); if (!vlr->v3->flag) - displace_render_vert(re, vlr->obr, &shi, vlr->v3, 2, scale); + displace_render_vert(re, obr, &shi, vlr->v3, 2, scale, mat, imat); if (vlr->v4) { if (!vlr->v4->flag) - displace_render_vert(re, vlr->obr, &shi, vlr->v4, 3, scale); + displace_render_vert(re, obr, &shi, vlr->v4, 3, scale, mat, imat); /* closest in displace value. This will help smooth edges. */ if ( fabs(vlr->v1->accum - vlr->v3->accum) > fabs(vlr->v2->accum - vlr->v4->accum)) @@ -2171,7 +2301,7 @@ static void displace_render_face(Render *re, VlakRen *vlr, float *scale) } } -static void do_displacement(Render *re, ObjectRen *obr) +static void do_displacement(Render *re, ObjectRen *obr, float mat[][4], float imat[][3]) { VertRen *vr; VlakRen *vlr; @@ -2196,11 +2326,11 @@ static void do_displacement(Render *re, ObjectRen *obr) for(i=0; i<obr->totvlak; i++){ vlr=RE_findOrAddVlak(obr, i); - displace_render_face(re, vlr, scale); + displace_render_face(re, obr, vlr, scale, mat, imat); } /* Recalc vertex normals */ - calc_vertexnormals(re, obr, 0); + calc_vertexnormals(re, obr, 0, 0); } /* ------------------------------------------------------------------------- */ @@ -2214,8 +2344,8 @@ static void init_render_mball(Render *re, ObjectRen *obr) VertRen *ver; VlakRen *vlr, *vlr1; Material *ma; - float *data, *nors, mat[4][4], imat[3][3], xn, yn, zn; - int a, need_orco, *index; + float *data, *nors, *orco, mat[4][4], imat[3][3], xn, yn, zn; + int a, need_orco, vlakindex, *index; if (ob!=find_basis_mball(ob)) return; @@ -2237,8 +2367,9 @@ static void init_render_mball(Render *re, ObjectRen *obr) data= dl->verts; nors= dl->nors; + orco= get_object_orco(re, ob); - for(a=0; a<dl->nr; a++, data+=3, nors+=3) { + for(a=0; a<dl->nr; a++, data+=3, nors+=3, orco+=3) { ver= RE_findOrAddVert(obr, obr->totvert++); VECCOPY(ver->co, data); @@ -2256,14 +2387,13 @@ static void init_render_mball(Render *re, ObjectRen *obr) Normalize(ver->n); //if(ob->transflag & OB_NEG_SCALE) VecMulf(ver->n. -1.0); - if(need_orco) ver->orco= data; + if(need_orco) ver->orco= orco; } index= dl->index; for(a=0; a<dl->parts; a++, index+=4) { vlr= RE_findOrAddVlak(obr, obr->totvlak++); - vlr->obr= obr; vlr->v1= RE_findOrAddVert(obr, index[0]); vlr->v2= RE_findOrAddVert(obr, index[1]); vlr->v3= RE_findOrAddVert(obr, index[2]); @@ -2277,12 +2407,13 @@ static void init_render_mball(Render *re, ObjectRen *obr) vlr->mat= ma; vlr->flag= ME_SMOOTH+R_NOPUNOFLIP; vlr->ec= 0; - vlr->lay= ob->lay; /* mball -too bad- always has triangles, because quads can be non-planar */ if(index[3] && index[3]!=index[2]) { vlr1= RE_findOrAddVlak(obr, obr->totvlak++); + vlakindex= vlr1->index; *vlr1= *vlr; + vlr1->index= vlakindex; vlr1->v2= vlr1->v3; vlr1->v3= RE_findOrAddVert(obr, index[3]); if(ob->transflag & OB_NEG_SCALE) @@ -2292,14 +2423,8 @@ static void init_render_mball(Render *re, ObjectRen *obr) } } - if(need_orco) { - /* store displist and scale */ - make_orco_mball(ob); - } - else { - /* enforce display lists remade */ - freedisplist(&ob->disp); - } + /* enforce display lists remade */ + freedisplist(&ob->disp); /* this enforces remake for real, orco displist is small (in scale) */ ob->recalc |= OB_RECALC_DATA; @@ -2316,7 +2441,7 @@ static int dl_surf_to_renderdata(ObjectRen *obr, DispList *dl, Material **matar, VertRen *v1, *v2, *v3, *v4, *ver; VlakRen *vlr, *vlr1, *vlr2, *vlr3; Curve *cu= ob->data; - float *data, n1[3], flen; + float *data, n1[3]; int u, v, orcoret= 0; int p1, p2, p3, p4, a; int sizeu, nsizeu, sizev, nsizev; @@ -2386,13 +2511,12 @@ static int dl_surf_to_renderdata(ObjectRen *obr, DispList *dl, Material **matar, v4= RE_findOrAddVert(obr, p4); vlr= RE_findOrAddVlak(obr, obr->totvlak++); - vlr->obr= obr; vlr->v1= v1; vlr->v2= v2; vlr->v3= v3; vlr->v4= v4; - flen= CalcNormFloat4(vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co, n1); + CalcNormFloat4(vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co, n1); + VECCOPY(vlr->n, n1); - vlr->lay= ob->lay; vlr->mat= matar[ dl->col]; vlr->ec= ME_V1V2+ME_V2V3; vlr->flag= dl->rt; @@ -2524,7 +2648,7 @@ static void init_render_surf(Render *re, ObjectRen *obr) freedisplist(&displist); } -static void init_render_curve(Render *re, ObjectRen *obr, int only_verts) +static void init_render_curve(Render *re, ObjectRen *obr, int timeoffset) { Object *ob= obr->ob; Curve *cu; @@ -2539,7 +2663,8 @@ static void init_render_curve(Render *re, ObjectRen *obr, int only_verts) int frontside, need_orco=0; cu= ob->data; - if(cu->nurb.first==NULL) return; + if(ob->type==OB_FONT && cu->str==NULL) return; + else if(ob->type==OB_CURVE && cu->nurb.first==NULL) return; /* no modifier call here, is in makedisp */ @@ -2602,13 +2727,12 @@ static void init_render_curve(Render *re, ObjectRen *obr, int only_verts) } } - if(only_verts==0) { + if(timeoffset==0) { startvlak= obr->totvlak; index= dl->index; for(a=0; a<dl->parts; a++, index+=3) { vlr= RE_findOrAddVlak(obr, obr->totvlak++); - vlr->obr = obr; vlr->v1= RE_findOrAddVert(obr, startvert+index[0]); vlr->v2= RE_findOrAddVert(obr, startvert+index[1]); vlr->v3= RE_findOrAddVert(obr, startvert+index[2]); @@ -2627,7 +2751,6 @@ static void init_render_curve(Render *re, ObjectRen *obr, int only_verts) vlr->flag |= R_NOPUNOFLIP; } vlr->ec= 0; - vlr->lay= ob->lay; } } } @@ -2657,14 +2780,16 @@ static void init_render_curve(Render *re, ObjectRen *obr, int only_verts) } } - if(dl->bevelSplitFlag || only_verts==0) { + if(dl->bevelSplitFlag || timeoffset==0) { startvlak= obr->totvlak; for(a=0; a<dl->parts; a++) { frontside= (a >= dl->nr/2); - - DL_SURFINDEX(dl->flag & DL_CYCL_U, dl->flag & DL_CYCL_V, dl->nr, dl->parts); + + if (surfindex_displist(dl, a, &b, &p1, &p2, &p3, &p4)==0) + break; + p1+= startvert; p2+= startvert; p3+= startvert; @@ -2672,7 +2797,6 @@ static void init_render_curve(Render *re, ObjectRen *obr, int only_verts) for(; b<dl->nr; b++) { vlr= RE_findOrAddVlak(obr, obr->totvlak++); - vlr->obr= obr; vlr->v1= RE_findOrAddVert(obr, p2); vlr->v2= RE_findOrAddVert(obr, p1); vlr->v3= RE_findOrAddVert(obr, p3); @@ -2681,7 +2805,6 @@ static void init_render_curve(Render *re, ObjectRen *obr, int only_verts) if(a==0) vlr->ec+= ME_V1V2; vlr->flag= dl->rt; - vlr->lay= ob->lay; /* this is not really scientific: the vertices * 2, 3 en 4 seem to give better vertexnormals than 1 2 3: @@ -2875,7 +2998,7 @@ static void use_mesh_edge_lookup(ObjectRen *obr, DerivedMesh *dm, MEdge *medge, } } -static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) +static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) { Object *ob= obr->ob; Mesh *me; @@ -2889,9 +3012,9 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) CustomDataMask mask; float xn, yn, zn, imat[3][3], mat[4][4]; //nor[3], float *orco=0; - int a, a1, ok, need_orco=0, need_stress=0, need_tangent=0, vertofs; + int need_orco=0, need_stress=0, need_nmap_tangent=0, need_tangent=0; + int a, a1, ok, vertofs; int end, do_autosmooth=0, totvert = 0; - int useFluidmeshNormals= 0; // NT fluidsim, use smoothed normals? int use_original_normals= 0; me= ob->data; @@ -2912,11 +3035,19 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) if(ma->texco & TEXCO_STRESS) need_stress= 1; /* normalmaps, test if tangents needed, separated from shading */ - if ((ma->mode_l & MA_TANGENT_V) || (ma->mode_l & MA_NORMAP_TANG)) { + if(ma->mode_l & MA_TANGENT_V) { need_tangent= 1; if(me->mtface==NULL) need_orco= 1; } + if(ma->mode_l & MA_NORMAP_TANG) { + if(me->mtface==NULL) { + need_orco= 1; + need_tangent= 1; + } + need_nmap_tangent= 1; + } + /* radio faces need autosmooth, to separate shared vertices in corners */ if(re->r.mode & R_RADIO) if(ma->mode & MA_RADIO) @@ -2926,23 +3057,32 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) if(re->flag & R_NEED_TANGENT) { /* exception for tangent space baking */ - need_tangent= 1; - if(me->mtface==NULL) + if(me->mtface==NULL) { need_orco= 1; + need_tangent= 1; + } + need_nmap_tangent= 1; } /* check autosmooth and displacement, we then have to skip only-verts optimize */ do_autosmooth |= (me->flag & ME_AUTOSMOOTH); if(do_autosmooth) - only_verts= 0; + timeoffset= 0; if(test_for_displace(re, ob ) ) - only_verts= 0; + timeoffset= 0; mask= CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; - if(!only_verts) + if(!timeoffset) if(need_orco) mask |= CD_MASK_ORCO; + if(me->mr) { + if(re->flag & R_SKIP_MULTIRES) + me->mr->flag |= MULTIRES_NO_RENDER; + else + me->mr->flag &= ~MULTIRES_NO_RENDER; + } + dm= mesh_create_derived_render(ob, mask); if(dm==NULL) return; /* in case duplicated object fails? */ @@ -2954,12 +3094,6 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) } } - if((ob->fluidsimFlag & OB_FLUIDSIM_ENABLE) && - (ob->fluidsimSettings->type & OB_FLUIDSIM_DOMAIN)&& - (ob->fluidsimSettings->meshSurface) ) { - useFluidmeshNormals = 1; - } - mvert= dm->getVertArray(dm); totvert= dm->getNumVerts(dm); @@ -2982,17 +3116,6 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) if(do_autosmooth==0) /* autosmooth on original unrotated data to prevent differences between frames */ MTC_Mat4MulVecfl(mat, ver->co); - if(useFluidmeshNormals) { - /* normals are inverted in render */ - xn = -mvert->no[0]/ 32767.0; - yn = -mvert->no[1]/ 32767.0; - zn = -mvert->no[2]/ 32767.0; - /* transfor to cam space */ - ver->n[0]= imat[0][0]*xn+imat[0][1]*yn+imat[0][2]*zn; - ver->n[1]= imat[1][0]*xn+imat[1][1]*yn+imat[1][2]*zn; - ver->n[2]= imat[2][0]*xn+imat[2][1]*yn+imat[2][2]*zn; - } // useFluidmeshNormals - if(orco) { ver->orco= orco; orco+=3; @@ -3005,7 +3128,7 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) } } - if(!only_verts) { + if(!timeoffset) { /* store customdata names, because DerivedMesh is freed */ RE_set_customdata_names(obr, &dm->faceData); @@ -3052,7 +3175,6 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) flag= mface->flag & ME_SMOOTH; vlr= RE_findOrAddVlak(obr, obr->totvlak++); - vlr->obr= obr; vlr->v1= RE_findOrAddVert(obr, vertofs+v1); vlr->v2= RE_findOrAddVert(obr, vertofs+v2); vlr->v3= RE_findOrAddVert(obr, vertofs+v3); @@ -3082,7 +3204,6 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) vlr->flag |= R_NOPUNOFLIP; } vlr->ec= 0; /* mesh edges rendered separately */ - vlr->lay= ob->lay; if(len==0) obr->totvlak--; else { @@ -3133,7 +3254,6 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) MVert *v1 = &mvert[medge->v2]; vlr= RE_findOrAddVlak(obr, obr->totvlak++); - vlr->obr= obr; vlr->v1= RE_findOrAddVert(obr, vertofs+medge->v1); vlr->v2= RE_findOrAddVert(obr, vertofs+medge->v2); vlr->v3= vlr->v2; @@ -3154,7 +3274,6 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) vlr->mat= ma; vlr->flag= 0; vlr->ec= ME_V1V2; - vlr->lay= ob->lay; } } if(edgetable) @@ -3163,22 +3282,20 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int only_verts) } } - if(!only_verts) { + if(!timeoffset) { if (test_for_displace(re, ob ) ) { - calc_vertexnormals(re, obr, 0); - do_displacement(re, obr); + calc_vertexnormals(re, obr, 0, 0); + if(do_autosmooth) + do_displacement(re, obr, mat, imat); + else + do_displacement(re, obr, NULL, NULL); } if(do_autosmooth) { autosmooth(re, obr, mat, me->smoothresh); } - if(useFluidmeshNormals) { - // do not recalculate, only init render data - calc_fluidsimnormals(re, obr, need_tangent); - } else { - calc_vertexnormals(re, obr, need_tangent); - } + calc_vertexnormals(re, obr, need_tangent, need_nmap_tangent); if(need_stress) calc_edge_stress(re, obr, me); @@ -3209,8 +3326,6 @@ static void initshadowbuf(Render *re, LampRen *lar, float mat[][4]) /* percentage render: keep track of min and max */ shb->size= (lar->bufsize*re->r.size)/100; - if(lar->buffers>1) shb->size/= 2; - if(shb->size<512) shb->size= 512; else if(shb->size > lar->bufsize) shb->size= lar->bufsize; @@ -3282,6 +3397,7 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) LampRen *lar; GroupObject *go; float mat[4][4], angle, xn, yn; + float vec[3]; int c; /* previewrender sets this to zero... prevent accidents */ @@ -3341,6 +3457,9 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) lar->r= lar->energy*la->r; lar->g= lar->energy*la->g; lar->b= lar->energy*la->b; + lar->shdwr= la->shdwr; + lar->shdwg= la->shdwg; + lar->shdwb= la->shdwb; lar->k= la->k; // area @@ -3353,12 +3472,20 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) lar->area_sizez= la->area_sizez; lar->area_shape= la->area_shape; + + /* Annoying, lamp UI does this, but the UI might not have been used? - add here too. + * make sure this matches buttons_shading.c's logic */ + if(ELEM4(la->type, LA_AREA, LA_SPOT, LA_SUN, LA_LOCAL) && (la->mode & LA_SHAD_RAY)) + if (ELEM3(la->type, LA_SPOT, LA_SUN, LA_LOCAL)) + if (la->ray_samp_method == LA_SAMP_CONSTANT) la->ray_samp_method = LA_SAMP_HALTON; + lar->ray_samp_method= la->ray_samp_method; lar->ray_samp_type= la->ray_samp_type; lar->adapt_thresh= la->adapt_thresh; + lar->sunsky = NULL; - if( ELEM3(lar->type, LA_SPOT, LA_SUN, LA_LOCAL)) { + if( ELEM(lar->type, LA_SPOT, LA_LOCAL)) { lar->ray_totsamp= lar->ray_samp*lar->ray_samp; lar->area_shape = LA_AREA_SQUARE; lar->area_sizey= lar->area_size; @@ -3388,6 +3515,27 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) area_lamp_vectors(lar); init_jitter_plane(lar); // subsamples } + else if(lar->type==LA_SUN){ + lar->ray_totsamp= lar->ray_samp*lar->ray_samp; + lar->area_shape = LA_AREA_SQUARE; + lar->area_sizey= lar->area_size; + + if((la->sun_effect_type & LA_SUN_EFFECT_SKY) || + (la->sun_effect_type & LA_SUN_EFFECT_AP)){ + lar->sunsky = (struct SunSky*)MEM_callocN(sizeof(struct SunSky), "sunskyren"); + lar->sunsky->effect_type = la->sun_effect_type; + + VECCOPY(vec,ob->obmat[2]); + Normalize(vec); + + InitSunSky(lar->sunsky, la->atm_turbidity, vec, la->horizon_brightness, + la->spread, la->sun_brightness, la->sun_size, la->backscattered_light, + la->skyblendfac, la->skyblendtype, la->sky_exposure, la->sky_colorspace); + + InitAtmosphere(lar->sunsky, la->sun_intensity, 1.0, 1.0, la->atm_inscattering_factor, la->atm_extinction_factor, + la->atm_distance_factor); + } + } else lar->ray_totsamp= 0; #ifndef DISABLE_YAFRAY @@ -3465,7 +3613,10 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) for(c=0; c<MAX_MTEX; c++) { if(la->mtex[c] && la->mtex[c]->tex) { - lar->mode |= LA_TEXTURE; + if (la->mtex[c]->mapto & LAMAP_COL) + lar->mode |= LA_TEXTURE; + if (la->mtex[c]->mapto & LAMAP_SHAD) + lar->mode |= LA_SHAD_TEX; if(G.rendering) { if(re->osa) { @@ -3485,9 +3636,6 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) if(re->r.mode & R_SHADOW) { - if ((lar->mode & LA_SHAD_RAY) && (lar->ray_samp_method == LA_SAMP_HAMMERSLEY)) { - init_lamp_hammersley(lar); - } if(la->type==LA_AREA && (lar->mode & LA_SHAD_RAY) && (lar->ray_samp_method == LA_SAMP_CONSTANT)) { init_jitter_plane(lar); } @@ -3501,16 +3649,24 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) /* this is the way used all over to check for shadow */ if(lar->shb || (lar->mode & LA_SHAD_RAY)) { + LampShadowSample *ls; LampShadowSubSample *lss; - int a, b, tot= re->r.threads*re->r.osa; + int a, b; + + memset(re->shadowsamplenr, 0, sizeof(re->shadowsamplenr)); lar->shadsamp= MEM_mallocN(re->r.threads*sizeof(LampShadowSample), "lamp shadow sample"); - lss= lar->shadsamp[0].s; + ls= lar->shadsamp; + /* shadfacs actually mean light, let's put them to 1 to prevent unitialized accidents */ - for(a=0; a<tot; a++, lss++) { - for(b=0; b<4; b++) { + for(a=0; a<re->r.threads; a++, ls++) { + lss= ls->s; + for(b=0; b<re->r.osa; b++, lss++) { lss->samplenr= -1; /* used to detect whether we store or read */ - lss->shadfac[b]= 1.0f; + lss->shadfac[0]= 1.0f; + lss->shadfac[1]= 1.0f; + lss->shadfac[2]= 1.0f; + lss->shadfac[3]= 1.0f; } } } @@ -3611,13 +3767,18 @@ void init_render_world(Render *re) if(re->osa) while(re->wrld.aosamp*re->wrld.aosamp < re->osa) re->wrld.aosamp++; - if(!(re->r.mode & R_RAYTRACE)) + if(!(re->r.mode & R_RAYTRACE) && (re->wrld.ao_gather_method == WO_AOGATHER_RAYTRACE)) re->wrld.mode &= ~WO_AMB_OCC; } else { memset(&re->wrld, 0, sizeof(World)); - re->wrld.exp= 0.0; - re->wrld.range= 1.0; + re->wrld.exp= 0.0f; + re->wrld.range= 1.0f; + + /* for mist pass */ + re->wrld.miststa= re->clipsta; + re->wrld.mistdist= re->clipend-re->clipsta; + re->wrld.misi= 1.0f; } re->wrld.linfac= 1.0 + pow((2.0*re->wrld.exp + 0.5), -10); @@ -3679,7 +3840,7 @@ static void set_phong_threshold(ObjectRen *obr) } /* per face check if all samples should be taken. - if raytrace, do always for raytraced material, or when material full_osa set */ + if raytrace or multisample, do always for raytraced material, or when material full_osa set */ static void set_fullsample_flag(Render *re, ObjectRen *obr) { VlakRen *vlr; @@ -3693,7 +3854,8 @@ static void set_fullsample_flag(Render *re, ObjectRen *obr) for(a=obr->totvlak-1; a>=0; a--) { vlr= RE_findOrAddVlak(obr, a); - if(vlr->mat->mode & MA_FULL_OSA) vlr->flag |= R_FULL_OSA; + if(vlr->mat->mode & MA_FULL_OSA) + vlr->flag |= R_FULL_OSA; else if(trace) { if(vlr->mat->mode & MA_SHLESS); else if(vlr->mat->mode & (MA_RAYTRANSP|MA_RAYMIRROR)) @@ -3705,6 +3867,57 @@ static void set_fullsample_flag(Render *re, ObjectRen *obr) } } +/* split quads for pradictable baking + * dir 1 == (0,1,2) (0,2,3), 2 == (1,3,0) (1,2,3) + */ +static void split_quads(ObjectRen *obr, int dir) +{ + VlakRen *vlr, *vlr1; + int a; + + for(a=obr->totvlak-1; a>=0; a--) { + vlr= RE_findOrAddVlak(obr, a); + + /* test if rendering as a quad or triangle, skip wire */ + if(vlr->v4 && (vlr->flag & R_STRAND)==0 && (vlr->mat->mode & MA_WIRE)==0) { + + if(vlr->v4) { + + vlr1= RE_vlakren_copy(obr, vlr); + vlr1->flag |= R_FACE_SPLIT; + + if( dir==2 ) vlr->flag |= R_DIVIDE_24; + else vlr->flag &= ~R_DIVIDE_24; + + /* new vertex pointers */ + if (vlr->flag & R_DIVIDE_24) { + vlr1->v1= vlr->v2; + vlr1->v2= vlr->v3; + vlr1->v3= vlr->v4; + + vlr->v3 = vlr->v4; + + vlr1->flag |= R_DIVIDE_24; + } + else { + vlr1->v1= vlr->v1; + vlr1->v2= vlr->v3; + vlr1->v3= vlr->v4; + + vlr1->flag &= ~R_DIVIDE_24; + } + vlr->v4 = vlr1->v4 = NULL; + + /* new normals */ + CalcNormFloat(vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n); + CalcNormFloat(vlr1->v3->co, vlr1->v2->co, vlr1->v1->co, vlr1->n); + } + /* clear the flag when not divided */ + else vlr->flag &= ~R_DIVIDE_24; + } + } +} + static void check_non_flat_quads(ObjectRen *obr) { VlakRen *vlr, *vlr1; @@ -3767,6 +3980,7 @@ static void check_non_flat_quads(ObjectRen *obr) xn= nor[0]*vlr->n[0] + nor[1]*vlr->n[1] + nor[2]*vlr->n[2]; if(ABS(xn) < 0.999995 ) { // checked on noisy fractal grid + float d1, d2; vlr1= RE_vlakren_copy(obr, vlr); @@ -3778,10 +3992,10 @@ static void check_non_flat_quads(ObjectRen *obr) CalcNormFloat(vlr->v2->co, vlr->v3->co, vlr->v4->co, nor); d2= nor[0]*vlr->v2->n[0] + nor[1]*vlr->v2->n[1] + nor[2]*vlr->v2->n[2]; - + if( fabs(d1) < fabs(d2) ) vlr->flag |= R_DIVIDE_24; else vlr->flag &= ~R_DIVIDE_24; - + /* new vertex pointers */ if (vlr->flag & R_DIVIDE_24) { vlr1->v1= vlr->v2; @@ -3812,26 +4026,67 @@ static void check_non_flat_quads(ObjectRen *obr) } } -static void finalize_render_object(Render *re, ObjectRen *obr, int only_verts) +static void finalize_render_object(Render *re, ObjectRen *obr, int timeoffset) { Object *ob= obr->ob; + VertRen *ver= NULL; + StrandRen *strand= NULL; + StrandBound *sbound= NULL; + float min[3], max[3], smin[3], smax[3]; + int a, b; if(obr->totvert || obr->totvlak || obr->tothalo || obr->totstrand) { /* the exception below is because displace code now is in init_render_mesh call, I will look at means to have autosmooth enabled for all object types and have it as general postprocess, like displace */ if(ob->type!=OB_MESH && test_for_displace(re, ob)) - do_displacement(re, obr); + do_displacement(re, obr, NULL, NULL); - if(!only_verts) { + if(!timeoffset) { /* phong normal interpolation can cause error in tracing * (terminator problem) */ ob->smoothresh= 0.0; if((re->r.mode & R_RAYTRACE) && (re->r.mode & R_SHADOW)) set_phong_threshold(obr); - - check_non_flat_quads(obr); + + if (re->flag & R_BAKING && re->r.bake_quad_split != 0) { + /* Baking lets us define a quad split order */ + split_quads(obr, re->r.bake_quad_split); + } else { + check_non_flat_quads(obr); + } + set_fullsample_flag(re, obr); + + /* compute bounding boxes for clipping */ + INIT_MINMAX(min, max); + for(a=0; a<obr->totvert; a++) { + if((a & 255)==0) ver= obr->vertnodes[a>>8].vert; + else ver++; + + DO_MINMAX(ver->co, min, max); + } + + if(obr->strandbuf) { + sbound= obr->strandbuf->bound; + for(b=0; b<obr->strandbuf->totbound; b++, sbound++) { + INIT_MINMAX(smin, smax); + + for(a=sbound->start; a<sbound->end; a++) { + strand= RE_findOrAddStrand(obr, a); + strand_minmax(strand, smin, smax); + } + + VECCOPY(sbound->boundbox[0], smin); + VECCOPY(sbound->boundbox[1], smax); + + DO_MINMAX(smin, min, max); + DO_MINMAX(smax, min, max); + } + } + + VECCOPY(obr->boundbox[0], min); + VECCOPY(obr->boundbox[1], max); } } } @@ -3845,7 +4100,128 @@ static int render_object_type(int type) return ELEM5(type, OB_FONT, OB_CURVE, OB_SURF, OB_MESH, OB_MBALL); } -static void init_render_object_data(Render *re, ObjectRen *obr, int only_verts) +static void find_dupli_instances(Render *re, ObjectRen *obr) +{ + ObjectInstanceRen *obi; + float imat[4][4], obmat[4][4], obimat[4][4], nmat[3][3]; + int first = 1; + + Mat4MulMat4(obmat, obr->obmat, re->viewmat); + Mat4Invert(imat, obmat); + + /* for objects instanced by dupliverts/faces/particles, we go over the + * list of instances to find ones that instance obr, and setup their + * matrices and obr pointer */ + for(obi=re->instancetable.last; obi; obi=obi->prev) { + if(!obi->obr && obi->ob == obr->ob && obi->psysindex == obr->psysindex) { + obi->obr= obr; + + /* compute difference between object matrix and + * object matrix with dupli transform, in viewspace */ + Mat4CpyMat4(obimat, obi->mat); + Mat4MulMat4(obi->mat, imat, obimat); + + Mat3CpyMat4(nmat, obi->mat); + Mat3Inv(obi->nmat, nmat); + Mat3Transp(obi->nmat); + + if(!first) { + re->totvert += obr->totvert; + re->totvlak += obr->totvlak; + re->tothalo += obr->tothalo; + re->totstrand += obr->totstrand; + } + else + first= 0; + } + } +} + +static void assign_dupligroup_dupli(Render *re, ObjectInstanceRen *obi, ObjectRen *obr) +{ + float imat[4][4], obmat[4][4], obimat[4][4], nmat[3][3]; + + Mat4MulMat4(obmat, obr->obmat, re->viewmat); + Mat4Invert(imat, obmat); + + obi->obr= obr; + + /* compute difference between object matrix and + * object matrix with dupli transform, in viewspace */ + Mat4CpyMat4(obimat, obi->mat); + Mat4MulMat4(obi->mat, imat, obimat); + + Mat3CpyMat4(nmat, obi->mat); + Mat3Inv(obi->nmat, nmat); + Mat3Transp(obi->nmat); + + re->totvert += obr->totvert; + re->totvlak += obr->totvlak; + re->tothalo += obr->tothalo; + re->totstrand += obr->totstrand; +} + +static ObjectRen *find_dupligroup_dupli(Render *re, Object *ob, int psysindex) +{ + ObjectRen *obr; + + /* if the object is itself instanced, we don't want to create an instance + * for it */ + if(ob->transflag & OB_RENDER_DUPLI) + return NULL; + + /* try to find an object that was already created so we can reuse it + * and save memory */ + for(obr=re->objecttable.first; obr; obr=obr->next) + if(obr->ob == ob && obr->psysindex == psysindex && (obr->flag & R_INSTANCEABLE)) + return obr; + + return NULL; +} + +static void set_dupli_tex_mat(Render *re, ObjectInstanceRen *obi, DupliObject *dob) +{ + /* For duplis we need to have a matrix that transform the coordinate back + * to it's original position, without the dupli transforms. We also check + * the matrix is actually needed, to save memory on lots of dupliverts for + * example */ + static Object *lastob= NULL; + static int needtexmat= 0; + + /* init */ + if(!re) { + lastob= NULL; + needtexmat= 0; + return; + } + + /* check if we actually need it */ + if(lastob != dob->ob) { + Material ***material; + short a, *totmaterial; + + lastob= dob->ob; + needtexmat= 0; + + totmaterial= give_totcolp(dob->ob); + material= give_matarar(dob->ob); + + if(totmaterial && material) + for(a= 0; a<*totmaterial; a++) + if((*material)[a] && (*material)[a]->texco & TEXCO_OBJECT) + needtexmat= 1; + } + + if(needtexmat) { + float imat[4][4]; + + obi->duplitexmat= BLI_memarena_alloc(re->memArena, sizeof(float)*4*4); + Mat4Invert(imat, dob->mat); + MTC_Mat4MulSerie(obi->duplitexmat, re->viewmat, dob->omat, imat, re->viewinv, 0, 0, 0, 0); + } +} + +static void init_render_object_data(Render *re, ObjectRen *obr, int timeoffset) { Object *ob= obr->ob; ParticleSystem *psys; @@ -3863,20 +4239,20 @@ static void init_render_object_data(Render *re, ObjectRen *obr, int only_verts) for(psys=ob->particlesystem.first, i=0; i<obr->psysindex-1; i++) psys= psys->next; - render_new_particle_system(re, obr, psys); + render_new_particle_system(re, obr, psys, timeoffset); } else { if ELEM(ob->type, OB_FONT, OB_CURVE) - init_render_curve(re, obr, only_verts); + init_render_curve(re, obr, timeoffset); else if(ob->type==OB_SURF) init_render_surf(re, obr); else if(ob->type==OB_MESH) - init_render_mesh(re, obr, only_verts); + init_render_mesh(re, obr, timeoffset); else if(ob->type==OB_MBALL) init_render_mball(re, obr); } - finalize_render_object(re, obr, only_verts); + finalize_render_object(re, obr, timeoffset); re->totvert += obr->totvert; re->totvlak += obr->totvlak; @@ -3884,11 +4260,14 @@ static void init_render_object_data(Render *re, ObjectRen *obr, int only_verts) re->totstrand += obr->totstrand; } -static void add_render_object(Render *re, Object *ob, Object *par, int index, int only_verts) +static void add_render_object(Render *re, Object *ob, Object *par, DupliObject *dob, int timeoffset, int vectorlay) { ObjectRen *obr; + ObjectInstanceRen *obi; ParticleSystem *psys; - int show_emitter, allow_render= 1, psysindex; + int show_emitter, allow_render= 1, index, psysindex; + + index= (dob)? dob->index: 0; /* the emitter has to be processed first (render levels of modifiers) */ /* so here we only check if the emitter should be rendered */ @@ -3896,7 +4275,7 @@ static void add_render_object(Render *re, Object *ob, Object *par, int index, in show_emitter= 0; for(psys=ob->particlesystem.first; psys; psys=psys->next) { show_emitter += psys->part->draw & PART_DRAW_EMITTER; - psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy); + psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy, timeoffset); } /* if no psys has "show emitter" selected don't render emitter */ @@ -3906,32 +4285,52 @@ static void add_render_object(Render *re, Object *ob, Object *par, int index, in /* one render object for the data itself */ if(allow_render) { - obr= RE_addRenderObject(re, ob, par, index, 0); - init_render_object_data(re, obr, only_verts); + obr= RE_addRenderObject(re, ob, par, index, 0, ob->lay); + if((dob && !dob->animated) || (ob->transflag & OB_RENDER_DUPLI)) { + obr->flag |= R_INSTANCEABLE; + Mat4CpyMat4(obr->obmat, ob->obmat); + } + if(obr->lay & vectorlay) + obr->flag |= R_NEED_VECTORS; + init_render_object_data(re, obr, timeoffset); /* only add instance for objects that have not been used for dupli */ - if(!(ob->transflag & OB_RENDER_DUPLI)) - RE_addRenderInstance(re, obr, ob, par, index, 0, NULL); + if(!(ob->transflag & OB_RENDER_DUPLI)) { + obi= RE_addRenderInstance(re, obr, ob, par, index, 0, NULL, ob->lay); + if(dob) set_dupli_tex_mat(re, obi, dob); + } + else + find_dupli_instances(re, obr); } /* and one render object per particle system */ if(ob->particlesystem.first) { psysindex= 1; for(psys=ob->particlesystem.first; psys; psys=psys->next, psysindex++) { - obr= RE_addRenderObject(re, ob, par, index, psysindex); - init_render_object_data(re, obr, only_verts); + obr= RE_addRenderObject(re, ob, par, index, psysindex, ob->lay); + if((dob && !dob->animated) || (ob->transflag & OB_RENDER_DUPLI)) { + obr->flag |= R_INSTANCEABLE; + Mat4CpyMat4(obr->obmat, ob->obmat); + } + if(obr->lay & vectorlay) + obr->flag |= R_NEED_VECTORS; + init_render_object_data(re, obr, timeoffset); psys_render_restore(ob, psys); /* only add instance for objects that have not been used for dupli */ - if(!(ob->transflag & OB_RENDER_DUPLI)) - RE_addRenderInstance(re, obr, ob, par, index, psysindex, NULL); + if(!(ob->transflag & OB_RENDER_DUPLI)) { + obi= RE_addRenderInstance(re, obr, ob, par, index, psysindex, NULL, ob->lay); + if(dob) set_dupli_tex_mat(re, obi, dob); + } + else + find_dupli_instances(re, obr); } } } /* par = pointer to duplicator parent, needed for object lookup table */ /* index = when duplicater copies same object (particle), the counter */ -static void init_render_object(Render *re, Object *ob, Object *par, int index, int only_verts) +static void init_render_object(Render *re, Object *ob, Object *par, DupliObject *dob, int timeoffset, int vectorlay) { static double lasttime= 0.0; double time; @@ -3940,7 +4339,7 @@ static void init_render_object(Render *re, Object *ob, Object *par, int index, i if(ob->type==OB_LAMP) add_render_lamp(re, ob); else if(render_object_type(ob->type)) - add_render_object(re, ob, par, index, only_verts); + add_render_object(re, ob, par, dob, timeoffset, vectorlay); else { MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat); MTC_Mat4Invert(ob->imat, mat); @@ -3965,6 +4364,14 @@ void RE_Database_Free(Render *re) { Object *ob = NULL; LampRen *lar; + + /* statistics for debugging render memory usage */ + if((G.f & G_DEBUG) && (G.rendering)) { + if((re->r.scemode & R_PREVIEWBUTS)==0) { + BKE_image_print_memlist(); + MEM_printmemlist_stats(); + } + } /* FREE */ @@ -3972,7 +4379,7 @@ void RE_Database_Free(Render *re) freeshadowbuf(lar); if(lar->jitter) MEM_freeN(lar->jitter); if(lar->shadsamp) MEM_freeN(lar->shadsamp); - if(lar->qsa) free_lamp_qmcsampler(lar); + if(lar->sunsky) MEM_freeN(lar->sunsky); curvemapping_free(lar->curfalloff); } @@ -4010,13 +4417,14 @@ void RE_Database_Free(Render *re) re->wrld.aotables= NULL; re->scene->world->aotables= NULL; } - if((re->r.mode & R_RAYTRACE) && (re->wrld.mode & WO_AMB_OCC) && - (re->wrld.ao_samp_method == WO_AOSAMP_HAMMERSLEY) && (re->qsa)) + if(re->r.mode & R_RAYTRACE) free_render_qmcsampler(re); if(re->r.mode & R_RAYTRACE) freeraytree(re); free_sss(re); + free_occ(re); + free_strand_surface(re); re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0; re->i.convertdone= 0; @@ -4045,16 +4453,15 @@ static int allow_render_object(Object *ob, int nolamps, int onlyselected, Object for(psys=ob->particlesystem.first; psys; psys=psys->next){ part=psys->part; - if((part->draw_as==PART_DRAW_OB && part->dup_ob) || (part->draw_as==PART_DRAW_GR && part->dup_group)) - if(part->draw & PART_DRAW_EMITTER) - allow= 1; + if(part->draw & PART_DRAW_EMITTER) + allow= 1; } } if(!allow) return 0; } - else if(ob->transflag & OB_DUPLI) + else if((ob->transflag & OB_DUPLI) && !(ob->transflag & OB_DUPLIFRAMES)) return 0; if(nolamps && (ob->type==OB_LAMP)) @@ -4066,19 +4473,128 @@ static int allow_render_object(Object *ob, int nolamps, int onlyselected, Object return 1; } -static int allow_render_dupli_instance(Render *re, Object *ob, Object *obd) +static int allow_render_dupli_instance(Render *re, DupliObject *dob, Object *obd) { + ParticleSystem *psys; + Material *ma; + short a, *totmaterial; + + /* don't allow objects with halos. we need to have + * all halo's to sort them globally in advance */ + totmaterial= give_totcolp(obd); + + if(totmaterial) { + for(a= 0; a<*totmaterial; a++) { + ma= give_current_material(obd, a); + if(ma && (ma->mode & MA_HALO)) + return 0; + } + } + + for(psys=obd->particlesystem.first; psys; psys=psys->next) + if(!ELEM5(psys->part->draw_as, PART_DRAW_BB, PART_DRAW_LINE, PART_DRAW_PATH, PART_DRAW_OB, PART_DRAW_GR)) + return 0; + + /* don't allow lamp, animated duplis, or radio render */ return (render_object_type(obd->type) && - (!(ob->transflag & OB_DUPLIGROUP)) && + (!(dob->type == OB_DUPLIGROUP) || !dob->animated) && !(re->r.mode & R_RADIO)); } -static void database_init_objects(Render *re, unsigned int lay, int nolamps, int onlyselected, Object *actob, int only_verts) +static void dupli_render_particle_set(Render *re, Object *ob, int timeoffset, int level, int enable) +{ + /* ugly function, but we need to set particle systems to their render + * settings before calling object_duplilist, to get render level duplis */ + Group *group; + GroupObject *go; + ParticleSystem *psys; + DerivedMesh *dm; + + if(level >= MAX_DUPLI_RECUR) + return; + + if(ob->transflag & OB_DUPLIPARTS) { + for(psys=ob->particlesystem.first; psys; psys=psys->next) { + if(ELEM(psys->part->draw_as, PART_DRAW_OB, PART_DRAW_GR)) { + if(enable) + psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy, timeoffset); + else + psys_render_restore(ob, psys); + } + } + + if(level == 0 && enable) { + /* this is to make sure we get render level duplis in groups: + * the derivedmesh must be created before init_render_mesh, + * since object_duplilist does dupliparticles before that */ + dm = mesh_create_derived_render(ob, CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL); + dm->release(dm); + + for(psys=ob->particlesystem.first; psys; psys=psys->next) + psys_get_modifier(ob, psys)->flag &= ~eParticleSystemFlag_psys_updated; + } + } + + if(ob->dup_group==NULL) return; + group= ob->dup_group; + + for(go= group->gobject.first; go; go= go->next) + dupli_render_particle_set(re, go->ob, timeoffset, level+1, enable); +} + +static int get_vector_renderlayers(Scene *sce) +{ + SceneRenderLayer *srl; + int lay= 0; + + for(srl= sce->r.layers.first; srl; srl= srl->next) + if(srl->passflag & SCE_PASS_VECTOR) + lay |= srl->lay; + + return lay; +} + +static void add_group_render_dupli_obs(Render *re, Group *group, int nolamps, int onlyselected, Object *actob, int timeoffset, int vectorlay, int level) +{ + GroupObject *go; + Object *ob; + + /* simple preventing of too deep nested groups */ + if(level>MAX_DUPLI_RECUR) return; + + /* recursively go into dupligroups to find objects with OB_RENDER_DUPLI + * that were not created yet */ + for(go= group->gobject.first; go; go= go->next) { + ob= go->ob; + + if(ob->flag & OB_DONE) { + if(ob->transflag & OB_RENDER_DUPLI) { + if(allow_render_object(ob, nolamps, onlyselected, actob)) { + init_render_object(re, ob, NULL, 0, timeoffset, vectorlay); + ob->transflag &= ~OB_RENDER_DUPLI; + + if(ob->dup_group) + add_group_render_dupli_obs(re, ob->dup_group, nolamps, onlyselected, actob, timeoffset, vectorlay, level+1); + } + } + } + } +} + +static void database_init_objects(Render *re, unsigned int renderlay, int nolamps, int onlyselected, Object *actob, int timeoffset) { Base *base; Object *ob; + Group *group; + ObjectInstanceRen *obi; Scene *sce; float mat[4][4]; + int lay, vectorlay, redoimat= 0; + + /* for duplis we need the Object texture mapping to work as if + * untransformed, set_dupli_tex_mat sets the matrix to allow that + * NULL is just for init */ + set_dupli_tex_mat(NULL, NULL, NULL); for(SETLOOPER(re->scene, base)) { ob= base->object; @@ -4092,76 +4608,149 @@ static void database_init_objects(Render *re, unsigned int lay, int nolamps, int for(SETLOOPER(re->scene, base)) { ob= base->object; - + + /* in the prev/next pass for making speed vectors, avoid creating + * objects that are not on a renderlayer with a vector pass, can + * save a lot of time in complex scenes */ + vectorlay= get_vector_renderlayers(sce); + lay= (timeoffset)? renderlay & vectorlay: renderlay; + /* if the object has been restricted from rendering in the outliner, ignore it */ if(ob->restrictflag & OB_RESTRICT_RENDER) continue; /* OB_DONE means the object itself got duplicated, so was already converted */ if(ob->flag & OB_DONE) { - if(ob->transflag & OB_RENDER_DUPLI) - if(allow_render_object(ob, nolamps, onlyselected, actob)) - init_render_object(re, ob, NULL, 0, only_verts); + /* OB_RENDER_DUPLI means instances for it were already created, now + * it still needs to create the ObjectRen containing the data */ + if(ob->transflag & OB_RENDER_DUPLI) { + if(allow_render_object(ob, nolamps, onlyselected, actob)) { + init_render_object(re, ob, NULL, 0, timeoffset, vectorlay); + ob->transflag &= ~OB_RENDER_DUPLI; + } + } } else if((base->lay & lay) || (ob->type==OB_LAMP && (base->lay & re->scene->lay)) ) { if((ob->transflag & OB_DUPLI) && (ob->type!=OB_MBALL)) { DupliObject *dob; ListBase *lb; + redoimat= 1; + + /* create list of duplis generated by this object, particle + * system need to have render settings set for dupli particles */ + dupli_render_particle_set(re, ob, timeoffset, 0, 1); lb= object_duplilist(sce, ob); + dupli_render_particle_set(re, ob, timeoffset, 0, 0); + for(dob= lb->first; dob; dob= dob->next) { Object *obd= dob->ob; Mat4CpyMat4(obd->obmat, dob->mat); /* group duplis need to set ob matrices correct, for deform. so no_draw is part handled */ - if(dob->no_draw) + if(!(obd->transflag & OB_RENDER_DUPLI) && dob->no_draw) continue; if(obd->restrictflag & OB_RESTRICT_RENDER) continue; - + if(obd->type==OB_MBALL) continue; if(!allow_render_object(obd, nolamps, onlyselected, actob)) continue; - if(allow_render_dupli_instance(re, ob, obd)) { + if(allow_render_dupli_instance(re, dob, obd)) { ParticleSystem *psys; + ObjectRen *obr = NULL; int psysindex; - float imat[4][4], mat[4][4]; - - /* compute difference between object matrix and - * object matrix with dupli transform, in viewspace */ - Mat4Invert(imat, dob->omat); - MTC_Mat4MulSerie(mat, re->viewmat, dob->mat, imat, re->viewinv, 0, 0, 0, 0); - - RE_addRenderInstance(re, NULL, obd, ob, dob->index, 0, mat); + float mat[4][4]; + + /* instances instead of the actual object are added in two cases, either + * this is a duplivert/face/particle, or it is a non-animated object in + * a dupligroup that has already been created before */ + if(dob->type != OB_DUPLIGROUP || (obr=find_dupligroup_dupli(re, obd, 0))) { + Mat4MulMat4(mat, dob->mat, re->viewmat); + obi= RE_addRenderInstance(re, NULL, obd, ob, dob->index, 0, mat, obd->lay); + + /* fill in instance variables for texturing */ + set_dupli_tex_mat(re, obi, dob); + if(dob->type != OB_DUPLIGROUP) { + VECCOPY(obi->dupliorco, dob->orco); + obi->dupliuv[0]= dob->uv[0]; + obi->dupliuv[1]= dob->uv[1]; + } + else { + /* for the second case, setup instance to point to the already + * created object, and possibly setup instances if this object + * itself was duplicated. for the first case find_dupli_instances + * will be called later. */ + assign_dupligroup_dupli(re, obi, obr); + if(obd->transflag & OB_RENDER_DUPLI) + find_dupli_instances(re, obr); + } + } + else + /* can't instance, just create the object */ + init_render_object(re, obd, ob, dob, timeoffset, vectorlay); + /* same logic for particles, each particle system has it's own object, so + * need to go over them separately */ psysindex= 1; - for(psys=obd->particlesystem.first; psys; psys=psys->next) - RE_addRenderInstance(re, NULL, obd, ob, dob->index, psysindex++, mat); + for(psys=obd->particlesystem.first; psys; psys=psys->next) { + if(dob->type != OB_DUPLIGROUP || (obr=find_dupligroup_dupli(re, ob, psysindex))) { + obi= RE_addRenderInstance(re, NULL, obd, ob, dob->index, psysindex++, mat, obd->lay); + + set_dupli_tex_mat(re, obi, dob); + if(dob->type != OB_DUPLIGROUP) { + VECCOPY(obi->dupliorco, dob->orco); + obi->dupliuv[0]= dob->uv[0]; + obi->dupliuv[1]= dob->uv[1]; + } + else { + assign_dupligroup_dupli(re, obi, obr); + if(obd->transflag & OB_RENDER_DUPLI) + find_dupli_instances(re, obr); + } + } + } - obd->flag |= OB_DONE; - obd->transflag |= OB_RENDER_DUPLI; + if(dob->type != OB_DUPLIGROUP) { + obd->flag |= OB_DONE; + obd->transflag |= OB_RENDER_DUPLI; + } } else - init_render_object(re, obd, ob, dob->index, only_verts); + init_render_object(re, obd, ob, dob, timeoffset, vectorlay); if(re->test_break()) break; } free_object_duplilist(lb); if(allow_render_object(ob, nolamps, onlyselected, actob)) - init_render_object(re, ob, NULL, 0, only_verts); + init_render_object(re, ob, NULL, 0, timeoffset, vectorlay); } else if(allow_render_object(ob, nolamps, onlyselected, actob)) - init_render_object(re, ob, NULL, 0, only_verts); + init_render_object(re, ob, NULL, 0, timeoffset, vectorlay); } if(re->test_break()) break; } + /* objects in groups with OB_RENDER_DUPLI set still need to be created, + * since they may not be part of the scene */ + for(group= G.main->group.first; group; group=group->id.next) + add_group_render_dupli_obs(re, group, nolamps, onlyselected, actob, timeoffset, renderlay, 0); + + /* imat objects has to be done again, since groups can mess it up */ + if(redoimat) { + for(SETLOOPER(re->scene, base)) { + ob= base->object; + MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat); + MTC_Mat4Invert(ob->imat, mat); + } + } + if(!re->test_break()) RE_makeRenderInstances(re); } @@ -4202,14 +4791,16 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) Mat4Ortho(re->scene->camera->obmat); Mat4Invert(mat, re->scene->camera->obmat); RE_SetView(re, mat); + re->scene->camera->recalc= OB_RECALC_OB; /* force correct matrix for scaled cameras */ } init_render_world(re); /* do first, because of ambient. also requires re->osa set correct */ - if(re->wrld.mode & WO_AMB_OCC) { - if (re->wrld.ao_samp_method == WO_AOSAMP_HAMMERSLEY) - init_render_hammersley(re); - else if (re->wrld.ao_samp_method == WO_AOSAMP_CONSTANT) - init_ao_sphere(&re->wrld); + if(re->r.mode & R_RAYTRACE) { + init_render_qmcsampler(re); + + if(re->wrld.mode & WO_AMB_OCC) + if (re->wrld.ao_samp_method == WO_AOSAMP_CONSTANT) + init_ao_sphere(&re->wrld); } /* still bad... doing all */ @@ -4270,10 +4861,17 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) if(!re->test_break()) project_renderdata(re, projectverto, re->r.mode & R_PANORAMA, 0, 1); + + /* Occlusion */ + if((re->wrld.mode & WO_AMB_OCC) && !re->test_break()) + if(re->wrld.ao_gather_method == WO_AOGATHER_APPROX) + if(re->r.renderer==R_INTERN) + if(re->r.mode & R_SHADOW) + make_occ_tree(re); /* SSS */ if((re->r.mode & R_SSS) && !re->test_break()) - if (re->r.renderer==R_INTERN) + if(re->r.renderer==R_INTERN) make_sss_tree(re); } @@ -4334,7 +4932,7 @@ static void database_fromscene_vectors(Render *re, Scene *scene, int timeoffset) } /* MAKE RENDER DATA */ - database_init_objects(re, lay, 0, 0, 0, 1); + database_init_objects(re, lay, 0, 0, 0, timeoffset); if(!re->test_break()) project_renderdata(re, projectverto, re->r.mode & R_PANORAMA, 0, 1); @@ -4429,14 +5027,49 @@ static void calculate_speedvector(float *vectors, int step, float winsq, float w } } +static float *calculate_strandsurface_speedvectors(Render *re, ObjectInstanceRen *obi, StrandSurface *mesh) +{ + float winsq= re->winx*re->winy, winroot= sqrt(winsq), (*winspeed)[4]; + float ho[4], prevho[4], nextho[4], winmat[4][4], vec[2]; + int a; + + if(mesh->co && mesh->prevco && mesh->nextco) { + if(obi->flag & R_TRANSFORMED) + Mat4MulMat4(winmat, obi->mat, re->winmat); + else + Mat4CpyMat4(winmat, re->winmat); + + winspeed= MEM_callocN(sizeof(float)*4*mesh->totvert, "StrandSurfWin"); + + for(a=0; a<mesh->totvert; a++) { + projectvert(mesh->co[a], winmat, ho); + + projectvert(mesh->prevco[a], winmat, prevho); + speedvector_project(NULL, vec, mesh->prevco[a], prevho); + calculate_speedvector(vec, 0, winsq, winroot, mesh->co[a], ho, winspeed[a]); + + projectvert(mesh->nextco[a], winmat, nextho); + speedvector_project(NULL, vec, mesh->nextco[a], nextho); + calculate_speedvector(vec, 1, winsq, winroot, mesh->co[a], ho, winspeed[a]); + } + + return (float*)winspeed; + } + + return NULL; +} + static void calculate_speedvectors(Render *re, ObjectInstanceRen *obi, float *vectors, int step) { ObjectRen *obr= obi->obr; VertRen *ver= NULL; StrandRen *strand= NULL; - float *speed, ho[4], winmat[4][4]; + StrandBuffer *strandbuf; + StrandSurface *mesh= NULL; + float *speed, (*winspeed)[4]=NULL, ho[4], winmat[4][4]; + float *co1, *co2, *co3, *co4, w[4]; float winsq= re->winx*re->winy, winroot= sqrt(winsq); - int a; + int a, *face, *index; if(obi->flag & R_TRANSFORMED) Mat4MulMat4(winmat, obi->mat, re->winmat); @@ -4455,13 +5088,42 @@ static void calculate_speedvectors(Render *re, ObjectInstanceRen *obi, float *ve } if(obr->strandnodes) { - for(a=0; a<obr->totstrand; a++, vectors+=2) { - if((a & 255)==0) strand= obr->strandnodes[a>>8].strand; - else strand++; + strandbuf= obr->strandbuf; + mesh= (strandbuf)? strandbuf->surface: NULL; + + /* compute speed vectors at surface vertices */ + if(mesh) + winspeed= (float(*)[4])calculate_strandsurface_speedvectors(re, obi, mesh); + + if(winspeed) { + for(a=0; a<obr->totstrand; a++, vectors+=2) { + if((a & 255)==0) strand= obr->strandnodes[a>>8].strand; + else strand++; + + index= RE_strandren_get_face(obr, strand, 0); + if(index && *index < mesh->totface) { + speed= RE_strandren_get_winspeed(obi, strand, 1); + + /* interpolate speed vectors from strand surface */ + face= mesh->face[*index]; + + co1= mesh->co[face[0]]; + co2= mesh->co[face[1]]; + co3= mesh->co[face[2]]; + co4= (face[3])? mesh->co[face[3]]: NULL; + + InterpWeightsQ3Dfl(co1, co2, co3, co4, strand->vert->co, w); + + speed[0]= speed[1]= speed[2]= speed[3]= 0.0f; + QUATADDFAC(speed, speed, winspeed[face[0]], w[0]); + QUATADDFAC(speed, speed, winspeed[face[1]], w[1]); + QUATADDFAC(speed, speed, winspeed[face[2]], w[2]); + if(face[3]) + QUATADDFAC(speed, speed, winspeed[face[3]], w[3]); + } + } - speed= RE_strandren_get_winspeed(obi, strand, 1); - projectvert(strand->vert->co, winmat, ho); - calculate_speedvector(vectors, step, winsq, winroot, strand->vert->co, ho, speed); + MEM_freeN(winspeed); } } } @@ -4471,36 +5133,54 @@ static int load_fluidsimspeedvectors(Render *re, ObjectInstanceRen *obi, float * ObjectRen *obr= obi->obr; Object *fsob= obr->ob; VertRen *ver= NULL; - float *speed, div, zco[2]; + float *speed, div, zco[2], avgvel[4] = {0.0, 0.0, 0.0, 0.0}; float zmulx= re->winx/2, zmuly= re->winy/2, len; float winsq= re->winx*re->winy, winroot= sqrt(winsq); int a, j; float hoco[4], ho[4], fsvec[4], camco[4]; float mat[4][4], winmat[4][4]; float imat[4][4]; - MVert *vverts; - + FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(fsob, eModifierType_Fluidsim); + FluidsimSettings *fss = fluidmd->fss; + float *velarray = NULL; + /* only one step needed */ if(step) return 1; + if(fluidmd) + fss = fluidmd->fss; + else + return 0; + Mat4CpyMat4(mat, re->viewmat); MTC_Mat4Invert(imat, mat); /* set first vertex OK */ - if( (!fsob->fluidsimSettings) || (!fsob->fluidsimSettings->meshSurfNormals) ) return 0; - vverts = fsob->fluidsimSettings->meshSurfNormals; - //fprintf(stderr, "GZ_VEL obj '%s', calc load_fluidsimspeedvectors\n",fsob->id.name); // NT DEBUG - - if( obr->totvert != fsob->fluidsimSettings->meshSurface->totvert ) { + if(!fss->meshSurfNormals) return 0; + + if( obr->totvert != GET_INT_FROM_POINTER(fss->meshSurface) ) { //fprintf(stderr, "load_fluidsimspeedvectors - modified fluidsim mesh, not using speed vectors (%d,%d)...\n", obr->totvert, fsob->fluidsimSettings->meshSurface->totvert); // DEBUG return 0; } + + velarray = (float *)fss->meshSurfNormals; if(obi->flag & R_TRANSFORMED) Mat4MulMat4(winmat, obi->mat, re->winmat); else Mat4CpyMat4(winmat, re->winmat); + /* (bad) HACK calculate average velocity */ + /* better solution would be fixing getVelocityAt() in intern/elbeem/intern/solver_util.cpp + so that also small drops/little water volumes return a velocity != 0. + But I had no luck in fixing that function - DG */ + for(a=0; a<obr->totvert; a++) { + for(j=0;j<3;j++) avgvel[j] += velarray[3*a + j]; + + } + for(j=0;j<3;j++) avgvel[j] /= (float)(obr->totvert); + + for(a=0; a<obr->totvert; a++, vectors+=2) { if((a & 255)==0) ver= obr->vertnodes[a>>8].vert; @@ -4510,8 +5190,16 @@ static int load_fluidsimspeedvectors(Render *re, ObjectInstanceRen *obi, float * // get fluid velocity fsvec[3] = 0.; //fsvec[0] = fsvec[1] = fsvec[2] = fsvec[3] = 0.; fsvec[2] = 2.; // NT fixed test - for(j=0;j<3;j++) fsvec[j] = vverts[a].co[j]; - + for(j=0;j<3;j++) fsvec[j] = velarray[3*a + j]; + + /* (bad) HACK insert average velocity if none is there (see previous comment) */ + if((fsvec[0] == 0.0) && (fsvec[1] == 0.0) && (fsvec[2] == 0.0)) + { + fsvec[0] = avgvel[0]; + fsvec[1] = avgvel[1]; + fsvec[2] = avgvel[2]; + } + // transform (=rotate) to cam space camco[0]= imat[0][0]*fsvec[0] + imat[0][1]*fsvec[1] + imat[0][2]*fsvec[2]; camco[1]= imat[1][0]*fsvec[0] + imat[1][1]*fsvec[1] + imat[1][2]*fsvec[2]; @@ -4552,7 +5240,6 @@ static void copy_dbase_object_vectors(Render *re, ListBase *lb) ObjectInstanceRen *obi, *obilb; ObjectRen *obr; VertRen *ver= NULL; - StrandRen *strand= NULL; float *vec, ho[4], winmat[4][4]; int a, totvector; @@ -4563,7 +5250,7 @@ static void copy_dbase_object_vectors(Render *re, ListBase *lb) memcpy(obilb, obi, sizeof(ObjectInstanceRen)); BLI_addtail(lb, obilb); - obilb->totvector= totvector= obr->totvert + obr->totstrand; + obilb->totvector= totvector= obr->totvert; if(totvector > 0) { vec= obilb->vectors= MEM_mallocN(2*sizeof(float)*totvector, "vector array"); @@ -4580,14 +5267,6 @@ static void copy_dbase_object_vectors(Render *re, ListBase *lb) projectvert(ver->co, winmat, ho); speedvector_project(NULL, vec, ver->co, ho); } - - for(a=0; a<obr->totstrand; a++, vec+=2) { - if((a & 255)==0) strand= obr->strandnodes[a>>8].strand; - else strand++; - - projectvert(strand->vert->co, winmat, ho); - speedvector_project(NULL, vec, strand->vert->co, ho); - } } } } @@ -4605,8 +5284,10 @@ static void free_dbase_object_vectors(ListBase *lb) void RE_Database_FromScene_Vectors(Render *re, Scene *sce) { ObjectInstanceRen *obi, *oldobi; + StrandSurface *mesh; ListBase *table; ListBase oldtable= {NULL, NULL}, newtable= {NULL, NULL}; + ListBase strandsurface; int step; re->i.infostr= "Calculating previous vectors"; @@ -4621,7 +5302,10 @@ void RE_Database_FromScene_Vectors(Render *re, Scene *sce) copy_dbase_object_vectors(re, &oldtable); /* free dbase and make the future one */ + strandsurface= re->strandsurface; + memset(&re->strandsurface, 0, sizeof(ListBase)); RE_Database_Free(re); + re->strandsurface= strandsurface; if(!re->test_break()) { /* creates entire dbase */ @@ -4633,7 +5317,10 @@ void RE_Database_FromScene_Vectors(Render *re, Scene *sce) copy_dbase_object_vectors(re, &newtable); /* free dbase and make the real one */ + strandsurface= re->strandsurface; + memset(&re->strandsurface, 0, sizeof(ListBase)); RE_Database_Free(re); + re->strandsurface= strandsurface; if(!re->test_break()) RE_Database_FromScene(re, sce, 1); @@ -4647,10 +5334,14 @@ void RE_Database_FromScene_Vectors(Render *re, Scene *sce) table= &oldtable; oldobi= table->first; - for(obi= re->instancetable.first; obi && oldobi; obi= obi->next, oldobi= oldobi->next) { + for(obi= re->instancetable.first; obi && oldobi; obi= obi->next) { int ok= 1; + FluidsimModifierData *fluidmd; + + if(!(obi->obr->flag & R_NEED_VECTORS)) + continue; - obi->totvector= obi->obr->totvert + obi->obr->totstrand; + obi->totvector= obi->obr->totvert; /* find matching object in old table */ if(oldobi->ob!=obi->ob || oldobi->par!=obi->par || oldobi->index!=obi->index || oldobi->psysindex!=obi->psysindex) { @@ -4669,25 +5360,38 @@ void RE_Database_FromScene_Vectors(Render *re, Scene *sce) } // NT check for fluidsim special treatment - if((obi->ob->fluidsimFlag & OB_FLUIDSIM_ENABLE) && (obi->ob->fluidsimSettings->type & OB_FLUIDSIM_DOMAIN)) { + fluidmd = (FluidsimModifierData *)modifiers_findByType(obi->ob, eModifierType_Fluidsim); + if(fluidmd && fluidmd->fss && (fluidmd->fss->type & OB_FLUIDSIM_DOMAIN)) { // use preloaded per vertex simulation data , only does calculation for step=1 // NOTE/FIXME - velocities and meshes loaded unnecessarily often during the database_fromscene_vectors calls... load_fluidsimspeedvectors(re, obi, oldobi->vectors, step); - } else { + } + else { /* check if both have same amounts of vertices */ - if(obi->totvector!=oldobi->totvector) { + if(obi->totvector==oldobi->totvector) + calculate_speedvectors(re, obi, oldobi->vectors, step); + else printf("Warning: object %s has different amount of vertices or strands on other frame\n", obi->ob->id.name+2); - continue; - } - - calculate_speedvectors(re, obi, oldobi->vectors, step); } // not fluidsim + + oldobi= oldobi->next; } } } free_dbase_object_vectors(&oldtable); free_dbase_object_vectors(&newtable); + + for(mesh=re->strandsurface.first; mesh; mesh=mesh->next) { + if(mesh->prevco) { + MEM_freeN(mesh->prevco); + mesh->prevco= NULL; + } + if(mesh->nextco) { + MEM_freeN(mesh->nextco); + mesh->nextco= NULL; + } + } re->i.infostr= NULL; re->stats_draw(&re->i); @@ -4705,6 +5409,8 @@ void RE_Database_FromScene_Vectors(Render *re, Scene *sce) RE_BAKE_NORMALS:for baking, no lamps and only selected objects RE_BAKE_AO: for baking, no lamps, but all objects RE_BAKE_TEXTURE:for baking, no lamps, only selected objects + RE_BAKE_DISPLACEMENT:for baking, no lamps, only selected objects + RE_BAKE_SHADOW: for baking, only shadows, but all objects */ void RE_Database_Baking(Render *re, Scene *scene, int type, Object *actob) { @@ -4716,21 +5422,31 @@ void RE_Database_Baking(Render *re, Scene *scene, int type, Object *actob) /* renderdata setup and exceptions */ re->r= scene->r; - re->r.mode &= ~R_OSA; + + RE_init_threadcount(re); + re->flag |= R_GLOB_NOPUNOFLIP; + re->flag |= R_BAKING; re->excludeob= actob; + if(type == RE_BAKE_LIGHT) + re->flag |= R_SKIP_MULTIRES; + if(actob) + re->flag |= R_BAKE_TRACE; if(type==RE_BAKE_NORMALS && re->r.bake_normal_space==R_BAKE_SPACE_TANGENT) re->flag |= R_NEED_TANGENT; - if(!actob && ELEM3(type, RE_BAKE_LIGHT, RE_BAKE_NORMALS, RE_BAKE_TEXTURE)) { + if(!actob && ELEM4(type, RE_BAKE_LIGHT, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_DISPLACEMENT)) { re->r.mode &= ~R_SHADOW; re->r.mode &= ~R_RAYTRACE; } + if(!actob && (type==RE_BAKE_SHADOW)) { + re->r.mode |= R_SHADOW; + } + /* setup render stuff */ - if(type!=RE_BAKE_LIGHT) - re->memArena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); + re->memArena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0; re->lights.first= re->lights.last= NULL; @@ -4752,11 +5468,12 @@ void RE_Database_Baking(Render *re, Scene *scene, int type, Object *actob) } init_render_world(re); /* do first, because of ambient. also requires re->osa set correct */ - if(re->wrld.mode & WO_AMB_OCC) { - if (re->wrld.ao_samp_method == WO_AOSAMP_HAMMERSLEY) - init_render_hammersley(re); - else if (re->wrld.ao_samp_method == WO_AOSAMP_CONSTANT) - init_ao_sphere(&re->wrld); + if(re->r.mode & R_RAYTRACE) { + init_render_qmcsampler(re); + + if(re->wrld.mode & WO_AMB_OCC) + if (re->wrld.ao_samp_method == WO_AOSAMP_CONSTANT) + init_ao_sphere(&re->wrld); } /* still bad... doing all */ @@ -4765,8 +5482,8 @@ void RE_Database_Baking(Render *re, Scene *scene, int type, Object *actob) set_node_shader_lamp_loop(shade_material_loop); /* MAKE RENDER DATA */ - nolamps= !ELEM(type, RE_BAKE_LIGHT, RE_BAKE_ALL); - onlyselected= ELEM(type, RE_BAKE_NORMALS, RE_BAKE_TEXTURE); + nolamps= !ELEM3(type, RE_BAKE_LIGHT, RE_BAKE_ALL, RE_BAKE_SHADOW); + onlyselected= ELEM3(type, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_DISPLACEMENT); database_init_objects(re, lay, nolamps, onlyselected, actob, 0); @@ -4781,6 +5498,12 @@ void RE_Database_Baking(Render *re, Scene *scene, int type, Object *actob) if(!re->test_break()) if(re->r.mode & R_RAYTRACE) makeraytree(re); + + /* occlusion */ + if((re->wrld.mode & WO_AMB_OCC) && !re->test_break()) + if(re->wrld.ao_gather_method == WO_AOGATHER_APPROX) + if(re->r.mode & R_SHADOW) + make_occ_tree(re); } /* ------------------------------------------------------------------------- */ @@ -4813,7 +5536,7 @@ void RE_make_sticky(void) } re= RE_NewRender("_make sticky_"); - RE_InitState(re, &G.scene->r, G.scene->r.xsch, G.scene->r.ysch, NULL); + RE_InitState(re, NULL, &G.scene->r, G.scene->r.xsch, G.scene->r.ysch, NULL); /* use renderdata and camera to set viewplane */ RE_SetCamera(re, G.scene->camera); diff --git a/source/blender/render/intern/source/envmap.c b/source/blender/render/intern/source/envmap.c index b1b907aebb6..13fa9b17b71 100644 --- a/source/blender/render/intern/source/envmap.c +++ b/source/blender/render/intern/source/envmap.c @@ -127,7 +127,7 @@ static Render *envmap_render_copy(Render *re, EnvMap *env) envre->r.size= 100; envre->r.yasp= envre->r.xasp= 1; - RE_InitState(envre, &envre->r, cuberes, cuberes, NULL); + RE_InitState(envre, NULL, &envre->r, cuberes, cuberes, NULL); envre->scene= re->scene; /* unsure about this... */ /* view stuff in env render */ @@ -150,14 +150,15 @@ static Render *envmap_render_copy(Render *re, EnvMap *env) envre->tothalo= re->tothalo; envre->totstrand= re->totstrand; envre->totlamp= re->totlamp; + envre->sortedhalos= re->sortedhalos; envre->lights= re->lights; envre->objecttable= re->objecttable; - envre->strandbuckets= re->strandbuckets; envre->customdata_names= re->customdata_names; envre->raytree= re->raytree; envre->totinstance= re->totinstance; envre->instancetable= re->instancetable; envre->objectinstance= re->objectinstance; + envre->qmcsamplers= re->qmcsamplers; return envre; } @@ -171,13 +172,14 @@ static void envmap_free_render_copy(Render *envre) envre->totstrand= 0; envre->totlamp= 0; envre->totinstance= 0; + envre->sortedhalos= NULL; envre->lights.first= envre->lights.last= NULL; envre->objecttable.first= envre->objecttable.last= NULL; - envre->strandbuckets= NULL; envre->customdata_names.first= envre->customdata_names.last= NULL; envre->raytree= NULL; envre->instancetable.first= envre->instancetable.last= NULL; envre->objectinstance= NULL; + envre->qmcsamplers= NULL; RE_FreeRender(envre); } @@ -223,7 +225,7 @@ static void env_rotate_scene(Render *re, float mat[][4], int mode) ObjectInstanceRen *obi; LampRen *lar = NULL; HaloRen *har = NULL; - float imat[3][3], pmat[4][4], smat[4][4], tmat[4][4], cmat[3][3]; + float imat[3][3], pmat[4][4], smat[4][4], tmat[4][4], cmat[3][3], tmpmat[4][4]; int a; if(mode==0) { @@ -237,15 +239,18 @@ static void env_rotate_scene(Render *re, float mat[][4], int mode) for(obi=re->instancetable.first; obi; obi=obi->next) { /* append or set matrix depending on dupli */ - if(obi->flag & R_DUPLI_TRANSFORMED) - Mat4MulMat4(obi->mat, tmat, obi->mat); + if(obi->flag & R_DUPLI_TRANSFORMED) { + Mat4CpyMat4(tmpmat, obi->mat); + Mat4MulMat4(obi->mat, tmpmat, tmat); + } else if(mode==1) Mat4CpyMat4(obi->mat, tmat); else Mat4One(obi->mat); Mat3CpyMat4(cmat, obi->mat); - Mat3Inv(obi->imat, cmat); + Mat3Inv(obi->nmat, cmat); + Mat3Transp(obi->nmat); /* indicate the renderer has to use transform matrices */ if(mode==0) @@ -313,12 +318,13 @@ static void env_layerflags(Render *re, unsigned int notlay) notlay= ~notlay; for(obr=re->objecttable.first; obr; obr=obr->next) { - for(a=0; a<obr->totvlak; a++) { - if((a & 255)==0) vlr= obr->vlaknodes[a>>8].vlak; - else vlr++; + if((obr->lay & notlay)==0) { + for(a=0; a<obr->totvlak; a++) { + if((a & 255)==0) vlr= obr->vlaknodes[a>>8].vlak; + else vlr++; - if((vlr->lay & notlay)==0) vlr->flag |= R_HIDDEN; + } } } } @@ -433,12 +439,19 @@ static void render_envmap(Render *re, EnvMap *env) if(re->test_break()==0) { RenderLayer *rl= envre->result->layers.first; + int y; + char *alpha; ibuf= IMB_allocImBuf(envre->rectx, envre->recty, 24, IB_rect, 0); ibuf->rect_float= rl->rectf; IMB_rect_from_float(ibuf); ibuf->rect_float= NULL; - + + /* envmap renders without alpha */ + alpha= ((char *)ibuf->rect)+3; + for(y= ibuf->x*ibuf->y - 1; y>=0; y--, alpha+=4) + *alpha= 255; + env->cube[part]= ibuf; } @@ -715,7 +728,7 @@ int envmaptex(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, TexRe fac= (texres->ta+texr1.ta+texr2.ta); if(fac!=0.0) { fac= 1.0/fac; - + texres->tr= fac*(texres->ta*texres->tr + texr1.ta*texr1.tr + texr2.ta*texr2.tr ); texres->tg= fac*(texres->ta*texres->tg + texr1.ta*texr1.tg + texr2.ta*texr2.tg ); texres->tb= fac*(texres->ta*texres->tb + texr1.ta*texr1.tb + texr2.ta*texr2.tb ); diff --git a/source/blender/render/intern/source/imagetexture.c b/source/blender/render/intern/source/imagetexture.c index 23b5e597070..b9a2acb8b1c 100644 --- a/source/blender/render/intern/source/imagetexture.c +++ b/source/blender/render/intern/source/imagetexture.c @@ -32,6 +32,7 @@ #include <string.h> #include <fcntl.h> #include <math.h> +#include <float.h> #ifndef WIN32 #include <unistd.h> #else @@ -93,10 +94,10 @@ static void ibuf_get_color(float *col, struct ImBuf *ibuf, int x, int y) else { char *rect = (char *)( ibuf->rect+ ofs); - col[0] = ((float)rect[0])/255.0f; - col[1] = ((float)rect[1])/255.0f; - col[2] = ((float)rect[2])/255.0f; - col[3] = ((float)rect[3])/255.0f; + col[0] = ((float)rect[0])*(1.0f/255.0f); + col[1] = ((float)rect[1])*(1.0f/255.0f); + col[2] = ((float)rect[2])*(1.0f/255.0f); + col[3] = ((float)rect[3])*(1.0f/255.0f); } } @@ -245,7 +246,7 @@ int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, TexResult *texre if(tex->flag & TEX_NEGALPHA) texres->ta= 1.0f-texres->ta; /* de-premul, this is being premulled in shade_input_do_shade() */ - if(texres->ta!=1.0f && texres->ta!=0.0f) { + if(texres->ta!=1.0f && texres->ta>FLT_EPSILON) { fx= 1.0f/texres->ta; texres->tr*= fx; texres->tg*= fx; @@ -576,10 +577,10 @@ static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float max if(texres->talpha==0) texres->ta= 1.0; if(alphaclip!=1.0) { - /* this is for later investigation, premul or not? */ - /* texres->tr*= alphaclip; */ - /* texres->tg*= alphaclip; */ - /* texres->tb*= alphaclip; */ + /* premul it all */ + texres->tr*= alphaclip; + texres->tg*= alphaclip; + texres->tb*= alphaclip; texres->ta*= alphaclip; } } @@ -615,6 +616,7 @@ void ibuf_sample(ImBuf *ibuf, float fx, float fy, float dx, float dy, float *res return; } + memset(&texres, 0, sizeof(texres)); boxsample(ibuf, fx, fy, fx+dx, fy+dy, &texres, 0, 1); result[0]= texres.tr; result[1]= texres.tg; @@ -699,10 +701,22 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *dxt, f maxy= MAX3(dxt[1],dyt[1],dxt[1]+dyt[1] ); /* tex_sharper has been removed */ - minx= tex->filtersize*(maxx-minx)/2.0f; - miny= tex->filtersize*(maxy-miny)/2.0f; + minx= (maxx-minx)/2.0f; + miny= (maxy-miny)/2.0f; - if(tex->filtersize!=1.0f) { + if(tex->imaflag & TEX_FILTER_MIN) { + /* make sure the filtersize is minimal in pixels (normal, ref map can have miniature pixel dx/dy) */ + float addval= (0.5f * tex->filtersize) / (float) MIN2(ibuf->x, ibuf->y); + + if(addval > minx) + minx= addval; + if(addval > miny) + miny= addval; + } + else if(tex->filtersize!=1.0f) { + minx*= tex->filtersize; + miny*= tex->filtersize; + dxt[0]*= tex->filtersize; dxt[1]*= tex->filtersize; dyt[0]*= tex->filtersize; @@ -977,7 +991,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *dxt, f } /* de-premul, this is being premulled in shade_input_do_shade() */ - if(texres->ta!=1.0f && texres->ta!=0.0f) { + if(texres->ta!=1.0f && texres->ta>FLT_EPSILON) { fx= 1.0f/texres->ta; texres->tr*= fx; texres->tg*= fx; diff --git a/source/blender/render/intern/source/initrender.c b/source/blender/render/intern/source/initrender.c index 3ea8c6edffa..40c0edb6e5f 100644 --- a/source/blender/render/intern/source/initrender.c +++ b/source/blender/render/intern/source/initrender.c @@ -257,7 +257,7 @@ void make_sample_tables(Render *re) { static int firsttime= 1; SampleTables *st; - float flweight[32], fmask[256]; + float flweight[32]; float weight[32], totw, val, *fpx1, *fpx2, *fpy1, *fpy2, *m3, *m4; int i, j, a; @@ -300,11 +300,6 @@ void make_sample_tables(Render *re) st->centLut[a]= -0.45+((float)a)/16.0; } - val= 1.0/((float)re->osa); - for(a=0; a<256; a++) { - fmask[a]= ((float)st->cmask[a])*val; - } - /* calculate totw */ totw= 0.0; for(j= -1; j<2; j++) { @@ -459,7 +454,10 @@ void RE_SetCamera(Render *re, Object *camera) if(cam->type==CAM_ORTHO) re->r.mode |= R_ORTHO; - /* solve this too... all time depending stuff is in convertblender.c? */ + /* solve this too... all time depending stuff is in convertblender.c? + * Need to update the camera early because it's used for projection matrices + * and other stuff BEFORE the animation update loop is done + * */ if(cam->ipo) { calc_ipo(cam->ipo, frame_to_float(re->r.cfra)); execute_ipo(&cam->id, cam->ipo); @@ -603,24 +601,20 @@ void initparts(Render *re) /* mininum part size, but for exr tile saving it was checked already */ if(!(re->r.scemode & R_EXR_TILE_FILE)) { if(re->r.mode & R_PANORAMA) { - if(re->rectx/xparts < 8) + if(ceil(re->rectx/(float)xparts) < 8) xparts= 1 + re->rectx/8; } else - if(re->rectx/xparts < 64) + if(ceil(re->rectx/(float)xparts) < 64) xparts= 1 + re->rectx/64; - if(re->recty/yparts < 64) + if(ceil(re->recty/(float)yparts) < 64) yparts= 1 + re->recty/64; } /* part size */ - partx= re->rectx/xparts; - party= re->recty/yparts; - - /* if remainder pixel, add one, then parts are more equal in size for large panoramas */ - if(re->rectx % partx) - partx++; + partx= ceil(re->rectx/(float)xparts); + party= ceil(re->recty/(float)yparts); re->xparts= xparts; re->yparts= yparts; diff --git a/source/blender/render/intern/source/occlusion.c b/source/blender/render/intern/source/occlusion.c new file mode 100644 index 00000000000..d2d2cf3fb77 --- /dev/null +++ b/source/blender/render/intern/source/occlusion.c @@ -0,0 +1,1763 @@ +/* + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_material_types.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_memarena.h" +#include "BLI_threads.h" + +#include "BKE_global.h" +#include "BKE_scene.h" +#include "BKE_utildefines.h" + +#include "RE_shader_ext.h" + +/* local includes */ +#include "occlusion.h" +#include "render_types.h" +#include "rendercore.h" +#include "renderdatabase.h" +#include "pixelshading.h" +#include "shading.h" +#include "zbuf.h" + +/* ------------------------- Declarations --------------------------- */ + +#define INVALID_INDEX ((int)(~0)) +#define INVPI 0.31830988618379069f +#define TOTCHILD 8 +#define CACHE_STEP 3 + +typedef struct OcclusionCacheSample { + float co[3], n[3], col[3], intensity, dist2; + int x, y, filled; +} OcclusionCacheSample; + +typedef struct OcclusionCache { + OcclusionCacheSample *sample; + int x, y, w, h, step; +} OcclusionCache; + +typedef struct OccFace { + int obi; + int facenr; +} OccFace; + +typedef struct OccNode { + float co[3], area; + float sh[9], dco; + float occlusion; + int childflag; + union { + //OccFace face; + int face; + struct OccNode *node; + } child[TOTCHILD]; +} OccNode; + +typedef struct OcclusionTree { + MemArena *arena; + + float (*co)[3]; /* temporary during build */ + + OccFace *face; /* instance and face indices */ + float *occlusion; /* occlusion for faces */ + + OccNode *root; + + OccNode **stack[BLENDER_MAX_THREADS]; + int maxdepth; + + int totface; + + float error; + float distfac; + + int dothreadedbuild; + int totbuildthread; + + OcclusionCache *cache; +} OcclusionTree; + +typedef struct OcclusionThread { + Render *re; + StrandSurface *mesh; + float (*facecol)[3]; + int begin, end; + int thread; +} OcclusionThread; + +typedef struct OcclusionBuildThread { + OcclusionTree *tree; + int begin, end, depth; + OccNode *node; +} OcclusionBuildThread; + +/* ------------------------- Shading --------------------------- */ + +extern Render R; // meh + +#if 0 +static void occ_shade(ShadeSample *ssamp, ObjectInstanceRen *obi, VlakRen *vlr, float *rad) +{ + ShadeInput *shi= ssamp->shi; + ShadeResult *shr= ssamp->shr; + float l, u, v, *v1, *v2, *v3; + + /* init */ + if(vlr->v4) { + shi->u= u= 0.5f; + shi->v= v= 0.5f; + } + else { + shi->u= u= 1.0f/3.0f; + shi->v= v= 1.0f/3.0f; + } + + /* setup render coordinates */ + 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]; + + shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2); + + /* set up view vector */ + VECCOPY(shi->view, shi->co); + Normalize(shi->view); + + /* cache for shadow */ + shi->samplenr++; + + shi->xs= 0; // TODO + shi->ys= 0; + + shade_input_set_normals(shi); + + /* no normal flip */ + 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) { + VecMulf(shi->vn, -1.0f); + VecMulf(shi->vno, -1.0f); + } + + /* init material vars */ + // note, keep this synced with render_types.h + memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); + shi->har= shi->mat->har; + + /* render */ + shade_input_set_shade_texco(shi); + shade_material_loop(shi, shr); /* todo: nodes */ + + VECCOPY(rad, shr->combined); +} + +static void occ_build_shade(Render *re, OcclusionTree *tree) +{ + ShadeSample ssamp; + ObjectInstanceRen *obi; + VlakRen *vlr; + int a; + + R= *re; + + /* setup shade sample with correct passes */ + memset(&ssamp, 0, sizeof(ShadeSample)); + ssamp.shi[0].lay= re->scene->lay; + ssamp.shi[0].passflag= SCE_PASS_DIFFUSE|SCE_PASS_RGBA; + ssamp.shi[0].combinedflag= ~(SCE_PASS_SPEC); + ssamp.tot= 1; + + for(a=0; a<tree->totface; a++) { + obi= &R.objectinstance[tree->face[a].obi]; + vlr= RE_findOrAddVlak(obi->obr, tree->face[a].vlr); + + occ_shade(&ssamp, obi, vlr, tree->rad[a]); + } +} +#endif + +/* ------------------------- Spherical Harmonics --------------------------- */ + +/* Use 2nd order SH => 9 coefficients, stored in this order: + 0 = (0,0), + 1 = (1,-1), 2 = (1,0), 3 = (1,1), + 4 = (2,-2), 5 = (2,-1), 6 = (2,0), 7 = (2,1), 8 = (2,2) */ + +static void sh_copy(float *shresult, float *sh) +{ + memcpy(shresult, sh, sizeof(float)*9); +} + +static void sh_mul(float *sh, float f) +{ + int i; + + for(i=0; i<9; i++) + sh[i] *= f; +} + +static void sh_add(float *shresult, float *sh1, float *sh2) +{ + int i; + + for(i=0; i<9; i++) + shresult[i]= sh1[i] + sh2[i]; +} + +static void sh_from_disc(float *n, float area, float *shresult) +{ + /* See formula (3) in: + "An Efficient Representation for Irradiance Environment Maps" */ + float sh[9], x, y, z; + + x= n[0]; + y= n[1]; + z= n[2]; + + sh[0]= 0.282095f; + + sh[1]= 0.488603f*y; + sh[2]= 0.488603f*z; + sh[3]= 0.488603f*x; + + sh[4]= 1.092548f*x*y; + sh[5]= 1.092548f*y*z; + sh[6]= 0.315392f*(3.0f*z*z - 1.0f); + sh[7]= 1.092548f*x*z; + sh[8]= 0.546274f*(x*x - y*y); + + sh_mul(sh, area); + sh_copy(shresult, sh); +} + +static float sh_eval(float *sh, float *v) +{ + /* See formula (13) in: + "An Efficient Representation for Irradiance Environment Maps" */ + static const float c1 = 0.429043f, c2 = 0.511664f, c3 = 0.743125f; + static const float c4 = 0.886227f, c5 = 0.247708f; + float x, y, z, sum; + + x= v[0]; + y= v[1]; + z= v[2]; + + sum= c1*sh[8]*(x*x - y*y); + sum += c3*sh[6]*z*z; + sum += c4*sh[0]; + sum += -c5*sh[6]; + sum += 2.0f*c1*(sh[4]*x*y + sh[7]*x*z + sh[5]*y*z); + sum += 2.0f*c2*(sh[3]*x + sh[1]*y + sh[2]*z); + + return sum; +} + +/* ------------------------------ Building --------------------------------- */ + +static void occ_face(const OccFace *face, float *co, float *normal, float *area) +{ + ObjectInstanceRen *obi; + VlakRen *vlr; + float v1[3], v2[3], v3[3], v4[3]; + + obi= &R.objectinstance[face->obi]; + vlr= RE_findOrAddVlak(obi->obr, face->facenr); + + if(co) { + if(vlr->v4) + VecLerpf(co, vlr->v1->co, vlr->v3->co, 0.5f); + else + CalcCent3f(co, vlr->v1->co, vlr->v2->co, vlr->v3->co); + + if(obi->flag & R_TRANSFORMED) + Mat4MulVecfl(obi->mat, co); + } + + if(normal) { + normal[0]= -vlr->n[0]; + normal[1]= -vlr->n[1]; + normal[2]= -vlr->n[2]; + + if(obi->flag & R_TRANSFORMED) + Mat3MulVecfl(obi->nmat, normal); + } + + if(area) { + VECCOPY(v1, vlr->v1->co); + VECCOPY(v2, vlr->v2->co); + VECCOPY(v3, vlr->v3->co); + if(vlr->v4) VECCOPY(v4, vlr->v4->co); + + if(obi->flag & R_TRANSFORMED) { + Mat4MulVecfl(obi->mat, v1); + Mat4MulVecfl(obi->mat, v2); + Mat4MulVecfl(obi->mat, v3); + if(vlr->v4) Mat4MulVecfl(obi->mat, v4); + } + + /* todo: correct area for instances */ + if(vlr->v4) + *area= AreaQ3Dfl(v1, v2, v3, v4); + else + *area= AreaT3Dfl(v1, v2, v3); + } +} + +static void occ_sum_occlusion(OcclusionTree *tree, OccNode *node) +{ + OccNode *child; + float occ, area, totarea; + int a, b; + + occ= 0.0f; + totarea= 0.0f; + + for(b=0; b<TOTCHILD; b++) { + if(node->childflag & (1<<b)) { + a= node->child[b].face; + occ_face(&tree->face[a], 0, 0, &area); + occ += area*tree->occlusion[a]; + totarea += area; + } + else if(node->child[b].node) { + child= node->child[b].node; + occ_sum_occlusion(tree, child); + + occ += child->area*child->occlusion; + totarea += child->area; + } + } + + if(totarea != 0.0f) + occ /= totarea; + + node->occlusion= occ; +} + +static int occ_find_bbox_axis(OcclusionTree *tree, int begin, int end, float *min, float *max) +{ + float len, maxlen= -1.0f; + int a, axis = 0; + + INIT_MINMAX(min, max); + + for(a=begin; a<end; a++) + DO_MINMAX(tree->co[a], min, max) + + for(a=0; a<3; a++) { + len= max[a] - min[a]; + + if(len > maxlen) { + maxlen= len; + axis= a; + } + } + + return axis; +} + +static void occ_node_from_face(OccFace *face, OccNode *node) +{ + float n[3]; + + occ_face(face, node->co, n, &node->area); + node->dco= 0.0f; + sh_from_disc(n, node->area, node->sh); +} + +static void occ_build_dco(OcclusionTree *tree, OccNode *node, float *co, float *dco) +{ + OccNode *child; + float dist, d[3], nco[3]; + int b; + + for(b=0; b<TOTCHILD; b++) { + if(node->childflag & (1<<b)) { + occ_face(tree->face+node->child[b].face, nco, 0, 0); + } + else if(node->child[b].node) { + child= node->child[b].node; + occ_build_dco(tree, child, co, dco); + VECCOPY(nco, child->co); + } + + VECSUB(d, nco, co); + dist= INPR(d, d); + if(dist > *dco) + *dco= dist; + } +} + +static void occ_build_split(OcclusionTree *tree, int begin, int end, int *split) +{ + float min[3], max[3], mid; + int axis, a, enda; + + /* split in middle of boundbox. this seems faster than median split + * on complex scenes, possibly since it avoids two distant faces to + * be in the same node better? */ + axis= occ_find_bbox_axis(tree, begin, end, min, max); + mid= 0.5f*(min[axis]+max[axis]); + + a= begin; + enda= end; + while(a<enda) { + if(tree->co[a][axis] > mid) { + enda--; + SWAP(OccFace, tree->face[a], tree->face[enda]); + SWAP(float, tree->co[a][0], tree->co[enda][0]); + SWAP(float, tree->co[a][1], tree->co[enda][1]); + SWAP(float, tree->co[a][2], tree->co[enda][2]); + } + else + a++; + } + + *split= enda; +} + +static void occ_build_8_split(OcclusionTree *tree, int begin, int end, int *offset, int *count) +{ + /* split faces into eight groups */ + int b, splitx, splity[2], splitz[4]; + + occ_build_split(tree, begin, end, &splitx); + + /* force split if none found, to deal with degenerate geometry */ + if(splitx == begin || splitx == end) + splitx= (begin+end)/2; + + occ_build_split(tree, begin, splitx, &splity[0]); + occ_build_split(tree, splitx, end, &splity[1]); + + occ_build_split(tree, begin, splity[0], &splitz[0]); + occ_build_split(tree, splity[0], splitx, &splitz[1]); + occ_build_split(tree, splitx, splity[1], &splitz[2]); + occ_build_split(tree, splity[1], end, &splitz[3]); + + offset[0]= begin; + offset[1]= splitz[0]; + offset[2]= splity[0]; + offset[3]= splitz[1]; + offset[4]= splitx; + offset[5]= splitz[2]; + offset[6]= splity[1]; + offset[7]= splitz[3]; + + for(b=0; b<7; b++) + count[b]= offset[b+1] - offset[b]; + count[7]= end - offset[7]; +} + +static void occ_build_recursive(OcclusionTree *tree, OccNode *node, int begin, int end, int depth); + +static void *exec_occ_build(void *data) +{ + OcclusionBuildThread *othread= (OcclusionBuildThread*)data; + + occ_build_recursive(othread->tree, othread->node, othread->begin, othread->end, othread->depth); + + return 0; +} + +static void occ_build_recursive(OcclusionTree *tree, OccNode *node, int begin, int end, int depth) +{ + ListBase threads; + OcclusionBuildThread othreads[BLENDER_MAX_THREADS]; + OccNode *child, tmpnode; + OccFace *face; + int a, b, totthread=0, offset[TOTCHILD], count[TOTCHILD]; + + /* keep track of maximum depth for stack */ + if(depth > tree->maxdepth) + tree->maxdepth= depth; + + /* add a new node */ + node->occlusion= 1.0f; + + /* leaf node with only children */ + if(end - begin <= TOTCHILD) { + for(a=begin, b=0; a<end; a++, b++) { + face= &tree->face[a]; + node->child[b].face= a; + node->childflag |= (1<<b); + } + } + else { + /* order faces */ + occ_build_8_split(tree, begin, end, offset, count); + + if(depth == 1 && tree->dothreadedbuild) + BLI_init_threads(&threads, exec_occ_build, tree->totbuildthread); + + for(b=0; b<TOTCHILD; b++) { + if(count[b] == 0) { + node->child[b].node= NULL; + } + else if(count[b] == 1) { + face= &tree->face[offset[b]]; + node->child[b].face= offset[b]; + node->childflag |= (1<<b); + } + else { + if(tree->dothreadedbuild) + BLI_lock_thread(LOCK_CUSTOM1); + + child= BLI_memarena_alloc(tree->arena, sizeof(OccNode)); + node->child[b].node= child; + + if(tree->dothreadedbuild) + BLI_unlock_thread(LOCK_CUSTOM1); + + if(depth == 1 && tree->dothreadedbuild) { + othreads[totthread].tree= tree; + othreads[totthread].node= child; + othreads[totthread].begin= offset[b]; + othreads[totthread].end= offset[b]+count[b]; + othreads[totthread].depth= depth+1; + BLI_insert_thread(&threads, &othreads[totthread]); + totthread++; + } + else + occ_build_recursive(tree, child, offset[b], offset[b]+count[b], depth+1); + } + } + + if(depth == 1 && tree->dothreadedbuild) + BLI_end_threads(&threads); + } + + /* combine area, position and sh */ + for(b=0; b<TOTCHILD; b++) { + if(node->childflag & (1<<b)) { + child= &tmpnode; + occ_node_from_face(tree->face+node->child[b].face, &tmpnode); + } + else { + child= node->child[b].node; + } + + if(child) { + node->area += child->area; + sh_add(node->sh, node->sh, child->sh); + VECADDFAC(node->co, node->co, child->co, child->area); + } + } + + if(node->area != 0.0f) + VecMulf(node->co, 1.0f/node->area); + + /* compute maximum distance from center */ + node->dco= 0.0f; + occ_build_dco(tree, node, node->co, &node->dco); +} + +static void occ_build_sh_normalize(OccNode *node) +{ + /* normalize spherical harmonics to not include area, so + * we can clamp the dot product and then mutliply by area */ + int b; + + if(node->area != 0.0f) + sh_mul(node->sh, 1.0f/node->area); + + for(b=0; b<TOTCHILD; b++) { + if(node->childflag & (1<<b)); + else if(node->child[b].node) + occ_build_sh_normalize(node->child[b].node); + } +} + +static OcclusionTree *occ_tree_build(Render *re) +{ + OcclusionTree *tree; + ObjectInstanceRen *obi; + ObjectRen *obr; + VlakRen *vlr= NULL; + int a, b, c, totface; + + /* count */ + totface= 0; + for(obi=re->instancetable.first; obi; obi=obi->next) { + obr= obi->obr; + for(a=0; a<obr->totvlak; a++) { + if((a & 255)==0) vlr= obr->vlaknodes[a>>8].vlak; + else vlr++; + + if(vlr->mat->mode & MA_TRACEBLE) + totface++; + } + } + + if(totface == 0) + return NULL; + + tree= MEM_callocN(sizeof(OcclusionTree), "OcclusionTree"); + tree->totface= totface; + + /* parameters */ + tree->error= get_render_aosss_error(&re->r, re->wrld.ao_approx_error); + tree->distfac= (re->wrld.aomode & WO_AODIST)? re->wrld.aodistfac: 0.0f; + + /* allocation */ + tree->arena= BLI_memarena_new(0x8000 * sizeof(OccNode)); + BLI_memarena_use_calloc(tree->arena); + + if(re->wrld.aomode & WO_AOCACHE) + tree->cache= MEM_callocN(sizeof(OcclusionCache)*BLENDER_MAX_THREADS, "OcclusionCache"); + + tree->face= MEM_callocN(sizeof(OccFace)*totface, "OcclusionFace"); + tree->co= MEM_callocN(sizeof(float)*3*totface, "OcclusionCo"); + tree->occlusion= MEM_callocN(sizeof(float)*totface, "OcclusionOcclusion"); + + /* make array of face pointers */ + for(b=0, c=0, obi=re->instancetable.first; obi; obi=obi->next, c++) { + obr= obi->obr; + for(a=0; a<obr->totvlak; a++) { + if((a & 255)==0) vlr= obr->vlaknodes[a>>8].vlak; + else vlr++; + + if(vlr->mat->mode & MA_TRACEBLE) { + tree->face[b].obi= c; + tree->face[b].facenr= a; + tree->occlusion[b]= 1.0f; + occ_face(&tree->face[b], tree->co[b], NULL, NULL); + b++; + } + } + } + + /* threads */ + tree->totbuildthread= (re->r.threads > 1 && totface > 10000)? 8: 1; + tree->dothreadedbuild= (tree->totbuildthread > 1); + + /* recurse */ + tree->root= BLI_memarena_alloc(tree->arena, sizeof(OccNode)); + occ_build_recursive(tree, tree->root, 0, totface, 1); + +#if 0 + if(tree->doindirect) { + occ_build_shade(re, tree); + occ_sum_occlusion(tree, tree->root); + } +#endif + + MEM_freeN(tree->co); + tree->co= NULL; + + occ_build_sh_normalize(tree->root); + + for(a=0; a<BLENDER_MAX_THREADS; a++) + tree->stack[a]= MEM_callocN(sizeof(OccNode)*TOTCHILD*(tree->maxdepth+1), "OccStack"); + + return tree; +} + +static void occ_free_tree(OcclusionTree *tree) +{ + int a; + + if(tree) { + if(tree->arena) BLI_memarena_free(tree->arena); + for(a=0; a<BLENDER_MAX_THREADS; a++) + if(tree->stack[a]) + MEM_freeN(tree->stack[a]); + if(tree->occlusion) MEM_freeN(tree->occlusion); + if(tree->face) MEM_freeN(tree->face); + if(tree->cache) MEM_freeN(tree->cache); + MEM_freeN(tree); + } +} + +/* ------------------------- Traversal --------------------------- */ + +static float occ_solid_angle(OccNode *node, float *v, float d2, float invd2, float *receivenormal) +{ + float dotreceive, dotemit; + float ev[3]; + + ev[0]= -v[0]*invd2; + ev[1]= -v[1]*invd2; + ev[2]= -v[2]*invd2; + dotemit= sh_eval(node->sh, ev); + dotreceive= INPR(receivenormal, v)*invd2; + + CLAMP(dotemit, 0.0f, 1.0f); + CLAMP(dotreceive, 0.0f, 1.0f); + + return ((node->area*dotemit*dotreceive)/(d2 + node->area*INVPI))*INVPI; +} + +static void VecAddDir(float *result, float *v1, float *v2, float fac) +{ + result[0]= v1[0] + fac*(v2[0] - v1[0]); + result[1]= v1[1] + fac*(v2[1] - v1[1]); + result[2]= v1[2] + fac*(v2[2] - v1[2]); +} + +static int occ_visible_quad(float *p, float *n, float *v0, float *v1, float *v2, float *q0, float *q1, float *q2, float *q3) +{ + static const float epsilon = 1e-6f; + float c, sd[3]; + + c= INPR(n, p); + + /* signed distances from the vertices to the plane. */ + sd[0]= INPR(n, v0) - c; + sd[1]= INPR(n, v1) - c; + sd[2]= INPR(n, v2) - c; + + if(fabs(sd[0]) < epsilon) sd[0] = 0.0f; + if(fabs(sd[1]) < epsilon) sd[1] = 0.0f; + if(fabs(sd[2]) < epsilon) sd[2] = 0.0f; + + if(sd[0] > 0) { + if(sd[1] > 0) { + if(sd[2] > 0) { + // +++ + VECCOPY(q0, v0); + VECCOPY(q1, v1); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + else if(sd[2] < 0) { + // ++- + VECCOPY(q0, v0); + VECCOPY(q1, v1); + VecAddDir(q2, v1, v2, (sd[1]/(sd[1]-sd[2]))); + VecAddDir(q3, v0, v2, (sd[0]/(sd[0]-sd[2]))); + } + else { + // ++0 + VECCOPY(q0, v0); + VECCOPY(q1, v1); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + } + else if(sd[1] < 0) { + if(sd[2] > 0) { + // +-+ + VECCOPY(q0, v0); + VecAddDir(q1, v0, v1, (sd[0]/(sd[0]-sd[1]))); + VecAddDir(q2, v1, v2, (sd[1]/(sd[1]-sd[2]))); + VECCOPY(q3, v2); + } + else if(sd[2] < 0) { + // +-- + VECCOPY(q0, v0); + VecAddDir(q1, v0, v1, (sd[0]/(sd[0]-sd[1]))); + VecAddDir(q2, v0, v2, (sd[0]/(sd[0]-sd[2]))); + VECCOPY(q3, q2); + } + else { + // +-0 + VECCOPY(q0, v0); + VecAddDir(q1, v0, v1, (sd[0]/(sd[0]-sd[1]))); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + } + else { + if(sd[2] > 0) { + // +0+ + VECCOPY(q0, v0); + VECCOPY(q1, v1); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + else if(sd[2] < 0) { + // +0- + VECCOPY(q0, v0); + VECCOPY(q1, v1); + VecAddDir(q2, v0, v2, (sd[0]/(sd[0]-sd[2]))); + VECCOPY(q3, q2); + } + else { + // +00 + VECCOPY(q0, v0); + VECCOPY(q1, v1); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + } + } + else if(sd[0] < 0) { + if(sd[1] > 0) { + if(sd[2] > 0) { + // -++ + VecAddDir(q0, v0, v1, (sd[0]/(sd[0]-sd[1]))); + VECCOPY(q1, v1); + VECCOPY(q2, v2); + VecAddDir(q3, v0, v2, (sd[0]/(sd[0]-sd[2]))); + } + else if(sd[2] < 0) { + // -+- + VecAddDir(q0, v0, v1, (sd[0]/(sd[0]-sd[1]))); + VECCOPY(q1, v1); + VecAddDir(q2, v1, v2, (sd[1]/(sd[1]-sd[2]))); + VECCOPY(q3, q2); + } + else { + // -+0 + VecAddDir(q0, v0, v1, (sd[0]/(sd[0]-sd[1]))); + VECCOPY(q1, v1); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + } + else if(sd[1] < 0) { + if(sd[2] > 0) { + // --+ + VecAddDir(q0, v0, v2, (sd[0]/(sd[0]-sd[2]))); + VecAddDir(q1, v1, v2, (sd[1]/(sd[1]-sd[2]))); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + else if(sd[2] < 0) { + // --- + return 0; + } + else { + // --0 + return 0; + } + } + else { + if(sd[2] > 0) { + // -0+ + VecAddDir(q0, v0, v2, (sd[0]/(sd[0]-sd[2]))); + VECCOPY(q1, v1); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + else if(sd[2] < 0) { + // -0- + return 0; + } + else { + // -00 + return 0; + } + } + } + else { + if(sd[1] > 0) { + if(sd[2] > 0) { + // 0++ + VECCOPY(q0, v0); + VECCOPY(q1, v1); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + else if(sd[2] < 0) { + // 0+- + VECCOPY(q0, v0); + VECCOPY(q1, v1); + VecAddDir(q2, v1, v2, (sd[1]/(sd[1]-sd[2]))); + VECCOPY(q3, q2); + } + else { + // 0+0 + VECCOPY(q0, v0); + VECCOPY(q1, v1); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + } + else if(sd[1] < 0) { + if(sd[2] > 0) { + // 0-+ + VECCOPY(q0, v0); + VecAddDir(q1, v1, v2, (sd[1]/(sd[1]-sd[2]))); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + else if(sd[2] < 0) { + // 0-- + return 0; + } + else { + // 0-0 + return 0; + } + } + else { + if(sd[2] > 0) { + // 00+ + VECCOPY(q0, v0); + VECCOPY(q1, v1); + VECCOPY(q2, v2); + VECCOPY(q3, q2); + } + else if(sd[2] < 0) { + // 00- + return 0; + } + else { + // 000 + return 0; + } + } + } + + return 1; +} + +/* altivec optimization, this works, but is unused */ + +#if 0 +#include <Accelerate/Accelerate.h> + +typedef union { + vFloat v; + float f[4]; +} vFloatResult; + +static vFloat vec_splat_float(float val) +{ + return (vFloat){val, val, val, val}; +} + +static float occ_quad_form_factor(float *p, float *n, float *q0, float *q1, float *q2, float *q3) +{ + vFloat vcos, rlen, vrx, vry, vrz, vsrx, vsry, vsrz, gx, gy, gz, vangle; + vUInt8 rotate = (vUInt8){4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3}; + vFloatResult vresult; + float result; + + /* compute r* */ + vrx = (vFloat){q0[0], q1[0], q2[0], q3[0]} - vec_splat_float(p[0]); + vry = (vFloat){q0[1], q1[1], q2[1], q3[1]} - vec_splat_float(p[1]); + vrz = (vFloat){q0[2], q1[2], q2[2], q3[2]} - vec_splat_float(p[2]); + + /* normalize r* */ + rlen = vec_rsqrte(vrx*vrx + vry*vry + vrz*vrz + vec_splat_float(1e-16f)); + vrx = vrx*rlen; + vry = vry*rlen; + vrz = vrz*rlen; + + /* rotate r* for cross and dot */ + vsrx= vec_perm(vrx, vrx, rotate); + vsry= vec_perm(vry, vry, rotate); + vsrz= vec_perm(vrz, vrz, rotate); + + /* cross product */ + gx = vsry*vrz - vsrz*vry; + gy = vsrz*vrx - vsrx*vrz; + gz = vsrx*vry - vsry*vrx; + + /* normalize */ + rlen = vec_rsqrte(gx*gx + gy*gy + gz*gz + vec_splat_float(1e-16f)); + gx = gx*rlen; + gy = gy*rlen; + gz = gz*rlen; + + /* angle */ + vcos = vrx*vsrx + vry*vsry + vrz*vsrz; + vcos= vec_max(vec_min(vcos, vec_splat_float(1.0f)), vec_splat_float(-1.0f)); + vangle= vacosf(vcos); + + /* dot */ + vresult.v = (vec_splat_float(n[0])*gx + + vec_splat_float(n[1])*gy + + vec_splat_float(n[2])*gz)*vangle; + + result= (vresult.f[0] + vresult.f[1] + vresult.f[2] + vresult.f[3])*(0.5f/(float)M_PI); + result= MAX2(result, 0.0f); + + return result; +} + +#endif + +/* SSE optimization, acos code doesn't work */ + +#if 0 + +#include <xmmintrin.h> + +static __m128 sse_approx_acos(__m128 x) +{ + /* needs a better approximation than taylor expansion of acos, since that + * gives big erros for near 1.0 values, sqrt(2*x)*acos(1-x) should work + * better, see http://www.tom.womack.net/projects/sse-fast-arctrig.html */ + + return _mm_set_ps1(1.0f); +} + +static float occ_quad_form_factor(float *p, float *n, float *q0, float *q1, float *q2, float *q3) +{ + float r0[3], r1[3], r2[3], r3[3], g0[3], g1[3], g2[3], g3[3]; + float a1, a2, a3, a4, dot1, dot2, dot3, dot4, result; + float fresult[4] __attribute__((aligned(16))); + __m128 qx, qy, qz, rx, ry, rz, rlen, srx, sry, srz, gx, gy, gz, glen, rcos, angle, aresult; + + /* compute r */ + qx = _mm_set_ps(q3[0], q2[0], q1[0], q0[0]); + qy = _mm_set_ps(q3[1], q2[1], q1[1], q0[1]); + qz = _mm_set_ps(q3[2], q2[2], q1[2], q0[2]); + + rx = qx - _mm_set_ps1(p[0]); + ry = qy - _mm_set_ps1(p[1]); + rz = qz - _mm_set_ps1(p[2]); + + /* normalize r */ + rlen = _mm_rsqrt_ps(rx*rx + ry*ry + rz*rz + _mm_set_ps1(1e-16f)); + rx = rx*rlen; + ry = ry*rlen; + rz = rz*rlen; + + /* cross product */ + srx = _mm_shuffle_ps(rx, rx, _MM_SHUFFLE(0,3,2,1)); + sry = _mm_shuffle_ps(ry, ry, _MM_SHUFFLE(0,3,2,1)); + srz = _mm_shuffle_ps(rz, rz, _MM_SHUFFLE(0,3,2,1)); + + gx = sry*rz - srz*ry; + gy = srz*rx - srx*rz; + gz = srx*ry - sry*rx; + + /* normalize g */ + glen = _mm_rsqrt_ps(gx*gx + gy*gy + gz*gz + _mm_set_ps1(1e-16f)); + gx = gx*glen; + gy = gy*glen; + gz = gz*glen; + + /* compute angle */ + rcos = rx*srx + ry*sry + rz*srz; + rcos= _mm_max_ps(_mm_min_ps(rcos, _mm_set_ps1(1.0f)), _mm_set_ps1(-1.0f)); + + angle = sse_approx_cos(rcos); + aresult = (_mm_set_ps1(n[0])*gx + _mm_set_ps1(n[1])*gy + _mm_set_ps1(n[2])*gz)*angle; + + /* sum together */ + result= (fresult[0] + fresult[1] + fresult[2] + fresult[3])*(0.5f/(float)M_PI); + result= MAX2(result, 0.0f); + + return result; +} + +#endif + +static float saacosf(float fac) +{ + if(fac<= -1.0f) return (float)M_PI; + else if(fac>=1.0f) return 0.0f; + else return acos(fac); /* acosf(fac) */ +} + +static void normalizef(float *n) +{ + float d; + + d= INPR(n, n); + + if(d > 1.0e-35F) { + d= 1.0f/sqrt(d); /* sqrtf(d) */ + + n[0] *= d; + n[1] *= d; + n[2] *= d; + } +} + +static float occ_quad_form_factor(float *p, float *n, float *q0, float *q1, float *q2, float *q3) +{ + float r0[3], r1[3], r2[3], r3[3], g0[3], g1[3], g2[3], g3[3]; + float a1, a2, a3, a4, dot1, dot2, dot3, dot4, result; + + VECSUB(r0, q0, p); + VECSUB(r1, q1, p); + VECSUB(r2, q2, p); + VECSUB(r3, q3, p); + + normalizef(r0); + normalizef(r1); + normalizef(r2); + normalizef(r3); + + Crossf(g0, r1, r0); normalizef(g0); + Crossf(g1, r2, r1); normalizef(g1); + Crossf(g2, r3, r2); normalizef(g2); + Crossf(g3, r0, r3); normalizef(g3); + + a1= saacosf(INPR(r0, r1)); + a2= saacosf(INPR(r1, r2)); + a3= saacosf(INPR(r2, r3)); + a4= saacosf(INPR(r3, r0)); + + dot1= INPR(n, g0); + dot2= INPR(n, g1); + dot3= INPR(n, g2); + dot4= INPR(n, g3); + + result= (a1*dot1 + a2*dot2 + a3*dot3 + a4*dot4)*0.5f/(float)M_PI; + result= MAX2(result, 0.0f); + + return result; +} + +static float occ_form_factor(OccFace *face, float *p, float *n) +{ + ObjectInstanceRen *obi; + VlakRen *vlr; + float v1[3], v2[3], v3[3], v4[3], q0[3], q1[3], q2[3], q3[3], contrib= 0.0f; + + obi= &R.objectinstance[face->obi]; + vlr= RE_findOrAddVlak(obi->obr, face->facenr); + + VECCOPY(v1, vlr->v1->co); + VECCOPY(v2, vlr->v2->co); + VECCOPY(v3, vlr->v3->co); + + if(obi->flag & R_TRANSFORMED) { + Mat4MulVecfl(obi->mat, v1); + Mat4MulVecfl(obi->mat, v2); + Mat4MulVecfl(obi->mat, v3); + } + + if(occ_visible_quad(p, n, v1, v2, v3, q0, q1, q2, q3)) + contrib += occ_quad_form_factor(p, n, q0, q1, q2, q3); + + if(vlr->v4) { + VECCOPY(v4, vlr->v4->co); + if(obi->flag & R_TRANSFORMED) + Mat4MulVecfl(obi->mat, v4); + + if(occ_visible_quad(p, n, v1, v3, v4, q0, q1, q2, q3)) + contrib += occ_quad_form_factor(p, n, q0, q1, q2, q3); + } + + return contrib; +} + +static void occ_lookup(OcclusionTree *tree, int thread, OccFace *exclude, float *pp, float *pn, float *occ, float *bentn) +{ + OccNode *node, **stack; + OccFace *face; + float resultocc, v[3], p[3], n[3], co[3], invd2; + float distfac, fac, error, d2, weight, emitarea; + int b, totstack; + + /* init variables */ + VECCOPY(p, pp); + VECCOPY(n, pn); + VECADDFAC(p, p, n, 1e-4f); + + if(bentn) + VECCOPY(bentn, n); + + error= tree->error; + distfac= tree->distfac; + + resultocc= 0.0f; + + /* init stack */ + stack= tree->stack[thread]; + stack[0]= tree->root; + totstack= 1; + + while(totstack) { + /* pop point off the stack */ + node= stack[--totstack]; + + VECSUB(v, node->co, p); + d2= INPR(v, v) + 1e-16f; + emitarea= MAX2(node->area, node->dco); + + if(d2*error > emitarea) { + if(distfac != 0.0f) { + fac= 1.0f/(1.0f + distfac*d2); + if(fac < 0.01f) + continue; + } + else + fac= 1.0f; + + /* accumulate occlusion from spherical harmonics */ + invd2 = 1.0f/sqrt(d2); + weight= occ_solid_angle(node, v, d2, invd2, n); + weight *= node->occlusion; + + if(bentn) { + bentn[0] -= weight*invd2*v[0]; + bentn[1] -= weight*invd2*v[1]; + bentn[2] -= weight*invd2*v[2]; + } + + resultocc += weight*fac; + } + else { + /* traverse into children */ + for(b=0; b<TOTCHILD; b++) { + if(node->childflag & (1<<b)) { + face= tree->face+node->child[b].face; + + /* accumulate occlusion with face form factor */ + if(!exclude || !(face->obi == exclude->obi && face->facenr == exclude->facenr)) { + if(bentn || distfac != 0.0f) { + occ_face(face, co, NULL, NULL); + VECSUB(v, co, p); + d2= INPR(v, v) + 1e-16f; + + fac= (distfac == 0.0f)? 1.0f: 1.0f/(1.0f + distfac*d2); + if(fac < 0.01f) + continue; + } + else + fac= 1.0f; + + weight= occ_form_factor(face, p, n); + weight *= tree->occlusion[node->child[b].face]; + + if(bentn) { + invd2= 1.0f/sqrt(d2); + bentn[0] -= weight*invd2*v[0]; + bentn[1] -= weight*invd2*v[1]; + bentn[2] -= weight*invd2*v[2]; + } + + resultocc += weight*fac; + } + } + else if(node->child[b].node) { + /* push child on the stack */ + stack[totstack++]= node->child[b].node; + } + } + } + } + + if(occ) *occ= resultocc; + if(bentn) Normalize(bentn); +} + +static void occ_compute_passes(Render *re, OcclusionTree *tree, int totpass) +{ + float *occ, co[3], n[3]; + int pass, i; + + occ= MEM_callocN(sizeof(float)*tree->totface, "OcclusionPassOcc"); + + for(pass=0; pass<totpass; pass++) { + for(i=0; i<tree->totface; i++) { + occ_face(&tree->face[i], co, n, NULL); + VecMulf(n, -1.0f); + VECADDFAC(co, co, n, 1e-8f); + + occ_lookup(tree, 0, &tree->face[i], co, n, &occ[i], NULL); + if(re->test_break()) + break; + } + + if(re->test_break()) + break; + + for(i=0; i<tree->totface; i++) { + tree->occlusion[i] -= occ[i]; //MAX2(1.0f-occ[i], 0.0f); + if(tree->occlusion[i] < 0.0f) + tree->occlusion[i]= 0.0f; + } + + occ_sum_occlusion(tree, tree->root); + } + + MEM_freeN(occ); +} + +static void sample_occ_tree(Render *re, OcclusionTree *tree, OccFace *exclude, float *co, float *n, int thread, int onlyshadow, float *skycol) +{ + float nn[3], bn[3], fac, occ, occlusion, correction; + int aocolor; + + aocolor= re->wrld.aocolor; + if(onlyshadow) + aocolor= WO_AOPLAIN; + + VECCOPY(nn, n); + VecMulf(nn, -1.0f); + + occ_lookup(tree, thread, exclude, co, nn, &occ, (aocolor)? bn: NULL); + + correction= re->wrld.ao_approx_correction; + + occlusion= (1.0f-correction)*(1.0f-occ); + CLAMP(occlusion, 0.0f, 1.0f); + if(correction != 0.0f) + occlusion += correction*exp(-occ); + + if(aocolor) { + /* sky shading using bent normal */ + if(ELEM(aocolor, WO_AOSKYCOL, WO_AOSKYTEX)) { + fac= 0.5*(1.0f+bn[0]*re->grvec[0]+ bn[1]*re->grvec[1]+ bn[2]*re->grvec[2]); + skycol[0]= (1.0f-fac)*re->wrld.horr + fac*re->wrld.zenr; + skycol[1]= (1.0f-fac)*re->wrld.horg + fac*re->wrld.zeng; + skycol[2]= (1.0f-fac)*re->wrld.horb + fac*re->wrld.zenb; + } +#if 0 + else { /* WO_AOSKYTEX */ + float dxyview[3]; + bn[0]= -bn[0]; + bn[1]= -bn[1]; + bn[2]= -bn[2]; + dxyview[0]= 1.0f; + dxyview[1]= 1.0f; + dxyview[2]= 0.0f; + shadeSkyView(skycol, co, bn, dxyview); + } +#endif + + VecMulf(skycol, occlusion); + } + else { + skycol[0]= occlusion; + skycol[1]= occlusion; + skycol[2]= occlusion; + } +} + +/* ---------------------------- Caching ------------------------------- */ + +static OcclusionCacheSample *find_occ_sample(OcclusionCache *cache, int x, int y) +{ + x -= cache->x; + y -= cache->y; + + x /= cache->step; + y /= cache->step; + x *= cache->step; + y *= cache->step; + + if(x < 0 || x >= cache->w || y < 0 || y >= cache->h) + return NULL; + else + return &cache->sample[y*cache->w + x]; +} + +static int sample_occ_cache(OcclusionTree *tree, float *co, float *n, int x, int y, int thread, float *col) +{ + OcclusionCache *cache; + OcclusionCacheSample *samples[4], *sample; + float wn[4], wz[4], wb[4], tx, ty, w, totw, mino, maxo; + float d[3], dist2; + int i, x1, y1, x2, y2; + + if(!tree->cache) + return 0; + + /* first try to find a sample in the same pixel */ + cache= &tree->cache[thread]; + + if(cache->sample && cache->step) { + sample= &cache->sample[(y-cache->y)*cache->w + (x-cache->x)]; + if(sample->filled) { + VECSUB(d, sample->co, co); + dist2= INPR(d, d); + if(dist2 < 0.5f*sample->dist2 && INPR(sample->n, n) > 0.98f) { + VECCOPY(col, sample->col); + return 1; + } + } + } + else + return 0; + + /* try to interpolate between 4 neighbouring pixels */ + samples[0]= find_occ_sample(cache, x, y); + samples[1]= find_occ_sample(cache, x+cache->step, y); + samples[2]= find_occ_sample(cache, x, y+cache->step); + samples[3]= find_occ_sample(cache, x+cache->step, y+cache->step); + + for(i=0; i<4; i++) + if(!samples[i] || !samples[i]->filled) + return 0; + + /* require intensities not being too different */ + mino= MIN4(samples[0]->intensity, samples[1]->intensity, samples[2]->intensity, samples[3]->intensity); + maxo= MAX4(samples[0]->intensity, samples[1]->intensity, samples[2]->intensity, samples[3]->intensity); + + if(maxo - mino > 0.05f) + return 0; + + /* compute weighted interpolation between samples */ + col[0]= col[1]= col[2]= 0.0f; + totw= 0.0f; + + x1= samples[0]->x; + y1= samples[0]->y; + x2= samples[3]->x; + y2= samples[3]->y; + + tx= (float)(x2 - x)/(float)(x2 - x1); + ty= (float)(y2 - y)/(float)(y2 - y1); + + wb[3]= (1.0f-tx)*(1.0f-ty); + wb[2]= (tx)*(1.0f-ty); + wb[1]= (1.0f-tx)*(ty); + wb[0]= tx*ty; + + for(i=0; i<4; i++) { + VECSUB(d, samples[i]->co, co); + dist2= INPR(d, d); + + wz[i]= 1.0f; //(samples[i]->dist2/(1e-4f + dist2)); + wn[i]= pow(INPR(samples[i]->n, n), 32.0f); + + w= wb[i]*wn[i]*wz[i]; + + totw += w; + col[0] += w*samples[i]->col[0]; + col[1] += w*samples[i]->col[1]; + col[2] += w*samples[i]->col[2]; + } + + if(totw >= 0.9f) { + totw= 1.0f/totw; + col[0] *= totw; + col[1] *= totw; + col[2] *= totw; + return 1; + } + + return 0; +} + +static void sample_occ_surface(ShadeInput *shi) +{ + StrandRen *strand= shi->strand; + StrandSurface *mesh= strand->buffer->surface; + int *face, *index = RE_strandren_get_face(shi->obr, strand, 0); + float w[4], *co1, *co2, *co3, *co4; + + if(mesh && mesh->face && mesh->co && mesh->col && index) { + face= mesh->face[*index]; + + co1= mesh->co[face[0]]; + co2= mesh->co[face[1]]; + co3= mesh->co[face[2]]; + co4= (face[3])? mesh->co[face[3]]: NULL; + + InterpWeightsQ3Dfl(co1, co2, co3, co4, strand->vert->co, w); + + shi->ao[0]= shi->ao[1]= shi->ao[2]= 0.0f; + VECADDFAC(shi->ao, shi->ao, mesh->col[face[0]], w[0]); + VECADDFAC(shi->ao, shi->ao, mesh->col[face[1]], w[1]); + VECADDFAC(shi->ao, shi->ao, mesh->col[face[2]], w[2]); + if(face[3]) + VECADDFAC(shi->ao, shi->ao, mesh->col[face[3]], w[3]); + } + else { + shi->ao[0]= 1.0f; + shi->ao[1]= 1.0f; + shi->ao[2]= 1.0f; + } +} + +/* ------------------------- External Functions --------------------------- */ + +static void *exec_strandsurface_sample(void *data) +{ + OcclusionThread *othread= (OcclusionThread*)data; + Render *re= othread->re; + StrandSurface *mesh= othread->mesh; + float col[3], co[3], n[3], *co1, *co2, *co3, *co4; + int a, *face; + + for(a=othread->begin; a<othread->end; a++) { + face= mesh->face[a]; + co1= mesh->co[face[0]]; + co2= mesh->co[face[1]]; + co3= mesh->co[face[2]]; + + if(face[3]) { + co4= mesh->co[face[3]]; + + VecLerpf(co, co1, co3, 0.5f); + CalcNormFloat4(co1, co2, co3, co4, n); + } + else { + CalcCent3f(co, co1, co2, co3); + CalcNormFloat(co1, co2, co3, n); + } + VecMulf(n, -1.0f); + + sample_occ_tree(re, re->occlusiontree, NULL, co, n, othread->thread, 0, col); + VECCOPY(othread->facecol[a], col); + } + + return 0; +} + +void make_occ_tree(Render *re) +{ + OcclusionThread othreads[BLENDER_MAX_THREADS]; + StrandSurface *mesh; + ListBase threads; + float col[3], (*facecol)[3]; + int a, totface, totthread, *face, *count; + + /* ugly, needed for occ_face */ + R= *re; + + re->i.infostr= "Occlusion preprocessing"; + re->stats_draw(&re->i); + + re->occlusiontree= occ_tree_build(re); + + if(re->occlusiontree) { + if(re->wrld.ao_approx_passes) + occ_compute_passes(re, re->occlusiontree, re->wrld.ao_approx_passes); + + for(mesh=re->strandsurface.first; mesh; mesh=mesh->next) { + if(!mesh->face || !mesh->co || !mesh->col) + continue; + + count= MEM_callocN(sizeof(int)*mesh->totvert, "OcclusionCount"); + facecol= MEM_callocN(sizeof(float)*3*mesh->totface, "StrandSurfFaceCol"); + + totthread= (mesh->totface > 10000)? re->r.threads: 1; + totface= mesh->totface/totthread; + for(a=0; a<totthread; a++) { + othreads[a].re= re; + othreads[a].facecol= facecol; + othreads[a].thread= a; + othreads[a].mesh= mesh; + othreads[a].begin= a*totface; + othreads[a].end= (a == totthread-1)? mesh->totface: (a+1)*totface; + } + + if(totthread == 1) { + exec_strandsurface_sample(&othreads[0]); + } + else { + BLI_init_threads(&threads, exec_strandsurface_sample, totthread); + + for(a=0; a<totthread; a++) + BLI_insert_thread(&threads, &othreads[a]); + + BLI_end_threads(&threads); + } + + for(a=0; a<mesh->totface; a++) { + face= mesh->face[a]; + + VECCOPY(col, facecol[a]); + VECADD(mesh->col[face[0]], mesh->col[face[0]], col); + count[face[0]]++; + VECADD(mesh->col[face[1]], mesh->col[face[1]], col); + count[face[1]]++; + VECADD(mesh->col[face[2]], mesh->col[face[2]], col); + count[face[2]]++; + + if(face[3]) { + VECADD(mesh->col[face[3]], mesh->col[face[3]], col); + count[face[3]]++; + } + } + + for(a=0; a<mesh->totvert; a++) + if(count[a]) + VecMulf(mesh->col[a], 1.0f/count[a]); + + MEM_freeN(count); + MEM_freeN(facecol); + } + } +} + +void free_occ(Render *re) +{ + if(re->occlusiontree) { + occ_free_tree(re->occlusiontree); + re->occlusiontree = NULL; + } +} + +void sample_occ(Render *re, ShadeInput *shi) +{ + OcclusionTree *tree= re->occlusiontree; + OcclusionCache *cache; + OcclusionCacheSample *sample; + OccFace exclude; + int onlyshadow; + + if(tree) { + if(shi->strand) { + sample_occ_surface(shi); + } + /* try to get result from the cache if possible */ + else if(!sample_occ_cache(tree, shi->co, shi->vno, shi->xs, shi->ys, shi->thread, shi->ao)) { + /* no luck, let's sample the occlusion */ + exclude.obi= shi->obi - re->objectinstance; + exclude.facenr= shi->vlr->index; + onlyshadow= (shi->mat->mode & MA_ONLYSHADOW); + sample_occ_tree(re, tree, &exclude, shi->co, shi->vno, shi->thread, onlyshadow, shi->ao); + + /* fill result into sample, each time */ + if(tree->cache) { + cache= &tree->cache[shi->thread]; + + if(cache->sample && cache->step) { + sample= &cache->sample[(shi->ys-cache->y)*cache->w + (shi->xs-cache->x)]; + VECCOPY(sample->co, shi->co); + VECCOPY(sample->n, shi->vno); + VECCOPY(sample->col, shi->ao); + sample->intensity= MAX3(sample->col[0], sample->col[1], sample->col[2]); + sample->dist2= INPR(shi->dxco, shi->dxco) + INPR(shi->dyco, shi->dyco); + sample->filled= 1; + } + } + } + } + else { + shi->ao[0]= 1.0f; + shi->ao[1]= 1.0f; + shi->ao[2]= 1.0f; + } +} + +void cache_occ_samples(Render *re, RenderPart *pa, ShadeSample *ssamp) +{ + OcclusionTree *tree= re->occlusiontree; + PixStr ps; + OcclusionCache *cache; + OcclusionCacheSample *sample; + OccFace exclude; + ShadeInput *shi; + intptr_t *rd=NULL; + int *ro=NULL, *rp=NULL, *rz=NULL, onlyshadow; + int x, y, step = CACHE_STEP; + + if(!tree->cache) + return; + + cache= &tree->cache[pa->thread]; + cache->w= pa->rectx; + cache->h= pa->recty; + cache->x= pa->disprect.xmin; + cache->y= pa->disprect.ymin; + cache->step= step; + cache->sample= MEM_callocN(sizeof(OcclusionCacheSample)*cache->w*cache->h, "OcclusionCacheSample"); + sample= cache->sample; + + if(re->osa) { + rd= pa->rectdaps; + } + else { + /* fake pixel struct for non-osa */ + ps.next= NULL; + ps.mask= 0xFFFF; + + ro= pa->recto; + rp= pa->rectp; + rz= pa->rectz; + } + + /* compute a sample at every step pixels */ + for(y=pa->disprect.ymin; y<pa->disprect.ymax; y++) { + for(x=pa->disprect.xmin; x<pa->disprect.xmax; x++, sample++, rd++, ro++, rp++, rz++) { + if(!(((x - pa->disprect.xmin + step) % step) == 0 || x == pa->disprect.xmax-1)) + continue; + if(!(((y - pa->disprect.ymin + step) % step) == 0 || y == pa->disprect.ymax-1)) + continue; + + if(re->osa) { + if(!*rd) continue; + + shade_samples_fill_with_ps(ssamp, (PixStr *)(*rd), x, y); + } + else { + if(!*rp) continue; + + ps.obi= *ro; + ps.facenr= *rp; + ps.z= *rz; + shade_samples_fill_with_ps(ssamp, &ps, x, y); + } + + shi= ssamp->shi; + if(shi->vlr) { + onlyshadow= (shi->mat->mode & MA_ONLYSHADOW); + exclude.obi= shi->obi - re->objectinstance; + exclude.facenr= shi->vlr->index; + sample_occ_tree(re, tree, &exclude, shi->co, shi->vno, shi->thread, onlyshadow, shi->ao); + + VECCOPY(sample->co, shi->co); + VECCOPY(sample->n, shi->vno); + VECCOPY(sample->col, shi->ao); + sample->intensity= MAX3(sample->col[0], sample->col[1], sample->col[2]); + sample->dist2= INPR(shi->dxco, shi->dxco) + INPR(shi->dyco, shi->dyco); + sample->x= shi->xs; + sample->y= shi->ys; + sample->filled= 1; + } + + if(re->test_break()) + break; + } + } +} + +void free_occ_samples(Render *re, RenderPart *pa) +{ + OcclusionTree *tree= re->occlusiontree; + OcclusionCache *cache; + + if(tree->cache) { + cache= &tree->cache[pa->thread]; + + if(cache->sample) + MEM_freeN(cache->sample); + + cache->w= 0; + cache->h= 0; + cache->step= 0; + } +} + diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 5315552310a..2d7d954b70d 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -38,6 +38,7 @@ #include "DNA_scene_types.h" #include "DNA_userdef_types.h" +#include "BKE_utildefines.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" @@ -76,6 +77,7 @@ #include "envmap.h" #include "initrender.h" #include "shadbuf.h" +#include "pixelblending.h" #include "zbuf.h" @@ -114,7 +116,7 @@ static struct ListBase RenderList= {NULL, NULL}; Render R; /* commandline thread override */ -static int commandline_threads= 0; +static int commandline_threads= -1; /* ********* alloc and free ******** */ @@ -135,30 +137,27 @@ static void print_error(char *str) {printf("ERROR: %s\n", str);} static void stats_background(RenderStats *rs) { - extern unsigned long mem_in_use; + uintptr_t mem_in_use= MEM_get_memory_in_use(); float megs_used_memory= mem_in_use/(1024.0*1024.0); char str[400], *spos= str; - if(rs->convertdone) { - - spos+= sprintf(spos, "Fra:%d Mem:%.2fM ", G.scene->r.cfra, megs_used_memory); - - if(rs->curfield) - spos+= sprintf(spos, "Field %d ", rs->curfield); - if(rs->curblur) - spos+= sprintf(spos, "Blur %d ", rs->curblur); - - if(rs->infostr) { - spos+= sprintf(spos, "| %s", rs->infostr); - } - else { - if(rs->tothalo) - spos+= sprintf(spos, "Sce: %s Ve:%d Fa:%d Ha:%d La:%d", G.scene->id.name+2, rs->totvert, rs->totface, rs->tothalo, rs->totlamp); - else - spos+= sprintf(spos, "Sce: %s Ve:%d Fa:%d La:%d", G.scene->id.name+2, rs->totvert, rs->totface, rs->totlamp); - } - printf(str); printf("\n"); - } + spos+= sprintf(spos, "Fra:%d Mem:%.2fM ", G.scene->r.cfra, megs_used_memory); + + if(rs->curfield) + spos+= sprintf(spos, "Field %d ", rs->curfield); + if(rs->curblur) + spos+= sprintf(spos, "Blur %d ", rs->curblur); + + if(rs->infostr) { + spos+= sprintf(spos, "| %s", rs->infostr); + } + else { + if(rs->tothalo) + spos+= sprintf(spos, "Sce: %s Ve:%d Fa:%d Ha:%d La:%d", G.scene->id.name+2, rs->totvert, rs->totface, rs->tothalo, rs->totlamp); + else + spos+= sprintf(spos, "Sce: %s Ve:%d Fa:%d La:%d", G.scene->id.name+2, rs->totvert, rs->totface, rs->totlamp); + } + printf(str); printf("\n"); } void RE_FreeRenderResult(RenderResult *res) @@ -193,6 +192,22 @@ void RE_FreeRenderResult(RenderResult *res) MEM_freeN(res); } +/* version that's compatible with fullsample buffers */ +static void free_render_result(ListBase *lb, RenderResult *rr) +{ + RenderResult *rrnext; + + for(; rr; rr= rrnext) { + rrnext= rr->next; + + if(lb && lb->first) + BLI_remlink(lb, rr); + + RE_FreeRenderResult(rr); + } +} + + /* all layers except the active one get temporally pushed away */ static void push_render_result(Render *re) { @@ -329,6 +344,10 @@ static char *get_pass_name(int passtype, int channel) if(channel==-1) return "IndexOB"; return "IndexOB.X"; } + if(passtype == SCE_PASS_MIST) { + if(channel==-1) return "Mist"; + return "Mist.Z"; + } return "Unknown"; } @@ -377,23 +396,25 @@ static int passtype_from_name(char *str) if(strcmp(str, "IndexOB")==0) return SCE_PASS_INDEXOB; + if(strcmp(str, "Mist")==0) + return SCE_PASS_MIST; + return 0; } - - -static void render_unique_exr_name(Render *re, char *str) +static void render_unique_exr_name(Render *re, char *str, int sample) { char di[FILE_MAX], name[FILE_MAXFILE], fi[FILE_MAXFILE]; BLI_strncpy(di, G.sce, FILE_MAX); BLI_splitdirstring(di, fi); - sprintf(name, "%s_%s.exr", fi, re->scene->id.name+2); - if(G.background) - BLI_make_file_string("/", str, "/tmp/", name); + + if(sample==0) + sprintf(name, "%s_%s.exr", fi, re->scene->id.name+2); else - BLI_make_file_string("/", str, U.tempdir, name); - + sprintf(name, "%s_%s%d.exr", fi, re->scene->id.name+2, sample); + + BLI_make_file_string("/", str, btempdir, name); } static void render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype) @@ -412,17 +433,22 @@ static void render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channel IMB_exr_add_channel(rr->exrhandle, rl->name, get_pass_name(passtype, a), 0, 0, NULL); } else { + float *rect; + int x; + + rpass->rect= MEM_mapallocN(sizeof(float)*rectsize, typestr); + if(passtype==SCE_PASS_VECTOR) { - float *rect; - int x; - /* initialize to max speed */ - rect= rpass->rect= MEM_mapallocN(sizeof(float)*rectsize, typestr); + rect= rpass->rect; for(x= rectsize-1; x>=0; x--) rect[x]= PASS_VECTOR_MAX; } - else - rpass->rect= MEM_mapallocN(sizeof(float)*rectsize, typestr); + else if(passtype==SCE_PASS_Z) { + rect= rpass->rect; + for(x= rectsize-1; x>=0; x--) + rect[x]= 10e10; + } } } @@ -496,6 +522,7 @@ static RenderResult *new_render_result(Render *re, rcti *partrct, int crop, int strcpy(rl->name, srl->name); rl->lay= srl->lay; + rl->lay_zmask= srl->lay_zmask; rl->layflag= srl->layflag; rl->passflag= srl->passflag; rl->pass_xor= srl->pass_xor; @@ -525,22 +552,20 @@ static RenderResult *new_render_result(Render *re, rcti *partrct, int crop, int render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE); if(srl->passflag & SCE_PASS_SPEC) render_layer_add_pass(rr, rl, 3, SCE_PASS_SPEC); - if(re->r.mode & R_SHADOW) - if(srl->passflag & SCE_PASS_SHADOW) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SHADOW); - if(re->r.mode & R_RAYTRACE) { - if(srl->passflag & SCE_PASS_AO) - render_layer_add_pass(rr, rl, 3, SCE_PASS_AO); - if(srl->passflag & SCE_PASS_REFLECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_REFLECT); - if(srl->passflag & SCE_PASS_REFRACT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_REFRACT); - } - if(re->r.mode & R_RADIO) - if(srl->passflag & SCE_PASS_RADIO) - render_layer_add_pass(rr, rl, 3, SCE_PASS_RADIO); + if(srl->passflag & SCE_PASS_AO) + render_layer_add_pass(rr, rl, 3, SCE_PASS_AO); + if(srl->passflag & SCE_PASS_SHADOW) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SHADOW); + if(srl->passflag & SCE_PASS_REFLECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_REFLECT); + if(srl->passflag & SCE_PASS_REFRACT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_REFRACT); + if(srl->passflag & SCE_PASS_RADIO) + render_layer_add_pass(rr, rl, 3, SCE_PASS_RADIO); if(srl->passflag & SCE_PASS_INDEXOB) render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXOB); + if(srl->passflag & SCE_PASS_MIST) + render_layer_add_pass(rr, rl, 1, SCE_PASS_MIST); } /* sss, previewrender and envmap don't do layers, so we make a default one */ @@ -575,14 +600,13 @@ static RenderResult *new_render_result(Render *re, rcti *partrct, int crop, int static int render_scene_needs_vector(Render *re) { - if((re->r.scemode & R_DOCOMP) || re->r.imtype==R_MULTILAYER) { - SceneRenderLayer *srl; + SceneRenderLayer *srl; - for(srl= re->scene->r.layers.first; srl; srl= srl->next) - if(!(srl->layflag & SCE_LAY_DISABLE)) - if(srl->passflag & SCE_PASS_VECTOR) - return 1; - } + for(srl= re->scene->r.layers.first; srl; srl= srl->next) + if(!(srl->layflag & SCE_LAY_DISABLE)) + if(srl->passflag & SCE_PASS_VECTOR) + return 1; + return 0; } @@ -640,9 +664,8 @@ static void merge_render_result(RenderResult *rr, RenderResult *rrpart) } -static void save_render_result_tile(Render *re, RenderPart *pa) +static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart) { - RenderResult *rrpart= pa->result; RenderLayer *rlp; RenderPass *rpassp; int offs, partx, party; @@ -662,23 +685,23 @@ static void save_render_result_tile(Render *re, RenderPart *pa) if(rlp->rectf) { int a, xstride= 4; for(a=0; a<xstride; a++) - IMB_exr_set_channel(re->result->exrhandle, rlp->name, get_pass_name(SCE_PASS_COMBINED, a), - xstride, xstride*pa->rectx, rlp->rectf+a + xstride*offs); + IMB_exr_set_channel(rr->exrhandle, rlp->name, get_pass_name(SCE_PASS_COMBINED, a), + xstride, xstride*rrpart->rectx, rlp->rectf+a + xstride*offs); } /* passes are allocated in sync */ for(rpassp= rlp->passes.first; rpassp; rpassp= rpassp->next) { int a, xstride= rpassp->channels; for(a=0; a<xstride; a++) - IMB_exr_set_channel(re->result->exrhandle, rlp->name, get_pass_name(rpassp->passtype, a), - xstride, xstride*pa->rectx, rpassp->rect+a + xstride*offs); + IMB_exr_set_channel(rr->exrhandle, rlp->name, get_pass_name(rpassp->passtype, a), + xstride, xstride*rrpart->rectx, rpassp->rect+a + xstride*offs); } } party= rrpart->tilerect.ymin + rrpart->crop; partx= rrpart->tilerect.xmin + rrpart->crop; - IMB_exrtile_write_channels(re->result->exrhandle, partx, party); + IMB_exrtile_write_channels(rr->exrhandle, partx, party, 0); BLI_unlock_thread(LOCK_IMAGE); @@ -687,14 +710,17 @@ static void save_render_result_tile(Render *re, RenderPart *pa) static void save_empty_result_tiles(Render *re) { RenderPart *pa; + RenderResult *rr; - IMB_exrtile_clear_channels(re->result->exrhandle); + for(rr= re->result; rr; rr= rr->next) { + IMB_exrtile_clear_channels(rr->exrhandle); - for(pa= re->parts.first; pa; pa= pa->next) { - if(pa->ready==0) { - int party= pa->disprect.ymin - re->disprect.ymin + pa->crop; - int partx= pa->disprect.xmin - re->disprect.xmin + pa->crop; - IMB_exrtile_write_channels(re->result->exrhandle, partx, party); + for(pa= re->parts.first; pa; pa= pa->next) { + if(pa->ready==0) { + int party= pa->disprect.ymin - re->disprect.ymin + pa->crop; + int partx= pa->disprect.xmin - re->disprect.xmin + pa->crop; + IMB_exrtile_write_channels(rr->exrhandle, partx, party, 0); + } } } } @@ -722,6 +748,8 @@ void RE_WriteRenderResult(RenderResult *rr, char *filename, int compress) RenderLayer *rl; RenderPass *rpass; void *exrhandle= IMB_exr_get_handle(); + + BLI_make_existing_file(filename); /* composite result */ if(rr->rectf) { @@ -821,7 +849,7 @@ static void renderresult_add_names(RenderResult *rr) /* only for temp buffer files, makes exact copy of render result */ -static void read_render_result(Render *re) +static void read_render_result(Render *re, int sample) { RenderLayer *rl; RenderPass *rpass; @@ -832,14 +860,16 @@ static void read_render_result(Render *re) RE_FreeRenderResult(re->result); re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM); - render_unique_exr_name(re, str); + render_unique_exr_name(re, str, sample); if(IMB_exr_begin_read(exrhandle, str, &rectx, &recty)==0) { IMB_exr_close(exrhandle); printf("cannot read: %s\n", str); return; } - if(rectx!=re->result->rectx || recty!=re->result->recty) { + printf("read exr tmp file: %s\n", str); + + if(re->result == NULL || rectx!=re->result->rectx || recty!=re->result->recty) { printf("error in reading render result\n"); } else { @@ -930,7 +960,6 @@ void RE_GetResultImage(Render *re, RenderResult *rr) } } -#define FTOCHAR(val) val<=0.0f?0: (val>=1.0f?255: (char)(255.0f*val)) /* caller is responsible for allocating rect in correct size! */ void RE_ResultGet32(Render *re, unsigned int *rect) { @@ -962,6 +991,7 @@ RenderStats *RE_GetStats(Render *re) return &re->i; } +/* Note, when rendering from a scene, ALWAYS use G.scene->id.name, else compositing wont work */ Render *RE_NewRender(const char *name) { Render *re; @@ -1022,7 +1052,7 @@ void RE_FreeAllRender(void) /* what doesn't change during entire render sequence */ /* disprect is optional, if NULL it assumes full window render */ -void RE_InitState(Render *re, RenderData *rd, int winx, int winy, rcti *disprect) +void RE_InitState(Render *re, Render *source, RenderData *rd, int winx, int winy, rcti *disprect) { re->ok= TRUE; /* maybe flag */ @@ -1045,18 +1075,36 @@ void RE_InitState(Render *re, RenderData *rd, int winx, int winy, rcti *disprect } if(re->rectx < 2 || re->recty < 2 || (BKE_imtype_is_movie(rd->imtype) && -(re->rectx < 16 || re->recty < 16) )) { + (re->rectx < 16 || re->recty < 16) )) { re->error("Image too small"); re->ok= 0; } else { - /* check state variables, osa? */ - if(re->r.mode & (R_OSA)) { - re->osa= re->r.osa; - if(re->osa>16) re->osa= 16; - } - else re->osa= 0; +#ifndef WITH_OPENEXR + /* can't do this without openexr support */ + re->r.scemode &= ~R_EXR_TILE_FILE; +#endif + + if(!(re->r.scemode & R_EXR_TILE_FILE)) + re->r.scemode &= ~R_FULL_SAMPLE; /* clear, so we can use this flag for test both */ + /* fullsample wants uniform osa levels */ + if(source && (re->r.scemode & R_FULL_SAMPLE)) { + /* but, if source has no full sample we disable it */ + if((source->r.scemode & R_FULL_SAMPLE)==0) + re->r.scemode &= ~R_FULL_SAMPLE; + else + re->r.osa= re->osa= source->osa; + } + else { + /* check state variables, osa? */ + if(re->r.mode & (R_OSA)) { + re->osa= re->r.osa; + if(re->osa>16) re->osa= 16; + } + else re->osa= 0; + } + /* always call, checks for gamma, gamma tables and jitter too */ make_sample_tables(re); @@ -1069,11 +1117,11 @@ void RE_InitState(Render *re, RenderData *rd, int winx, int winy, rcti *disprect /* we clip faces with a minimum of 2 pixel boundary outside of image border. see zbuf.c */ re->clipcrop= 1.0f + 2.0f/(float)(re->winx>re->winy?re->winy:re->winx); - if(commandline_threads>0 && commandline_threads<=BLENDER_MAX_THREADS) - re->r.threads= commandline_threads; + RE_init_threadcount(re); } } +/* part of external api, not called for regular render pipeline */ void RE_SetDispRect (struct Render *re, rcti *disprect) { re->disprect= *disprect; @@ -1174,13 +1222,36 @@ static int render_display_draw_enabled(Render *re) return 1; } +/* allocate osa new results for samples */ +static RenderResult *new_full_sample_buffers(Render *re, ListBase *lb, rcti *partrct, int crop) +{ + int a; + + if(re->osa==0) + return new_render_result(re, partrct, crop, RR_USEMEM); + + for(a=0; a<re->osa; a++) { + RenderResult *rr= new_render_result(re, partrct, crop, RR_USEMEM); + BLI_addtail(lb, rr); + rr->sample_nr= a; + } + + return lb->first; +} + + +/* the main thread call, renders an entire part */ static void *do_part_thread(void *pa_v) { RenderPart *pa= pa_v; /* need to return nicely all parts on esc */ if(R.test_break()==0) { - pa->result= new_render_result(&R, &pa->disprect, pa->crop, RR_USEMEM); + + if(!R.sss_points && (R.r.scemode & R_FULL_SAMPLE)) + pa->result= new_full_sample_buffers(&R, &pa->fullresult, &pa->disprect, pa->crop); + else + pa->result= new_render_result(&R, &pa->disprect, pa->crop, RR_USEMEM); if(R.sss_points) zbufshade_sss_tile(pa); @@ -1190,8 +1261,13 @@ static void *do_part_thread(void *pa_v) zbufshade_tile(pa); /* merge too on break! */ - if(R.result->exrhandle) - save_render_result_tile(&R, pa); + if(R.result->exrhandle) { + RenderResult *rr, *rrpart; + + for(rr= R.result, rrpart= pa->result; rr && rrpart; rr= rr->next, rrpart= rrpart->next) + save_render_result_tile(rr, rrpart); + + } else if(render_display_draw_enabled(&R)) merge_render_result(R.result, pa->result); } @@ -1365,33 +1441,54 @@ static void print_part_stats(Render *re, RenderPart *pa) re->i.infostr= NULL; } +/* make osa new results for samples */ +static RenderResult *new_full_sample_buffers_exr(Render *re) +{ + int a; + + for(a=0; a<re->osa; a++) { + RenderResult *rr= new_render_result(re, &re->disprect, 0, 1); + BLI_addtail(&re->fullresult, rr); + rr->sample_nr= a; + } + + return re->fullresult.first; +} + static void threaded_tile_processor(Render *re) { ListBase threads; RenderPart *pa, *nextpa; - RenderResult *rr; rctf viewplane= re->viewplane; int rendering=1, counter= 1, drawtimer=0, hasdrawn, minx=0; - /* first step; the entire render result, or prepare exr buffer saving */ + /* first step; free the entire render result, make new, and/or prepare exr buffer saving */ RE_FreeRenderResult(re->result); - rr= re->result= new_render_result(re, &re->disprect, 0, re->r.scemode & R_EXR_TILE_FILE); - if(rr==NULL) + if(re->sss_points) + re->result= new_render_result(re, &re->disprect, 0, 0); + else if(re->r.scemode & R_FULL_SAMPLE) + re->result= new_full_sample_buffers_exr(re); + else + re->result= new_render_result(re, &re->disprect, 0, re->r.scemode & R_EXR_TILE_FILE); + + if(re->result==NULL) return; + /* warning; no return here without closing exr file */ -// if(re->re->test_break()) -// return; initparts(re); - if(rr->exrhandle) { + if(re->result->exrhandle) { + RenderResult *rr; char str[FILE_MAX]; - render_unique_exr_name(re, str); + for(rr= re->result; rr; rr= rr->next) { + render_unique_exr_name(re, str, rr->sample_nr); - printf("write exr tmp file, %dx%d, %s\n", rr->rectx, rr->recty, str); - IMB_exrtile_begin_write(rr->exrhandle, str, rr->rectx, rr->recty, rr->rectx/re->xparts, rr->recty/re->yparts); + printf("write exr tmp file, %dx%d, %s\n", rr->rectx, rr->recty, str); + IMB_exrtile_begin_write(rr->exrhandle, str, 0, rr->rectx, rr->recty, re->partx, re->party); + } } BLI_init_threads(&threads, do_part_thread, re->r.threads); @@ -1446,7 +1543,7 @@ static void threaded_tile_processor(Render *re) re->display_draw(pa->result, NULL); print_part_stats(re, pa); - RE_FreeRenderResult(pa->result); + free_render_result(&pa->fullresult, pa->result); pa->result= NULL; re->i.partsdone++; hasdrawn= 1; @@ -1470,11 +1567,20 @@ static void threaded_tile_processor(Render *re) } - if(rr->exrhandle) { + if(re->result->exrhandle) { + RenderResult *rr; + save_empty_result_tiles(re); - IMB_exr_close(rr->exrhandle); - rr->exrhandle= NULL; - read_render_result(re); + + for(rr= re->result; rr; rr= rr->next) { + IMB_exr_close(rr->exrhandle); + rr->exrhandle= NULL; + } + + free_render_result(&re->fullresult, re->result); + re->result= NULL; + + read_render_result(re, 0); } /* unset threadsafety */ @@ -1492,14 +1598,16 @@ void RE_TileProcessor(Render *re, int firsttile, int threaded) re->i.partsdone= firsttile; - re->i.starttime= PIL_check_seconds_timer(); + if(!re->sss_points) + re->i.starttime= PIL_check_seconds_timer(); if(threaded) threaded_tile_processor(re); else render_tile_processor(re, firsttile); - re->i.lastframetime= PIL_check_seconds_timer()- re->i.starttime; + if(!re->sss_points) + re->i.lastframetime= PIL_check_seconds_timer()- re->i.starttime; re->stats_draw(&re->i); } @@ -1722,7 +1830,8 @@ static void load_backbuffer(Render *re) char name[256]; strcpy(name, re->r.backbuf); - BLI_convertstringcode(name, G.sce, re->r.cfra); + BLI_convertstringcode(name, G.sce); + BLI_convertstringframe(name, re->r.cfra); if(re->backbuf) { re->backbuf->id.us--; @@ -1778,6 +1887,7 @@ static void do_render_fields_blur_3d(Render *re) re->result->tilerect= re->disprect; /* this copying sequence could become function? */ + /* weak is: it chances disprect from border */ re->disprect.xmin= re->disprect.ymin= 0; re->disprect.xmax= re->winx; re->disprect.ymax= re->winy; @@ -1807,14 +1917,18 @@ static void do_render_fields_blur_3d(Render *re) static void render_scene(Render *re, Scene *sce, int cfra) { Render *resc= RE_NewRender(sce->id.name); + int winx= re->winx, winy= re->winy; sce->r.cfra= cfra; - /* initial setup */ - RE_InitState(resc, &sce->r, re->winx, re->winy, &re->disprect); + /* exception: scene uses own size (unfinished code) */ + if(0) { + winx= (sce->r.size*sce->r.xsch)/100; + winy= (sce->r.size*sce->r.ysch)/100; + } - /* this to enable this scene to create speed vectors */ - resc->r.scemode |= R_DOCOMP; + /* initial setup */ + RE_InitState(resc, re, &sce->r, winx, winy, &re->disprect); /* still unsure entity this... */ resc->scene= sce; @@ -1830,10 +1944,15 @@ static void render_scene(Render *re, Scene *sce, int cfra) do_render_fields_blur_3d(resc); } -static void ntree_render_scenes(Render *re) +static void tag_scenes_for_render(Render *re) { bNode *node; - int cfra= re->scene->r.cfra; + Scene *sce; + + for(sce= G.main->scene.first; sce; sce= sce->id.next) + sce->id.flag &= ~LIB_DOIT; + + re->scene->id.flag |= LIB_DOIT; if(re->scene->nodetree==NULL) return; @@ -1843,12 +1962,21 @@ static void ntree_render_scenes(Render *re) if(node->id) { if(node->id != (ID *)re->scene) node->id->flag |= LIB_DOIT; - else - node->id->flag &= ~LIB_DOIT; } } } +} + +static void ntree_render_scenes(Render *re) +{ + bNode *node; + int cfra= re->scene->r.cfra; + + if(re->scene->nodetree==NULL) return; + + tag_scenes_for_render(re); + /* now foreach render-result node tagged we do a full render */ /* results are stored in a way compisitor will find it */ for(node= re->scene->nodetree->nodes.first; node; node= node->next) { @@ -1894,6 +2022,115 @@ static void render_composit_stats(char *str) R.i.infostr= NULL; } + +/* reads all buffers, calls optional composite, merges in first result->rectf */ +static void do_merge_fullsample(Render *re, bNodeTree *ntree) +{ + float *rectf, filt[3][3]; + int sample; + + /* filtmask needs it */ + R= *re; + + /* we accumulate in here */ + rectf= MEM_mapallocN(re->rectx*re->recty*sizeof(float)*4, "fullsample rgba"); + + for(sample=0; sample<re->r.osa; sample++) { + RenderResult rres; + int x, y, mask; + + /* set all involved renders on the samplebuffers (first was done by render itself) */ + /* also function below assumes this */ + if(sample) { + Render *re1; + + tag_scenes_for_render(re); + for(re1= RenderList.first; re1; re1= re1->next) { + if(re1->scene->id.flag & LIB_DOIT) + if(re1->r.scemode & R_FULL_SAMPLE) + read_render_result(re1, sample); + } + } + + /* composite */ + if(ntree) { + ntreeCompositTagRender(re->scene); + ntreeCompositTagAnimated(ntree); + + ntreeCompositExecTree(ntree, &re->r, G.background==0); + } + + /* ensure we get either composited result or the active layer */ + RE_GetResultImage(re, &rres); + + /* accumulate with filter, and clip */ + mask= (1<<sample); + mask_array(mask, filt); + + for(y=0; y<re->recty; y++) { + float *rf= rectf + 4*y*re->rectx; + float *col= rres.rectf + 4*y*re->rectx; + + for(x=0; x<re->rectx; x++, rf+=4, col+=4) { + if(col[0]<0.0f) col[0]=0.0f; else if(col[0] > 1.0f) col[0]= 1.0f; + if(col[1]<0.0f) col[1]=0.0f; else if(col[1] > 1.0f) col[1]= 1.0f; + if(col[2]<0.0f) col[2]=0.0f; else if(col[2] > 1.0f) col[2]= 1.0f; + + add_filt_fmask_coord(filt, col, rf, re->rectx, re->recty, x, y); + } + } + + /* show stuff */ + if(sample!=re->osa-1) { + /* weak... the display callback wants an active renderlayer pointer... */ + re->result->renlay= render_get_active_layer(re, re->result); + re->display_draw(re->result, NULL); + } + + if(re->test_break()) + break; + } + + if(re->result->rectf) + MEM_freeN(re->result->rectf); + re->result->rectf= rectf; +} + +void RE_MergeFullSample(Render *re, Scene *sce, bNodeTree *ntree) +{ + Scene *scene; + bNode *node; + + /* first call RE_ReadRenderResult on every renderlayer scene. this creates Render structs */ + + /* tag scenes unread */ + for(scene= G.main->scene.first; scene; scene= scene->id.next) + scene->id.flag |= LIB_DOIT; + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->type==CMP_NODE_R_LAYERS) { + Scene *nodescene= (Scene *)node->id; + + if(nodescene==NULL) nodescene= sce; + if(nodescene->id.flag & LIB_DOIT) { + nodescene->r.mode |= R_OSA; /* render struct needs tables */ + RE_ReadRenderResult(sce, nodescene); + nodescene->id.flag &= ~LIB_DOIT; + } + } + } + + /* own render result should be read/allocated */ + if(G.scene->id.flag & LIB_DOIT) + RE_ReadRenderResult(G.scene, G.scene); + + /* and now we can draw (result is there) */ + re->display_init(re->result); + re->display_clear(re->result); + + do_merge_fullsample(re, ntree); +} + /* returns fully composited render-result on given time step (in RenderData) */ static void do_render_composite_fields_blur_3d(Render *re) { @@ -1913,25 +2150,36 @@ static void do_render_composite_fields_blur_3d(Render *re) if(re->r.scemode & R_SINGLE_LAYER) pop_render_result(re); - if(!re->test_break() && ntree) { - ntreeCompositTagRender(re->scene); - ntreeCompositTagAnimated(ntree); + if(!re->test_break()) { - if(re->r.scemode & R_DOCOMP) { - /* checks if there are render-result nodes that need scene */ - if((re->r.scemode & R_SINGLE_LAYER)==0) - ntree_render_scenes(re); - - if(!re->test_break()) { - ntree->stats_draw= render_composit_stats; - ntree->test_break= re->test_break; - /* in case it was never initialized */ - R.stats_draw= re->stats_draw; + if(ntree) { + ntreeCompositTagRender(re->scene); + ntreeCompositTagAnimated(ntree); + } + + if(1 || !(re->r.scemode & R_COMP_RERENDER)) { + if(ntree && re->r.scemode & R_DOCOMP) { + /* checks if there are render-result nodes that need scene */ + if((re->r.scemode & R_SINGLE_LAYER)==0) + ntree_render_scenes(re); - ntreeCompositExecTree(ntree, &re->r, G.background==0); - ntree->stats_draw= NULL; - ntree->test_break= NULL; + if(!re->test_break()) { + ntree->stats_draw= render_composit_stats; + ntree->test_break= re->test_break; + /* in case it was never initialized */ + R.stats_draw= re->stats_draw; + + if(re->r.scemode & R_FULL_SAMPLE) + do_merge_fullsample(re, ntree); + else + ntreeCompositExecTree(ntree, &re->r, G.background==0); + + ntree->stats_draw= NULL; + ntree->test_break= NULL; + } } + else if(re->r.scemode & R_FULL_SAMPLE) + do_merge_fullsample(re, NULL); } } @@ -2008,7 +2256,7 @@ static void renderresult_stampinfo() RenderResult rres; /* this is the basic trick to get the displayed float or char rect from render result */ RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres); - BKE_stamp_buf((unsigned char *)rres.rect32, rres.rectf, rres.rectx, rres.recty); + BKE_stamp_buf((unsigned char *)rres.rect32, rres.rectf, rres.rectx, rres.recty, 4); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -2086,14 +2334,26 @@ static int is_rendering_allowed(Render *re) if(re->r.scemode & R_EXR_TILE_FILE) { char str[FILE_MAX]; - render_unique_exr_name(re, str); + render_unique_exr_name(re, str, 0); if (BLI_is_writable(str)==0) { re->error("Can not save render buffers, check the temp default path"); return 0; } + /* no osa + fullsample won't work... */ + if(re->osa==0) + re->r.scemode &= ~R_FULL_SAMPLE; + + /* no fullsample and edge */ + if((re->r.scemode & R_FULL_SAMPLE) && (re->r.mode & R_EDGE)) { + re->error("Full Sample doesn't support Edge Enhance"); + return 0; + } + } + else + re->r.scemode &= ~R_FULL_SAMPLE; /* clear to be sure */ if(re->r.scemode & R_DOCOMP) { if(re->scene->use_nodes) { @@ -2152,7 +2412,7 @@ static int is_rendering_allowed(Render *re) } /* evaluating scene options for general Blender render */ -static int render_initialize_from_scene(Render *re, Scene *scene) +static int render_initialize_from_scene(Render *re, Scene *scene, int anim) { int winx, winy; rcti disprect; @@ -2178,31 +2438,27 @@ static int render_initialize_from_scene(Render *re, Scene *scene) disprect.ymax= winy; } - if(scene->r.scemode & R_EXR_TILE_FILE) { - int partx= winx/scene->r.xparts, party= winy/scene->r.yparts; - - /* stupid exr tiles dont like different sizes */ - if(winx != partx*scene->r.xparts || winy != party*scene->r.yparts) { - re->error("Sorry... exr tile saving only allowed with equally sized parts"); - return 0; - } - if((scene->r.mode & R_FIELDS) && (party & 1)) { - re->error("Sorry... exr tile saving only allowed with equally sized parts"); - return 0; - } + re->scene= scene; + + /* not too nice, but it survives anim-border render */ + if(anim) { + re->disprect= disprect; + return 1; } + /* check all scenes involved */ + tag_scenes_for_render(re); + if(scene->r.scemode & R_SINGLE_LAYER) push_render_result(re); - RE_InitState(re, &scene->r, winx, winy, &disprect); + RE_InitState(re, NULL, &scene->r, winx, winy, &disprect); if(!re->ok) /* if an error was printed, abort */ return 0; /* initstate makes new result, have to send changed tags around */ ntreeCompositTagRender(re->scene); - re->scene= scene; if(!is_rendering_allowed(re)) return 0; @@ -2221,7 +2477,7 @@ void RE_BlenderFrame(Render *re, Scene *scene, int frame) scene->r.cfra= frame; - if(render_initialize_from_scene(re, scene)) { + if(render_initialize_from_scene(re, scene, 0)) { do_render_all_options(re); } @@ -2301,13 +2557,15 @@ static void do_write_image_or_movie(Render *re, Scene *scene, bMovieHandle *mh) } /* saves images to disk */ -void RE_BlenderAnim(Render *re, Scene *scene, int sfra, int efra) +void RE_BlenderAnim(Render *re, Scene *scene, int sfra, int efra, int tfra) { bMovieHandle *mh= BKE_get_movie_handle(scene->r.imtype); + unsigned int lay; int cfrao= scene->r.cfra; + int nfra; - /* do not call for each frame, it initializes & pops output window */ - if(!render_initialize_from_scene(re, scene)) + /* do not fully call for each frame, it initializes & pops output window */ + if(!render_initialize_from_scene(re, scene, 0)) return; /* ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol */ @@ -2333,17 +2591,58 @@ void RE_BlenderAnim(Render *re, Scene *scene, int sfra, int efra) } } } else { - for(scene->r.cfra= sfra; - scene->r.cfra<=efra; scene->r.cfra++) { + for(nfra= sfra, scene->r.cfra= sfra; scene->r.cfra<=efra; scene->r.cfra++) { + char name[FILE_MAX]; + + /* only border now, todo: camera lens. (ton) */ + render_initialize_from_scene(re, scene, 1); + + if(nfra!=scene->r.cfra) { + /* + * Skip this frame, but update for physics and particles system. + * From convertblender.c: + * in localview, lamps are using normal layers, objects only local bits. + */ + if(scene->lay & 0xFF000000) + lay= scene->lay & 0xFF000000; + else + lay= scene->lay; + + scene_update_for_newframe(scene, lay); + continue; + } + else + nfra+= tfra; + + if (scene->r.mode & (R_NO_OVERWRITE | R_TOUCH) ) { + BKE_makepicstring(name, scene->r.pic, scene->r.cfra, scene->r.imtype); + } + + if (scene->r.mode & R_NO_OVERWRITE && BLI_exist(name)) { + printf("skipping existing frame \"%s\"\n", name); + continue; + } + if (scene->r.mode & R_TOUCH && !BLI_exist(name)) { + BLI_make_existing_file(name); /* makes the dir if its not there */ + BLI_touch(name); + } + re->r.cfra= scene->r.cfra; /* weak.... */ - + do_render_all_options(re); - + if(re->test_break() == 0) { do_write_image_or_movie(re, scene, mh); } - if(G.afbreek==1) break; + if(G.afbreek==1) { + /* remove touched file */ + if (scene->r.mode & R_TOUCH && BLI_exist(name) && BLI_filepathsize(name) == 0) { + BLI_delete(name, 0, 0); + } + + break; + } } } @@ -2359,7 +2658,7 @@ void RE_BlenderAnim(Render *re, Scene *scene, int sfra, int efra) /* note; repeated win/disprect calc... solve that nicer, also in compo */ -/* only temp file! */ +/* only the temp file! */ void RE_ReadRenderResult(Scene *scene, Scene *scenode) { Render *re; @@ -2387,17 +2686,32 @@ void RE_ReadRenderResult(Scene *scene, Scene *scenode) if(scenode) scene= scenode; - re= RE_NewRender(scene->id.name); - RE_InitState(re, &scene->r, winx, winy, &disprect); + /* get render: it can be called from UI with draw callbacks */ + re= RE_GetRender(scene->id.name); + if(re==NULL) + re= RE_NewRender(scene->id.name); + RE_InitState(re, NULL, &scene->r, winx, winy, &disprect); re->scene= scene; - read_render_result(re); + read_render_result(re, 0); } void RE_set_max_threads(int threads) { - if(threads>0 && threads<=BLENDER_MAX_THREADS) + if (threads==0) { + commandline_threads = BLI_system_thread_count(); + } else if(threads>=1 && threads<=BLENDER_MAX_THREADS) { commandline_threads= threads; - else + } else { printf("Error, threads has to be in range 1-%d\n", BLENDER_MAX_THREADS); + } +} + +void RE_init_threadcount(Render *re) +{ + if(commandline_threads >= 1) { /* only set as an arg in background mode */ + re->r.threads= MIN2(commandline_threads, BLENDER_MAX_THREADS); + } else if ((re->r.mode & R_FIXED_THREADS)==0 || commandline_threads == 0) { /* Automatic threads */ + re->r.threads = BLI_system_thread_count(); + } } diff --git a/source/blender/render/intern/source/pixelblending.c b/source/blender/render/intern/source/pixelblending.c index 119cceaf3c2..0e453d461ab 100644 --- a/source/blender/render/intern/source/pixelblending.c +++ b/source/blender/render/intern/source/pixelblending.c @@ -31,6 +31,7 @@ */ #include <math.h> +#include <string.h> /* global includes */ #include "BLI_arithb.h" @@ -91,15 +92,6 @@ void addAlphaOverFloat(float *dest, float *source) void addAlphaUnderFloat(float *dest, float *source) { float mul; - - if( (-RE_EMPTY_COLOR_FLOAT < dest[3]) - && (dest[3] < RE_EMPTY_COLOR_FLOAT) ) { - dest[0] = source[0]; - dest[1] = source[1]; - dest[2] = source[2]; - dest[3] = source[3]; - return; - } mul= 1.0 - dest[3]; @@ -212,6 +204,121 @@ void add_filt_fmask(unsigned int mask, float *col, float *rowbuf, int row_w) } } + +void mask_array(unsigned int mask, float filt[][3]) +{ + float **fmask1= R.samples->fmask1, **fmask2=R.samples->fmask2; + unsigned int maskand= (mask & 255); + unsigned int maskshift= (mask >>8); + int a, j; + + for(j=2; j>=0; j--) { + + a= j; + + filt[2][2-j]= *(fmask1[a] +maskand) + *(fmask2[a] +maskshift); + + a+=3; + + filt[1][2-j]= *(fmask1[a] +maskand) + *(fmask2[a] +maskshift); + + a+=3; + + filt[0][2-j]= *(fmask1[a] +maskand) + *(fmask2[a] +maskshift); + } +} + + +/* + +index ordering, scanline based: + + --- --- --- +| 2,0 | 2,1 | 2,2 | + --- --- --- +| 1,0 | 1,1 | 1,2 | + --- --- --- +| 0,0 | 0,1 | 0,2 | + --- --- --- +*/ + +void add_filt_fmask_coord(float filt[][3], float *col, float *rowbuf, int row_w, int col_h, int x, int y) +{ + float *fpoin[3][3]; + float val, r, g, b, al, lfilt[3][3]; + + r= col[0]; + g= col[1]; + b= col[2]; + al= col[3]; + + memcpy(lfilt, filt, sizeof(lfilt)); + + fpoin[0][1]= rowbuf-4*row_w; + fpoin[1][1]= rowbuf; + fpoin[2][1]= rowbuf+4*row_w; + + fpoin[0][0]= fpoin[0][1] - 4; + fpoin[1][0]= fpoin[1][1] - 4; + fpoin[2][0]= fpoin[2][1] - 4; + + fpoin[0][2]= fpoin[0][1] + 4; + fpoin[1][2]= fpoin[1][1] + 4; + fpoin[2][2]= fpoin[2][1] + 4; + + if(y==0) { + fpoin[0][0]= fpoin[1][0]; + fpoin[0][1]= fpoin[1][1]; + fpoin[0][2]= fpoin[1][2]; + /* filter needs the opposite value yes! */ + lfilt[0][0]= filt[2][0]; + lfilt[0][1]= filt[2][1]; + lfilt[0][2]= filt[2][2]; + } + else if(y==col_h-1) { + fpoin[2][0]= fpoin[1][0]; + fpoin[2][1]= fpoin[1][1]; + fpoin[2][2]= fpoin[1][2]; + + lfilt[2][0]= filt[0][0]; + lfilt[2][1]= filt[0][1]; + lfilt[2][2]= filt[0][2]; + } + + if(x==0) { + fpoin[2][0]= fpoin[2][1]; + fpoin[1][0]= fpoin[1][1]; + fpoin[0][0]= fpoin[0][1]; + + lfilt[2][0]= filt[2][2]; + lfilt[1][0]= filt[1][2]; + lfilt[0][0]= filt[0][2]; + } + else if(x==row_w-1) { + fpoin[2][2]= fpoin[2][1]; + fpoin[1][2]= fpoin[1][1]; + fpoin[0][2]= fpoin[0][1]; + + lfilt[2][2]= filt[2][0]; + lfilt[1][2]= filt[1][0]; + lfilt[0][2]= filt[0][0]; + } + + + /* loop unroll */ +#define MASKFILT(i, j) val= lfilt[i][j]; if(val!=0.0f) {float *fp= fpoin[i][j]; fp[0]+= val*r; fp[1]+= val*g; fp[2]+= val*b; fp[3]+= val*al; } + + MASKFILT(0, 0) + MASKFILT(0, 1) + MASKFILT(0, 2) + MASKFILT(1, 0) + MASKFILT(1, 1) + MASKFILT(1, 2) + MASKFILT(2, 0) + MASKFILT(2, 1) + MASKFILT(2, 2) +} + void add_filt_fmask_pixsize(unsigned int mask, float *in, float *rowbuf, int row_w, int pixsize) { /* calc the value of mask */ diff --git a/source/blender/render/intern/source/pixelshading.c b/source/blender/render/intern/source/pixelshading.c index cffed99c738..60723963af9 100644 --- a/source/blender/render/intern/source/pixelshading.c +++ b/source/blender/render/intern/source/pixelshading.c @@ -24,7 +24,9 @@ * ***** END GPL LICENSE BLOCK ***** */ +#include <float.h> #include <math.h> +#include <string.h> #include "BLI_arithb.h" /* External modules: */ @@ -43,6 +45,7 @@ #include "BKE_image.h" #include "BKE_global.h" +#include "BKE_material.h" #include "BKE_texture.h" #include "BKE_utildefines.h" @@ -55,6 +58,7 @@ #include "rendercore.h" #include "shadbuf.h" #include "pixelshading.h" +#include "sunsky.h" /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* defined in pipeline.c, is hardcopy of active dynamic allocated Render */ @@ -130,9 +134,15 @@ static void render_lighting_halo(HaloRen *har, float *colf) if(lar->mode & LA_TEXTURE) { ShadeInput shi; + + /* Warning, This is not that nice, and possibly a bit slow, + however some variables were not initialized properly in, unless using shade_input_initialize(...), we need to do a memset */ + memset(&shi, 0, sizeof(ShadeInput)); + /* end warning! - Campbell */ + VECCOPY(shi.co, rco); shi.osatex= 0; - do_lamp_tex(lar, lv, &shi, lacol); + do_lamp_tex(lar, lv, &shi, lacol, LA_TEXTURE); } if(lar->type==LA_SPOT) { @@ -175,7 +185,7 @@ static void render_lighting_halo(HaloRen *har, float *colf) inp= vn[0]*lv[0] + vn[1]*lv[1] + vn[2]*lv[2]; if(inp>0.0) { /* testshadowbuf==0.0 : 100% shadow */ - shadfac = testshadowbuf(lar->shb, rco, dco, dco, inp); + shadfac = testshadowbuf(&R, lar->shb, rco, dco, dco, inp, 0.0f); if( shadfac>0.0 ) { shadfac*= inp*soft*lar->energy; ir -= shadfac; @@ -212,7 +222,7 @@ static void render_lighting_halo(HaloRen *har, float *colf) if(i> -0.41) { /* heuristic valua! */ shadfac= 1.0; if(lar->shb) { - shadfac = testshadowbuf(lar->shb, rco, dco, dco, inp); + shadfac = testshadowbuf(&R, lar->shb, rco, dco, dco, inp, 0.0f); if(shadfac==0.0) continue; i*= shadfac; } @@ -236,8 +246,36 @@ static void render_lighting_halo(HaloRen *har, float *colf) } +/** + * Converts a halo z-buffer value to distance from the camera's near plane + * @param z The z-buffer value to convert + * @return a distance from the camera's near plane in blender units + */ +static float haloZtoDist(int z) +{ + float zco = 0; -void shadeHaloFloat(HaloRen *har, float *col, int zz, + if(z >= 0x7FFFFF) + return 10e10; + else { + zco = (float)z/(float)0x7FFFFF; + if(R.r.mode & R_ORTHO) + return (R.winmat[3][2] - zco*R.winmat[3][3])/(R.winmat[2][2]); + else + return (R.winmat[3][2])/(R.winmat[2][2] - R.winmat[2][3]*zco); + } +} + +/** + * @param col (float[4]) Store the rgb color here (with alpha) + * The alpha is used to blend the color to the background + * color_new = (1-alpha)*color_background + color + * @param zz The current zbuffer value at the place of this pixel + * @param dist Distance of the pixel from the center of the halo squared. Given in pixels + * @param xn The x coordinate of the pixel relaticve to the center of the halo. given in pixels + * @param yn The y coordinate of the pixel relaticve to the center of the halo. given in pixels + */ +int shadeHaloFloat(HaloRen *har, float *col, int zz, float dist, float xn, float yn, short flarec) { /* fill in col */ @@ -256,12 +294,40 @@ void shadeHaloFloat(HaloRen *har, float *col, int zz, } else alpha= har->alfa; - if(alpha==0.0) { - col[0] = 0.0; - col[1] = 0.0; - col[2] = 0.0; - col[3] = 0.0; - return; + if(alpha==0.0) + return 0; + + /* soften the halo if it intersects geometry */ + if(har->mat && har->mat->mode & MA_HALO_SOFT) { + float segment_length, halo_depth, distance_from_z, visible_depth, soften; + + /* calculate halo depth */ + segment_length= har->hasize*sasqrt(1.0f - dist/(har->rad*har->rad)); + halo_depth= 2.0f*segment_length; + + if(halo_depth < FLT_EPSILON) + return 0; + + /* calculate how much of this depth is visible */ + distance_from_z = haloZtoDist(zz) - haloZtoDist(har->zs); + visible_depth = halo_depth; + if(distance_from_z < segment_length) { + soften= (segment_length + distance_from_z)/halo_depth; + + /* apply softening to alpha */ + if(soften < 1.0f) + alpha *= soften; + if(alpha <= 0.0f) + return 0; + } + } + else { + /* not a soft halo. use the old softening code */ + /* halo being intersected? */ + if(har->zs> zz-har->zd) { + t= ((float)(zz-har->zs))/(float)har->zd; + alpha*= sqrt(sqrt(t)); + } } radist= sqrt(dist); @@ -359,21 +425,10 @@ void shadeHaloFloat(HaloRen *har, float *col, int zz, if(ster<1.0) dist*= sqrt(ster); } } - - /* halo being intersected? */ - if(har->zs> zz-har->zd) { - t= ((float)(zz-har->zs))/(float)har->zd; - alpha*= sqrt(sqrt(t)); - } /* disputable optimize... (ton) */ - if(dist<=0.00001) { - col[0] = 0.0; - col[1] = 0.0; - col[2] = 0.0; - col[3] = 0.0; - return; - } + if(dist<=0.00001) + return 0; dist*= alpha; ringf*= dist; @@ -434,6 +489,8 @@ void shadeHaloFloat(HaloRen *har, float *col, int zz, /* alpha requires clip, gives black dots */ if(col[3] > 1.0f) col[3]= 1.0f; + + return 1; } /* ------------------------------------------------------------------------- */ @@ -512,13 +569,48 @@ void shadeSkyView(float *colf, float *rco, float *view, float *dxyview) } } +/* shade sky according to sun lamps, all parameters are like shadeSkyView except sunsky*/ +void shadeSunView(float *colf, float *view) +{ + GroupObject *go; + LampRen *lar; + float sview[3]; + int do_init= 1; + + 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_SKY)){ + float sun_collector[3]; + float colorxyz[3]; + + if(do_init) { + + VECCOPY(sview, view); + Normalize(sview); + MTC_Mat3MulVecfl(R.imat, sview); + if (sview[2] < 0.0) + sview[2] = 0.0; + Normalize(sview); + do_init= 0; + } + + GetSkyXYZRadiancef(lar->sunsky, sview, colorxyz); + xyz_to_rgb(colorxyz[0], colorxyz[1], colorxyz[2], &sun_collector[0], &sun_collector[1], &sun_collector[2], + lar->sunsky->sky_colorspace); + + ramp_blend(lar->sunsky->skyblendtype, colf, colf+1, colf+2, lar->sunsky->skyblendfac, sun_collector); + } + } +} + + /* Stuff the sky color into the collector. */ void shadeSkyPixel(float *collector, float fx, float fy) { float view[3], dxyview[2]; - + /* The rules for sky: 1. Draw an image, if a background image was provided. Stop @@ -530,7 +622,6 @@ void shadeSkyPixel(float *collector, float fx, float fy) /* 1. Do a backbuffer image: */ if(R.r.bufflag & 1) { fillBackgroundImage(collector, fx, fy); - return; } else if((R.wrld.skytype & (WO_SKYBLEND+WO_SKYTEX))==0) { /* 2. solid color */ @@ -565,7 +656,20 @@ void shadeSkyPixel(float *collector, float fx, float fy) shadeSkyView(collector, NULL, view, dxyview); collector[3] = 0.0f; } + + calc_view_vector(view, fx, fy); + shadeSunView(collector, view); } +/* aerial perspective */ +void shadeAtmPixel(struct SunSky *sunsky, float *collector, float fx, float fy, float distance) +{ + float view[3]; + + calc_view_vector(view, fx, fy); + Normalize(view); + /*MTC_Mat3MulVecfl(R.imat, view);*/ + AtmospherePixleShader(sunsky, view, distance, collector); +} /* eof */ diff --git a/source/blender/render/intern/source/rayshade.c b/source/blender/render/intern/source/rayshade.c index 69e90a7537c..f822d41bb85 100644 --- a/source/blender/render/intern/source/rayshade.c +++ b/source/blender/render/intern/source/rayshade.c @@ -40,8 +40,9 @@ #include "BKE_utildefines.h" #include "BLI_arithb.h" -#include "BLI_rand.h" +#include "BLI_blenlib.h" #include "BLI_jitter.h" +#include "BLI_rand.h" #include "PIL_time.h" @@ -77,15 +78,21 @@ static void vlr_face_coords(RayFace *face, float **v1, float **v2, float **v3, f *v4 = (vlr->v4)? vlr->v4->co: NULL; } -static int vlr_check_intersect(Isect *is, RayFace *face) +static int vlr_check_intersect(Isect *is, int ob, RayFace *face) { + ObjectInstanceRen *obi= RAY_OBJECT_GET((Render*)is->userdata, ob); VlakRen *vlr = (VlakRen*)face; + /* for baking selected to active non-traceable materials might still + * be in the raytree */ + if(!(vlr->mat->mode & MA_TRACEBLE)) + return 0; + /* I know... cpu cycle waste, might do smarter once */ if(is->mode==RE_RAY_MIRROR) return !(vlr->mat->mode & MA_ONLYCAST); else - return (is->lay & vlr->lay); + return (is->lay & obi->lay); } static float *vlr_get_transform(void *userdata, int i) @@ -110,7 +117,7 @@ void makeraytree(Render *re) VlakRen *vlr= NULL; float min[3], max[3], co1[3], co2[3], co3[3], co4[3]; double lasttime= PIL_check_seconds_timer(); - int v, totface = 0; + int v, totv = 0, totface = 0; INIT_MINMAX(min, max); @@ -124,7 +131,8 @@ void makeraytree(Render *re) for(v=0;v<obr->totvlak;v++) { if((v & 255)==0) vlr= obr->vlaknodes[v>>8].vlak; else vlr++; - if(vlr->mat->mode & MA_TRACEBLE) { + /* baking selected to active needs non-traceable too */ + if((re->flag & R_BAKE_TRACE) || (vlr->mat->mode & MA_TRACEBLE)) { if((vlr->mat->mode & MA_WIRE)==0) { VECCOPY(co1, vlr->v1->co); VECCOPY(co2, vlr->v2->co); @@ -167,7 +175,7 @@ void makeraytree(Render *re) if(re->excludeob && obr->ob == re->excludeob) continue; - for(v=0; v<obr->totvlak; v++) { + for(v=0; v<obr->totvlak; v++, totv++) { if((v & 255)==0) { double time= PIL_check_seconds_timer(); @@ -176,7 +184,7 @@ void makeraytree(Render *re) break; if(time-lasttime>1.0f) { char str[32]; - sprintf(str, "Filling Octree: %d", v); + sprintf(str, "Filling Octree: %d", totv); re->i.infostr= str; re->stats_draw(&re->i); re->i.infostr= NULL; @@ -185,7 +193,7 @@ void makeraytree(Render *re) } else vlr++; - if(vlr->mat->mode & MA_TRACEBLE) + if((re->flag & R_BAKE_TRACE) || (vlr->mat->mode & MA_TRACEBLE)) if((vlr->mat->mode & MA_WIRE)==0) RE_ray_tree_add_face(re->raytree, RAY_OBJECT_SET(re, obi), vlr); } @@ -255,7 +263,12 @@ static void shade_ray(Isect *is, ShadeInput *shi, ShadeResult *shr) shade_input_set_shade_texco(shi); if(is->mode==RE_RAY_SHADOW_TRA) - shade_color(shi, shr); + 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_color(shi, shr); else { if(shi->mat->nodetree && shi->mat->use_nodes) { ntreeShaderExecTree(shi->mat->nodetree, shi, shr); @@ -376,7 +389,7 @@ static float shade_by_transmission(Isect *is, ShadeInput *shi, ShadeResult *shr) static void ray_fadeout_endcolor(float *col, ShadeInput *origshi, ShadeInput *shi, ShadeResult *shr, Isect *isec, float *vec) { - /* un-intersected rays get either rendered material colour or sky colour */ + /* un-intersected rays get either rendered material color or sky color */ if (origshi->mat->fadeto_mir == MA_RAYMIR_FADETOMAT) { VECCOPY(col, shr->combined); } else if (origshi->mat->fadeto_mir == MA_RAYMIR_FADETOSKY) { @@ -384,12 +397,13 @@ static void ray_fadeout_endcolor(float *col, ShadeInput *origshi, ShadeInput *sh Normalize(shi->view); shadeSkyView(col, isec->start, shi->view, NULL); + shadeSunView(col, shi->view); } } static void ray_fadeout(Isect *is, ShadeInput *shi, float *col, float *blendcol, float dist_mir) { - /* if fading out, linear blend against fade colour */ + /* if fading out, linear blend against fade color */ float blendfac; blendfac = 1.0 - VecLenf(shi->co, is->start)/dist_mir; @@ -409,6 +423,11 @@ static void traceray(ShadeInput *origshi, ShadeResult *origshr, short depth, flo float ref[3], maxsize=RE_ray_tree_max_size(R.raytree); float dist_mir = origshi->mat->dist_mir; + /* Warning, This is not that nice, and possibly a bit slow for every ray, + however some variables were not initialized properly in, unless using shade_input_initialize(...), we need to do a memset */ + memset(&shi, 0, sizeof(ShadeInput)); + /* end warning! - Campbell */ + VECCOPY(isec.start, start); if (dist_mir > 0.0) { isec.end[0]= start[0]+dist_mir*vec[0]; @@ -430,13 +449,13 @@ static void traceray(ShadeInput *origshi, ShadeResult *origshr, short depth, flo shi.osatex= origshi->osatex; shi.depth= 1; /* only used to indicate tracing */ shi.thread= origshi->thread; - shi.sample= 0; + //shi.sample= 0; // memset above, so dont need this shi.xs= origshi->xs; shi.ys= origshi->ys; shi.lay= origshi->lay; shi.passflag= SCE_PASS_COMBINED; /* result of tracing needs no pass info */ shi.combinedflag= 0xFFFFFF; /* ray trace does all options */ - shi.do_preview= 0; + //shi.do_preview= 0; // memset above, so dont need this shi.light_override= origshi->light_override; shi.mat_override= origshi->mat_override; @@ -531,8 +550,8 @@ static void traceray(ShadeInput *origshi, ShadeResult *origshr, short depth, flo if (dist_mir > 0.0) { float blendcol[3]; - /* max ray distance set, but found an intersection, so fade this colour - * out towards the sky/material colour for a smooth transition */ + /* max ray distance set, but found an intersection, so fade this color + * out towards the sky/material color for a smooth transition */ ray_fadeout_endcolor(blendcol, origshi, &shi, origshr, &isec, vec); ray_fadeout(&isec, &shi, col, blendcol, dist_mir); } @@ -615,24 +634,27 @@ void init_jitter_plane(LampRen *lar) /* at least 4, or max threads+1 tables */ if(BLENDER_MAX_THREADS < 4) x= 4; else x= BLENDER_MAX_THREADS+1; - fp= lar->jitter= MEM_mallocN(x*tot*2*sizeof(float), "lamp jitter tab"); - - /* set per-lamp fixed seed */ - BLI_srandom(tot); - - /* fill table with random locations, area_size large */ - for(x=0; x<tot; x++, fp+=2) { - fp[0]= (BLI_frand()-0.5)*lar->area_size; - fp[1]= (BLI_frand()-0.5)*lar->area_sizey; - } + fp= lar->jitter= MEM_callocN(x*tot*2*sizeof(float), "lamp jitter tab"); - while(iter--) { - fp= lar->jitter; - for(x=tot; x>0; x--, fp+=2) { - DP_energy(lar->jitter, fp, tot, lar->area_size, lar->area_sizey); + /* if 1 sample, we leave table to be zero's */ + if(tot>1) { + + /* set per-lamp fixed seed */ + BLI_srandom(tot); + + /* fill table with random locations, area_size large */ + for(x=0; x<tot; x++, fp+=2) { + fp[0]= (BLI_frand()-0.5)*lar->area_size; + fp[1]= (BLI_frand()-0.5)*lar->area_sizey; } - } - + + while(iter--) { + fp= lar->jitter; + for(x=tot; x>0; x--, fp+=2) { + DP_energy(lar->jitter, fp, tot, lar->area_size, lar->area_sizey); + } + } + } /* create the dithered tables (could just check lamp type!) */ jitter_plane_offset(lar->jitter, lar->jitter+2*tot, tot, lar->area_size, lar->area_sizey, 0.5f, 0.0f); jitter_plane_offset(lar->jitter, lar->jitter+4*tot, tot, lar->area_size, lar->area_sizey, 0.5f, 0.5f); @@ -715,10 +737,10 @@ static void hammersley_create(double *out, int n) } } -struct QMCSampler *QMC_initSampler(int type, int tot) +static struct QMCSampler *QMC_initSampler(int type, int tot) { - QMCSampler *qsa = MEM_mallocN(sizeof(QMCSampler), "qmc sampler"); - qsa->samp2d = MEM_mallocN(2*sizeof(double)*tot, "qmc sample table"); + QMCSampler *qsa = MEM_callocN(sizeof(QMCSampler), "qmc sampler"); + qsa->samp2d = MEM_callocN(2*sizeof(double)*tot, "qmc sample table"); qsa->tot = tot; qsa->type = type; @@ -859,25 +881,55 @@ static void QMC_sampleHemiCosine(float *vec, QMCSampler *qsa, int thread, int nu #endif /* called from convertBlenderScene.c */ -/* samples don't change per pixel, so build the samples in advance for efficiency */ -void init_lamp_hammersley(LampRen *lar) +void init_render_qmcsampler(Render *re) { - lar->qsa = QMC_initSampler(SAMP_TYPE_HAMMERSLEY, lar->ray_totsamp); + re->qmcsamplers= MEM_callocN(sizeof(ListBase)*BLENDER_MAX_THREADS, "QMCListBase"); } -void init_render_hammersley(Render *re) +static QMCSampler *get_thread_qmcsampler(Render *re, int thread, int type, int tot) { - re->qsa = QMC_initSampler(SAMP_TYPE_HAMMERSLEY, (re->wrld.aosamp * re->wrld.aosamp)); + QMCSampler *qsa; + + /* create qmc samplers as needed, since recursion makes it hard to + * predict how many are needed */ + + for(qsa=re->qmcsamplers[thread].first; qsa; qsa=qsa->next) { + if(qsa->type == type && qsa->tot == tot && !qsa->used) { + qsa->used= 1; + return qsa; + } + } + + qsa= QMC_initSampler(type, tot); + qsa->used= 1; + BLI_addtail(&re->qmcsamplers[thread], qsa); + + return qsa; } -void free_lamp_qmcsampler(LampRen *lar) +static void release_thread_qmcsampler(Render *re, int thread, QMCSampler *qsa) { - QMC_freeSampler(lar->qsa); + qsa->used= 0; } void free_render_qmcsampler(Render *re) { - QMC_freeSampler(re->qsa); + QMCSampler *qsa, *next; + int a; + + if(re->qmcsamplers) { + for(a=0; a<BLENDER_MAX_THREADS; a++) { + for(qsa=re->qmcsamplers[a].first; qsa; qsa=next) { + next= qsa->next; + QMC_freeSampler(qsa); + } + + re->qmcsamplers[a].first= re->qmcsamplers[a].last= NULL; + } + + MEM_freeN(re->qmcsamplers); + re->qmcsamplers= NULL; + } } static int adaptive_sample_variance(int samples, float *col, float *colsq, float thresh) @@ -954,7 +1006,7 @@ static void trace_refract(float *col, ShadeInput *shi, ShadeResult *shr) else samp_type = SAMP_TYPE_HAMMERSLEY; /* all samples are generated per pixel */ - qsa = QMC_initSampler(samp_type, max_samples); + qsa = get_thread_qmcsampler(&R, shi->thread, samp_type, max_samples); QMC_initPixel(qsa, shi->thread); } else max_samples = 1; @@ -1012,7 +1064,8 @@ static void trace_refract(float *col, ShadeInput *shi, ShadeResult *shr) col[2] /= (float)samples; col[3] /= (float)samples; - if (qsa) QMC_freeSampler(qsa); + if (qsa) + release_thread_qmcsampler(&R, shi->thread, qsa); } static void trace_reflect(float *col, ShadeInput *shi, ShadeResult *shr, float fresnelfac) @@ -1039,7 +1092,7 @@ static void trace_reflect(float *col, ShadeInput *shi, ShadeResult *shr, float f else samp_type = SAMP_TYPE_HAMMERSLEY; /* all samples are generated per pixel */ - qsa = QMC_initSampler(samp_type, max_samples); + qsa = get_thread_qmcsampler(&R, shi->thread, samp_type, max_samples); QMC_initPixel(qsa, shi->thread); } else max_samples = 1; @@ -1117,13 +1170,13 @@ static void trace_reflect(float *col, ShadeInput *shi, ShadeResult *shr, float f col[1] /= (float)samples; col[2] /= (float)samples; - if (qsa) QMC_freeSampler(qsa); + if (qsa) + release_thread_qmcsampler(&R, shi->thread, qsa); } /* extern call from render loop */ void ray_trace(ShadeInput *shi, ShadeResult *shr) { - VlakRen *vlr; float i, f, f1, fr, fg, fb; float mircol[4], tracol[4]; float diff[3]; @@ -1131,7 +1184,7 @@ void ray_trace(ShadeInput *shi, ShadeResult *shr) do_tra= ((shi->mat->mode & (MA_RAYTRANSP)) && shr->alpha!=1.0f); do_mir= ((shi->mat->mode & MA_RAYMIRROR) && shi->ray_mirror!=0.0f); - vlr= shi->vlr; + /* raytrace mirror amd refract like to separate the spec color */ if(shi->combinedflag & SCE_PASS_SPEC) @@ -1231,16 +1284,22 @@ static void ray_trace_shadow_tra(Isect *is, int depth, int traflag) float d= 1.0f; /* we got a face */ + /* Warning, This is not that nice, and possibly a bit slow for every ray, + however some variables were not initialized properly in, unless using shade_input_initialize(...), we need to do a memset */ + memset(&shi, 0, sizeof(ShadeInput)); + /* end warning! - Campbell */ + shi.depth= 1; /* only used to indicate tracing */ shi.mask= 1; - shi.osatex= 0; + + /*shi.osatex= 0; shi.thread= shi.sample= 0; shi.lay= 0; shi.passflag= 0; shi.combinedflag= 0; shi.do_preview= 0; shi.light_override= NULL; - shi.mat_override= NULL; + shi.mat_override= NULL;*/ shade_ray(is, &shi, &shr); if (traflag & RAY_TRA) @@ -1300,6 +1359,12 @@ int ray_trace_shadow_rad(ShadeInput *ship, ShadeResult *shr) if(RE_ray_tree_intersect(R.raytree, &isec)) { float fac; + + /* Warning, This is not that nice, and possibly a bit slow for every ray, + however some variables were not initialized properly in, unless using shade_input_initialize(...), we need to do a memset */ + memset(&shi, 0, sizeof(ShadeInput)); + /* end warning! - Campbell */ + shade_ray(&isec, &shi, &shr_t); fac= isec.labda*isec.labda; fac= 1.0f; @@ -1416,14 +1481,15 @@ static float *sphere_sampler(int type, int resol, int thread, int xs, int ys) int tot; float *vec; - if(resol>16) resol= 16; - tot= 2*resol*resol; if (type & WO_AORNDSMP) { - static float sphere[2*3*256]; + float *sphere; int a; + // always returns table + sphere= threadsafe_table_sphere(0, thread, xs, ys, tot); + /* total random sampling. NOT THREADSAFE! (should be removed, is not useful) */ vec= sphere; for (a=0; a<tot; a++, vec+=3) { @@ -1438,7 +1504,8 @@ static float *sphere_sampler(int type, int resol, int thread, int xs, int ys) float ang, *vec1; int a; - sphere= threadsafe_table_sphere(1, thread, xs, ys, tot); // returns table if xs and ys were equal to last call + // returns table if xs and ys were equal to last call + sphere= threadsafe_table_sphere(1, thread, xs, ys, tot); if(sphere==NULL) { sphere= threadsafe_table_sphere(0, thread, xs, ys, tot); @@ -1460,7 +1527,7 @@ static float *sphere_sampler(int type, int resol, int thread, int xs, int ys) } } -void ray_ao_qmc(ShadeInput *shi, float *shadfac) +static void ray_ao_qmc(ShadeInput *shi, float *shadfac) { Isect isec; QMCSampler *qsa=NULL; @@ -1471,7 +1538,6 @@ void ray_ao_qmc(ShadeInput *shi, float *shadfac) float fac=0.0f, prev=0.0f; float adapt_thresh = G.scene->world->ao_adapt_thresh; float adapt_speed_fac = G.scene->world->ao_adapt_speed_fac; - float bias = G.scene->world->aobias; int samples=0; int max_samples = R.wrld.aosamp*R.wrld.aosamp; @@ -1485,7 +1551,6 @@ void ray_ao_qmc(ShadeInput *shi, float *shadfac) isec.ob_last= 0; isec.mode= (R.wrld.aomode & WO_AODIST)?RE_RAY_SHADOW_TRA:RE_RAY_SHADOW; isec.lay= -1; - VECCOPY(isec.start, shi->co); shadfac[0]= shadfac[1]= shadfac[2]= 0.0f; @@ -1500,13 +1565,10 @@ void ray_ao_qmc(ShadeInput *shi, float *shadfac) dxyview[2]= 0.0f; } - /* bias prevents smoothed faces to appear flat */ if(shi->vlr->flag & R_SMOOTH) { - bias= G.scene->world->aobias; VECCOPY(nrm, shi->vn); } else { - bias= 0.0f; VECCOPY(nrm, shi->facenor); } @@ -1521,9 +1583,9 @@ void ray_ao_qmc(ShadeInput *shi, float *shadfac) max_samples /= speedfac; if (max_samples < 5) max_samples = 5; - qsa = QMC_initSampler(SAMP_TYPE_HALTON, max_samples); + qsa = get_thread_qmcsampler(&R, shi->thread, SAMP_TYPE_HALTON, max_samples); } else if (R.wrld.ao_samp_method==WO_AOSAMP_HAMMERSLEY) - qsa = R.qsa; + qsa = get_thread_qmcsampler(&R, shi->thread, SAMP_TYPE_HAMMERSLEY, max_samples); QMC_initPixel(qsa, shi->thread); @@ -1538,6 +1600,7 @@ void ray_ao_qmc(ShadeInput *shi, float *shadfac) Normalize(dir); + VECCOPY(isec.start, shi->co); isec.end[0] = shi->co[0] - maxdist*dir[0]; isec.end[1] = shi->co[1] - maxdist*dir[1]; isec.end[2] = shi->co[2] - maxdist*dir[2]; @@ -1565,6 +1628,7 @@ void ray_ao_qmc(ShadeInput *shi, float *shadfac) } else { /* WO_AOSKYTEX */ shadeSkyView(skycol, isec.start, view, dxyview); + shadeSunView(skycol, shi->view); shadfac[0]+= skycol[0]; shadfac[1]+= skycol[1]; shadfac[2]+= skycol[2]; @@ -1595,17 +1659,18 @@ void ray_ao_qmc(ShadeInput *shi, float *shadfac) shadfac[0]= shadfac[1]= shadfac[2]= 1.0f - fac/(float)samples; } - if ((qsa) && (qsa->type == SAMP_TYPE_HALTON)) QMC_freeSampler(qsa); + if (qsa) + release_thread_qmcsampler(&R, shi->thread, qsa); } /* extern call from shade_lamp_loop, ambient occlusion calculus */ -void ray_ao_spheresamp(ShadeInput *shi, float *shadfac) +static void ray_ao_spheresamp(ShadeInput *shi, float *shadfac) { Isect isec; float *vec, *nrm, div, bias, sh=0.0f; float maxdist = R.wrld.aodist; float dxyview[3]; - int j= -1, tot, actual=0, skyadded=0, aocolor; + int j= -1, tot, actual=0, skyadded=0, aocolor, resol= R.wrld.aosamp; isec.faceorig= (RayFace*)shi->vlr; isec.oborig= RAY_OBJECT_SET(&R, shi->obi); @@ -1632,14 +1697,16 @@ void ray_ao_spheresamp(ShadeInput *shi, float *shadfac) if(shi->mat->mode & MA_ONLYSHADOW) aocolor= WO_AOPLAIN; - vec= sphere_sampler(R.wrld.aomode, R.wrld.aosamp, shi->thread, shi->xs, shi->ys); + if(resol>32) resol= 32; + + vec= sphere_sampler(R.wrld.aomode, resol, shi->thread, shi->xs, shi->ys); // warning: since we use full sphere now, and dotproduct is below, we do twice as much - tot= 2*R.wrld.aosamp*R.wrld.aosamp; + tot= 2*resol*resol; if(aocolor == WO_AOSKYTEX) { - dxyview[0]= 1.0f/(float)R.wrld.aosamp; - dxyview[1]= 1.0f/(float)R.wrld.aosamp; + dxyview[0]= 1.0f/(float)resol; + dxyview[1]= 1.0f/(float)resol; dxyview[2]= 0.0f; } @@ -1686,6 +1753,7 @@ void ray_ao_spheresamp(ShadeInput *shi, float *shadfac) } else { /* WO_AOSKYTEX */ shadeSkyView(skycol, isec.start, view, dxyview); + shadeSunView(skycol, shi->view); shadfac[0]+= skycol[0]; shadfac[1]+= skycol[1]; shadfac[2]+= skycol[2]; @@ -1724,21 +1792,62 @@ void ray_ao(ShadeInput *shi, float *shadfac) ray_ao_spheresamp(shi, shadfac); } +static void ray_shadow_jittered_coords(ShadeInput *shi, int max, float jitco[RE_MAX_OSA][3], int *totjitco) +{ + /* magic numbers for reordering sample positions to give better + * results with adaptive sample, when it usually only takes 4 samples */ + int order8[8] = {0, 1, 5, 6, 2, 3, 4, 7}; + int order11[11] = {1, 3, 8, 10, 0, 2, 4, 5, 6, 7, 9}; + int order16[16] = {1, 3, 9, 12, 0, 6, 7, 8, 13, 2, 4, 5, 10, 11, 14, 15}; + int count = count_mask(shi->mask); + + /* for better antialising shadow samples are distributed over the subpixel + * sample coordinates, this only works for raytracing depth 0 though */ + if(!shi->strand && shi->depth == 0 && count > 1 && count <= max) { + float xs, ys, zs, view[3]; + int samp, ordsamp, tot= 0; + + for(samp=0; samp<R.osa; samp++) { + if(R.osa == 8) ordsamp = order8[samp]; + else if(R.osa == 11) ordsamp = order11[samp]; + else if(R.osa == 16) ordsamp = order16[samp]; + else ordsamp = samp; + + if(shi->mask & (1<<ordsamp)) { + /* zbuffer has this inverse corrected, ensures xs,ys are inside pixel */ + xs= (float)shi->scanco[0] + R.jit[ordsamp][0] + 0.5f; + ys= (float)shi->scanco[1] + R.jit[ordsamp][1] + 0.5f; + zs= shi->scanco[2]; + + shade_input_calc_viewco(shi, xs, ys, zs, view, NULL, jitco[tot], NULL, NULL); + tot++; + } + } + + *totjitco= tot; + } + else { + VECCOPY(jitco[0], shi->co); + *totjitco= 1; + } +} static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float *shadfac, Isect *isec) { QMCSampler *qsa=NULL; - QMCSampler *qsa_jit=NULL; int samples=0; - float samp3d[3], jit[3]; + float samp3d[3]; float fac=0.0f, vec[3]; float colsq[4]; float adapt_thresh = lar->adapt_thresh; - int max_samples = lar->ray_totsamp; - float pos[3]; + int min_adapt_samples=4, max_samples = lar->ray_totsamp; + float *co; int do_soft=1, full_osa=0; + float jitco[RE_MAX_OSA][3]; + int totjitco; + colsq[0] = colsq[1] = colsq[2] = 0.0; if(isec->mode==RE_RAY_SHADOW_TRA) { shadfac[0]= shadfac[1]= shadfac[2]= shadfac[3]= 0.0f; @@ -1756,17 +1865,15 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * else max_samples = (R.osa > 4)?R.osa:5; } + ray_shadow_jittered_coords(shi, max_samples, jitco, &totjitco); + /* sampling init */ - if (lar->ray_samp_method==LA_SAMP_HALTON) { - qsa = QMC_initSampler(SAMP_TYPE_HALTON, max_samples); - qsa_jit = QMC_initSampler(SAMP_TYPE_HALTON, max_samples); - } else if (lar->ray_samp_method==LA_SAMP_HAMMERSLEY) { - qsa = lar->qsa; - qsa_jit = QMC_initSampler(SAMP_TYPE_HAMMERSLEY, max_samples); - } + if (lar->ray_samp_method==LA_SAMP_HALTON) + qsa = get_thread_qmcsampler(&R, shi->thread, SAMP_TYPE_HALTON, max_samples); + else if (lar->ray_samp_method==LA_SAMP_HAMMERSLEY) + qsa = get_thread_qmcsampler(&R, shi->thread, SAMP_TYPE_HAMMERSLEY, max_samples); QMC_initPixel(qsa, shi->thread); - QMC_initPixel(qsa_jit, shi->thread); VECCOPY(vec, lampco); @@ -1774,18 +1881,11 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * while (samples < max_samples) { isec->faceorig= (RayFace*)shi->vlr; isec->oborig= RAY_OBJECT_SET(&R, shi->obi); - + /* manually jitter the start shading co-ord per sample * based on the pre-generated OSA texture sampling offsets, * for anti-aliasing sharp shadow edges. */ - VECCOPY(pos, shi->co); - if (shi->vlr && !full_osa) { - QMC_sampleRect(jit, qsa_jit, shi->thread, samples, 1.0, 1.0); - - pos[0] += shi->dxco[0]*jit[0] + shi->dyco[0]*jit[1]; - pos[1] += shi->dxco[1]*jit[0] + shi->dyco[1]*jit[1]; - pos[2] += shi->dxco[2]*jit[0] + shi->dyco[2]*jit[1]; - } + co = jitco[samples % totjitco]; if (do_soft) { /* sphere shadow source */ @@ -1793,9 +1893,9 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * float ru[3], rv[3], v[3], s[3]; /* calc tangent plane vectors */ - v[0] = pos[0] - lampco[0]; - v[1] = pos[1] - lampco[1]; - v[2] = pos[2] - lampco[2]; + v[0] = co[0] - lampco[0]; + v[1] = co[1] - lampco[1]; + v[2] = co[2] - lampco[2]; Normalize(v); VecOrthoBasisf(v, ru, rv); @@ -1822,8 +1922,21 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * } else { VECCOPY(isec->end, vec); } - VECCOPY(isec->start, pos); - + + if(shi->strand) { + /* bias away somewhat to avoid self intersection */ + float jitbias= 0.5f*(VecLength(shi->dxco) + VecLength(shi->dyco)); + float v[3]; + + VECSUB(v, co, isec->end); + Normalize(v); + + co[0] -= jitbias*v[0]; + co[1] -= jitbias*v[1]; + co[2] -= jitbias*v[2]; + } + + VECCOPY(isec->start, co); /* trace the ray */ if(isec->mode==RE_RAY_SHADOW_TRA) { @@ -1850,7 +1963,7 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * if ((lar->ray_samp_method == LA_SAMP_HALTON)) { /* adaptive sampling - consider samples below threshold as in shadow (or vice versa) and exit early */ - if ((max_samples > 4) && (adapt_thresh > 0.0) && (samples > max_samples / 3)) { + if ((max_samples > min_adapt_samples) && (adapt_thresh > 0.0) && (samples > max_samples / 3)) { if (isec->mode==RE_RAY_SHADOW_TRA) { if ((shadfac[3] / samples > (1.0-adapt_thresh)) || (shadfac[3] / samples < adapt_thresh)) break; @@ -1872,8 +1985,8 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * } else shadfac[3]= 1.0f-fac/samples; - if (qsa_jit) QMC_freeSampler(qsa_jit); - if ((qsa) && (qsa->type == SAMP_TYPE_HALTON)) QMC_freeSampler(qsa); + if (qsa) + release_thread_qmcsampler(&R, shi->thread, qsa); } static void ray_shadow_jitter(ShadeInput *shi, LampRen *lar, float *lampco, float *shadfac, Isect *isec) @@ -1964,7 +2077,10 @@ void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) if(shi->mat->mode & MA_SHADOW_TRA) isec.mode= RE_RAY_SHADOW_TRA; else isec.mode= RE_RAY_SHADOW; - if(lar->mode & LA_LAYER) isec.lay= lar->lay; else isec.lay= -1; + if(lar->mode & (LA_LAYER|LA_LAYER_SHADOW)) + isec.lay= lar->lay; + else + isec.lay= -1; /* only when not mir tracing, first hit optimm */ if(shi->depth==0) { @@ -2025,7 +2141,7 @@ void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) } /* only when face points away from lamp, in direction of lamp, trace ray and find first exit point */ -void ray_translucent(ShadeInput *shi, LampRen *lar, float *distfac, float *co) +static void ray_translucent(ShadeInput *shi, LampRen *lar, float *distfac, float *co) { Isect isec; float lampco[3], maxsize; diff --git a/source/blender/render/intern/source/raytrace.c b/source/blender/render/intern/source/raytrace.c index 54e8e4cfab5..ec47df74d04 100644 --- a/source/blender/render/intern/source/raytrace.c +++ b/source/blender/render/intern/source/raytrace.c @@ -635,6 +635,8 @@ void RE_ray_tree_done(RayTree *tree) /* ************ raytracer **************** */ +#define ISECT_EPSILON ((float)FLT_EPSILON) + /* only for self-intersecting test with current render face (where ray left) */ static int intersection2(RayFace *face, int ob, RayObjectTransformFunc transformfunc, RayCoordsFunc coordsfunc, void *userdata, float r0, float r1, float r2, float rx1, float ry1, float rz1) { @@ -692,13 +694,13 @@ static int intersection2(RayFace *face, int ob, RayObjectTransformFunc transform if(divdet!=0.0f) { u1= det1/divdet; - if(u1<=0.0f) { + if(u1<ISECT_EPSILON) { det= t00*(m1*r2-m2*r1); det+= t01*(m2*r0-m0*r2); det+= t02*(m0*r1-m1*r0); v= det/divdet; - if(v<=0.0f && (u1 + v) >= -1.0f) { + if(v<ISECT_EPSILON && (u1 + v) > -(1.0f+ISECT_EPSILON)) { return 1; } } @@ -714,13 +716,13 @@ static int intersection2(RayFace *face, int ob, RayObjectTransformFunc transform if(divdet!=0.0f) { u2= det1/divdet; - if(u2<=0.0f) { + if(u2<ISECT_EPSILON) { det= t20*(m1*r2-m2*r1); det+= t21*(m2*r0-m0*r2); det+= t22*(m0*r1-m1*r0); v= det/divdet; - if(v<=0.0f && (u2 + v) >= -1.0f) { + if(v<ISECT_EPSILON && (u2 + v) >= -(1.0f+ISECT_EPSILON)) { return 2; } } @@ -860,7 +862,7 @@ int RE_ray_face_intersection(Isect *is, RayObjectTransformFunc transformfunc, Ra divdet= 1.0f/divdet; u= det1*divdet; - if(u<0.0f && u>-1.0f) { + if(u<ISECT_EPSILON && u>-(1.0f+ISECT_EPSILON)) { float v, cros0, cros1, cros2; cros0= m1*t02-m2*t01; @@ -868,11 +870,11 @@ int RE_ray_face_intersection(Isect *is, RayObjectTransformFunc transformfunc, Ra cros2= m0*t01-m1*t00; v= divdet*(cros0*r0 + cros1*r1 + cros2*r2); - if(v<0.0f && (u + v) > -1.0f) { + if(v<ISECT_EPSILON && (u + v) > -(1.0f+ISECT_EPSILON)) { float labda; labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12); - if(labda>0.0f && labda<1.0f) { + if(labda>-ISECT_EPSILON && labda<1.0f+ISECT_EPSILON) { is->labda= labda; is->u= u; is->v= v; ok= 1; @@ -893,18 +895,18 @@ int RE_ray_face_intersection(Isect *is, RayObjectTransformFunc transformfunc, Ra divdet= 1.0f/divdet; u = det1*divdet; - if(u<0.0f && u>-1.0f) { + if(u<ISECT_EPSILON && u>-(1.0f+ISECT_EPSILON)) { float v, cros0, cros1, cros2; cros0= m1*t22-m2*t21; cros1= m2*t20-m0*t22; cros2= m0*t21-m1*t20; v= divdet*(cros0*r0 + cros1*r1 + cros2*r2); - if(v<0.0f && (u + v) > -1.0f) { + if(v<ISECT_EPSILON && (u + v) >-(1.0f+ISECT_EPSILON)) { float labda; labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12); - if(labda>0.0f && labda<1.0f) { + if(labda>-ISECT_EPSILON && labda<1.0f+ISECT_EPSILON) { ok= 2; is->labda= labda; is->u= u; is->v= v; @@ -983,7 +985,7 @@ static int testnode(Octree *oc, Isect *is, Node *no, OcVal ocval, RayCheckFunc c if(!(is->faceorig == face && is->oborig == ob)) { - if(checkfunc(is, face)) { + if(checkfunc(is, ob, face)) { ov= no->ov+nr; if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) { @@ -1023,7 +1025,7 @@ static int testnode(Octree *oc, Isect *is, Node *no, OcVal ocval, RayCheckFunc c while(face) { if(!(is->faceorig == face && is->oborig == ob)) { - if(checkfunc(is, face)) { + if(checkfunc(is, ob, face)) { ov= no->ov+nr; if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) { //accepted++; @@ -1031,8 +1033,11 @@ static int testnode(Octree *oc, Isect *is, Node *no, OcVal ocval, RayCheckFunc c isect.ob= ob; isect.face= face; if(RE_ray_face_intersection(&isect, oc->transformfunc, oc->coordsfunc)) { - if(isect.labda<is->labda) *is= isect; - found= 1; + if(isect.labda<is->labda) { + *is= isect; + found= 1; + } + } } //else rejected++; @@ -1205,7 +1210,7 @@ int RE_ray_tree_intersect_check(RayTree *tree, Isect *is, RayCheckFunc checkfunc /* check with last intersected shadow face */ if(is->face_last!=NULL && !(is->face_last==is->faceorig && is->ob_last==is->oborig)) { - if(checkfunc(is, is->face_last)) { + if(checkfunc(is, is->ob_last, is->face_last)) { is->ob= is->ob_last; is->face= is->face_last; VECSUB(is->vec, is->end, is->start); diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c index 0a9078b32f0..1eb42bca569 100644 --- a/source/blender/render/intern/source/rendercore.c +++ b/source/blender/render/intern/source/rendercore.c @@ -29,6 +29,7 @@ /* system includes */ #include <stdio.h> #include <math.h> +#include <float.h> #include <string.h> /* External modules: */ @@ -46,6 +47,7 @@ #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" @@ -60,6 +62,7 @@ #include "renderpipeline.h" #include "render_types.h" #include "renderdatabase.h" +#include "occlusion.h" #include "pixelblending.h" #include "pixelshading.h" #include "shadbuf.h" @@ -155,7 +158,7 @@ static int calchalo_z(HaloRen *har, int zz) { if(har->type & HA_ONLYSKY) { - if(zz!=0x7FFFFFFF) zz= - 0x7FFFFF; + if(zz < 0x7FFFFFF0) zz= - 0x7FFFFF; /* edge render messes zvalues */ } else { zz= (zz>>8); @@ -163,11 +166,14 @@ static int calchalo_z(HaloRen *har, int zz) return zz; } -static void halo_pixelstruct(HaloRen *har, float *rb, float dist, float xn, float yn, PixStr *ps) + + +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]; - int amount, amountm, zz, flarec; + 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; @@ -177,50 +183,70 @@ static void halo_pixelstruct(HaloRen *har, float *rb, float dist, float xn, floa amount+= amountm; zz= calchalo_z(har, ps->z); - if(zz> har->zs) { - float fac; - - shadeHaloFloat(har, col, zz, dist, xn, yn, flarec); - 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]; - flarec= 0; + if((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) { + if(shadeHaloFloat(har, col, zz, dist, xn, yn, flarec)) { + flarec= 0; + + if(fullsample) { + for(sample=0; sample<totsample; sample++) + if(ps->mask & (1 << sample)) + 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) { - float fac; + 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]; + } + } + } - shadeHaloFloat(har, col, 0x7FFFFF, dist, xn, yn, flarec); - fac= ((float)amount)/(float)R.osa; - accol[0]+= fac*col[0]; - accol[1]+= fac*col[1]; - accol[2]+= fac*col[2]; - accol[3]+= fac*col[3]; + if(fullsample) { + for(sample=0; sample<totsample; sample++) + if(!(mask & (1 << sample))) + addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + } + else { + col[0]= accol[0]; + col[1]= accol[1]; + col[2]= accol[2]; + col[3]= accol[3]; + + for(sample=0; sample<totsample; sample++) + addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); } - col[0]= accol[0]; - col[1]= accol[1]; - col[2]= accol[2]; - col[3]= accol[3]; - - addalphaAddfacFloat(rb, col, har->add); - } -static void halo_tile(RenderPart *pa, float *pass, unsigned int lay) +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, *rb; + float dist, xsq, ysq, xn, yn; float col[4]; - long *rd= NULL; - int a, *rz, zz, y; + 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) { @@ -230,6 +256,8 @@ static void halo_tile(RenderPart *pa, float *pass, unsigned int lay) testrect.ymax-= pa->crop; } + totsample= get_sample_layers(pa, rl, rlpp); + for(a=0; a<R.tothalo; a++) { har= R.sortedhalos[a]; @@ -254,8 +282,8 @@ static void halo_tile(RenderPart *pa, float *pass, unsigned int lay) for(y=miny; y<maxy; y++) { int rectofs= (y-disprect.ymin)*pa->rectx + (minx - disprect.xmin); - rb= pass + 4*rectofs; rz= pa->rectz + rectofs; + od= rectofs; if(pa->rectdaps) rd= pa->rectdaps + rectofs; @@ -263,19 +291,21 @@ static void halo_tile(RenderPart *pa, float *pass, unsigned int lay) yn= (y-har->ys)*R.ycor; ysq= yn*yn; - for(x=minx; x<maxx; x++, rb+=4, rz++) { + for(x=minx; x<maxx; x++, rz++, od++) { xn= x- har->xs; xsq= xn*xn; dist= xsq+ysq; if(dist<har->radsq) { if(rd && *rd) { - halo_pixelstruct(har, rb, dist, xn, yn, (PixStr *)*rd); + halo_pixelstruct(har, rlpp, totsample, od, dist, xn, yn, (PixStr *)*rd); } else { zz= calchalo_z(har, *rz); - if(zz> har->zs) { - shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec); - addalphaAddfacFloat(rb, col, har->add); + if((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) { + if(shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec)) { + for(sample=0; sample<totsample; sample++) + addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + } } } } @@ -290,22 +320,27 @@ static void halo_tile(RenderPart *pa, float *pass, unsigned int lay) static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) { + RenderLayer *rlpp[RE_MAX_OSA]; ShadeInput shi; - float *pass= rl->rectf; - float fac; - long *rd= pa->rectdaps; - int x, y, *rz= pa->rectz; + float *pass; + float fac, col[4]; + intptr_t *rd= pa->rectdaps; + int *rz= pa->rectz; + int x, y, sample, totsample, fullsample, od; - shade_input_initialize(&shi, pa, rl, 0); + totsample= get_sample_layers(pa, rl, rlpp); + fullsample= (totsample > 1); + + shade_input_initialize(&shi, pa, rl, 0); /* this zero's ShadeInput for us */ - for(y=pa->disprect.ymin; y<pa->disprect.ymax; y++) { - for(x=pa->disprect.xmin; x<pa->disprect.xmax; x++, rz++, pass+=4) { + for(od=0, y=pa->disprect.ymin; y<pa->disprect.ymax; y++) { + for(x=pa->disprect.xmin; x<pa->disprect.xmax; x++, rz++, od++) { calc_view_vector(shi.view, x, y); if(rd && *rd) { PixStr *ps= (PixStr *)*rd; - int samp, totsamp= 0; + int count, totsamp= 0, mask= 0; while(ps) { if(R.r.mode & R_ORTHO) @@ -313,15 +348,64 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) else calc_renderco_zbuf(shi.co, shi.view, ps->z); - totsamp+= samp= count_mask(ps->mask); - fac= ((float)samp)/(float)R.osa; - renderspothalo(&shi, pass, fac); + totsamp+= count= count_mask(ps->mask); + mask |= ps->mask; + + col[0]= col[1]= col[2]= col[3]= 0.0f; + renderspothalo(&shi, col, 1.0f); + + if(fullsample) { + for(sample=0; sample<totsample; sample++) { + if(ps->mask & (1 << sample)) { + pass= 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(totsamp<R.osa) { - fac= ((float)R.osa-totsamp)/(float)R.osa; shi.co[2]= 0.0f; - renderspothalo(&shi, pass, fac); + + col[0]= col[1]= col[2]= col[3]= 0.0f; + renderspothalo(&shi, col, 1.0f); + + if(fullsample) { + for(sample=0; sample<totsample; sample++) { + if(!(mask & (1 << sample))) { + pass= 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)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 { @@ -330,7 +414,17 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) else calc_renderco_zbuf(shi.co, shi.view, *rz); - renderspothalo(&shi, pass, 1.0f); + col[0]= col[1]= col[2]= col[3]= 0.0f; + renderspothalo(&shi, col, 1.0f); + + for(sample=0; sample<totsample; 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; + } } if(rd) rd++; @@ -347,12 +441,19 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) 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; @@ -399,6 +500,12 @@ static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset, *fp= (float)shi->obr->ob->index; } break; + case SCE_PASS_MIST: + /* */ + col= &shr->mist; + pixsize= 1; + break; + case SCE_PASS_VECTOR: { /* add minimum speed in pixel, no filter */ @@ -425,12 +532,20 @@ static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset, static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult *shr) { RenderPass *rpass; + float *fp; + + fp= rl->rectf + 4*offset; + QUATCOPY(fp, shr->combined); for(rpass= rl->passes.first; rpass; rpass= rpass->next) { - float *fp, *col= NULL, uvcol[3]; + 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; @@ -477,6 +592,10 @@ static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult *fp= (float)shi->obr->ob->index; } break; + case SCE_PASS_MIST: + fp= rpass->rect + offset; + *fp= shr->mist; + break; } if(col) { fp= rpass->rect + pixsize*offset; @@ -486,26 +605,60 @@ static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult } } +int get_sample_layers(RenderPart *pa, RenderLayer *rl, RenderLayer **rlpp) +{ + + if(pa->fullresult.first) { + int sample, nr= BLI_findindex(&pa->result->layers, rl); + + for(sample=0; sample<R.osa; sample++) { + RenderResult *rr= BLI_findlink(&pa->fullresult, sample); + + rlpp[sample]= BLI_findlink(&rr->layers, nr); + } + return R.osa; + } + else { + rlpp[0]= rl; + return 1; + } +} + + /* only do sky, is default in the solid layer (shade_tile) btw */ -static void sky_tile(RenderPart *pa, float *pass) +static void sky_tile(RenderPart *pa, RenderLayer *rl) { - float col[4]; - int x, y; + RenderLayer *rlpp[RE_MAX_OSA]; + int x, y, od=0, totsample; if(R.r.alphamode!=R_ADDSKY) return; + totsample= get_sample_layers(pa, rl, rlpp); + for(y=pa->disprect.ymin; y<pa->disprect.ymax; y++) { - for(x=pa->disprect.xmin; x<pa->disprect.xmax; x++, pass+=4) { - if(pass[3]<1.0f) { - if(pass[3]==0.0f) - shadeSkyPixel(pass, x, y); - else { - shadeSkyPixel(col, x, y); - addAlphaOverFloat(col, pass); - QUATCOPY(pass, col); + for(x=pa->disprect.xmin; x<pa->disprect.xmax; x++, od+=4) { + float col[4]; + int sample, done= 0; + + for(sample= 0; sample<totsample; sample++) { + float *pass= rlpp[sample]->rectf + od; + + if(pass[3]<1.0f) { + + if(done==0) { + shadeSkyPixel(col, x, y); + done= 1; + } + + if(pass[3]==0.0f) { + QUATCOPY(pass, col); + } + else { + addAlphaUnderFloat(pass, col); + } } - } + } } if(y&1) @@ -513,14 +666,99 @@ static void sky_tile(RenderPart *pa, float *pass) } } +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 is enabled */ + for(go=R.lights.first; go; go= go->next) { + lar= go->lampren; + if(lar->type==LA_SUN && lar->sunsky && (lar->sunsky->effect_type & LA_SUN_EFFECT_AP)) + break; + } + /* do nothign and return if there is no sun lamp */ + if(go==NULL) + return; + + /* for each x,y and each sample, and each sun lamp*/ + for(y=pa->disprect.ymin; y<pa->disprect.ymax; y++) { + for(x=pa->disprect.xmin; x<pa->disprect.xmax; x++, od++) { + int sample; + + for(sample=0; sample<totsample; sample++) { + float *zrect= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_Z) + od; + float *rgbrect = rlpp[sample]->rectf + 4*od; + float rgb[3]; + int done= 0; + + 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.9e10 || rgbrect[3]==0.0f) { + continue; + } + + if((lar->sunsky->effect_type & LA_SUN_EFFECT_AP)) { + float tmp_rgb[3]; + + VECCOPY(tmp_rgb, rgbrect); + if(rgbrect[3]!=1.0f) { /* de-premul */ + float div= 1.0f/rgbrect[3]; + VECMUL(tmp_rgb, div); + } + shadeAtmPixel(lar->sunsky, tmp_rgb, x, y, *zrect); + if(rgbrect[3]!=1.0f) { /* premul */ + VECMUL(tmp_rgb, rgbrect[3]); + } + + if(done==0) { + VECCOPY(rgb, tmp_rgb); + done = 1; + } + 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) { + VECCOPY(rgbrect, rgb); + } + } + } + } +} + static void shadeDA_tile(RenderPart *pa, RenderLayer *rl) { RenderResult *rr= pa->result; ShadeSample ssamp; - float *fcol, *rf, *rectf= rl->rectf; - long *rd, *rectdaps= pa->rectdaps; + intptr_t *rd, *rectdaps= pa->rectdaps; int samp; - int x, y, seed, crop=0, offs=0, od, addpassflag; + int x, y, seed, crop=0, offs=0, od; if(R.test_break()) return; @@ -533,12 +771,14 @@ static void shadeDA_tile(RenderPart *pa, RenderLayer *rl) /* general shader info, passes */ shade_sample_initialize(&ssamp, pa, rl); - addpassflag= rl->passflag & ~(SCE_PASS_Z|SCE_PASS_COMBINED); - + + /* 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; - rectf+= 4*(pa->rectx + 1); rectdaps+= pa->rectx + 1; offs= pa->rectx + 1; } @@ -549,28 +789,35 @@ static void shadeDA_tile(RenderPart *pa, RenderLayer *rl) rr->renlay= rl; for(y=pa->disprect.ymin+crop; y<pa->disprect.ymax-crop; y++, rr->renrect.ymax++) { - rf= rectf; rd= rectdaps; od= offs; - for(x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, rd++, rf+=4, od++) { + for(x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, rd++, od++) { BLI_thread_srandom(pa->thread, seed++); if(*rd) { if(shade_samples(&ssamp, (PixStr *)(*rd), x, y)) { - for(samp=0; samp<ssamp.tot; samp++) { - - fcol= ssamp.shr[samp].combined; - add_filt_fmask(ssamp.shi[samp].mask, fcol, rf, pa->rectx); - - if(addpassflag) + + /* multisample buffers or filtered mask filling? */ + if(pa->fullresult.first) { + int a; + for(samp=0; samp<ssamp.tot; samp++) { + int smask= ssamp.shi[samp].mask; + for(a=0; a<R.osa; a++) { + int mask= 1<<a; + if(smask & mask) + add_passes(ssamp.rlpp[a], od, &ssamp.shi[samp], &ssamp.shr[samp]); + } + } + } + else { + for(samp=0; samp<ssamp.tot; samp++) add_filt_passes(rl, ssamp.shi[samp].mask, pa->rectx, od, &ssamp.shi[samp], &ssamp.shr[samp]); } } } } - rectf+= 4*pa->rectx; rectdaps+= pa->rectx; offs+= pa->rectx; @@ -582,6 +829,9 @@ static void shadeDA_tile(RenderPart *pa, RenderLayer *rl) if(R.r.mode & R_SHADOW) ISB_free(pa); + + if(R.occlusiontree) + free_occ_samples(&R, pa); } /* ************* pixel struct ******** */ @@ -613,7 +863,7 @@ static void freeps(ListBase *lb) lb->first= lb->last= NULL; } -static void addps(ListBase *lb, long *rd, int obi, int facenr, int z, unsigned short mask) +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; @@ -640,12 +890,13 @@ static void addps(ListBase *lb, long *rd, int obi, int facenr, int z, unsigned s ps= psm->ps + psm->counter++; if(last) last->next= ps; - else *rd= (long)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; } @@ -669,23 +920,29 @@ static void edge_enhance_add(RenderPart *pa, float *rectf, float *arect) } } - -static void convert_to_key_alpha(RenderPart *pa, float *rectf) +static void convert_to_key_alpha(RenderPart *pa, RenderLayer *rl) { - int y; - - 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]; + RenderLayer *rlpp[RE_MAX_OSA]; + int y, sample, totsample; + + totsample= get_sample_layers(pa, rl, rlpp); + + for(sample= 0; sample<totsample; sample++) { + float *rectf= rlpp[sample]->rectf; + + 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 */ -void edge_enhance_tile(RenderPart *pa, float *rectf) +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; @@ -693,13 +950,13 @@ void edge_enhance_tile(RenderPart *pa, float *rectf) float *rf; /* shift values in zbuffer 4 to the right (anti overflows), for filter we need multiplying with 12 max */ - rz= pa->rectz; + rz= rectz; if(rz==NULL) return; for(y=0; y<pa->recty; y++) for(x=0; x<pa->rectx; x++, rz++) (*rz)>>= 4; - rz1= pa->rectz; + rz1= rectz; rz2= rz1+pa->rectx; rz3= rz2+pa->rectx; @@ -739,7 +996,7 @@ void edge_enhance_tile(RenderPart *pa, float *rectf) } /* shift back zbuf values, we might need it still */ - rz= pa->rectz; + rz= rectz; for(y=0; y<pa->recty; y++) for(x=0; x<pa->rectx; x++, rz++) (*rz)<<= 4; @@ -748,20 +1005,24 @@ void edge_enhance_tile(RenderPart *pa, float *rectf) 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; - - fp= RE_RenderLayerGetPass(rl, SCE_PASS_VECTOR); - if(fp==NULL) return; + int a, sample, totsample; - for(a= 4*pa->rectx*pa->recty - 1; a>=0; a--) - if(fp[a] == PASS_VECTOR_MAX) fp[a]= 0.0f; -} + totsample= get_sample_layers(pa, rl, rlpp); + for(sample= 0; sample<totsample; sample++) { + fp= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_VECTOR); + if(fp==NULL) break; + + for(a= 4*pa->rectx*pa->recty - 1; a>=0; a--) + if(fp[a] == PASS_VECTOR_MAX) fp[a]= 0.0f; + } +} static unsigned short *make_solid_mask(RenderPart *pa) { - long *rd= pa->rectdaps; + intptr_t *rd= pa->rectdaps; unsigned short *solidmask, *sp; int x; @@ -826,25 +1087,25 @@ void make_pixelstructs(RenderPart *pa, ZSpan *zspan, int sample, void *data) { ZbufSolidData *sdata= (ZbufSolidData*)data; ListBase *lb= sdata->psmlist; - long *rd= pa->rectdaps; + 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<<sample; for(y=0; y<pa->recty; y++) { - for(x=0; x<pa->rectx; x++, rd++, rp++, ro++) { + for(x=0; x<pa->rectx; x++, rd++, rp++, ro++, rz++, rm++) { if(*rp) { - addps(lb, rd, *ro, *rp, *(rz+x), mask); + addps(lb, rd, *ro, *rp, *rz, (zspan->rectmask)? *rm: 0, mask); } } - rz+= pa->rectx; } if(sdata->rl->layflag & SCE_LAY_EDGE) if(R.r.mode & R_EDGE) - edge_enhance_tile(pa, sdata->edgerect); + edge_enhance_tile(pa, sdata->edgerect, zspan->rectz); } /* main call for shading Delta Accum, for OSA */ @@ -861,12 +1122,13 @@ void zbufshadeDA_tile(RenderPart *pa) 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(long)*pa->rectx*pa->recty+4, "zbufDArectd"); + 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) @@ -879,7 +1141,7 @@ void zbufshadeDA_tile(RenderPart *pa) sdata.rl= rl; sdata.psmlist= &psmlist; sdata.edgerect= edgerect; - zbuffer_solid(pa, rl->lay, rl->layflag, make_pixelstructs, &sdata); + zbuffer_solid(pa, rl, make_pixelstructs, &sdata); if(R.test_break()) break; } @@ -895,108 +1157,74 @@ void zbufshadeDA_tile(RenderPart *pa) /* halo before ztra, because ztra fills in zbuffer now */ if(R.flag & R_HALO) if(rl->layflag & SCE_LAY_HALO) - halo_tile(pa, rl->rectf, rl->lay); + halo_tile(pa, rl); /* transp layer */ - if(R.flag & R_ZTRA) { - if(rl->layflag & SCE_LAY_ZTRA) { - 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); - 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<<R.osa)-1; - float *fcol= rl->rectf; float *acol= rl->acolrect; - int x; + 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 */ - 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); + /* 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<<R.osa)-1; + float *fcol= rl->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); + 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); - } - } - - /* strand rendering */ - if((rl->layflag & SCE_LAY_STRAND) && R.totstrand) { - float *fcol, *scol; - unsigned short *strandmask, *solidmask= NULL; /* 16 bits, MAX_OSA */ - int x; - - /* allocate, but not free here, for asynchronous display of this rect in main thread */ - rl->scolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "strand layer"); - - /* swap for live updates, and it is used in zbuf.c!!! */ - SWAP(float*, rl->scolrect, rl->rectf); - strandmask= zbuffer_strands_shade(&R, pa, rl, rl->rectf); - SWAP(float*, rl->scolrect, rl->rectf); - - /* zbuffer strands only returns strandmask if there's solid rendered */ - if(strandmask) - solidmask= make_solid_mask(pa); - - if(strandmask && solidmask) { - unsigned short *sps= solidmask, *spz= strandmask; - unsigned short fullmask= (1<<R.osa)-1; - - fcol= rl->rectf; scol= rl->scolrect; - for(x=pa->rectx*pa->recty; x>0; x--, scol+=4, fcol+=4, sps++, spz++) { - if(*sps == fullmask) - addAlphaOverFloat(fcol, scol); - else - addAlphaOverFloatMask(fcol, scol, *sps, *spz); + if(solidmask) MEM_freeN(solidmask); + if(ztramask) MEM_freeN(ztramask); } } - else { - fcol= rl->rectf; scol= rl->scolrect; - for(x=pa->rectx*pa->recty; x>0; x--, scol+=4, fcol+=4) - addAlphaOverFloat(fcol, scol); - } - - if(solidmask) MEM_freeN(solidmask); - if(strandmask) MEM_freeN(strandmask); } + /* 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->rectf); + 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_Z) - convert_zbuf_to_distbuf(pa, rl); - 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->rectf); + convert_to_key_alpha(pa, rl); /* free stuff within loop! */ MEM_freeN(pa->rectdaps); pa->rectdaps= NULL; @@ -1004,6 +1232,11 @@ void zbufshadeDA_tile(RenderPart *pa) if(edgerect) MEM_freeN(edgerect); edgerect= NULL; + + if(pa->rectmask) { + MEM_freeN(pa->rectmask); + pa->rectmask= NULL; + } } /* free all */ @@ -1028,7 +1261,6 @@ void zbufshade_tile(RenderPart *pa) RenderLayer *rl; PixStr ps; float *edgerect= NULL; - int addpassflag; /* fake pixel struct, to comply to osa render */ ps.next= NULL; @@ -1040,12 +1272,13 @@ void zbufshade_tile(RenderPart *pa) 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); - addpassflag= rl->passflag & ~(SCE_PASS_Z|SCE_PASS_COMBINED); - zbuffer_solid(pa, rl->lay, rl->layflag, NULL, NULL); + zbuffer_solid(pa, rl, NULL, NULL); if(!R.test_break()) { /* NOTE: this if() is not consistant */ @@ -1053,7 +1286,7 @@ void zbufshade_tile(RenderPart *pa) 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); + edge_enhance_tile(pa, edgerect, pa->rectz); } } @@ -1072,6 +1305,9 @@ void zbufshade_tile(RenderPart *pa) /* irregular shadowb buffer creation */ if(R.r.mode & R_SHADOW) ISB_create(pa, NULL); + + if(R.occlusiontree) + cache_occ_samples(&R, pa, &ssamp); for(y=pa->disprect.ymin; y<pa->disprect.ymax; y++, rr->renrect.ymax++) { for(x=pa->disprect.xmin; x<pa->disprect.xmax; x++, ro++, rz++, rp++, fcol+=4, offs++) { @@ -1083,14 +1319,8 @@ void zbufshade_tile(RenderPart *pa) ps.facenr= *rp; ps.z= *rz; if(shade_samples(&ssamp, &ps, x, y)) { - QUATCOPY(fcol, ssamp.shr[0].combined); - - if(!(fcol[0] == fcol[0])) - printvecf("fudgecol", fcol); - - /* passes */ - if(addpassflag) - add_passes(rl, offs, ssamp.shi, ssamp.shr); + /* combined and passes */ + add_passes(rl, offs, ssamp.shi, ssamp.shr); } } } @@ -1098,6 +1328,9 @@ void zbufshade_tile(RenderPart *pa) if(R.test_break()) break; } + if(R.occlusiontree) + free_occ_samples(&R, pa); + if(R.r.mode & R_SHADOW) ISB_free(pa); } @@ -1114,10 +1347,10 @@ void zbufshade_tile(RenderPart *pa) /* halo before ztra, because ztra fills in zbuffer now */ if(R.flag & R_HALO) if(rl->layflag & SCE_LAY_HALO) - halo_tile(pa, rl->rectf, rl->lay); + halo_tile(pa, rl); - if(R.flag & R_ZTRA) { - if(rl->layflag & SCE_LAY_ZTRA) { + if(R.flag & R_ZTRA || R.totstrand) { + if(rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) { float *fcol, *acol; int x; @@ -1126,7 +1359,7 @@ void zbufshade_tile(RenderPart *pa) /* swap for live updates */ SWAP(float *, rl->acolrect, rl->rectf); - zbuffer_transp_shade(pa, rl, rl->rectf); + zbuffer_transp_shade(pa, rl, rl->rectf, NULL); SWAP(float *, rl->acolrect, rl->rectf); fcol= rl->rectf; acol= rl->acolrect; @@ -1135,28 +1368,14 @@ void zbufshade_tile(RenderPart *pa) } } } - - /* strand rendering */ - if((rl->layflag & SCE_LAY_STRAND) && R.totstrand) { - float *fcol, *scol; - int x; - - /* allocate, but not free here, for asynchronous display of this rect in main thread */ - rl->scolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "strand layer"); - - /* swap for live updates */ - SWAP(float*, rl->scolrect, rl->rectf); - zbuffer_strands_shade(&R, pa, rl, rl->rectf); - SWAP(float*, rl->scolrect, rl->rectf); - - fcol= rl->rectf; scol= rl->scolrect; - for(x=pa->rectx*pa->recty; x>0; x--, scol+=4, fcol+=4) - addAlphaOverFloat(fcol, scol); - } + + /* 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->rectf); + sky_tile(pa, rl); if(!R.test_break()) { if(rl->layflag & SCE_LAY_EDGE) @@ -1164,18 +1383,20 @@ void zbufshade_tile(RenderPart *pa) edge_enhance_add(pa, rl->rectf, edgerect); } - if(rl->passflag & SCE_PASS_Z) - convert_zbuf_to_distbuf(pa, rl); - 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->rectf); + 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 */ @@ -1207,9 +1428,9 @@ static void addps_sss(void *cb_handle, int obi, int facenr, int x, int y, int z) return; if(pa->rectall) { - long *rs= pa->rectall + pa->rectx*y + x; + intptr_t *rs= pa->rectall + pa->rectx*y + x; - addps(&handle->psmlist, rs, obi, facenr, z, 0); + addps(&handle->psmlist, rs, obi, facenr, z, 0, 0); handle->totps++; } if(pa->rectz) { @@ -1244,10 +1465,10 @@ static void shade_sample_sss(ShadeSample *ssamp, Material *mat, ObjectInstanceRe { ShadeInput *shi= ssamp->shi; ShadeResult shr; - float texfac, orthoarea, nor[3]; + float texfac, orthoarea, nor[3], alpha, sx, sy; /* cache for shadow */ - shi->samplenr++; + shi->samplenr= R.shadowsamplenr[shi->thread]++; if(quad) shade_input_set_triangle_i(shi, obi, vlr, 0, 2, 3); @@ -1255,8 +1476,8 @@ static void shade_sample_sss(ShadeSample *ssamp, Material *mat, ObjectInstanceRe shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2); /* center pixel */ - x += 0.5f; - y += 0.5f; + 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 @@ -1265,13 +1486,13 @@ static void shade_sample_sss(ShadeSample *ssamp, Material *mat, ObjectInstanceRe shi->osatex= 1; VECCOPY(nor, shi->facenor); - calc_view_vector(shi->facenor, x, y); + calc_view_vector(shi->facenor, sx, sy); Normalize(shi->facenor); - shade_input_set_viewco(shi, x, y, z); + shade_input_set_viewco(shi, x, y, sx, sy, z); orthoarea= VecLength(shi->dxco)*VecLength(shi->dyco); VECCOPY(shi->facenor, nor); - shade_input_set_viewco(shi, x, y, z); + shade_input_set_viewco(shi, x, y, sx, sy, z); *area= VecLength(shi->dxco)*VecLength(shi->dyco); *area= MIN2(*area, 2.0f*orthoarea); @@ -1310,16 +1531,8 @@ static void shade_sample_sss(ShadeSample *ssamp, Material *mat, ObjectInstanceRe /* texture blending */ texfac= shi->mat->sss_texfac; - if(texfac == 0.0f) { - if(shr.col[0]!=0.0f) color[0] /= shr.col[0]; - if(shr.col[1]!=0.0f) color[1] /= shr.col[1]; - if(shr.col[2]!=0.0f) color[2] /= shr.col[2]; - } - else if(texfac != 1.0f) { - if(shr.col[0]!=0.0f) color[0] *= pow(shr.col[0], texfac)/shr.col[0]; - if(shr.col[1]!=0.0f) color[1] *= pow(shr.col[1], texfac)/shr.col[1]; - if(shr.col[2]!=0.0f) color[2] *= pow(shr.col[2], texfac)/shr.col[2]; - } + alpha= shr.combined[3]; + *area *= alpha; } static void zbufshade_sss_free(RenderPart *pa) @@ -1343,15 +1556,15 @@ void zbufshade_sss_tile(RenderPart *pa) ShadeSample ssamp; ZBufSSSHandle handle; RenderResult *rr= pa->result; - RenderLayer *rl= rr->layers.first; + RenderLayer *rl; VlakRen *vlr; Material *mat= re->sss_mat; - float (*co)[3], (*color)[3], *area, *fcol= rl->rectf; + 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; + int *ro, *rz, *rp, *rbo, *rbz, *rbp, lay; #if 0 PixStr *ps; - long *rs; + intptr_t *rs; int z; #endif @@ -1363,7 +1576,7 @@ void zbufshade_sss_tile(RenderPart *pa) handle.psmlist.first= handle.psmlist.last= NULL; addpsmain(&handle.psmlist); - pa->rectall= MEM_callocN(sizeof(long)*pa->rectx*pa->recty+4, "rectall"); + 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"); @@ -1373,13 +1586,34 @@ void zbufshade_sss_tile(RenderPart *pa) 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, rl->lay, &handle, addps_sss); + 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"); @@ -1391,14 +1625,6 @@ void zbufshade_sss_tile(RenderPart *pa) ISB_create(pa, NULL); #endif - /* setup shade sample with correct passes */ - memset(&ssamp, 0, sizeof(ssamp)); - shade_sample_initialize(&ssamp, pa, rl); - ssamp.shi[0].passflag= SCE_PASS_DIFFUSE|SCE_PASS_AO|SCE_PASS_RADIO; - ssamp.shi[0].passflag |= SCE_PASS_RGBA; - ssamp.shi[0].combinedflag= ~(SCE_PASS_SPEC); - ssamp.tot= 1; - if(display) { /* initialize scanline updates for main thread */ rr->renrect.ymin= 0; @@ -1563,8 +1789,8 @@ static void renderhalo_post(RenderResult *rr, float *rectf, HaloRen *har) /* pos dist= xsq+ysq; if(dist<har->radsq) { - shadeHaloFloat(har, colf, 0x7FFFFF, dist, xn, yn, har->flarec); - addalphaAddfacFloat(rtf, colf, har->add); + if(shadeHaloFloat(har, colf, 0x7FFFFF, dist, xn, yn, har->flarec)) + addalphaAddfacFloat(rtf, colf, har->add); } rtf+=4; } @@ -1701,6 +1927,8 @@ void add_halo_flare(Render *re) void RE_shade_external(Render *re, ShadeInput *shi, ShadeResult *shr) { static VlakRen vlr; + static ObjectRen obr; + static ObjectInstanceRen obi; /* init */ if(re) { @@ -1708,11 +1936,16 @@ void RE_shade_external(Render *re, ShadeInput *shi, ShadeResult *shr) /* fake render face */ memset(&vlr, 0, sizeof(VlakRen)); - vlr.lay= -1; + memset(&obr, 0, sizeof(ObjectRen)); + memset(&obi, 0, sizeof(ObjectInstanceRen)); + obr.lay= -1; + obi.obr= &obr; return; } shi->vlr= &vlr; + shi->obr= &obr; + shi->obi= &obi; if(shi->mat->nodetree && shi->mat->use_nodes) ntreeShaderExecTree(shi->mat->nodetree, shi, shr); @@ -1727,7 +1960,6 @@ void RE_shade_external(Render *re, ShadeInput *shi, ShadeResult *shr) /* ************************* bake ************************ */ -#define FTOCHAR(val) val<=0.0f?0: (val>=1.0f?255: (char)(255.0f*val)) typedef struct BakeShade { ShadeSample ssamp; @@ -1745,8 +1977,81 @@ typedef struct BakeShade { unsigned int *rect; float *rect_float; + + int usemask; + char *rect_mask; /* bake pixel mask */ + + float dxco[3], dyco[3]; } BakeShade; +/* bake uses a char mask to know what has been baked */ +#define BAKE_MASK_NULL 0 +#define BAKE_MASK_MARGIN 1 +#define BAKE_MASK_BAKED 2 +static void bake_mask_filter_extend( char *mask, int width, int height ) +{ + char *row1, *row2, *row3; + int rowlen, x, y; + char *temprect; + + rowlen= width; + + /* make a copy, to prevent flooding */ + temprect= MEM_dupallocN(mask); + + for(y=1; y<=height; y++) { + /* setup rows */ + row1= (char *)(temprect + (y-2)*rowlen); + row2= row1 + rowlen; + row3= row2 + rowlen; + if(y==1) + row1= row2; + else if(y==height) + row3= row2; + + for(x=0; x<rowlen; x++) { + if (mask[((y-1)*rowlen)+x]==0) { + if (*row1 || *row2 || *row3 || *(row1+1) || *(row3+1) ) { + mask[((y-1)*rowlen)+x] = BAKE_MASK_MARGIN; + } else if((x!=rowlen-1) && (*(row1+2) || *(row2+2) || *(row3+2)) ) { + mask[((y-1)*rowlen)+x] = BAKE_MASK_MARGIN; + } + } + + if(x!=0) { + row1++; row2++; row3++; + } + } + } + MEM_freeN(temprect); +} + +static void bake_mask_clear( ImBuf *ibuf, char *mask, char val ) +{ + int x,y; + if (ibuf->rect_float) { + for(x=0; x<ibuf->x; x++) { + for(y=0; y<ibuf->y; y++) { + if (mask[ibuf->x*y + x] == val) { + float *col= ibuf->rect_float + 4*(ibuf->x*y + x); + col[0] = col[1] = col[2] = col[3] = 0.0f; + } + } + } + + } else { + /* char buffer */ + for(x=0; x<ibuf->x; x++) { + for(y=0; y<ibuf->y; y++) { + if (mask[ibuf->x*y + x] == val) { + char *col= (char *)(ibuf->rect + ibuf->x*y + x); + col[0] = col[1] = col[2] = col[3] = 0; + } + } + } + } +} + static void bake_set_shade_input(ObjectInstanceRen *obi, VlakRen *vlr, ShadeInput *shi, int quad, int isect, int x, int y, float u, float v) { if(isect) { @@ -1768,23 +2073,28 @@ static void bake_set_shade_input(ObjectInstanceRen *obi, VlakRen *vlr, ShadeInpu shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2); } - /* set up view vector */ - VECCOPY(shi->view, shi->co); - Normalize(shi->view); - /* cache for shadow */ - shi->samplenr++; + 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 quad, int x, int y, float u, float v, float *tvn, float *ttang) @@ -1800,12 +2110,20 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int quad, int if(bs->type==RE_BAKE_AO) { ambient_occlusion(shi); - ambient_occlusion_to_diffuse(shi, shr.combined); + + if(R.r.bake_flag & R_BAKE_NORMALIZE) + VECCOPY(shr.combined, shi->ao) + else + ambient_occlusion_to_diffuse(shi, shr.combined); } 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); - shade_samples_do_AO(ssamp); + if(!ELEM3(bs->type, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_SHADOW)) + shade_samples_do_AO(ssamp); if(shi->mat->nodetree && shi->mat->use_nodes) { ntreeShaderExecTree(shi->mat->nodetree, shi, &shr); @@ -1830,8 +2148,8 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int quad, int VECCOPY(mat[2], tvn); } else { - VECCOPY(mat[0], shi->tang); - Crossf(mat[1], shi->vn, shi->tang); + VECCOPY(mat[0], shi->nmaptang); + Crossf(mat[1], shi->vn, shi->nmaptang); VECCOPY(mat[2], shi->vn); } @@ -1853,35 +2171,80 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int quad, int 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) { + VECCOPY(shr.combined, shr.shad); + shr.alpha = shi->alpha; } } - if(bs->rect) { + if(bs->rect_float) { + float *col= bs->rect_float + 4*(bs->rectx*y + x); + VECCOPY(col, shr.combined); + if (bs->type==RE_BAKE_ALL || bs->type==RE_BAKE_TEXTURE) { + col[3]= shr.alpha; + } else { + col[3]= 1.0; + } + } + else { char *col= (char *)(bs->rect + bs->rectx*y + x); col[0]= FTOCHAR(shr.combined[0]); col[1]= FTOCHAR(shr.combined[1]); col[2]= FTOCHAR(shr.combined[2]); - col[3]= 255; + + + if (bs->type==RE_BAKE_ALL || bs->type==RE_BAKE_TEXTURE) { + col[3]= FTOCHAR(shr.alpha); + } else { + col[3]= 255; + } } - else { + + if (bs->rect_mask) { + bs->rect_mask[bs->rectx*y + x] = BAKE_MASK_BAKED; + } +} + +static void bake_displacement(void *handle, ShadeInput *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.5 + 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); - VECCOPY(col, shr.combined); + col[0] = col[1] = col[2] = disp; col[3]= 1.0f; + } else { + char *col= (char *)(bs->rect + bs->rectx*y + x); + col[0]= FTOCHAR(disp); + col[1]= FTOCHAR(disp); + col[2]= FTOCHAR(disp); + col[3]= 255; + } + if (bs->rect_mask) { + bs->rect_mask[bs->rectx*y + x] = BAKE_MASK_BAKED; } } -static int bake_check_intersect(Isect *is, RayFace *face) +static int bake_check_intersect(Isect *is, int ob, RayFace *face) { - VlakRen *vlr = (VlakRen*)face; BakeShade *bs = (BakeShade*)is->userdata; /* no direction checking for now, doesn't always improve the result * (INPR(shi->facenor, bs->dir) > 0.0f); */ - return (vlr->obr->ob != bs->actob); + return (R.objectinstance[ob].obr->ob != bs->actob); } -static int bake_intersect_tree(RayTree* raytree, Isect* isect, float *dir, float sign, float *hitco) +static int bake_intersect_tree(RayTree* raytree, Isect* isect, float *start, float *dir, float sign, float *hitco, float *dist) { float maxdist; int hit; @@ -1890,7 +2253,9 @@ static int bake_intersect_tree(RayTree* raytree, Isect* isect, float *dir, float if(R.r.bake_maxdist > 0.0f) maxdist= R.r.bake_maxdist; else - maxdist= RE_ray_tree_max_size(R.raytree); + maxdist= RE_ray_tree_max_size(R.raytree) + R.r.bake_biasdist; + + VECADDFAC(isect->start, start, dir, -R.r.bake_biasdist); isect->end[0] = isect->start[0] + dir[0]*maxdist*sign; isect->end[1] = isect->start[1] + dir[1]*maxdist*sign; @@ -1901,11 +2266,62 @@ static int bake_intersect_tree(RayTree* raytree, Isect* isect, float *dir, float hitco[0] = isect->start[0] + isect->labda*isect->vec[0]; hitco[1] = isect->start[1] + isect->labda*isect->vec[1]; hitco[2] = isect->start[2] + isect->labda*isect->vec[2]; + + *dist= VecLenf(start, hitco); } 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(fabs(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) { + Mat3MulVecfl(bs->obi->nmat, bs->dxco); + Mat3MulVecfl(bs->obi->nmat, bs->dyco); + } +} + static void do_bake_shade(void *handle, int x, int y, float u, float v) { BakeShade *bs= handle; @@ -1943,52 +2359,64 @@ static void do_bake_shade(void *handle, int x, int y, float u, float v) if(obi->flag & R_TRANSFORMED) Mat4MulVecfl(obi->mat, shi->co); + VECCOPY(shi->dxco, bs->dxco); + VECCOPY(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); VECCOPY(tvn, shi->vn); - VECCOPY(ttang, shi->tang); + VECCOPY(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]; - int hit, sign; + 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; - + VECCOPY(bs->dir, shi->vn); - + for(sign=-1; sign<=1; sign+=2) { memset(&isec, 0, sizeof(isec)); - VECCOPY(isec.start, shi->co); isec.mode= RE_RAY_MIRROR; isec.faceorig= (RayFace*)vlr; isec.oborig= RAY_OBJECT_SET(&R, obi); isec.userdata= bs; - - if(bake_intersect_tree(R.raytree, &isec, shi->vn, sign, co)) { + + if(bake_intersect_tree(R.raytree, &isec, shi->co, shi->vn, sign, co, &dist)) { if(!hit || VecLenf(shi->co, co) < VecLenf(shi->co, minco)) { minisec= isec; + mindist= dist; VECCOPY(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) { vlr= (VlakRen*)minisec.face; obi= RAY_OBJECT_GET(&R, minisec.ob); quad= (minisec.isect == 2); VECCOPY(shi->co, minco); - + u= -minisec.u; v= -minisec.v; bake_set_shade_input(obi, vlr, shi, quad, 1, x, y, u, v); @@ -2025,7 +2453,7 @@ static int get_next_bake_face(BakeShade *bs) 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->actmtface, NULL, 0); + tface= RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0); if(tface && tface->tpage) { Image *ima= tface->tpage; @@ -2079,7 +2507,7 @@ 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->actmtface, NULL, 0); + 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; @@ -2099,6 +2527,19 @@ static void shade_tface(BakeShade *bs) bs->rect_float= bs->ibuf->rect_float; bs->quad= 0; + if (bs->usemask) { + 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++) { vec[a][0]= tface->uv[a][0]*(float)bs->rectx - 0.5f; @@ -2108,10 +2549,12 @@ static void shade_tface(BakeShade *bs) /* 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); } } @@ -2140,8 +2583,8 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob) BakeShade handles[BLENDER_MAX_THREADS]; ListBase threads; Image *ima; - int a, vdone=0; - + int a, vdone=0, usemask=0; + /* initialize render global */ R= *re; R.bakebuf= NULL; @@ -2149,9 +2592,17 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob) /* initialize static vars */ get_next_bake_face(NULL); + /* do we need a mask? */ + if (re->r.bake_filter && (re->r.bake_flag & R_BAKE_CLEAR)==0) + usemask = 1; + /* baker uses this flag to detect if image was initialized */ - for(ima= G.main->image.first; ima; ima= ima->id.next) + for(ima= G.main->image.first; ima; ima= ima->id.next) { + ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL); ima->id.flag |= LIB_DOIT; + if (ibuf) + ibuf->userdata = NULL; /* use for masking if needed */ + } BLI_init_threads(&threads, do_bake_thread, re->r.threads); @@ -2161,7 +2612,12 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob) memset(&handles[a], 0, sizeof(BakeShade)); handles[a].ssamp.shi[0].lay= re->scene->lay; - handles[a].ssamp.shi[0].passflag= SCE_PASS_COMBINED; + + 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; @@ -2170,6 +2626,8 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob) handles[a].actob= actob; handles[a].zspan= MEM_callocN(sizeof(ZSpan), "zspan for bake"); + handles[a].usemask = usemask; + BLI_insert_thread(&threads, &handles[a]); } @@ -2184,23 +2642,52 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob) break; } - /* filter images */ + /* 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); - for(a=0; a<re->r.bake_filter; a++) - IMB_filter_extend(ibuf); + if (re->r.bake_filter) { + if (usemask) { + /* extend the mask +2 pixels from the image, + * this is so colors dont blend in from outside */ + char *temprect; + + for(a=0; a<re->r.bake_filter; a++) + bake_mask_filter_extend((char *)ibuf->userdata, ibuf->x, ibuf->y); + + temprect = MEM_dupallocN(ibuf->userdata); + + /* expand twice to clear this many pixels, so they blend back in */ + bake_mask_filter_extend(temprect, ibuf->x, ibuf->y); + bake_mask_filter_extend(temprect, ibuf->x, ibuf->y); + + /* clear all pixels in the margin*/ + bake_mask_clear(ibuf, temprect, BAKE_MASK_MARGIN); + MEM_freeN(temprect); + } + + for(a=0; a<re->r.bake_filter; a++) { + /*the mask, ibuf->userdata - can be null, in this case only zero alpha is used */ + IMB_filter_extend(ibuf, (char *)ibuf->userdata); + } + + if (ibuf->userdata) { + MEM_freeN(ibuf->userdata); + ibuf->userdata= NULL; + } + } ibuf->userflags |= IB_BITMAPDIRTY; + if (ibuf->rect_float) IMB_rect_from_float(ibuf); } } /* calculate return value */ - for(a=0; a<re->r.threads; a++) { + for(a=0; a<re->r.threads; a++) { vdone+= handles[a].vdone; zbuf_free_span(handles[a].zspan); MEM_freeN(handles[a].zspan); - } + } BLI_end_threads(&threads); return vdone; diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c index 79c87252fc2..d44b49cc706 100644 --- a/source/blender/render/intern/source/renderdatabase.c +++ b/source/blender/render/intern/source/renderdatabase.c @@ -104,7 +104,10 @@ #define RE_MCOL_ELEMS 4 #define RE_UV_ELEMS 2 #define RE_SURFNOR_ELEMS 3 +#define RE_RADFACE_ELEMS 1 #define RE_SIMPLIFY_ELEMS 2 +#define RE_FACE_ELEMS 1 +#define RE_NMAP_TANGENT_ELEMS 12 float *RE_vertren_get_sticky(ObjectRen *obr, VertRen *ver, int verify) { @@ -362,12 +365,43 @@ float *RE_vlakren_get_surfnor(ObjectRen *obr, VlakRen *vlak, int verify) return surfnor + (vlak->index & 255)*RE_SURFNOR_ELEMS; } +float *RE_vlakren_get_nmap_tangent(ObjectRen *obr, VlakRen *vlak, int verify) +{ + float *tangent; + int nr= vlak->index>>8; + + tangent= obr->vlaknodes[nr].tangent; + if(tangent==NULL) { + if(verify) + tangent= obr->vlaknodes[nr].tangent= MEM_callocN(256*RE_NMAP_TANGENT_ELEMS*sizeof(float), "tangent table"); + else + return NULL; + } + return tangent + (vlak->index & 255)*RE_NMAP_TANGENT_ELEMS; +} + +RadFace **RE_vlakren_get_radface(ObjectRen *obr, VlakRen *vlak, int verify) +{ + RadFace **radface; + int nr= vlak->index>>8; + + radface= obr->vlaknodes[nr].radface; + if(radface==NULL) { + if(verify) + radface= obr->vlaknodes[nr].radface= MEM_callocN(256*RE_RADFACE_ELEMS*sizeof(void*), "radface table"); + else + return NULL; + } + return radface + (vlak->index & 255)*RE_RADFACE_ELEMS; +} + VlakRen *RE_vlakren_copy(ObjectRen *obr, VlakRen *vlr) { VlakRen *vlr1 = RE_findOrAddVlak(obr, obr->totvlak++); MTFace *mtface, *mtface1; MCol *mcol, *mcol1; - float *surfnor, *surfnor1; + float *surfnor, *surfnor1, *tangent, *tangent1; + RadFace **radface, **radface1; int i, index = vlr1->index; char *name; @@ -390,24 +424,31 @@ VlakRen *RE_vlakren_copy(ObjectRen *obr, VlakRen *vlr) VECCOPY(surfnor1, surfnor); } + tangent= RE_vlakren_get_nmap_tangent(obr, vlr, 0); + if(tangent) { + tangent1= RE_vlakren_get_nmap_tangent(obr, vlr1, 1); + memcpy(tangent1, tangent, sizeof(float)*RE_NMAP_TANGENT_ELEMS); + } + + radface= RE_vlakren_get_radface(obr, vlr, 0); + if(radface) { + radface1= RE_vlakren_get_radface(obr, vlr1, 1); + *radface1= *radface; + } + return vlr1; } int RE_vlakren_get_normal(Render *re, ObjectInstanceRen *obi, VlakRen *vlr, float *nor) { - float xn, yn, zn, v1[3]; - float (*imat)[3]= obi->imat; + float v1[3], (*nmat)[3]= obi->nmat; int flipped= 0; if(obi->flag & R_TRANSFORMED) { - xn= vlr->n[0]; - yn= vlr->n[1]; - zn= vlr->n[2]; + VECCOPY(nor, vlr->n); - /* transpose! */ - nor[0]= imat[0][0]*xn+imat[0][1]*yn+imat[0][2]*zn; - nor[1]= imat[1][0]*xn+imat[1][1]*yn+imat[1][2]*zn; - nor[2]= imat[2][0]*xn+imat[2][1]*yn+imat[2][2]*zn; + Mat3MulVecfl(nmat, nor); + Normalize(nor); } else VECCOPY(nor, vlr->n); @@ -442,16 +483,16 @@ void RE_set_customdata_names(ObjectRen *obr, CustomData *data) DerivedMesh which stores the layers is freed */ CustomDataLayer *layer; - int numlayers, i, mtfn, mcn; + int numtf = 0, numcol = 0, i, mtfn, mcn; if (CustomData_has_layer(data, CD_MTFACE)) { - numlayers= CustomData_number_of_layers(data, CD_MTFACE); - obr->mtface= MEM_callocN(sizeof(*obr->mtface)*numlayers, "mtfacenames"); + numtf= CustomData_number_of_layers(data, CD_MTFACE); + obr->mtface= MEM_callocN(sizeof(*obr->mtface)*numtf, "mtfacenames"); } if (CustomData_has_layer(data, CD_MCOL)) { - numlayers= CustomData_number_of_layers(data, CD_MCOL); - obr->mcol= MEM_callocN(sizeof(*obr->mcol)*numlayers, "mcolnames"); + numcol= CustomData_number_of_layers(data, CD_MCOL); + obr->mcol= MEM_callocN(sizeof(*obr->mcol)*numcol, "mcolnames"); } for (i=0, mtfn=0, mcn=0; i < data->totlayer; i++) { @@ -459,11 +500,12 @@ void RE_set_customdata_names(ObjectRen *obr, CustomData *data) if (layer->type == CD_MTFACE) { strcpy(obr->mtface[mtfn++], layer->name); - obr->actmtface= layer->active_rnd; + obr->actmtface= CLAMPIS(layer->active_rnd, 0, numtf); + obr->bakemtface= layer->active; } else if (layer->type == CD_MCOL) { strcpy(obr->mcol[mcn++], layer->name); - obr->actmcol= layer->active_rnd; + obr->actmcol= CLAMPIS(layer->active_rnd, 0, numcol); } } } @@ -516,7 +558,7 @@ float *RE_strandren_get_surfnor(ObjectRen *obr, StrandRen *strand, int verify) surfnor= obr->strandnodes[nr].surfnor; if(surfnor==NULL) { if(verify) - surfnor= obr->strandnodes[nr].surfnor= MEM_callocN(256*RE_SURFNOR_ELEMS*sizeof(float), "surfnor table"); + surfnor= obr->strandnodes[nr].surfnor= MEM_callocN(256*RE_SURFNOR_ELEMS*sizeof(float), "surfnor strand table"); else return NULL; } @@ -536,7 +578,7 @@ float *RE_strandren_get_uv(ObjectRen *obr, StrandRen *strand, int n, char **name float *uv= node->uv; int size= (n+1)*256; - node->uv= MEM_callocN(size*sizeof(float)*RE_UV_ELEMS, "Strand uv"); + node->uv= MEM_callocN(size*sizeof(float)*RE_UV_ELEMS, "strand uv table"); if(uv) { size= node->totuv*256; @@ -570,7 +612,7 @@ MCol *RE_strandren_get_mcol(ObjectRen *obr, StrandRen *strand, int n, char **nam MCol *mcol= node->mcol; int size= (n+1)*256; - node->mcol= MEM_callocN(size*sizeof(MCol)*RE_MCOL_ELEMS, "Strand mcol"); + node->mcol= MEM_callocN(size*sizeof(MCol)*RE_MCOL_ELEMS, "strand mcol table"); if(mcol) { size= node->totmcol*256; @@ -599,13 +641,28 @@ float *RE_strandren_get_simplify(struct ObjectRen *obr, struct StrandRen *strand simplify= obr->strandnodes[nr].simplify; if(simplify==NULL) { if(verify) - simplify= obr->strandnodes[nr].simplify= MEM_callocN(256*RE_SIMPLIFY_ELEMS*sizeof(float), "simplify table"); + simplify= obr->strandnodes[nr].simplify= MEM_callocN(256*RE_SIMPLIFY_ELEMS*sizeof(float), "simplify strand table"); else return NULL; } return simplify + (strand->index & 255)*RE_SIMPLIFY_ELEMS; } +int *RE_strandren_get_face(ObjectRen *obr, StrandRen *strand, int verify) +{ + int *face; + int nr= strand->index>>8; + + face= obr->strandnodes[nr].face; + if(face==NULL) { + if(verify) + face= obr->strandnodes[nr].face= MEM_callocN(256*RE_FACE_ELEMS*sizeof(int), "face strand table"); + else + return NULL; + } + return face + (strand->index & 255)*RE_FACE_ELEMS; +} + /* winspeed is exception, it is stored per instance */ float *RE_strandren_get_winspeed(ObjectInstanceRen *obi, StrandRen *strand, int verify) { @@ -616,7 +673,7 @@ float *RE_strandren_get_winspeed(ObjectInstanceRen *obi, StrandRen *strand, int if(winspeed==NULL) { if(verify) { totvector= obi->obr->totvert + obi->obr->totstrand; - winspeed= obi->vectors= MEM_callocN(totvector*RE_WINSPEED_ELEMS*sizeof(float), "winspeed table"); + winspeed= obi->vectors= MEM_callocN(totvector*RE_WINSPEED_ELEMS*sizeof(float), "winspeed strand table"); } else return NULL; @@ -671,14 +728,14 @@ StrandBuffer *RE_addStrandBuffer(ObjectRen *obr, int totvert) strandbuf->totvert= totvert; strandbuf->obr= obr; - BLI_addtail(&obr->strandbufs, strandbuf); + obr->strandbuf= strandbuf; return strandbuf; } /* ------------------------------------------------------------------------ */ -ObjectRen *RE_addRenderObject(Render *re, Object *ob, Object *par, int index, int psysindex) +ObjectRen *RE_addRenderObject(Render *re, Object *ob, Object *par, int index, int psysindex, int lay) { ObjectRen *obr= MEM_callocN(sizeof(ObjectRen), "object render struct"); @@ -687,11 +744,7 @@ ObjectRen *RE_addRenderObject(Render *re, Object *ob, Object *par, int index, in obr->par= par; obr->index= index; obr->psysindex= psysindex; - - if(!re->objecthash) - re->objecthash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); - if(!BLI_ghash_lookup(re->objecthash, ob)) - BLI_ghash_insert(re->objecthash, ob, obr); + obr->lay= lay; return obr; } @@ -737,6 +790,10 @@ void free_renderdata_vlaknodes(VlakTableNode *vlaknodes) MEM_freeN(vlaknodes[a].mcol); if(vlaknodes[a].surfnor) MEM_freeN(vlaknodes[a].surfnor); + if(vlaknodes[a].tangent) + MEM_freeN(vlaknodes[a].tangent); + if(vlaknodes[a].radface) + MEM_freeN(vlaknodes[a].radface); } MEM_freeN(vlaknodes); @@ -761,6 +818,8 @@ void free_renderdata_strandnodes(StrandTableNode *strandnodes) MEM_freeN(strandnodes[a].surfnor); if(strandnodes[a].simplify) MEM_freeN(strandnodes[a].simplify); + if(strandnodes[a].face) + MEM_freeN(strandnodes[a].face); } MEM_freeN(strandnodes); @@ -802,9 +861,12 @@ void free_renderdata_tables(Render *re) obr->strandnodeslen= 0; } - for(strandbuf=obr->strandbufs.first; strandbuf; strandbuf=strandbuf->next) + strandbuf= obr->strandbuf; + if(strandbuf) { if(strandbuf->vert) MEM_freeN(strandbuf->vert); - BLI_freelistN(&obr->strandbufs); + if(strandbuf->bound) MEM_freeN(strandbuf->bound); + MEM_freeN(strandbuf); + } if(obr->mtface) MEM_freeN(obr->mtface); @@ -822,27 +884,15 @@ void free_renderdata_tables(Render *re) re->totinstance= 0; re->instancetable.first= re->instancetable.last= NULL; } - else { - BLI_freelistN(&re->instancetable); - - if(re->objecthash) { - BLI_ghash_free(re->objecthash, NULL, NULL); - re->objecthash= NULL; - } - } if(re->sortedhalos) { MEM_freeN(re->sortedhalos); re->sortedhalos= NULL; } - if(re->strandbuckets) { - free_buckets(re->strandbuckets); - re->strandbuckets= NULL; - } - BLI_freelistN(&re->customdata_names); BLI_freelistN(&re->objecttable); + BLI_freelistN(&re->instancetable); } /* ------------------------------------------------------------------------ */ @@ -1257,13 +1307,11 @@ void project_renderdata(Render *re, void (*projectfunc)(float *, float mat[][4], } } - - project_strands(re, projectfunc, do_pano, do_buckets); } /* ------------------------------------------------------------------------- */ -void RE_addRenderInstance(Render *re, ObjectRen *obr, Object *ob, Object *par, int index, int psysindex, float mat[][4]) +ObjectInstanceRen *RE_addRenderInstance(Render *re, ObjectRen *obr, Object *ob, Object *par, int index, int psysindex, float mat[][4], int lay) { ObjectInstanceRen *obi; float mat3[3][3]; @@ -1274,15 +1322,19 @@ void RE_addRenderInstance(Render *re, ObjectRen *obr, Object *ob, Object *par, i obi->par= par; obi->index= index; obi->psysindex= psysindex; + obi->lay= lay; if(mat) { Mat4CpyMat4(obi->mat, mat); Mat3CpyMat4(mat3, mat); - Mat3Inv(obi->imat, mat3); + Mat3Inv(obi->nmat, mat3); + Mat3Transp(obi->nmat); obi->flag |= R_DUPLI_TRANSFORMED; } BLI_addtail(&re->instancetable, obi); + + return obi; } void RE_makeRenderInstances(Render *re) @@ -1301,15 +1353,6 @@ void RE_makeRenderInstances(Render *re) for(oldobi=re->instancetable.first; oldobi; oldobi=oldobi->next) { *obi= *oldobi; - if(!obi->obr) { - /* dupli objects are created after object instances, so they were - * stored in a object -> objectren hash, we do lookup of the actual - * pointer here */ - if(re->objecthash && (obi->obr=BLI_ghash_lookup(re->objecthash, obi->ob))) - while(obi->obr && obi->obr->psysindex != obi->psysindex) - obi->obr= obi->obr->next; - } - if(obi->obr) { obi->prev= obi->next= NULL; BLI_addtail(&newlist, obi); @@ -1319,11 +1362,44 @@ void RE_makeRenderInstances(Render *re) re->totinstance--; } - if(re->objecthash) { - BLI_ghash_free(re->objecthash, NULL, NULL); - re->objecthash= NULL; - } BLI_freelistN(&re->instancetable); re->instancetable= newlist; } +int clip_render_object(float boundbox[][3], float *bounds, float winmat[][4]) +{ + float mat[4][4], vec[4]; + int a, fl, flag= -1; + + Mat4CpyMat4(mat, winmat); + + for(a=0; a<8; a++) { + vec[0]= (a & 1)? boundbox[0][0]: boundbox[1][0]; + vec[1]= (a & 2)? boundbox[0][1]: boundbox[1][1]; + vec[2]= (a & 4)? boundbox[0][2]: boundbox[1][2]; + vec[3]= 1.0; + Mat4MulVec4fl(mat, vec); + + fl= 0; + if(bounds) { + if(vec[0] > bounds[1]*vec[3]) fl |= 1; + if(vec[0]< bounds[0]*vec[3]) fl |= 2; + if(vec[1] > bounds[3]*vec[3]) fl |= 4; + if(vec[1]< bounds[2]*vec[3]) fl |= 8; + } + else { + if(vec[0] < -vec[3]) fl |= 1; + if(vec[0] > vec[3]) fl |= 2; + if(vec[1] < -vec[3]) fl |= 4; + if(vec[1] > vec[3]) fl |= 8; + } + if(vec[2] < -vec[3]) fl |= 16; + if(vec[2] > vec[3]) fl |= 32; + + flag &= fl; + if(flag==0) return 0; + } + + return flag; +} + diff --git a/source/blender/render/intern/source/shadbuf.c b/source/blender/render/intern/source/shadbuf.c index 8e37606cdf9..c53a2b68c9c 100644 --- a/source/blender/render/intern/source/shadbuf.c +++ b/source/blender/render/intern/source/shadbuf.c @@ -34,6 +34,7 @@ #include "DNA_material_types.h" #include "BKE_global.h" +#include "BKE_scene.h" #include "BKE_utildefines.h" #include "BLI_arithb.h" @@ -54,7 +55,7 @@ /* XXX, could be better implemented... this is for endian issues */ -#if defined(__sgi) || defined(__sparc) || defined(__sparc__) || defined (__PPC__) || defined (__ppc__) || defined (__BIG_ENDIAN__) +#if defined(__sgi) || defined(__sparc) || defined(__sparc__) || defined (__PPC__) || defined (__ppc__) || defined (__hppa__) || defined (__BIG_ENDIAN__) #define RCOMP 3 #define GCOMP 2 #define BCOMP 1 @@ -140,10 +141,11 @@ static float *give_jitter_tab(int samp) } -static void make_jitter_weight_tab(ShadBuf *shb, short filtertype) +static void make_jitter_weight_tab(Render *re, ShadBuf *shb, short filtertype) { float *jit, totw= 0.0f; - int a, tot=shb->samp*shb->samp; + int samp= get_render_shadow_samples(&re->r, shb->samp); + int a, tot=samp*samp; shb->weight= MEM_mallocN(sizeof(float)*tot, "weight tab lamp"); @@ -169,7 +171,7 @@ static void compress_shadowbuf(ShadBuf *shb, int *rectz, int square) { ShadSampleBuf *shsample; float dist; - unsigned long *ztile; + uintptr_t *ztile; int *rz, *rz1, verg, verg1, size= shb->size; int a, x, y, minx, miny, byt1, byt2; char *rc, *rcline, *ctile, *zt; @@ -177,10 +179,10 @@ static void compress_shadowbuf(ShadBuf *shb, int *rectz, int square) shsample= MEM_mallocN( sizeof(ShadSampleBuf), "shad sample buf"); BLI_addtail(&shb->buffers, shsample); - shsample->zbuf= MEM_mallocN( sizeof(unsigned long)*(size*size)/256, "initshadbuf2"); + shsample->zbuf= MEM_mallocN( sizeof(uintptr_t)*(size*size)/256, "initshadbuf2"); shsample->cbuf= MEM_callocN( (size*size)/256, "initshadbuf3"); - ztile= (unsigned long *)shsample->zbuf; + ztile= (uintptr_t *)shsample->zbuf; ctile= shsample->cbuf; /* help buffer */ @@ -235,7 +237,7 @@ static void compress_shadowbuf(ShadBuf *shb, int *rectz, int square) } if(byt1 && byt2) { /* only store byte */ *ctile= 1; - *ztile= (unsigned long)MEM_mallocN(256+4, "tile1"); + *ztile= (uintptr_t)MEM_mallocN(256+4, "tile1"); rz= (int *)*ztile; *rz= *rz1; @@ -245,7 +247,7 @@ static void compress_shadowbuf(ShadBuf *shb, int *rectz, int square) } else if(byt1) { /* only store short */ *ctile= 2; - *ztile= (unsigned long)MEM_mallocN(2*256+4,"Tile2"); + *ztile= (uintptr_t)MEM_mallocN(2*256+4,"Tile2"); rz= (int *)*ztile; *rz= *rz1; @@ -258,7 +260,7 @@ static void compress_shadowbuf(ShadBuf *shb, int *rectz, int square) } else { /* store triple */ *ctile= 3; - *ztile= (unsigned long)MEM_mallocN(3*256,"Tile3"); + *ztile= (uintptr_t)MEM_mallocN(3*256,"Tile3"); zt= (char *)*ztile; rc= rcline; @@ -288,15 +290,19 @@ static void shadowbuf_autoclip(Render *re, LampRen *lar) Material *ma= NULL; float minz, maxz, vec[3], viewmat[4][4], obviewmat[4][4]; unsigned int lay = -1; - int i, a, ok= 1; + int i, a, maxtotvert, ok= 1; char *clipflag; minz= 1.0e30f; maxz= -1.0e30f; Mat4CpyMat4(viewmat, lar->shb->viewmat); - if(lar->mode & LA_LAYER) lay= lar->lay; + if(lar->mode & (LA_LAYER|LA_LAYER_SHADOW)) lay= lar->lay; - clipflag= MEM_callocN(sizeof(char)*re->totvert, "autoclipflag"); + maxtotvert= 0; + for(obr=re->objecttable.first; obr; obr=obr->next) + maxtotvert= MAX2(obr->totvert, maxtotvert); + + clipflag= MEM_callocN(sizeof(char)*maxtotvert, "autoclipflag"); /* set clip in vertices when face visible */ for(i=0, obi=re->instancetable.first; obi; i++, obi=obi->next) { @@ -321,7 +327,7 @@ static void shadowbuf_autoclip(Render *re, LampRen *lar) if((ma->mode & MA_SHADBUF)==0) ok= 0; } - if(ok && (vlr->lay & lay)) { + if(ok && (obi->lay & lay)) { clipflag[vlr->v1->index]= 1; clipflag[vlr->v2->index]= 1; clipflag[vlr->v3->index]= 1; @@ -402,8 +408,8 @@ void makeshadowbuf(Render *re, LampRen *lar) if(ELEM(lar->buftype, LA_SHADBUF_REGULAR, LA_SHADBUF_HALFWAY)) { /* jitter, weights - not threadsafe! */ BLI_lock_thread(LOCK_CUSTOM1); - shb->jit= give_jitter_tab(shb->samp); - make_jitter_weight_tab(shb, lar->filtertype); + shb->jit= give_jitter_tab(get_render_shadow_samples(&re->r, shb->samp)); + make_jitter_weight_tab(re, shb, lar->filtertype); BLI_unlock_thread(LOCK_CUSTOM1); shb->totbuf= lar->buffers; @@ -536,7 +542,7 @@ void freeshadowbuf(LampRen *lar) v= (shb->size*shb->size)/256; for(shsample= shb->buffers.first; shsample; shsample= shsample->next) { - long *ztile= shsample->zbuf; + intptr_t *ztile= shsample->zbuf; char *ctile= shsample->cbuf; for(b=0; b<v; b++, ztile++, ctile++) @@ -630,7 +636,7 @@ static float readshadowbuf(ShadBuf *shb, ShadSampleBuf *shsample, int bias, int else { /* got warning on this for 64 bits.... */ /* but it's working code! in this case rz is not a pointer but zvalue (ton) */ - zsamp= (int) rz; + zsamp= GET_INT_FROM_POINTER(rz); } /* tricky stuff here; we use ints which can overflow easily with bias values */ @@ -648,11 +654,11 @@ static float readshadowbuf(ShadBuf *shb, ShadSampleBuf *shsample, int bias, int /* the externally called shadow testing (reading) function */ /* return 1.0: no shadow at all */ -float testshadowbuf(ShadBuf *shb, float *rco, float *dxco, float *dyco, float inp) +float testshadowbuf(Render *re, ShadBuf *shb, float *rco, float *dxco, float *dyco, float inp, float mat_bias) { ShadSampleBuf *shsample; float fac, co[4], dx[3], dy[3], shadfac=0.0f; - float xs1,ys1, siz, *jit, *weight, xres, yres; + float xs1,ys1, siz, *jit, *weight, xres, yres, biasf; int xs, ys, zs, bias, *rz; short a, num; @@ -686,13 +692,16 @@ float testshadowbuf(ShadBuf *shb, float *rco, float *dxco, float *dyco, float in zs= ((float)0x7FFFFFFF)*fac; /* take num*num samples, increase area with fac */ - num= shb->samp*shb->samp; + num= get_render_shadow_samples(&re->r, shb->samp); + num= num*num; fac= shb->soft; + if(mat_bias!=0.0f) biasf= shb->bias*mat_bias; + else biasf= shb->bias; /* with inp==1.0, bias is half the size. correction value was 1.1, giving errors on cube edges, with one side being almost frontal lighted (ton) */ - bias= (1.5f-inp*inp)*shb->bias; - + bias= (1.5f-inp*inp)*biasf; + if(num==1) { for(shsample= shb->buffers.first; shsample; shsample= shsample->next) shadfac += readshadowbuf(shb, shsample, bias, (int)xs1, (int)ys1, zs); @@ -807,7 +816,7 @@ static float readshadowbuf_halo(ShadBuf *shb, ShadSampleBuf *shsample, int xs, i else { /* same as before */ /* still working code! (ton) */ - zsamp= (int) rz; + zsamp= GET_INT_FROM_POINTER(rz); } /* NO schadow when sampled at 'eternal' distance */ @@ -1511,7 +1520,7 @@ static void isb_bsp_fillfaces(Render *re, LampRen *lar, ISBBranch *root) minmaxf[2]= (2.0f*root->box.ymin - size-2.0f)/size; minmaxf[3]= (2.0f*root->box.ymax - size+2.0f)/size; - if(lar->mode & LA_LAYER) lay= lar->lay; + if(lar->mode & (LA_LAYER|LA_LAYER_SHADOW)) lay= lar->lay; /* (ab)use zspan, since we use zbuffer clipping code */ zbuf_alloc_span(&zspan, size, size, re->clipcrop); @@ -1552,7 +1561,7 @@ static void isb_bsp_fillfaces(Render *re, LampRen *lar, ISBBranch *root) zspanstrand.shad_alpha= zspan.shad_alpha= ma->shad_alpha; } - if(ok && (vlr->lay & lay)) { + if(ok && (obi->lay & lay)) { float hoco[4][4]; int c1, c2, c3, c4=0; int d1, d2, d3, d4=0; @@ -1743,7 +1752,7 @@ static void isb_make_buffer(RenderPart *pa, LampRen *lar) ISBSample *samp, *samplebuf[16]; /* should be RE_MAX_OSA */ ISBBranch root; MemArena *memarena; - long *rd; + intptr_t *rd; int *recto, *rectp, x, y, sindex, sample, bsp_err=0; /* storage for shadow, per thread */ diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c index f937218cdb6..476330152ec 100644 --- a/source/blender/render/intern/source/shadeinput.c +++ b/source/blender/render/intern/source/shadeinput.c @@ -31,6 +31,7 @@ #include "MTC_matrixops.h" #include "BLI_arithb.h" +#include "BLI_blenlib.h" #include "DNA_curve_types.h" #include "DNA_group_types.h" @@ -129,7 +130,7 @@ void shade_material_loop(ShadeInput *shi, ShadeResult *shr) } /* disable adding of sky for raytransp */ if(shi->mat->mode & MA_RAYTRANSP) - if(shi->layflag & SCE_LAY_SKY) + if((shi->layflag & SCE_LAY_SKY) && (R.r.alphamode==R_ADDSKY)) shr->alpha= 1.0f; } } @@ -161,11 +162,16 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr) } /* MIST */ - if((R.wrld.mode & WO_MIST) && (shi->mat->mode & MA_NOMIST)==0 ) { + if((shi->passflag & SCE_PASS_MIST) || ((R.wrld.mode & WO_MIST) && (shi->mat->mode & MA_NOMIST)==0)) { if(R.r.mode & R_ORTHO) - alpha= mistfactor(-shi->co[2], shi->co); + shr->mist= mistfactor(-shi->co[2], shi->co); else - alpha= mistfactor(VecLength(shi->co), shi->co); + shr->mist= mistfactor(VecLength(shi->co), shi->co); + } + else shr->mist= 0.0f; + + if((R.wrld.mode & WO_MIST) && (shi->mat->mode & MA_NOMIST)==0 ) { + alpha= shr->mist; } else alpha= 1.0f; @@ -180,6 +186,8 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr) } else shr->combined[3]= 1.0f; + /* add z */ + shr->z= -shi->co[2]; } /* **************************************************************************** */ @@ -209,19 +217,6 @@ void vlr_set_uv_indices(VlakRen *vlr, int *i1, int *i2, int *i3) } } -static void normal_transform(float imat[][3], float *nor) -{ - float xn, yn, zn; - - xn= nor[0]; - yn= nor[1]; - zn= nor[2]; - - nor[0]= imat[0][0]*xn+imat[0][1]*yn+imat[0][2]*zn; - nor[1]= imat[1][0]*xn+imat[1][1]*yn+imat[1][2]*zn; - nor[2]= imat[2][0]*xn+imat[2][1]*yn+imat[2][2]*zn; -} - /* copy data from face to ShadeInput, general case */ /* indices 0 1 2 3 only */ void shade_input_set_triangle_i(ShadeInput *shi, ObjectInstanceRen *obi, VlakRen *vlr, short i1, short i2, short i3) @@ -231,7 +226,7 @@ void shade_input_set_triangle_i(ShadeInput *shi, ObjectInstanceRen *obi, VlakRen shi->vlr= vlr; shi->obi= obi; shi->obr= obi->obr; - + shi->v1= vpp[i1]; shi->v2= vpp[i2]; shi->v3= vpp[i3]; @@ -261,9 +256,9 @@ void shade_input_set_triangle_i(ShadeInput *shi, ObjectInstanceRen *obi, VlakRen VECCOPY(shi->n3, shi->v3->n); if(obi->flag & R_TRANSFORMED) { - normal_transform(obi->imat, shi->n1); - normal_transform(obi->imat, shi->n2); - normal_transform(obi->imat, shi->n3); + Mat3MulVecfl(obi->nmat, shi->n1); + Mat3MulVecfl(obi->nmat, shi->n2); + Mat3MulVecfl(obi->nmat, shi->n3); } if(!(vlr->flag & (R_NOPUNOFLIP|R_TANGENT))) { @@ -348,10 +343,19 @@ void shade_input_set_strand(ShadeInput *shi, StrandRen *strand, StrandPoint *spo VECCOPY(shi->orignor, shi->facenor); /* shade_input_set_normals equivalent */ - if(shi->mat->mode & MA_TANGENT_STR) + if(shi->mat->mode & MA_TANGENT_STR) { VECCOPY(shi->vn, spoint->tan) - else - VECCOPY(shi->vn, spoint->nor) + } + else { + float cross[3]; + + Crossf(cross, spoint->co, spoint->tan); + Crossf(shi->vn, cross, spoint->tan); + Normalize(shi->vn); + + if(INPR(shi->vn, shi->view) < 0.0f) + VecMulf(shi->vn, -1.0f); + } VECCOPY(shi->vno, shi->vn); } @@ -374,6 +378,7 @@ void shade_input_set_strand_texco(ShadeInput *shi, StrandRen *strand, StrandVert if(mode & (MA_TANGENT_V|MA_NORMAP_TANG)) { VECCOPY(shi->tang, spoint->tan); + VECCOPY(shi->nmaptang, spoint->tan); } if(mode & MA_STR_SURFDIFF) { @@ -388,7 +393,7 @@ void shade_input_set_strand_texco(ShadeInput *shi, StrandRen *strand, StrandVert shi->surfdist= 0.0f; for(sv=strand->vert; sv!=svert; sv++) shi->surfdist+=VecLenf(sv->co, (sv+1)->co); - shi->surfdist += 0.5f*(spoint->strandco+1.0f)*VecLenf(sv->co, (sv+1)->co); + shi->surfdist += spoint->t*VecLenf(sv->co, (sv+1)->co); } } @@ -422,7 +427,7 @@ void shade_input_set_strand_texco(ShadeInput *shi, StrandRen *strand, StrandVert } if(texco & TEXCO_STRAND) { - shi->strand= spoint->strandco; + shi->strandco= spoint->strandco; if(shi->osatex) { shi->dxstrand= spoint->dtstrandco; @@ -438,6 +443,8 @@ void shade_input_set_strand_texco(ShadeInput *shi, StrandRen *strand, StrandVert shi->totuv= 0; shi->totcol= 0; + shi->actuv= obr->actmtface; + shi->actcol= obr->actmcol; if(mode & (MA_VERTEXCOL|MA_VERTEXCOLP)) { for (i=0; (mcol=RE_strandren_get_mcol(obr, strand, i, &name, 0)); i++) { @@ -447,15 +454,15 @@ void shade_input_set_strand_texco(ShadeInput *shi, StrandRen *strand, StrandVert shi->totcol++; scol->name= name; - scol->col[0]= cp[0]/255.0f; - scol->col[1]= cp[1]/255.0f; - scol->col[2]= cp[2]/255.0f; + scol->col[0]= cp[3]/255.0f; + scol->col[1]= cp[2]/255.0f; + scol->col[2]= cp[1]/255.0f; } if(shi->totcol) { - shi->vcol[0]= shi->col[0].col[0]; - shi->vcol[1]= shi->col[0].col[1]; - shi->vcol[2]= shi->col[0].col[2]; + shi->vcol[0]= shi->col[shi->actcol].col[0]; + shi->vcol[1]= shi->col[shi->actcol].col[1]; + shi->vcol[2]= shi->col[shi->actcol].col[2]; } else { shi->vcol[0]= 0.0f; @@ -488,7 +495,7 @@ void shade_input_set_strand_texco(ShadeInput *shi, StrandRen *strand, StrandVert suv->dyuv[1]= 0.0f; } - if((mode & MA_FACETEXTURE) && i==0) { + if((mode & MA_FACETEXTURE) && i==obr->actmtface) { if((mode & (MA_VERTEXCOL|MA_VERTEXCOLP))==0) { shi->vcol[0]= 1.0f; shi->vcol[1]= 1.0f; @@ -537,6 +544,7 @@ void shade_input_set_strand_texco(ShadeInput *shi, StrandRen *strand, StrandVert if((mode & MA_TANGENT_V)==0) { /* just prevent surprises */ shi->tang[0]= shi->tang[1]= shi->tang[2]= 0.0f; + shi->nmaptang[0]= shi->nmaptang[1]= shi->nmaptang[2]= 0.0f; } } } @@ -565,31 +573,25 @@ void shade_input_set_strand_texco(ShadeInput *shi, StrandRen *strand, StrandVert } } -/* scanline pixel coordinates */ -/* requires set_triangle */ -void shade_input_set_viewco(ShadeInput *shi, float x, float y, float z) +/* from scanline pixel coordinates to 3d coordinates, requires set_triangle */ +void shade_input_calc_viewco(ShadeInput *shi, float x, float y, float z, float *view, float *dxyview, float *co, float *dxco, float *dyco) { - float fac; - - /* currently in use for dithering (soft shadow), node preview, irregular shad */ - shi->xs= (int)(x); - shi->ys= (int)(y); + /* returns not normalized, so is in viewplane coords */ + calc_view_vector(view, x, y); - calc_view_vector(shi->view, x, y); /* returns not normalized, so is in viewplane coords */ - - /* wire cannot use normal for calculating shi->co */ if(shi->mat->mode & MA_WIRE) { - + /* wire cannot use normal for calculating shi->co, so + * we reconstruct the coordinate less accurate */ if(R.r.mode & R_ORTHO) - calc_renderco_ortho(shi->co, x, y, z); + calc_renderco_ortho(co, x, y, z); else - calc_renderco_zbuf(shi->co, shi->view, z); + calc_renderco_zbuf(co, view, z); } else { - float dface, v1[3]; + /* for non-wire, intersect with the triangle to get the exact coord */ + float fac, dface, v1[3]; VECCOPY(v1, shi->v1->co); - if(shi->obi->flag & R_TRANSFORMED) Mat4MulVecfl(shi->obi->mat, v1); @@ -601,72 +603,98 @@ void shade_input_set_viewco(ShadeInput *shi, float x, float y, float z) float fx= 2.0f/(R.winx*R.winmat[0][0]); float fy= 2.0f/(R.winy*R.winmat[1][1]); - shi->co[0]= (x - 0.5f*R.winx)*fx - R.winmat[3][0]/R.winmat[0][0]; - shi->co[1]= (y - 0.5f*R.winy)*fy - R.winmat[3][1]/R.winmat[1][1]; + 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]; /* using a*x + b*y + c*z = d equation, (a b c) is normal */ if(shi->facenor[2]!=0.0f) - shi->co[2]= (dface - shi->facenor[0]*shi->co[0] - shi->facenor[1]*shi->co[1])/shi->facenor[2]; + co[2]= (dface - shi->facenor[0]*co[0] - shi->facenor[1]*co[1])/shi->facenor[2]; else - shi->co[2]= 0.0f; + co[2]= 0.0f; - if(shi->osatex || (R.r.mode & R_SHADOW) ) { - shi->dxco[0]= fx; - shi->dxco[1]= 0.0f; + if(dxco && dyco) { + dxco[0]= fx; + dxco[1]= 0.0f; if(shi->facenor[2]!=0.0f) - shi->dxco[2]= (shi->facenor[0]*fx)/shi->facenor[2]; + dxco[2]= (shi->facenor[0]*fx)/shi->facenor[2]; else - shi->dxco[2]= 0.0f; + dxco[2]= 0.0f; - shi->dyco[0]= 0.0f; - shi->dyco[1]= fy; + dyco[0]= 0.0f; + dyco[1]= fy; if(shi->facenor[2]!=0.0f) - shi->dyco[2]= (shi->facenor[1]*fy)/shi->facenor[2]; + dyco[2]= (shi->facenor[1]*fy)/shi->facenor[2]; else - shi->dyco[2]= 0.0f; + dyco[2]= 0.0f; - if( (shi->mat->texco & TEXCO_REFL) ) { - if(shi->co[2]!=0.0f) fac= 1.0f/shi->co[2]; else fac= 0.0f; - shi->dxview= -R.viewdx*fac; - shi->dyview= -R.viewdy*fac; + if(dxyview) { + if(co[2]!=0.0f) fac= 1.0f/co[2]; else fac= 0.0f; + dxyview[0]= -R.viewdx*fac; + dxyview[1]= -R.viewdy*fac; } } } else { float div; - div= shi->facenor[0]*shi->view[0] + shi->facenor[1]*shi->view[1] + shi->facenor[2]*shi->view[2]; + div= shi->facenor[0]*view[0] + shi->facenor[1]*view[1] + shi->facenor[2]*view[2]; if (div!=0.0f) fac= dface/div; else fac= 0.0f; - shi->co[0]= fac*shi->view[0]; - shi->co[1]= fac*shi->view[1]; - shi->co[2]= fac*shi->view[2]; + co[0]= fac*view[0]; + co[1]= fac*view[1]; + co[2]= fac*view[2]; /* pixel dx/dy for render coord */ - if(shi->osatex || (R.r.mode & R_SHADOW) ) { + if(dxco && dyco) { float u= dface/(div - R.viewdx*shi->facenor[0]); float v= dface/(div - R.viewdy*shi->facenor[1]); - shi->dxco[0]= shi->co[0]- (shi->view[0]-R.viewdx)*u; - shi->dxco[1]= shi->co[1]- (shi->view[1])*u; - shi->dxco[2]= shi->co[2]- (shi->view[2])*u; + dxco[0]= co[0]- (view[0]-R.viewdx)*u; + dxco[1]= co[1]- (view[1])*u; + dxco[2]= co[2]- (view[2])*u; - shi->dyco[0]= shi->co[0]- (shi->view[0])*v; - shi->dyco[1]= shi->co[1]- (shi->view[1]-R.viewdy)*v; - shi->dyco[2]= shi->co[2]- (shi->view[2])*v; + dyco[0]= co[0]- (view[0])*v; + dyco[1]= co[1]- (view[1]-R.viewdy)*v; + dyco[2]= co[2]- (view[2])*v; - if( (shi->mat->texco & TEXCO_REFL) ) { + if(dxyview) { if(fac!=0.0f) fac= 1.0f/fac; - shi->dxview= -R.viewdx*fac; - shi->dyview= -R.viewdy*fac; + dxyview[0]= -R.viewdx*fac; + dxyview[1]= -R.viewdy*fac; } } } } /* cannot normalize earlier, code above needs it at viewplane level */ - Normalize(shi->view); + Normalize(view); +} + +/* from scanline pixel coordinates to 3d coordinates, requires set_triangle */ +void shade_input_set_viewco(ShadeInput *shi, float x, float y, float xs, float ys, float z) +{ + float *dxyview= NULL, *dxco= NULL, *dyco= NULL; + + /* currently in use for dithering (soft shadow), node preview, irregular shad */ + shi->xs= (int)xs; + shi->ys= (int)ys; + + /* original scanline coordinate without jitter */ + shi->scanco[0]= x; + shi->scanco[1]= y; + shi->scanco[2]= z; + + /* check if we need derivatives */ + if(shi->osatex || (R.r.mode & R_SHADOW)) { + dxco= shi->dxco; + dyco= shi->dyco; + + if((shi->mat->texco & TEXCO_REFL)) + dxyview= &shi->dxview; + } + + shade_input_calc_viewco(shi, xs, ys, z, shi->view, dxyview, shi->co, dxco, dyco); } /* calculate U and V, for scanline (silly render face u and v are in range -1 to 0) */ @@ -798,7 +826,7 @@ void shade_input_set_shade_texco(ShadeInput *shi) int mode= shi->mode; /* or-ed result for all nodes */ short texco= shi->mat->texco; - /* calculate dxno and tangents */ + /* calculate dxno */ if(shi->vlr->flag & R_SMOOTH) { if(shi->osatex && (texco & (TEXCO_NORM|TEXCO_REFL)) ) { @@ -814,47 +842,68 @@ void shade_input_set_shade_texco(ShadeInput *shi) shi->dyno[2]= dl*n3[2]-shi->dy_u*n1[2]-shi->dy_v*n2[2]; } - - /* qdn: normalmap tangent space */ - if (mode & (MA_TANGENT_V|MA_NORMAP_TANG) || R.flag & R_NEED_TANGENT) { - float *s1, *s2, *s3; + } + + /* calc tangents */ + if (mode & (MA_TANGENT_V|MA_NORMAP_TANG) || R.flag & R_NEED_TANGENT) { + float *tangent, *s1, *s2, *s3; + float tl, tu, tv; + + if(shi->vlr->flag & R_SMOOTH) { + tl= l; + tu= u; + tv= v; + } + else { + /* qdn: flat faces have tangents too, + could pick either one, using average here */ + tl= 1.0f/3.0f; + tu= -1.0f/3.0f; + tv= -1.0f/3.0f; + } + + shi->tang[0]= shi->tang[1]= shi->tang[2]= 0.0f; + shi->nmaptang[0]= shi->nmaptang[1]= shi->nmaptang[2]= 0.0f; + + if(mode & MA_TANGENT_V) { + s1 = RE_vertren_get_tangent(obr, v1, 0); + s2 = RE_vertren_get_tangent(obr, v2, 0); + s3 = RE_vertren_get_tangent(obr, v3, 0); - s1= RE_vertren_get_tangent(obr, v1, 0); - s2= RE_vertren_get_tangent(obr, v2, 0); - s3= RE_vertren_get_tangent(obr, v3, 0); if(s1 && s2 && s3) { - shi->tang[0]= (l*s3[0] - u*s1[0] - v*s2[0]); - shi->tang[1]= (l*s3[1] - u*s1[1] - v*s2[1]); - shi->tang[2]= (l*s3[2] - u*s1[2] - v*s2[2]); + shi->tang[0]= (tl*s3[0] - tu*s1[0] - tv*s2[0]); + shi->tang[1]= (tl*s3[1] - tu*s1[1] - tv*s2[1]); + shi->tang[2]= (tl*s3[2] - tu*s1[2] - tv*s2[2]); if(obi->flag & R_TRANSFORMED) - normal_transform(obi->imat, shi->tang); + Mat3MulVecfl(obi->nmat, shi->tang); - /* qdn: normalize just in case */ Normalize(shi->tang); + VECCOPY(shi->nmaptang, shi->tang); } - else shi->tang[0]= shi->tang[1]= shi->tang[2]= 0.0f; } - } - else { - /* qdn: normalmap tangent space */ - if (mode & (MA_TANGENT_V|MA_NORMAP_TANG) || R.flag & R_NEED_TANGENT) { - /* qdn: flat faces have tangents too, - could pick either one, using average here */ - float *s1 = RE_vertren_get_tangent(obr, v1, 0); - float *s2 = RE_vertren_get_tangent(obr, v2, 0); - float *s3 = RE_vertren_get_tangent(obr, v3, 0); - if (s1 && s2 && s3) { - shi->tang[0] = (s1[0] + s2[0] + s3[0]); - shi->tang[1] = (s1[1] + s2[1] + s3[1]); - shi->tang[2] = (s1[2] + s2[2] + s3[2]); + + if(mode & MA_NORMAP_TANG || R.flag & R_NEED_TANGENT) { + tangent= RE_vlakren_get_nmap_tangent(obr, shi->vlr, 0); + + if(tangent) { + int j1= shi->i1, j2= shi->i2, j3= shi->i3; + + vlr_set_uv_indices(shi->vlr, &j1, &j2, &j3); + + s1= &tangent[j1*3]; + s2= &tangent[j2*3]; + s3= &tangent[j3*3]; + + shi->nmaptang[0]= (tl*s3[0] - tu*s1[0] - tv*s2[0]); + shi->nmaptang[1]= (tl*s3[1] - tu*s1[1] - tv*s2[1]); + shi->nmaptang[2]= (tl*s3[2] - tu*s1[2] - tv*s2[2]); if(obi->flag & R_TRANSFORMED) - normal_transform(obi->imat, shi->tang); + Mat3MulVecfl(obi->nmat, shi->nmaptang); - Normalize(shi->tang); + Normalize(shi->nmaptang); } - else shi->tang[0]= shi->tang[1]= shi->tang[2]= 0.0f; } } @@ -864,7 +913,7 @@ void shade_input_set_shade_texco(ShadeInput *shi) if(surfnor) { VECCOPY(shi->surfnor, surfnor) if(obi->flag & R_TRANSFORMED) - normal_transform(obi->imat, shi->surfnor); + Mat3MulVecfl(obi->nmat, shi->surfnor); } else VECCOPY(shi->surfnor, shi->vn) @@ -919,6 +968,8 @@ void shade_input_set_shade_texco(ShadeInput *shi) shi->dylo[2]= dl*o3[2]-shi->dy_u*o1[2]-shi->dy_v*o2[2]; } } + + VECCOPY(shi->duplilo, obi->dupliorco); } if(texco & TEXCO_GLOB) { @@ -933,7 +984,7 @@ void shade_input_set_shade_texco(ShadeInput *shi) } if(texco & TEXCO_STRAND) { - shi->strand= (l*v3->accum - u*v1->accum - v*v2->accum); + shi->strandco= (l*v3->accum - u*v1->accum - v*v2->accum); if(shi->osatex) { dl= shi->dx_u+shi->dx_v; shi->dxstrand= dl*v3->accum-shi->dx_u*v1->accum-shi->dx_v*v2->accum; @@ -975,9 +1026,9 @@ void shade_input_set_shade_texco(ShadeInput *shi) } if(shi->totcol) { - shi->vcol[0]= shi->col[0].col[0]; - shi->vcol[1]= shi->col[0].col[1]; - shi->vcol[2]= shi->col[0].col[2]; + shi->vcol[0]= shi->col[shi->actcol].col[0]; + shi->vcol[1]= shi->col[shi->actcol].col[1]; + shi->vcol[2]= shi->col[shi->actcol].col[2]; shi->vcol[3]= 1.0f; } else { @@ -1033,6 +1084,10 @@ void shade_input_set_shade_texco(ShadeInput *shi) } } + shi->dupliuv[0]= -1.0f + 2.0f*obi->dupliuv[0]; + shi->dupliuv[1]= -1.0f + 2.0f*obi->dupliuv[1]; + shi->dupliuv[2]= 0.0f; + if(shi->totuv == 0) { ShadeInputUV *suv= &shi->uv[0]; @@ -1097,6 +1152,7 @@ void shade_input_set_shade_texco(ShadeInput *shi) if((mode & MA_TANGENT_V)==0) { /* just prevent surprises */ shi->tang[0]= shi->tang[1]= shi->tang[2]= 0.0f; + shi->nmaptang[0]= shi->nmaptang[1]= shi->nmaptang[2]= 0.0f; } } } @@ -1181,7 +1237,9 @@ void shade_input_set_shade_texco(ShadeInput *shi) } } } - } + } /* else { + Note! For raytracing winco is not set, important because thus means all shader input's need to have their variables set to zero else in-initialized values are used + */ } /* ****************** ShadeSample ************************************** */ @@ -1201,8 +1259,9 @@ void shade_input_initialize(ShadeInput *shi, RenderPart *pa, RenderLayer *rl, in shi->combinedflag= ~rl->pass_xor; shi->mat_override= rl->mat_override; shi->light_override= rl->light_override; - +// shi->rl= rl; /* note shi.depth==0 means first hit, not raytracing */ + } /* initialize per part, not per pixel! */ @@ -1217,7 +1276,7 @@ void shade_sample_initialize(ShadeSample *ssamp, RenderPart *pa, RenderLayer *rl memset(&ssamp->shr[a], 0, sizeof(ShadeResult)); } - ssamp->samplenr= 0; /* counter, detect shadow-reuse for shaders */ + get_sample_layers(pa, rl, ssamp->rlpp); } /* Do AO or (future) GI */ @@ -1228,19 +1287,22 @@ void shade_samples_do_AO(ShadeSample *ssamp) if(!(R.r.mode & R_SHADOW)) return; - if(!(R.r.mode & R_RAYTRACE)) + if(!(R.r.mode & R_RAYTRACE) && !(R.wrld.ao_gather_method == WO_AOGATHER_APPROX)) return; - if(R.wrld.mode & WO_AMB_OCC) - if(ssamp->shi[0].passflag & (SCE_PASS_COMBINED|SCE_PASS_AO)) + if(R.wrld.mode & WO_AMB_OCC) { + shi= &ssamp->shi[0]; + + if(((shi->passflag & SCE_PASS_COMBINED) && (shi->combinedflag & SCE_PASS_AO)) + || (shi->passflag & SCE_PASS_AO)) for(sample=0, shi= ssamp->shi; sample<ssamp->tot; shi++, sample++) if(!(shi->mode & MA_SHLESS)) ambient_occlusion(shi); /* stores in shi->ao[] */ - + } } -static void shade_samples_fill_with_ps(ShadeSample *ssamp, PixStr *ps, int x, int y) +void shade_samples_fill_with_ps(ShadeSample *ssamp, PixStr *ps, int x, int y) { ShadeInput *shi; float xs, ys; @@ -1259,15 +1321,17 @@ static void shade_samples_fill_with_ps(ShadeSample *ssamp, PixStr *ps, int x, in for(samp=0; samp<R.osa; samp++) { if(curmask & (1<<samp)) { - xs= (float)x + R.jit[samp][0] + 0.5f; /* zbuffer has this inverse corrected, ensures xs,ys are inside pixel */ + /* zbuffer has this inverse corrected, ensures xs,ys are inside pixel */ + xs= (float)x + R.jit[samp][0] + 0.5f; ys= (float)y + R.jit[samp][1] + 0.5f; if(shi_cp) shade_input_copy_triangle(shi, shi-1); shi->mask= (1<<samp); - shi->samplenr= ssamp->samplenr++; - shade_input_set_viewco(shi, xs, ys, (float)ps->z); +// shi->rl= ssamp->rlpp[samp]; + shi->samplenr= R.shadowsamplenr[shi->thread]++; /* this counter is not being reset per pixel */ + shade_input_set_viewco(shi, x, y, xs, ys, (float)ps->z); shade_input_set_uv(shi); shade_input_set_normals(shi); @@ -1286,9 +1350,10 @@ static void shade_samples_fill_with_ps(ShadeSample *ssamp, PixStr *ps, int x, in xs= (float)x + 0.5f; ys= (float)y + 0.5f; } + shi->mask= curmask; - shi->samplenr= ssamp->samplenr++; - shade_input_set_viewco(shi, xs, ys, (float)ps->z); + shi->samplenr= R.shadowsamplenr[shi->thread]++; + shade_input_set_viewco(shi, x, y, xs, ys, (float)ps->z); shade_input_set_uv(shi); shade_input_set_normals(shi); shi++; diff --git a/source/blender/render/intern/source/shadeoutput.c b/source/blender/render/intern/source/shadeoutput.c index a08046da0da..4c627056c1d 100644 --- a/source/blender/render/intern/source/shadeoutput.c +++ b/source/blender/render/intern/source/shadeoutput.c @@ -43,6 +43,7 @@ #include "DNA_material_types.h" /* local include */ +#include "occlusion.h" #include "renderpipeline.h" #include "render_types.h" #include "pixelblending.h" @@ -354,7 +355,7 @@ void renderspothalo(ShadeInput *shi, float *col, float alpha) if(lar->type==LA_SPOT && (lar->mode & LA_HALO) && lar->haint>0) { if(lar->mode & LA_LAYER) - if(shi->vlr && (lar->lay & shi->vlr->lay)==0) + if(shi->vlr && (lar->lay & shi->obi->lay)==0) continue; if((lar->lay & shi->lay)==0) continue; @@ -428,10 +429,6 @@ static float area_lamp_energy(float (*area)[3], float *co, float *vn) double cross[4][3]; /* cross products of this */ double rad[4]; /* angles between vecs */ - /* extra test for dot */ - if ( INPR(co, vn) <= 0.0f) - return 0.0f; - VECSUB(vec[0], co, area[0]); VECSUB(vec[1], co, area[1]); VECSUB(vec[2], co, area[2]); @@ -480,8 +477,12 @@ static float area_lamp_energy_multisample(LampRen *lar, float *co, float *vn) float *jitlamp= lar->jitter, vec[3]; float area[4][3], intens= 0.0f; int a= lar->ray_totsamp; - - + + /* test if co is behind lamp */ + VECSUB(vec, co, lar->co); + if(INPR(vec, lar->vec) < 0.0f) + return 0.0f; + while(a--) { vec[0]= jitlamp[0]; vec[1]= jitlamp[1]; @@ -1006,8 +1007,9 @@ static void do_specular_ramp(ShadeInput *shi, float is, float t, float *spec) /* pure AO, check for raytrace and world should have been done */ void ambient_occlusion(ShadeInput *shi) { - - if((R.r.mode & R_RAYTRACE) && shi->mat->amb!=0.0f) + if((R.wrld.ao_gather_method == WO_AOGATHER_APPROX) && shi->mat->amb!=0.0f) + sample_occ(&R, shi); + else if((R.r.mode & R_RAYTRACE) && shi->mat->amb!=0.0f) ray_ao(shi, shi->ao); else shi->ao[0]= shi->ao[1]= shi->ao[2]= 1.0f; @@ -1017,25 +1019,28 @@ void ambient_occlusion(ShadeInput *shi) /* wrld mode was checked for */ void ambient_occlusion_to_diffuse(ShadeInput *shi, float *diff) { - - if((R.r.mode & R_RAYTRACE) && shi->mat->amb!=0.0f) { - float f= R.wrld.aoenergy*shi->mat->amb; - - if (R.wrld.aomix==WO_AOADDSUB) { - diff[0] = 2.0f*shi->ao[0]-1.0f; - diff[1] = 2.0f*shi->ao[1]-1.0f; - diff[2] = 2.0f*shi->ao[2]-1.0f; - } - else if (R.wrld.aomix==WO_AOSUB) { - diff[0] = shi->ao[0]-1.0f; - diff[1] = shi->ao[1]-1.0f; - diff[2] = shi->ao[2]-1.0f; - } - else { - VECCOPY(diff, shi->ao); + if((R.r.mode & R_RAYTRACE) || R.wrld.ao_gather_method == WO_AOGATHER_APPROX) { + if(shi->mat->amb!=0.0f) { + float f= R.wrld.aoenergy*shi->mat->amb; + + if (R.wrld.aomix==WO_AOADDSUB) { + diff[0] = 2.0f*shi->ao[0]-1.0f; + diff[1] = 2.0f*shi->ao[1]-1.0f; + diff[2] = 2.0f*shi->ao[2]-1.0f; + } + else if (R.wrld.aomix==WO_AOSUB) { + diff[0] = shi->ao[0]-1.0f; + diff[1] = shi->ao[1]-1.0f; + diff[2] = shi->ao[2]-1.0f; + } + else { + VECCOPY(diff, shi->ao); + } + + VECMUL(diff, f); } - - VECMUL(diff, f); + else + diff[0]= diff[1]= diff[2]= 0.0f; } else diff[0]= diff[1]= diff[2]= 0.0f; @@ -1054,7 +1059,7 @@ void lamp_get_shadow(LampRen *lar, ShadeInput *shi, float inp, float *shadfac, i if(lar->buftype==LA_SHADBUF_IRREGULAR) shadfac[3]= ISB_getshadow(shi, lar->shb); else - shadfac[3] = testshadowbuf(lar->shb, shi->co, shi->dxco, shi->dyco, inp); + shadfac[3] = testshadowbuf(&R, lar->shb, shi->co, shi->dxco, shi->dyco, inp, shi->mat->lbias); } else if(lar->mode & LA_SHAD_RAY) { ray_shadow(shi, lar, shadfac); @@ -1174,15 +1179,18 @@ static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int { Material *ma= shi->mat; VlakRen *vlr= shi->vlr; - float lv[3], lampdist, lacol[3], shadfac[4]; + float lv[3], lampdist, lacol[3], shadfac[4], lashdw[3]; float i, is, i_noshad, inp, *vn, *view, vnor[3], phongcorr=1.0f; float visifac; vn= shi->vn; view= shi->view; - if (lar->energy == 0.0) return; + if (lar->energy == 0.0) return; + /* only shadow lamps shouldn't affect shadow-less materials at all */ + if ((lar->mode & LA_ONLYSHADOW) && (!(ma->mode & MA_SHADOW) || !(R.r.mode & R_SHADOW))) + return; /* optimisation, don't render fully black lamps */ if (!(lar->mode & LA_TEXTURE) && (lar->r + lar->g + lar->b == 0.0f)) return; @@ -1211,7 +1219,12 @@ static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int lacol[1]= lar->g; lacol[2]= lar->b; - if(lar->mode & LA_TEXTURE) do_lamp_tex(lar, lv, shi, lacol); + lashdw[0]= lar->shdwr; + lashdw[1]= lar->shdwg; + lashdw[2]= lar->shdwb; + + if(lar->mode & LA_TEXTURE) do_lamp_tex(lar, lv, shi, lacol, LA_TEXTURE); + if(lar->mode & LA_SHAD_TEX) do_lamp_tex(lar, lv, shi, lashdw, LA_SHAD_TEX); /* tangent case; calculate fake face normal, aligned with lampvector */ /* note, vnor==vn is used as tangent trigger for buffer shadow */ @@ -1223,6 +1236,7 @@ static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int Crossf(nstrand, vn, cross); blend= INPR(nstrand, shi->surfnor); + blend= 1.0f - blend; CLAMP(blend, 0.0f, 1.0f); VecLerpf(vnor, nstrand, shi->surfnor, blend); @@ -1231,6 +1245,7 @@ static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int else { Crossf(cross, lv, vn); Crossf(vnor, cross, vn); + Normalize(vnor); } if(ma->strand_surfnor > 0.0f) { @@ -1248,6 +1263,7 @@ static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int float cross[3]; Crossf(cross, lv, shi->tang); Crossf(vnor, cross, shi->tang); + Normalize(vnor); vnor[0]= -vnor[0];vnor[1]= -vnor[1];vnor[2]= -vnor[2]; vn= vnor; } @@ -1297,7 +1313,10 @@ static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int else is= inp; // Lambert } - /* i is diffuse */ + /* 'is' is diffuse */ + if((ma->shade_flag & MA_CUBIC) && is>0.0f && is<1.0f) + is= 3.0*is*is - 2.0*is*is*is; // nicer termination of shades + i= is*phongcorr; if(i>0.0f) { @@ -1328,13 +1347,13 @@ static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int if((lar->mode & LA_ONLYSHADOW) && i>0.0) { shadfac[3]= i*lar->energy*(1.0f-shadfac[3]); - shr->shad[0] -= shadfac[3]*shi->r; - shr->shad[1] -= shadfac[3]*shi->g; - shr->shad[2] -= shadfac[3]*shi->b; + shr->shad[0] -= shadfac[3]*shi->r*(1.0f-lashdw[0]); + shr->shad[1] -= shadfac[3]*shi->g*(1.0f-lashdw[1]); + shr->shad[2] -= shadfac[3]*shi->b*(1.0f-lashdw[2]); - shr->spec[0] -= shadfac[3]*shi->specr; - shr->spec[1] -= shadfac[3]*shi->specg; - shr->spec[2] -= shadfac[3]*shi->specb; + shr->spec[0] -= shadfac[3]*shi->specr*(1.0f-lashdw[0]); + shr->spec[1] -= shadfac[3]*shi->specg*(1.0f-lashdw[1]); + shr->spec[2] -= shadfac[3]*shi->specb*(1.0f-lashdw[2]); return; } @@ -1352,6 +1371,10 @@ static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int else add_to_diffuse(shr->shad, shi, is, i*lacol[0], i*lacol[1], i*lacol[2]); } + /* add light for colored shadow */ + if (i_noshad>i && !(lashdw[0]==0 && lashdw[1]==0 && lashdw[2]==0)) { + add_to_diffuse(shr->shad, shi, is, lashdw[0]*(i_noshad-i)*lacol[0], lashdw[1]*(i_noshad-i)*lacol[1], lashdw[2]*(i_noshad-i)*lacol[2]); + } if(i_noshad>0.0f) { if(passflag & (SCE_PASS_DIFFUSE|SCE_PASS_SHADOW)) { if(ma->mode & MA_SHADOW_TRA) @@ -1365,6 +1388,8 @@ static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int } /* specularity */ + shadfac[3]*= phongcorr; /* note, shadfac not allowed to be stored nonlocal */ + if(shadfac[3]>0.0f && shi->spec!=0.0f && !(lar->mode & LA_NO_SPEC) && !(lar->mode & LA_ONLYSHADOW)) { if(!(passflag & (SCE_PASS_COMBINED|SCE_PASS_SPEC))); @@ -1435,10 +1460,10 @@ static void shade_lamp_loop_only_shadow(ShadeInput *shi, ShadeResult *shr) LampRen *lar; GroupObject *go; float inpr, lv[3]; - float *vn, *view, shadfac[4]; + float *view, shadfac[4]; float ir, accum, visifac, lampdist; - vn= shi->vn; + view= shi->view; accum= ir= 0.0f; @@ -1451,7 +1476,7 @@ static void shade_lamp_loop_only_shadow(ShadeInput *shi, ShadeResult *shr) /* yafray: ignore shading by photonlights, not used in Blender */ if (lar->type==LA_YF_PHOTON) continue; - if(lar->mode & LA_LAYER) if((lar->lay & shi->vlr->lay)==0) continue; + if(lar->mode & LA_LAYER) if((lar->lay & shi->obi->lay)==0) continue; if((lar->lay & shi->lay)==0) continue; if(lar->shb || (lar->mode & LA_SHAD_RAY)) { @@ -1501,8 +1526,10 @@ static void shade_lamp_loop_only_shadow(ShadeInput *shi, ShadeResult *shr) } } +/* let's map negative light as if it mirrors positive light, otherwise negative values disappear */ static void wrld_exposure_correct(float *diff) { + diff[0]= R.wrld.linfac*(1.0f-exp( diff[0]*R.wrld.logfac) ); diff[1]= R.wrld.linfac*(1.0f-exp( diff[1]*R.wrld.logfac) ); diff[2]= R.wrld.linfac*(1.0f-exp( diff[2]*R.wrld.logfac) ); @@ -1511,7 +1538,6 @@ static void wrld_exposure_correct(float *diff) void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr) { Material *ma= shi->mat; - VlakRen *vlr= shi->vlr; int passflag= shi->passflag; memset(shr, 0, sizeof(ShadeResult)); @@ -1541,6 +1567,24 @@ void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr) shr->col[1]= shi->g*shi->alpha; shr->col[2]= shi->b*shi->alpha; shr->col[3]= shi->alpha; + + if((ma->sss_flag & MA_DIFF_SSS) && !sss_pass_done(&R, ma)) { + if(ma->sss_texfac == 0.0f) { + shi->r= shi->g= shi->b= shi->alpha= 1.0f; + shr->col[0]= shr->col[1]= shr->col[2]= shr->col[3]= 1.0f; + } + else { + shi->r= pow(shi->r, ma->sss_texfac); + shi->g= pow(shi->g, ma->sss_texfac); + shi->b= pow(shi->b, ma->sss_texfac); + shi->alpha= pow(shi->alpha, ma->sss_texfac); + + shr->col[0]= pow(shr->col[0], ma->sss_texfac); + shr->col[1]= pow(shr->col[1], ma->sss_texfac); + shr->col[2]= pow(shr->col[2], ma->sss_texfac); + shr->col[3]= pow(shr->col[3], ma->sss_texfac); + } + } } if(ma->mode & MA_SHLESS) { @@ -1565,7 +1609,8 @@ void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr) /* AO pass */ if(R.wrld.mode & WO_AMB_OCC) { - if(passflag & (SCE_PASS_COMBINED|SCE_PASS_AO)) { + if(((passflag & SCE_PASS_COMBINED) && (shi->combinedflag & SCE_PASS_AO)) + || (passflag & SCE_PASS_AO)) { /* AO was calculated for scanline already */ if(shi->depth) ambient_occlusion(shi); @@ -1588,7 +1633,7 @@ void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr) if (lar->type==LA_YF_PHOTON) continue; /* test for lamp layer */ - if(lar->mode & LA_LAYER) if((lar->lay & vlr->lay)==0) continue; + if(lar->mode & LA_LAYER) if((lar->lay & shi->obi->lay)==0) continue; if((lar->lay & shi->lay)==0) continue; /* accumulates in shr->diff and shr->spec and shr->shad (diffuse with shadow!) */ @@ -1606,20 +1651,26 @@ void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr) if (shr->shad[2] < 0) shr->shad[2] = 0; if(ma->sss_flag & MA_DIFF_SSS) { - float sss[3], col[3], texfac= ma->sss_texfac; + float sss[3], col[3], invalpha, texfac= ma->sss_texfac; /* this will return false in the preprocess stage */ if(sample_sss(&R, ma, shi->co, sss)) { + invalpha= (shr->col[3] > FLT_EPSILON)? 1.0f/shr->col[3]: 1.0f; + if(texfac==0.0f) { VECCOPY(col, shr->col); + VecMulf(col, invalpha); } else if(texfac==1.0f) { col[0]= col[1]= col[2]= 1.0f; + VecMulf(col, invalpha); } else { - col[0]= pow(shr->col[0], 1.0f-texfac); - col[1]= pow(shr->col[1], 1.0f-texfac); - col[2]= pow(shr->col[2], 1.0f-texfac); + VECCOPY(col, shr->col); + VecMulf(col, invalpha); + col[0]= pow(col[0], 1.0f-texfac); + col[1]= pow(col[1], 1.0f-texfac); + col[2]= pow(col[2], 1.0f-texfac); } shr->diff[0]= sss[0]*col[0]; @@ -1627,9 +1678,9 @@ void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr) shr->diff[2]= sss[2]*col[2]; if(shi->combinedflag & SCE_PASS_SHADOW) { - shr->shad[0]= sss[0]*col[0]; - shr->shad[1]= sss[1]*col[1]; - shr->shad[2]= sss[2]*col[2]; + shr->shad[0]= shr->diff[0]; + shr->shad[1]= shr->diff[1]; + shr->shad[2]= shr->diff[2]; } } } @@ -1647,7 +1698,7 @@ void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr) } /* exposure correction */ - if(R.wrld.exp!=0.0f || R.wrld.range!=1.0f) { + if((R.wrld.exp!=0.0f || R.wrld.range!=1.0f) && !R.sss_points) { wrld_exposure_correct(shr->combined); /* has no spec! */ wrld_exposure_correct(shr->spec); } @@ -1671,10 +1722,16 @@ void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr) shr->alpha= shi->alpha; /* from now stuff everything in shr->combined: ambient, AO, radio, ramps, exposure */ - if(!(ma->sss_flag & MA_DIFF_SSS) || !has_sss_tree(&R, ma)) { - shr->combined[0]+= shi->ambr + shi->r*shi->amb*shi->rad[0]; - shr->combined[1]+= shi->ambg + shi->g*shi->amb*shi->rad[1]; - shr->combined[2]+= shi->ambb + shi->b*shi->amb*shi->rad[2]; + if(!(ma->sss_flag & MA_DIFF_SSS) || !sss_pass_done(&R, ma)) { + shr->combined[0]+= shi->ambr; + shr->combined[1]+= shi->ambg; + shr->combined[2]+= shi->ambb; + + if(shi->combinedflag & SCE_PASS_RADIO) { + shr->combined[0]+= shi->r*shi->amb*shi->rad[0]; + shr->combined[1]+= shi->g*shi->amb*shi->rad[1]; + shr->combined[2]+= shi->b*shi->amb*shi->rad[2]; + } /* add AO in combined? */ if(R.wrld.mode & WO_AMB_OCC) { @@ -1713,7 +1770,21 @@ void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr) if(shi->combinedflag & SCE_PASS_SPEC) VECADD(shr->combined, shr->combined, shr->spec); + /* modulate by the object color */ + if((ma->shade_flag & MA_OBCOLOR) && shi->obr->ob) { + if(!(ma->sss_flag & MA_DIFF_SSS) || !sss_pass_done(&R, ma)) { + float obcol[4]; + + QUATCOPY(obcol, shi->obr->ob->col); + CLAMP(obcol[3], 0.0f, 1.0f); + + shr->combined[0] *= obcol[0]; + shr->combined[1] *= obcol[1]; + shr->combined[2] *= obcol[2]; + shr->alpha *= obcol[3]; + } + } + shr->combined[3]= shr->alpha; } - diff --git a/source/blender/render/intern/source/sss.c b/source/blender/render/intern/source/sss.c index 09a3b9ef66d..9bde6675798 100644 --- a/source/blender/render/intern/source/sss.c +++ b/source/blender/render/intern/source/sss.c @@ -59,6 +59,7 @@ #include "BKE_main.h" #include "BKE_material.h" #include "BKE_node.h" +#include "BKE_scene.h" #include "BKE_utildefines.h" /* this module */ @@ -450,13 +451,13 @@ static void compute_radiance(ScatterTree *tree, float *co, float *rad) VECCOPY(rdsum, result.rdsum); VECADD(backrdsum, result.rdsum, result.backrdsum); - if(rdsum[0] > 0.0f) rad[0]= tree->ss[0]->color*rad[0]/rdsum[0]; - if(rdsum[1] > 0.0f) rad[1]= tree->ss[1]->color*rad[1]/rdsum[1]; - if(rdsum[2] > 0.0f) rad[2]= tree->ss[2]->color*rad[2]/rdsum[2]; + if(rdsum[0] > 1e-16f) rad[0]= tree->ss[0]->color*rad[0]/rdsum[0]; + if(rdsum[1] > 1e-16f) rad[1]= tree->ss[1]->color*rad[1]/rdsum[1]; + if(rdsum[2] > 1e-16f) rad[2]= tree->ss[2]->color*rad[2]/rdsum[2]; - if(backrdsum[0] > 0.0f) backrad[0]= tree->ss[0]->color*backrad[0]/backrdsum[0]; - if(backrdsum[1] > 0.0f) backrad[1]= tree->ss[1]->color*backrad[1]/backrdsum[1]; - if(backrdsum[2] > 0.0f) backrad[2]= tree->ss[2]->color*backrad[2]/backrdsum[2]; + if(backrdsum[0] > 1e-16f) backrad[0]= tree->ss[0]->color*backrad[0]/backrdsum[0]; + if(backrdsum[1] > 1e-16f) backrad[1]= tree->ss[1]->color*backrad[1]/backrdsum[1]; + if(backrdsum[2] > 1e-16f) backrad[2]= tree->ss[2]->color*backrad[2]/backrdsum[2]; rad[0]= MAX2(rad[0], backrad[0]); rad[1]= MAX2(rad[1], backrad[1]); @@ -503,20 +504,20 @@ static void sum_leaf_radiance(ScatterTree *tree, ScatterNode *node) } } - if(node->area > 0) { + if(node->area > 1e-16f) { inv= 1.0/node->area; node->rad[0] *= inv; node->rad[1] *= inv; node->rad[2] *= inv; } - if(node->backarea > 0) { + if(node->backarea > 1e-16f) { inv= 1.0/node->backarea; node->backrad[0] *= inv; node->backrad[1] *= inv; node->backrad[2] *= inv; } - if(totrad > 0.0f) { + if(totrad > 1e-16f) { inv= 1.0/totrad; node->co[0] *= inv; node->co[1] *= inv; @@ -577,20 +578,20 @@ static void sum_branch_radiance(ScatterTree *tree, ScatterNode *node) node->backarea += subnode->backarea; } - if(node->area > 0) { + if(node->area > 1e-16f) { inv= 1.0/node->area; node->rad[0] *= inv; node->rad[1] *= inv; node->rad[2] *= inv; } - if(node->backarea > 0) { + if(node->backarea > 1e-16f) { inv= 1.0/node->backarea; node->backrad[0] *= inv; node->backrad[1] *= inv; node->backrad[2] *= inv; } - if(totrad > 0.0f) { + if(totrad > 1e-16f) { inv= 1.0/totrad; node->co[0] *= inv; node->co[1] *= inv; @@ -847,7 +848,7 @@ static void sss_create_tree_mat(Render *re, Material *mat) { SSSPoints *p; RenderResult *rr; - ListBase layers, points; + ListBase points; float (*co)[3] = NULL, (*color)[3] = NULL, *area = NULL; int totpoint = 0, osa, osaflag, partsdone; @@ -860,13 +861,11 @@ static void sss_create_tree_mat(Render *re, Material *mat) setting them back, maybe we need to create our own Render? */ /* do SSS preprocessing render */ - layers= re->r.layers; + rr= re->result; osa= re->osa; osaflag= re->r.mode & R_OSA; partsdone= re->i.partsdone; - rr= re->result; - re->r.layers.first= re->r.layers.last= NULL; re->osa= 0; re->r.mode &= ~R_OSA; re->sss_points= &points; @@ -881,7 +880,6 @@ static void sss_create_tree_mat(Render *re, Material *mat) re->i.partsdone= partsdone; re->sss_mat= NULL; re->sss_points= NULL; - re->r.layers= layers; re->osa= osa; if (osaflag) re->r.mode |= R_OSA; @@ -921,7 +919,8 @@ static void sss_create_tree_mat(Render *re, Material *mat) float *col= mat->sss_col, *radius= mat->sss_radius; float fw= mat->sss_front, bw= mat->sss_back; float error = mat->sss_error; - + + error= get_render_aosss_error(&re->r, error); if((re->r.scemode & R_PREVIEWBUTS) && error < 0.5f) error= 0.5f; @@ -985,7 +984,7 @@ void make_sss_tree(Render *re) re->stats_draw(&re->i); for(mat= G.main->mat.first; mat; mat= mat->id.next) - if(mat->id.us && (mat->sss_flag & MA_DIFF_SSS)) + if(mat->id.us && (mat->flag & MA_IS_USED) && (mat->sss_flag & MA_DIFF_SSS)) sss_create_tree_mat(re, mat); } @@ -1024,8 +1023,8 @@ int sample_sss(Render *re, Material *mat, float *co, float *color) return 0; } -int has_sss_tree(struct Render *re, struct Material *mat) +int sss_pass_done(struct Render *re, struct Material *mat) { - return (re->sss_hash && BLI_ghash_lookup(re->sss_hash, mat)); + return ((re->flag & R_BAKING) || !(re->r.mode & R_SSS) || (re->sss_hash && BLI_ghash_lookup(re->sss_hash, mat))); } diff --git a/source/blender/render/intern/source/strand.c b/source/blender/render/intern/source/strand.c index 4f0e9764a43..05e36160f0e 100644 --- a/source/blender/render/intern/source/strand.c +++ b/source/blender/render/intern/source/strand.c @@ -35,12 +35,14 @@ #include "DNA_key_types.h" #include "DNA_material_types.h" +#include "DNA_meshdata_types.h" #include "BLI_arithb.h" #include "BLI_blenlib.h" #include "BLI_ghash.h" #include "BLI_memarena.h" +#include "BKE_DerivedMesh.h" #include "BKE_key.h" #include "BKE_utildefines.h" @@ -55,248 +57,9 @@ #include "zbuf.h" /* to be removed */ -void merge_transp_passes(RenderLayer *rl, ShadeResult *shr); -void add_transp_passes(RenderLayer *rl, int offset, ShadeResult *shr, float alpha); void hoco_to_zco(ZSpan *zspan, float *zco, float *hoco); void zspan_scanconvert_strand(ZSpan *zspan, void *handle, float *v1, float *v2, float *v3, void (*func)(void *, int, int, float, float, float) ); -void zbufsinglewire(ZSpan *zspan, ObjectRen *obr, int zvlnr, float *ho1, float *ho2); -int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassflag); -void add_transp_speed(RenderLayer *rl, int offset, float *speed, float alpha, long *rdrect); -void reset_sky_speedvectors(RenderPart *pa, RenderLayer *rl, float *rectf); - -/* *************** */ - -#define BUCKETPRIMS_SIZE 256 - -typedef struct BucketPrims { - struct BucketPrims *next, *prev; - void *prim[BUCKETPRIMS_SIZE]; - int totprim; -} BucketPrims; - -typedef struct RenderBuckets { - ListBase all; - ListBase *inside; - ListBase *overlap; - int x, y; - float insize[2]; - float zmulx, zmuly, zofsx, zofsy; -} RenderBuckets; - -static void add_bucket_prim(ListBase *lb, void *prim) -{ - BucketPrims *bpr= lb->last; - - if(!bpr || bpr->totprim == BUCKETPRIMS_SIZE) { - bpr= MEM_callocN(sizeof(BucketPrims), "BucketPrims"); - BLI_addtail(lb, bpr); - } - - bpr->prim[bpr->totprim++]= prim; -} - -RenderBuckets *init_buckets(Render *re) -{ - RenderBuckets *buckets; - RenderPart *pa; - float scalex, scaley, cropx, cropy; - int x, y, tempparts= 0; - - buckets= MEM_callocN(sizeof(RenderBuckets), "RenderBuckets"); - - if(!re->parts.first) { - initparts(re); - tempparts= 1; - } - - pa= re->parts.first; - if(!pa) - return buckets; - - x= re->xparts+1; - y= re->yparts+1; - buckets->x= x; - buckets->y= y; - - scalex= (2.0f - re->xparts*re->partx/(float)re->winx); - scaley= (2.0f - re->yparts*re->party/(float)re->winy); - - cropx= pa->crop/(float)re->partx; - cropy= pa->crop/(float)re->party; - - buckets->insize[0]= 1.0f - 2.0f*cropx; - buckets->insize[1]= 1.0f - 2.0f*cropy; - - buckets->zmulx= re->xparts*scalex; - buckets->zmuly= re->yparts*scaley; - buckets->zofsx= scalex*(1.0f - cropx); - buckets->zofsy= scaley*(1.0f - cropy); - - buckets->inside= MEM_callocN(sizeof(ListBase)*x*y, "BucketPrimsInside"); - buckets->overlap= MEM_callocN(sizeof(ListBase)*x*y, "BucketPrimsOverlap"); - - if(tempparts) - freeparts(re); - - return buckets; -} - -void add_buckets_primitive(RenderBuckets *buckets, float *min, float *max, void *prim) -{ - float end[3]; - int x, y, a; - - x= (int)min[0]; - y= (int)min[1]; - - if(x >= 0 && x < buckets->x && y >= 0 && y < buckets->y) { - a= y*buckets->x + x; - - end[0]= x + buckets->insize[0]; - end[1]= y + buckets->insize[1]; - - if(max[0] <= end[0] && max[1] <= end[1]) { - add_bucket_prim(&buckets->inside[a], prim); - return; - } - else { - end[0]= x + 2; - end[1]= y + 2; - - if(max[0] <= end[0] && max[1] <= end[1]) { - add_bucket_prim(&buckets->overlap[a], prim); - return; - } - } - } - - add_bucket_prim(&buckets->all, prim); -} - -void free_buckets(RenderBuckets *buckets) -{ - int a, size; - - BLI_freelistN(&buckets->all); - - size= buckets->x*buckets->y; - for(a=0; a<size; a++) { - BLI_freelistN(&buckets->inside[a]); - BLI_freelistN(&buckets->overlap[a]); - } - - if(buckets->inside) - MEM_freeN(buckets->inside); - if(buckets->overlap) - MEM_freeN(buckets->overlap); - - MEM_freeN(buckets); -} - -void project_hoco_to_bucket(RenderBuckets *buckets, float *hoco, float *bucketco) -{ - float div; - - div= 1.0f/hoco[3]; - bucketco[0]= buckets->zmulx*(0.5 + 0.5f*hoco[0]*div) + buckets->zofsx; - bucketco[1]= buckets->zmuly*(0.5 + 0.5f*hoco[1]*div) + buckets->zofsy; -} - -typedef struct RenderPrimitiveIterator { - Render *re; - RenderBuckets *buckets; - ListBase *list[6]; - int listindex, totlist; - BucketPrims *bpr; - int bprindex; - - ObjectInstanceRen *obi; - StrandRen *strand; - int index, tot; -} RenderPrimitiveIterator; - -RenderPrimitiveIterator *init_primitive_iterator(Render *re, RenderBuckets *buckets, RenderPart *pa) -{ - RenderPrimitiveIterator *iter; - int nr, x, y, width; - - iter= MEM_callocN(sizeof(RenderPrimitiveIterator), "RenderPrimitiveIterator"); - iter->re= re; - - if(buckets) { - iter->buckets= buckets; - - nr= BLI_findindex(&re->parts, pa); - width= buckets->x - 1; - x= (nr % width) + 1; - y= (nr / width) + 1; - - iter->list[iter->totlist++]= &buckets->all; - iter->list[iter->totlist++]= &buckets->inside[y*buckets->x + x]; - iter->list[iter->totlist++]= &buckets->overlap[y*buckets->x + x]; - iter->list[iter->totlist++]= &buckets->overlap[y*buckets->x + (x-1)]; - iter->list[iter->totlist++]= &buckets->overlap[(y-1)*buckets->x + (x-1)]; - iter->list[iter->totlist++]= &buckets->overlap[(y-1)*buckets->x + x]; - } - else { - iter->index= 0; - iter->obi= re->instancetable.first; - if(iter->obi) - iter->tot= iter->obi->obr->totstrand; - } - - return iter; -} - -void *next_primitive_iterator(RenderPrimitiveIterator *iter) -{ - if(iter->buckets) { - if(iter->bpr && iter->bprindex >= iter->bpr->totprim) { - iter->bpr= iter->bpr->next; - iter->bprindex= 0; - } - - while(iter->bpr == NULL) { - if(iter->listindex == iter->totlist) - return NULL; - - iter->bpr= iter->list[iter->listindex++]->first; - iter->bprindex= 0; - } - - return iter->bpr->prim[iter->bprindex++]; - } - else { - if(!iter->obi) - return NULL; - - if(iter->index >= iter->tot) { - while((iter->obi=iter->obi->next) && !iter->obi->obr->totstrand) - iter->obi= iter->obi->next; - - if(iter->obi) - iter->tot= iter->obi->obr->totstrand; - else - return NULL; - } - - if(iter->index < iter->tot) { - if((iter->index & 255)==0) - iter->strand= iter->obi->obr->strandnodes[iter->index>>8].strand; - else - iter->strand++; - - return iter->strand; - } - else - return NULL; - } -} - -void free_primitive_iterator(RenderPrimitiveIterator *iter) -{ - MEM_freeN(iter); -} +void zbufsinglewire(ZSpan *zspan, int obi, int zvlnr, float *ho1, float *ho2); /* *************** */ @@ -388,9 +151,9 @@ void strand_eval_point(StrandSegment *sseg, StrandPoint *spoint) Crossf(cross, spoint->co, spoint->tan); w= spoint->co[2]*strandbuf->winmat[2][3] + strandbuf->winmat[3][3]; - dx= strandbuf->winx*cross[0]*strandbuf->winmat[0][0]; - dy= strandbuf->winy*cross[1]*strandbuf->winmat[1][1]; - w= sqrt(dx*dx + dy*dy)/w; + dx= strandbuf->winx*cross[0]*strandbuf->winmat[0][0]/w; + dy= strandbuf->winy*cross[1]*strandbuf->winmat[1][1]/w; + w= sqrt(dx*dx + dy*dy); if(w > 0.0f) { if(strandbuf->flag & R_STRAND_B_UNITS) { @@ -420,42 +183,9 @@ void strand_eval_point(StrandSegment *sseg, StrandPoint *spoint) /* *************** */ -typedef struct StrandPart { - Render *re; - ZSpan *zspan; - - RenderLayer *rl; - ShadeResult *result; - float *pass; - int *rectz, *outrectz; - unsigned short *mask; - int rectx, recty; - int addpassflag, addzbuf, sample; - - StrandSegment *segment; - GHash *hash; - StrandPoint point1, point2; - ShadeSample ssamp1, ssamp2, ssamp; - float t[3]; -} StrandPart; - -typedef struct StrandSortSegment { - struct StrandSortSegment *next; - int obi, strand, segment; - float z; -} StrandSortSegment; - -static int compare_strand_segment(const void *poin1, const void *poin2) +static void interpolate_vec1(float *v1, float *v2, float t, float negt, float *v) { - const StrandSortSegment *seg1= (const StrandSortSegment*)poin1; - const StrandSortSegment *seg2= (const StrandSortSegment*)poin2; - - if(seg1->z < seg2->z) - return -1; - else if(seg1->z == seg2->z) - return 0; - else - return 1; + v[0]= negt*v1[0] + t*v2[0]; } static void interpolate_vec3(float *v1, float *v2, float t, float negt, float *v) @@ -473,7 +203,7 @@ static void interpolate_vec4(float *v1, float *v2, float t, float negt, float *v v[3]= negt*v1[3] + t*v2[3]; } -static void interpolate_shade_result(ShadeResult *shr1, ShadeResult *shr2, float t, ShadeResult *shr, int addpassflag) +void interpolate_shade_result(ShadeResult *shr1, ShadeResult *shr2, float t, ShadeResult *shr, int addpassflag) { float negt= 1.0f - t; @@ -484,6 +214,8 @@ static void interpolate_shade_result(ShadeResult *shr1, ShadeResult *shr2, float } /* optim... */ if(addpassflag & ~(SCE_PASS_VECTOR)) { + if(addpassflag & SCE_PASS_Z) + interpolate_vec1(&shr1->z, &shr2->z, t, negt, &shr->z); if(addpassflag & SCE_PASS_RGBA) interpolate_vec4(shr1->col, shr2->col, t, negt, shr->col); if(addpassflag & SCE_PASS_NORMAL) { @@ -504,142 +236,336 @@ static void interpolate_shade_result(ShadeResult *shr1, ShadeResult *shr2, float interpolate_vec3(shr1->refr, shr2->refr, t, negt, shr->refr); if(addpassflag & SCE_PASS_RADIO) interpolate_vec3(shr1->rad, shr2->rad, t, negt, shr->rad); + if(addpassflag & SCE_PASS_MIST) + interpolate_vec1(&shr1->mist, &shr2->mist, t, negt, &shr->mist); } } -static void add_strand_obindex(RenderLayer *rl, int offset, ObjectRen *obr) +void strand_apply_shaderesult_alpha(ShadeResult *shr, float alpha) { - RenderPass *rpass; - - for(rpass= rl->passes.first; rpass; rpass= rpass->next) { - if(rpass->passtype == SCE_PASS_INDEXOB) { - float *fp= rpass->rect + offset; - *fp= (float)obr->ob->index; - break; - } + if(alpha < 1.0f) { + shr->combined[0] *= alpha; + shr->combined[1] *= alpha; + shr->combined[2] *= alpha; + shr->combined[3] *= alpha; + + shr->col[0] *= alpha; + shr->col[1] *= alpha; + shr->col[2] *= alpha; + shr->col[3] *= alpha; + + shr->alpha *= alpha; } } -static void do_strand_point_project(float winmat[][4], ZSpan *zspan, float *co, float *hoco, float *zco) +void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint) { - projectvert(co, winmat, hoco); - hoco_to_zco(zspan, zco, hoco); + ShadeInput *shi= ssamp->shi; + ShadeResult *shr= ssamp->shr; + VlakRen vlr; + + memset(&vlr, 0, sizeof(vlr)); + vlr.flag= R_SMOOTH; + if(sseg->buffer->ma->mode & MA_TANGENT_STR) + vlr.flag |= R_TANGENT; + + shi->vlr= &vlr; + shi->strand= sseg->strand; + shi->obi= sseg->obi; + shi->obr= sseg->obi->obr; + + /* cache for shadow */ + shi->samplenr= re->shadowsamplenr[shi->thread]++; + + shade_input_set_strand(shi, sseg->strand, spoint); + shade_input_set_strand_texco(shi, sseg->strand, sseg->v[1], spoint); + + /* init material vars */ + // note, keep this synced with render_types.h + memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); + shi->har= shi->mat->har; + + /* shade */ + shade_samples_do_AO(ssamp); + shade_input_do_shade(shi, shr); + + /* apply simplification */ + strand_apply_shaderesult_alpha(shr, spoint->alpha); + + /* include lamphalos for strand, since halo layer was added already */ + if(re->flag & R_LAMPHALO) + if(shi->layflag & SCE_LAY_HALO) + renderspothalo(shi, shr->combined, shr->combined[3]); + + shi->strand= NULL; } -static void strand_project_point(float winmat[][4], float winx, float winy, StrandPoint *spoint) -{ - float div; +/* *************** */ - projectvert(spoint->co, winmat, spoint->hoco); +struct StrandShadeCache { + GHash *resulthash; + GHash *refcounthash; + MemArena *memarena; +}; - div= 1.0f/spoint->hoco[3]; - spoint->x= spoint->hoco[0]*div*winx*0.5f; - spoint->y= spoint->hoco[1]*div*winy*0.5f; +StrandShadeCache *strand_shade_cache_create() +{ + StrandShadeCache *cache; + + cache= MEM_callocN(sizeof(StrandShadeCache), "StrandShadeCache"); + cache->resulthash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + cache->refcounthash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + cache->memarena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); + + return cache; } -#include "BLI_rand.h" -static void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint); +void strand_shade_cache_free(StrandShadeCache *cache) +{ + BLI_ghash_free(cache->refcounthash, NULL, NULL); + BLI_ghash_free(cache->resulthash, NULL, (GHashValFreeFP)MEM_freeN); + BLI_memarena_free(cache->memarena); + MEM_freeN(cache); +} -static void strand_shade_get(StrandPart *spart, int lookup, ShadeSample *ssamp, StrandPoint *spoint, StrandVert *svert, StrandSegment *sseg) +static void strand_shade_get(Render *re, StrandShadeCache *cache, ShadeSample *ssamp, StrandSegment *sseg, StrandVert *svert) { ShadeResult *hashshr; + StrandPoint p; + int *refcount; - if(lookup) { - hashshr= BLI_ghash_lookup(spart->hash, svert); + hashshr= BLI_ghash_lookup(cache->resulthash, svert); + refcount= BLI_ghash_lookup(cache->refcounthash, svert); - if(!hashshr) { - strand_shade_point(spart->re, ssamp, sseg, spoint); + if(!hashshr) { + /* not shaded yet, shade and insert into hash */ + p.t= (sseg->v[1] == svert)? 0.0f: 1.0f; + strand_eval_point(sseg, &p); + strand_shade_point(re, ssamp, sseg, &p); - hashshr= MEM_callocN(sizeof(ShadeResult), "HashShadeResult"); - *hashshr= ssamp->shr[0]; - BLI_ghash_insert(spart->hash, svert, hashshr); - } - else { - ssamp->shr[0]= *hashshr; - BLI_ghash_remove(spart->hash, svert, NULL, (GHashValFreeFP)MEM_freeN); - } + hashshr= MEM_callocN(sizeof(ShadeResult), "HashShadeResult"); + *hashshr= ssamp->shr[0]; + BLI_ghash_insert(cache->resulthash, svert, hashshr); } else - strand_shade_point(spart->re, ssamp, sseg, spoint); + /* already shaded, just copy previous result from hash */ + ssamp->shr[0]= *hashshr; + + /* lower reference count and remove if not needed anymore by any samples */ + (*refcount)--; + if(*refcount == 0) { + BLI_ghash_remove(cache->resulthash, svert, NULL, (GHashValFreeFP)MEM_freeN); + BLI_ghash_remove(cache->refcounthash, svert, NULL, NULL); + } } -static void strand_shade_segment(StrandPart *spart) +void strand_shade_segment(Render *re, StrandShadeCache *cache, StrandSegment *sseg, ShadeSample *ssamp, float t, float s, int addpassflag) { - StrandSegment *sseg= spart->segment; - int first, last; + ShadeResult shr1, shr2; - if(!sseg->shaded) { - first= (sseg->v[1] == &sseg->strand->vert[0]); - last= (sseg->v[2] == &sseg->strand->vert[sseg->strand->totvert-1]); + /* get shading for two endpoints and interpolate */ + strand_shade_get(re, cache, ssamp, sseg, sseg->v[1]); + shr1= ssamp->shr[0]; + strand_shade_get(re, cache, ssamp, sseg, sseg->v[2]); + shr2= ssamp->shr[0]; - strand_shade_get(spart, !first, &spart->ssamp1, &sseg->point1, sseg->v[1], sseg); - strand_shade_get(spart, !last, &spart->ssamp2, &sseg->point2, sseg->v[2], sseg); - sseg->shaded= 1; + interpolate_shade_result(&shr1, &shr2, t, ssamp->shr, addpassflag); + + /* apply alpha along width */ + if(sseg->buffer->widthfade != 0.0f) { + s = 1.0f - pow(fabs(s), sseg->buffer->widthfade); + + strand_apply_shaderesult_alpha(ssamp->shr, s); } +} -#if 0 - float c[3]; - - c[0]= BLI_frand(); - c[1]= BLI_frand(); - c[2]= BLI_frand(); +void strand_shade_unref(StrandShadeCache *cache, StrandVert *svert) +{ + int *refcount; - spart->ssamp1.shr[0].combined[0] *= c[0]; - spart->ssamp1.shr[0].combined[1] *= c[1]; - spart->ssamp1.shr[0].combined[2] *= c[2]; + /* lower reference count and remove if not needed anymore by any samples */ + refcount= BLI_ghash_lookup(cache->refcounthash, svert); - spart->ssamp2.shr[0].combined[0] *= c[0]; - spart->ssamp2.shr[0].combined[1] *= c[1]; - spart->ssamp2.shr[0].combined[2] *= c[2]; -#endif + (*refcount)--; + if(*refcount == 0) { + BLI_ghash_remove(cache->resulthash, svert, NULL, (GHashValFreeFP)MEM_freeN); + BLI_ghash_remove(cache->refcounthash, svert, NULL, NULL); + } } -static void do_strand_blend(void *handle, int x, int y, float u, float v, float z) +static void strand_shade_refcount(StrandShadeCache *cache, StrandVert *svert) { - StrandPart *spart= (StrandPart*)handle; - StrandBuffer *buffer= spart->segment->buffer; - ShadeResult *shr; - float /**pass,*/ t; - int offset, zverg; + int *refcount= BLI_ghash_lookup(cache->refcounthash, svert); - /* check again solid z-buffer */ - offset = y*spart->rectx + x; - zverg= (int)z; + if(!refcount) { + refcount= BLI_memarena_alloc(cache->memarena, sizeof(int)); + *refcount= 1; + BLI_ghash_insert(cache->refcounthash, svert, refcount); + } + else + (*refcount)++; +} - if(zverg < spart->rectz[offset]) { - /* fill in output z-buffer if needed */ - if(spart->addzbuf) - if(zverg < spart->outrectz[offset]) - spart->outrectz[offset]= zverg; +/* *************** */ - /* check alpha limit */ - shr= spart->result + offset*(spart->re->osa? spart->re->osa: 1); - if(shr[spart->sample].combined[3]>0.999f) - return; +typedef struct StrandPart { + Render *re; + ZSpan *zspan; - /* shade points if not shaded yet */ - strand_shade_segment(spart); + APixstrand *apixbuf; + int *totapixbuf; + int *rectz; + int *rectmask; + intptr_t *rectdaps; + int rectx, recty; + int sample; - /* interpolate shading from two control points */ - t = u*spart->t[0] + v*spart->t[1] + (1.0f-u-v)*spart->t[2]; - interpolate_shade_result(spart->ssamp1.shr, spart->ssamp2.shr, t, - spart->ssamp.shr, spart->addpassflag); + StrandSegment *segment; + float t[3], s[3]; - /* add in shaderesult array for part */ - spart->ssamp.shi[0].mask= (1<<spart->sample); - addtosamp_shr(shr, &spart->ssamp, spart->addpassflag); - spart->mask[offset] |= (1<<spart->sample); + StrandShadeCache *cache; +} StrandPart; -#if 0 - /* fill in pass for preview */ - if(spart->sample == 0) { - pass= spart->pass + offset*4; - QUATCOPY(pass, shr->combined); +typedef struct StrandSortSegment { + struct StrandSortSegment *next; + int obi, strand, segment; + float z; +} StrandSortSegment; + +static int compare_strand_segment(const void *poin1, const void *poin2) +{ + const StrandSortSegment *seg1= (const StrandSortSegment*)poin1; + const StrandSortSegment *seg2= (const StrandSortSegment*)poin2; + + if(seg1->z < seg2->z) + return -1; + else if(seg1->z == seg2->z) + return 0; + else + return 1; +} + +static void do_strand_point_project(float winmat[][4], ZSpan *zspan, float *co, float *hoco, float *zco) +{ + projectvert(co, winmat, hoco); + hoco_to_zco(zspan, zco, hoco); +} + +static void strand_project_point(float winmat[][4], float winx, float winy, StrandPoint *spoint) +{ + float div; + + projectvert(spoint->co, winmat, spoint->hoco); + + div= 1.0f/spoint->hoco[3]; + spoint->x= spoint->hoco[0]*div*winx*0.5f; + spoint->y= spoint->hoco[1]*div*winy*0.5f; +} + +static APixstrand *addpsmainAstrand(ListBase *lb) +{ + APixstrMain *psm; + + psm= MEM_mallocN(sizeof(APixstrMain), "addpsmainA"); + BLI_addtail(lb, psm); + psm->ps= MEM_callocN(4096*sizeof(APixstrand),"pixstr"); + + return psm->ps; +} + +static APixstrand *addpsAstrand(ZSpan *zspan) +{ + /* make new PS */ + if(zspan->apstrandmcounter==0) { + zspan->curpstrand= addpsmainAstrand(zspan->apsmbase); + zspan->apstrandmcounter= 4095; + } + else { + zspan->curpstrand++; + zspan->apstrandmcounter--; + } + return zspan->curpstrand; +} + +#define MAX_ZROW 2000 + +static void do_strand_fillac(void *handle, int x, int y, float u, float v, float z) +{ + StrandPart *spart= (StrandPart*)handle; + StrandShadeCache *cache= spart->cache; + StrandSegment *sseg= spart->segment; + APixstrand *apn, *apnew; + float t, s; + int offset, mask, obi, strnr, seg, zverg, bufferz, maskz=0; + + offset = y*spart->rectx + x; + obi= sseg->obi - spart->re->objectinstance; + strnr= sseg->strand->index + 1; + seg= sseg->v[1] - sseg->strand->vert; + mask= (1<<spart->sample); + + /* check against solid z-buffer */ + zverg= (int)z; + + if(spart->rectdaps) { + /* find the z of the sample */ + PixStr *ps; + intptr_t *rd= spart->rectdaps + offset; + + bufferz= 0x7FFFFFFF; + if(spart->rectmask) maskz= 0x7FFFFFFF; + + if(*rd) { + for(ps= (PixStr *)(*rd); ps; ps= ps->next) { + if(mask & ps->mask) { + bufferz= ps->z; + if(spart->rectmask) + maskz= ps->maskz; + break; + } + } } -#endif + } + else { + bufferz= spart->rectz[offset]; + if(spart->rectmask) + maskz= spart->rectmask[offset]; + } + +#define CHECK_ADD(n) \ + if(apn->p[n]==strnr && apn->obi[n]==obi && apn->seg[n]==seg) \ + { if(!(apn->mask[n] & mask)) { apn->mask[n] |= mask; apn->v[n] += t; apn->u[n] += s; } break; } +#define CHECK_ASSIGN(n) \ + if(apn->p[n]==0) \ + {apn->obi[n]= obi; apn->p[n]= strnr; apn->z[n]= zverg; apn->mask[n]= mask; apn->v[n]= t; apn->u[n]= s; apn->seg[n]= seg; break; } + + /* add to pixel list */ + if(zverg < bufferz && (spart->totapixbuf[offset] < MAX_ZROW)) { + if(!spart->rectmask || zverg > maskz) { + t = u*spart->t[0] + v*spart->t[1] + (1.0f-u-v)*spart->t[2]; + s = fabs(u*spart->s[0] + v*spart->s[1] + (1.0f-u-v)*spart->s[2]); + + apn= spart->apixbuf + offset; + while(apn) { + CHECK_ADD(0); + CHECK_ADD(1); + CHECK_ADD(2); + CHECK_ADD(3); + CHECK_ASSIGN(0); + CHECK_ASSIGN(1); + CHECK_ASSIGN(2); + CHECK_ASSIGN(3); + + apnew= addpsAstrand(spart->zspan); + SWAP(APixstrand, *apnew, *apn); + apn->next= apnew; + CHECK_ASSIGN(0); + } - if(spart->addpassflag & SCE_PASS_INDEXOB) - add_strand_obindex(spart->rl, offset, buffer->obr); + strand_shade_refcount(cache, sseg->v[1]); + strand_shade_refcount(cache, sseg->v[2]); + spart->totapixbuf[offset]++; + } } } @@ -663,59 +589,6 @@ static int strand_test_clip(float winmat[][4], ZSpan *zspan, float *bounds, floa return clipflag; } -static void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint) -{ - ShadeInput *shi= ssamp->shi; - ShadeResult *shr= ssamp->shr; - VlakRen vlr; - - memset(&vlr, 0, sizeof(vlr)); - vlr.flag= R_SMOOTH; - vlr.lay= sseg->strand->buffer->lay; - vlr.obr= sseg->strand->buffer->obr; - if(sseg->buffer->ma->mode & MA_TANGENT_STR) - vlr.flag |= R_TANGENT; - - shi->vlr= &vlr; - shi->obi= sseg->obi; - shi->obr= sseg->obi->obr; - - /* cache for shadow */ - shi->samplenr++; - - shade_input_set_strand(shi, sseg->strand, spoint); - shade_input_set_strand_texco(shi, sseg->strand, sseg->v[1], spoint); - - /* init material vars */ - // note, keep this synced with render_types.h - memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); - shi->har= shi->mat->har; - - /* shade */ - shade_samples_do_AO(ssamp); - shade_input_do_shade(shi, shr); - - /* apply simplification */ - if(spoint->alpha < 1.0f) { - shr->combined[0] *= spoint->alpha; - shr->combined[1] *= spoint->alpha; - shr->combined[2] *= spoint->alpha; - shr->combined[3] *= spoint->alpha; - - shr->col[0] *= spoint->alpha; - shr->col[1] *= spoint->alpha; - shr->col[2] *= spoint->alpha; - shr->col[3] *= spoint->alpha; - - shr->alpha *= spoint->alpha; - } - - /* include lamphalos for strand, since halo layer was added already */ - if(re->flag & R_LAMPHALO) - if(shi->layflag & SCE_LAY_HALO) - renderspothalo(shi, shr->combined, shr->combined[3]); -} - static void do_scanconvert_strand(Render *re, StrandPart *spart, ZSpan *zspan, float t, float dt, float *co1, float *co2, float *co3, float *co4, int sample) { float jco1[3], jco2[3], jco3[3], jco4[3], jx, jy; @@ -747,16 +620,22 @@ static void do_scanconvert_strand(Render *re, StrandPart *spart, ZSpan *zspan, f spart->sample= sample; spart->t[0]= t-dt; + spart->s[0]= -1.0f; spart->t[1]= t-dt; + spart->s[1]= 1.0f; spart->t[2]= t; - zspan_scanconvert_strand(zspan, spart, jco1, jco2, jco3, do_strand_blend); + spart->s[2]= 1.0f; + zspan_scanconvert_strand(zspan, spart, jco1, jco2, jco3, do_strand_fillac); spart->t[0]= t-dt; + spart->s[0]= -1.0f; spart->t[1]= t; + spart->s[1]= 1.0f; spart->t[2]= t; - zspan_scanconvert_strand(zspan, spart, jco1, jco3, jco4, do_strand_blend); + spart->s[2]= -1.0f; + zspan_scanconvert_strand(zspan, spart, jco1, jco3, jco4, do_strand_fillac); } -static void strand_render(Render *re, float winmat[][4], StrandPart *spart, ZSpan *zspan, StrandPoint *p1, StrandPoint *p2) +static void strand_render(Render *re, StrandSegment *sseg, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandPoint *p1, StrandPoint *p2) { if(spart) { float t= p2->t; @@ -771,18 +650,28 @@ static void strand_render(Render *re, float winmat[][4], StrandPart *spart, ZSpa do_scanconvert_strand(re, spart, zspan, t, dt, p1->zco2, p1->zco1, p2->zco1, p2->zco2, 0); } else { - float hoco1[4], hoco2[3]; - - projectvert(p1->co, winmat, hoco1); - projectvert(p2->co, winmat, hoco2); - - /* render both strand and single pixel wire to counter aliasing */ - zbufclip4(zspan, 0, 0, p1->hoco2, p1->hoco1, p2->hoco1, p2->hoco2, 0, 0, 0, 0); - zbufsinglewire(zspan, 0, 0, hoco1, hoco2); + float hoco1[4], hoco2[4]; + int a, obi, index; + + obi= sseg->obi - re->objectinstance; + index= sseg->strand->index; + + projectvert(p1->co, winmat, hoco1); + projectvert(p2->co, winmat, hoco2); + + for(a=0; a<totzspan; a++) { +#if 0 + /* render both strand and single pixel wire to counter aliasing */ + zbufclip4(re, &zspan[a], obi, index, p1->hoco2, p1->hoco1, p2->hoco1, p2->hoco2, p1->clip2, p1->clip1, p2->clip1, p2->clip2); +#endif + /* only render a line for now, which makes the shadow map more + similiar across frames, and so reduces flicker */ + zbufsinglewire(&zspan[a], obi, index, hoco1, hoco2); + } } } - -static int strand_segment_recursive(Render *re, float winmat[][4], StrandPart *spart, ZSpan *zspan, StrandSegment *sseg, StrandPoint *p1, StrandPoint *p2, int depth) + +static int strand_segment_recursive(Render *re, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandSegment *sseg, StrandPoint *p1, StrandPoint *p2, int depth) { StrandPoint p; StrandBuffer *buffer= sseg->buffer; @@ -815,19 +704,23 @@ static int strand_segment_recursive(Render *re, float winmat[][4], StrandPart *s do_strand_point_project(winmat, zspan, p.co2, p.hoco2, p.zco2); } else { +#if 0 projectvert(p.co1, winmat, p.hoco1); projectvert(p.co2, winmat, p.hoco2); + p.clip1= testclip(p.hoco1); + p.clip2= testclip(p.hoco2); +#endif } - if(!strand_segment_recursive(re, winmat, spart, zspan, sseg, p1, &p, depth+1)) - strand_render(re, winmat, spart, zspan, p1, &p); - if(!strand_segment_recursive(re, winmat, spart, zspan, sseg, &p, p2, depth+1)) - strand_render(re, winmat, spart, zspan, &p, p2); + if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, p1, &p, depth+1)) + strand_render(re, sseg, winmat, spart, zspan, totzspan, p1, &p); + if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, &p, p2, depth+1)) + strand_render(re, sseg, winmat, spart, zspan, totzspan, &p, p2); return 1; } -void render_strand_segment(Render *re, float winmat[][4], StrandPart *spart, ZSpan *zspan, StrandSegment *sseg) +void render_strand_segment(Render *re, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandSegment *sseg) { StrandBuffer *buffer= sseg->buffer; StrandPoint *p1= &sseg->point1; @@ -848,149 +741,55 @@ void render_strand_segment(Render *re, float winmat[][4], StrandPart *spart, ZSp do_strand_point_project(winmat, zspan, p2->co2, p2->hoco2, p2->zco2); } else { +#if 0 projectvert(p1->co1, winmat, p1->hoco1); projectvert(p1->co2, winmat, p1->hoco2); projectvert(p2->co1, winmat, p2->hoco1); projectvert(p2->co2, winmat, p2->hoco2); + p1->clip1= testclip(p1->hoco1); + p1->clip2= testclip(p1->hoco2); + p2->clip1= testclip(p2->hoco1); + p2->clip2= testclip(p2->hoco2); +#endif } - if(!strand_segment_recursive(re, winmat, spart, zspan, sseg, p1, p2, 0)) - strand_render(re, winmat, spart, zspan, p1, p2); -} - -static void zbuffer_strands_filter(Render *re, RenderPart *pa, RenderLayer *rl, StrandPart *spart, float *pass) -{ - RenderResult *rr= pa->result; - ShadeResult *shr, *shrrect= spart->result; - float *passrect= pass; - long *rdrect; - int osa, x, y, a, crop= 0, offs=0, od; - - osa= (re->osa? re->osa: 1); - - /* filtered render, for now we assume only 1 filter size */ - if(pa->crop) { - crop= 1; - offs= pa->rectx + 1; - passrect+= 4*offs; - shrrect+= offs*osa; - } - - rdrect= pa->rectdaps; - - /* zero alpha pixels get speed vector max again */ - if(spart->addpassflag & SCE_PASS_VECTOR) - if(rl->layflag & SCE_LAY_SOLID) - reset_sky_speedvectors(pa, rl, rl->scolrect); - - /* init scanline updates */ - rr->renrect.ymin= 0; - rr->renrect.ymax= -pa->crop; - rr->renlay= rl; - - /* filter the shade results */ - for(y=pa->disprect.ymin+crop; y<pa->disprect.ymax-crop; y++, rr->renrect.ymax++) { - pass= passrect; - shr= shrrect; - od= offs; - - for(x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, shr+=osa, pass+=4, od++) { - if(spart->mask[od] == 0) { - if(spart->addpassflag & SCE_PASS_VECTOR) - add_transp_speed(rl, od, NULL, 0.0f, rdrect); - } - else { - if(re->osa == 0) { - addAlphaUnderFloat(pass, shr->combined); - } - else { - for(a=0; a<re->osa; a++) - add_filt_fmask(1<<a, shr[a].combined, pass, rr->rectx); - } - - if(spart->addpassflag) { - /* merge all in one, and then add */ - merge_transp_passes(rl, shr); - add_transp_passes(rl, od, shr, pass[3]); - - if(spart->addpassflag & SCE_PASS_VECTOR) - add_transp_speed(rl, od, shr->winspeed, pass[3], rdrect); - } - } - } - - shrrect+= pa->rectx*osa; - passrect+= 4*pa->rectx; - offs+= pa->rectx; - } - - /* disable scanline updating */ - rr->renlay= NULL; + if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, p1, p2, 0)) + strand_render(re, sseg, winmat, spart, zspan, totzspan, p1, p2); } /* render call to fill in strands */ -unsigned short *zbuffer_strands_shade(Render *re, RenderPart *pa, RenderLayer *rl, float *pass) +int zbuffer_strands_abuf(Render *re, RenderPart *pa, RenderLayer *rl, APixstrand *apixbuf, ListBase *apsmbase, StrandShadeCache *cache) { - //struct RenderPrimitiveIterator *iter; ObjectRen *obr; ObjectInstanceRen *obi; ZSpan zspan; StrandRen *strand=0; StrandVert *svert; + StrandBound *sbound; StrandPart spart; StrandSegment sseg; StrandSortSegment *sortsegments = NULL, *sortseg, *firstseg; MemArena *memarena; float z[4], bounds[4], winmat[4][4]; - int a, b, i, resultsize, totsegment, clip[4]; + int a, b, c, i, totsegment, clip[4]; if(re->test_break()) - return NULL; + return 0; if(re->totstrand == 0) - return NULL; + return 0; /* setup StrandPart */ memset(&spart, 0, sizeof(spart)); spart.re= re; - spart.rl= rl; - spart.pass= pass; spart.rectx= pa->rectx; spart.recty= pa->recty; + spart.apixbuf= apixbuf; + spart.zspan= &zspan; + spart.rectdaps= pa->rectdaps; spart.rectz= pa->rectz; - spart.addpassflag= rl->passflag & ~(SCE_PASS_Z|SCE_PASS_COMBINED); - spart.addzbuf= rl->passflag & SCE_PASS_Z; - - if(re->osa) resultsize= pa->rectx*pa->recty*re->osa; - else resultsize= pa->rectx*pa->recty; - spart.result= MEM_callocN(sizeof(ShadeResult)*resultsize, "StrandPartResult"); - spart.mask= MEM_callocN(pa->rectx*pa->recty*sizeof(short), "StrandPartMask"); - - if(spart.addpassflag & SCE_PASS_VECTOR) { - /* initialize speed vectors */ - for(a=0; a<resultsize; a++) { - spart.result[a].winspeed[0]= PASS_VECTOR_MAX; - spart.result[a].winspeed[1]= PASS_VECTOR_MAX; - spart.result[a].winspeed[2]= PASS_VECTOR_MAX; - spart.result[a].winspeed[3]= PASS_VECTOR_MAX; - } - } - - if(spart.addzbuf) { - /* duplicate rectz so we can read from the old buffer, while - * writing new z values */ - spart.rectz= MEM_dupallocN(pa->rectz); - spart.outrectz= pa->rectz; - } - - shade_sample_initialize(&spart.ssamp1, pa, rl); - shade_sample_initialize(&spart.ssamp2, pa, rl); - shade_sample_initialize(&spart.ssamp, pa, rl); - spart.ssamp1.shi[0].sample= 0; - spart.ssamp2.shi[0].sample= 1; - spart.ssamp1.tot= 1; - spart.ssamp2.tot= 1; - spart.ssamp.tot= 1; + spart.rectmask= pa->rectmask; + spart.cache= cache; zbuf_alloc_span(&zspan, pa->rectx, pa->recty, re->clipcrop); @@ -1005,83 +804,83 @@ unsigned short *zbuffer_strands_shade(Render *re, RenderPart *pa, RenderLayer *r zspan.zofsx -= 0.5f; zspan.zofsy -= 0.5f; + zspan.apsmbase= apsmbase; + /* clipping setup */ bounds[0]= (2*pa->disprect.xmin - re->winx-1)/(float)re->winx; bounds[1]= (2*pa->disprect.xmax - re->winx+1)/(float)re->winx; bounds[2]= (2*pa->disprect.ymin - re->winy-1)/(float)re->winy; bounds[3]= (2*pa->disprect.ymax - re->winy+1)/(float)re->winy; - /* sort segments */ - //iter= init_primitive_iterator(re, re->strandbuckets, pa); - memarena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); firstseg= NULL; sortseg= sortsegments; totsegment= 0; - //while((strand = next_primitive_iterator(iter))) { + /* for all object instances */ for(obi=re->instancetable.first, i=0; obi; obi=obi->next, i++) { obr= obi->obr; + if(!obr->strandbuf || !(obr->strandbuf->lay & rl->lay)) + continue; + + /* compute matrix and try clipping whole object */ if(obi->flag & R_TRANSFORMED) zbuf_make_winmat(re, obi->mat, winmat); else zbuf_make_winmat(re, NULL, winmat); - for(a=0; a<obr->totstrand; a++) { - if((a & 255)==0) strand= obr->strandnodes[a>>8].strand; - else strand++; + if(clip_render_object(obi->obr->boundbox, bounds, winmat)) + continue; - if(re->test_break()) - break; - -#if 0 - if(strand->clip) + /* for each bounding box containing a number of strands */ + sbound= obr->strandbuf->bound; + for(c=0; c<obr->strandbuf->totbound; c++, sbound++) { + if(clip_render_object(sbound->boundbox, bounds, winmat)) continue; -#endif - - svert= strand->vert; - - /* keep clipping and z depth for 4 control points */ - clip[1]= strand_test_clip(winmat, &zspan, bounds, svert->co, &z[1]); - clip[2]= strand_test_clip(winmat, &zspan, bounds, (svert+1)->co, &z[2]); - clip[0]= clip[1]; z[0]= z[1]; - for(b=0; b<strand->totvert-1; b++, svert++) { - /* compute 4th point clipping and z depth */ - if(b < strand->totvert-2) { - clip[3]= strand_test_clip(winmat, &zspan, bounds, (svert+2)->co, &z[3]); - } - else { - clip[3]= clip[2]; z[3]= z[2]; - } - - /* check clipping and add to sortsegments buffer */ - if(!(clip[0] & clip[1] & clip[2] & clip[3])) { - sortseg= BLI_memarena_alloc(memarena, sizeof(StrandSortSegment)); - sortseg->obi= i; - sortseg->strand= strand->index; - sortseg->segment= b; + /* for each strand in this bounding box */ + for(a=sbound->start; a<sbound->end; a++) { + strand= RE_findOrAddStrand(obr, a); + svert= strand->vert; - sortseg->z= 0.5f*(z[1] + z[2]); + /* keep clipping and z depth for 4 control points */ + clip[1]= strand_test_clip(winmat, &zspan, bounds, svert->co, &z[1]); + clip[2]= strand_test_clip(winmat, &zspan, bounds, (svert+1)->co, &z[2]); + clip[0]= clip[1]; z[0]= z[1]; - sortseg->next= firstseg; - firstseg= sortseg; - totsegment++; + for(b=0; b<strand->totvert-1; b++, svert++) { + /* compute 4th point clipping and z depth */ + if(b < strand->totvert-2) { + clip[3]= strand_test_clip(winmat, &zspan, bounds, (svert+2)->co, &z[3]); + } + else { + clip[3]= clip[2]; z[3]= z[2]; + } + + /* check clipping and add to sortsegments buffer */ + if(!(clip[0] & clip[1] & clip[2] & clip[3])) { + sortseg= BLI_memarena_alloc(memarena, sizeof(StrandSortSegment)); + sortseg->obi= i; + sortseg->strand= strand->index; + sortseg->segment= b; + + sortseg->z= 0.5f*(z[1] + z[2]); + + sortseg->next= firstseg; + firstseg= sortseg; + totsegment++; + } + + /* shift clipping and z depth */ + clip[0]= clip[1]; z[0]= z[1]; + clip[1]= clip[2]; z[1]= z[2]; + clip[2]= clip[3]; z[2]= z[3]; } - - /* shift clipping and z depth */ - clip[0]= clip[1]; z[0]= z[1]; - clip[1]= clip[2]; z[1]= z[2]; - clip[2]= clip[3]; z[2]= z[3]; } } } -#if 0 - free_primitive_iterator(iter); -#endif - if(!re->test_break()) { /* convert list to array and sort */ sortsegments= MEM_mallocN(sizeof(StrandSortSegment)*totsegment, "StrandSortSegment"); @@ -1092,7 +891,7 @@ unsigned short *zbuffer_strands_shade(Render *re, RenderPart *pa, RenderLayer *r BLI_memarena_free(memarena); - spart.hash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + spart.totapixbuf= MEM_callocN(sizeof(int)*pa->rectx*pa->recty, "totapixbuf"); if(!re->test_break()) { /* render segments in sorted order */ @@ -1120,135 +919,94 @@ unsigned short *zbuffer_strands_shade(Render *re, RenderPart *pa, RenderLayer *r spart.segment= &sseg; - render_strand_segment(re, winmat, &spart, &zspan, &sseg); + render_strand_segment(re, winmat, &spart, &zspan, 1, &sseg); } } - // TODO printf(">>> %d\n", BLI_ghash_size(spart.hash)); - BLI_ghash_free(spart.hash, NULL, (GHashValFreeFP)MEM_freeN); - - zbuffer_strands_filter(re, pa, rl, &spart, pass); - - /* free */ - MEM_freeN(spart.result); - - if(spart.addzbuf) - MEM_freeN(spart.rectz); - if(sortsegments) MEM_freeN(sortsegments); + MEM_freeN(spart.totapixbuf); zbuf_free_span(&zspan); - if(!(re->osa && (rl->layflag & SCE_LAY_SOLID))) { - MEM_freeN(spart.mask); - spart.mask= NULL; - } - - return spart.mask; + return totsegment; } -void project_strands(Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, int do_buckets) -{ -#if 0 - ObjectRen *obr; - StrandRen *strand = NULL; - StrandVert *svert; - float hoco[4], min[2], max[2], bucketco[2], vec[3]; - int a, b; - /* float bmin[3], bmax[3], bpad[3], padding[2]; */ - - if(re->strandbuckets) { - free_buckets(re->strandbuckets); - re->strandbuckets= NULL; - } - - if(re->totstrand == 0) - return; - - if(do_buckets) - re->strandbuckets= init_buckets(re); +/* *************** */ - /* calculate view coordinates (and zbuffer value) */ - for(obr=re->objecttable.first; obr; obr=obr->next) { - for(a=0; a<obr->totstrand; a++) { - if((a & 255)==0) strand= obr->strandnodes[a>>8].strand; - else strand++; +StrandSurface *cache_strand_surface(Render *re, ObjectRen *obr, DerivedMesh *dm, float mat[][4], int timeoffset) +{ + StrandSurface *mesh; + MFace *mface; + MVert *mvert; + float (*co)[3]; + int a, totvert, totface; + + totvert= dm->getNumVerts(dm); + totface= dm->getNumFaces(dm); + + for(mesh=re->strandsurface.first; mesh; mesh=mesh->next) + if(mesh->obr.ob == obr->ob && mesh->obr.par == obr->par + && mesh->obr.index == obr->index && mesh->totvert==totvert && mesh->totface==totface) + break; - strand->clip= ~0; + if(!mesh) { + mesh= MEM_callocN(sizeof(StrandSurface), "StrandSurface"); + mesh->obr= *obr; + mesh->totvert= totvert; + mesh->totface= totface; + mesh->face= MEM_callocN(sizeof(int)*4*mesh->totface, "StrandSurfFaces"); + mesh->col= MEM_callocN(sizeof(float)*3*mesh->totvert, "StrandSurfCol"); + BLI_addtail(&re->strandsurface, mesh); + } + + if(timeoffset == -1 && !mesh->prevco) + mesh->prevco= co= MEM_callocN(sizeof(float)*3*mesh->totvert, "StrandSurfCo"); + else if(timeoffset == 0 && !mesh->co) + mesh->co= co= MEM_callocN(sizeof(float)*3*mesh->totvert, "StrandSurfCo"); + else if(timeoffset == 1 && !mesh->nextco) + mesh->nextco= co= MEM_callocN(sizeof(float)*3*mesh->totvert, "StrandSurfCo"); + else + return mesh; -#if 0 - if(!(strand->buffer->flag & R_STRAND_BSPLINE)) { - INIT_MINMAX(bmin, bmax); - svert= strand->vert; - for(b=0; b<strand->totvert; b++, svert++) - DO_MINMAX(svert->co, bmin, bmax) + mvert= dm->getVertArray(dm); + for(a=0; a<mesh->totvert; a++, mvert++) { + VECCOPY(co[a], mvert->co); + Mat4MulVecfl(mat, co[a]); + } - bpad[0]= (bmax[0]-bmin[0])*0.2f; - bpad[1]= (bmax[1]-bmin[1])*0.2f; - bpad[2]= (bmax[2]-bmin[2])*0.2f; - } - else - bpad[0]= bpad[1]= bpad[2]= 0.0f; - - ma= strand->buffer->ma; - width= MAX2(ma->strand_sta, ma->strand_end); - if(strand->buffer->flag & R_STRAND_B_UNITS) { - bpad[0] += 0.5f*width; - bpad[1] += 0.5f*width; - bpad[2] += 0.5f*width; - } -#endif + mface= dm->getFaceArray(dm); + for(a=0; a<mesh->totface; a++, mface++) { + mesh->face[a][0]= mface->v1; + mesh->face[a][1]= mface->v2; + mesh->face[a][2]= mface->v3; + mesh->face[a][3]= mface->v4; + } - INIT_MINMAX2(min, max); - svert= strand->vert; - for(b=0; b<strand->totvert; b++, svert++) { - //VECADD(vec, svert->co, bpad); + return mesh; +} - /* same as VertRen */ - if(do_pano) { - vec[0]= re->panoco*svert->co[0] + re->panosi*svert->co[2]; - vec[1]= svert->co[1]; - vec[2]= -re->panosi*svert->co[0] + re->panoco*svert->co[2]; - } - else - VECCOPY(vec, svert->co) +void free_strand_surface(Render *re) +{ + StrandSurface *mesh; - /* Go from wcs to hcs ... */ - projectfunc(vec, re->winmat, hoco); - /* ... and clip in that system. */ - strand->clip &= testclip(hoco); + for(mesh=re->strandsurface.first; mesh; mesh=mesh->next) { + if(mesh->co) MEM_freeN(mesh->co); + if(mesh->prevco) MEM_freeN(mesh->prevco); + if(mesh->nextco) MEM_freeN(mesh->nextco); + if(mesh->col) MEM_freeN(mesh->col); + if(mesh->face) MEM_freeN(mesh->face); + } -#if 0 - if(do_buckets) { - project_hoco_to_bucket(re->strandbuckets, hoco, bucketco); - DO_MINMAX2(bucketco, min, max); - } -#endif - } + BLI_freelistN(&re->strandsurface); +} -#if 0 - if(do_buckets) { - if(strand->buffer->flag & R_STRAND_BSPLINE) { - min[0] -= width; - min[1] -= width; - max[0] += width; - max[1] += width; - } - else { - /* catmull-rom stays within 1.2f bounds in object space, - * is this still true after projection? */ - min[0] -= width + (max[0]-min[0])*0.2f; - min[1] -= width + (max[1]-min[1])*0.2f; - max[0] += width + (max[0]-min[0])*0.2f; - max[1] += width + (max[1]-min[1])*0.2f; - } +void strand_minmax(StrandRen *strand, float *min, float *max) +{ + StrandVert *svert; + int a; - add_buckets_primitive(re->strandbuckets, min, max, strand); - } -#endif - } - } -#endif + for(a=0, svert=strand->vert; a<strand->totvert; a++, svert++) + DO_MINMAX(svert->co, min, max) } diff --git a/source/blender/render/intern/source/sunsky.c b/source/blender/render/intern/source/sunsky.c new file mode 100644 index 00000000000..2b490e71142 --- /dev/null +++ b/source/blender/render/intern/source/sunsky.c @@ -0,0 +1,499 @@ + /** + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include "sunsky.h" +#include "math.h" +#include "BLI_arithb.h" +#include "BKE_global.h" + +/** + * These macros are defined for vector operations + * */ + +/** + * compute v1 = v2 op v3 + * v1, v2 and v3 are vectors contains 3 float + * */ +#define vec3opv(v1, v2, op, v3) \ + v1[0] = (v2[0] op v3[0]); \ + v1[1] = (v2[1] op v3[1]);\ + v1[2] = (v2[2] op v3[2]); + +/** + * compute v1 = v2 op f1 + * v1, v2 are vectors contains 3 float + * and f1 is a float + * */ +#define vec3opf(v1, v2, op, f1)\ + v1[0] = (v2[0] op (f1));\ + v1[1] = (v2[1] op (f1));\ + v1[2] = (v2[2] op (f1)); + +/** + * compute v1 = f1 op v2 + * v1, v2 are vectors contains 3 float + * and f1 is a float + * */ +#define fopvec3(v1, f1, op, v2)\ + v1[0] = ((f1) op v2[0]);\ + v1[1] = ((f1) op v2[1]);\ + v1[2] = ((f1) op v2[2]); + +/** + * ClipColor: + * clip a color to range [0,1]; + * */ +void ClipColor(float c[3]) +{ + if (c[0] > 1.0) c[0] = 1.0; + if (c[0] < 0.0) c[0] = 0.0; + if (c[1] > 1.0) c[1] = 1.0; + if (c[1] < 0.0) c[1] = 0.0; + if (c[2] > 1.0) c[2] = 1.0; + if (c[2] < 0.0) c[2] = 0.0; +} + +/** + * AngleBetween: + * compute angle between to direction + * all angles are in radians + * */ +static float AngleBetween(float thetav, float phiv, float theta, float phi) +{ + float cospsi = sin(thetav) * sin(theta) * cos(phi - phiv) + cos(thetav) * cos(theta); + + if (cospsi > 1.0) + return 0; + if (cospsi < -1.0) + return M_PI; + + return acos(cospsi); +} + +/** + * DirectionToThetaPhi: + * this function convert a direction to it's theta and phi value + * parameters: + * toSun: contains direction information + * theta, phi, are return values from this conversion + * */ +static void DirectionToThetaPhi(float *toSun, float *theta, float *phi) +{ + *theta = acos(toSun[2]); + if (fabs(*theta) < 1e-5) + *phi = 0; + else + *phi = atan2(toSun[1], toSun[0]); +} + +/** + * PerezFunction: + * compute perez function value based on input paramters + * */ +float PerezFunction(struct SunSky *sunsky, const float *lam, float theta, float gamma, float lvz) +{ + float den, num; + + den = ((1 + lam[0] * exp(lam[1])) * + (1 + lam[2] * exp(lam[3] * sunsky->theta) + lam[4] * cos(sunsky->theta) * cos(sunsky->theta))); + + num = ((1 + lam[0] * exp(lam[1] / cos(theta))) * + (1 + lam[2] * exp(lam[3] * gamma) + lam[4] * cos(gamma) * cos(gamma))); + + return(lvz * num / den);} + +/** + * InitSunSky: + * this function compute some sun,sky parameters according to input parameters and also initiate some other sun, sky parameters + * parameters: + * sunSky, is a structure that contains informtion about sun, sky and atmosphere, in this function, most of its values initiated + * turb, is atmosphere turbidity + * toSun, contains sun direction + * horizon_brighness, controls the brightness of the horizon colors + * spread, controls colors spreed at horizon + * sun_brightness, controls sun's brightness + * sun_size, controls sun's size + * back_scatter, controls back scatter light + * */ +void InitSunSky(struct SunSky *sunsky, float turb, float *toSun, float horizon_brightness, + float spread,float sun_brightness, float sun_size, float back_scatter, + float skyblendfac, short skyblendtype, float sky_exposure, float sky_colorspace) +{ + + float theta2; + float theta3; + float T; + float T2; + float chi; + + sunsky->turbidity = turb; + + sunsky->horizon_brightness = horizon_brightness; + sunsky->spread = spread; + sunsky->sun_brightness = sun_brightness; + sunsky->sun_size = sun_size; + sunsky->backscattered_light = back_scatter; + sunsky->skyblendfac= skyblendfac; + sunsky->skyblendtype= skyblendtype; + sunsky->sky_exposure= -sky_exposure; + sunsky->sky_colorspace= sky_colorspace; + + sunsky->toSun[0] = toSun[0]; + sunsky->toSun[1] = toSun[1]; + sunsky->toSun[2] = toSun[2]; + + DirectionToThetaPhi(sunsky->toSun, &sunsky->theta, &sunsky->phi); + + sunsky->sunSolidAngle = 0.25 * M_PI * 1.39 * 1.39 / (150 * 150); // = 6.7443e-05 + + theta2 = sunsky->theta*sunsky->theta; + theta3 = theta2 * sunsky->theta; + T = turb; + T2 = turb*turb; + + chi = (4.0 / 9.0 - T / 120.0) * (M_PI - 2 * sunsky->theta); + sunsky->zenith_Y = (4.0453 * T - 4.9710) * tan(chi) - .2155 * T + 2.4192; + sunsky->zenith_Y *= 1000; // conversion from kcd/m^2 to cd/m^2 + + if (sunsky->zenith_Y<=0) + sunsky->zenith_Y = 1e-6; + + sunsky->zenith_x = + ( + 0.00165 * theta3 - 0.00374 * theta2 + 0.00208 * sunsky->theta + 0) * T2 + + ( -0.02902 * theta3 + 0.06377 * theta2 - 0.03202 * sunsky->theta + 0.00394) * T + + ( + 0.11693 * theta3 - 0.21196 * theta2 + 0.06052 * sunsky->theta + 0.25885); + + sunsky->zenith_y = + ( + 0.00275 * theta3 - 0.00610 * theta2 + 0.00316 * sunsky->theta + 0) * T2 + + ( -0.04214 * theta3 + 0.08970 * theta2 - 0.04153 * sunsky->theta + 0.00515) * T + + ( + 0.15346 * theta3 - 0.26756 * theta2 + 0.06669 * sunsky->theta + 0.26688); + + + sunsky->perez_Y[0] = 0.17872 * T - 1.46303; + sunsky->perez_Y[1] = -0.35540 * T + 0.42749; + sunsky->perez_Y[2] = -0.02266 * T + 5.32505; + sunsky->perez_Y[3] = 0.12064 * T - 2.57705; + sunsky->perez_Y[4] = -0.06696 * T + 0.37027; + + sunsky->perez_x[0] = -0.01925 * T - 0.25922; + sunsky->perez_x[1] = -0.06651 * T + 0.00081; + sunsky->perez_x[2] = -0.00041 * T + 0.21247; + sunsky->perez_x[3] = -0.06409 * T - 0.89887; + sunsky->perez_x[4] = -0.00325 * T + 0.04517; + + sunsky->perez_y[0] = -0.01669 * T - 0.26078; + sunsky->perez_y[1] = -0.09495 * T + 0.00921; + sunsky->perez_y[2] = -0.00792 * T + 0.21023; + sunsky->perez_y[3] = -0.04405 * T - 1.65369; + sunsky->perez_y[4] = -0.01092 * T + 0.05291; + + /* suggested by glome in + * http://projects.blender.org/tracker/?func=detail&atid=127&aid=8063&group_id=9*/ + sunsky->perez_Y[0] *= sunsky->horizon_brightness; + sunsky->perez_x[0] *= sunsky->horizon_brightness; + sunsky->perez_y[0] *= sunsky->horizon_brightness; + + sunsky->perez_Y[1] *= sunsky->spread; + sunsky->perez_x[1] *= sunsky->spread; + sunsky->perez_y[1] *= sunsky->spread; + + sunsky->perez_Y[2] *= sunsky->sun_brightness; + sunsky->perez_x[2] *= sunsky->sun_brightness; + sunsky->perez_y[2] *= sunsky->sun_brightness; + + sunsky->perez_Y[3] *= sunsky->sun_size; + sunsky->perez_x[3] *= sunsky->sun_size; + sunsky->perez_y[3] *= sunsky->sun_size; + + sunsky->perez_Y[4] *= sunsky->backscattered_light; + sunsky->perez_x[4] *= sunsky->backscattered_light; + sunsky->perez_y[4] *= sunsky->backscattered_light; +} + +/** + * GetSkyXYZRadiance: + * this function compute sky radiance according to a view parameters `theta' and `phi'and sunSky values + * parameters: + * sunSky, sontains sun and sky parameters + * theta, is sun's theta + * phi, is sun's phi + * color_out, is computed color that shows sky radiance in XYZ color format + * */ +void GetSkyXYZRadiance(struct SunSky* sunsky, float theta, float phi, float color_out[3]) +{ + float gamma; + float x,y,Y,X,Z; + float hfade=1, nfade=1; + + + if (theta>(0.5*M_PI)) { + hfade = 1.0-(theta*M_1_PI-0.5)*2.0; + hfade = hfade*hfade*(3.0-2.0*hfade); + theta = 0.5*M_PI; + } + + if (sunsky->theta>(0.5*M_PI)) { + if (theta<=0.5*M_PI) { + nfade = 1.0-(0.5-theta*M_1_PI)*2.0; + nfade *= 1.0-(sunsky->theta*M_1_PI-0.5)*2.0; + nfade = nfade*nfade*(3.0-2.0*nfade); + } + } + + gamma = AngleBetween(theta, phi, sunsky->theta, sunsky->phi); + + // Compute xyY values + x = PerezFunction(sunsky, sunsky->perez_x, theta, gamma, sunsky->zenith_x); + y = PerezFunction(sunsky, sunsky->perez_y, theta, gamma, sunsky->zenith_y); + Y = 6.666666667e-5 * nfade * hfade * PerezFunction(sunsky, sunsky->perez_Y, theta, gamma, sunsky->zenith_Y); + + if(sunsky->sky_exposure!=0.0f) + Y = 1.0 - exp(Y*sunsky->sky_exposure); + + X = (x / y) * Y; + Z = ((1 - x - y) / y) * Y; + + color_out[0] = X; + color_out[1] = Y; + color_out[2] = Z; +} + +/** + * GetSkyXYZRadiancef: + * this function compute sky radiance according to a view direction `varg' and sunSky values + * parameters: + * sunSky, sontains sun and sky parameters + * varg, shows direction + * color_out, is computed color that shows sky radiance in XYZ color format + * */ +void GetSkyXYZRadiancef(struct SunSky* sunsky, const float varg[3], float color_out[3]) +{ + float theta, phi; + float v[3]; + + VecCopyf(v, (float*)varg); + Normalize(v); + + if (v[2] < 0.001){ + v[2] = 0.001; + Normalize(v); + } + + DirectionToThetaPhi(v, &theta, &phi); + GetSkyXYZRadiance(sunsky, theta, phi, color_out); +} + +/** + * ComputeAttenuatedSunlight: + * this function compute attenuated sun light based on sun's theta and atmosphere turbidity + * parameters: + * theta, is sun's theta + * turbidity: is atmosphere turbidity + * fTau: contains computed attenuated sun light + * */ +void ComputeAttenuatedSunlight(float theta, int turbidity, float fTau[3]) +{ + float fBeta ; + float fTauR, fTauA; + float m ; + float fAlpha; + + int i; + float fLambda[3]; + fLambda[0] = 0.65f; + fLambda[1] = 0.57f; + fLambda[2] = 0.475f; + + fAlpha = 1.3f; + fBeta = 0.04608365822050f * turbidity - 0.04586025928522f; + + m = 1.0/(cos(theta) + 0.15f*pow(93.885f-theta/M_PI*180.0f,-1.253f)); + + for(i = 0; i < 3; i++) + { + // Rayleigh Scattering + fTauR = exp( -m * 0.008735f * pow(fLambda[i], (float)(-4.08f))); + + // Aerosal (water + dust) attenuation + fTauA = exp(-m * fBeta * pow(fLambda[i], -fAlpha)); + + fTau[i] = fTauR * fTauA; + } +} + +/** + * InitAtmosphere: + * this function intiate sunSky structure with user input parameters. + * parameters: + * sunSky, contains information about sun, and in this function some atmosphere parameters will initiated + * sun_intens, shows sun intensity value + * mief, Mie scattering factor this factor currently call with 1.0 + * rayf, Rayleigh scattering factor, this factor currently call with 1.0 + * inscattf, inscatter light factor that range from 0.0 to 1.0, 0.0 means no inscatter light and 1.0 means full inscatter light + * extincf, extinction light factor that range from 0.0 to 1.0, 0.0 means no extinction and 1.0 means full extinction + * disf, is distance factor, multiplyed to pixle's z value to compute each pixle's distance to camera, + * */ +void InitAtmosphere(struct SunSky *sunSky, float sun_intens, float mief, float rayf, + float inscattf, float extincf, float disf) +{ + const float pi = 3.14159265358f; + const float n = 1.003f; // refractive index + const float N = 2.545e25; + const float pn = 0.035f; + const float T = 2.0f; + float fTemp, fTemp2, fTemp3, fBeta, fBetaDash; + float c = (6.544*T - 6.51)*1e-17; + float K[3] = {0.685f, 0.679f, 0.670f}; + float vBetaMieTemp[3]; + + float fLambda[3],fLambda2[3], fLambda4[3]; + float vLambda2[3]; + float vLambda4[3]; + + int i; + + sunSky->atm_SunIntensity = sun_intens; + sunSky->atm_BetaMieMultiplier = mief; + sunSky->atm_BetaRayMultiplier = rayf; + sunSky->atm_InscatteringMultiplier = inscattf; + sunSky->atm_ExtinctionMultiplier = extincf; + sunSky->atm_DistanceMultiplier = disf; + + sunSky->atm_HGg=0.8; + + fLambda[0] = 1/650e-9f; + fLambda[1] = 1/570e-9f; + fLambda[2] = 1/475e-9f; + for (i=0; i < 3; i++) + { + fLambda2[i] = fLambda[i]*fLambda[i]; + fLambda4[i] = fLambda2[i]*fLambda2[i]; + } + + vLambda2[0] = fLambda2[0]; + vLambda2[1] = fLambda2[1]; + vLambda2[2] = fLambda2[2]; + + vLambda4[0] = fLambda4[0]; + vLambda4[1] = fLambda4[1]; + vLambda4[2] = fLambda4[2]; + + // Rayleigh scattering constants. + fTemp = pi*pi*(n*n-1)*(n*n-1)*(6+3*pn)/(6-7*pn)/N; + fBeta = 8*fTemp*pi/3; + + vec3opf(sunSky->atm_BetaRay, vLambda4, *, fBeta); + fBetaDash = fTemp/2; + vec3opf(sunSky->atm_BetaDashRay, vLambda4,*, fBetaDash); + + + // Mie scattering constants. + fTemp2 = 0.434*c*(2*pi)*(2*pi)*0.5f; + vec3opf(sunSky->atm_BetaDashMie, vLambda2, *, fTemp2); + + fTemp3 = 0.434f*c*pi*(2*pi)*(2*pi); + + vec3opv(vBetaMieTemp, K, *, fLambda); + vec3opf(sunSky->atm_BetaMie, vBetaMieTemp,*, fTemp3); + +} + +/** + * AtmospherePixleShader: + * this function apply atmosphere effect on a pixle color `rgb' at distance `s' + * parameters: + * sunSky, contains information about sun parameters and user values + * view, is camera view vector + * s, is distance + * rgb, contains rendered color value for a pixle + * */ +void AtmospherePixleShader( struct SunSky* sunSky, float view[3], float s, float rgb[3]) +{ + float costheta; + float Phase_1; + float Phase_2; + float sunColor[3]; + + float E[3]; + float E1[3]; + + + float I[3]; + float fTemp; + float vTemp1[3], vTemp2[3]; + + float sunDirection[3]; + + s *= sunSky->atm_DistanceMultiplier; + + sunDirection[0] = sunSky->toSun[0]; + sunDirection[1] = sunSky->toSun[1]; + sunDirection[2] = sunSky->toSun[2]; + + costheta = Inpf(view, sunDirection); // cos(theta) + Phase_1 = 1 + (costheta * costheta); // Phase_1 + + vec3opf(sunSky->atm_BetaRay, sunSky->atm_BetaRay, *, sunSky->atm_BetaRayMultiplier); + vec3opf(sunSky->atm_BetaMie, sunSky->atm_BetaMie, *, sunSky->atm_BetaMieMultiplier); + vec3opv(sunSky->atm_BetaRM, sunSky->atm_BetaRay, +, sunSky->atm_BetaMie); + + //e^(-(beta_1 + beta_2) * s) = E1 + vec3opf(E1, sunSky->atm_BetaRM, *, -s/log(2)); + E1[0] = exp(E1[0]); + E1[1] = exp(E1[1]); + E1[2] = exp(E1[2]); + + VecCopyf(E, E1); + + //Phase2(theta) = (1-g^2)/(1+g-2g*cos(theta))^(3/2) + fTemp = 1 + sunSky->atm_HGg - 2 * sunSky->atm_HGg * costheta; + fTemp = fTemp * sqrt(fTemp); + Phase_2 = (1 - sunSky->atm_HGg * sunSky->atm_HGg)/fTemp; + + vec3opf(vTemp1, sunSky->atm_BetaDashRay, *, Phase_1); + vec3opf(vTemp2, sunSky->atm_BetaDashMie, *, Phase_2); + + vec3opv(vTemp1, vTemp1, +, vTemp2); + fopvec3(vTemp2, 1.0, -, E1); + vec3opv(vTemp1, vTemp1, *, vTemp2); + + fopvec3(vTemp2, 1.0, / , sunSky->atm_BetaRM); + + vec3opv(I, vTemp1, *, vTemp2); + + vec3opf(I, I, *, sunSky->atm_InscatteringMultiplier); + vec3opf(E, E, *, sunSky->atm_ExtinctionMultiplier); + + //scale to color sun + ComputeAttenuatedSunlight(sunSky->theta, sunSky->turbidity, sunColor); + vec3opv(E, E, *, sunColor); + + vec3opf(I, I, *, sunSky->atm_SunIntensity); + + vec3opv(rgb, rgb, *, E); + vec3opv(rgb, rgb, +, I); +} + +#undef vec3opv +#undef vec3opf +#undef fopvec3 + +/* EOF */ diff --git a/source/blender/render/intern/source/texture.c b/source/blender/render/intern/source/texture.c index b502fb2b421..7ce66ff6d12 100644 --- a/source/blender/render/intern/source/texture.c +++ b/source/blender/render/intern/source/texture.c @@ -53,6 +53,7 @@ #include "BKE_global.h" #include "BKE_main.h" +#include "BKE_material.h" #include "BKE_library.h" #include "BKE_image.h" @@ -98,7 +99,7 @@ void init_render_texture(Render *re, Tex *tex) } else if(tex->type==TEX_ENVMAP) { /* just in case */ - tex->imaflag= TEX_INTERPOL | TEX_MIPMAP; + tex->imaflag |= TEX_INTERPOL | TEX_MIPMAP; tex->extend= TEX_CLIP; if(tex->env) { @@ -720,30 +721,59 @@ static int plugintex(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex { PluginTex *pit; int rgbnor=0; + float result[ 8 ]; texres->tin= 0.0; pit= tex->plugin; if(pit && pit->doit) { if(texres->nor) { - VECCOPY(pit->result+5, texres->nor); + if (pit->version < 6) { + VECCOPY(pit->result+5, texres->nor); + } else { + VECCOPY(result+5, texres->nor); + } + } + if (pit->version < 6) { + if(osatex) rgbnor= ((TexDoitold)pit->doit)(tex->stype, + pit->data, texvec, dxt, dyt); + else rgbnor= ((TexDoitold)pit->doit)(tex->stype, + pit->data, texvec, 0, 0); + } else { + if(osatex) rgbnor= ((TexDoit)pit->doit)(tex->stype, + pit->data, texvec, dxt, dyt, result); + else rgbnor= ((TexDoit)pit->doit)(tex->stype, + pit->data, texvec, 0, 0, result); } - if(osatex) rgbnor= ((TexDoit)pit->doit)(tex->stype, pit->data, texvec, dxt, dyt); - else rgbnor= ((TexDoit)pit->doit)(tex->stype, pit->data, texvec, 0, 0); - texres->tin= pit->result[0]; + if (pit->version < 6) { + texres->tin = pit->result[0]; + } else { + texres->tin = result[0]; + } if(rgbnor & TEX_NOR) { if(texres->nor) { - VECCOPY(texres->nor, pit->result+5); + if (pit->version < 6) { + VECCOPY(texres->nor, pit->result+5); + } else { + VECCOPY(texres->nor, result+5); + } } } if(rgbnor & TEX_RGB) { - texres->tr= pit->result[1]; - texres->tg= pit->result[2]; - texres->tb= pit->result[3]; - texres->ta= pit->result[4]; + if (pit->version < 6) { + texres->tr = pit->result[1]; + texres->tg = pit->result[2]; + texres->tb = pit->result[3]; + texres->ta = pit->result[4]; + } else { + texres->tr = result[1]; + texres->tg = result[2]; + texres->tb = result[3]; + texres->ta = result[4]; + } BRICONTRGB; } @@ -1202,6 +1232,8 @@ static int multitex(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, return retval; } +/* Warning, if the texres's values are not declared zero, check the return value to be sure + * the color values are set before using the r/g/b values, otherwise you may use uninitialized values - Campbell */ int multitex_ext(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, TexResult *texres) { @@ -1255,7 +1287,7 @@ void texture_rgb_blend(float *in, float *tex, float *out, float fact, float facg in[1]= (fact*tex[1] + facm*out[1]); in[2]= (fact*tex[2] + facm*out[2]); break; - + case MTEX_MUL: fact*= facg; facm= 1.0-facg; @@ -1343,9 +1375,28 @@ void texture_rgb_blend(float *in, float *tex, float *out, float fact, float facg col= fact*tex[2]; if(col > out[2]) in[2]= col; else in[2]= out[2]; break; + + case MTEX_BLEND_HUE: + fact*= facg; + VECCOPY(in, out); + ramp_blend(MA_RAMP_HUE, in, in+1, in+2, fact, tex); + break; + case MTEX_BLEND_SAT: + fact*= facg; + VECCOPY(in, out); + ramp_blend(MA_RAMP_SAT, in, in+1, in+2, fact, tex); + break; + case MTEX_BLEND_VAL: + fact*= facg; + VECCOPY(in, out); + ramp_blend(MA_RAMP_VAL, in, in+1, in+2, fact, tex); + break; + case MTEX_BLEND_COLOR: + fact*= facg; + VECCOPY(in, out); + ramp_blend(MA_RAMP_COLOR, in, in+1, in+2, fact, tex); + break; } - - } float texture_value_blend(float tex, float out, float fact, float facg, int blendtype, int flip) @@ -1427,7 +1478,14 @@ void do_material_tex(ShadeInput *shi) /* which coords */ if(mtex->texco==TEXCO_ORCO) { - co= shi->lo; dx= shi->dxlo; dy= shi->dylo; + if(mtex->texflag & MTEX_DUPLI_MAPTO) { + co= shi->duplilo; dx= dxt; dy= dyt; + dxt[0]= dxt[1]= dxt[2]= 0.0f; + dyt[0]= dyt[1]= dyt[2]= 0.0f; + } + else { + co= shi->lo; dx= shi->dxlo; dy= shi->dylo; + } } else if(mtex->texco==TEXCO_STICKY) { co= shi->sticky; dx= shi->dxsticky; dy= shi->dysticky; @@ -1439,6 +1497,9 @@ void do_material_tex(ShadeInput *shi) dx= dxt; dy= dyt; VECCOPY(tempvec, shi->co); + if(mtex->texflag & MTEX_OB_DUPLI_ORIG) + if(shi->obi && shi->obi->duplitexmat) + MTC_Mat4MulVecfl(shi->obi->duplitexmat, tempvec); MTC_Mat4MulVecfl(ob->imat, tempvec); if(shi->osatex) { VECCOPY(dxt, shi->dxco); @@ -1466,28 +1527,35 @@ void do_material_tex(ShadeInput *shi) co= shi->gl; dx= shi->dxco; dy= shi->dyco; } else if(mtex->texco==TEXCO_UV) { - ShadeInputUV *suv= &shi->uv[shi->actuv]; - int i; - - if(mtex->uvname[0] != 0) { - for(i = 0; i < shi->totuv; i++) { - if(strcmp(shi->uv[i].name, mtex->uvname)==0) { - suv= &shi->uv[i]; - break; + if(mtex->texflag & MTEX_DUPLI_MAPTO) { + co= shi->dupliuv; dx= dxt; dy= dyt; + dxt[0]= dxt[1]= dxt[2]= 0.0f; + dyt[0]= dyt[1]= dyt[2]= 0.0f; + } + else { + ShadeInputUV *suv= &shi->uv[shi->actuv]; + int i; + + if(mtex->uvname[0] != 0) { + for(i = 0; i < shi->totuv; i++) { + if(strcmp(shi->uv[i].name, mtex->uvname)==0) { + suv= &shi->uv[i]; + break; + } } } - } - co= suv->uv; - dx= suv->dxuv; - dy= suv->dyuv; + co= suv->uv; + dx= suv->dxuv; + dy= suv->dyuv; + } } else if(mtex->texco==TEXCO_WINDOW) { co= shi->winco; dx= shi->dxwin; dy= shi->dywin; } else if(mtex->texco==TEXCO_STRAND) { co= tempvec; dx= dxt; dy= dyt; - co[0]= shi->strand; + co[0]= shi->strandco; co[1]= co[2]= 0.0f; dx[0]= shi->dxstrand; dx[1]= dx[2]= 0.0f; @@ -1644,18 +1712,18 @@ void do_material_tex(ShadeInput *shi) texres.nor[2]= texres.tb; } else { - float co= 0.5*cos(texres.tin-0.5); + float co_nor= 0.5*cos(texres.tin-0.5); float si= 0.5*sin(texres.tin-0.5); float f1, f2; f1= shi->vn[0]; f2= shi->vn[1]; - texres.nor[0]= f1*co+f2*si; - texres.nor[1]= f2*co-f1*si; + texres.nor[0]= f1*co_nor+f2*si; + texres.nor[1]= f2*co_nor-f1*si; f1= shi->vn[1]; f2= shi->vn[2]; - texres.nor[1]= f1*co+f2*si; - texres.nor[2]= f2*co-f1*si; + texres.nor[1]= f1*co_nor+f2*si; + texres.nor[2]= f2*co_nor-f1*si; } } // warping, local space @@ -1743,11 +1811,11 @@ void do_material_tex(ShadeInput *shi) if(mtex->normapspace == MTEX_NSPACE_TANGENT) { /* qdn: tangent space */ float B[3], tv[3]; - Crossf(B, shi->vn, shi->tang); /* bitangent */ + Crossf(B, shi->vn, shi->nmaptang); /* bitangent */ /* transform norvec from tangent space to object surface in camera space */ - tv[0] = texres.nor[0]*shi->tang[0] + texres.nor[1]*B[0] + texres.nor[2]*shi->vn[0]; - tv[1] = texres.nor[0]*shi->tang[1] + texres.nor[1]*B[1] + texres.nor[2]*shi->vn[1]; - tv[2] = texres.nor[0]*shi->tang[2] + texres.nor[1]*B[2] + texres.nor[2]*shi->vn[2]; + tv[0] = texres.nor[0]*shi->nmaptang[0] + texres.nor[1]*B[0] + texres.nor[2]*shi->vn[0]; + tv[1] = texres.nor[0]*shi->nmaptang[1] + texres.nor[1]*B[1] + texres.nor[2]*shi->vn[1]; + tv[2] = texres.nor[0]*shi->nmaptang[2] + texres.nor[1]*B[2] + texres.nor[2]*shi->vn[2]; shi->vn[0]= facm*shi->vn[0] + fact*tv[0]; shi->vn[1]= facm*shi->vn[1] + fact*tv[1]; shi->vn[2]= facm*shi->vn[2] + fact*tv[2]; @@ -1762,8 +1830,8 @@ void do_material_tex(ShadeInput *shi) Mat4Mul3Vecfl(R.viewmat, nor); } else if(mtex->normapspace == MTEX_NSPACE_OBJECT) { - if(shi->vlr && shi->vlr->obr->ob) - Mat4Mul3Vecfl(shi->vlr->obr->ob->obmat, nor); + if(shi->obr && shi->obr->ob) + Mat4Mul3Vecfl(shi->obr->ob->obmat, nor); Mat4Mul3Vecfl(R.viewmat, nor); } @@ -1776,24 +1844,24 @@ void do_material_tex(ShadeInput *shi) } } else { + float nor[3], dot; + if(shi->mat->mode & MA_TANGENT_V) { shi->tang[0]+= Tnor*tex->norfac*texres.nor[0]; shi->tang[1]+= Tnor*tex->norfac*texres.nor[1]; shi->tang[2]+= Tnor*tex->norfac*texres.nor[2]; } - else { - float nor[3], dot; - /* prevent bump to become negative normal */ - nor[0]= Tnor*tex->norfac*texres.nor[0]; - nor[1]= Tnor*tex->norfac*texres.nor[1]; - nor[2]= Tnor*tex->norfac*texres.nor[2]; - - dot= 0.5f + 0.5f*INPR(nor, shi->vn); - - shi->vn[0]+= dot*nor[0]; - shi->vn[1]+= dot*nor[1]; - shi->vn[2]+= dot*nor[2]; - } + + /* prevent bump to become negative normal */ + nor[0]= Tnor*tex->norfac*texres.nor[0]; + nor[1]= Tnor*tex->norfac*texres.nor[1]; + nor[2]= Tnor*tex->norfac*texres.nor[2]; + + dot= 0.5f + 0.5f*INPR(nor, shi->vn); + + shi->vn[0]+= dot*nor[0]; + shi->vn[1]+= dot*nor[1]; + shi->vn[2]+= dot*nor[2]; } Normalize(shi->vn); @@ -2233,7 +2301,7 @@ void do_sky_tex(float *rco, float *lo, float *dxyview, float *hor, float *zen, f /* ------------------------------------------------------------------------- */ /* colf supposed to be initialized with la->r,g,b */ -void do_lamp_tex(LampRen *la, float *lavec, ShadeInput *shi, float *colf) +void do_lamp_tex(LampRen *la, float *lavec, ShadeInput *shi, float *colf, int effect) { Object *ob; MTex *mtex; @@ -2372,7 +2440,7 @@ void do_lamp_tex(LampRen *la, float *lavec, ShadeInput *shi, float *colf) } /* mapping */ - if(mtex->mapto & LAMAP_COL) { + if(((mtex->mapto & LAMAP_COL) && (effect & LA_TEXTURE))||((mtex->mapto & LAMAP_SHAD) && (effect & LA_SHAD_TEX))) { float col[3]; if(rgb==0) { @@ -2450,21 +2518,31 @@ int externtex(MTex *mtex, float *vec, float *tin, float *tr, float *tg, float *t void render_realtime_texture(ShadeInput *shi, Image *ima) { TexResult texr; - static Tex tex1, tex2; // threadsafe + static Tex imatex[BLENDER_MAX_THREADS]; // threadsafe static int firsttime= 1; Tex *tex; float texvec[3], dx[2], dy[2]; ShadeInputUV *suv= &shi->uv[shi->actuv]; + int a; + + if(R.r.scemode & R_NO_TEX) return; if(firsttime) { - firsttime= 0; - default_tex(&tex1); - default_tex(&tex2); - tex1.type= TEX_IMAGE; - tex2.type= TEX_IMAGE; + BLI_lock_thread(LOCK_IMAGE); + if(firsttime) { + for(a=0; a<BLENDER_MAX_THREADS; a++) { + memset(&imatex[a], 0, sizeof(Tex)); + default_tex(&imatex[a]); + imatex[a].type= TEX_IMAGE; + } + + firsttime= 0; + } + BLI_unlock_thread(LOCK_IMAGE); } - if(shi->ys & 1) tex= &tex1; else tex= &tex2; // threadsafe + tex= &imatex[shi->thread]; + tex->iuser.ok= ima->ok; texvec[0]= 0.5+0.5*suv->uv[0]; texvec[1]= 0.5+0.5*suv->uv[1]; @@ -2480,7 +2558,7 @@ void render_realtime_texture(ShadeInput *shi, Image *ima) if(shi->osatex) imagewraposa(tex, ima, NULL, texvec, dx, dy, &texr); else imagewrap(tex, ima, NULL, texvec, &texr); - + shi->vcol[0]*= texr.tr; shi->vcol[1]*= texr.tg; shi->vcol[2]*= texr.tb; diff --git a/source/blender/render/intern/source/zbuf.c b/source/blender/render/intern/source/zbuf.c index 57d2bbb3489..509ac81c58b 100644 --- a/source/blender/render/intern/source/zbuf.c +++ b/source/blender/render/intern/source/zbuf.c @@ -196,7 +196,6 @@ static void zbuf_add_to_span(ZSpan *zspan, float *v1, float *v2) /* Functions */ /*-----------------------------------------------------------*/ - void fillrect(int *rect, int x, int y, int val) { int len, *drect; @@ -307,8 +306,8 @@ static void zbuffillAc4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v2, float x0,y0,z0; float x1,y1,z1,x2,y2,z2,xx1; float *span1, *span2; - int *rz, x, y; - int sn1, sn2, rectx, *rectzofs, my0, my2, mask; + int *rz, *rm, x, y; + int sn1, sn2, rectx, *rectzofs, *rectmaskofs, my0, my2, mask; /* init */ zbuf_init_span(zspan); @@ -353,6 +352,7 @@ static void zbuffillAc4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v2, /* start-offset in rect */ rectx= zspan->rectx; rectzofs= (int *)(zspan->arectz+rectx*(my2)); + rectmaskofs= (int *)(zspan->rectmask+rectx*(my2)); apofs= (zspan->apixbuf+ rectx*(my2)); mask= zspan->mask; @@ -377,35 +377,40 @@ static void zbuffillAc4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v2, 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) { - if( (int)zverg < *rz) { -// int i= zvlnr & 3; - - apn= ap; - while(apn) { - if(apn->p[0]==0) {apn->obi[0]= obi; apn->p[0]= zvlnr; apn->z[0]= zverg; 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]= zverg; 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]= zverg; 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]= zverg; apn->mask[3]= mask; break; } - if(apn->p[3]==zvlnr && apn->obi[3]==obi) {apn->mask[3]|= mask; break; } -// if(apn->p[i]==0) {apn->obi[i]= obi; apn->p[i]= zvlnr; apn->z[i]= zverg; apn->mask[i]= mask; break; } -// if(apn->p[i]==zvlnr && apn->obi[i]==obi) {apn->mask[i]|= mask; break; } - if(apn->next==NULL) apn->next= addpsA(zspan); - apn= apn->next; - } + 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--; } @@ -413,6 +418,7 @@ static void zbuffillAc4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v2, zy0-=zyd; rectzofs-= rectx; + rectmaskofs-= rectx; apofs-= rectx; } } @@ -422,7 +428,7 @@ static void zbuffillAc4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v2, static void zbuflineAc(ZSpan *zspan, int obi, int zvlnr, float *vec1, float *vec2) { APixstr *ap, *apn; - int *rectz; + int *rectz, *rectmask; int start, end, x, y, oldx, oldy, ofs; int dz, vergz, mask, maxtest=0; float dx, dy; @@ -459,37 +465,40 @@ static void zbuflineAc(ZSpan *zspan, int obi, int zvlnr, float *vec1, float *vec 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++, ap++) { + 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 && y<zspan->recty) { if(vergz<*rectz) { - - 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==0) apn->next= addpsA(zspan); - apn= apn->next; - } + 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==0) apn->next= addpsA(zspan); + apn= apn->next; + } + } } } @@ -527,37 +536,40 @@ static void zbuflineAc(ZSpan *zspan, int obi, int zvlnr, float *vec1, float *vec 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, ap+=zspan->rectx) { + 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 && x<zspan->rectx) { if(vergz<*rectz) { - - apn= ap; - while(apn) { /* loop unrolled */ - if(apn->p[0]==0) {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->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->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->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==0) apn->next= addpsA(zspan); - apn= apn->next; - } - + 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==0) apn->next= addpsA(zspan); + apn= apn->next; + } + } } } @@ -572,7 +584,7 @@ static void zbuflineAc(ZSpan *zspan, int obi, int zvlnr, float *vec1, float *vec static void zbufline(ZSpan *zspan, int obi, int zvlnr, float *vec1, float *vec2) { - int *rectz, *rectp, *recto; + int *rectz, *rectp, *recto, *rectmask; int start, end, x, y, oldx, oldy, ofs; int dz, vergz, maxtest= 0; float dx, dy; @@ -608,11 +620,12 @@ static void zbufline(ZSpan *zspan, int obi, int zvlnr, float *vec1, float *vec2) 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++) { + for(x= start; x<=end; x++, rectz++, rectp++, recto++, rectmask++) { y= floor(v1[1]); if(y!=oldy) { @@ -620,13 +633,16 @@ static void zbufline(ZSpan *zspan, int obi, int zvlnr, float *vec1, float *vec2) rectz+= ofs; rectp+= ofs; recto+= ofs; + rectmask+= ofs; } if(x>=0 && y>=0 && y<zspan->recty) { if(vergz<*rectz) { - *recto= obi; - *rectz= vergz; - *rectp= zvlnr; + if(!zspan->rectmask || vergz>*rectmask) { + *recto= obi; + *rectz= vergz; + *rectp= zvlnr; + } } } @@ -663,11 +679,12 @@ static void zbufline(ZSpan *zspan, int obi, int zvlnr, float *vec1, float *vec2) 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) { + 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) { @@ -675,13 +692,16 @@ static void zbufline(ZSpan *zspan, int obi, int zvlnr, float *vec1, float *vec2) rectz+= ofs; rectp+= ofs; recto+= ofs; + rectmask+= ofs; } if(x>=0 && y>=0 && x<zspan->rectx) { if(vergz<*rectz) { - *rectz= vergz; - *rectp= zvlnr; - *recto= obi; + if(!zspan->rectmask || vergz>*rectmask) { + *rectz= vergz; + *rectp= zvlnr; + *recto= obi; + } } } @@ -1022,6 +1042,7 @@ static void zbuffillGLinv4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v float *span1, *span2; int *rectoofs, *ro; int *rectpofs, *rp; + int *rectmaskofs, *rm; int *rz, x, y; int sn1, sn2, rectx, *rectzofs, my0, my2; @@ -1072,6 +1093,7 @@ static void zbuffillGLinv4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v 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; @@ -1094,22 +1116,30 @@ static void zbuffillGLinv4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v 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) { - if( (int)zverg > *rz || *rz==0x7FFFFFFF) { - *ro= obi; - *rz= (int)zverg; - *rp= zvlnr; + intzverg= (int)CLAMPIS(zverg, INT_MIN, INT_MAX); + + if( intzverg > *rz || *rz==0x7FFFFFFF) { + if(!zspan->rectmask || intzverg > *rm) { + *ro= obi; + *rz= intzverg; + *rp= zvlnr; + } } zverg+= zxd; rz++; rp++; ro++; + rm++; x--; } } @@ -1118,6 +1148,7 @@ static void zbuffillGLinv4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v rectzofs-= rectx; rectpofs-= rectx; rectoofs-= rectx; + rectmaskofs-= rectx; } } @@ -1131,6 +1162,7 @@ static void zbuffillGL4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v2, float *span1, *span2; int *rectoofs, *ro; int *rectpofs, *rp; + int *rectmaskofs, *rm; int *rz, x, y; int sn1, sn2, rectx, *rectzofs, my0, my2; @@ -1181,6 +1213,7 @@ static void zbuffillGL4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v2, 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; @@ -1203,22 +1236,30 @@ static void zbuffillGL4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v2, 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) { - if( (int)zverg < *rz) { - *rz= (int)zverg; - *rp= zvlnr; - *ro= obi; + intzverg= (int)CLAMPIS(zverg, INT_MIN, INT_MAX); + + if(intzverg < *rz) { + if(!zspan->rectmask || intzverg > *rm) { + *rz= intzverg; + *rp= zvlnr; + *ro= obi; + } } zverg+= zxd; rz++; rp++; ro++; + rm++; x--; } } @@ -1227,6 +1268,7 @@ static void zbuffillGL4(ZSpan *zspan, int obi, int zvlnr, float *v1, float *v2, rectzofs-= rectx; rectpofs-= rectx; rectoofs-= rectx; + rectmaskofs-= rectx; } } @@ -1326,7 +1368,8 @@ static void zbuffillGL_onlyZ(ZSpan *zspan, int obi, int zvlnr, float *v1, float x= sn2-sn1; while(x>=0) { - int zvergi= (int)zverg; + 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; @@ -1922,10 +1965,82 @@ void zbufclip4(ZSpan *zspan, int obi, int zvlnr, float *f1, float *f2, float *f3 zspan->zbuffunc(zspan, obi, zvlnr, vez, vez+4, vez+8, vez+12); } +/* ************** ZMASK ******************************** */ + +#define EXTEND_PIXEL(a) if(temprectp[a]) {z+= rectz[a]; tot++;} + +/* 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<xs; x++, curp++, curz++) { + if(curp[0]==0) { + int tot= 0; + float z= 0.0f; + + EXTEND_PIXEL(row1); + EXTEND_PIXEL(row2); + EXTEND_PIXEL(row3); + EXTEND_PIXEL(row1 + 1); + EXTEND_PIXEL(row3 + 1); + if(x!=xs-1) { + EXTEND_PIXEL(row1 + 2); + EXTEND_PIXEL(row2 + 2); + EXTEND_PIXEL(row3 + 2); + } + if(tot) { + len++; + curz[0]= (int)(z/(float)tot); + curp[0]= -1; /* env */ + } + } + + if(x!=0) { + row1++; row2++; row3++; + } + } + } + + MEM_freeN(temprectp); + + if(neg); /* z values for negative are already correct */ + else { + /* clear not filled z values */ + for(len= xs*ys -1; len>=0; len--) { + if(rectp[len]==0) { + rectz[len] = -0x7FFFFFFF; + rectp[len]= -1; /* env code */ + } + } + } +} + + + /* ***************** ZBUFFER MAIN ROUTINES **************** */ -void zbuffer_solid(RenderPart *pa, unsigned int lay, short layflag, void(*fillfunc)(RenderPart*, ZSpan*, int, void*), void *data) +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 */ @@ -1935,9 +2050,12 @@ void zbuffer_solid(RenderPart *pa, unsigned int lay, short layflag, void(*fillfu ObjectInstanceRen *obi; ObjectRen *obr; float winmat[4][4], bounds[4], 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, all_z= layflag & SCE_LAY_ALL_Z; - + 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); + samples= (R.osa? R.osa: 1); samples= MIN2(4, samples-pa->sample); @@ -1969,9 +2087,13 @@ void zbuffer_solid(RenderPart *pa, unsigned int lay, short layflag, void(*fillfu /* the buffers */ if(zsample == samples-1) { - zspan->rectz= pa->rectz; 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"); @@ -1982,104 +2104,153 @@ void zbuffer_solid(RenderPart *pa, unsigned int lay, short layflag, void(*fillfu fillrect(zspan->rectz, pa->rectx, pa->recty, 0x7FFFFFFF); fillrect(zspan->rectp, pa->rectx, pa->recty, 0); fillrect(zspan->recto, pa->rectx, pa->recty, 0); - - /* filling methods */ - zspan->zbuffunc= zbuffillGL4; - zspan->zbuflinefunc= zbufline; } - for(i=0, obi=R.instancetable.first; obi; i++, obi=obi->next) { - obr= obi->obr; + /* 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; - if(obi->flag & R_TRANSFORMED) - zbuf_make_winmat(&R, obi->mat, winmat); - else - zbuf_make_winmat(&R, NULL, winmat); + /* filling methods */ + for(zsample=0; zsample<samples; zsample++) { + zspan= &zspans[zsample]; - zbuf_project_cache_clear(cache, obr->totvert); + if(zmaskpass && neg_zmask) + zspan->zbuffunc= zbuffillGLinv4; + else + zspan->zbuffunc= zbuffillGL4; + zspan->zbuflinefunc= zbufline; + } - for(v=0; v<obr->totvlak; v++) { - if((v & 255)==0) vlr= obr->vlaknodes[v>>8].vlak; - else vlr++; + /* regular zbuffering loop, does all sample buffers */ + for(i=0, obi=R.instancetable.first; obi; i++, obi=obi->next) { + obr= obi->obr; - /* three cases, visible for render, only z values and nothing */ - if(vlr->lay & lay) { - if(vlr->mat!=ma) { - ma= vlr->mat; - nofill= ma->mode & (MA_ZTRA|MA_ONLYCAST); - env= (ma->mode & MA_ENV); - wire= (ma->mode & MA_WIRE); - - for(zsample=0; zsample<samples; zsample++) { - if(ma->mode & MA_ZINV) zspans[zsample].zbuffunc= zbuffillGLinv4; - else zspans[zsample].zbuffunc= zbuffillGL4; - } - } - } - else if(all_z) { - env= 1; - nofill= 0; - ma= NULL; - } - else { - nofill= 1; - ma= NULL; /* otherwise nofill can hang */ + /* 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) + zbuf_make_winmat(&R, obi->mat, winmat); + else + zbuf_make_winmat(&R, NULL, winmat); - 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, winmat, bounds, v1->co, ho1); - c2= zbuf_part_project(cache, v2->index, winmat, bounds, v2->co, ho2); - c3= zbuf_part_project(cache, v3->index, winmat, bounds, v3->co, ho3); - - /* partclipping doesn't need viewplane clipping */ - partclip= c1 & c2 & c3; - if(v4) { - c4= zbuf_part_project(cache, v4->index, winmat, bounds, v4->co, ho4); - partclip &= c4; + if(clip_render_object(obi->obr->boundbox, bounds, winmat)) + continue; + + zbuf_project_cache_clear(cache, obr->totvert); + + for(v=0; v<obr->totvlak; 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_ZTRA|MA_ONLYCAST); + env= (ma->mode & MA_ENV); + wire= (ma->mode & MA_WIRE); + + for(zsample=0; zsample<samples; zsample++) { + if(ma->mode & 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(partclip==0) { + if(!(vlr->flag & R_HIDDEN) && nofill==0) { + unsigned short partclip; - if(env) zvlnr= -1; - else zvlnr= v+1; + v1= vlr->v1; + v2= vlr->v2; + v3= vlr->v3; + v4= vlr->v4; + + c1= zbuf_part_project(cache, v1->index, winmat, bounds, v1->co, ho1); + c2= zbuf_part_project(cache, v2->index, winmat, bounds, v2->co, ho2); + c3= zbuf_part_project(cache, v3->index, winmat, bounds, v3->co, ho3); + + /* partclipping doesn't need viewplane clipping */ + partclip= c1 & c2 & c3; + if(v4) { + c4= zbuf_part_project(cache, v4->index, winmat, bounds, v4->co, ho4); + partclip &= c4; + } - c1= testclip(ho1); - c2= testclip(ho2); - c3= testclip(ho3); - if(v4) - c4= testclip(ho4); + if(partclip==0) { + + if(env) zvlnr= -1; + else zvlnr= v+1; - for(zsample=0; zsample<samples; zsample++) { - zspan= &zspans[zsample]; + c1= testclip(ho1); + c2= testclip(ho2); + c3= testclip(ho3); + if(v4) + c4= testclip(ho4); - if(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, 0, 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); + for(zsample=0; zsample<samples; zsample++) { + zspan= &zspans[zsample]; + + if(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, 0, c1, c2, c3, 0); } 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); + /* 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; zsample<samples; zsample++) { + zspan= &zspans[zsample]; + + if(neg_zmask) { + zspan->rectmask= 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; zsample<samples; zsample++) { @@ -2092,6 +2263,8 @@ void zbuffer_solid(RenderPart *pa, unsigned int lay, short layflag, void(*fillfu MEM_freeN(zspan->rectz); MEM_freeN(zspan->rectp); MEM_freeN(zspan->recto); + if(zspan->rectmask) + MEM_freeN(zspan->rectmask); } zbuf_free_span(zspan); @@ -2115,7 +2288,7 @@ static int hashlist_projectvert(float *v1, float winmat[][4], float *hoco) return 0; } - buck= &bucket[ (((long)v1)/16) & 255 ]; + buck= &bucket[ (((intptr_t)v1)/16) & 255 ]; if(buck->vert==v1) { QUATCOPY(hoco, buck->hoco); return buck->clip; @@ -2191,7 +2364,7 @@ void RE_zbufferall_radio(struct RadView *vw, RNode **rg_elem, int rg_totelem, Re else { /* radio render */ ObjectRen *obr; VlakRen *vlr=NULL; - RadFace *rf; + RadFace **radface, *rf; int totface=0; /* note: radio render doesn't support duplis */ @@ -2201,8 +2374,8 @@ void RE_zbufferall_radio(struct RadView *vw, RNode **rg_elem, int rg_totelem, Re for(a=0; a<obr->totvlak; a++) { if((a & 255)==0) vlr= obr->vlaknodes[a>>8].vlak; else vlr++; - if(vlr->radface) { - rf= vlr->radface; + if((radface=RE_vlakren_get_radface(obr, vlr, 0)) && *radface) { + rf= *radface; if( (rf->flag & RAD_SHOOT)==0 ) { /* no shootelement */ if( rf->flag & RAD_TWOSIDED) zvlnr= totface; @@ -2243,10 +2416,11 @@ void zbuffer_shadow(Render *re, float winmat[][4], LampRen *lar, int *rectz, int StrandSegment sseg; StrandRen *strand= NULL; StrandVert *svert; + StrandBound *sbound; float obwinmat[4][4], ho1[4], ho2[4], ho3[4], ho4[4]; - int a, b, i, c1, c2, c3, c4, ok=1, lay= -1; + int a, b, c, i, c1, c2, c3, c4, ok=1, lay= -1; - if(lar->mode & LA_LAYER) lay= lar->lay; + 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); @@ -2272,12 +2446,17 @@ void zbuffer_shadow(Render *re, float winmat[][4], LampRen *lar, int *rectz, int if(obr->ob==re->excludeob) continue; + else if(!(obi->lay & lay)) + continue; if(obi->flag & R_TRANSFORMED) Mat4MulMat4(obwinmat, obi->mat, winmat); else Mat4CpyMat4(obwinmat, winmat); + if(clip_render_object(obi->obr->boundbox, NULL, obwinmat)) + continue; + zbuf_project_cache_clear(cache, obr->totvert); /* faces */ @@ -2293,7 +2472,7 @@ void zbuffer_shadow(Render *re, float winmat[][4], LampRen *lar, int *rectz, int if((ma->mode & MA_SHADBUF)==0) ok= 0; } - if(ok && (vlr->lay & lay) && !(vlr->flag & R_HIDDEN)) { + 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); @@ -2321,45 +2500,54 @@ void zbuffer_shadow(Render *re, float winmat[][4], LampRen *lar, int *rectz, int } /* strands */ - for(a=0; a<obr->totstrand; a++) { - if((a & 255)==0) strand= obr->strandnodes[a>>8].strand; - else strand++; - - sseg.obi= obi; - sseg.buffer= strand->buffer; - sseg.sqadaptcos= sseg.buffer->adaptcos; - sseg.sqadaptcos *= sseg.sqadaptcos; - sseg.strand= strand; - svert= strand->vert; + if(obr->strandbuf) { + /* for each bounding box containing a number of strands */ + sbound= obr->strandbuf->bound; + for(c=0; c<obr->strandbuf->totbound; c++, sbound++) { + if(clip_render_object(sbound->boundbox, NULL, obwinmat)) + continue; + + /* for each strand in this bounding box */ + for(a=sbound->start; a<sbound->end; 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->mode & MA_SHADBUF)==0) ok= 0; + } - /* note, these conditions are copied in shadowbuf_autoclip() */ - if(sseg.buffer->ma!= ma) { - ma= sseg.buffer->ma; - ok= 1; - if((ma->mode & MA_SHADBUF)==0) ok= 0; - } + if(ok && (sseg.buffer->lay & lay)) { + zbuf_project_cache_clear(cache, strand->totvert); - if(ok && (sseg.buffer->lay & lay)) { - zbuf_project_cache_clear(cache, strand->totvert); + for(b=0; b<strand->totvert-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; - for(b=0; b<strand->totvert-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); - 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(!(c1 & c2 & c3 & c4)) - render_strand_segment(NULL, winmat, NULL, &zspan, &sseg); + if((a & 255)==255 && re->test_break()) + break; } } - - if((a & 255)==255 && re->test_break()) - break; } if(re->test_break()) @@ -2495,11 +2683,17 @@ void zbuffer_sss(RenderPart *pa, unsigned int lay, void *handle, void (*func)(vo 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) zbuf_make_winmat(&R, obi->mat, winmat); else zbuf_make_winmat(&R, NULL, winmat); + if(clip_render_object(obi->obr->boundbox, bounds, winmat)) + continue; + zbuf_project_cache_clear(cache, obr->totvert); for(v=0; v<obr->totvlak; v++) { @@ -2508,7 +2702,7 @@ void zbuffer_sss(RenderPart *pa, unsigned int lay, void *handle, void (*func)(vo if(material_in_material(vlr->mat, sss_ma)) { /* three cases, visible for render, only z values and nothing */ - if(vlr->lay & lay) { + if(obi->lay & lay) { if(vlr->mat!=ma) { ma= vlr->mat; nofill= ma->mode & MA_ONLYCAST; @@ -2762,14 +2956,46 @@ void antialias_tagbuf(int xsize, int ysize, char *rectmove) } } +/* 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 - .5*p1 - .5*p3 */ + p2[0]= -0.5*p1[0] - 0.5*p3[0]; + p2[1]= -0.5*p1[1] - 0.5*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[16][2]; + static float jit[256][2]; float v1[3], v2[3], v3[3], v4[3], fx, fy; - float *rectvz, *dvz, *dimg, *dvec1, *dvec2, *dz1, *dz2, *rectz, *minvecbufrect= NULL; - float maxspeedsq= (float)nbd->maxspeed*nbd->maxspeed; + 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, totfac; int y, x, step, maxspeed=nbd->maxspeed, samples= nbd->samples; int tsktsk= 0; static int firsttime= 1; @@ -2788,6 +3014,9 @@ void RE_zbuf_accumulate_vecblur(NodeBlurData *nbd, int xsize, int ysize, float * 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; @@ -2830,29 +3059,21 @@ void RE_zbuf_accumulate_vecblur(NodeBlurData *nbd, int xsize, int ysize, float * } /* make vertex buffer with averaged speed and zvalues */ - rectvz= MEM_mapallocN(5*sizeof(float)*(xsize+1)*(ysize+1), "vertices"); + rectvz= MEM_mapallocN(4*sizeof(float)*(xsize+1)*(ysize+1), "vertices"); dvz= rectvz; for(y=0; y<=ysize; y++) { - if(y==0) { + if(y==0) dvec1= vecbufrect + 4*y*xsize; - dz1= zbufrect + y*xsize; - } - else { + else dvec1= vecbufrect + 4*(y-1)*xsize; - dz1= zbufrect + (y-1)*xsize; - } - if(y==ysize) { + if(y==ysize) dvec2= vecbufrect + 4*(y-1)*xsize; - dz2= zbufrect + (y-1)*xsize; - } - else { + else dvec2= vecbufrect + 4*y*xsize; - dz2= zbufrect + y*xsize; - } - for(x=0; x<=xsize; x++, dz1++, dz2++) { + for(x=0; x<=xsize; x++) { /* two vectors, so a step loop */ for(step=0; step<2; step++, dvec1+=2, dvec2+=2, dvz+=2) { @@ -2910,30 +3131,21 @@ void RE_zbuf_accumulate_vecblur(NodeBlurData *nbd, int xsize, int ysize, float * } } } - /* the z coordinate */ - if(x!=0) { - if(x!=xsize) - dvz[0]= 0.25f*(dz1[-1] + dz2[-1] + dz1[0] + dz2[0]); - else dvz[0]= 0.5f*(dz1[0] + dz2[0]); - } - else dvz[0]= 0.5f*(dz1[-1] + dz2[-1]); - - dvz++; } } /* set border speeds to keep border speeds on border */ dz1= rectvz; - dz2= rectvz+5*(ysize)*(xsize+1); - for(x=0; x<=xsize; x++, dz1+=5, dz2+=5) { + 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+5*(xsize); - for(y=0; y<=ysize; y++, dz1+=5*(xsize+1), dz2+=5*(xsize+1)) { + 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; @@ -2944,7 +3156,7 @@ void RE_zbuf_accumulate_vecblur(NodeBlurData *nbd, int xsize, int ysize, float * 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) + if((dvec1[0]!=0.0f || dvec1[1]!=0.0f || dvec1[2]!=0.0f || dvec1[3]!=0.0f)) *dm= 255; } @@ -2953,17 +3165,20 @@ void RE_zbuf_accumulate_vecblur(NodeBlurData *nbd, int xsize, int ysize, float * /* has to become static, the init-jit calls a random-seed, screwing up texture noise node */ if(firsttime) { firsttime= 0; - BLI_initjit(jit[0], 16); + BLI_initjit(jit[0], 256); } + memset(newrect, 0, sizeof(float)*xsize*ysize*4); + totfac= 0.0f; + /* accumulate */ samples/= 2; for(step= 1; step<=samples; step++) { float speedfac= 0.5f*nbd->fac*(float)step/(float)(samples+1); - float blendfac= 1.0f/(ABS(step)+1); - int side, z= 4; + 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) @@ -2981,58 +3196,111 @@ void RE_zbuf_accumulate_vecblur(NodeBlurData *nbd, int xsize, int ysize, float * dimg= imgrect; dm= rectmove; + dz= zbufrect; dz1= rectvz; - dz2= rectvz + 5*(xsize + 1); + dz2= rectvz + 4*(xsize + 1); if(side) { - dz1+= 2; - dz2+= 2; - z= 2; + if(nbd->curved==0) { + dz1+= 2; + dz2+= 2; + } speedfac= -speedfac; } - - for(fy= -0.5f+jit[step & 15][0], y=0; y<ysize; y++, fy+=1.0f) { - for(fx= -0.5f+jit[step & 15][1], x=0; x<xsize; x++, fx+=1.0f, dimg+=4, dz1+=5, dz2+=5, dm++) { + + set_quad_bezier_ipo(0.5f + 0.5f*speedfac, ipodata); + + for(fy= -0.5f+jit[step & 255][0], y=0; y<ysize; y++, fy+=1.0f) { + for(fx= -0.5f+jit[step & 255][1], x=0; x<xsize; x++, fx+=1.0f, dimg+=4, dz1+=4, dz2+=4, dm++, dz++) { if(*dm>1) { + float jfx = fx + 0.5f; + float jfy = fy + 0.5f; DrawBufPixel col; /* make vertices */ - v1[0]= speedfac*dz1[0]+fx; v1[1]= speedfac*dz1[1]+fy; v1[2]= dz1[z]; - v2[0]= speedfac*dz1[5]+fx+1.0f; v2[1]= speedfac*dz1[6]+fy; v2[2]= dz1[z+5]; - v3[0]= speedfac*dz2[5]+fx+1.0f; v3[1]= speedfac*dz2[6]+fy+1.0f; v3[2]= dz2[z+5]; - v4[0]= speedfac*dz2[0]+fx; v4[1]= speedfac*dz2[1]+fy+1.0f; v4[2]= dz2[z]; - + 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+=5; - dz2+=5; + 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 */ - for(dr= rectdraw, dz2=newrect, x= xsize*ysize-1; x>=0; x--, dr++, dz2+=4) { + 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; - float mf= 1.0f - bfac; - dz2[0]= mf*dz2[0] + bfac*dr->colpoin[0]; - dz2[1]= mf*dz2[1] + bfac*dr->colpoin[1]; - dz2[2]= mf*dz2[2] + bfac*dr->colpoin[2]; - dz2[3]= mf*dz2[3] + bfac*dr->colpoin[3]; + 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); } @@ -3043,18 +3311,21 @@ void RE_zbuf_accumulate_vecblur(NodeBlurData *nbd, int xsize, int ysize, float * * Copy results from the solid face z buffering to the transparent * buffer. */ -static void copyto_abufz(RenderPart *pa, int *arectz, int sample) +static void copyto_abufz(RenderPart *pa, int *arectz, int *rectmask, int sample) { PixStr *ps; - int x, y, *rza; - long *rd; + int x, y, *rza, *rma; + intptr_t *rd; if(R.osa==0) { - memcpy(arectz, pa->rectz, 4*pa->rectx*pa->recty); + 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; } rza= arectz; + rma= rectmask; rd= pa->rectdaps; sample= (1<<sample); @@ -3063,17 +3334,19 @@ static void copyto_abufz(RenderPart *pa, int *arectz, int sample) for(x=0; x<pa->rectx; 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++; + rd++; rza++, rma++; } } } @@ -3085,7 +3358,7 @@ static void copyto_abufz(RenderPart *pa, int *arectz, int sample) * Do accumulation z buffering. */ -static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, unsigned int lay) +static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, RenderLayer *rl, unsigned int lay) { ZbufProjectCache cache[ZBUF_PROJECT_CACHE_SIZE]; ZSpan zspans[16], *zspan; /* MAX_OSA */ @@ -3116,11 +3389,14 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, un zspan->apixbuf= APixbuf; zspan->apsmbase= apsmbase; + if((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) + zspan->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "Arectmask"); + /* filling methods */ zspan->zbuffunc= zbuffillAc4; zspan->zbuflinefunc= zbuflineAc; - copyto_abufz(pa, zspan->arectz, zsample); /* init zbuffer */ + copyto_abufz(pa, zspan->arectz, zspan->rectmask, zsample); /* init zbuffer */ zspan->mask= 1<<zsample; if(R.osa) { @@ -3138,7 +3414,6 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, un /* to center the sample position */ zspan->zofsx -= 0.5f; zspan->zofsy -= 0.5f; - } /* we use this to test if nothing was filled in */ @@ -3147,11 +3422,17 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, un 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) zbuf_make_winmat(&R, obi->mat, winmat); else zbuf_make_winmat(&R, NULL, winmat); + if(clip_render_object(obi->obr->boundbox, bounds, winmat)) + continue; + zbuf_project_cache_clear(cache, obr->totvert); for(v=0; v<obr->totvlak; v++) { @@ -3165,7 +3446,7 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, un } if(dofill) { - if(!(vlr->flag & R_HIDDEN) && (vlr->lay & lay)) { + if(!(vlr->flag & R_HIDDEN) && (obi->lay & lay)) { unsigned short partclip; v1= vlr->v1; @@ -3243,6 +3524,8 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, un for(zsample=0; zsample<samples; zsample++) { zspan= &zspans[zsample]; MEM_freeN(zspan->arectz); + if(zspan->rectmask) + MEM_freeN(zspan->rectmask); zbuf_free_span(zspan); } @@ -3253,7 +3536,7 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, un /* 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 */ -void add_transp_speed(RenderLayer *rl, int offset, float *speed, float alpha, long *rdrect) +void add_transp_speed(RenderLayer *rl, int offset, float *speed, float alpha, intptr_t *rdrect) { RenderPass *rpass; @@ -3287,20 +3570,15 @@ void add_transp_speed(RenderLayer *rl, int offset, float *speed, float alpha, lo } } -static void add_transp_obindex(RenderLayer *rl, int offset, int obi, int facenr) +static void add_transp_obindex(RenderLayer *rl, int offset, Object *ob) { - ObjectRen *obr= R.objectinstance[obi].obr; - VlakRen *vlr= RE_findOrAddVlak(obr, (facenr-1) & RE_QUAD_MASK); - - if(vlr && obr->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)obr->ob->index; - break; - } + 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; } } } @@ -3315,7 +3593,7 @@ void merge_transp_passes(RenderLayer *rl, ShadeResult *shr) for(rpass= rl->passes.first; rpass; rpass= rpass->next) { float *col= NULL; - int pixsize= 0; + int pixsize= 3; switch(rpass->passtype) { case SCE_PASS_RGBA: @@ -3346,6 +3624,14 @@ void merge_transp_passes(RenderLayer *rl, ShadeResult *shr) 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: { @@ -3378,14 +3664,18 @@ void merge_transp_passes(RenderLayer *rl, ShadeResult *shr) for(samp= 1; samp<R.osa; samp++, fp+=delta) { col[0]+= fp[0]; - col[1]+= fp[1]; - col[2]+= fp[2]; - if(pixsize) col[3]+= fp[3]; + if(pixsize>1) { + col[1]+= fp[1]; + col[2]+= fp[2]; + if(pixsize==4) col[3]+= fp[3]; + } } col[0]*= weight; - col[1]*= weight; - col[2]*= weight; - if(pixsize) col[3]*= weight; + if(pixsize>1) { + col[1]*= weight; + col[2]*= weight; + if(pixsize==4) col[3]*= weight; + } } } @@ -3397,8 +3687,14 @@ void add_transp_passes(RenderLayer *rl, int offset, ShadeResult *shr, float alph 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); @@ -3427,23 +3723,30 @@ void add_transp_passes(RenderLayer *rl, int offset, ShadeResult *shr, float alph case SCE_PASS_NORMAL: col= shr->nor; break; + case SCE_PASS_MIST: + col= &shr->mist; + pixsize= 1; + break; } if(col) { - fp= rpass->rect + 3*offset; - fp[0]= alpha*col[0] + (1.0f-alpha)*fp[0]; - fp[1]= alpha*col[1] + (1.0f-alpha)*fp[1]; - fp[2]= alpha*col[2] + (1.0f-alpha)*fp[2]; + 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) @@ -3455,6 +3758,56 @@ static int vergzvlak(const void *a1, const void *a2) return 0; } +static void shade_strand_samples(StrandShadeCache *cache, ShadeSample *ssamp, int x, int 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, svert); + strand_shade_unref(cache, 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; @@ -3481,8 +3834,8 @@ static void shade_tra_samples_fill(ShadeSample *ssamp, int x, int y, int z, int shi++; } shi->mask= (1<<samp); - shi->samplenr= ssamp->samplenr++; - shade_input_set_viewco(shi, xs, ys, (float)z); + 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); @@ -3501,8 +3854,8 @@ static void shade_tra_samples_fill(ShadeSample *ssamp, int x, int y, int z, int ys= (float)y + 0.5f; } shi->mask= curmask; - shi->samplenr= ssamp->samplenr++; - shade_input_set_viewco(shi, xs, ys, (float)z); + 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); } @@ -3512,8 +3865,13 @@ static void shade_tra_samples_fill(ShadeSample *ssamp, int x, int y, int z, int } } -static int shade_tra_samples(ShadeSample *ssamp, int x, int y, ZTranspRow *row) +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) { @@ -3548,7 +3906,7 @@ static void addvecmul(float *v1, float *v2, float fac) v1[2]= v1[2]+fac*v2[2]; } -int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassflag) +static int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassflag) { int a, sample, osa = (R.osa? R.osa: 1), retval = osa; @@ -3563,6 +3921,8 @@ int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassflag) addAlphaUnderFloat(samp_shr->combined, shr->combined); + samp_shr->z= MIN2(samp_shr->z, shr->z); + if(addpassflag & SCE_PASS_VECTOR) { QUATCOPY(samp_shr->winspeed, shr->winspeed); } @@ -3595,6 +3955,10 @@ int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassflag) if(addpassflag & SCE_PASS_RADIO) addvecmul(samp_shr->rad, shr->rad, fac); + + if(addpassflag & SCE_PASS_MIST) + samp_shr->mist= samp_shr->mist+fac*shr->mist; + } } } @@ -3604,7 +3968,7 @@ int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassflag) return retval; } -void reset_sky_speedvectors(RenderPart *pa, RenderLayer *rl, float *rectf) +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 */ @@ -3627,23 +3991,27 @@ void reset_sky_speedvectors(RenderPart *pa, RenderLayer *rl, float *rectf) #define MAX_ZROW 2000 -/* main render call to fill in pass the full transparent layer */ +/* 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) +unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pass, ListBase *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]; - float sampalpha, *passrect= pass; - long *rdrect; - int x, y, crop=0, a, totface; - int addpassflag, offs= 0, od, addzbuf; - unsigned short *ztramask= NULL; - + 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()) return NULL; @@ -3655,14 +4023,14 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas } 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_Z|SCE_PASS_COMBINED); - if((rl->layflag & SCE_LAY_STRAND) && R.totstrand) - addzbuf= 1; /* strands layer needs the z-buffer */ - else - addzbuf= rl->passflag & SCE_PASS_Z; + addpassflag= rl->passflag & ~(SCE_PASS_COMBINED); if(R.osa) sampalpha= 1.0f/(float)R.osa; @@ -3670,28 +4038,42 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas sampalpha= 1.0f; /* fill the Apixbuf */ - if(0 == zbuffer_abuf(pa, APixbuf, &apsmbase, rl->lay)) { + doztra= 0; + if(rl->layflag & SCE_LAY_ZTRA) + doztra+= zbuffer_abuf(pa, APixbuf, &apsmbase, rl, rl->lay); + if((rl->layflag & SCE_LAY_STRAND) && APixbufstrand) + doztra+= zbuffer_strands_abuf(&R, pa, rl, APixbufstrand, &apsmbase, 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)) + 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); + 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) { @@ -3699,6 +4081,7 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas offs= pa->rectx + 1; passrect+= 4*offs; aprect+= offs; + aprectstrand+= offs; } /* init scanline updates */ @@ -3710,14 +4093,15 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas for(y=pa->disprect.ymin+crop; y<pa->disprect.ymax-crop; y++, rr->renrect.ymax++) { pass= passrect; ap= aprect; + apstrand= aprectstrand; od= offs; if(R.test_break()) break; - for(x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, ap++, pass+=4, od++) { + for(x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, ap++, apstrand++, pass+=4, od++) { - if(ap->p[0]==0) { + if(ap->p[0]==0 && (!APixbufstrand || apstrand->p[0]==0)) { if(addpassflag & SCE_PASS_VECTOR) add_transp_speed(rl, od, NULL, 0.0f, rdrect); } @@ -3732,6 +4116,7 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas 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; } @@ -3739,7 +4124,35 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas } 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; b<R.osa; b++) + if(zrow[totface].mask & (1<<b)) + totsample++; + } + else + totsample= 1; + + zrow[totface].u= apnstrand->u[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]); @@ -3750,70 +4163,110 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas qsort(zrow, totface, sizeof(ZTranspRow), vergzvlak); } - /* zbuffer and index pass for transparent, no AA or filters */ - if(addzbuf) - if(pa->rectz[od]>zrow[totface-1].z) - pa->rectz[od]= zrow[totface-1].z; - - if(addpassflag & SCE_PASS_INDEXOB) - add_transp_obindex(rl, od, zrow[totface-1].obi, zrow[totface-1].p); - + /* 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; a<totfullsample; a++) + add_transp_obindex(rlpp[a], od, obr->ob); + } + } + /* for each mask-sample we alpha-under colors. then in end it's added using filter */ + memset(samp_shr, 0, sizeof(ShadeResult)*osa); + for(a=0; a<osa; a++) { + samp_shr[a].z= 10e10f; + if(addpassflag & SCE_PASS_VECTOR) { + samp_shr[a].winspeed[0]= PASS_VECTOR_MAX; + samp_shr[a].winspeed[1]= PASS_VECTOR_MAX; + samp_shr[a].winspeed[2]= PASS_VECTOR_MAX; + samp_shr[a].winspeed[3]= PASS_VECTOR_MAX; + } + } + if(R.osa==0) { while(totface>0) { totface--; - if(shade_tra_samples(&ssamp, x, y, &zrow[totface])) { - if(addpassflag) - add_transp_passes(rl, od, ssamp.shr, (1.0f-pass[3])*ssamp.shr[0].combined[3]); - + 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(pass[3]>=0.999) break; + + if(filled == 0) { + if(sscache) + unref_strand_samples(sscache, zrow, totface); + break; + } } } - if(addpassflag & SCE_PASS_VECTOR) - add_transp_speed(rl, od, ssamp.shr[0].winspeed, pass[3], rdrect); + + 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 filled, *sp= (short *)(ztramask+od); - - /* for each mask-sample we alpha-under colors. then in end it's added using filter */ - memset(samp_shr, 0, sizeof(ShadeResult)*R.osa); - - /* nice this memset, but speed vectors are not initialized OK then. it is sufficient to only clear 1 (see merge_transp_passes) */ - if(addpassflag & SCE_PASS_VECTOR) - samp_shr->winspeed[0]= samp_shr->winspeed[1]= samp_shr->winspeed[2]= samp_shr->winspeed[3]= PASS_VECTOR_MAX; + short *sp= (short *)(ztramask+od); while(totface>0) { totface--; - if(shade_tra_samples(&ssamp, x, y, &zrow[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(filled==0) { + if(sscache) + unref_strand_samples(sscache, zrow, totface); break; + } } } - for(a=0; a<R.osa; a++) { - add_filt_fmask(1<<a, samp_shr[a].combined, pass, rr->rectx); + /* multisample buffers or filtered mask filling? */ + if(pa->fullresult.first) { + for(a=0; a<R.osa; a++) { + alpha= samp_shr[a].combined[3]; + if(alpha!=0.0f) { + RenderLayer *rl= ssamp.rlpp[a]; + + addAlphaOverFloat(rl->rectf + 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); + } + } } - - if(addpassflag) { - /* merge all in one, and then add */ - merge_transp_passes(rl, samp_shr); - add_transp_passes(rl, od, samp_shr, pass[3]); + else { + alpha= 0.0f; - if(addpassflag & SCE_PASS_VECTOR) - add_transp_speed(rl, od, samp_shr[0].winspeed, pass[3], rdrect); + /* note; cannot use pass[3] for alpha due to filtermask */ + for(a=0; a<R.osa; a++) { + add_filt_fmask(1<<a, samp_shr[a].combined, pass, rr->rectx); + 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; } @@ -3822,6 +4275,10 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas 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) @@ -3830,45 +4287,6 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas return ztramask; } -/* *************** */ - -/* uses part zbuffer values to convert into distances from camera in renderlayer */ -void convert_zbuf_to_distbuf(RenderPart *pa, RenderLayer *rl) -{ - RenderPass *rpass; - float *rectzf, zco; - int a, *rectz, ortho= R.r.mode & R_ORTHO; - - if(pa->rectz==NULL) return; - for(rpass= rl->passes.first; rpass; rpass= rpass->next) - if(rpass->passtype==SCE_PASS_Z) - break; - - if(rpass==NULL) { - printf("called convert zbuf wrong...\n"); - return; - } - - rectzf= rpass->rect; - rectz= pa->rectz; - - for(a=pa->rectx*pa->recty; a>0; a--, rectz++, rectzf++) { - if(*rectz>=0x7FFFFFF0) - *rectzf= 10e10; - else { - /* inverse of zbuf calc: zbuf = MAXZ*hoco_z/hoco_w */ - /* or: (R.winmat[3][2] - zco*R.winmat[3][3])/(R.winmat[2][2] - R.winmat[2][3]*zco); */ - /* if ortho [2][3] is zero, else [3][3] is zero */ - - zco= ((float)*rectz)/2147483647.0f; - if(ortho) - *rectzf= (R.winmat[3][2] - zco*R.winmat[3][3])/(R.winmat[2][2]); - else - *rectzf= (R.winmat[3][2])/(R.winmat[2][2] - R.winmat[2][3]*zco); - } - } -} - /* end of zbuf.c */ |