From 8e94028ed57861595e959d30476a74c48868a991 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 29 Jan 2008 12:20:42 +0000 Subject: [PATCH] Bounding box clipping in the render engine. Now bounding boxes are computed per object, and checked first before zbuffering objects. For strands, bounding boxes are computed per original face in the mesh. Overall the speed improvement from this is quite small (zbuffering is rarely the bottleneck), but it seems a sensible thing to do anyway. --- .../blenkernel/intern/particle_system.c | 22 +++++ .../render/intern/include/render_types.h | 11 ++- .../render/intern/include/renderdatabase.h | 1 + source/blender/render/intern/include/strand.h | 1 + .../render/intern/source/convertblender.c | 61 +++++++++++- .../render/intern/source/renderdatabase.c | 27 ++--- source/blender/render/intern/source/strand.c | 98 +++++++++++-------- source/blender/render/intern/source/zbuf.c | 87 +++++++++------- 8 files changed, 211 insertions(+), 97 deletions(-) diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 3db42f091d4..c0ffb7c62f8 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -780,6 +780,21 @@ void *exec_distribution(void *data) return 0; } +/* not thread safe, but qsort doesn't take userdata argument */ +static int *COMPARE_ORIG_INDEX = NULL; +static int compare_orig_index(const void *p1, const void *p2) +{ + int index1 = COMPARE_ORIG_INDEX[*(const int*)p1]; + int index2 = COMPARE_ORIG_INDEX[*(const int*)p2]; + + if(index1 < index2) + return -1; + else if(index1 == index2) + return 0; + else + return 1; +} + /* creates a distribution of coordinates on a DerivedMesh */ /* */ /* 1. lets check from what we are emitting */ @@ -1157,6 +1172,13 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm MEM_freeN(sum); + /* for hair, sort by origindex, allows optimizations in rendering */ + if(part->type == PART_HAIR) { + COMPARE_ORIG_INDEX= dm->getFaceDataArray(dm, CD_ORIGINDEX); + if(COMPARE_ORIG_INDEX) + qsort(index, totpart, sizeof(int), compare_orig_index); + } + /* weights are no longer used except for FROM_PARTICLE, which needs them zeroed for indexing */ if(from==PART_FROM_PARTICLE){ for(i=0; itotchild; 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 */ @@ -1653,6 +1656,18 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem 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; atotbound= 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; } } } @@ -1802,6 +1817,13 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem cache = psys->childcache[a-totpart]; max_k = (int)cache->steps; } + + 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 */ @@ -1845,6 +1867,8 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem } } } + + sbound->end++; } /* strandco computation setup */ @@ -3841,6 +3865,11 @@ static void check_non_flat_quads(ObjectRen *obr) 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, @@ -3858,6 +3887,36 @@ static void finalize_render_object(Render *re, ObjectRen *obr, int timeoffset) check_non_flat_quads(obr); set_fullsample_flag(re, obr); + + /* compute bounding boxes for clipping */ + INIT_MINMAX(min, max); + for(a=0; atotvert; 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; bstrandbuf->totbound; b++, sbound++) { + INIT_MINMAX(smin, smax); + + for(a=sbound->start; aend; 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); } } } @@ -4674,7 +4733,7 @@ static void calculate_speedvectors(Render *re, ObjectInstanceRen *obi, float *ve } if(obr->strandnodes) { - strandbuf= obr->strandbufs.first; + strandbuf= obr->strandbuf; mesh= (strandbuf)? strandbuf->surface: NULL; /* compute speed vectors at surface vertices */ diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c index 9495360f089..5a749515416 100644 --- a/source/blender/render/intern/source/renderdatabase.c +++ b/source/blender/render/intern/source/renderdatabase.c @@ -711,7 +711,7 @@ StrandBuffer *RE_addStrandBuffer(ObjectRen *obr, int totvert) strandbuf->totvert= totvert; strandbuf->obr= obr; - BLI_addtail(&obr->strandbufs, strandbuf); + obr->strandbuf= strandbuf; return strandbuf; } @@ -842,9 +842,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); @@ -1342,13 +1345,11 @@ void RE_makeRenderInstances(Render *re) re->instancetable= newlist; } -#if 0 -int clip_render_object(ObjectInstanceRen *obi, float *bounds, float winmat[][4]) +int clip_render_object(float boundbox[][3], float *bounds, float winmat[][4]) { - float mat[4][4], vec[4], max, min, (*boundbox)[3]; + float mat[4][4], vec[4]; int a, fl, flag= -1; - boundbox= obi->obr->boundbox; Mat4CpyMat4(mat, winmat); for(a=0; a<8; a++) { @@ -1374,22 +1375,10 @@ int clip_render_object(ObjectInstanceRen *obi, float *bounds, float winmat[][4]) if(vec[2] < -vec[3]) fl |= 16; if(vec[2] > vec[3]) fl |= 32; -#if 0 - max= vec[3]; - min= -vec[3]; - - wco= ho[3]; - if(vec[0] < min) fl |= 1; - if(vec[0] > max) fl |= 2; - if(vec[1] < min) fl |= 4; - if(vec[1] > max) fl |= 8; -#endif - flag &= fl; if(flag==0) return 0; } return flag; } -#endif diff --git a/source/blender/render/intern/source/strand.c b/source/blender/render/intern/source/strand.c index 91b3dfbe621..e66d12de15b 100644 --- a/source/blender/render/intern/source/strand.c +++ b/source/blender/render/intern/source/strand.c @@ -750,12 +750,13 @@ int zbuffer_strands_abuf(Render *re, RenderPart *pa, RenderLayer *rl, APixstrand 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, totsegment, clip[4]; + int a, b, c, i, totsegment, clip[4]; if(re->test_break()) return 0; @@ -800,58 +801,66 @@ int zbuffer_strands_abuf(Render *re, RenderPart *pa, RenderLayer *rl, APixstrand sortseg= sortsegments; totsegment= 0; + /* 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; atotstrand; 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(!(strand->buffer->lay & rl->lay)) + /* for each bounding box containing a number of strands */ + sbound= obr->strandbuf->bound; + for(c=0; cstrandbuf->totbound; c++, sbound++) { + if(clip_render_object(sbound->boundbox, bounds, winmat)) continue; - svert= strand->vert; + /* for each strand in this bounding box */ + for(a=sbound->start; aend; a++) { + strand= RE_findOrAddStrand(obr, a); + 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; btotvert-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 */ + /* 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]; - clip[1]= clip[2]; z[1]= z[2]; - clip[2]= clip[3]; z[2]= z[3]; + + for(b=0; btotvert-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]; + } } } } @@ -973,3 +982,12 @@ void free_strand_surface(Render *re) BLI_freelistN(&re->strandsurface); } +void strand_minmax(StrandRen *strand, float *min, float *max) +{ + StrandVert *svert; + int a; + + for(a=0, svert=strand->vert; atotvert; a++, svert++) + DO_MINMAX(svert->co, min, max) +} + diff --git a/source/blender/render/intern/source/zbuf.c b/source/blender/render/intern/source/zbuf.c index 21c31d55e87..080a66e46a7 100644 --- a/source/blender/render/intern/source/zbuf.c +++ b/source/blender/render/intern/source/zbuf.c @@ -2081,6 +2081,9 @@ void zbuffer_solid(RenderPart *pa, RenderLayer *rl, void(*fillfunc)(RenderPart*, 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; vtotvlak; v++) { @@ -2337,8 +2340,9 @@ 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; @@ -2374,6 +2378,9 @@ void zbuffer_shadow(Render *re, float winmat[][4], LampRen *lar, int *rectz, int else Mat4CpyMat4(obwinmat, winmat); + if(clip_render_object(obi->obr->boundbox, NULL, obwinmat)) + continue; + zbuf_project_cache_clear(cache, obr->totvert); /* faces */ @@ -2417,45 +2424,54 @@ void zbuffer_shadow(Render *re, float winmat[][4], LampRen *lar, int *rectz, int } /* strands */ - for(a=0; atotstrand; a++) { - if((a & 255)==0) strand= obr->strandnodes[a>>8].strand; - else strand++; + if(obr->strandbuf) { + /* for each bounding box containing a number of strands */ + sbound= obr->strandbuf->bound; + for(c=0; cstrandbuf->totbound; c++, sbound++) { + if(clip_render_object(sbound->boundbox, NULL, obwinmat)) + continue; - sseg.obi= obi; - sseg.buffer= strand->buffer; - sseg.sqadaptcos= sseg.buffer->adaptcos; - sseg.sqadaptcos *= sseg.sqadaptcos; - sseg.strand= strand; - svert= strand->vert; + /* for each strand in this bounding box */ + for(a=sbound->start; aend; a++) { + strand= RE_findOrAddStrand(obr, a); - /* 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; - } + sseg.obi= obi; + sseg.buffer= strand->buffer; + sseg.sqadaptcos= sseg.buffer->adaptcos; + sseg.sqadaptcos *= sseg.sqadaptcos; + sseg.strand= strand; + svert= strand->vert; - if(ok && (sseg.buffer->lay & lay)) { - zbuf_project_cache_clear(cache, strand->totvert); + /* 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; + } - for(b=0; btotvert-1; b++, svert++) { - sseg.v[0]= (b > 0)? (svert-1): svert; - sseg.v[1]= svert; - sseg.v[2]= svert+1; - sseg.v[3]= (b < strand->totvert-2)? svert+2: svert+1; + if(ok && (sseg.buffer->lay & lay)) { + zbuf_project_cache_clear(cache, strand->totvert); - 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); + for(b=0; btotvert-1; b++, svert++) { + sseg.v[0]= (b > 0)? (svert-1): svert; + sseg.v[1]= svert; + sseg.v[2]= svert+1; + sseg.v[3]= (b < strand->totvert-2)? svert+2: svert+1; - if(!(c1 & c2 & c3 & c4)) - render_strand_segment(re, winmat, NULL, &zspan, 1, &sseg); + c1= zbuf_shadow_project(cache, sseg.v[0]-strand->vert, obwinmat, sseg.v[0]->co, ho1); + c2= zbuf_shadow_project(cache, sseg.v[1]-strand->vert, obwinmat, sseg.v[1]->co, ho2); + c3= zbuf_shadow_project(cache, sseg.v[2]-strand->vert, obwinmat, sseg.v[2]->co, ho3); + c4= zbuf_shadow_project(cache, sseg.v[3]-strand->vert, obwinmat, sseg.v[3]->co, ho4); + + if(!(c1 & c2 & c3 & c4)) + render_strand_segment(re, winmat, NULL, &zspan, 1, &sseg); + } + } + + if((a & 255)==255 && re->test_break()) + break; } } - - if((a & 255)==255 && re->test_break()) - break; } if(re->test_break()) @@ -2599,6 +2615,9 @@ void zbuffer_sss(RenderPart *pa, unsigned int lay, void *handle, void (*func)(vo 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; vtotvlak; v++) { @@ -3286,7 +3305,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 */ @@ -3303,6 +3321,9 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, un 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; vtotvlak; v++) {