diff options
author | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2007-12-04 16:57:28 +0300 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2007-12-04 16:57:28 +0300 |
commit | 32a8b4f8e686938ec2f0f95e6acc2eb3c25ebfdf (patch) | |
tree | ff232d89da0b2f0db1da4becb1980af513ea2274 | |
parent | ebfedd20b290e356f35eb1fcbcb5ddfb8e8bf0a9 (diff) |
Particles
=========
- Fix crash in particle transform with the particle system not editable.
- Particle child distribution and caching is now multithreaded.
- Child particles now have a separate Render Amount next to the existing
Amount. The render amount particles are now only distributed and cached
at render time, which should make editing with child particles faster.
- Two new options for diffuse strand shading:
- Surface Diffuse: computes the strand normal taking the normal at
the surface into account.
- Blending Distance: the distance in Blender units over which to
blend in the normal at the surface.
- Special strand rendering for more memory efficient and faster hair and
grass. This is a work in progress, and has a number of known issues,
don't report bugs to me for this feature yet.
More info:
http://www.blender.org/development/current-projects/changes-since-244/particles/
35 files changed, 3479 insertions, 832 deletions
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index afed219dbc6..d419eac2fab 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -54,6 +54,7 @@ struct MVert; struct IpoCurve; struct LinkNode; struct KDTree; +struct RNG; typedef struct ParticleEffectorCache { struct ParticleEffectorCache *next, *prev; @@ -148,6 +149,42 @@ typedef struct ParticleEdit{ int totkeys; } ParticleEdit; +typedef struct ParticleThreadContext { + /* shared */ + struct Object *ob; + struct DerivedMesh *dm; + struct ParticleSystemModifierData *psmd; + struct ParticleSystem *psys; + struct Material *ma; + + /* distribution */ + struct KDTree *tree; + + struct ParticleSeam *seams; + int totseam; + + float *jit, *jitoff, *weight; + float maxweight; + int *index, jitlevel; + + int from, cfrom, distr; + + /* path caching */ + int editupdate, between, steps; + int totchild, totparent; + + float cfra; + + float *vg_length, *vg_clump, *vg_kink; + float *vg_rough1, *vg_rough2, *vg_roughe; +} ParticleThreadContext; + +typedef struct ParticleThread { + ParticleThreadContext *ctx; + struct RNG *rng, *rng_path; + int num, tot; +} ParticleThread; + /* ----------- functions needed outside particlesystem ---------------- */ /* particle.c */ int count_particles(struct ParticleSystem *psys); @@ -170,6 +207,7 @@ int psys_in_edit_mode(struct ParticleSystem *psys); void psys_free_settings(struct ParticleSettings *part); void free_child_path_cache(struct ParticleSystem *psys); void psys_free_path_cache(struct ParticleSystem *psys); +void psys_free_render_memory(struct Object *ob, struct ParticleSystem *psys); void free_hair(struct ParticleSystem *psys); void free_keyed_keys(struct ParticleSystem *psys); void psys_free(struct Object * ob, struct ParticleSystem * psys); @@ -195,11 +233,19 @@ void psys_cache_child_paths(struct Object *ob, struct ParticleSystem *psys, floa int do_guide(struct ParticleKey *state, int pa_num, float time, struct ListBase *lb); float psys_get_size(struct Object *ob, struct Material *ma, struct ParticleSystemModifierData *psmd, struct IpoCurve *icu_size, struct ParticleSystem *psys, struct ParticleSettings *part, struct ParticleData *pa, float *vg_size); float psys_get_timestep(struct ParticleSettings *part); -float psys_get_child_time(struct ParticleSystem *psys, int child_nbr, float cfra); -float psys_get_child_size(struct ParticleSystem *psys, int child_nbr, float cfra, float *pa_time); +float psys_get_child_time(struct ParticleSystem *psys, struct ChildParticle *cpa, float cfra); +float psys_get_child_size(struct ParticleSystem *psys, struct ChildParticle *cpa, float cfra, float *pa_time); void psys_get_particle_on_path(struct Object *ob, struct ParticleSystem *psys, int pa_num, struct ParticleKey *state, int vel); int psys_get_particle_state(struct Object *ob, struct ParticleSystem *psys, int p, struct ParticleKey *state, int always); +ParticleThread *psys_threads_create(struct Object *ob, struct ParticleSystem *psys, int totthread); +int psys_threads_init_distribution(ParticleThread *threads, struct DerivedMesh *dm, int from); +int psys_threads_init_path(ParticleThread *threads, float cfra, int editupdate); +void psys_threads_free(ParticleThread *threads); + +void psys_thread_distribute_particle(ParticleThread *thread, struct ParticleData *pa, struct ChildParticle *cpa, int p); +void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, ParticleCacheKey *keys, int i); + /* particle_system.c */ int psys_count_keyed_targets(struct Object *ob, struct ParticleSystem *psys); void psys_get_reactor_target(struct Object *ob, struct ParticleSystem *psys, struct Object **target_ob, struct ParticleSystem **target_psys); diff --git a/source/blender/blenkernel/intern/anim.c b/source/blender/blenkernel/intern/anim.c index ab17366163f..4aebd099885 100644 --- a/source/blender/blenkernel/intern/anim.c +++ b/source/blender/blenkernel/intern/anim.c @@ -640,7 +640,7 @@ static void new_particle_duplilist(ListBase *lb, Scene *sce, Object *par, Partic pa_num = a; pa_time = psys->particles[psys->child[a - totpart].parent].time; - size=psys_get_child_size(psys, a - totpart, ctime, 0); + size=psys_get_child_size(psys, &psys->child[a - totpart], ctime, 0); } if(part->draw_as==PART_DRAW_GR) { @@ -783,7 +783,7 @@ ListBase *object_duplilist(Scene *sce, Object *ob) { ListBase *duplilist= MEM_mallocN(sizeof(ListBase), "duplilist"); duplilist->first= duplilist->last= NULL; - + if(ob->transflag & OB_DUPLI) { if(ob->transflag & OB_DUPLIPARTS) { ParticleSystem *psys = ob->particlesystem.first; @@ -808,7 +808,7 @@ ListBase *object_duplilist(Scene *sce, Object *ob) DupliObject *dob; group_duplilist(duplilist, ob, 0); /* now recursive */ - + /* make copy already, because in group dupli's deform displists can be makde, requiring parent matrices */ for(dob= duplilist->first; dob; dob= dob->next) Mat4CpyMat4(dob->ob->obmat, dob->mat); diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 0699e3b8655..42cac46c241 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -687,6 +687,9 @@ static void do_init_render_material(Material *ma, int r_mode, float *amb) /* will become or-ed result of all node modes */ ma->mode_l= ma->mode; ma->mode_l &= ~MA_SHLESS; + + if(ma->strand_surfnor > 0.0f) + ma->mode_l |= MA_STR_SURFDIFF; } static void init_render_nodetree(bNodeTree *ntree, Material *basemat, int r_mode, float *amb) diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 4fbe714d45c..f85efd23ad6 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -56,6 +56,7 @@ #include "BLI_kdtree.h" #include "BLI_linklist.h" #include "BLI_rand.h" +#include "BLI_threads.h" #include "BKE_anim.h" @@ -299,6 +300,27 @@ void psys_free_path_cache(ParticleSystem *psys) } free_child_path_cache(psys); } +void psys_free_render_memory(Object *ob, ParticleSystem *psys) +{ + ParticleSystemModifierData *psmd; + + /* this is a bad function, but saves a lot of memory rendering. + * particles should really be generated on the fly with render + * settings! */ + psys_free_path_cache(psys); + + if(psys->child){ + MEM_freeN(psys->child); + psys->child=0; + psys->totchild=0; + } + + psmd= psys_get_modifier(ob, psys); + psmd->flag &= ~eParticleSystemFlag_psys_updated; + + psys->recalc |= PSYS_ALLOC|PSYS_DISTR; + //DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); +} /* free everything */ void psys_free(Object *ob, ParticleSystem * psys) { @@ -1195,41 +1217,33 @@ static void do_rough_end(float *loc, float t, float fac, float shape, ParticleKe VECADD(state->co,state->co,rough); } -static int check_path_length(int k, int p, ParticleCacheKey **cache, ParticleCacheKey *state, float length, float *dvec) +static int check_path_length(int k, ParticleCacheKey *keys, ParticleCacheKey *state, float max_length, float *cur_length, float length, float *dvec) { - static float max_length = 1.0, cur_length = 0.0; - - if(k) { - if(cur_length + length > max_length){ - //if(p<totparent){ - // if(k<=(int)cache[totpart+p]->time){ - // /* parents need to be calculated fully first so that they don't mess up their children */ - // /* we'll make a note of where we got to though so that they're easy to finish later */ - // state->time=(max_length-cur_length)/length; - // cache[totpart+p]->time=(float)k; - // } - //} - //else{ - VecMulf(dvec, (max_length - cur_length) / length); - VECADD(state->co, (state - 1)->co, dvec); - cache[p]->steps = k; - /* something over the maximum step value */ - return k=100000; - //} - } - else { - cur_length+=length; - } + if(*cur_length + length > max_length){ + //if(p<totparent){ + // if(k<=(int)cache[totpart+p]->time){ + // /* parents need to be calculated fully first so that they don't mess up their children */ + // /* we'll make a note of where we got to though so that they're easy to finish later */ + // state->time=(max_length-*cur_length)/length; + // cache[totpart+p]->time=(float)k; + // } + //} + //else{ + VecMulf(dvec, (max_length - *cur_length) / length); + VECADD(state->co, (state - 1)->co, dvec); + keys->steps = k; + /* something over the maximum step value */ + return k=100000; + //} } - else {/* reset signal */ - max_length=length; - cur_length=0.0; + else { + *cur_length+=length; + return k; } - return k; } -static void finalize_path_length(int p, ParticleCacheKey **cache) +static void finalize_path_length(ParticleCacheKey *keys) { - ParticleCacheKey *state = cache[p]; + ParticleCacheKey *state = keys; float dvec[3]; state += state->steps; @@ -1301,42 +1315,54 @@ void psys_find_parents(Object *ob, ParticleSystemModifierData *psmd, ParticleSys if(orcos) MEM_freeN(orcos); } -void psys_cache_child_paths(Object *ob, ParticleSystem *psys, float cfra, int editupdate) + +static void get_strand_normal(Material *ma, float *surfnor, float surfdist, float *nor) { - ParticleSettings *part = psys->part; - ParticleEditSettings *pset = &G.scene->toolsettings->particle; - ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys); - ParticleData *pa; - ChildParticle *cpa; - ParticleCacheKey **cache = psys->childcache, **pcache = psys->pathcache; - ParticleCacheKey *tcache, *state, *par=0, *key[4]; - ParticleTexture ptex; - Material *ma = give_current_material(ob, part->omat); + float cross[3], nstrand[3], vnor[3], blend; - float length, pa_length = 1.0, pa_clump = 1.0, pa_kink = 1.0; - float pa_rough1 = 1.0, pa_rough2 = 1.0, pa_roughe = 1.0; - float t, rough_t; - float dvec[3], orco[3], ornor[3], imat[4][4]; - float *vg_length = 0, *vg_clump = 0, *vg_kink = 0; - float *vg_rough1 = 0, *vg_rough2 = 0, *vg_roughe = 0; - float cpa_1st[3]; + if(!((ma->mode & MA_STR_SURFDIFF) || (ma->strand_surfnor > 0.0f))) + return; - int k, i, totparent=0, between=0, edit=0; - int steps = (int)pow(2.0,(double)part->draw_step); - int totchild = psys->totchild; - int cpa_num; short cpa_from; + if(ma->mode & MA_STR_SURFDIFF) { + Crossf(cross, surfnor, nor); + Crossf(nstrand, nor, cross); + + blend= INPR(nstrand, surfnor); + CLAMP(blend, 0.0f, 1.0f); - if(part->flag & PART_ANIM_BRANCHING) - BLI_srandom(31415926 + psys->seed + (int)cfra); + VecLerpf(vnor, nstrand, surfnor, blend); + Normalize(vnor); + } else - BLI_srandom(31415926 + psys->seed); + VECCOPY(vnor, nor) + + if(ma->strand_surfnor > 0.0f) { + if(ma->strand_surfnor > surfdist) { + blend= (ma->strand_surfnor - surfdist)/ma->strand_surfnor; + VecLerpf(vnor, vnor, surfnor, blend); + Normalize(vnor); + } + } + + VECCOPY(nor, vnor); +} + +int psys_threads_init_path(ParticleThread *threads, float cfra, int editupdate) +{ + ParticleThreadContext *ctx= threads[0].ctx; + Object *ob= ctx->ob; + ParticleSystem *psys= ctx->psys; + ParticleSettings *part = psys->part; + ParticleEditSettings *pset = &G.scene->toolsettings->particle; + int totparent=0, between=0; + int steps = (int)pow(2.0,(double)part->draw_step); + int totchild = psys->totchild; + int i, seed, totthread= threads[0].tot; /*---start figuring out what is actually wanted---*/ - if(psys_in_edit_mode(psys)){ + if(psys_in_edit_mode(psys)) if(G.rendering==0 && (psys->edit==NULL || pset->flag & PE_SHOW_CHILD)==0) totchild=0; - edit=1; - } if(totchild && part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ totparent=(int)(totchild*part->parents*0.3); @@ -1346,39 +1372,44 @@ void psys_cache_child_paths(Object *ob, ParticleSystem *psys, float cfra, int ed if(G.rendering) steps=(int)pow(2.0,(double)part->ren_step); - else if(part->flag & PART_CHILD_RENDER){ - totchild=0; - } else{ totchild=(int)((float)totchild*(float)part->disp/100.0f); totparent=MIN2(totparent,totchild); } - if(totchild==0) return; + if(totchild==0) return 0; - if(editupdate && psys->childcache && !(part->flag & PART_BRANCHING) && totchild == psys->totchildcache) { - cache = psys->childcache; + /* init random number generator */ + if(ctx->psys->part->flag & PART_ANIM_BRANCHING) + seed= 31415926 + ctx->psys->seed + (int)cfra; + else + seed= 31415926 + ctx->psys->seed; + + if(part->flag & PART_BRANCHING || ctx->editupdate || totchild < 10000) + totthread= 1; + + for(i=0; i<totthread; i++) { + threads[i].rng_path= rng_new(seed); + threads[i].tot= totthread; } - else { - /* clear out old and create new empty path cache */ - free_child_path_cache(psys); - cache = psys->childcache = MEM_callocN(totchild*sizeof(void *), "Child path cache array"); - tcache = MEM_callocN(totchild * (steps + 1) * sizeof(ParticleCacheKey), "Child path cache"); - for(i=0; i<totchild; i++) - cache[i] = tcache + i * (steps + 1); - } + /* fill context values */ + ctx->between= between; + ctx->steps= steps; + ctx->totchild= totchild; + ctx->totparent= totparent; + ctx->cfra= cfra; - psys->lattice = psys_get_lattice(ob,psys); + psys->lattice = psys_get_lattice(ob, psys); /* cache all relevant vertex groups if they exist */ if(part->from!=PART_FROM_PARTICLE){ - vg_length = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_LENGTH); - vg_clump = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_CLUMP); - vg_kink = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_KINK); - vg_rough1 = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROUGH1); - vg_rough2 = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROUGH2); - vg_roughe = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROUGHE); + ctx->vg_length = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_LENGTH); + ctx->vg_clump = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_CLUMP); + ctx->vg_kink = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_KINK); + ctx->vg_rough1 = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_ROUGH1); + ctx->vg_rough2 = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_ROUGH2); + ctx->vg_roughe = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_ROUGHE); } /* set correct ipo timing */ @@ -1387,294 +1418,373 @@ void psys_cache_child_paths(Object *ob, ParticleSystem *psys, float cfra, int ed execute_ipo((ID *)part, part->ipo); } - Mat4Invert(imat,ob->obmat); - - for(i=0,cpa=psys->child; i<totchild; i++, cpa++){ - int guided=0; - float *cpa_fuv=0; - float branch_begin=0.0f, branch_end=0.0f, branch_prob=0.0f; - float branchfac, rough_rand=0.0f; - - if(part->flag & PART_BRANCHING) { - branch_begin=BLI_frand(); - branch_end=branch_begin+(1.0f-branch_begin)*BLI_frand(); - branch_prob=BLI_frand(); - rough_rand=BLI_frand(); - } - - if(i<psys->totpart){ - branch_begin=0.0f; - branch_end=1.0f; - branch_prob=0.0f; - } + return 1; +} - if(between){ - int w, needupdate; - float foffset; +/* note: this function must be thread safe, except for branching! */ +void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, ParticleCacheKey *keys, int i) +{ + ParticleThreadContext *ctx= thread->ctx; + Object *ob= ctx->ob; + ParticleSystem *psys = ctx->psys; + ParticleSettings *part = psys->part; + ParticleCacheKey **cache= psys->childcache; + ParticleCacheKey **pcache= psys->pathcache; + ParticleCacheKey *state, *par = NULL, *key[4]; + ParticleData *pa; + ParticleTexture ptex; + float *cpa_fuv=0; + float orco[3], ornor[3], t, rough_t, cpa_1st[3], dvec[3]; + float branch_begin, branch_end, branch_prob, branchfac, rough_rand; + float pa_rough1, pa_rough2, pa_roughe, length, pa_length, pa_clump, pa_kink; + float max_length = 1.0f, cur_length = 0.0f; + int k, cpa_num, guided=0; + short cpa_from; + + if(part->flag & PART_BRANCHING) { + branch_begin=rng_getFloat(thread->rng_path); + branch_end=branch_begin+(1.0f-branch_begin)*rng_getFloat(thread->rng_path); + branch_prob=rng_getFloat(thread->rng_path); + rough_rand=rng_getFloat(thread->rng_path); + } + else { + branch_begin= 0.0f; + branch_end= 0.0f; + branch_prob= 0.0f; + rough_rand= 0.0f; + } - if(editupdate && !(part->flag & PART_BRANCHING)) { - needupdate= 0; - w= 0; - while(w<4 && cpa->pa[w]>=0) { - if(psys->particles[cpa->pa[w]].flag & PARS_EDIT_RECALC) { - needupdate= 1; - break; - } - w++; - } + if(i<psys->totpart){ + branch_begin=0.0f; + branch_end=1.0f; + branch_prob=0.0f; + } - if(!needupdate) - continue; - else - memset(cache[i], 0, sizeof(*cache[i])*(steps+1)); - } + if(ctx->between){ + int w, needupdate; + float foffset; - /* get parent paths */ + if(ctx->editupdate && !(part->flag & PART_BRANCHING)) { + needupdate= 0; w= 0; - while(w<4 && cpa->pa[w]>=0){ - key[w] = pcache[cpa->pa[w]]; + while(w<4 && cpa->pa[w]>=0) { + if(psys->particles[cpa->pa[w]].flag & PARS_EDIT_RECALC) { + needupdate= 1; + break; + } w++; } - /* get the original coordinates (orco) for texture usage */ - cpa_num = cpa->num; - - foffset= cpa->foffset; - if(part->childtype == PART_CHILD_FACES) - foffset = -(2.0f + part->childspread); - cpa_fuv = cpa->fuv; - cpa_from = PART_FROM_FACE; + if(!needupdate) + return; + else + memset(keys, 0, sizeof(*keys)*(ctx->steps+1)); + } + + /* get parent paths */ + w= 0; + while(w<4 && cpa->pa[w]>=0){ + key[w] = pcache[cpa->pa[w]]; + w++; + } - psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa->fuv,foffset,orco,ornor,0,0); + /* get the original coordinates (orco) for texture usage */ + cpa_num = cpa->num; + + foffset= cpa->foffset; + if(part->childtype == PART_CHILD_FACES) + foffset = -(2.0f + part->childspread); + cpa_fuv = cpa->fuv; + cpa_from = PART_FROM_FACE; - /* we need to save the actual root position of the child for positioning it accurately to the surface of the emitter */ - VECCOPY(cpa_1st,orco); - Mat4MulVecfl(ob->obmat,cpa_1st); + psys_particle_on_emitter(ob,ctx->psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa->fuv,foffset,orco,ornor,0,0); - pa=0; + /* we need to save the actual root position of the child for positioning it accurately to the surface of the emitter */ + VECCOPY(cpa_1st,orco); + Mat4MulVecfl(ob->obmat,cpa_1st); + + pa=0; + } + else{ + if(ctx->editupdate && !(part->flag & PART_BRANCHING)) { + if(!(psys->particles[cpa->parent].flag & PARS_EDIT_RECALC)) + return; + + memset(keys, 0, sizeof(*keys)*(ctx->steps+1)); } - else{ - if(editupdate && !(part->flag & PART_BRANCHING)) { - if(!(psys->particles[cpa->parent].flag & PARS_EDIT_RECALC)) - continue; - memset(cache[i], 0, sizeof(*cache[i])*(steps+1)); - } + /* get the parent path */ + key[0]=pcache[cpa->parent]; - /* get the parent path */ - key[0]=pcache[cpa->parent]; + /* get the original coordinates (orco) for texture usage */ + pa=psys->particles+cpa->parent; - /* get the original coordinates (orco) for texture usage */ - pa=psys->particles+cpa->parent; + cpa_from=part->from; + cpa_num=pa->num; + cpa_fuv=pa->fuv; - cpa_from=part->from; - cpa_num=pa->num; - cpa_fuv=pa->fuv; + psys_particle_on_emitter(ob,ctx->psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,orco,ornor,0,0); + } - psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,orco,ornor,0,0); - } + keys->steps = ctx->steps; - cache[i]->steps = steps; + /* correct child ipo timing */ + if((part->flag&PART_ABS_TIME)==0 && part->ipo){ + float dsta=part->end-part->sta; + calc_ipo(part->ipo, 100.0f*(ctx->cfra-(part->sta+dsta*cpa->rand[1]))/(part->lifetime*(1.0f - part->randlife*cpa->rand[0]))); + execute_ipo((ID *)part, part->ipo); + } - /* correct child ipo timing */ - if((part->flag&PART_ABS_TIME)==0 && part->ipo){ - float dsta=part->end-part->sta; - calc_ipo(part->ipo, 100.0f*(cfra-(part->sta+dsta*cpa->rand[1]))/(part->lifetime*(1.0f - part->randlife*cpa->rand[0]))); - execute_ipo((ID *)part, part->ipo); - } + /* get different child parameters from textures & vgroups */ + ptex.length=part->length*(1.0f - part->randlength*cpa->rand[0]); + ptex.clump=1.0; + ptex.kink=1.0; - /* get different child parameters from textures & vgroups */ - ptex.length=part->length*(1.0f - part->randlength*cpa->rand[0]); - ptex.clump=1.0; - ptex.kink=1.0; + get_cpa_texture(ctx->dm,ctx->ma,cpa_num,cpa_fuv,orco,&ptex,MAP_PA_CACHE); + + pa_length=ptex.length; + pa_clump=ptex.clump; + pa_kink=ptex.kink; + pa_rough1=1.0; + pa_rough2=1.0; + pa_roughe=1.0; + + if(ctx->vg_length) + pa_length*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_length); + if(ctx->vg_clump) + pa_clump*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_clump); + if(ctx->vg_kink) + pa_kink*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_kink); + if(ctx->vg_rough1) + pa_rough1*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_rough1); + if(ctx->vg_rough2) + pa_rough2*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_rough2); + if(ctx->vg_roughe) + pa_roughe*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_roughe); + + /* create the child path */ + for(k=0,state=keys; k<=ctx->steps; k++,state++){ + t=(float)k/(float)ctx->steps; + + if(ctx->between){ + int w=0; - get_cpa_texture(psmd->dm,ma,cpa_num,cpa_fuv,orco,&ptex,MAP_PA_CACHE); - - pa_length=ptex.length; - pa_clump=ptex.clump; - pa_kink=ptex.kink; - pa_rough1=1.0; - pa_rough2=1.0; - pa_roughe=1.0; - - if(vg_length) - pa_length*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_length); - if(vg_clump) - pa_clump*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_clump); - if(vg_kink) - pa_kink*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_kink); - if(vg_rough1) - pa_rough1*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_rough1); - if(vg_rough2) - pa_rough2*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_rough2); - if(vg_roughe) - pa_roughe*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_roughe); - - /* create the child path */ - for(k=0,state=cache[i]; k<=steps; k++,state++){ - t=(float)k/(float)steps; - - if(between){ - int w=0; - - state->co[0] = state->co[1] = state->co[2] = 0.0f; - state->vel[0] = state->vel[1] = state->vel[2] = 0.0f; - - //QUATCOPY(state->rot,key[0]->rot); - - /* child position is the weighted sum of parent positions */ - while(w<4 && cpa->pa[w]>=0){ - state->co[0] += cpa->w[w] * key[w]->co[0]; - state->co[1] += cpa->w[w] * key[w]->co[1]; - state->co[2] += cpa->w[w] * key[w]->co[2]; - - state->vel[0] += cpa->w[w] * key[w]->vel[0]; - state->vel[1] += cpa->w[w] * key[w]->vel[1]; - state->vel[2] += cpa->w[w] * key[w]->vel[2]; - key[w]++; - w++; - } - if(k==0){ - /* calculate the offset between actual child root position and first position interpolated from parents */ - VECSUB(cpa_1st,cpa_1st,state->co); - } - /* apply offset for correct positioning */ - VECADD(state->co,state->co,cpa_1st); - } - else{ - /* offset the child from the parent position */ - offset_child(cpa, (ParticleKey*)key[0], (ParticleKey*)state, part->childflat, part->childrad); + state->co[0] = state->co[1] = state->co[2] = 0.0f; + state->vel[0] = state->vel[1] = state->vel[2] = 0.0f; - key[0]++; - } + //QUATCOPY(state->rot,key[0]->rot); - if(totparent){ - if(i>=totparent) - par = cache[cpa->parent] + k; - else - par=0; + /* child position is the weighted sum of parent positions */ + while(w<4 && cpa->pa[w]>=0){ + state->co[0] += cpa->w[w] * key[w]->co[0]; + state->co[1] += cpa->w[w] * key[w]->co[1]; + state->co[2] += cpa->w[w] * key[w]->co[2]; + + state->vel[0] += cpa->w[w] * key[w]->vel[0]; + state->vel[1] += cpa->w[w] * key[w]->vel[1]; + state->vel[2] += cpa->w[w] * key[w]->vel[2]; + key[w]++; + w++; } - else if(cpa->parent>=0){ - par=pcache[cpa->parent]+k; + if(k==0){ + /* calculate the offset between actual child root position and first position interpolated from parents */ + VECSUB(cpa_1st,cpa_1st,state->co); } + /* apply offset for correct positioning */ + VECADD(state->co,state->co,cpa_1st); + } + else{ + /* offset the child from the parent position */ + offset_child(cpa, (ParticleKey*)key[0], (ParticleKey*)state, part->childflat, part->childrad); - /* apply different deformations to the child path */ - if(part->flag & PART_CHILD_GUIDE) - guided = do_guide((ParticleKey*)state, i, t, &(psys->effectors)); //safe to cast, since only co and vel are used + key[0]++; + } - if(guided==0){ - if(part->kink) - do_prekink((ParticleKey*)state, (ParticleKey*)par, par->rot, t, - part->kink_freq * pa_kink, part->kink_shape, part->kink_amp, part->kink, part->kink_axis, ob->obmat); - - do_clump((ParticleKey*)state, (ParticleKey*)par, t, part->clumpfac, part->clumppow, pa_clump); + if(ctx->totparent){ + if(i>=ctx->totparent) + /* this is not threadsafe, but should only happen for + * branching particles particles, which are not threaded */ + par = cache[cpa->parent] + k; + else + par=0; + } + else if(cpa->parent>=0){ + par=pcache[cpa->parent]+k; + } - if(part->kink) - do_postkink((ParticleKey*)state, (ParticleKey*)par, par->rot, t, - part->kink_freq * pa_kink, part->kink_shape, part->kink_amp, part->kink, part->kink_axis, ob->obmat); - } + /* apply different deformations to the child path */ + if(part->flag & PART_CHILD_GUIDE) + guided = do_guide((ParticleKey*)state, i, t, &(psys->effectors)); //safe to cast, since only co and vel are used + + if(guided==0){ + if(part->kink) + do_prekink((ParticleKey*)state, (ParticleKey*)par, par->rot, t, + part->kink_freq * pa_kink, part->kink_shape, part->kink_amp, part->kink, part->kink_axis, ob->obmat); + + do_clump((ParticleKey*)state, (ParticleKey*)par, t, part->clumpfac, part->clumppow, pa_clump); + + if(part->kink) + do_postkink((ParticleKey*)state, (ParticleKey*)par, par->rot, t, + part->kink_freq * pa_kink, part->kink_shape, part->kink_amp, part->kink, part->kink_axis, ob->obmat); + } - if(part->flag & PART_BRANCHING && between == 0 && part->flag & PART_ANIM_BRANCHING) - rough_t = t * rough_rand; - else - rough_t = t; + if(part->flag & PART_BRANCHING && ctx->between == 0 && part->flag & PART_ANIM_BRANCHING) + rough_t = t * rough_rand; + else + rough_t = t; + + if(part->rough1 != 0.0 && pa_rough1 != 0.0) + do_rough(orco, rough_t, pa_rough1*part->rough1, part->rough1_size, 0.0, (ParticleKey*)state); - if(part->rough1 != 0.0 && pa_rough1 != 0.0) - do_rough(orco, rough_t, pa_rough1*part->rough1, part->rough1_size, 0.0, (ParticleKey*)state); + if(part->rough2 != 0.0 && pa_rough2 != 0.0) + do_rough(cpa->rand, rough_t, pa_rough2*part->rough2, part->rough2_size, part->rough2_thres, (ParticleKey*)state); - if(part->rough2 != 0.0 && pa_rough2 != 0.0) - do_rough(cpa->rand, rough_t, pa_rough2*part->rough2, part->rough2_size, part->rough2_thres, (ParticleKey*)state); + if(part->rough_end != 0.0 && pa_roughe != 0.0) + do_rough_end(cpa->rand, rough_t, pa_roughe*part->rough_end, part->rough_end_shape, (ParticleKey*)state, (ParticleKey*)par); - if(part->rough_end != 0.0 && pa_roughe != 0.0) - do_rough_end(cpa->rand, rough_t, pa_roughe*part->rough_end, part->rough_end_shape, (ParticleKey*)state, (ParticleKey*)par); + if(part->flag & PART_BRANCHING && ctx->between==0){ + if(branch_prob > part->branch_thres){ + branchfac=0.0f; + } + else{ + if(part->flag & PART_SYMM_BRANCHING){ + if(t < branch_begin || t > branch_end) + branchfac=0.0f; + else{ + if((t-branch_begin)/(branch_end-branch_begin)<0.5) + branchfac=2.0f*(t-branch_begin)/(branch_end-branch_begin); + else + branchfac=2.0f*(branch_end-t)/(branch_end-branch_begin); - if(part->flag & PART_BRANCHING && between==0){ - if(branch_prob > part->branch_thres){ - branchfac=0.0f; + CLAMP(branchfac,0.0f,1.0f); + } } else{ - if(part->flag & PART_SYMM_BRANCHING){ - if(t < branch_begin || t > branch_end) - branchfac=0.0f; - else{ - if((t-branch_begin)/(branch_end-branch_begin)<0.5) - branchfac=2.0f*(t-branch_begin)/(branch_end-branch_begin); - else - branchfac=2.0f*(branch_end-t)/(branch_end-branch_begin); - - CLAMP(branchfac,0.0f,1.0f); - } + if(t < branch_begin){ + branchfac=0.0f; } else{ - if(t < branch_begin){ - branchfac=0.0f; - } - else{ - branchfac=(t-branch_begin)/((1.0f-branch_begin)*0.5f); - CLAMP(branchfac,0.0f,1.0f); - } + branchfac=(t-branch_begin)/((1.0f-branch_begin)*0.5f); + CLAMP(branchfac,0.0f,1.0f); } } - - if(i<psys->totpart){ - VecLerpf(state->co, (pcache[i] + k)->co, state->co, branchfac); - } - else - VecLerpf(state->co, (cache[i - psys->totpart] + k)->co, state->co, branchfac); } - /* we have to correct velocity because of kink & clump */ - if(k>1){ - VECSUB((state-1)->vel,state->co,(state-2)->co); - VecMulf((state-1)->vel,0.5); - } - - /* check if path needs to be cut before actual end of data points */ - if(k){ - VECSUB(dvec,state->co,(state-1)->co); - if(part->flag&PART_ABS_LENGTH) - length=VecLength(dvec); - else - length=1.0f/(float)steps; + if(i<psys->totpart) + VecLerpf(state->co, (pcache[i] + k)->co, state->co, branchfac); + else + /* this is not threadsafe, but should only happen for + * branching particles particles, which are not threaded */ + VecLerpf(state->co, (cache[i - psys->totpart] + k)->co, state->co, branchfac); + } - k=check_path_length(k,i,cache,state,length,dvec); - } - else{ - /* initialize length calculation */ - if(part->flag&PART_ABS_LENGTH) - check_path_length(0,0,0,0,part->abslength*pa_length,0); - else - check_path_length(0,0,0,0,pa_length,0); - } + /* we have to correct velocity because of kink & clump */ + if(k>1){ + VECSUB((state-1)->vel,state->co,(state-2)->co); + VecMulf((state-1)->vel,0.5); if(part->draw & PART_DRAW_MAT_COL) - VECCOPY(state->col, &ma->r) + get_strand_normal(ctx->ma, ornor, cur_length, (state-1)->vel); + } + + /* check if path needs to be cut before actual end of data points */ + if(k){ + VECSUB(dvec,state->co,(state-1)->co); + if(part->flag&PART_ABS_LENGTH) + length=VecLength(dvec); + else + length=1.0f/(float)ctx->steps; + + k=check_path_length(k,keys,state,max_length,&cur_length,length,dvec); + } + else{ + /* initialize length calculation */ + if(part->flag&PART_ABS_LENGTH) + max_length= part->abslength*pa_length; + else + max_length= pa_length; + + cur_length= 0.0f; + } + + if(part->draw & PART_DRAW_MAT_COL) { + VECCOPY(state->col, &ctx->ma->r) + get_strand_normal(ctx->ma, ornor, cur_length, state->vel); } } + /* now let's finalise the interpolated parents that we might have left half done before */ - if(totchild) for(i=0,cpa=psys->child; i<totparent; i++, cpa++) - finalize_path_length(i,cache); - - if(vg_length) - MEM_freeN(vg_length); - if(vg_clump) - MEM_freeN(vg_clump); - if(vg_kink) - MEM_freeN(vg_kink); - if(vg_rough1) - MEM_freeN(vg_rough1); - if(vg_rough2) - MEM_freeN(vg_roughe); - if(vg_roughe) - MEM_freeN(vg_roughe); - - psys->totchildcache = totchild; - - if(psys->lattice){ - end_latt_deform(); - psys->lattice=0; + if(i<ctx->totparent) + finalize_path_length(keys); +} + +void *exec_child_path_cache(void *data) +{ + ParticleThread *thread= (ParticleThread*)data; + ParticleThreadContext *ctx= thread->ctx; + ParticleSystem *psys= ctx->psys; + ParticleCacheKey **cache= psys->childcache; + ChildParticle *cpa; + int i, totchild= ctx->totchild; + + cpa= psys->child + thread->num; + for(i=thread->num; i<totchild; i+=thread->tot, cpa+=thread->tot) + psys_thread_create_path(thread, cpa, cache[i], i); + + return 0; +} + +void psys_cache_child_paths(Object *ob, ParticleSystem *psys, float cfra, int editupdate) +{ + ParticleSettings *part = psys->part; + ParticleThread *pthreads; + ParticleThreadContext *ctx; + ParticleCacheKey **cache, *tcache; + ListBase threads; + int i, totchild, totparent, totthread; + + pthreads= psys_threads_create(ob, psys, G.scene->r.threads); + + if(!psys_threads_init_path(pthreads, cfra, editupdate)) { + psys_threads_free(pthreads); + return; + } + + ctx= pthreads[0].ctx; + totchild= ctx->totchild; + totparent= ctx->totparent; + + if(editupdate && psys->childcache && !(part->flag & PART_BRANCHING) && totchild == psys->totchildcache) { + cache = psys->childcache; + } + else { + /* clear out old and create new empty path cache */ + free_child_path_cache(psys); + + cache = psys->childcache = MEM_callocN(totchild*sizeof(void *), "Child path cache array"); + tcache = MEM_callocN(totchild * (ctx->steps + 1) * sizeof(ParticleCacheKey), "Child path cache"); + for(i=0; i<totchild; i++) + cache[i] = tcache + i * (ctx->steps + 1); + + psys->totchildcache = totchild; + } + + totthread= pthreads[0].tot; + + if(totthread > 1) { + BLI_init_threads(&threads, exec_child_path_cache, totthread); + + for(i=0; i<totthread; i++) + BLI_insert_thread(&threads, &pthreads[i]); + + BLI_end_threads(&threads); } + else + exec_child_path_cache(&pthreads[0]); + + psys_threads_free(pthreads); } + /* Calculates paths ready for drawing/rendering. */ /* -Usefull for making use of opengl vertex arrays for super fast strand drawing. */ /* -Makes child strands possible and creates them too into the cache. */ @@ -2260,6 +2370,7 @@ static void default_particle_settings(ParticleSettings *part) part->childsize=1.0; part->child_nbr=10; + part->ren_child_nbr=100; part->childrad=0.2f; part->childflat=0.0f; part->clumppow=0.0f; @@ -2491,12 +2602,14 @@ void psys_get_texture(Object *ob, Material *ma, ParticleSystemModifierData *psmd else //psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->fuv,pa->foffset,texco,0,0,0); /* <jahka> anyways I think it will be too small a difference to notice, so psys_get_texture should only know about the original mesh structure.. no dm needed anywhere */ - psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,-1,pa->fuv,pa->foffset,texco,0,0,0); + /* <brecht> the code only does dm based lookup now, so passing num_dmcache anyway to avoid^ + * massive slowdown here */ + psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,texco,0,0,0); } else{ //psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->fuv,pa->offset,texco,0,0,0); /* ditto above */ - psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,-1,pa->fuv,pa->foffset,texco,0,0,0); + psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,texco,0,0,0); } externtex(mtex, texco, &value, rgba, rgba+1, rgba+2, rgba+3); @@ -2566,10 +2679,9 @@ float psys_get_size(Object *ob, Material *ma, ParticleSystemModifierData *psmd, return size*part->size; } -float psys_get_child_time(ParticleSystem *psys, int child_nbr, float cfra) +float psys_get_child_time(ParticleSystem *psys, ChildParticle *cpa, float cfra) { ParticleSettings *part = psys->part; - ChildParticle *cpa=psys->child+child_nbr; if(part->childtype==PART_CHILD_FACES){ float time; @@ -2587,17 +2699,16 @@ float psys_get_child_time(ParticleSystem *psys, int child_nbr, float cfra) return (cfra-pa->time)/pa->lifetime; } } -float psys_get_child_size(ParticleSystem *psys, int child_nbr, float cfra, float *pa_time) +float psys_get_child_size(ParticleSystem *psys, ChildParticle *cpa, float cfra, float *pa_time) { ParticleSettings *part = psys->part; - ChildParticle *cpa = psys->child + child_nbr; float size, time; if(part->childtype==PART_CHILD_FACES){ if(pa_time) time=*pa_time; else - time=psys_get_child_time(psys,child_nbr,cfra); + time=psys_get_child_time(psys,cpa,cfra); if((part->flag&PART_ABS_TIME)==0 && part->ipo){ calc_ipo(part->ipo, 100*time); @@ -2645,9 +2756,6 @@ void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, Particle // edit=1; //} - if(G.rendering==0 && part->flag & PART_CHILD_RENDER) - totchild=0; - /* user want's cubic interpolation but only without sb it possible */ //if(interpolation==PART_INTER_CUBIC && baked && psys->softflag==OB_SB_ENABLE) // interpolation=PART_INTER_BSPLINE; @@ -2907,8 +3015,6 @@ int psys_get_particle_state(Object *ob, ParticleSystem *psys, int p, ParticleKey cfra=bsystem_time(0,(float)G.scene->r.cfra,0.0); if(psys->totchild && p>=totpart){ - if(G.rendering==0 && part->flag&PART_CHILD_RENDER) - return 0; if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ between=1; } @@ -2919,7 +3025,7 @@ int psys_get_particle_state(Object *ob, ParticleSystem *psys, int p, ParticleKey pa=psys->particles+p; if(between){ - state->time = psys_get_child_time(psys,p-totpart,cfra); + state->time = psys_get_child_time(psys,&psys->child[p-totpart],cfra); if(always==0) if((state->time<0.0 && (part->flag & PART_UNBORN)==0) diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 30f061743c4..c8a0c2632be 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -57,6 +57,7 @@ #include "BLI_blenlib.h" #include "BLI_kdtree.h" #include "BLI_linklist.h" +#include "BLI_threads.h" #include "BKE_anim.h" #include "BKE_bad_level_calls.h" @@ -106,7 +107,7 @@ static int get_current_display_percentage(ParticleSystem *psys) static void alloc_particles(ParticleSystem *psys, int new_totpart) { ParticleData *newpars = 0, *pa; - int i, totpart, totsaved = 0; + int i, child_nbr, totpart, totsaved = 0; if(new_totpart<0){ if(psys->part->distr==PART_DISTR_GRID){ @@ -134,13 +135,14 @@ static void alloc_particles(ParticleSystem *psys, int new_totpart) } psys->particles=newpars; - if(psys->part->child_nbr && psys->part->childtype){ + child_nbr= (G.rendering)? psys->part->ren_child_nbr: psys->part->child_nbr; + if(child_nbr && psys->part->childtype){ if(psys->child) MEM_freeN(psys->child); psys->child = NULL; if(totpart) - psys->child= MEM_callocN(totpart*psys->part->child_nbr*sizeof(ChildParticle), "child_particles"); - psys->totchild=totpart*psys->part->child_nbr; + psys->child= MEM_callocN(totpart*child_nbr*sizeof(ChildParticle), "child_particles"); + psys->totchild=totpart*child_nbr; } else if(psys->child){ MEM_freeN(psys->child); @@ -462,6 +464,297 @@ static int binary_search_distribution(float *sum, int n, float value) return low; } +/* note: this function must be thread safe, for from == PART_FROM_CHILD */ +#define ONLY_WORKING_WITH_PA_VERTS 0 +void psys_thread_distribute_particle(ParticleThread *thread, ParticleData *pa, ChildParticle *cpa, int p) +{ + ParticleThreadContext *ctx= thread->ctx; + Object *ob= ctx->ob; + DerivedMesh *dm= ctx->dm; + ParticleData *tpars=0, *tpa; + ParticleSettings *part= ctx->psys->part; + float *v1, *v2, *v3, *v4, nor[3], co1[3], co2[3], nor1[3]; + float cur_d, min_d; + int from= ctx->from; + int cfrom= ctx->cfrom; + int distr= ctx->distr; + int i, intersect, tot; + + if(from == PART_FROM_VERT) { + /* TODO_PARTICLE - use original index */ + pa->num= ctx->index[p]; + pa->fuv[0] = 1.0f; + pa->fuv[1] = pa->fuv[2] = pa->fuv[3] = 0.0; + //pa->verts[0] = pa->verts[1] = pa->verts[2] = 0; + +#if ONLY_WORKING_WITH_PA_VERTS + if(ctx->tree){ + KDTreeNearest ptn[3]; + int w, maxw; + + psys_particle_on_dm(ctx->ob,ctx->dm,from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co1,0,0,0); + maxw = BLI_kdtree_find_n_nearest(ctx->tree,3,co1,NULL,ptn); + + for(w=0; w<maxw; w++){ + pa->verts[w]=ptn->num; + } + } +#endif + } + else if(from == PART_FROM_FACE || from == PART_FROM_VOLUME) { + MFace *mface; + + pa->num = i = ctx->index[p]; + mface = dm->getFaceData(dm,i,CD_MFACE); + + switch(distr){ + case PART_DISTR_JIT: + ctx->jitoff[i] = fmod(ctx->jitoff[i],(float)ctx->jitlevel); + psys_uv_to_w(ctx->jit[2*(int)ctx->jitoff[i]], ctx->jit[2*(int)ctx->jitoff[i]+1], mface->v4, pa->fuv); + ctx->jitoff[i]++; + //ctx->jitoff[i]=(float)fmod(ctx->jitoff[i]+ctx->maxweight/ctx->weight[i],(float)ctx->jitlevel); + break; + case PART_DISTR_RAND: + psys_uv_to_w(rng_getFloat(thread->rng), rng_getFloat(thread->rng), mface->v4, pa->fuv); + break; + } + pa->foffset= 0.0f; + + /* + pa->verts[0] = mface->v1; + pa->verts[1] = mface->v2; + pa->verts[2] = mface->v3; + */ + + /* experimental */ + if(from==PART_FROM_VOLUME){ + MVert *mvert=dm->getVertDataArray(dm,CD_MVERT); + + tot=dm->getNumFaces(dm); + + psys_interpolate_face(mvert,mface,0,pa->fuv,co1,nor,0,0); + + Normalize(nor); + VecMulf(nor,-100.0); + + VECADD(co2,co1,nor); + + min_d=2.0; + intersect=0; + + for(i=0,mface=dm->getFaceDataArray(dm,CD_MFACE); i<tot; i++,mface++){ + if(i==pa->num) continue; + + v1=mvert[mface->v1].co; + v2=mvert[mface->v2].co; + v3=mvert[mface->v3].co; + + if(LineIntersectsTriangle(co1, co2, v2, v3, v1, &cur_d, 0)){ + if(cur_d<min_d){ + min_d=cur_d; + pa->foffset=cur_d*50.0f; /* to the middle of volume */ + intersect=1; + } + } + if(mface->v4){ + v4=mvert[mface->v4].co; + + if(LineIntersectsTriangle(co1, co2, v4, v1, v3, &cur_d, 0)){ + if(cur_d<min_d){ + min_d=cur_d; + pa->foffset=cur_d*50.0f; /* to the middle of volume */ + intersect=1; + } + } + } + } + if(intersect==0) + pa->foffset=0.0; + else switch(distr){ + case PART_DISTR_JIT: + pa->foffset*= ctx->jit[2*(int)ctx->jitoff[i]]; + break; + case PART_DISTR_RAND: + pa->foffset*=BLI_frand(); + break; + } + } + } + else if(from == PART_FROM_PARTICLE) { + //pa->verts[0]=0; /* not applicable */ + //pa->verts[1]=0; + //pa->verts[2]=0; + + tpa=tpars+ctx->index[p]; + pa->num=ctx->index[p]; + pa->fuv[0]=tpa->fuv[0]; + pa->fuv[1]=tpa->fuv[1]; + /* abusing foffset a little for timing in near reaction */ + pa->foffset=ctx->weight[ctx->index[p]]; + ctx->weight[ctx->index[p]]+=ctx->maxweight; + } + else if(from == PART_FROM_CHILD) { + MFace *mf; + + if(ctx->index[p] < 0) { + cpa->num=0; + cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]=0.0f; + cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0; + cpa->rand[0]=cpa->rand[1]=cpa->rand[2]=0.0f; + return; + } + + mf= dm->getFaceData(dm, ctx->index[p], CD_MFACE); + + //switch(distr){ + // case PART_DISTR_JIT: + // i=index[p]; + // psys_uv_to_w(ctx->jit[2*(int)ctx->jitoff[i]], ctx->jit[2*(int)ctx->jitoff[i]+1], mf->v4, cpa->fuv); + // ctx->jitoff[i]=(float)fmod(ctx->jitoff[i]+ctx->maxweight/ctx->weight[i],(float)ctx->jitlevel); + // break; + // case PART_DISTR_RAND: + psys_uv_to_w(rng_getFloat(thread->rng), rng_getFloat(thread->rng), mf->v4, cpa->fuv); + // break; + //} + + cpa->rand[0] = rng_getFloat(thread->rng); + cpa->rand[1] = rng_getFloat(thread->rng); + cpa->rand[2] = rng_getFloat(thread->rng); + cpa->num = ctx->index[p]; + + if(ctx->tree){ + KDTreeNearest ptn[10]; + int w,maxw, do_seams; + float maxd,mind,dd,totw=0.0; + int parent[10]; + float pweight[10]; + + do_seams= (part->flag&PART_CHILD_SEAMS && ctx->seams); + + psys_particle_on_dm(ob,dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,0,0); + maxw = BLI_kdtree_find_n_nearest(ctx->tree,(do_seams)?10:4,co1,nor1,ptn); + + maxd=ptn[maxw-1].dist; + mind=ptn[0].dist; + dd=maxd-mind; + + /* the weights here could be done better */ + for(w=0; w<maxw; w++){ + parent[w]=ptn[w].index; + pweight[w]=(float)pow(2.0,(double)(-6.0f*ptn[w].dist/maxd)); + //totw+=cpa->w[w]; + } + for(;w<10; w++){ + parent[w]=-1; + pweight[w]=0.0f; + } + if(do_seams){ + ParticleSeam *seam=ctx->seams; + float temp[3],temp2[3],tan[3]; + float inp,cur_len,min_len=10000.0f; + int min_seam=0, near_vert=0; + /* find closest seam */ + for(i=0; i<ctx->totseam; i++, seam++){ + VecSubf(temp,co1,seam->v0); + inp=Inpf(temp,seam->dir)/seam->length2; + if(inp<0.0f){ + cur_len=VecLenf(co1,seam->v0); + } + else if(inp>1.0f){ + cur_len=VecLenf(co1,seam->v1); + } + else{ + VecCopyf(temp2,seam->dir); + VecMulf(temp2,inp); + cur_len=VecLenf(temp,temp2); + } + if(cur_len<min_len){ + min_len=cur_len; + min_seam=i; + if(inp<0.0f) near_vert=-1; + else if(inp>1.0f) near_vert=1; + else near_vert=0; + } + } + seam=ctx->seams+min_seam; + + VecCopyf(temp,seam->v0); + + if(near_vert){ + if(near_vert==-1) + VecSubf(tan,co1,seam->v0); + else{ + VecSubf(tan,co1,seam->v1); + VecCopyf(temp,seam->v1); + } + + Normalize(tan); + } + else{ + VecCopyf(tan,seam->tan); + VecSubf(temp2,co1,temp); + if(Inpf(tan,temp2)<0.0f) + VecMulf(tan,-1.0f); + } + for(w=0; w<maxw; w++){ + VecSubf(temp2,ptn[w].co,temp); + if(Inpf(tan,temp2)<0.0f){ + parent[w]=-1; + pweight[w]=0.0f; + } + } + + } + + for(w=0,i=0; w<maxw && i<4; w++){ + if(parent[w]>=0){ + cpa->pa[i]=parent[w]; + cpa->w[i]=pweight[w]; + totw+=pweight[w]; + i++; + } + } + for(;i<4; i++){ + cpa->pa[i]=-1; + cpa->w[i]=0.0f; + } + + if(totw>0.0f) for(w=0; w<4; w++) + cpa->w[w]/=totw; + + cpa->parent=cpa->pa[0]; + } + } +} + +void *exec_distribution(void *data) +{ + ParticleThread *thread= (ParticleThread*)data; + ParticleSystem *psys= thread->ctx->psys; + ParticleData *pa; + ChildParticle *cpa; + int p, totpart; + + if(thread->ctx->from == PART_FROM_CHILD) { + totpart= psys->totchild; + cpa= psys->child + thread->num; + + rng_skip(thread->rng, 5*thread->num); + for(p=thread->num; p<totpart; p+=thread->tot, cpa+=thread->tot) { + psys_thread_distribute_particle(thread, NULL, cpa, p); + rng_skip(thread->rng, 5*(thread->tot-1)); + } + } + else { + totpart= psys->totpart; + pa= psys->particles + thread->num; + for(p=thread->num; p<totpart; p+=thread->tot, pa+=thread->tot) + psys_thread_distribute_particle(thread, pa, NULL, p); + } + + return 0; +} + /* creates a distribution of coordinates on a DerivedMesh */ /* */ /* 1. lets check from what we are emitting */ @@ -477,39 +770,39 @@ static int binary_search_distribution(float *sum, int n, float value) /* 6. and we're done! */ /* This is to denote functionality that does not yet work with mesh - only derived mesh */ -#define ONLY_WORKING_WITH_PA_VERTS 0 -static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, ParticleSystem *psys, int from) +int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm, int from) { + ParticleThreadContext *ctx= threads[0].ctx; + Object *ob= ctx->ob; + ParticleSystem *psys= ctx->psys; Object *tob; - ParticleData *pa=0, *tpars=0, *tpa; + ParticleData *pa=0, *tpars; ParticleSettings *part; ParticleSystem *tpsys; + ParticleSeam *seams= 0; ChildParticle *cpa=0; KDTree *tree=0; - ParticleSeam *seams=0; + DerivedMesh *dm= NULL; float *jit= NULL; - int p=0,i; + int i, seed, p=0, totthread= threads[0].tot; int no_distr=0, cfrom=0; int tot=0, totpart, *index=0, children=0, totseam=0; //int *vertpart=0; - int jitlevel= 1, intersect, distr; + int jitlevel= 1, distr; float *weight=0,*sum=0,*jitoff=0; - float cur, maxweight=0.0, tweight, totweight; - float *v1, *v2, *v3, *v4, co[3], nor[3], co1[3], co2[3], nor1[3]; - float cur_d, min_d; - DerivedMesh *dm= NULL; + float cur, maxweight=0.0, tweight, totweight, co[3], nor[3]; if(ob==0 || psys==0 || psys->part==0) - return; + return 0; part=psys->part; totpart=psys->totpart; if(totpart==0) - return; + return 0; if (!finaldm->deformedOnly && !CustomData_has_layer( &finaldm->faceData, CD_ORIGINDEX ) ) { error("Can't paint with the current modifier stack, disable destructive modifiers"); - return; + return 0; } BLI_srandom(31415926 + psys->seed); @@ -579,7 +872,9 @@ static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, Particl } else{ /* no need to figure out distribution */ - for(i=0; i<part->child_nbr; i++){ + int child_nbr= (G.rendering)? part->ren_child_nbr: part->child_nbr; + + for(i=0; i<child_nbr; i++){ for(p=0; p<psys->totpart; p++,cpa++){ float length=2.0; cpa->parent=p; @@ -600,7 +895,7 @@ static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, Particl } } - return; + return 0; } } else{ @@ -610,7 +905,7 @@ static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, Particl if(part->distr==PART_DISTR_GRID){ distribute_particles_in_grid(dm,psys); dm->release(dm); - return; + return 0; } distr=part->distr; @@ -674,7 +969,7 @@ static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, Particl } if(dm != finaldm) dm->release(dm); - return; + return 0; } /* 2. */ @@ -796,6 +1091,8 @@ static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, Particl } } + MEM_freeN(sum); + /* weights are no longer used except for FROM_PARTICLE, which needs them zeroed for indexing */ if(from==PART_FROM_PARTICLE){ for(i=0; i<tot; i++) @@ -820,277 +1117,71 @@ static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, Particl } /* 5. */ - if(children) from=PART_FROM_CHILD; - for(p=0,pa=psys->particles; p<totpart; p++,pa++,cpa++){ - switch(from){ - case PART_FROM_VERT: - /* TODO_PARTICLE - use original index */ - pa->num=index[p]; - pa->fuv[0] = 1.0f; - pa->fuv[1] = pa->fuv[2] = pa->fuv[3] = 0.0; - //pa->verts[0] = pa->verts[1] = pa->verts[2] = 0; - -#if ONLY_WORKING_WITH_PA_VERTS - if(tree){ - KDTreeNearest ptn[3]; - int w,maxw; - - psys_particle_on_dm(ob,dm,from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co1,0,0,0); - maxw = BLI_kdtree_find_n_nearest(tree,3,co1,NULL,ptn); - - for(w=0; w<maxw; w++){ - pa->verts[w]=ptn->num; - } - } -#endif - break; - case PART_FROM_FACE: - case PART_FROM_VOLUME: - { - MFace *mface; - pa->num = i = index[p]; - mface = dm->getFaceData(dm,i,CD_MFACE); - - switch(distr){ - case PART_DISTR_JIT: - jitoff[i] = fmod(jitoff[i],(float)jitlevel); - psys_uv_to_w(jit[2*(int)jitoff[i]], jit[2*(int)jitoff[i]+1], mface->v4, pa->fuv); - jitoff[i]++; - //jitoff[i]=(float)fmod(jitoff[i]+maxweight/weight[i],(float)jitlevel); - break; - case PART_DISTR_RAND: - psys_uv_to_w(BLI_frand(), BLI_frand(), mface->v4, pa->fuv); - break; - } - pa->foffset= 0.0f; - - /* - pa->verts[0] = mface->v1; - pa->verts[1] = mface->v2; - pa->verts[2] = mface->v3; - */ - - /* experimental */ - if(from==PART_FROM_VOLUME){ - MVert *mvert=dm->getVertDataArray(dm,CD_MVERT); - - tot=dm->getNumFaces(dm); - - psys_interpolate_face(mvert,mface,0,pa->fuv,co1,nor,0,0); - - Normalize(nor); - VecMulf(nor,-100.0); - - VECADD(co2,co1,nor); - - min_d=2.0; - intersect=0; - - for(i=0,mface=dm->getFaceDataArray(dm,CD_MFACE); i<tot; i++,mface++){ - if(i==pa->num) continue; - - v1=mvert[mface->v1].co; - v2=mvert[mface->v2].co; - v3=mvert[mface->v3].co; - - if(LineIntersectsTriangle(co1, co2, v2, v3, v1, &cur_d, 0)){ - if(cur_d<min_d){ - min_d=cur_d; - pa->foffset=cur_d*50.0f; /* to the middle of volume */ - intersect=1; - } - } - if(mface->v4){ - v4=mvert[mface->v4].co; + if(children) + from=PART_FROM_CHILD; + + ctx->tree= tree; + ctx->seams= seams; + ctx->totseam= totseam; + ctx->psys= psys; + ctx->index= index; + ctx->jit= jit; + ctx->jitlevel= jitlevel; + ctx->jitoff= jitoff; + ctx->weight= weight; + ctx->maxweight= maxweight; + ctx->from= from; + ctx->cfrom= cfrom; + ctx->distr= distr; + ctx->dm= dm; + + seed= 31415926 + ctx->psys->seed; + + if(from!=PART_FROM_CHILD || psys->totchild < 10000) + totthread= 1; + + for(i=0; i<totthread; i++) { + threads[i].rng= rng_new(seed); + threads[i].tot= totthread; + } - if(LineIntersectsTriangle(co1, co2, v4, v1, v3, &cur_d, 0)){ - if(cur_d<min_d){ - min_d=cur_d; - pa->foffset=cur_d*50.0f; /* to the middle of volume */ - intersect=1; - } - } - } - } - if(intersect==0) - pa->foffset=0.0; - else switch(distr){ - case PART_DISTR_JIT: - pa->foffset*= jit[2*(int)jitoff[i]]; - break; - case PART_DISTR_RAND: - pa->foffset*=BLI_frand(); - break; - } - } - break; - } - case PART_FROM_PARTICLE: - - //pa->verts[0]=0; /* not applicable */ - //pa->verts[1]=0; - //pa->verts[2]=0; - - tpa=tpars+index[p]; - pa->num=index[p]; - pa->fuv[0]=tpa->fuv[0]; - pa->fuv[1]=tpa->fuv[1]; - /* abusing foffset a little for timing in near reaction */ - pa->foffset=weight[index[p]]; - weight[index[p]]+=maxweight; - break; - case PART_FROM_CHILD: - if(index[p]>=0){ - MFace *mf; + return 1; +} - mf=dm->getFaceData(dm,index[p],CD_MFACE); - - //switch(distr){ - // case PART_DISTR_JIT: - // i=index[p]; - // psys_uv_to_w(jit[2*(int)jitoff[i]], jit[2*(int)jitoff[i]+1], mf->v4, cpa->fuv); - // jitoff[i]=(float)fmod(jitoff[i]+maxweight/weight[i],(float)jitlevel); - // break; - // case PART_DISTR_RAND: - psys_uv_to_w(BLI_frand(), BLI_frand(), mf->v4, cpa->fuv); - // break; - //} - - cpa->rand[0] = BLI_frand(); - cpa->rand[1] = BLI_frand(); - cpa->rand[2] = BLI_frand(); - cpa->num = index[p]; - - if(tree){ - KDTreeNearest ptn[10]; - int w,maxw, do_seams; - float maxd,mind,dd,totw=0.0; - int parent[10]; - float pweight[10]; - - do_seams= (part->flag&PART_CHILD_SEAMS && seams); - - psys_particle_on_dm(ob,dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,0,0); - maxw = BLI_kdtree_find_n_nearest(tree,(do_seams)?10:4,co1,nor1,ptn); - - maxd=ptn[maxw-1].dist; - mind=ptn[0].dist; - dd=maxd-mind; - - /* the weights here could be done better */ - for(w=0; w<maxw; w++){ - parent[w]=ptn[w].index; - pweight[w]=(float)pow(2.0,(double)(-6.0f*ptn[w].dist/maxd)); - //totw+=cpa->w[w]; - } - for(;w<10; w++){ - parent[w]=-1; - pweight[w]=0.0f; - } - if(do_seams){ - ParticleSeam *seam=seams; - float temp[3],temp2[3],tan[3]; - float inp,cur_len,min_len=10000.0f; - int min_seam=0, near_vert=0; - /* find closest seam */ - for(i=0; i<totseam; i++, seam++){ - VecSubf(temp,co1,seam->v0); - inp=Inpf(temp,seam->dir)/seam->length2; - if(inp<0.0f){ - cur_len=VecLenf(co1,seam->v0); - } - else if(inp>1.0f){ - cur_len=VecLenf(co1,seam->v1); - } - else{ - VecCopyf(temp2,seam->dir); - VecMulf(temp2,inp); - cur_len=VecLenf(temp,temp2); - } - if(cur_len<min_len){ - min_len=cur_len; - min_seam=i; - if(inp<0.0f) near_vert=-1; - else if(inp>1.0f) near_vert=1; - else near_vert=0; - } - } - seam=seams+min_seam; - - VecCopyf(temp,seam->v0); - - if(near_vert){ - if(near_vert==-1) - VecSubf(tan,co1,seam->v0); - else{ - VecSubf(tan,co1,seam->v1); - VecCopyf(temp,seam->v1); - } +static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, ParticleSystem *psys, int from) +{ + ListBase threads; + ParticleThread *pthreads; + ParticleThreadContext *ctx; + int i, totthread; - Normalize(tan); - } - else{ - VecCopyf(tan,seam->tan); - VecSubf(temp2,co1,temp); - if(Inpf(tan,temp2)<0.0f) - VecMulf(tan,-1.0f); - } - for(w=0; w<maxw; w++){ - VecSubf(temp2,ptn[w].co,temp); - if(Inpf(tan,temp2)<0.0f){ - parent[w]=-1; - pweight[w]=0.0f; - } - } + pthreads= psys_threads_create(ob, psys, G.scene->r.threads); - } + if(!psys_threads_init_distribution(pthreads, finaldm, from)) { + psys_threads_free(pthreads); + return; + } - for(w=0,i=0; w<maxw && i<4; w++){ - if(parent[w]>=0){ - cpa->pa[i]=parent[w]; - cpa->w[i]=pweight[w]; - totw+=pweight[w]; - i++; - } - } - for(;i<4; i++){ - cpa->pa[i]=-1; - cpa->w[i]=0.0f; - } + totthread= pthreads[0].tot; + if(totthread > 1) { + BLI_init_threads(&threads, exec_distribution, totthread); - if(totw>0.0f) for(w=0; w<4; w++) - cpa->w[w]/=totw; + for(i=0; i<totthread; i++) + BLI_insert_thread(&threads, &pthreads[i]); - cpa->parent=cpa->pa[0]; - } - } - else{ - cpa->num=0; - cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]=0.0f; - cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0; - cpa->rand[0]=cpa->rand[1]=cpa->rand[2]=0.0f; - } - break; - } + BLI_end_threads(&threads); } + else + exec_distribution(&pthreads[0]); - /* 6. */ - if(jit) MEM_freeN(jit); - if(sum) MEM_freeN(sum); - if(jitoff) MEM_freeN(jitoff); - if(weight){ - MEM_freeN(weight); - weight=0; - } - if(index) MEM_freeN(index); - if(seams) MEM_freeN(seams); - //if(vertpart) MEM_freeN(vertpart); - BLI_kdtree_free(tree); - if (from == PART_FROM_FACE) psys_calc_dmfaces(ob, finaldm, psys); - - if(dm != finaldm) dm->release(dm); + + ctx= pthreads[0].ctx; + if(ctx->dm != finaldm) + ctx->dm->release(ctx->dm); + + psys_threads_free(pthreads); } /* ready for future use, to emit particles without geometry */ @@ -1135,6 +1226,79 @@ static void distribute_particles(Object *ob, ParticleSystem *psys, int from) } } } + +/* threaded child particle distribution and path caching */ +ParticleThread *psys_threads_create(struct Object *ob, struct ParticleSystem *psys, int totthread) +{ + ParticleThread *threads; + ParticleThreadContext *ctx; + int i; + + threads= MEM_callocN(sizeof(ParticleThread)*totthread, "ParticleThread"); + ctx= MEM_callocN(sizeof(ParticleThreadContext), "ParticleThreadContext"); + + ctx->ob= ob; + ctx->psys= psys; + ctx->psmd= psys_get_modifier(ob, psys); + ctx->dm= ctx->psmd->dm; + ctx->ma= give_current_material(ob, psys->part->omat); + + memset(threads, 0, sizeof(ParticleThread)*totthread); + + for(i=0; i<totthread; i++) { + threads[i].ctx= ctx; + threads[i].num= i; + threads[i].tot= totthread; + } + + return threads; +} + +void psys_threads_free(ParticleThread *threads) +{ + ParticleThreadContext *ctx= threads[0].ctx; + int i, totthread= threads[0].tot; + + /* path caching */ + if(ctx->vg_length) + MEM_freeN(ctx->vg_length); + if(ctx->vg_clump) + MEM_freeN(ctx->vg_clump); + if(ctx->vg_kink) + MEM_freeN(ctx->vg_kink); + if(ctx->vg_rough1) + MEM_freeN(ctx->vg_rough1); + if(ctx->vg_rough2) + MEM_freeN(ctx->vg_roughe); + if(ctx->vg_roughe) + MEM_freeN(ctx->vg_roughe); + + if(ctx->psys->lattice){ + end_latt_deform(); + ctx->psys->lattice=0; + } + + /* distribution */ + if(ctx->jit) MEM_freeN(ctx->jit); + if(ctx->jitoff) MEM_freeN(ctx->jitoff); + if(ctx->weight) MEM_freeN(ctx->weight); + if(ctx->index) MEM_freeN(ctx->index); + if(ctx->seams) MEM_freeN(ctx->seams); + //if(ctx->vertpart) MEM_freeN(ctx->vertpart); + BLI_kdtree_free(ctx->tree); + + /* threads */ + for(i=0; i<totthread; i++) { + if(threads[i].rng) + rng_free(threads[i].rng); + if(threads[i].rng_path) + rng_free(threads[i].rng_path); + } + + MEM_freeN(ctx); + MEM_freeN(threads); +} + /* set particle parameters that don't change during particle's life */ void initialize_particle(ParticleData *pa, int p, Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd) { @@ -3980,8 +4144,9 @@ static void psys_update_path_cache(Object *ob, ParticleSystemModifierData *psmd, ParticleSettings *part=psys->part; ParticleEditSettings *pset=&G.scene->toolsettings->particle; int distr=0,alloc=0; + int child_nbr= (G.rendering)? part->ren_child_nbr: part->child_nbr; - if((psys->part->childtype && psys->totchild != psys->totpart*part->child_nbr) || psys->recalc&PSYS_ALLOC) + if((psys->part->childtype && psys->totchild != psys->totpart*child_nbr) || psys->recalc&PSYS_ALLOC) alloc=1; if(alloc || psys->recalc&PSYS_DISTR || (psys->vgroup[PSYS_VG_DENSITY] && (G.f & G_WEIGHTPAINT))) @@ -4003,9 +4168,9 @@ static void psys_update_path_cache(Object *ob, ParticleSystemModifierData *psmd, || part->draw_as==PART_DRAW_PATH || part->draw&PART_DRAW_KEYS)){ psys_cache_paths(ob, psys, cfra, 0); - if(part->childtype){ - if((G.rendering || (part->flag&PART_CHILD_RENDER)==0) - || (psys_in_edit_mode(psys) && (pset->flag&PE_SHOW_CHILD))) + /* for render, child particle paths are computed on the fly */ + if(part->childtype) { + if(((psys->totchild!=0)) || (psys_in_edit_mode(psys) && (pset->flag&PE_SHOW_CHILD))) psys_cache_child_paths(ob, psys, cfra, 0); } } @@ -4133,6 +4298,7 @@ static void system_step(Object *ob, ParticleSystem *psys, ParticleSystemModifier int totpart,oldtotpart=0,p; float disp, *vg_vel=0, *vg_tan=0, *vg_rot=0, *vg_size=0; int init=0,distr=0,alloc=0; + int child_nbr; /*----start validity checks----*/ @@ -4197,7 +4363,8 @@ static void system_step(Object *ob, ParticleSystem *psys, ParticleSystemModifier else totpart = psys->part->totpart; - if(oldtotpart != totpart || psys->recalc&PSYS_ALLOC || (psys->part->childtype && psys->totchild != psys->totpart*part->child_nbr)) + child_nbr= (G.rendering)? part->ren_child_nbr: part->child_nbr; + if(oldtotpart != totpart || psys->recalc&PSYS_ALLOC || (psys->part->childtype && psys->totchild != psys->totpart*child_nbr)) alloc = 1; if(alloc || psys->recalc&PSYS_DISTR || (psys->vgroup[PSYS_VG_DENSITY] && (G.f & G_WEIGHTPAINT) && ob==OBACT)) @@ -4368,7 +4535,7 @@ void particle_system_update(Object *ob, ParticleSystem *psys){ if(psys->softflag&OB_SB_ENABLE) psys_to_softbody(ob,psys,1); } - + system_step(ob,psys,psmd,cfra); Mat4CpyMat4(psys->imat, ob->imat); /* used for duplicators */ diff --git a/source/blender/blenlib/BLI_rand.h b/source/blender/blenlib/BLI_rand.h index da2ecb79651..638f36b7414 100644 --- a/source/blender/blenlib/BLI_rand.h +++ b/source/blender/blenlib/BLI_rand.h @@ -52,6 +52,9 @@ double rng_getDouble (struct RNG* rng); float rng_getFloat (struct RNG* rng); void rng_shuffleArray(struct RNG *rng, void *data, int elemSize, int numElems); + /** Note that skipping is as slow as generating n numbers! */ +void rng_skip (struct RNG *rng, int n); + /** Seed the random number generator */ void BLI_srand (unsigned int seed); diff --git a/source/blender/blenlib/intern/rand.c b/source/blender/blenlib/intern/rand.c index df01288aab8..71df0ede037 100644 --- a/source/blender/blenlib/intern/rand.c +++ b/source/blender/blenlib/intern/rand.c @@ -116,6 +116,14 @@ void rng_shuffleArray(RNG *rng, void *data, int elemSize, int numElems) free(temp); } +void rng_skip(RNG *rng, int n) +{ + int i; + + for(i=0; i<n; i++) + rng_getInt(rng); +} + /***/ static RNG theBLI_rng = {0}; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index b67cdc61dc6..43bddc275b7 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6671,6 +6671,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) Image *ima; Lamp *la; Material *ma; + ParticleSettings *part; /* unless the file was created 2.44.3 but not 2.45, update the constraints */ if ( !(main->versionfile==244 && main->subversionfile==3) && @@ -6833,7 +6834,10 @@ static void do_versions(FileData *fd, Library *lib, Main *main) ma->fadeto_mir = MA_RAYMIR_FADETOSKY; } } - + + for(part=main->particle.first; part; part=part->id.next) + if(part->ren_child_nbr==0) + part->ren_child_nbr= part->child_nbr; } if ((main->versionfile < 245) || (main->versionfile == 245 && main->subversionfile < 2)) { @@ -7075,7 +7079,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) /* convert settings from old particle system */ /* general settings */ - part->totpart = paf->totpart; + part->totpart = MIN2(paf->totpart, 100000); part->sta = paf->sta; part->end = paf->end; part->lifetime = paf->lifetime; @@ -7149,7 +7153,6 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } - for(sce= main->scene.first; sce; sce=sce->id.next) { ParticleEditSettings *pset= &sce->toolsettings->particle; int a; @@ -7184,11 +7187,10 @@ static void do_versions(FileData *fd, Library *lib, Main *main) if ((main->versionfile < 245) || (main->versionfile == 245 && main->subversionfile < 10)) { Object *ob; + /* dupliface scale */ - for(ob= main->object.first; ob; ob= ob->id.next) { + for(ob= main->object.first; ob; ob= ob->id.next) ob->dupfacesca = 1.0f; - } - } diff --git a/source/blender/include/BIF_editparticle.h b/source/blender/include/BIF_editparticle.h index d4c6a92b8c8..3a32fac431a 100644 --- a/source/blender/include/BIF_editparticle.h +++ b/source/blender/include/BIF_editparticle.h @@ -45,6 +45,7 @@ void PE_create_particle_edit(struct Object *ob, struct ParticleSystem *psys); void PE_free_particle_edit(struct ParticleSystem *psys); void PE_change_act(void *ob_v, void *act_v); +int PE_can_edit(struct ParticleSystem *psys); /* access */ struct ParticleSystem *PE_get_current(struct Object *ob); diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index c7301d4f974..489ba71d639 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -84,11 +84,11 @@ typedef struct Material { int mode, mode_l; /* mode_l is the or-ed result of all layer modes */ short flarec, starc, linec, ringc; float hasize, flaresize, subsize, flareboost; - float strand_sta, strand_end, strand_ease; + float strand_sta, strand_end, strand_ease, strand_surfnor; char strand_uvname[32]; float sbias; /* shadow bias */ - float shad_alpha, padf; /* in use for irregular shadowbuffer */ + float shad_alpha; /* in use for irregular shadowbuffer */ /* for buttons and render*/ char rgbsel, texact, pr_type, use_nodes; @@ -121,6 +121,7 @@ typedef struct Material { float fhdist, xyfrict; short dynamode, pad2; + /* subsurface scattering */ float sss_radius[3], sss_col[3]; float sss_error, sss_scale, sss_ior; float sss_colfac, sss_texfac; @@ -191,6 +192,7 @@ typedef struct Material { #define MA_GROUP_NOLAY 0x10000000 #define MA_FACETEXTURE_ALPHA 0x20000000 #define MA_STR_B_UNITS 0x40000000 +#define MA_STR_SURFDIFF 0x80000000 #define MA_MODE_MASK 0x4fffffff /* all valid mode bits */ diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index f41a0d63142..fc2fa7cab20 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -134,9 +134,9 @@ typedef struct ParticleSettings { /* global physical properties */ float acc[3], dragfac, brownfac, dampfac; /* length */ - float length, abslength, randlength; + float length, abslength, randlength, pad; /* children */ - int child_nbr; + int child_nbr, ren_child_nbr; float parents, childsize, childrandsize; float childrad, childflat, childspread; /* clumping */ @@ -204,7 +204,7 @@ typedef struct ParticleSystem{ /* no special why's, just seem reasonable */ /* changing these (atleast upwards) should not cause any major problems */ #define MAX_PARTS 100000 /* real particles/system */ -#define MAX_PART_CHILDREN 1000 /* child particles/real particles */ +#define MAX_PART_CHILDREN 10000 /* child particles/real particles */ #define MAX_BOIDNEIGHBOURS 10 /* neigbours considered/boid */ /* part->type */ @@ -299,9 +299,10 @@ typedef struct ParticleSystem{ #define PART_DRAW_NUM 512 #define PART_DRAW_RAND_GR 1024 #define PART_DRAW_REN_ADAPT 2048 -#define PART_DRAW_VEL_LENGTH (1<<12) -#define PART_DRAW_MAT_COL (1<<13) -#define PART_DRAW_WHOLE_GR (1<<14) +#define PART_DRAW_VEL_LENGTH (1<<12) +#define PART_DRAW_MAT_COL (1<<13) +#define PART_DRAW_WHOLE_GR (1<<14) +#define PART_DRAW_REN_STRAND (1<<15) /* part->bb_align */ #define PART_BB_X 0 diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index 2d9583af649..d202ea1eed5 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -81,6 +81,7 @@ typedef struct RenderLayer { float *rectf; /* 4 float, standard rgba buffer (read not above!) */ float *acolrect; /* 4 float, optional transparent buffer, needs storage for display updates */ + float *scolrect; /* 4 float, optional strand buffer, needs storage for display updates */ ListBase passes; @@ -121,7 +122,7 @@ typedef struct RenderResult { } RenderResult; typedef struct RenderStats { - int totface, totvert, tothalo, totlamp, totpart; + int totface, totvert, totstrand, tothalo, totlamp, totpart; short curfield, curblur, curpart, partsdone, convertdone; double starttime, lastframetime; char *infostr; diff --git a/source/blender/render/extern/include/RE_shader_ext.h b/source/blender/render/extern/include/RE_shader_ext.h index 7717d6fdc48..19b5637522e 100644 --- a/source/blender/render/extern/include/RE_shader_ext.h +++ b/source/blender/render/extern/include/RE_shader_ext.h @@ -154,6 +154,8 @@ typedef struct ShadeInput /* stored copy of original face normal (facenor) * before flipping. Used in Front/back output on geometry node */ float orignor[3]; + /* for strand shading, normal at the surface */ + float surfnor[3], surfdist; /* from initialize, part or renderlayer */ short do_preview; /* for nodes, in previewrender */ diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index c20d27bfcdc..f7f2222e98d 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -48,6 +48,7 @@ struct MemArena; struct VertTableNode; struct VlakTableNode; struct GHash; +struct RenderBuckets; #define TABLEINITSIZE 1024 #define LAMPINITSIZE 256 @@ -159,7 +160,7 @@ struct Render float cfra; /* render database */ - int totvlak, totvert, tothalo, totlamp; + int totvlak, totvert, tothalo, totstrand, totlamp; ListBase lights; /* GroupObject pointers */ ListBase lampren; /* storage, for free */ @@ -167,9 +168,13 @@ struct Render struct VertTableNode *vertnodes; int vlaknodeslen; struct VlakTableNode *vlaknodes; + int strandnodeslen; + struct StrandTableNode *strandnodes; int blohalen; struct HaloRen **bloha; ListBase objecttable; + ListBase strandbufs; + struct RenderBuckets *strandbuckets; struct Image *backbuf, *bakebuf; @@ -234,7 +239,7 @@ typedef struct ShadBuf { typedef struct ObjectRen { struct ObjectRen *next, *prev; struct Object *ob, *par; - int index, startvert, endvert, startface, endface; + int index, startvert, endvert, startface, endface, startstrand, endstrand; float *vectors; } ObjectRen; @@ -297,6 +302,36 @@ typedef struct HaloRen struct Material *mat; } HaloRen; +typedef struct StrandVert { + float co[3]; + float strandco; +} StrandVert; + +typedef struct StrandBuffer { + struct StrandBuffer *next, *prev; + struct StrandVert *vert; + int totvert; + + struct Object *ob; + struct Material *ma; + unsigned int lay; + int overrideuv; + int flag, maxdepth; + float adaptcos; + + float winmat[4][4]; + int winx, winy; +} StrandBuffer; + +typedef struct StrandRen { + StrandVert *vert; + StrandBuffer *buffer; + int totvert, flag; + int clip, index; + float orco[3]; +} StrandRen; + + struct LampRen; struct MTex; @@ -417,6 +452,9 @@ typedef struct LampRen { #define R_SNPROJ_Z 4 #define R_FLIPPED_NO 8 +/* strandbuffer->flag */ +#define R_STRAND_BSPLINE 1 +#define R_STRAND_B_UNITS 2 #endif /* RENDER_TYPES_H */ diff --git a/source/blender/render/intern/include/renderdatabase.h b/source/blender/render/intern/include/renderdatabase.h index dc70ade25ae..febf90d9d6e 100644 --- a/source/blender/render/intern/include/renderdatabase.h +++ b/source/blender/render/intern/include/renderdatabase.h @@ -39,6 +39,8 @@ struct Render; struct MCol; struct MTFace; struct CustomData; +struct StrandBuffer; +struct StrandRen; #define RE_QUAD_MASK 0x7FFFFFF #define RE_QUAD_OFFS 0x8000000 @@ -59,9 +61,20 @@ typedef struct VlakTableNode { struct MTFace **mtface; struct MCol **mcol; int totmtface, totmcol; + float *surfnor; struct CustomDataNames **names; } VlakTableNode; +typedef struct StrandTableNode { + struct StrandRen *strand; + float *winspeed; + float *surfnor; + struct MCol **mcol; + float **uv; + int totuv, totmcol; + struct CustomDataNames **names; +} StrandTableNode; + typedef struct CustomDataNames{ struct CustomDataNames *next, *prev; @@ -75,18 +88,20 @@ void free_renderdata_vertnodes(struct VertTableNode *vertnodes); void free_renderdata_vlaknodes(struct VlakTableNode *vlaknodes); void set_normalflags(Render *re); -void project_renderdata(struct Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, float xoffs); +void project_renderdata(struct Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, float xoffs, int do_buckets); /* functions are not exported... so wrong names */ struct VlakRen *RE_findOrAddVlak(struct Render *re, int nr); struct VertRen *RE_findOrAddVert(struct Render *re, int nr); +struct StrandRen *RE_findOrAddStrand(struct Render *re, int nr); struct HaloRen *RE_findOrAddHalo(struct Render *re, int nr); struct HaloRen *RE_inithalo(struct Render *re, struct Material *ma, float *vec, float *vec1, float *orco, float hasize, float vectsize, int seed); struct HaloRen *RE_inithalo_particle(struct Render *re, struct DerivedMesh *dm, struct Material *ma, float *vec, float *vec1, float *orco, float *uvco, float hasize, float vectsize, int seed); -void RE_addRenderObject(struct Render *re, struct Object *ob, struct Object *par, int index, int sve, int eve, int sfa, int efa); +void RE_addRenderObject(struct Render *re, struct Object *ob, struct Object *par, int index, int sve, int eve, int sfa, int efa, int sst, int est); +struct StrandBuffer *RE_addStrandBuffer(struct Render *re, struct Object *ob, int totvert); float *RE_vertren_get_sticky(struct Render *re, struct VertRen *ver, int verify); float *RE_vertren_get_stress(struct Render *re, struct VertRen *ver, int verify); @@ -97,6 +112,12 @@ float *RE_vertren_get_winspeed(struct Render *re, struct VertRen *ver, int verif struct MTFace *RE_vlakren_get_tface(struct Render *re, VlakRen *ren, int n, char **name, int verify); struct MCol *RE_vlakren_get_mcol(struct Render *re, VlakRen *ren, int n, char **name, int verify); +float *RE_vlakren_get_surfnor(struct Render *re, VlakRen *ren, int verify); + +float *RE_strandren_get_winspeed(struct Render *re, struct StrandRen *strand, int verify); +float *RE_strandren_get_surfnor(struct Render *re, struct StrandRen *strand, int verify); +float *RE_strandren_get_uv(struct Render *re, struct StrandRen *strand, int n, char **name, int verify); +struct MCol *RE_strandren_get_mcol(struct Render *re, struct StrandRen *strand, int n, char **name, int verify); struct VertRen *RE_vertren_copy(struct Render *re, struct VertRen *ver); struct VlakRen *RE_vlakren_copy(struct Render *re, struct VlakRen *vlr); diff --git a/source/blender/render/intern/include/shading.h b/source/blender/render/intern/include/shading.h index 902899bce94..3390c32ebfe 100644 --- a/source/blender/render/intern/include/shading.h +++ b/source/blender/render/intern/include/shading.h @@ -29,6 +29,9 @@ struct RenderPart; struct RenderLayer; struct PixStr; struct LampRen; +struct VlakRen; +struct StrandSegment; +struct StrandPoint; /* shadeinput.c */ @@ -56,6 +59,8 @@ void shade_input_set_viewco(struct ShadeInput *shi, float x, float y, float z); void shade_input_set_uv(struct ShadeInput *shi); void shade_input_set_normals(struct ShadeInput *shi); void shade_input_set_shade_texco(struct ShadeInput *shi); +void shade_input_set_strand(struct ShadeInput *shi, struct StrandRen *strand, struct StrandPoint *spoint); +void shade_input_set_strand_texco(struct ShadeInput *shi, struct StrandRen *strand, struct StrandVert *svert, struct StrandPoint *spoint); void shade_input_do_shade(struct ShadeInput *shi, struct ShadeResult *shr); void shade_input_initialize(struct ShadeInput *shi, struct RenderPart *pa, struct RenderLayer *rl, int sample); diff --git a/source/blender/render/intern/include/strand.h b/source/blender/render/intern/include/strand.h new file mode 100644 index 00000000000..55c789de764 --- /dev/null +++ b/source/blender/render/intern/include/strand.h @@ -0,0 +1,192 @@ +/* + * $Id$ + * + * ***** BEGIN GPL/BL DUAL 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: none of this file. + * + * Contributor(s): Brecht Van Lommel. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifndef STRAND_H +#define STRAND_H + +struct StrandVert; +struct StrandRen; +struct StrandBuffer; +struct ShadeSample; +struct StrandPart; +struct Render; +struct RenderPart; +struct RenderBuckets; +struct RenderPrimitiveIterator; +struct ZSpan; + +typedef struct StrandPoint { + /* position within segment */ + float t; + + /* camera space */ + float co[3]; + float nor[3]; + float tan[3]; + float strandco; + float width; + + /* derivatives */ + float dtco[3], dsco[3]; + float dtstrandco; + + /* outer points */ + float co1[3], co2[3]; + float hoco1[4], hoco2[4]; + float zco1[3], zco2[3]; + + /* screen space */ + float hoco[4]; + float x, y; +} StrandPoint; + +typedef struct StrandSegment { + struct StrandVert *v[4]; + struct StrandRen *strand; + struct StrandBuffer *buffer; + float sqadaptcos; + + StrandPoint point1, point2; + int shaded; +} StrandSegment; + +void strand_eval_point(StrandSegment *sseg, StrandPoint *spoint); +void render_strand_segment(struct Render *re, struct StrandPart *spart, struct ZSpan *zspan, StrandSegment *sseg); +void project_strands(Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, int do_buckets); + +struct RenderBuckets *init_buckets(struct Render *re); +void add_buckets_primitive(struct RenderBuckets *buckets, float *min, float *max, void *prim); +void free_buckets(struct RenderBuckets *buckets); +void project_hoco_to_bucket(struct RenderBuckets *buckets, float *hoco, float *bucketco); + +struct RenderPrimitiveIterator *init_primitive_iterator(struct Render *re, struct RenderBuckets *buckets, struct RenderPart *pa); +void *next_primitive_iterator(struct RenderPrimitiveIterator *iter); +void free_primitive_iterator(struct RenderPrimitiveIterator *iter); + +#endif + +/* + * $Id$ + * + * ***** BEGIN GPL/BL DUAL 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: none of this file. + * + * Contributor(s): Brecht Van Lommel. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifndef STRAND_H +#define STRAND_H + +struct StrandVert; +struct StrandRen; +struct StrandBuffer; +struct ShadeSample; +struct StrandPart; +struct Render; +struct RenderPart; +struct RenderBuckets; +struct RenderPrimitiveIterator; +struct ZSpan; + +typedef struct StrandPoint { + /* position within segment */ + float t; + + /* camera space */ + float co[3]; + float nor[3]; + float tan[3]; + float strandco; + float width; + + /* derivatives */ + float dtco[3], dsco[3]; + float dtstrandco; + + /* outer points */ + float co1[3], co2[3]; + float hoco1[4], hoco2[4]; + float zco1[3], zco2[3]; + + /* screen space */ + float hoco[4]; + float x, y; +} StrandPoint; + +typedef struct StrandSegment { + struct StrandVert *v[4]; + struct StrandRen *strand; + struct StrandBuffer *buffer; + float sqadaptcos; + + StrandPoint point1, point2; + int shaded; +} StrandSegment; + +void strand_eval_point(StrandSegment *sseg, StrandPoint *spoint); +void render_strand_segment(struct Render *re, struct StrandPart *spart, struct ZSpan *zspan, StrandSegment *sseg); +void project_strands(Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, int do_buckets); + +struct RenderBuckets *init_buckets(struct Render *re); +void add_buckets_primitive(struct RenderBuckets *buckets, float *min, float *max, void *prim); +void free_buckets(struct RenderBuckets *buckets); +void project_hoco_to_bucket(struct RenderBuckets *buckets, float *hoco, float *bucketco); + +struct RenderPrimitiveIterator *init_primitive_iterator(struct Render *re, struct RenderBuckets *buckets, struct RenderPart *pa); +void *next_primitive_iterator(struct RenderPrimitiveIterator *iter); +void free_primitive_iterator(struct RenderPrimitiveIterator *iter); + +#endif + diff --git a/source/blender/render/intern/include/zbuf.h b/source/blender/render/intern/include/zbuf.h index ed3f93adfdd..0d88d40e7ac 100644 --- a/source/blender/render/intern/include/zbuf.h +++ b/source/blender/render/intern/include/zbuf.h @@ -50,6 +50,7 @@ void set_part_zbuf_clipflag(struct RenderPart *pa); void zbuffer_shadow(struct Render *re, struct LampRen *lar, int *rectz, int size, float jitx, float jity); void zbuffer_solid(struct RenderPart *pa, unsigned int layer, short layflag); unsigned short *zbuffer_transp_shade(struct RenderPart *pa, struct RenderLayer *rl, float *pass); +unsigned short *zbuffer_strands_shade(struct Render *re, struct RenderPart *pa, struct RenderLayer *rl, float *pass); void convert_zbuf_to_distbuf(struct RenderPart *pa, struct RenderLayer *rl); void zbuffer_sss(RenderPart *pa, unsigned int lay, void *handle, void (*func)(void *, int, int, int, int)); diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index b6e0616fbbc..9bf62e52f31 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -1212,12 +1212,10 @@ static void render_particle_system(Render *re, Object *ob, Object *par, PartEff if(useFluidsimParticles) { ma->alpha = iniAlpha; }// FSPARTICLE restore... } -/* ------------------------------------------------------------------------- */ - /* future thread problem... */ -static void static_particle_strand(Render *re, Object *ob, Material *ma, float *orco, float *uvco, int totuv, - float *vec, float *vec1, float ctime, int first, int line, - int adapt, float adapt_angle, float adapt_pix, int override_uv) +static void static_particle_strand(Render *re, Object *ob, Material *ma, float *orco, float *surfnor, + float *uvco, int totuv, 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; VlakRen *vlr; @@ -1314,6 +1312,11 @@ static void static_particle_strand(Render *re, Object *ob, Material *ma, float * vlr->ec= ME_V2V3; vlr->lay= ob->lay; + if(surfnor) { + float *snor= RE_vlakren_get_surfnor(re, vlr, 1); + VECCOPY(snor, surfnor); + } + if(uvco){ for(i=0; i<totuv; i++){ MTFace *mtf; @@ -1428,6 +1431,11 @@ static void static_particle_strand(Render *re, Object *ob, Material *ma, float * vlr->ec= ME_V2V3; vlr->lay= ob->lay; + if(surfnor) { + float *snor= RE_vlakren_get_surfnor(re, vlr, 1); + VECCOPY(snor, surfnor); + } + if(uvco){ for(i=0; i<totuv; i++){ MTFace *mtf; @@ -1683,7 +1691,7 @@ static void particle_billboard(Render *re, Object *ob, Material *ma, Object *bb_ } } static void render_new_particle(Render *re, Object *ob, DerivedMesh *dm, Material *ma, int path, int first, int line, - float time, float *loc, float *loc1, float *orco, int totuv, float *uvco, + 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) { HaloRen *har=0; @@ -1695,7 +1703,7 @@ static void render_new_particle(Render *re, Object *ob, DerivedMesh *dm, Materia if(har) har->lay= ob->lay; } else - static_particle_strand(re, ob, ma, orco, uvco, totuv, loc, loc1, time, first, line, adapt, adapt_angle, adapt_pix, override_uv); + static_particle_strand(re, ob, ma, orco, surfnor, uvco, totuv, loc, loc1, time, first, line, adapt, adapt_angle, adapt_pix, override_uv); } else{ har= RE_inithalo_particle(re, dm, ma, loc, NULL, orco, uvco, size, 0.0, seed); @@ -1715,8 +1723,12 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps ParticleKey *states=0; ParticleKey state; ParticleCacheKey *cache=0; - float loc[3],loc1[3],loc0[3],vel[3],imat[4][4], time; - float *orco=0,*uvco=0; + StrandBuffer *strandbuf=0; + StrandVert *svert=0; + StrandRen *strand=0; + RNG *rng= 0; + float loc[3],loc1[3],loc0[3],vel[3],mat[4][4],nmat[3][3],ornor[3],time; + float *orco=0,*surfnor=0,*uvco=0; 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; int i, a, k, max_k=0, totpart, totvlako, totverto, totuv=0, override_uv=-1; @@ -1750,15 +1762,12 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps return 0; if(G.rendering == 0) { /* preview render */ - if(part->flag & PART_CHILD_RENDER) - totchild = 0; - else - totchild = (int)((float)totchild * (float)part->disp / 100.0f); + totchild = (int)((float)totchild * (float)part->disp / 100.0f); } psys->flag|=PSYS_DRAWING; - BLI_srandom(psys->seed); + rng= rng_new(psys->seed); ma= give_render_material(re, ob, part->omat); @@ -1773,9 +1782,15 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps RE_vlakren_set_customdata_names(re, &psmd->dm->faceData); totuv=CustomData_number_of_layers(&psmd->dm->faceData,CD_MTFACE); - if(ma->texco & TEXCO_UV && totuv) + if(ma->texco & TEXCO_UV && totuv) { uvco = MEM_callocN(totuv*2*sizeof(float),"particle_uvs"); + if(ma->strand_uvname[0]) { + override_uv= CustomData_get_named_layer_index(&psmd->dm->faceData,CD_MTFACE,ma->strand_uvname); + override_uv-= CustomData_get_layer_index(&psmd->dm->faceData,CD_MTFACE); + } + } + if(part->draw_as==PART_DRAW_BB){ int first_uv=CustomData_get_layer_index(&psmd->dm->faceData,CD_MTFACE); @@ -1819,7 +1834,10 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps re->flag |= R_HALO; - Mat4Invert(imat,ob->obmat); + MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat); + MTC_Mat4Invert(ob->imat, mat); /* need to be that way, for imat texture */ + Mat3CpyMat4(nmat, ob->imat); + Mat3Transp(nmat); totvlako= re->totvlak; totverto= re->totvert; @@ -1838,8 +1856,8 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps //if(part->phystype==PART_PHYS_KEYED && (psys->flag&PSYS_BAKED)==0) // path_nbr*=psys->totkeyed; - if(path_nbr){ - if((ma->mode & (MA_HALO|MA_WIRE))==0){ + if(path_nbr) { + if((ma->mode & (MA_HALO|MA_WIRE))==0) { orco= MEM_mallocN(3*sizeof(float)*(totpart+totchild), "particle orcos"); if (!re->orco_hash) re->orco_hash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); @@ -1848,12 +1866,30 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps path=1; } - if(part->draw&PART_DRAW_REN_ADAPT){ + if(part->draw&PART_DRAW_REN_ADAPT) { adapt=1; adapt_pix=(float)part->adapt_pix; adapt_angle=cos((float)part->adapt_angle*(float)(M_PI/180.0)); } + if(re->r.renderer==R_INTERN && part->draw&PART_DRAW_REN_STRAND) { + strandbuf= RE_addStrandBuffer(re, ob, (totpart+totchild)*(path_nbr+1)); + strandbuf->ma= ma; + strandbuf->lay= ob->lay; + Mat4CpyMat4(strandbuf->winmat, re->winmat); + strandbuf->winx= re->winx; + strandbuf->winy= re->winy; + strandbuf->maxdepth= 2; /* TODO */ + strandbuf->adaptcos= cos((float)part->adapt_angle*(float)(M_PI/180.0)); + strandbuf->overrideuv= override_uv; + + 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; + } } } else if(keys_possible && part->draw&PART_DRAW_KEYS){ @@ -1872,7 +1908,8 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps /* 3. start creating renderable things */ for(a=0,pa=pars; a<totpart+totchild; a++, pa++) { - random = BLI_frand(); + random = rng_getFloat(rng); + if(a<totpart){ if(pa->flag & PARS_UNEXIST) continue; @@ -1895,10 +1932,10 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps /* 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,orco,0,0,0); + psys_particle_on_emitter(ob, psmd,tpart->from,tpa->num, -1,tpa->fuv,tpa->foffset,orco,ornor,0,0); } else - psys_particle_on_emitter(ob, psmd,part->from,pa->num,-1,pa->fuv,pa->foffset,orco,0,0,0); + psys_particle_on_emitter(ob, psmd,part->from,pa->num,-1,pa->fuv,pa->foffset,orco,ornor,0,0); 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); @@ -1913,8 +1950,6 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps psys_interpolate_uvs(mtface,mface->v4,pa->fuv,uvco+2*n); } - override_uv=CustomData_get_named_layer_index(&psmd->dm->faceData,CD_MTFACE,ma->strand_uvname)- - CustomData_get_layer_index(&psmd->dm->faceData,CD_MTFACE); } pa_size=pa->size; @@ -1930,7 +1965,8 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps } else{ ChildParticle *cpa= psys->child+a-totpart; - pa_time=psys_get_child_time(psys, a-totpart, cfra); + + pa_time=psys_get_child_time(psys, cpa, cfra); if((part->flag&PART_ABS_TIME)==0){ if(ma->ipo){ @@ -1945,14 +1981,14 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps } } - pa_size=psys_get_child_size(psys, a-totpart, cfra, &pa_time); + pa_size=psys_get_child_size(psys, cpa, cfra, &pa_time); r_tilt=2.0f*cpa->rand[2]; /* 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,orco,0,0,0); + cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,orco,ornor,0,0); if(uvco){ layer=psmd->dm->faceData.layers + CustomData_get_layer_index(&psmd->dm->faceData,CD_MFACE); @@ -1991,7 +2027,7 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps } } - if(path_nbr){ + if(path_nbr) { cache = psys->childcache[a-totpart]; max_k = (int)cache->steps; } @@ -2003,6 +2039,36 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps orco[2] = (orco[2]-loc_tex[2])/size_tex[2]; } + if(ma->mode_l & MA_STR_SURFDIFF) { + Mat3MulVecfl(nmat, ornor); + surfnor= ornor; + } + else + surfnor= NULL; + + if(strandbuf) { /* strand render */ + strand= RE_findOrAddStrand(re, re->totstrand++); + strand->buffer= strandbuf; + strand->vert= svert; + VECCOPY(strand->orco, orco); + + if(surfnor) { + float *snor= RE_strandren_get_surfnor(re, strand, 1); + VECCOPY(snor, surfnor); + } + + if(uvco){ + for(i=0; i<totuv; i++){ + if(i != override_uv) { + float *uv= RE_strandren_get_uv(re, strand, i, NULL, 1); + + uv[0]= uvco[2*i]; + uv[1]= uvco[2*i+1]; + } + } + } + } + for(k=0; k<=path_nbr; k++){ if(path_nbr){ time=(float)k/(float)path_nbr; @@ -2026,7 +2092,7 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps if(part->draw_as!=PART_DRAW_BB) MTC_Mat4MulVecfl(re->viewmat,loc); - if(part->draw_as==PART_DRAW_LINE){ + if(part->draw_as==PART_DRAW_LINE) { VECCOPY(vel,state.vel); //VECADD(vel,vel,state.co); MTC_Mat4Mul3Vecfl(re->viewmat,vel); @@ -2038,27 +2104,33 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps VECADDFAC(loc1,loc,vel,part->draw_line[1]); render_new_particle(re,ob,psmd->dm,ma,1,0,1,0.0f,loc0,loc1, - orco,totuv,uvco,hasize,seed,override_uv,0,0,0); + orco,surfnor,totuv,uvco,hasize,seed,override_uv,0,0,0); } - else if(part->draw_as==PART_DRAW_BB){ + else if(part->draw_as==PART_DRAW_BB) { VECCOPY(vel,state.vel); //MTC_Mat4Mul3Vecfl(re->viewmat,vel); particle_billboard(re,ob,ma,bb_ob,loc,vel,pa_size,part->bb_tilt*(1.0f-part->bb_rand_tilt*r_tilt), part->bb_align,part->draw&PART_DRAW_BB_LOCK, a,totpart+totchild,part->bb_uv_split,part->bb_anim,part->bb_split_offset,random,pa_time,part->bb_offset,uv); } + else if(strandbuf) { + VECCOPY(svert->co, loc); + svert->strandco= -1.0f + 2.0f*time; + svert++; + strand->totvert++; + } else{ if(k==1){ VECSUB(loc0,loc1,loc); VECADD(loc0,loc1,loc0); render_new_particle(re,ob,psmd->dm,ma,path,1,0,0.0f,loc1,loc0, - orco,totuv,uvco,hasize,seed,override_uv, + orco,surfnor,totuv,uvco,hasize,seed,override_uv, adapt,adapt_angle,adapt_pix); } if(path_nbr==0 || k) render_new_particle(re,ob,psmd->dm,ma,path,0,0,time,loc,loc1, - orco,totuv,uvco,hasize,seed,override_uv, + orco,surfnor,totuv,uvco,hasize,seed,override_uv, adapt,adapt_angle,adapt_pix); VECCOPY(loc1,loc); @@ -2067,6 +2139,9 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps if(orco1==0) orco+=3; + + if(re->test_break()) + break; } /* 4. clean up */ @@ -2083,6 +2158,8 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps if(states) MEM_freeN(states); + + rng_free(rng); psys->flag &= ~PSYS_DRAWING; @@ -2091,9 +2168,9 @@ static int render_new_particle_system(Render *re, Object *ob, ParticleSystem *ps psys->lattice=0; } - if(path && (ma->mode & MA_TANGENT_STR)==0) + if(path && (ma->mode_l & MA_TANGENT_STR)==0) calc_vertexnormals(re, totverto, totvlako, 0); - + return 1; } @@ -2249,7 +2326,7 @@ static void render_static_particle_system(Render *re, Object *ob, PartEff *paf) if(ctime + paf->staticstep < mtime) strandco= (ctime-pa->time)/(mtime-pa->time); - static_particle_strand(re, ob, ma, orco, 0, 0, vec, vec1, strandco, first, 0,0,0,0,-1); + static_particle_strand(re, ob, ma, orco, 0, 0, 0, vec, vec1, strandco, first, 0,0,0,0,-1); } } @@ -3704,10 +3781,11 @@ static void init_render_object(Render *re, Object *ob, Object *par, int index, i static double lasttime= 0.0; double time; float mat[4][4]; - int startface, startvert, allow_render=1; + int startface, startvert, startstrand, allow_render=1; startface=re->totvlak; startvert=re->totvert; + startstrand=re->totstrand; ob->flag |= OB_DONE; @@ -3748,18 +3826,20 @@ static void init_render_object(Render *re, Object *ob, Object *par, int index, i if(allow_render==0 && ob->type==OB_MESH) dm = mesh_create_derived_render(ob, CD_MASK_BAREMESH | CD_MASK_MTFACE | CD_MASK_MCOL); - for(; psys; psys=psys->next) + for(; psys; psys=psys->next) { render_new_particle_system(re, ob, psys); + psys_free_render_memory(ob, psys); + } if(dm) dm->release(dm); } /* generic post process here */ - if(startvert!=re->totvert) { - - RE_addRenderObject(re, ob, par, index, startvert, re->totvert, startface, re->totvlak); + if(startvert!=re->totvert || startstrand!=re->totstrand) + RE_addRenderObject(re, ob, par, index, startvert, re->totvert, startface, re->totvlak, startstrand, re->totstrand); + if(startvert!=re->totvert) { /* 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 */ @@ -3778,6 +3858,7 @@ static void init_render_object(Render *re, Object *ob, Object *par, int index, i /* clumsy copying still */ re->i.totvert= re->totvert; re->i.totface= re->totvlak; + re->i.totstrand= re->totstrand; re->i.tothalo= re->tothalo; re->i.totlamp= re->totlamp; re->stats_draw(&re->i); @@ -3841,7 +3922,7 @@ void RE_Database_Free(Render *re) free_sss(re); - re->totvlak=re->totvert=re->totlamp=re->tothalo= 0; + re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0; re->i.convertdone= 0; if(re->scene) @@ -4107,7 +4188,7 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) /* XXX add test if dbase was filled already? */ re->memArena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); - re->totvlak=re->totvert=re->totlamp=re->tothalo= 0; + re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0; re->lights.first= re->lights.last= NULL; re->lampren.first= re->lampren.last= NULL; @@ -4223,7 +4304,7 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) Object *obd= dob->ob; if (obd->restrictflag & OB_RESTRICT_RENDER) continue; - + Mat4CpyMat4(obd->obmat, dob->mat); /* group duplis need to set ob matrices correct, for deform. so no_draw is part handled */ @@ -4311,6 +4392,7 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) /* for now some clumsy copying still */ re->i.totvert= re->totvert; re->i.totface= re->totvlak; + re->i.totstrand= re->totstrand; re->i.tothalo= re->tothalo; re->i.totlamp= re->totlamp; re->stats_draw(&re->i); @@ -4354,7 +4436,7 @@ 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); + project_renderdata(re, projectverto, re->r.mode & R_PANORAMA, 0, 1); /* SSS */ if((re->r.mode & R_SSS) && !re->test_break()) @@ -4385,8 +4467,8 @@ static void database_fromscene_vectors(Render *re, Scene *scene, int timeoffset) /* XXX add test if dbase was filled already? */ re->memArena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); - re->totvlak=re->totvert=re->totlamp=re->tothalo= 0; - re->i.totface=re->i.totvert=re->i.totlamp=re->i.tothalo= 0; + re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0; + re->i.totface=re->i.totvert=re->i.totstrand=re->i.totlamp=re->i.tothalo= 0; re->lights.first= re->lights.last= NULL; slurph_opt= 0; @@ -4464,14 +4546,14 @@ static void database_fromscene_vectors(Render *re, Scene *scene, int timeoffset) } if(!re->test_break()) - project_renderdata(re, projectverto, re->r.mode & R_PANORAMA, 0); + project_renderdata(re, projectverto, re->r.mode & R_PANORAMA, 0, 1); /* do this in end, particles for example need cfra */ G.scene->r.cfra-=timeoffset; } /* choose to use static, to prevent giving too many args to this call */ -static void speedvector_project(Render *re, float *zco, VertRen *ver) +static void speedvector_project(Render *re, float *zco, float *co, float *ho) { static float pixelphix=0.0f, pixelphiy=0.0f, zmulx=0.0f, zmuly=0.0f; static int pano= 0; @@ -4499,14 +4581,14 @@ static void speedvector_project(Render *re, float *zco, VertRen *ver) } /* now map hocos to screenspace, uses very primitive clip still */ - if(ver->ho[3]<0.1f) div= 10.0f; - else div= 1.0f/ver->ho[3]; + if(ho[3]<0.1f) div= 10.0f; + else div= 1.0f/ho[3]; /* use cylinder projection */ if(pano) { float vec[3], ang; - /* angle between (0,0,-1) and (ver->co) */ - VECCOPY(vec, ver->co); + /* angle between (0,0,-1) and (co) */ + VECCOPY(vec, co); ang= saacos(-vec[2]/sqrt(vec[0]*vec[0] + vec[2]*vec[2])); if(vec[0]<0.0f) ang= -ang; @@ -4517,57 +4599,84 @@ static void speedvector_project(Render *re, float *zco, VertRen *ver) } else { - zco[0]= zmulx*(1.0f+ver->ho[0]*div); - zco[1]= zmuly*(1.0f+ver->ho[1]*div); + zco[0]= zmulx*(1.0f+ho[0]*div); + zco[1]= zmuly*(1.0f+ho[1]*div); + } +} + +static void calculate_speedvector(float *vectors, int step, float winsq, float winroot, float *co, float *ho, float *speed) +{ + float zco[2], len; + + speedvector_project(NULL, zco, co, ho); + + zco[0]= vectors[0] - zco[0]; + zco[1]= vectors[1] - zco[1]; + + /* enable nice masks for hardly moving stuff or float inaccuracy */ + if(zco[0]<0.1f && zco[0]>-0.1f && zco[1]<0.1f && zco[1]>-0.1f ) { + zco[0]= 0.0f; + zco[1]= 0.0f; + } + + /* maximize speed for image width, otherwise it never looks good */ + len= zco[0]*zco[0] + zco[1]*zco[1]; + if(len > winsq) { + len= winroot/sqrt(len); + zco[0]*= len; + zco[1]*= len; + } + + /* note; in main vecblur loop speedvec is negated again */ + if(step) { + speed[2]= -zco[0]; + speed[3]= -zco[1]; + } + else { + speed[0]= zco[0]; + speed[1]= zco[1]; } } -static void calculate_speedvectors(Render *re, float *vectors, int startvert, int endvert, int step) +static void calculate_speedvectors(Render *re, ObjectRen *obren, float *vectors, int step) { VertRen *ver= NULL; - float *speed, zco[2]; - float len; + StrandRen *strand= NULL; + float *speed, ho[4]; float winsq= re->winx*re->winy, winroot= sqrt(winsq); - int a; - - /* set first vertex OK */ - a= startvert-1; - ver= re->vertnodes[a>>8].vert + (a & 255); + int a, startvert, endvert, startstrand, endstrand; + + startvert= obren->startvert; + endvert= obren->endvert; + startstrand= obren->startstrand; + endstrand= obren->endstrand; - for(a=startvert; a<endvert; a++, vectors+=2) { - if((a & 255)==0) - ver= re->vertnodes[a>>8].vert; - else - ver++; - - speedvector_project(NULL, zco, ver); - - zco[0]= vectors[0] - zco[0]; - zco[1]= vectors[1] - zco[1]; - - /* enable nice masks for hardly moving stuff or float inaccuracy */ - if(zco[0]<0.1f && zco[0]>-0.1f && zco[1]<0.1f && zco[1]>-0.1f ) { - zco[0]= 0.0f; - zco[1]= 0.0f; - } + if(re->vertnodes) { + /* set first vertex OK */ + a= startvert-1; + ver= re->vertnodes[a>>8].vert + (a & 255); - /* maximize speed for image width, otherwise it never looks good */ - len= zco[0]*zco[0] + zco[1]*zco[1]; - if(len > winsq) { - len= winroot/sqrt(len); - zco[0]*= len; - zco[1]*= len; + for(a=startvert; a<endvert; a++, vectors+=2) { + if((a & 255)==0) ver= re->vertnodes[a>>8].vert; + else ver++; + + speed= RE_vertren_get_winspeed(re, ver, 1); + calculate_speedvector(vectors, step, winsq, winroot, ver->co, ver->ho, speed); } + } + + if(re->strandnodes) { + /* set first strand OK */ + a= startstrand-1; + strand= re->strandnodes[a>>8].strand + (a & 255); - speed= RE_vertren_get_winspeed(re, ver, 1); - /* note; in main vecblur loop speedvec is negated again */ - if(step) { - speed[2]= -zco[0]; - speed[3]= -zco[1]; - } - else { - speed[0]= zco[0]; - speed[1]= zco[1]; + for(a=startstrand; a<endstrand; a++, vectors+=2) { + if((a & 255)==0) strand= re->strandnodes[a>>8].strand; + else strand++; + + speed= RE_strandren_get_winspeed(re, strand, 1); + projectverto(strand->vert->co, re->winmat, ho); + calculate_speedvector(vectors, step, winsq, winroot, strand->vert->co, ho, speed); } } } @@ -4652,26 +4761,45 @@ static void copy_dbase_object_vectors(Render *re, ListBase *lb) { ObjectRen *obren, *obrenlb; VertRen *ver; - float *vec; - int a; - + StrandRen *strand; + float *vec, ho[4]; + int a, totvector; + for(obren= re->objecttable.first; obren; obren= obren->next) { obrenlb= MEM_dupallocN(obren); BLI_addtail(lb, obrenlb); - if(obren->endvert>obren->startvert) { - vec= obrenlb->vectors= MEM_mallocN(2*sizeof(float)*(obren->endvert- obren->startvert), "vector array"); - /* first vertex */ - a= obren->startvert-1; - ver= re->vertnodes[a>>8].vert + (a & 255); + totvector= obren->endvert - obren->startvert; + totvector+= obren->endstrand - obren->startstrand; - for(a=obren->startvert; a<obren->endvert; a++, vec+=2) { - if((a & 255)==0) - ver= re->vertnodes[a>>8].vert; - else - ver++; - - speedvector_project(NULL, vec, ver); + if(totvector > 0) { + vec= obrenlb->vectors= MEM_mallocN(2*sizeof(float)*totvector, "vector array"); + + if(obren->endvert > obren->startvert) { + /* first vertex */ + a= obren->startvert-1; + ver= re->vertnodes[a>>8].vert + (a & 255); + + for(a=obren->startvert; a<obren->endvert; a++, vec+=2) { + if((a & 255)==0) ver= re->vertnodes[a>>8].vert; + else ver++; + + speedvector_project(NULL, vec, ver->co, ver->ho); + } + } + + if(obren->endstrand > obren->startstrand) { + /* first strand */ + a= obren->startstrand-1; + strand= re->strandnodes[a>>8].strand + (a & 255); + + for(a=obren->startstrand; a<obren->endstrand; a++, vec+=2) { + if((a & 255)==0) strand= re->strandnodes[a>>8].strand; + else strand++; + + projectverto(strand->vert->co, re->winmat, ho); + speedvector_project(NULL, vec, strand->vert->co, ho); + } } } } @@ -4697,7 +4825,7 @@ void RE_Database_FromScene_Vectors(Render *re, Scene *sce) re->i.infostr= "Calculating previous vectors"; re->r.mode |= R_SPEED; - speedvector_project(re, NULL, NULL); /* initializes projection code */ + speedvector_project(re, NULL, NULL, NULL); /* initializes projection code */ /* creates entire dbase */ database_fromscene_vectors(re, sce, -1); @@ -4758,12 +4886,13 @@ void RE_Database_FromScene_Vectors(Render *re, Scene *sce) load_fluidsimspeedvectors(re, oldobren->vectors, obren->startvert, obren->endvert, step, obren->ob); } else { /* check if both have same amounts of vertices */ - if(obren->endvert-obren->startvert != oldobren->endvert-oldobren->startvert) { + if(obren->endvert-obren->startvert != oldobren->endvert-oldobren->startvert || + obren->endstrand-obren->startstrand != oldobren->endstrand-oldobren->startstrand) { printf("Warning: object %s has different amount of vertices on other frame\n", obren->ob->id.name+2); continue; } - calculate_speedvectors(re, oldobren->vectors, obren->startvert, obren->endvert, step); + calculate_speedvectors(re, obren, oldobren->vectors, step); } // not fluidsim } } @@ -4780,7 +4909,7 @@ void RE_Database_FromScene_Vectors(Render *re, Scene *sce) /* exported call to recalculate hoco for vertices, when winmat changed */ void RE_DataBase_ApplyWindow(Render *re) { - project_renderdata(re, projectverto, 0, 0); + project_renderdata(re, projectverto, 0, 0, 1); } /* setup for shaded view or bake, so only lamps and materials are initialized */ @@ -4819,7 +4948,7 @@ void RE_Database_Baking(Render *re, Scene *scene, int type, Object *actob) if(type!=RE_BAKE_LIGHT) re->memArena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); - re->totvlak=re->totvert=re->totlamp=re->tothalo= 0; + re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0; re->lights.first= re->lights.last= NULL; re->lampren.first= re->lampren.last= NULL; diff --git a/source/blender/render/intern/source/envmap.c b/source/blender/render/intern/source/envmap.c index ebb444833e8..cec96c1ab92 100644 --- a/source/blender/render/intern/source/envmap.c +++ b/source/blender/render/intern/source/envmap.c @@ -156,6 +156,9 @@ static Render *envmap_render_copy(Render *re, EnvMap *env) envre->bloha= re->bloha; envre->vlaknodeslen= re->vlaknodeslen; envre->vlaknodes= re->vlaknodes; + envre->strandnodeslen= re->strandnodeslen; + envre->strandnodes= re->strandnodes; + envre->strandbuckets= re->strandbuckets; envre->customdata_names= re->customdata_names; envre->raytree= re->raytree; @@ -176,6 +179,9 @@ static void envmap_free_render_copy(Render *envre) envre->bloha= NULL; envre->vlaknodeslen= 0; envre->vlaknodes= NULL; + envre->strandnodeslen= 0; + envre->strandnodes= NULL; + envre->strandbuckets= NULL; envre->customdata_names.first= envre->customdata_names.last= NULL; envre->raytree= NULL; @@ -403,7 +409,7 @@ static void render_envmap(Render *re, EnvMap *env) env_rotate_scene(envre, tmat, 1); init_render_world(envre); - project_renderdata(envre, projectverto, 0, 0); + project_renderdata(envre, projectverto, 0, 0, 1); env_layerflags(envre, env->notlay); env_hideobject(envre, env->object); env_set_imats(envre); diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 61bc6a84556..1c3867a702c 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -171,8 +171,9 @@ void RE_FreeRenderResult(RenderResult *res) RenderLayer *rl= res->layers.first; if(rl->rectf) MEM_freeN(rl->rectf); - /* acolrect is optionally allocated in shade_tile, only free here since it can be used for drawing */ + /* acolrect and scolrect are optionally allocated in shade_tile, only free here since it can be used for drawing */ if(rl->acolrect) MEM_freeN(rl->acolrect); + if(rl->scolrect) MEM_freeN(rl->scolrect); while(rl->passes.first) { RenderPass *rpass= rl->passes.first; @@ -1250,6 +1251,7 @@ static void render_tile_processor(Render *re, int firsttile) } freeparts(re); + R.strandbuckets= NULL; } /* calculus for how much 1 pixel rendered should rotate the 3d geometry */ @@ -1307,7 +1309,7 @@ static RenderPart *find_next_pano_slice(Render *re, int *minx, rctf *viewplane) Mat4CpyMat4(R.winmat, re->winmat); /* rotate database according to part coordinates */ - project_renderdata(re, projectverto, 1, -R.panodxp*phi); + project_renderdata(re, projectverto, 1, -R.panodxp*phi, 1); R.panosi= sin(R.panodxp*phi); R.panoco= cos(R.panodxp*phi); } @@ -1483,7 +1485,7 @@ static void threaded_tile_processor(Render *re) BLI_end_threads(&threads); freeparts(re); re->viewplane= viewplane; /* restore viewplane, modified by pano render */ - + R.strandbuckets= NULL; } /* currently only called by preview renders and envmap */ diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c index 636bee32e05..7fc4bbb3492 100644 --- a/source/blender/render/intern/source/rendercore.c +++ b/source/blender/render/intern/source/rendercore.c @@ -887,7 +887,7 @@ void zbufshadeDA_tile(RenderPart *pa) if(R.flag & R_HALO) if(rl->layflag & SCE_LAY_HALO) halo_tile(pa, rl->rectf, rl->lay); - + /* transp layer */ if(R.flag & R_ZTRA) { if(rl->layflag & SCE_LAY_ZTRA) { @@ -904,7 +904,7 @@ void zbufshadeDA_tile(RenderPart *pa) /* 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; @@ -929,6 +929,47 @@ void zbufshadeDA_tile(RenderPart *pa) if(ztramask) MEM_freeN(ztramask); } } + + /* strand rendering */ + if((rl->layflag & SCE_LAY_STRAND) && R.strandbufs.first) { + 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); + } + } + 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); + } + /* sky before edge */ if(rl->layflag & SCE_LAY_SKY) sky_tile(pa, rl->rectf); @@ -1081,6 +1122,24 @@ void zbufshade_tile(RenderPart *pa) } } } + + /* strand rendering */ + if((rl->layflag & SCE_LAY_STRAND) && R.strandbufs.first) { + 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); + } /* sky before edge */ if(rl->layflag & SCE_LAY_SKY) @@ -1594,7 +1653,7 @@ void add_halo_flare(Render *re) mode= R.r.mode; R.r.mode &= ~R_PANORAMA; - project_renderdata(&R, projectverto, 0, 0); + project_renderdata(&R, projectverto, 0, 0, 0); for(a=0; a<R.tothalo; a++) { if((a & 255)==0) har= R.bloha[a>>8]; diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c index 6939e70a71b..87306e9fbb5 100644 --- a/source/blender/render/intern/source/renderdatabase.c +++ b/source/blender/render/intern/source/renderdatabase.c @@ -79,6 +79,7 @@ #include "render_types.h" #include "renderdatabase.h" #include "texture.h" +#include "strand.h" #include "zbuf.h" /* ------------------------------------------------------------------------- */ @@ -100,6 +101,8 @@ #define RE_WINSPEED_ELEMS 4 #define RE_MTFACE_ELEMS 1 #define RE_MCOL_ELEMS 4 +#define RE_UV_ELEMS 2 +#define RE_SURFNOR_ELEMS 3 float *RE_vertren_get_sticky(Render *re, VertRen *ver, int verify) { @@ -368,6 +371,21 @@ MCol *RE_vlakren_get_mcol(Render *re, VlakRen *vlr, int n, char **name, int veri return node->mcol[index]; } +float *RE_vlakren_get_surfnor(Render *re, VlakRen *vlak, int verify) +{ + float *surfnor; + int nr= vlak->index>>8; + + surfnor= re->vlaknodes[nr].surfnor; + if(surfnor==NULL) { + if(verify) + surfnor= re->vlaknodes[nr].surfnor= MEM_callocN(256*RE_SURFNOR_ELEMS*sizeof(float), "surfnor table"); + else + return NULL; + } + return surfnor + (vlak->index & 255)*RE_SURFNOR_ELEMS; +} + VlakRen *RE_vlakren_copy(Render *re, VlakRen *vlr) { VlakRen *vlr1 = RE_findOrAddVlak(re, re->totvlak++); @@ -375,6 +393,7 @@ VlakRen *RE_vlakren_copy(Render *re, VlakRen *vlr) MCol *mcol, *mcol1; VlakTableNode *node = &re->vlaknodes[vlr->index>>8]; VlakTableNode *node1 = &re->vlaknodes[vlr1->index>>8]; + float *surfnor, *surfnor1; int i, index = vlr1->index; char *name; @@ -391,6 +410,12 @@ VlakRen *RE_vlakren_copy(Render *re, VlakRen *vlr) memcpy(mcol1, mcol, sizeof(MCol)*RE_MCOL_ELEMS); } + surfnor= RE_vlakren_get_surfnor(re, vlr, 0); + if(surfnor) { + surfnor1= RE_vlakren_get_surfnor(re, vlr1, 1); + VECCOPY(surfnor1, surfnor); + } + if (node->names && node1->names) node1->names[vlr1->index&255] = node->names[vlr->index&255]; @@ -480,7 +505,183 @@ VlakRen *RE_findOrAddVlak(Render *re, int nr) /* ------------------------------------------------------------------------ */ -void RE_addRenderObject(Render *re, Object *ob, Object *par, int index, int sve, int eve, int sfa, int efa) +float *RE_strandren_get_winspeed(Render *re, StrandRen *strand, int verify) +{ + float *winspeed; + int nr= strand->index>>8; + + winspeed= re->strandnodes[nr].winspeed; + if(winspeed==NULL) { + if(verify) + winspeed= re->strandnodes[nr].winspeed= MEM_callocN(256*RE_WINSPEED_ELEMS*sizeof(float), "winspeed table"); + else + return NULL; + } + return winspeed + (strand->index & 255)*RE_WINSPEED_ELEMS; +} + +float *RE_strandren_get_surfnor(Render *re, StrandRen *strand, int verify) +{ + float *surfnor; + int nr= strand->index>>8; + + surfnor= re->strandnodes[nr].surfnor; + if(surfnor==NULL) { + if(verify) + surfnor= re->strandnodes[nr].surfnor= MEM_callocN(256*RE_SURFNOR_ELEMS*sizeof(float), "surfnor table"); + else + return NULL; + } + return surfnor + (strand->index & 255)*RE_SURFNOR_ELEMS; +} + +float *RE_strandren_get_uv(Render *re, StrandRen *strand, int n, char **name, int verify) +{ + StrandTableNode *node; + int nr= strand->index>>8, strandindex= (strand->index&255); + int index= (n<<8) + strandindex; + + node= &re->strandnodes[nr]; + + if(verify) { + if(n>=node->totuv) { + float **uv= node->uv; + int size= (n+1)*256; + + node->uv= MEM_callocN(size*sizeof(MCol*), "Strand uv"); + + if(uv) { + size= node->totuv*256; + memcpy(node->uv, uv, size*sizeof(MCol*)); + MEM_freeN(uv); + } + + node->totuv= n+1; + + if (!node->names) { + size= sizeof(*node->names)*256; + node->names= MEM_callocN(size, "Strand names"); + } + } + + if(node->uv[index]==NULL) { + node->uv[index]= BLI_memarena_alloc(re->memArena, + sizeof(float)*RE_UV_ELEMS); + + node->names[strandindex]= re->customdata_names.last; + } + } + else { + if(n>=node->totuv || node->uv[index]==NULL) + return NULL; + + if(name) *name= node->names[strandindex]->mtface[n]; + } + + return node->uv[index]; +} + +MCol *RE_strandren_get_mcol(Render *re, StrandRen *strand, int n, char **name, int verify) +{ + StrandTableNode *node; + int nr= strand->index>>8, strandindex= (strand->index&255); + int index= (n<<8) + strandindex; + + node= &re->strandnodes[nr]; + + if(verify) { + if(n>=node->totmcol) { + MCol **mcol= node->mcol; + int size= (n+1)*256; + + node->mcol= MEM_callocN(size*sizeof(MCol*), "Strand mcol"); + + if(mcol) { + size= node->totmcol*256; + memcpy(node->mcol, mcol, size*sizeof(MCol*)); + MEM_freeN(mcol); + } + + node->totmcol= n+1; + + if (!node->names) { + size= sizeof(*node->names)*256; + node->names= MEM_callocN(size, "Strand names"); + } + } + + if(node->mcol[index]==NULL) { + node->mcol[index]= BLI_memarena_alloc(re->memArena, + sizeof(MCol)*RE_MCOL_ELEMS); + + node->names[strandindex]= re->customdata_names.last; + } + } + else { + if(n>=node->totmcol || node->mcol[index]==NULL) + return NULL; + + if(name) *name= node->names[strandindex]->mcol[n]; + } + + return node->mcol[index]; +} + +StrandRen *RE_findOrAddStrand(Render *re, int nr) +{ + StrandTableNode *temp; + StrandRen *v; + int a; + + if(nr<0) { + printf("error in findOrAddStrand: %d\n",nr); + return re->strandnodes[0].strand; + } + a= nr>>8; + + if (a>=re->strandnodeslen-1){ /* Need to allocate more columns..., and keep last element NULL for free loop */ + temp= re->strandnodes; + + re->strandnodes= MEM_mallocN(sizeof(StrandTableNode)*(re->strandnodeslen+TABLEINITSIZE) , "strandnodes"); + if(temp) memcpy(re->strandnodes, temp, re->strandnodeslen*sizeof(StrandTableNode)); + memset(re->strandnodes+re->strandnodeslen, 0, TABLEINITSIZE*sizeof(StrandTableNode)); + + re->strandnodeslen+=TABLEINITSIZE; /*Does this really need to be power of 2?*/ + if(temp) MEM_freeN(temp); + } + + v= re->strandnodes[a].strand; + + if(v==NULL) { + int i; + + v= (StrandRen *)MEM_callocN(256*sizeof(StrandRen),"findOrAddStrand"); + re->strandnodes[a].strand= v; + + for(i= (nr & 0xFFFFFF00), a=0; a<256; a++, i++) + v[a].index= i; + } + v+= (nr & 255); + return v; +} + +StrandBuffer *RE_addStrandBuffer(Render *re, Object *ob, int totvert) +{ + StrandBuffer *strandbuf; + + strandbuf= MEM_callocN(sizeof(StrandBuffer), "StrandBuffer"); + strandbuf->vert= MEM_callocN(sizeof(StrandVert)*totvert, "StrandVert"); + strandbuf->totvert= totvert; + strandbuf->ob= ob; + + BLI_addtail(&re->strandbufs, strandbuf); + + return strandbuf; +} + +/* ------------------------------------------------------------------------ */ + +void RE_addRenderObject(Render *re, Object *ob, Object *par, int index, int sve, int eve, int sfa, int efa, int sst, int est) { ObjectRen *obr= MEM_mallocN(sizeof(ObjectRen), "object render struct"); @@ -492,6 +693,8 @@ void RE_addRenderObject(Render *re, Object *ob, Object *par, int index, int sve, obr->endvert= eve; obr->startface= sfa; obr->endface= efa; + obr->startstrand= sst; + obr->endstrand= est; } void free_renderdata_vertnodes(VertTableNode *vertnodes) @@ -533,6 +736,8 @@ void free_renderdata_vlaknodes(VlakTableNode *vlaknodes) MEM_freeN(vlaknodes[a].mtface); if(vlaknodes[a].mcol) MEM_freeN(vlaknodes[a].mcol); + if(vlaknodes[a].surfnor) + MEM_freeN(vlaknodes[a].surfnor); if(vlaknodes[a].names) MEM_freeN(vlaknodes[a].names); } @@ -540,10 +745,35 @@ void free_renderdata_vlaknodes(VlakTableNode *vlaknodes) MEM_freeN(vlaknodes); } +void free_renderdata_strandnodes(StrandTableNode *strandnodes) +{ + int a; + + if(strandnodes==NULL) return; + + for(a=0; strandnodes[a].strand; a++) { + MEM_freeN(strandnodes[a].strand); + + if(strandnodes[a].uv) + MEM_freeN(strandnodes[a].uv); + if(strandnodes[a].mcol) + MEM_freeN(strandnodes[a].mcol); + if(strandnodes[a].winspeed) + MEM_freeN(strandnodes[a].winspeed); + if(strandnodes[a].surfnor) + MEM_freeN(strandnodes[a].surfnor); + if(strandnodes[a].names) + MEM_freeN(strandnodes[a].names); + } + + MEM_freeN(strandnodes); +} + void free_renderdata_tables(Render *re) { - int a=0; + StrandBuffer *strandbuf; CustomDataNames *cdn; + int a=0; if(re->bloha) { for(a=0; re->bloha[a]; a++) @@ -566,6 +796,17 @@ void free_renderdata_tables(Render *re) re->vlaknodeslen= 0; } + if(re->strandnodes) { + free_renderdata_strandnodes(re->strandnodes); + re->strandnodes= NULL; + re->strandnodeslen= 0; + } + + if(re->strandbuckets) { + free_buckets(re->strandbuckets); + re->strandbuckets= NULL; + } + for(cdn=re->customdata_names.first; cdn; cdn=cdn->next) { if(cdn->mtface) MEM_freeN(cdn->mtface); @@ -573,6 +814,10 @@ void free_renderdata_tables(Render *re) MEM_freeN(cdn->mcol); } + for(strandbuf=re->strandbufs.first; strandbuf; strandbuf=strandbuf->next) + if(strandbuf->vert) MEM_freeN(strandbuf->vert); + BLI_freelistN(&re->strandbufs); + BLI_freelistN(&re->customdata_names); BLI_freelistN(&re->objecttable); } @@ -906,14 +1151,14 @@ static int panotestclip(Render *re, int do_pano, float *v) - shadow buffering (shadbuf.c) */ -void project_renderdata(Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, float xoffs) +void project_renderdata(Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, float xoffs, int do_buckets) { VlakRen *vlr = NULL; VertRen *ver = NULL; HaloRen *har = NULL; float zn, vec[3], hoco[4]; int a; - + if(do_pano) { float panophi= xoffs; @@ -1029,6 +1274,7 @@ void project_renderdata(Render *re, void (*projectfunc)(float *, float mat[][4], vlr->flag &= ~R_VISIBLE; } + project_strands(re, projectfunc, do_pano, do_buckets); } /* ------------------------------------------------------------------------- */ diff --git a/source/blender/render/intern/source/shadbuf.c b/source/blender/render/intern/source/shadbuf.c index 5e010080ac0..006a26dc82e 100644 --- a/source/blender/render/intern/source/shadbuf.c +++ b/source/blender/render/intern/source/shadbuf.c @@ -403,7 +403,7 @@ void makeshadowbuf(Render *re, LampRen *lar) /* temp, will be restored */ MTC_Mat4SwapMat4(shb->persmat, re->winmat); - project_renderdata(re, projectvert, 0, 0); + project_renderdata(re, projectvert, 0, 0, 0); /* zbuffering */ rectz= MEM_mapallocN(sizeof(int)*shb->size*shb->size, "makeshadbuf"); diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c index 52a65b19824..acd04910565 100644 --- a/source/blender/render/intern/source/shadeinput.c +++ b/source/blender/render/intern/source/shadeinput.c @@ -48,6 +48,7 @@ #include "rendercore.h" #include "shadbuf.h" #include "shading.h" +#include "strand.h" #include "texture.h" /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -300,6 +301,252 @@ void shade_input_copy_triangle(ShadeInput *shi, ShadeInput *from) memcpy(shi, from, sizeof(struct ShadeInputCopy)); } +/* copy data from strand to shadeinput */ +void shade_input_set_strand(ShadeInput *shi, StrandRen *strand, StrandPoint *spoint) +{ + /* note, shi->mat is set in node shaders */ + shi->mat= shi->mat_override? shi->mat_override: strand->buffer->ma; + + shi->osatex= (shi->mat->texco & TEXCO_OSA); + shi->mode= shi->mat->mode_l; /* or-ed result for all nodes */ + + shi->puno= 0; /* always faces camera automatically */ + + /* shade_input_set_viewco equivalent */ + VECCOPY(shi->co, spoint->co); + VECCOPY(shi->view, shi->co); + Normalize(shi->view); + + shi->xs= (int)spoint->x; + shi->ys= (int)spoint->y; + + if(shi->osatex || (R.r.mode & R_SHADOW)) { + VECCOPY(shi->dxco, spoint->dtco); + VECCOPY(shi->dyco, spoint->dsco); + } + + /* dxview, dyview, not supported */ + + /* facenormal, simply viewco flipped */ + VECCOPY(shi->facenor, spoint->nor); + VECCOPY(shi->orignor, shi->facenor); + + /* shade_input_set_normals equivalent */ + if(shi->mat->mode & MA_TANGENT_STR) + VECCOPY(shi->vn, spoint->tan) + else + VECCOPY(shi->vn, spoint->nor) + + VECCOPY(shi->vno, shi->vn); +} + +void shade_input_set_strand_texco(ShadeInput *shi, StrandRen *strand, StrandVert *svert, StrandPoint *spoint) +{ + StrandBuffer *strandbuf= strand->buffer; + StrandVert *sv; + int mode= shi->mode; /* or-ed result for all nodes */ + short texco= shi->mat->texco; + + if((shi->mat->texco & TEXCO_REFL)) { + /* shi->dxview, shi->dyview, not supported */ + } + + if(shi->osatex && (texco & (TEXCO_NORM|TEXCO_REFL))) { + /* not supported */ + } + + if(mode & (MA_TANGENT_V|MA_NORMAP_TANG)) { + VECCOPY(shi->tang, spoint->tan); + } + + if(mode & MA_STR_SURFDIFF) { + float *surfnor= RE_strandren_get_surfnor(&R, strand, 0); + + if(surfnor) + VECCOPY(shi->surfnor, surfnor) + else + VECCOPY(shi->surfnor, shi->vn) + + if(shi->mat->strand_surfnor > 0.0f) { + 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); + } + } + + if(R.r.mode & R_SPEED) { + float *speed; + + speed= RE_strandren_get_winspeed(&R, strand, 0); + if(speed) + QUATCOPY(shi->winspeed, speed) + else + shi->winspeed[0]= shi->winspeed[1]= shi->winspeed[2]= shi->winspeed[3]= 0.0f; + } + + /* shade_input_set_shade_texco equivalent */ + if(texco & NEED_UV) { + if(texco & TEXCO_ORCO) { + VECCOPY(shi->lo, strand->orco); + /* no shi->osatex, orco derivatives are zero */ + } + + if(texco & TEXCO_GLOB) { + VECCOPY(shi->gl, shi->co); + MTC_Mat4MulVecfl(R.viewinv, shi->gl); + + if(shi->osatex) { + VECCOPY(shi->dxgl, shi->dxco); + MTC_Mat3MulVecfl(R.imat, shi->dxco); + VECCOPY(shi->dygl, shi->dyco); + MTC_Mat3MulVecfl(R.imat, shi->dyco); + } + } + + if(texco & TEXCO_STRAND) { + shi->strand= spoint->strandco; + + if(shi->osatex) { + shi->dxstrand= spoint->dtstrandco; + shi->dystrand= 0.0f; + } + } + + if((texco & TEXCO_UV) || (mode & (MA_VERTEXCOL|MA_VERTEXCOLP|MA_FACETEXTURE))) { + MCol *mcol; + float *uv; + char *name; + int i; + + shi->totuv= 0; + shi->totcol= 0; + + if(mode & (MA_VERTEXCOL|MA_VERTEXCOLP)) { + for (i=0; (mcol=RE_strandren_get_mcol(&R, strand, i, &name, 0)); i++) { + ShadeInputCol *scol= &shi->col[i]; + char *cp= (char*)mcol; + + 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; + } + + 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]; + } + else { + shi->vcol[0]= 0.0f; + shi->vcol[1]= 0.0f; + shi->vcol[2]= 0.0f; + } + } + + for (i=0; (uv=RE_strandren_get_uv(&R, strand, i, &name, 0)); i++) { + ShadeInputUV *suv= &shi->uv[i]; + + shi->totuv++; + suv->name= name; + + if(strandbuf->overrideuv == i) { + suv->uv[0]= -1.0f; + suv->uv[1]= spoint->strandco; + suv->uv[2]= 0.0f; + } + else { + suv->uv[0]= -1.0f + 2.0f*uv[0]; + suv->uv[1]= -1.0f + 2.0f*uv[1]; + suv->uv[2]= 0.0f; /* texture.c assumes there are 3 coords */ + } + + if(shi->osatex) { + suv->dxuv[0]= 0.0f; + suv->dxuv[1]= 0.0f; + suv->dyuv[0]= 0.0f; + suv->dyuv[1]= 0.0f; + } + + if((mode & MA_FACETEXTURE) && i==0) { + if((mode & (MA_VERTEXCOL|MA_VERTEXCOLP))==0) { + shi->vcol[0]= 1.0f; + shi->vcol[1]= 1.0f; + shi->vcol[2]= 1.0f; + } + } + } + + if(shi->totuv == 0) { + ShadeInputUV *suv= &shi->uv[0]; + + suv->uv[0]= 0.0f; + suv->uv[1]= spoint->strandco; + suv->uv[2]= 0.0f; /* texture.c assumes there are 3 coords */ + + if(mode & MA_FACETEXTURE) { + /* no tface? set at 1.0f */ + shi->vcol[0]= 1.0f; + shi->vcol[1]= 1.0f; + shi->vcol[2]= 1.0f; + } + } + + } + + if(texco & TEXCO_NORM) { + shi->orn[0]= -shi->vn[0]; + shi->orn[1]= -shi->vn[1]; + shi->orn[2]= -shi->vn[2]; + } + + if(mode & MA_RADIO) { + /* not supported */ + } + + if(texco & TEXCO_REFL) { + /* mirror reflection color textures (and envmap) */ + calc_R_ref(shi); /* wrong location for normal maps! XXXXXXXXXXXXXX */ + } + + if(texco & TEXCO_STRESS) { + /* not supported */ + } + + if(texco & TEXCO_TANGENT) { + if((mode & MA_TANGENT_V)==0) { + /* just prevent surprises */ + shi->tang[0]= shi->tang[1]= shi->tang[2]= 0.0f; + } + } + } + + shi->rad[0]= shi->rad[1]= shi->rad[2]= 0.0f; + + /* this only avalailable for scanline renders */ + if(shi->depth==0) { + if(texco & TEXCO_WINDOW) { + shi->winco[0]= -1.0f + 2.0f*spoint->x/(float)R.winx; + shi->winco[1]= -1.0f + 2.0f*spoint->y/(float)R.winy; + shi->winco[2]= 0.0f; + + /* not supported */ + if(shi->osatex) { + shi->dxwin[0]= 0.0f; + shi->dywin[1]= 0.0f; + shi->dxwin[0]= 0.0f; + shi->dywin[1]= 0.0f; + } + } + + if(texco & TEXCO_STICKY) { + /* not supported */ + } + } +} /* scanline pixel coordinates */ /* requires set_triangle */ @@ -571,6 +818,17 @@ void shade_input_set_shade_texco(ShadeInput *shi) else shi->tang[0]= shi->tang[1]= shi->tang[2]= 0.0f; } } + + if(mode & MA_STR_SURFDIFF) { + float *surfnor= RE_vlakren_get_surfnor(&R, shi->vlr, 0); + + if(surfnor) + VECCOPY(shi->surfnor, surfnor) + else + VECCOPY(shi->surfnor, shi->vn) + + shi->surfdist= 0.0f; + } if(R.r.mode & R_SPEED) { float *s1, *s2, *s3; diff --git a/source/blender/render/intern/source/shadeoutput.c b/source/blender/render/intern/source/shadeoutput.c index dc7dab7b1ca..037a3aa8409 100644 --- a/source/blender/render/intern/source/shadeoutput.c +++ b/source/blender/render/intern/source/shadeoutput.c @@ -1212,13 +1212,35 @@ static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int lacol[2]= lar->b; if(lar->mode & LA_TEXTURE) do_lamp_tex(lar, lv, shi, lacol); - - /* tangent case; calculate fake face normal, aligned with lampvector */ - /* note, vnor==vn is used as tangent trigger for buffer shadow */ + + /* tangent case; calculate fake face normal, aligned with lampvector */ + /* note, vnor==vn is used as tangent trigger for buffer shadow */ if(vlr->flag & R_TANGENT) { - float cross[3]; - Crossf(cross, lv, vn); - Crossf(vnor, cross, vn); + float cross[3], nstrand[3], blend; + + if(ma->mode & MA_STR_SURFDIFF) { + Crossf(cross, shi->surfnor, vn); + Crossf(nstrand, vn, cross); + + blend= INPR(nstrand, shi->surfnor); + CLAMP(blend, 0.0f, 1.0f); + + VecLerpf(vnor, nstrand, shi->surfnor, blend); + Normalize(vnor); + } + else { + Crossf(cross, lv, vn); + Crossf(vnor, cross, vn); + } + + if(ma->strand_surfnor > 0.0f) { + if(ma->strand_surfnor > shi->surfdist) { + blend= (ma->strand_surfnor - shi->surfdist)/ma->strand_surfnor; + VecLerpf(vnor, vnor, shi->surfnor, blend); + Normalize(vnor); + } + } + vnor[0]= -vnor[0];vnor[1]= -vnor[1];vnor[2]= -vnor[2]; vn= vnor; } diff --git a/source/blender/render/intern/source/strand.c b/source/blender/render/intern/source/strand.c new file mode 100644 index 00000000000..95768e1a0d8 --- /dev/null +++ b/source/blender/render/intern/source/strand.c @@ -0,0 +1,1138 @@ +/** + * $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) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: none of this file. + * + * Contributors: Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <math.h> +#include <string.h> +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_key_types.h" +#include "DNA_material_types.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_memarena.h" + +#include "BKE_key.h" +#include "BKE_utildefines.h" + +#include "render_types.h" +#include "initrender.h" +#include "rendercore.h" +#include "renderdatabase.h" +#include "renderpipeline.h" +#include "pixelblending.h" +#include "shading.h" +#include "strand.h" +#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, 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 { + RenderBuckets *buckets; + ListBase *list[6]; + int listindex, totlist; + BucketPrims *bpr; + int bprindex; +} 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->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]; + + return iter; +} + +void *next_primitive_iterator(RenderPrimitiveIterator *iter) +{ + 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++]; +} + +void free_primitive_iterator(RenderPrimitiveIterator *iter) +{ + MEM_freeN(iter); +} + +/* *************** */ + +static float strand_eval_width(Material *ma, float strandco) +{ + float fac; + + strandco= 0.5f*(strandco + 1.0f); + + if(ma->strand_ease!=0.0f) { + if(ma->strand_ease<0.0f) + fac= pow(strandco, 1.0+ma->strand_ease); + else + fac= pow(strandco, 1.0/(1.0f-ma->strand_ease)); + } + else fac= strandco; + + return ((1.0f-fac)*ma->strand_sta + (fac)*ma->strand_end); +} + +void strand_eval_point(StrandSegment *sseg, StrandPoint *spoint) +{ + Material *ma; + StrandBuffer *strandbuf; + float p[4][3], data[4], cross[3], w, dx, dy, t; + int type; + + strandbuf= sseg->buffer; + ma= sseg->buffer->ma; + t= spoint->t; + type= (strandbuf->flag & R_STRAND_BSPLINE)? KEY_BSPLINE: KEY_CARDINAL; + + VECCOPY(p[0], sseg->v[0]->co); + VECCOPY(p[1], sseg->v[1]->co); + VECCOPY(p[2], sseg->v[2]->co); + VECCOPY(p[3], sseg->v[3]->co); + + if(t == 0.0f) { + VECCOPY(spoint->co, sseg->v[1]->co); + spoint->strandco= sseg->v[1]->strandco; + + spoint->dtstrandco= (sseg->v[2]->strandco - sseg->v[0]->strandco); + if(sseg->v[0] != sseg->v[1]) + spoint->dtstrandco *= 0.5f; + } + else if(t == 1.0f) { + VECCOPY(spoint->co, sseg->v[2]->co); + spoint->strandco= sseg->v[2]->strandco; + + spoint->dtstrandco= (sseg->v[3]->strandco - sseg->v[1]->strandco); + if(sseg->v[3] != sseg->v[2]) + spoint->dtstrandco *= 0.5f; + } + else { + set_four_ipo(t, data, type); + spoint->co[0]= data[0]*p[0][0] + data[1]*p[1][0] + data[2]*p[2][0] + data[3]*p[3][0]; + spoint->co[1]= data[0]*p[0][1] + data[1]*p[1][1] + data[2]*p[2][1] + data[3]*p[3][1]; + spoint->co[2]= data[0]*p[0][2] + data[1]*p[1][2] + data[2]*p[2][2] + data[3]*p[3][2]; + spoint->strandco= (1.0f-t)*sseg->v[1]->strandco + t*sseg->v[2]->strandco; + } + + set_afgeleide_four_ipo(t, data, type); + spoint->dtco[0]= data[0]*p[0][0] + data[1]*p[1][0] + data[2]*p[2][0] + data[3]*p[3][0]; + spoint->dtco[1]= data[0]*p[0][1] + data[1]*p[1][1] + data[2]*p[2][1] + data[3]*p[3][1]; + spoint->dtco[2]= data[0]*p[0][2] + data[1]*p[1][2] + data[2]*p[2][2] + data[3]*p[3][2]; + + VECCOPY(spoint->tan, spoint->dtco); + Normalize(spoint->tan); + + VECCOPY(spoint->nor, spoint->co); + VECMUL(spoint->nor, -1.0f); + Normalize(spoint->nor); + + spoint->width= strand_eval_width(ma, spoint->strandco); + + /* outer points */ + Crossf(cross, spoint->co, spoint->tan); + + if(strandbuf->flag & R_STRAND_B_UNITS) + Normalize(cross); + + w= spoint->co[2]*strandbuf->winmat[2][3] + strandbuf->winmat[3][3]; + 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) { + w= 1.0f/w; + + if(spoint->width < w) + spoint->width= w; + VecMulf(cross, spoint->width*0.5f); + } + else + VecMulf(cross, spoint->width/w); + } + + VecSubf(spoint->co1, spoint->co, cross); + VecAddf(spoint->co2, spoint->co, cross); + + VECCOPY(spoint->dsco, cross); +} + +/* *************** */ + +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 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 interpolate_vec3(float *v1, float *v2, float t, float negt, float *v) +{ + v[0]= negt*v1[0] + t*v2[0]; + v[1]= negt*v1[1] + t*v2[1]; + v[2]= negt*v1[2] + t*v2[2]; +} + +static void interpolate_vec4(float *v1, float *v2, float t, float negt, float *v) +{ + v[0]= negt*v1[0] + t*v2[0]; + v[1]= negt*v1[1] + t*v2[1]; + v[2]= negt*v1[2] + t*v2[2]; + v[3]= negt*v1[3] + t*v2[3]; +} + +static void interpolate_shade_result(ShadeResult *shr1, ShadeResult *shr2, float t, ShadeResult *shr, int addpassflag) +{ + float negt= 1.0f - t; + + interpolate_vec4(shr1->combined, shr2->combined, t, negt, shr->combined); + + if(addpassflag & SCE_PASS_VECTOR) { + interpolate_vec4(shr1->winspeed, shr2->winspeed, t, negt, shr->winspeed); + } + /* optim... */ + if(addpassflag & ~(SCE_PASS_VECTOR)) { + if(addpassflag & SCE_PASS_RGBA) + interpolate_vec4(shr1->col, shr2->col, t, negt, shr->col); + if(addpassflag & SCE_PASS_NORMAL) { + interpolate_vec3(shr1->nor, shr2->nor, t, negt, shr->nor); + Normalize(shr->nor); + } + if(addpassflag & SCE_PASS_DIFFUSE) + interpolate_vec3(shr1->diff, shr2->diff, t, negt, shr->diff); + if(addpassflag & SCE_PASS_SPEC) + interpolate_vec3(shr1->spec, shr2->spec, t, negt, shr->spec); + if(addpassflag & SCE_PASS_SHADOW) + interpolate_vec3(shr1->shad, shr2->shad, t, negt, shr->shad); + if(addpassflag & SCE_PASS_AO) + interpolate_vec3(shr1->ao, shr2->ao, t, negt, shr->ao); + if(addpassflag & SCE_PASS_REFLECT) + interpolate_vec3(shr1->refl, shr2->refl, t, negt, shr->refl); + if(addpassflag & SCE_PASS_REFRACT) + 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); + } +} + +static void add_strand_obindex(RenderLayer *rl, int offset, Object *ob) +{ + RenderPass *rpass; + + for(rpass= rl->passes.first; rpass; rpass= rpass->next) { + if(rpass->passtype == SCE_PASS_INDEXOB) { + float *fp= rpass->rect + offset; + *fp= (float)ob->index; + break; + } + } +} + +static void do_strand_point_project(Render *re, ZSpan *zspan, float *co, float *hoco, float *zco) +{ + projectvert(co, re->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; +} + +#include "BLI_rand.h" +void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint); + +static void strand_shade_get(StrandPart *spart, int lookup, ShadeSample *ssamp, StrandPoint *spoint, StrandVert *svert, StrandSegment *sseg) +{ + ShadeResult *hashshr; + + if(lookup) { + hashshr= BLI_ghash_lookup(spart->hash, svert); + + if(!hashshr) { + strand_shade_point(spart->re, ssamp, sseg, spoint); + + 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); + } + } + else + strand_shade_point(spart->re, ssamp, sseg, spoint); +} + +static void strand_shade_segment(StrandPart *spart) +{ + StrandSegment *sseg= spart->segment; + int first, last; + + if(!sseg->shaded) { + first= (sseg->v[1] == &sseg->strand->vert[0]); + last= (sseg->v[2] == &sseg->strand->vert[sseg->strand->totvert-1]); + + 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; + } + +#if 0 + float c[3]; + + c[0]= BLI_frand(); + c[1]= BLI_frand(); + c[2]= BLI_frand(); + + spart->ssamp1.shr[0].combined[0] *= c[0]; + spart->ssamp1.shr[0].combined[1] *= c[1]; + spart->ssamp1.shr[0].combined[2] *= c[2]; + + 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 +} + +static void do_strand_blend(void *handle, int x, int y, float u, float v, float z) +{ + StrandPart *spart= (StrandPart*)handle; + StrandBuffer *buffer= spart->segment->buffer; + ShadeResult *shr; + float /**pass,*/ t; + int offset, zverg; + + /* check again solid z-buffer */ + offset = y*spart->rectx + x; + zverg= (int)z; + + 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; + + /* shade points if not shaded yet */ + strand_shade_segment(spart); + + /* 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); + + /* 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); + +#if 0 + /* fill in pass for preview */ + if(spart->sample == 0) { + pass= spart->pass + offset*4; + QUATCOPY(pass, shr->combined); + } +#endif + + if(spart->addpassflag & SCE_PASS_INDEXOB) + add_strand_obindex(spart->rl, offset, buffer->ob); + } +} + +static int strand_test_clip(Render *re, ZSpan *zspan, float *bounds, float *co, float *zcomp) +{ + float hoco[4]; + int clipflag= 0; + + projectvert(co, re->winmat, hoco); + + /* we compare z without perspective division for segment sorting */ + *zcomp= hoco[2]; + + if(hoco[0] > bounds[1]*hoco[3]) clipflag |= 1; + else if(hoco[0]< bounds[0]*hoco[3]) clipflag |= 2; + else if(hoco[1] > bounds[3]*hoco[3]) clipflag |= 4; + else if(hoco[1]< bounds[2]*hoco[3]) clipflag |= 8; + + return clipflag; +} + +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|R_VISIBLE; + if(sseg->buffer->ma->mode & MA_TANGENT_STR) + vlr.flag |= R_TANGENT; + shi->vlr= &vlr; + + /* 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); + + /* 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; + + VECCOPY(jco1, co1); + VECCOPY(jco2, co2); + VECCOPY(jco3, co3); + VECCOPY(jco4, co4); + + if(re->osa) { + jx= -re->jit[sample][0]; + jy= -re->jit[sample][1]; + + jco1[0] += jx; jco1[1] += jy; + jco2[0] += jx; jco2[1] += jy; + jco3[0] += jx; jco3[1] += jy; + jco4[0] += jx; jco4[1] += jy; + } + else if(re->i.curblur) { + jx= -re->jit[re->i.curblur-1][0]; + jy= -re->jit[re->i.curblur-1][1]; + + jco1[0] += jx; jco1[1] += jy; + jco2[0] += jx; jco2[1] += jy; + jco3[0] += jx; jco3[1] += jy; + jco4[0] += jx; jco4[1] += jy; + } + + spart->sample= sample; + + spart->t[0]= t-dt; + spart->t[1]= t-dt; + spart->t[2]= t; + zspan_scanconvert_strand(zspan, spart, jco1, jco2, jco3, do_strand_blend); + spart->t[0]= t-dt; + spart->t[1]= t; + spart->t[2]= t; + zspan_scanconvert_strand(zspan, spart, jco1, jco3, jco4, do_strand_blend); +} + +static void strand_render(Render *re, StrandPart *spart, ZSpan *zspan, StrandPoint *p1, StrandPoint *p2) +{ + if(spart) { + float t= p2->t; + float dt= p2->t - p1->t; + int a; + + if(re->osa) { + for(a=0; a<re->osa; a++) + do_scanconvert_strand(re, spart, zspan, t, dt, p1->zco2, p1->zco1, p2->zco1, p2->zco2, a); + } + else + 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, re->winmat, hoco1); + projectvert(p2->co, re->winmat, hoco2); + + /* render both strand and single pixel wire to counter aliasing */ + zbufclip4(zspan, 0, p1->hoco2, p1->hoco1, p2->hoco1, p2->hoco2, 0, 0, 0, 0); + zbufsinglewire(zspan, 0, hoco1, hoco2); + } +} + +static int strand_segment_recursive(Render *re, StrandPart *spart, ZSpan *zspan, StrandSegment *sseg, StrandPoint *p1, StrandPoint *p2, int depth) +{ + StrandPoint p; + StrandBuffer *buffer= sseg->buffer; + float dot, d1[2], d2[2], len1, len2; + + if(depth == buffer->maxdepth) + return 0; + + p.t= (p1->t + p2->t)*0.5f; + strand_eval_point(sseg, &p); + strand_project_point(buffer->winmat, buffer->winx, buffer->winy, &p); + + d1[0]= (p.x - p1->x); + d1[1]= (p.y - p1->y); + len1= d1[0]*d1[0] + d1[1]*d1[1]; + + d2[0]= (p2->x - p.x); + d2[1]= (p2->y - p.y); + len2= d2[0]*d2[0] + d2[1]*d2[1]; + + if(len1 == 0.0f || len2 == 0.0f) + return 0; + + dot= d1[0]*d2[0] + d1[1]*d2[1]; + if(dot*dot > sseg->sqadaptcos*len1*len2) + return 0; + + if(spart) { + do_strand_point_project(re, zspan, p.co1, p.hoco1, p.zco1); + do_strand_point_project(re, zspan, p.co2, p.hoco2, p.zco2); + } + else { + projectvert(p.co1, re->winmat, p.hoco1); + projectvert(p.co2, re->winmat, p.hoco2); + } + + if(!strand_segment_recursive(re, spart, zspan, sseg, p1, &p, depth+1)) + strand_render(re, spart, zspan, p1, &p); + if(!strand_segment_recursive(re, spart, zspan, sseg, &p, p2, depth+1)) + strand_render(re, spart, zspan, &p, p2); + + return 1; +} + +void render_strand_segment(Render *re, StrandPart *spart, ZSpan *zspan, StrandSegment *sseg) +{ + StrandBuffer *buffer= sseg->buffer; + StrandPoint *p1= &sseg->point1; + StrandPoint *p2= &sseg->point2; + + p1->t= 0.0f; + p2->t= 1.0f; + + strand_eval_point(sseg, p1); + strand_project_point(buffer->winmat, buffer->winx, buffer->winy, p1); + strand_eval_point(sseg, p2); + strand_project_point(buffer->winmat, buffer->winx, buffer->winy, p2); + + if(spart) { + do_strand_point_project(re, zspan, p1->co1, p1->hoco1, p1->zco1); + do_strand_point_project(re, zspan, p1->co2, p1->hoco2, p1->zco2); + do_strand_point_project(re, zspan, p2->co1, p2->hoco1, p2->zco1); + do_strand_point_project(re, zspan, p2->co2, p2->hoco2, p2->zco2); + } + else { + projectvert(p1->co1, re->winmat, p1->hoco1); + projectvert(p1->co2, re->winmat, p1->hoco2); + projectvert(p2->co1, re->winmat, p2->hoco1); + projectvert(p2->co2, re->winmat, p2->hoco2); + } + + if(!strand_segment_recursive(re, spart, zspan, sseg, p1, p2, 0)) + strand_render(re, 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; +} + +/* render call to fill in strands */ +unsigned short *zbuffer_strands_shade(Render *re, RenderPart *pa, RenderLayer *rl, float *pass) +{ + struct RenderPrimitiveIterator *iter; + ZSpan zspan; + StrandRen *strand=0; + StrandVert *svert; + StrandPart spart; + StrandSegment sseg; + StrandSortSegment *sortsegments = NULL, *sortseg, *firstseg; + MemArena *memarena; + float z[4], bounds[4]; + int a, b, resultsize, totsegment, clip[4]; + + if(re->test_break()) + return NULL; + if(re->strandbufs.first == NULL) + return NULL; + + /* 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.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; + + zbuf_alloc_span(&zspan, pa->rectx, pa->recty); + + /* needed for transform from hoco to zbuffer co */ + zspan.zmulx= ((float)re->winx)/2.0; + zspan.zmuly= ((float)re->winy)/2.0; + + zspan.zofsx= -pa->disprect.xmin; + zspan.zofsy= -pa->disprect.ymin; + + /* to center the sample position */ + zspan.zofsx -= 0.5f; + zspan.zofsy -= 0.5f; + + /* 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))) { + if(re->test_break()) + break; + + if(strand->clip) + continue; + + svert= strand->vert; + + /* keep clipping and z depth for 4 control points */ + clip[1]= strand_test_clip(re, &zspan, bounds, svert->co, &z[1]); + clip[2]= strand_test_clip(re, &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(re, &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->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]; + } + } + + free_primitive_iterator(iter); + + if(!re->test_break()) { + /* convert list to array and sort */ + sortsegments= MEM_mallocN(sizeof(StrandSortSegment)*totsegment, "StrandSortSegment"); + for(a=0, sortseg=firstseg; a<totsegment; a++, sortseg=sortseg->next) + sortsegments[a]= *sortseg; + qsort(sortsegments, totsegment, sizeof(StrandSortSegment), compare_strand_segment); + } + + BLI_memarena_free(memarena); + + spart.hash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + if(!re->test_break()) { + /* render segments in sorted order */ + sortseg= sortsegments; + for(a=0; a<totsegment; a++, sortseg++) { + if(re->test_break()) + break; + + sseg.strand= RE_findOrAddStrand(re, sortseg->strand); + sseg.buffer= sseg.strand->buffer; + sseg.sqadaptcos= sseg.buffer->adaptcos; + sseg.sqadaptcos *= sseg.sqadaptcos; + + svert= sseg.strand->vert + sortseg->segment; + sseg.v[0]= (sortseg->segment > 0)? (svert-1): svert; + sseg.v[1]= svert; + sseg.v[2]= svert+1; + sseg.v[3]= (sortseg->segment < sseg.strand->totvert-2)? svert+2: svert+1; + sseg.shaded= 0; + + spart.segment= &sseg; + + render_strand_segment(re, &spart, &zspan, &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); + + zbuf_free_span(&zspan); + + if(!(re->osa && (rl->layflag & SCE_LAY_SOLID))) { + MEM_freeN(spart.mask); + spart.mask= NULL; + } + + return spart.mask; +} + +void project_strands(Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, int do_buckets) +{ + 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(do_buckets) + re->strandbuckets= init_buckets(re); + + /* calculate view coordinates (and zbuffer value) */ + for(a=0; a<re->totstrand; a++) { + if((a & 255)==0) strand= re->strandnodes[a>>8].strand; + else strand++; + + strand->clip= ~0; + +#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) + + 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 + + INIT_MINMAX2(min, max); + svert= strand->vert; + for(b=0; b<strand->totvert; b++, svert++) { + //VECADD(vec, svert->co, bpad); + + /* 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) + + /* Go from wcs to hcs ... */ + projectfunc(vec, re->winmat, hoco); + /* ... and clip in that system. */ + strand->clip &= testclip(hoco); + + if(do_buckets) { + project_hoco_to_bucket(re->strandbuckets, hoco, bucketco); + DO_MINMAX2(bucketco, min, max); + } + } + + if(do_buckets) { +#if 0 + 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; + } +#endif + + add_buckets_primitive(re->strandbuckets, min, max, strand); + } + } +} + diff --git a/source/blender/render/intern/source/zbuf.c b/source/blender/render/intern/source/zbuf.c index 80b20a8e18e..cb92f8cefb3 100644 --- a/source/blender/render/intern/source/zbuf.c +++ b/source/blender/render/intern/source/zbuf.c @@ -39,8 +39,8 @@ #include "BLI_arithb.h" #include "BLI_blenlib.h" -#include "BLI_threads.h" #include "BLI_jitter.h" +#include "BLI_threads.h" #include "MTC_matrixops.h" #include "MEM_guardedalloc.h" @@ -69,6 +69,7 @@ #include "shadbuf.h" #include "shading.h" #include "sss.h" +#include "strand.h" /* own includes */ #include "zbuf.h" @@ -861,7 +862,7 @@ static int clipline(float *v1, float *v2) /* return 0: do not draw */ return 0; } -static void hoco_to_zco(ZSpan *zspan, float *zco, float *hoco) +void hoco_to_zco(ZSpan *zspan, float *zco, float *hoco) { float div; @@ -977,6 +978,34 @@ void zbufclipwire(ZSpan *zspan, int zvlnr, VlakRen *vlr) } +void zbufsinglewire(ZSpan *zspan, int zvlnr, float *ho1, float *ho2) +{ + float f1[4], f2[4]; + int c1, c2; + + c1= testclip(ho1); + c2= testclip(ho2); + + if(c1 | c2) { /* not in the middle */ + if(!(c1 & c2)) { /* not out completely */ + QUATCOPY(f1, ho1); + QUATCOPY(f2, ho2); + + if(clipline(f1, f2)) { + hoco_to_zco(zspan, f1, f1); + hoco_to_zco(zspan, f2, f2); + zspan->zbuflinefunc(zspan, zvlnr, f1, f2); + } + } + } + else { + hoco_to_zco(zspan, f1, ho1); + hoco_to_zco(zspan, f2, ho2); + + zspan->zbuflinefunc(zspan, zvlnr, f1, f2); + } +} + /** * Fill the z buffer, but invert z order, and add the face index to * the corresponing face buffer. @@ -1312,7 +1341,105 @@ static void zbuffillGL_onlyZ(ZSpan *zspan, int zvlnr, float *v1, float *v2, floa } /* 2d scanconvert for tria, calls func for each x,y coordinate and gives UV barycentrics */ -/* zspan should be initialized, has rect size and span buffers */ +void zspan_scanconvert_strand(ZSpan *zspan, void *handle, float *v1, float *v2, float *v3, void (*func)(void *, int, int, float, float, float) ) +{ + float x0, y0, x1, y1, x2, y2, z0, z1, z2, z; + float u, v, uxd, uyd, vxd, vyd, uy0, vy0, zxd, zyd, zy0, xx1; + float *span1, *span2; + int x, y, sn1, sn2, rectx= zspan->rectx, my0, my2; + + /* init */ + zbuf_init_span(zspan); + + /* set spans */ + zbuf_add_to_span(zspan, v1, v2); + zbuf_add_to_span(zspan, v2, v3); + zbuf_add_to_span(zspan, v3, v1); + + /* clipped */ + if(zspan->minp2==NULL || zspan->maxp2==NULL) return; + + if(zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; + if(zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; + + // printf("my %d %d\n", my0, my2); + if(my2<my0) return; + + /* ZBUF DX DY, in floats still */ + x1= v1[0]- v2[0]; + x2= v2[0]- v3[0]; + y1= v1[1]- v2[1]; + y2= v2[1]- v3[1]; + z1= v1[2]- v2[2]; + z2= v2[2]- v3[2]; + + x0= y1*z2-z1*y2; + y0= z1*x2-x1*z2; + z0= x1*y2-y1*x2; + + if(z0==0.0f) return; + + xx1= (x0*v1[0] + y0*v1[1])/z0 + v1[2]; + zxd= -(double)x0/(double)z0; + zyd= -(double)y0/(double)z0; + zy0= ((double)my2)*zyd + (double)xx1; + + z1= 1.0f; // (u1 - u2) + z2= 0.0f; // (u2 - u3) + + x0= y1*z2-z1*y2; + y0= z1*x2-x1*z2; + + xx1= (x0*v1[0] + y0*v1[1])/z0 + 1.0f; + uxd= -(double)x0/(double)z0; + uyd= -(double)y0/(double)z0; + uy0= ((double)my2)*uyd + (double)xx1; + + z1= -1.0f; // (v1 - v2) + z2= 1.0f; // (v2 - v3) + + x0= y1*z2-z1*y2; + y0= z1*x2-x1*z2; + + xx1= (x0*v1[0] + y0*v1[1])/z0; + vxd= -(double)x0/(double)z0; + vyd= -(double)y0/(double)z0; + vy0= ((double)my2)*vyd + (double)xx1; + + /* correct span */ + sn1= (my0 + my2)/2; + if(zspan->span1[sn1] < zspan->span2[sn1]) { + span1= zspan->span1+my2; + span2= zspan->span2+my2; + } + else { + span1= zspan->span2+my2; + span2= zspan->span1+my2; + } + + for(y=my2; y>=my0; y--, span1--, span2--) { + + sn1= floor(*span1); + sn2= floor(*span2); + sn1++; + + if(sn2>=rectx) sn2= rectx-1; + if(sn1<0) sn1= 0; + + u= (double)sn1*uxd + uy0; + v= (double)sn1*vxd + vy0; + z= (double)sn1*zxd + zy0; + + for(x= sn1; x<=sn2; x++, u+=uxd, v+=vxd, z+=zxd) + func(handle, x, y, u, v, z); + + uy0 -= uyd; + vy0 -= vyd; + zy0 -= zyd; + } +} + +/* scanconvert for strand triangles, calls func for each x,y coordinate and gives UV barycentrics and z */ void zspan_scanconvert(ZSpan *zspan, void *handle, float *v1, float *v2, float *v3, void (*func)(void *, int, int, float, float) ) { @@ -1957,7 +2084,10 @@ void zbuffer_shadow(Render *re, LampRen *lar, int *rectz, int size, float jitx, ZSpan zspan; VlakRen *vlr= NULL; Material *ma= NULL; - int a, ok=1, lay= -1; + StrandSegment sseg; + StrandRen *strand= NULL; + StrandVert *svert; + int a, b, ok=1, lay= -1; if(lar->mode & LA_LAYER) lay= lar->lay; @@ -2002,6 +2132,39 @@ void zbuffer_shadow(Render *re, LampRen *lar, int *rectz, int size, float jitx, } } } + + /* shadow */ + for(a=0; a<re->totstrand; a++) { + if((a & 255)==0) strand= re->strandnodes[a>>8].strand; + else strand++; + + if(strand->clip) + continue; + + 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; + } + + if(ok && (sseg.buffer->lay & lay)) { + 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; + + render_strand_segment(re, NULL, &zspan, &sseg); + } + } + } /* merge buffers */ if(lar->buftype==LA_SHADBUF_HALFWAY) { @@ -2019,7 +2182,6 @@ static void zbuffill_sss(ZSpan *zspan, int zvlnr, float *v1, float *v2, float *v double zxd, zyd, zy0, z; float x0, y0, x1, y1, x2, y2, z0, z1, z2, xx1, *span1, *span2; int x, y, sn1, sn2, rectx= zspan->rectx, my0, my2; - /* init */ zbuf_init_span(zspan); @@ -2824,7 +2986,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 */ -static 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, long *rdrect) { RenderPass *rpass; @@ -2852,7 +3014,6 @@ static void add_transp_speed(RenderLayer *rl, int offset, float *speed, float al fp[2]= speed[2]; fp[3]= speed[3]; } - } break; } @@ -2877,7 +3038,7 @@ static void add_transp_obindex(RenderLayer *rl, int offset, int facenr) /* ONLY OSA! merge all shaderesult samples to one */ /* target should have been cleared */ -static void merge_transp_passes(RenderLayer *rl, ShadeResult *shr) +void merge_transp_passes(RenderLayer *rl, ShadeResult *shr) { RenderPass *rpass; float weight= 1.0f/((float)R.osa); @@ -2961,7 +3122,7 @@ static void merge_transp_passes(RenderLayer *rl, ShadeResult *shr) } -static void add_transp_passes(RenderLayer *rl, int offset, ShadeResult *shr, float alpha) +void add_transp_passes(RenderLayer *rl, int offset, ShadeResult *shr, float alpha) { RenderPass *rpass; @@ -3113,11 +3274,11 @@ static void addvecmul(float *v1, float *v2, float fac) v1[2]= v1[2]+fac*v2[2]; } -static int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassflag) +int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassflag) { - int a, sample, retval = R.osa; + int a, sample, osa = (R.osa? R.osa: 1), retval = osa; - for(a=0; a < R.osa; a++, samp_shr++) { + for(a=0; a < osa; a++, samp_shr++) { ShadeInput *shi= ssamp->shi; ShadeResult *shr= ssamp->shr; @@ -3159,7 +3320,7 @@ static int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassf addvecmul(samp_shr->refr, shr->refr, fac); if(addpassflag & SCE_PASS_RADIO) - addvecmul(samp_shr->refr, shr->rad, fac); + addvecmul(samp_shr->rad, shr->rad, fac); } } } @@ -3169,7 +3330,7 @@ static int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassf return retval; } -static void reset_sky_speedvectors(RenderPart *pa, RenderLayer *rl) +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 */ @@ -3178,7 +3339,7 @@ static void reset_sky_speedvectors(RenderPart *pa, RenderLayer *rl) fp= RE_RenderLayerGetPass(rl, SCE_PASS_VECTOR); if(fp==NULL) return; - col= rl->acolrect+3; + col= rectf+3; for(a= 4*pa->rectx*pa->recty -4; a>=0; a-=4) { if(col[a]==0.0f) { @@ -3222,7 +3383,10 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas /* general shader info, passes */ shade_sample_initialize(&ssamp, pa, rl); addpassflag= rl->passflag & ~(SCE_PASS_Z|SCE_PASS_COMBINED); - addzbuf= rl->passflag & SCE_PASS_Z; + if((rl->layflag & SCE_LAY_STRAND) && R.strandbufs.first) + addzbuf= 1; /* strands layer needs the z-buffer */ + else + addzbuf= rl->passflag & SCE_PASS_Z; if(R.osa) sampalpha= 1.0f/(float)R.osa; @@ -3251,7 +3415,7 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas /* zero alpha pixels get speed vector max again */ if(addpassflag & SCE_PASS_VECTOR) if(rl->layflag & SCE_LAY_SOLID) - reset_sky_speedvectors(pa, rl); + reset_sky_speedvectors(pa, rl, rl->acolrect); /* filtered render, for now we assume only 1 filter size */ if(pa->crop) { diff --git a/source/blender/src/buttons_object.c b/source/blender/src/buttons_object.c index 0f0845cecd1..150ba466ecb 100644 --- a/source/blender/src/buttons_object.c +++ b/source/blender/src/buttons_object.c @@ -3603,8 +3603,7 @@ static void object_panel_particle_children(Object *ob) uiNewPanelTabbed("Extras", "Particle"); if(uiNewPanel(curarea, block, "Children", "Particle", 1300, 0, 318, 204)==0) return; - uiDefButS(block, MENU, B_PART_ALLOC_CHILD, "Children from:%t|Faces%x2|Particles%x1|None%x0", butx,buty,butw/2,buth, &part->childtype, 14.0, 0.0, 0, 0, "Create child particles"); - uiDefButBitI(block, TOG, PART_CHILD_RENDER, B_PART_RECALC_CHILD, "Only Render", butx+butw/2,buty,butw/2,buth, &part->flag, 0, 0, 0, 0, "Create child particles only when rendering"); + uiDefButS(block, MENU, B_PART_ALLOC_CHILD, "Children from:%t|Faces%x2|Particles%x1|None%x0", butx,buty,butw,buth, &part->childtype, 14.0, 0.0, 0, 0, "Create child particles"); if(part->childtype==0) return; @@ -3615,26 +3614,32 @@ static void object_panel_particle_children(Object *ob) } uiBlockBeginAlign(block); + + buty -= buth/2; + uiDefButI(block, NUM, B_PART_ALLOC_CHILD, "Amount:", butx,(buty-=buth),butw,buth, &part->child_nbr, 0.0, MAX_PART_CHILDREN, 0, 0, "Amount of children/parent"); + uiDefButI(block, NUM, B_DIFF, "Render Amount:", butx,(buty-=buth),butw,buth, &part->ren_child_nbr, 0.0, MAX_PART_CHILDREN, 0, 0, "Amount of children/parent for rendering"); if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES) { - uiDefButI(block, NUM, B_PART_ALLOC_CHILD, "Amount:", butx,(buty-=2*buth),butw,buth, &part->child_nbr, 0.0, MAX_PART_CHILDREN, 0, 0, "Amount of children/parent"); uiDefButF(block, NUMSLI, B_PART_DISTR_CHILD, "VParents:", butx,(buty-=buth),butw,buth, &part->parents, 0.0, 1.0, 1, 3, "Relative amount of virtual parents"); } else { - uiDefButI(block, NUM, B_PART_ALLOC_CHILD, "Amount:", butx,(buty-=2*buth),butw,buth, &part->child_nbr, 0.0, MAX_PART_CHILDREN, 0, 0, "Amount of children/parent"); uiDefButF(block, NUM, B_PART_RECALC_CHILD, "Rad:", butx,(buty-=buth),butw,buth, &part->childrad, 0.0, 10.0, 1, 3, "Radius of children around parent"); uiDefButF(block, NUMSLI, B_PART_RECALC_CHILD, "Round:", butx,(buty-=buth),butw,buth, &part->childflat, 0.0, 1.0, 1, 3, "Roundness of children around parent"); } uiBlockEndAlign(block); + buty -= buth/2; + /* clump */ uiBlockBeginAlign(block); uiDefButF(block, NUMSLI, B_PART_RECALC_CHILD, "Clump:", butx,(buty-=buth),butw,buth, &part->clumpfac, -1.0, 1.0, 1, 3, "Amount of clumpimg"); uiDefButF(block, NUMSLI, B_PART_RECALC_CHILD, "Shape:", butx,(buty-=buth),butw,buth, &part->clumppow, -0.999, 0.999, 1, 3, "Shape of clumpimg"); uiBlockEndAlign(block); + buty -= buth/2; + uiBlockBeginAlign(block); - uiDefButF(block, NUM, B_PART_REDRAW, "Size:", butx,(buty-=2*buth),butw/2,buth, &part->childsize, 0.01, 100, 10, 1, "A multiplier for the child particle size"); + uiDefButF(block, NUM, B_PART_REDRAW, "Size:", butx,(buty-=buth),butw/2,buth, &part->childsize, 0.01, 100, 10, 1, "A multiplier for the child particle size"); uiDefButF(block, NUM, B_PART_REDRAW, "Rand:", butx+butw/2,buty,butw/2,buth, &part->childrandsize, 0.0, 1.0, 10, 1, "Random variation to the size of the child particles"); if(part->childtype==PART_CHILD_FACES) { uiDefButF(block, NUM, B_PART_REDRAW, "Spread:",butx,(buty-=buth),butw/2,buth, &part->childspread, -1.0, 1.0, 10, 1, "Spread children from the faces"); @@ -3645,10 +3650,11 @@ static void object_panel_particle_children(Object *ob) butx=160; buty=180; - uiDefButBitS(block, TOG, 1, B_PART_REDRAW, "Kink/Branch", butx,(buty-=buth),butw,buth, &kink_ui, 0, 0, 0, 0, "Show kink and branch options"); if(kink_ui) { + buty -= buth/2; + /* kink */ uiBlockBeginAlign(block); if(part->kink) { @@ -3677,15 +3683,19 @@ static void object_panel_particle_children(Object *ob) } else { /* rough */ + buty -= buth/2; + uiBlockBeginAlign(block); - uiDefButF(block, NUMSLI, B_PART_RECALC_CHILD, "Rough1:", butx,(buty-=2*buth),butw,buth, &part->rough1, 0.0, 10.0, 1, 3, "Amount of location dependant rough"); + uiDefButF(block, NUMSLI, B_PART_RECALC_CHILD, "Rough1:", butx,(buty-=buth),butw,buth, &part->rough1, 0.0, 10.0, 1, 3, "Amount of location dependant rough"); uiDefButF(block, NUM, B_PART_RECALC_CHILD, "Size1:", butx,(buty-=buth),butw,buth, &part->rough1_size, 0.01, 10.0, 1, 3, "Size of location dependant rough"); uiBlockEndAlign(block); + buty -= buth/2; uiBlockBeginAlign(block); uiDefButF(block, NUMSLI, B_PART_RECALC_CHILD, "Rough2:", butx,(buty-=buth),butw,buth, &part->rough2, 0.0, 10.0, 1, 3, "Amount of random rough"); uiDefButF(block, NUM, B_PART_RECALC_CHILD, "Size2:", butx,(buty-=buth),butw,buth, &part->rough2_size, 0.01, 10.0, 1, 3, "Size of random rough"); uiDefButF(block, NUMSLI, B_PART_RECALC_CHILD, "Thresh:", butx,(buty-=buth),butw,buth, &part->rough2_thres, 0.00, 1.0, 1, 3, "Amount of particles left untouched by random rough"); uiBlockEndAlign(block); + buty -= buth/2; uiBlockBeginAlign(block); uiDefButF(block, NUMSLI, B_PART_RECALC_CHILD, "RoughE:", butx,(buty-=buth),butw,buth, &part->rough_end, 0.0, 10.0, 1, 3, "Amount of end point rough"); uiDefButF(block, NUMSLI, B_PART_RECALC_CHILD, "Shape:", butx,(buty-=buth),butw,buth, &part->rough_end_shape, 0.0, 10.0, 1, 3, "Shape of end point rough"); @@ -3861,7 +3871,7 @@ static void object_panel_particle_visual(Object *ob) uiDefBut(block, LABEL, 0, "Render:", butx,(buty-=buth),butw,buth, NULL, 0.0, 0, 0, 0, ""); uiBlockBeginAlign(block); uiDefButS(block, NUM, B_PART_DISTR, "Material:", butx,(buty-=buth),butw-30,buth, &part->omat, 1.0, 16.0, 0, 0, "Specify material used for the particles"); - uiDefButBitS(block, TOG, PART_DRAW_MAT_COL, B_PART_REDRAW, "Col", butx+butw-30,buty,30,buth, &part->draw, 0, 0, 0, 0, "Draw particles using material's diffuse color"); + uiDefButBitS(block, TOG, PART_DRAW_MAT_COL, B_PART_RECALC, "Col", butx+butw-30,buty,30,buth, &part->draw, 0, 0, 0, 0, "Draw particles using material's diffuse color"); uiDefButBitS(block, TOG, PART_DRAW_EMITTER, B_PART_REDRAW, "Emitter", butx,(buty-=buth),butw/2,buth, &part->draw, 0, 0, 0, 0, "Render emitter Object also"); uiDefButBitS(block, TOG, PART_DRAW_PARENT, B_PART_REDRAW, "Parents", butx+butw/2,buty,butw/2,buth, &part->draw, 0, 0, 0, 0, "Render parent particles"); uiDefButBitI(block, TOG, PART_UNBORN, B_PART_REDRAW, "Unborn", butx,(buty-=buth),butw/2,buth, &part->flag, 0, 0, 0, 0, "Show particles before they are emitted"); @@ -3917,10 +3927,16 @@ static void object_panel_particle_visual(Object *ob) uiDefButBitI(block, TOG, PART_HAIR_BSPLINE, B_PART_RECALC, "B-Spline", butx,(buty-=buth),butw,buth, &part->flag, 0, 0, 0, 0, "Interpolate hair using B-Splines"); uiBlockBeginAlign(block); - uiDefButBitS(block, TOG, PART_DRAW_REN_ADAPT, B_PART_REDRAW, "Adaptive render", butx,buty-=buth,butw,buth, &part->draw, 0, 0, 0, 0, "Draw steps of the particle path"); - if(part->draw & PART_DRAW_REN_ADAPT) { + uiDefButBitS(block, TOG, PART_DRAW_REN_STRAND, B_PART_REDRAW, "Strand render", butx,buty-=buth,butw,buth, &part->draw, 0, 0, 0, 0, "Use the strand primitive for rendering"); + if(part->draw & PART_DRAW_REN_STRAND) { uiDefButS(block, NUM, B_PART_REDRAW, "Angle:", butx,(buty-=buth),butw,buth, &part->adapt_angle, 0.0, 45.0, 0, 0, "How many degrees path has to curve to make another render segment"); - uiDefButS(block, NUM, B_PART_REDRAW, "Pixel:", butx,(buty-=buth),butw,buth, &part->adapt_pix, 0.0, 50.0, 0, 0, "How many pixels path has to cover to make another render segment"); + } + else { + uiDefButBitS(block, TOG, PART_DRAW_REN_ADAPT, B_PART_REDRAW, "Adaptive render", butx,buty-=buth,butw,buth, &part->draw, 0, 0, 0, 0, "Draw steps of the particle path"); + if(part->draw & PART_DRAW_REN_ADAPT) { + uiDefButS(block, NUM, B_PART_REDRAW, "Angle:", butx,(buty-=buth),butw/2,buth, &part->adapt_angle, 0.0, 45.0, 0, 0, "How many degrees path has to curve to make another render segment"); + uiDefButS(block, NUM, B_PART_REDRAW, "Pixel:", butx+butw/2,buty,(butw+1)/2,buth, &part->adapt_pix, 0.0, 50.0, 0, 0, "How many pixels path has to cover to make another render segment"); + } } } else { diff --git a/source/blender/src/buttons_scene.c b/source/blender/src/buttons_scene.c index 9da6cff1387..7a4093dd965 100644 --- a/source/blender/src/buttons_scene.c +++ b/source/blender/src/buttons_scene.c @@ -1967,11 +1967,12 @@ static void render_panel_layers(void) uiBlockBeginAlign(block); uiDefButBitI(block, TOG, SCE_LAY_ALL_Z, B_NOP,"AllZ", 10, 85, 40, 20, &srl->layflag, 0, 0, 0, 0, "Fill in Z values for all not-rendered faces, for masking"); uiBlockBeginAlign(block); - uiDefButBitI(block, TOG, SCE_LAY_SOLID, B_NOP,"Solid", 50, 85, 60, 20, &srl->layflag, 0, 0, 0, 0, "Render Solid faces in this Layer"); - uiDefButBitI(block, TOG, SCE_LAY_HALO, B_NOP,"Halo", 110, 85, 55, 20, &srl->layflag, 0, 0, 0, 0, "Render Halos in this Layer (on top of Solid)"); - uiDefButBitI(block, TOG, SCE_LAY_ZTRA, B_NOP,"Ztra", 165, 85, 55, 20, &srl->layflag, 0, 0, 0, 0, "Render Z-Transparent faces in this Layer (On top of Solid and Halos)"); - uiDefButBitI(block, TOG, SCE_LAY_SKY, B_NOP,"Sky", 220, 85, 40, 20, &srl->layflag, 0, 0, 0, 0, "Render Sky or backbuffer in this Layer"); - uiDefButBitI(block, TOG, SCE_LAY_EDGE, B_NOP,"Edge", 260, 85, 50, 20, &srl->layflag, 0, 0, 0, 0, "Render Edge-enhance in this Layer (only works for Solid faces)"); + uiDefButBitI(block, TOG, SCE_LAY_SOLID, B_NOP,"Solid", 50, 85, 45, 20, &srl->layflag, 0, 0, 0, 0, "Render Solid faces in this Layer"); + uiDefButBitI(block, TOG, SCE_LAY_HALO, B_NOP,"Halo", 95, 85, 40, 20, &srl->layflag, 0, 0, 0, 0, "Render Halos in this Layer (on top of Solid)"); + uiDefButBitI(block, TOG, SCE_LAY_ZTRA, B_NOP,"Ztra", 135, 85, 40, 20, &srl->layflag, 0, 0, 0, 0, "Render Z-Transparent faces in this Layer (On top of Solid and Halos)"); + uiDefButBitI(block, TOG, SCE_LAY_SKY, B_NOP,"Sky", 175, 85, 40, 20, &srl->layflag, 0, 0, 0, 0, "Render Sky or backbuffer in this Layer"); + uiDefButBitI(block, TOG, SCE_LAY_EDGE, B_NOP,"Edge", 215, 85, 45, 20, &srl->layflag, 0, 0, 0, 0, "Render Edge-enhance in this Layer (only works for Solid faces)"); + uiDefButBitI(block, TOG, SCE_LAY_STRAND, B_NOP,"Strand",260, 85, 50, 20, &srl->layflag, 0, 0, 0, 0, "Render Strands in this Layer"); uiDefIDPoinBut(block, test_grouppoin_but, ID_GR, B_SET_PASS, "Light:", 10, 65, 150, 20, &(srl->light_override), "Name of Group to use as Lamps instead"); uiDefIDPoinBut(block, test_matpoin_but, ID_MA, B_SET_PASS, "Mat:", 160, 65, 150, 20, &(srl->mat_override), "Name of Material to use as Materials instead"); diff --git a/source/blender/src/buttons_shading.c b/source/blender/src/buttons_shading.c index fbc449b992b..19f60801735 100644 --- a/source/blender/src/buttons_shading.c +++ b/source/blender/src/buttons_shading.c @@ -3852,25 +3852,30 @@ static uiBlock *strand_menu(void *mat_v) { Material *ma= mat_v; uiBlock *block; - + block= uiNewBlock(&curarea->uiblocks, "strand menu", UI_EMBOSS, UI_HELV, curarea->win); /* use this for a fake extra empy space around the buttons */ - uiDefBut(block, LABEL, 0, "", 0, 0, 250, 120, NULL, 0, 0, 0, 0, ""); - + uiDefBut(block, LABEL, 0, "", 0, 0, 250, 170, NULL, 0, 0, 0, 0, ""); + + uiBlockBeginAlign(block); + /* event return 0, to prevent menu to close */ + + uiDefButBitI(block, TOG, MA_TANGENT_STR, 0, "Use Tangent Shading", 10,140,230,20, &(ma->mode), 0, 0, 0, 0, "Uses direction of strands as normal for tangent-shading"); + uiDefButBitI(block, TOG, MA_STR_SURFDIFF, 0, "Surface Diffuse", 10,120,115,20, &(ma->mode), 0, 0, 0, 0, "Make diffuse shading more similar to shading the surface"); + uiDefButF(block, NUM, 0, "Dist", 125,120,115,20, &ma->strand_surfnor, 0.0f, 10.0f, 2, 0, "Distance in Blender units over which to blend in the surface normal"); + uiBlockBeginAlign(block); - /* event return 0, to prevent menu to close */ - uiDefButBitI(block, TOG, MA_TANGENT_STR, 0, "Use Tangent Shading", 10,90,115,20, &(ma->mode), 0, 0, 0, 0, "Uses direction of strands as normal for tangent-shading"); - uiDefButBitI(block, TOG, MA_STR_B_UNITS, 0, "Use Blender Units", 125,90,115,20, &(ma->mode), 0, 0, 0, 0, "Use actual Blender units for widths instead of pixels"); - if(ma->mode & MA_STR_B_UNITS){ - uiDefButF(block, NUMSLI, 0, "Start ", 10, 70, 230,20, &ma->strand_sta, 0.0001, 2.0, 2, 0, "Start size of strands in Blender units"); - uiDefButF(block, NUMSLI, 0, "End ", 10, 50, 230,20, &ma->strand_end, 0.0001, 1.0, 2, 0, "End size of strands in Blender units"); - } - else{ - uiDefButF(block, NUMSLI, 0, "Start ", 10, 70, 230,20, &ma->strand_sta, 0.25, 20.0, 2, 0, "Start size of strands in pixels"); - uiDefButF(block, NUMSLI, 0, "End ", 10, 50, 230,20, &ma->strand_end, 0.25, 10.0, 2, 0, "End size of strands in pixels"); - } - uiDefButF(block, NUMSLI, 0, "Shape ", 10, 30, 230,20, &ma->strand_ease, -0.9, 0.9, 2, 0, "Shape of strands, positive value makes it rounder, negative makes it spiky"); + uiDefButBitI(block, TOG, MA_STR_B_UNITS, 0, "Use Blender Units", 10,95,230,20, &(ma->mode), 0, 0, 0, 0, "Use actual Blender units for widths instead of pixels"); + if(ma->mode & MA_STR_B_UNITS){ + uiDefButF(block, NUMSLI, 0, "Start ", 10, 75, 230,20, &ma->strand_sta, 0.01, 20.0, 2, 0, "Start size of strands in Blender units"); + uiDefButF(block, NUMSLI, 0, "End ", 10, 55, 230,20, &ma->strand_end, 0.01, 10.0, 2, 0, "End size of strands in Blender units"); + } + else{ + uiDefButF(block, NUMSLI, 0, "Start ", 10, 75, 230,20, &ma->strand_sta, 0.25, 20.0, 2, 0, "Start size of strands in pixels"); + uiDefButF(block, NUMSLI, 0, "End ", 10, 55, 230,20, &ma->strand_end, 0.25, 10.0, 2, 0, "End size of strands in pixels"); + } + uiDefButF(block, NUMSLI, 0, "Shape ", 10, 35, 230,20, &ma->strand_ease, -0.9, 0.9, 2, 0, "Shape of strands, positive value makes it rounder, negative makes it spiky"); uiDefBut(block, TEX, B_MATPRV, "UV:", 10,10,230,20, ma->strand_uvname, 0, 31, 0, 0, "Set name of UV layer to override"); uiBlockSetDirection(block, UI_TOP); diff --git a/source/blender/src/drawobject.c b/source/blender/src/drawobject.c index e0b09d6d812..7752952381c 100644 --- a/source/blender/src/drawobject.c +++ b/source/blender/src/drawobject.c @@ -3030,7 +3030,7 @@ static void draw_new_particle_system(Base *base, ParticleSystem *psys) psys->flag|=PSYS_DRAWING; - if(part->flag&PART_CHILD_RENDER || !psys->childcache) + if(!psys->childcache) totchild=0; else totchild=psys->totchild*part->disp/100; @@ -3200,16 +3200,18 @@ static void draw_new_particle_system(Base *base, ParticleSystem *psys) } } else{ - pa_time=psys_get_child_time(psys,a-totpart,cfra); + ChildParticle *cpa= &psys->child[a-totpart]; + + pa_time=psys_get_child_time(psys,cpa,cfra); if((part->flag&PART_ABS_TIME)==0 && part->ipo){ calc_ipo(part->ipo, 100*pa_time); execute_ipo((ID *)part, part->ipo); } - pa_size=psys_get_child_size(psys,a-totpart,cfra,0); + pa_size=psys_get_child_size(psys,cpa,cfra,0); - r_tilt=2.0f*psys->child[a-totpart].rand[2]; + r_tilt=2.0f*cpa->rand[2]; if(path_nbr){ cache=psys->childcache[a-totpart]; k_max=(int)(cache->steps); diff --git a/source/blender/src/editparticle.c b/source/blender/src/editparticle.c index c9cf745fa44..d3f0fcfa09a 100644 --- a/source/blender/src/editparticle.c +++ b/source/blender/src/editparticle.c @@ -133,7 +133,7 @@ void PE_free_particle_edit(ParticleSystem *psys) /************************************************/ /* Edit Mode Helpers */ /************************************************/ -static int PE_can_edit(ParticleSystem *psys) +int PE_can_edit(ParticleSystem *psys) { return (psys && psys->edit && (G.f & G_PARTICLEEDIT)); } diff --git a/source/blender/src/renderwin.c b/source/blender/src/renderwin.c index 5db55ee3738..8250ba4902d 100644 --- a/source/blender/src/renderwin.c +++ b/source/blender/src/renderwin.c @@ -894,11 +894,11 @@ void make_renderinfo_string(RenderStats *rs, char *str) else if(G.scene->r.scemode & R_SINGLE_LAYER) spos+= sprintf(spos, "Single Layer | "); - if(rs->tothalo) - spos+= sprintf(spos, "Fra:%d Ve:%d Fa:%d Ha:%d La:%d Mem:%.2fM (%.2fM) ", (G.scene->r.cfra), rs->totvert, rs->totface, rs->tothalo, rs->totlamp, megs_used_memory, mmap_used_memory); - else - spos+= sprintf(spos, "Fra:%d Ve:%d Fa:%d La:%d Mem:%.2fM (%.2fM) ", (G.scene->r.cfra), rs->totvert, rs->totface, rs->totlamp, megs_used_memory, mmap_used_memory); - + spos+= sprintf(spos, "Fra:%d Ve:%d Fa:%d ", (G.scene->r.cfra), rs->totvert, rs->totface); + if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo); + if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand); + spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory); + if(rs->curfield) spos+= sprintf(spos, "Field %d ", rs->curfield); if(rs->curblur) diff --git a/source/blender/src/transform_conversions.c b/source/blender/src/transform_conversions.c index 085095c7fc1..1d18577668e 100644 --- a/source/blender/src/transform_conversions.c +++ b/source/blender/src/transform_conversions.c @@ -3489,7 +3489,7 @@ void createTransData(TransInfo *t) createTransPose(t, base->object); } } - else if (G.f & G_PARTICLEEDIT) { + else if (G.f & G_PARTICLEEDIT && PE_can_edit(PE_get_current(ob))) { createTransParticleVerts(t); if(t->data && t->flag & T_PROP_EDIT) { |