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 /source/blender/blenkernel | |
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/
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_particle.h | 50 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/anim.c | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/material.c | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/particle.c | 766 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/particle_system.c | 741 |
5 files changed, 944 insertions, 622 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 */ |