diff options
author | Janne Karhu <jhkarh@gmail.com> | 2011-01-07 14:24:34 +0300 |
---|---|---|
committer | Janne Karhu <jhkarh@gmail.com> | 2011-01-07 14:24:34 +0300 |
commit | 29efbf8a04c66a6c0e71c63140185a603d4e684b (patch) | |
tree | 49905b40499e2bde748e52d7aa735c9dd98e9e97 /source/blender | |
parent | 7ad97b04b7faf4640d953b8c6066b88b9154ba89 (diff) |
New hair child options:
* Renamed children to "simple" and "interpolated" as this is
easier to explain and more descriptive than "from particles"
and "from faces".
* Also shuffled the child ui around a bit to make it clearer.
* Child seed parameter allows to change the seed for children
independent of the main seed value.
* Long hair mode for interpolated children:
- Making even haircuts was impossible before as the child
strand lengths were even, but their root coordinates were
not similar in relation to the parent strands.
- The "long hair" option uses the tips of the parent strands
to calculate the child strand tips.
* Hair parting options:
- Hair parting can now be calculated dynamically on the fly
when in 2.49 there was a cumbersome way of using emitter mesh
seams to define parting lines.
- For long hair parting can be created by a tip distance/root
distance threshold. For example setting the minimum threshold
to 2.0 creates partings between children belonging to parents
with tip distance of three times the root distance
((1+2)*root distance).
- For short hair the parting thresholds are used as angles
between the root directions.
* New kink parameters:
- Kink flatness calculates kink into a shape that would have
been achieved with an actual curling iron.
- Kink amplitude clump determines how much the main clump value
effects the kink amplitude.
- The beginning of kink is now smoothed to make the hair look
more natural close to the roots.
* Some bugs fixed along the way too:
- Child parent's were not determined correctly in some cases.
- Children didn't always look correct in particle mode.
- Changing child parameters caused actual particles to be
recalculated.
* Also cleaned up some deprecated code.
All in all there should be no real changes to how old files look
(except perhaps a bit better!), but the new options should make
hair/fur creation a bit more enjoyable. I'll try to make a video
demonstrating the new stuff shortly.
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/intern/particle.c | 792 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/particle_system.c | 112 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pointcache.c | 9 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 1 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_particle_types.h | 24 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_particle.c | 60 |
6 files changed, 465 insertions, 533 deletions
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 95e14542e28..d3a0cd5df7c 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -1862,144 +1862,184 @@ static float vert_weight(MDeformVert *dvert, int group) return 0.0; } -static void do_prekink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, float freq, float shape, float amplitude, short type, short axis, float obmat[][4]) +static void do_kink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, float freq, float shape, float amplitude, float flat, short type, short axis, float obmat[][4], int smooth_start) { - float vec[3]={0.0,0.0,0.0}, q1[4]={1,0,0,0},q2[4]; - float t; + float kink[3]={1.f,0.f,0.f}, par_vec[3], q1[4]={1.f,0.f,0.f,0.f}; + float t, dt=1.f, result[3]; - CLAMP(time,0.0,1.0); + if(par == NULL || type == PART_KINK_NO) + return; + + CLAMP(time, 0.f, 1.f); if(shape!=0.0f && type!=PART_KINK_BRAID) { if(shape<0.0f) - time= (float)pow(time, 1.0+shape); + time= (float)pow(time, 1.f+shape); else - time= (float)pow(time, 1.0/(1.0-shape)); + time= (float)pow(time, 1.f/(1.f-shape)); } - t=time; + t = time * freq *(float)M_PI; + + if(smooth_start) { + dt = fabs(t); + /* smooth the beginning of kink */ + CLAMP(dt, 0.f, (float)M_PI); + dt = sin(dt/2.f); + } - t*=(float)M_PI*freq; + if(type != PART_KINK_RADIAL) { + float temp[3]; - if(par==0) return; + kink[axis]=1.f; - switch(type){ - case PART_KINK_CURL: - vec[axis]=1.0; - if(par_rot) - QUATCOPY(q2,par_rot) - else - vec_to_quat( q2,par->vel,axis,(axis+1)%3); - mul_qt_v3(q2,vec); - mul_v3_fl(vec,amplitude); - VECADD(state->co,state->co,vec); + if(obmat) + mul_mat3_m4_v3(obmat, kink); + + if(par_rot) + mul_qt_v3(par_rot, kink); - VECSUB(vec,state->co,par->co); + /* make sure kink is normal to strand */ + project_v3_v3v3(temp, kink, par->vel); + sub_v3_v3(kink, temp); + normalize_v3(kink); + } - if(t!=0.0) - axis_angle_to_quat(q1,par->vel,t); - - mul_qt_v3(q1,vec); - - VECADD(state->co,par->co,vec); - break; - case PART_KINK_RADIAL: - VECSUB(vec,state->co,par->co); + copy_v3_v3(result, state->co); + sub_v3_v3v3(par_vec, par->co, state->co); - normalize_v3(vec); - mul_v3_fl(vec,amplitude*(float)sin(t)); + switch(type) { + case PART_KINK_CURL: + { + mul_v3_fl(par_vec, -1.f); - VECADD(state->co,state->co,vec); - break; - case PART_KINK_WAVE: - vec[axis]=1.0; - if(obmat) - mul_mat3_m4_v3(obmat,vec); + if(flat > 0.f) { + float proj[3]; + project_v3_v3v3(proj, par_vec, par->vel); + madd_v3_v3fl(par_vec, proj, -flat); - if(par_rot) - mul_qt_v3(par_rot,vec); + project_v3_v3v3(proj, par_vec, kink); + madd_v3_v3fl(par_vec, proj, -flat); + } - project_v3_v3v3(q1,vec,par->vel); - - VECSUB(vec,vec,q1); - normalize_v3(vec); + axis_angle_to_quat(q1, kink, (float)M_PI/2.f); - mul_v3_fl(vec,amplitude*(float)sin(t)); + mul_qt_v3(q1, par_vec); - VECADD(state->co,state->co,vec); - break; - case PART_KINK_BRAID: - if(par){ - float y_vec[3]={0.0,1.0,0.0}; - float z_vec[3]={0.0,0.0,1.0}; - float vec_from_par[3], vec_one[3], radius, state_co[3]; - float inp_y,inp_z,length; - - if(par_rot) - QUATCOPY(q2,par_rot) - else - vec_to_quat(q2,par->vel,axis,(axis+1)%3); - mul_qt_v3(q2,y_vec); - mul_qt_v3(q2,z_vec); - - VECSUB(vec_from_par,state->co,par->co); - radius= normalize_v3_v3(vec_one, vec_from_par); + madd_v3_v3fl(par_vec, kink, amplitude); - inp_y=dot_v3v3(y_vec,vec_one); - inp_z=dot_v3v3(z_vec,vec_one); + /* rotate kink vector around strand tangent */ + if(t!=0.f) { + axis_angle_to_quat(q1, par->vel, t); + mul_qt_v3(q1, par_vec); + } - if(inp_y>0.5){ - VECCOPY(state_co,y_vec); + add_v3_v3v3(result, par->co, par_vec); + break; + } + case PART_KINK_RADIAL: + { + if(flat > 0.f) { + float proj[3]; + /* flatten along strand */ + project_v3_v3v3(proj, par_vec, par->vel); + madd_v3_v3fl(result, proj, flat); + } - mul_v3_fl(y_vec,amplitude*(float)cos(t)); - mul_v3_fl(z_vec,amplitude/2.0f*(float)sin(2.0f*t)); - } - else if(inp_z>0.0){ - VECCOPY(state_co,z_vec); - mul_v3_fl(state_co,(float)sin(M_PI/3.0f)); - VECADDFAC(state_co,state_co,y_vec,-0.5f); + madd_v3_v3fl(result, par_vec, -amplitude*(float)sin(t)); + break; + } + case PART_KINK_WAVE: + { + madd_v3_v3fl(result, kink, amplitude*(float)sin(t)); - mul_v3_fl(y_vec,-amplitude*(float)cos(t + M_PI/3.0f)); - mul_v3_fl(z_vec,amplitude/2.0f*(float)cos(2.0f*t + M_PI/6.0f)); - } - else{ - VECCOPY(state_co,z_vec); - mul_v3_fl(state_co,-(float)sin(M_PI/3.0f)); - VECADDFAC(state_co,state_co,y_vec,-0.5f); + if(flat > 0.f) { + float proj[3]; + /* flatten along wave */ + project_v3_v3v3(proj, par_vec, kink); + madd_v3_v3fl(result, proj, flat); - mul_v3_fl(y_vec,amplitude*(float)-sin(t+M_PI/6.0f)); - mul_v3_fl(z_vec,amplitude/2.0f*(float)-sin(2.0f*t+M_PI/3.0f)); - } + /* flatten along strand */ + project_v3_v3v3(proj, par_vec, par->vel); + madd_v3_v3fl(result, proj, flat); + } + break; + } + case PART_KINK_BRAID: + { + float y_vec[3]={0.f,1.f,0.f}; + float z_vec[3]={0.f,0.f,1.f}; + float vec_one[3], radius, state_co[3]; + float inp_y, inp_z, length; - mul_v3_fl(state_co,amplitude); - VECADD(state_co,state_co,par->co); - VECSUB(vec_from_par,state->co,state_co); + mul_qt_v3(par_rot, y_vec); + mul_qt_v3(par_rot, z_vec); + + mul_v3_fl(par_vec, -1.f); + radius= normalize_v3_v3(vec_one, par_vec); - length=normalize_v3(vec_from_par); - mul_v3_fl(vec_from_par,MIN2(length,amplitude/2.0f)); + inp_y=dot_v3v3(y_vec, vec_one); + inp_z=dot_v3v3(z_vec, vec_one); - VECADD(state_co,par->co,y_vec); - VECADD(state_co,state_co,z_vec); - VECADD(state_co,state_co,vec_from_par); + if(inp_y>0.5){ + copy_v3_v3(state_co, y_vec); - shape=(2.0f*(float)M_PI)*(1.0f+shape); + mul_v3_fl(y_vec, amplitude*(float)cos(t)); + mul_v3_fl(z_vec, amplitude/2.f*(float)sin(2.f*t)); + } + else if(inp_z>0.0){ + mul_v3_v3fl(state_co, z_vec, (float)sin(M_PI/3.f)); + VECADDFAC(state_co,state_co,y_vec,-0.5f); - if(t<shape){ - shape=t/shape; - shape=(float)sqrt((double)shape); - interp_v3_v3v3(state->co,state->co,state_co,shape); - } - else{ - VECCOPY(state->co,state_co); - } - } - break; + mul_v3_fl(y_vec, -amplitude * (float)cos(t + M_PI/3.f)); + mul_v3_fl(z_vec, amplitude/2.f * (float)cos(2.f*t + M_PI/6.f)); + } + else{ + mul_v3_v3fl(state_co, z_vec, -(float)sin(M_PI/3.f)); + madd_v3_v3fl(state_co, y_vec, -0.5f); + + mul_v3_fl(y_vec, amplitude * (float)-sin(t + M_PI/6.f)); + mul_v3_fl(z_vec, amplitude/2.f * (float)-sin(2.f*t + M_PI/3.f)); + } + + mul_v3_fl(state_co, amplitude); + add_v3_v3(state_co, par->co); + sub_v3_v3v3(par_vec, state->co, state_co); + + length = normalize_v3(par_vec); + mul_v3_fl(par_vec, MIN2(length, amplitude/2.f)); + + add_v3_v3v3(state_co, par->co, y_vec); + add_v3_v3(state_co, z_vec); + add_v3_v3(state_co, par_vec); + + shape = 2.f*(float)M_PI * (1.f+shape); + + if(t<shape){ + shape = t/shape; + shape = (float)sqrt((double)shape); + interp_v3_v3v3(result, result, state_co, shape); + } + else{ + copy_v3_v3(result, state_co); + } + break; } + } + + /* blend the start of the kink */ + if(dt < 1.f) + interp_v3_v3v3(state->co, state->co, result, dt); + else + copy_v3_v3(state->co, result); } -static void do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump) +static float do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump) { + float clump = 0.f; + if(par && clumpfac!=0.0){ - float clump, cpow; + float cpow; if(clumppow<0.0) cpow=1.0f+clumppow; @@ -2010,8 +2050,11 @@ static void do_clump(ParticleKey *state, ParticleKey *par, float time, float clu clump = -clumpfac*pa_clump*(float)pow(1.0-(double)time,(double)cpow); else clump = clumpfac*pa_clump*(float)pow((double)time,(double)cpow); + interp_v3_v3v3(state->co,state->co,par->co,clump); } + + return clump; } void precalc_guides(ParticleSimulationData *sim, ListBase *effectors) { @@ -2124,7 +2167,7 @@ int do_guides(ListBase *effectors, ParticleKey *state, int index, float time) } par.co[0] = par.co[1] = par.co[2] = 0.0f; VECCOPY(key.co, vec_to_point); - do_prekink(&key, &par, 0, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, pd->kink, pd->kink_axis, 0); + do_kink(&key, &par, 0, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, 0.f, pd->kink, pd->kink_axis, 0, 0); do_clump(&key, &par, guidetime, pd->clump_fac, pd->clump_pow, 1.0f); VECCOPY(vec_to_point, key.co); @@ -2229,20 +2272,23 @@ static int check_path_length(int k, ParticleCacheKey *keys, ParticleCacheKey *st return k; } } -static void offset_child(ChildParticle *cpa, ParticleKey *par, ParticleKey *child, float flat, float radius) +static void offset_child(ChildParticle *cpa, ParticleKey *par, float *par_rot, ParticleKey *child, float flat, float radius) { - VECCOPY(child->co,cpa->fuv); - mul_v3_fl(child->co,radius); + copy_v3_v3(child->co, cpa->fuv); + mul_v3_fl(child->co, radius); child->co[0]*=flat; - VECCOPY(child->vel,par->vel); - - mul_qt_v3(par->rot,child->co); + copy_v3_v3(child->vel, par->vel); - QUATCOPY(child->rot,par->rot); + if(par_rot) { + mul_qt_v3(par_rot, child->co); + copy_qt_qt(child->rot, par_rot); + } + else + unit_qt(child->rot); - VECADD(child->co,child->co,par->co); + add_v3_v3(child->co, par->co); } float *psys_cache_vgroup(DerivedMesh *dm, ParticleSystem *psys, int vgroup) { @@ -2372,12 +2418,9 @@ static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float c if(totchild==0) return 0; /* init random number generator */ - if(ctx->sim.psys->part->flag & PART_ANIM_BRANCHING) - seed= 31415926 + ctx->sim.psys->seed + (int)cfra; - else - seed= 31415926 + ctx->sim.psys->seed; + seed= 31415926 + ctx->sim.psys->seed; - if(part->flag & PART_BRANCHING || ctx->editupdate || totchild < 10000) + if(ctx->editupdate || totchild < 10000) totthread= 1; for(i=0; i<totthread; i++) { @@ -2420,7 +2463,7 @@ static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float c } /* note: this function must be thread safe, except for branching! */ -static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, ParticleCacheKey *keys, int i) +static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i) { ParticleThreadContext *ctx= thread->ctx; Object *ob= ctx->sim.ob; @@ -2428,44 +2471,29 @@ static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle ParticleSettings *part = psys->part; ParticleCacheKey **cache= psys->childcache; ParticleCacheKey **pcache= psys_in_edit_mode(ctx->sim.scene, psys) ? psys->edit->pathcache : psys->pathcache; - ParticleCacheKey *state, *par = NULL, *key[4]; - ParticleData *pa=NULL; + ParticleCacheKey *child, *par = NULL, *key[4]; ParticleTexture ptex; - float *cpa_fuv=0, *par_rot=0; - float co[3], orco[3], ornor[3], hairmat[4][4], t, cpa_1st[3], dvec[3]; - float branch_begin, branch_end, branch_prob, rough_rand; + float *cpa_fuv=0, *par_rot=0, rot[4]; + float orco[3], ornor[3], hairmat[4][4], t, dvec[3], off1[4][3], off2[4][3]; float length, max_length = 1.0f, cur_length = 0.0f; - float eff_length, eff_vec[3]; - int k, cpa_num; + float eff_length, eff_vec[3], weight[4]; + int k, cpa_num, maxw=0; short cpa_from; if(!pcache) return; - 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(i<psys->totpart){ - branch_begin=0.0f; - branch_end=1.0f; - branch_prob=0.0f; - } - if(ctx->between){ + ParticleData *pa = psys->particles + cpa->pa[0]; int w, needupdate; - float foffset; - - if(ctx->editupdate && !(part->flag & PART_BRANCHING)) { + float foffset, wsum=0.f; + float co[3]; + float p_min = part->parting_min; + float p_max = part->parting_max; + /* Virtual parents don't work nicely with parting. */ + float p_fac = part->parents > 0.f ? 0.f : part->parting_fac; + + if(ctx->editupdate) { needupdate= 0; w= 0; while(w<4 && cpa->pa[w]>=0) { @@ -2479,223 +2507,223 @@ static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle if(!needupdate) return; else - memset(keys, 0, sizeof(*keys)*(ctx->steps+1)); + memset(child_keys, 0, sizeof(*child_keys)*(ctx->steps+1)); } /* get parent paths */ - w= 0; - while(w<4 && cpa->pa[w]>=0){ - key[w] = pcache[cpa->pa[w]]; - w++; + for(w=0; w<4; w++) { + if(cpa->pa[w] >= 0) { + key[w] = pcache[cpa->pa[w]]; + weight[w] = cpa->w[w]; + } + else { + key[w] = pcache[0]; + weight[w] = 0.f; + } + } + + /* modify weights to create parting */ + if(p_fac > 0.f) { + for(w=0; w<4; w++) { + if(w && weight[w] > 0.f) { + float d; + if(part->flag & PART_CHILD_LONG_HAIR) { + /* For long hair use tip distance/root distance as parting factor instead of root to tip angle. */ + float d1 = len_v3v3(key[0]->co, key[w]->co); + float d2 = len_v3v3((key[0]+key[0]->steps-1)->co, (key[w]+key[w]->steps-1)->co); + + d = d1 > 0.f ? d2/d1 - 1.f : 10000.f; + } + else { + float v1[3], v2[3]; + sub_v3_v3v3(v1, (key[0]+key[0]->steps-1)->co, key[0]->co); + sub_v3_v3v3(v2, (key[w]+key[w]->steps-1)->co, key[w]->co); + normalize_v3(v1); + normalize_v3(v2); + + d = saacos(dot_v3v3(v1, v2)) * 180.f / M_PI; + } + + if(p_max > p_min) + d = (d - p_min)/(p_max - p_min); + else + d = (d - p_min) <= 0.f ? 0.f : 1.f; + + CLAMP(d, 0.f, 1.f); + + if(d > 0.f) + weight[w] *= (1.f - d); + } + wsum += weight[w]; + } + for(w=0; w<4; w++) + weight[w] /= wsum; + + interp_v4_v4v4(weight, cpa->w, weight, p_fac); } /* get the original coordinates (orco) for texture usage */ cpa_num = cpa->num; - foffset= cpa->foffset; + foffset = cpa->foffset; cpa_fuv = cpa->fuv; cpa_from = PART_FROM_FACE; psys_particle_on_emitter(ctx->sim.psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa->fuv,foffset,co,ornor,0,0,orco,0); - if(part->path_start==0.0f) { - /* we need to save the actual root position of the child for positioning it accurately to the surface of the emitter */ - VECCOPY(cpa_1st,co); - mul_m4_v3(ob->obmat,cpa_1st); - } + mul_m4_v3(ob->obmat, co); - pa = psys->particles + cpa->pa[0]; + for(w=0; w<4; w++) + sub_v3_v3v3(off1[w], co, key[w]->co); psys_mat_hair_to_global(ob, ctx->sim.psmd->dm, psys->part->from, pa, hairmat); - - pa=0; } else{ - if(ctx->editupdate && !(part->flag & PART_BRANCHING)) { + ParticleData *pa = psys->particles + cpa->parent; + float co[3]; + if(ctx->editupdate) { if(!(psys->edit->points[cpa->parent].flag & PEP_EDIT_RECALC)) return; - memset(keys, 0, sizeof(*keys)*(ctx->steps+1)); + memset(child_keys, 0, sizeof(*child_keys)*(ctx->steps+1)); } /* get the parent path */ - key[0]=pcache[cpa->parent]; + key[0] = pcache[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(ctx->sim.psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,co,ornor,0,0,orco,0); psys_mat_hair_to_global(ob, ctx->sim.psmd->dm, psys->part->from, pa, hairmat); } - keys->steps = ctx->steps; - - /* correct child ipo timing */ -#if 0 // XXX old animation system - 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); - } -#endif // XXX old animation system + child_keys->steps = ctx->steps; /* get different child parameters from textures & vgroups */ get_child_modifier_parameters(part, ctx, cpa, cpa_from, cpa_num, cpa_fuv, orco, &ptex); if(ptex.exist < PSYS_FRAND(i + 24)) { - keys->steps = -1; + child_keys->steps = -1; return; } /* create the child path */ - for(k=0,state=keys; k<=ctx->steps; k++,state++){ + for(k=0,child=child_keys; k<=ctx->steps; k++,child++){ if(ctx->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; - state->rot[0] = state->rot[1] = state->rot[2] = state->rot[3] = 0.0f; + zero_v3(child->co); + zero_v3(child->vel); + unit_qt(child->rot); - //QUATCOPY(state->rot,key[0]->rot); + for(w=0; w<4; w++) { + copy_v3_v3(off2[w], off1[w]); - /* 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(part->path_start==0.0f) { - if(k==0){ - /* calculate the offset between actual child root position and first position interpolated from parents */ - VECSUB(cpa_1st,cpa_1st,state->co); + if(part->flag & PART_CHILD_LONG_HAIR) { + /* Use parent rotation (in addition to emission location) to determine child offset. */ + if(k) + mul_qt_v3((key[w]+k)->rot, off2[w]); + + /* Fade the effect of rotation for even lengths in the end */ + project_v3_v3v3(dvec, off2[w], (key[w]+k)->vel); + madd_v3_v3fl(off2[w], dvec, -(float)k/(float)ctx->steps); } - /* apply offset for correct positioning */ - VECADD(state->co,state->co,cpa_1st); + + add_v3_v3(off2[w], (key[w]+k)->co); } + + /* child position is the weighted sum of parent positions */ + interp_v3_v3v3v3v3(child->co, off2[0], off2[1], off2[2], off2[3], weight); + interp_v3_v3v3v3v3(child->vel, (key[0]+k)->vel, (key[1]+k)->vel, (key[2]+k)->vel, (key[3]+k)->vel, weight); + + copy_qt_qt(child->rot, (key[0]+k)->rot); } else{ + if(k) { + mul_qt_qtqt(rot, (key[0]+k)->rot, key[0]->rot); + par_rot = rot; + } + else { + par_rot = key[0]->rot; + } /* offset the child from the parent position */ - offset_child(cpa, (ParticleKey*)key[0], (ParticleKey*)state, part->childflat, part->childrad); - - key[0]++; + offset_child(cpa, (ParticleKey*)(key[0]+k), par_rot, (ParticleKey*)child, part->childflat, part->childrad); } } /* apply effectors */ if(part->flag & PART_CHILD_EFFECT) { - for(k=0,state=keys; k<=ctx->steps; k++,state++) { + for(k=0,child=child_keys; k<=ctx->steps; k++,child++) { if(k) { - do_path_effectors(&ctx->sim, cpa->pa[0], state, k, ctx->steps, keys->co, ptex.effector, 0.0f, ctx->cfra, &eff_length, eff_vec); + do_path_effectors(&ctx->sim, cpa->pa[0], child, k, ctx->steps, child_keys->co, ptex.effector, 0.0f, ctx->cfra, &eff_length, eff_vec); } else { - sub_v3_v3v3(eff_vec,(state+1)->co,state->co); - eff_length= len_v3(eff_vec); + sub_v3_v3v3(eff_vec, (child+1)->co, child->co); + eff_length = len_v3(eff_vec); } } } - for(k=0,state=keys; k<=ctx->steps; k++,state++){ - t=(float)k/(float)ctx->steps; + for(k=0,child=child_keys; k<=ctx->steps; k++,child++){ + t = (float)k/(float)ctx->steps; + + if(ctx->totparent) + /* this is now threadsafe, virtual parents are calculated before rest of children */ + par = (i >= ctx->totparent) ? cache[cpa->parent] : NULL; + else if(cpa->parent >= 0) + par = pcache[cpa->parent]; - if(ctx->totparent){ - if(i>=ctx->totparent) { - /* this is now threadsafe, virtual parents are calculated before rest of children */ - par = cache[cpa->parent] + k; + if(par) { + if(k) { + mul_qt_qtqt(rot, (par+k)->rot, par->rot); + par_rot = rot; } - else - par=0; - } - else if(cpa->parent>=0){ - par=pcache[cpa->parent]+k; - par_rot = par->rot; + else { + par_rot = par->rot; + } + par += k; } /* apply different deformations to the child path */ - do_child_modifiers(&ctx->sim, &ptex, (ParticleKey *)par, par_rot, cpa, orco, hairmat, (ParticleKey *)state, t); - - /* TODO: better branching */ - //if(part->flag & PART_BRANCHING && ctx->between == 0 && part->flag & PART_ANIM_BRANCHING) - // rough_t = t * rough_rand; - //else - // rough_t = t; - - /* TODO: better branching */ - //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); - - // CLAMP(branchfac,0.0f,1.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); - // } - // } - // } - - // if(i<psys->totpart) - // interp_v3_v3v3(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 */ - // interp_v3_v3v3(state->co, (cache[i - psys->totpart] + k)->co, state->co, branchfac); - //} + do_child_modifiers(&ctx->sim, &ptex, (ParticleKey *)par, par_rot, cpa, orco, hairmat, (ParticleKey *)child, t); /* we have to correct velocity because of kink & clump */ if(k>1){ - VECSUB((state-1)->vel,state->co,(state-2)->co); - mul_v3_fl((state-1)->vel,0.5); + sub_v3_v3v3((child-1)->vel, child->co, (child-2)->co); + mul_v3_fl((child-1)->vel, 0.5); if(ctx->ma && (part->draw & PART_DRAW_MAT_COL)) - get_strand_normal(ctx->ma, ornor, cur_length, (state-1)->vel); + get_strand_normal(ctx->ma, ornor, cur_length, (child-1)->vel); } if(k == ctx->steps) - VECSUB(state->vel,state->co,(state-1)->co); + sub_v3_v3v3(child->vel, child->co, (child-1)->co); /* check if path needs to be cut before actual end of data points */ if(k){ - VECSUB(dvec,state->co,(state-1)->co); - length=1.0f/(float)ctx->steps; - k=check_path_length(k,keys,state,max_length,&cur_length,length,dvec); + sub_v3_v3v3(dvec, child->co, (child-1)->co); + length = 1.0f/(float)ctx->steps; + k = check_path_length(k, child_keys, child, max_length, &cur_length, length, dvec); } else{ /* initialize length calculation */ - max_length= ptex.length; - cur_length= 0.0f; + max_length = ptex.length; + cur_length = 0.0f; } if(ctx->ma && (part->draw & PART_DRAW_MAT_COL)) { - VECCOPY(state->col, &ctx->ma->r) - get_strand_normal(ctx->ma, ornor, cur_length, state->vel); + VECCOPY(child->col, &ctx->ma->r) + get_strand_normal(ctx->ma, ornor, cur_length, child->vel); } } + + /* Hide virtual parents */ + if(i < ctx->totparent) + child_keys->steps = -1; } static void *exec_child_path_cache(void *data) @@ -2742,7 +2770,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim, float cfra, int editupd totchild= ctx->totchild; totparent= ctx->totparent; - if(editupdate && sim->psys->childcache && !(part->flag & PART_BRANCHING) && totchild == sim->psys->totchildcache) { + if(editupdate && sim->psys->childcache && totchild == sim->psys->totchildcache) { cache = sim->psys->childcache; } else { @@ -2783,6 +2811,43 @@ void psys_cache_child_paths(ParticleSimulationData *sim, float cfra, int editupd psys_threads_free(pthreads); } +/* figure out incremental rotations along path starting from unit quat */ +static void cache_key_incremental_rotation(ParticleCacheKey *key0, ParticleCacheKey *key1, ParticleCacheKey *key2, float *prev_tangent, int i) +{ + float cosangle, angle, tangent[3], normal[3], q[4]; + + switch(i) { + case 0: + /* start from second key */ + break; + case 1: + /* calculate initial tangent for incremental rotations */ + sub_v3_v3v3(prev_tangent, key0->co, key1->co); + normalize_v3(prev_tangent); + unit_qt(key1->rot); + break; + default: + sub_v3_v3v3(tangent, key0->co, key1->co); + normalize_v3(tangent); + + cosangle= dot_v3v3(tangent, prev_tangent); + + /* note we do the comparison on cosangle instead of + * angle, since floating point accuracy makes it give + * different results across platforms */ + if(cosangle > 0.999999f) { + QUATCOPY(key1->rot, key2->rot); + } + else { + angle= saacos(cosangle); + cross_v3_v3v3(normal, prev_tangent, tangent); + axis_angle_to_quat( q,normal, angle); + mul_qt_qtqt(key1->rot, q, key2->rot); + } + + copy_v3_v3(prev_tangent, tangent); + } +} /* 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. */ @@ -2895,22 +2960,19 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) /*--interpolate actual path from data points--*/ for(k=0, ca=cache[p]; k<=steps; k++, ca++){ time = (float)k / (float)steps; - t = birthtime + time * (dietime - birthtime); - result.time = -t; - do_particle_interpolation(psys, p, pa, t, frs_sec, &pind, &result); + copy_v3_v3(ca->co, result.co); /* dynamic hair is in object space */ /* keyed and baked are already in global space */ if(hair_dm) - mul_m4_v3(sim->ob->obmat, result.co); + mul_m4_v3(sim->ob->obmat, ca->co); else if(!keyed && !baked && !(psys->flag & PSYS_GLOBAL_HAIR)) - mul_m4_v3(hairmat, result.co); + mul_m4_v3(hairmat, ca->co); - VECCOPY(ca->co, result.co); - VECCOPY(ca->col, col); + copy_v3_v3(ca->col, col); } /*--modify paths and calculate rotation & velocity--*/ @@ -2945,54 +3007,25 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) /* finally do rotation & velocity */ for(k=1, ca=cache[p]+1; k<=steps; k++, ca++) { - /* figure out rotation */ - float cosangle, angle, tangent[3], normal[3], q[4]; - - if(k == 1) { - /* calculate initial tangent for incremental rotations */ - VECSUB(tangent, ca->co, (ca - 1)->co); - normalize_v3_v3(prev_tangent, tangent); - - /* First rotation is based on emitting face orientation. */ - /* This is way better than having flipping rotations resulting */ - /* from using a global axis as a rotation pole (vec_to_quat()). */ - /* It's not an ideal solution though since it disregards the */ - /* initial tangent, but taking that in to account will allow */ - /* the possibility of flipping again. -jahka */ - mat3_to_quat_is_ok( (ca-1)->rot,rotmat); - } - else { - VECSUB(tangent, ca->co, (ca - 1)->co); - normalize_v3(tangent); - - cosangle= dot_v3v3(tangent, prev_tangent); - - /* note we do the comparison on cosangle instead of - * angle, since floating point accuracy makes it give - * different results across platforms */ - if(cosangle > 0.999999f) { - QUATCOPY((ca - 1)->rot, (ca - 2)->rot); - } - else { - angle= saacos(cosangle); - cross_v3_v3v3(normal, prev_tangent, tangent); - axis_angle_to_quat( q,normal, angle); - mul_qt_qtqt((ca - 1)->rot, q, (ca - 2)->rot); - } - - VECCOPY(prev_tangent, tangent); - } + cache_key_incremental_rotation(ca, ca - 1, ca - 2, prev_tangent, k); if(k == steps) - QUATCOPY(ca->rot, (ca - 1)->rot); - + copy_qt_qt(ca->rot, (ca - 1)->rot); /* set velocity */ - VECSUB(ca->vel, ca->co, (ca-1)->co); + sub_v3_v3v3(ca->vel, ca->co, (ca-1)->co); if(k==1) - VECCOPY((ca-1)->vel, ca->vel); + copy_v3_v3((ca-1)->vel, ca->vel); } + /* First rotation is based on emitting face orientation. + * This is way better than having flipping rotations resulting + * from using a global axis as a rotation pole (vec_to_quat()). + * It's not an ideal solution though since it disregards the + * initial tangent, but taking that in to account will allow + * the possibility of flipping again. -jahka + */ + mat3_to_quat_is_ok(cache[p]->rot, rotmat); } psys->totcached = totpart; @@ -3047,12 +3080,8 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf frs_sec = (psys || edit->pid.flag & PTCACHE_VEL_PER_SEC) ? 25.0f : 1.0f; - if(pset->brushtype == PE_BRUSH_WEIGHT){ - /* use weight painting colors now... */ -#if 0 - sel_col[0] = sel_col[1] = sel_col[2] = 1.0f; - nosel_col[0] = nosel_col[1] = nosel_col[2] = 0.0f; -#endif + if(pset->brushtype == PE_BRUSH_WEIGHT) { + ;/* use weight painting colors now... */ } else{ sel_col[0] = (float)edit->sel_col[0] / 255.0f; @@ -3093,9 +3122,9 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf if(psys) { psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat); - VECCOPY(rotmat[0], hairmat[2]); - VECCOPY(rotmat[1], hairmat[1]); - VECCOPY(rotmat[2], hairmat[0]); + copy_v3_v3(rotmat[0], hairmat[2]); + copy_v3_v3(rotmat[1], hairmat[1]); + copy_v3_v3(rotmat[2], hairmat[0]); } birthtime = pind.birthtime; @@ -3109,66 +3138,32 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf /*--interpolate actual path from data points--*/ for(k=0, ca=cache[i]; k<=steps; k++, ca++){ time = (float)k / (float)steps; - t = birthtime + time * (dietime - birthtime); - result.time = -t; - do_particle_interpolation(psys, i, pa, t, frs_sec, &pind, &result); + copy_v3_v3(ca->co, result.co); /* non-hair points are already in global space */ if(psys && !(psys->flag & PSYS_GLOBAL_HAIR)) { - mul_m4_v3(hairmat, result.co); + mul_m4_v3(hairmat, ca->co); - /* create rotations for proper creation of children */ if(k) { - float cosangle, angle, tangent[3], normal[3], q[4]; - - if(k == 1) { - /* calculate initial tangent for incremental rotations */ - VECSUB(tangent, ca->co, (ca - 1)->co); - normalize_v3_v3(prev_tangent, tangent); - - /* First rotation is based on emitting face orientation. */ - /* This is way better than having flipping rotations resulting */ - /* from using a global axis as a rotation pole (vec_to_quat()). */ - /* It's not an ideal solution though since it disregards the */ - /* initial tangent, but taking that in to account will allow */ - /* the possibility of flipping again. -jahka */ - mat3_to_quat_is_ok( (ca-1)->rot,rotmat); - } - else { - VECSUB(tangent, ca->co, (ca - 1)->co); - normalize_v3(tangent); - - cosangle= dot_v3v3(tangent, prev_tangent); - - /* note we do the comparison on cosangle instead of - * angle, since floating point accuracy makes it give - * different results across platforms */ - if(cosangle > 0.999999f) { - QUATCOPY((ca - 1)->rot, (ca - 2)->rot); - } - else { - angle= saacos(cosangle); - cross_v3_v3v3(normal, prev_tangent, tangent); - axis_angle_to_quat( q,normal, angle); - mul_qt_qtqt((ca - 1)->rot, q, (ca - 2)->rot); - } - - VECCOPY(prev_tangent, tangent); - } + cache_key_incremental_rotation(ca, ca - 1, ca - 2, prev_tangent, k); if(k == steps) - QUATCOPY(ca->rot, (ca - 1)->rot); - } + copy_qt_qt(ca->rot, (ca - 1)->rot); - } + /* set velocity */ + sub_v3_v3v3(ca->vel, ca->co, (ca - 1)->co); - VECCOPY(ca->co, result.co); - - ca->vel[0] = ca->vel[1] = 0.0f; - ca->vel[1] = 1.0f; + if(k==1) + copy_v3_v3((ca - 1)->vel, ca->vel); + } + } + else { + ca->vel[0] = ca->vel[1] = 0.0f; + ca->vel[1] = 1.0f; + } /* selection coloring in edit mode */ if(pset->brushtype==PE_BRUSH_WEIGHT){ @@ -3216,6 +3211,16 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf ca->time = t; } + if(psys && !(psys->flag & PSYS_GLOBAL_HAIR)) { + /* First rotation is based on emitting face orientation. + * This is way better than having flipping rotations resulting + * from using a global axis as a rotation pole (vec_to_quat()). + * It's not an ideal solution though since it disregards the + * initial tangent, but taking that in to account will allow + * the possibility of flipping again. -jahka + */ + mat3_to_quat_is_ok(cache[i]->rot, rotmat); + } } edit->totcached = totpart; @@ -3492,6 +3497,7 @@ static void default_particle_settings(ParticleSettings *part) part->adapt_angle= 5; part->adapt_pix= 3; part->kink_axis= 2; + part->kink_amp_clump= 1.f; part->reactevent= PART_EVENT_DEATH; part->disp=100; part->from= PART_FROM_FACE; @@ -3921,11 +3927,15 @@ static void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *pte guided = do_guides(sim->psys->effectors, (ParticleKey*)state, cpa->parent, t); if(guided==0){ - if(kink_freq > 0.f) - do_prekink(state, par, par_rot, t, kink_freq, part->kink_shape, - part->kink_amp, part->kink, part->kink_axis, sim->ob->obmat); - - do_clump(state, par, t, part->clumpfac, part->clumppow, ptex ? ptex->clump : 1.f); + float clump = do_clump(state, par, t, part->clumpfac, part->clumppow, ptex ? ptex->clump : 1.f); + + if(kink_freq != 0.f) { + float kink_amp = part->kink_amp * (1.f - part->kink_amp_clump * clump); + + do_kink(state, par, par_rot, t, kink_freq, part->kink_shape, + kink_amp, part->kink_flat, part->kink, part->kink_axis, + sim->ob->obmat, sim->psys->part->childtype == PART_CHILD_FACES); + } } if(rough1 > 0.f) @@ -4114,7 +4124,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey * } else{ /* offset the child from the parent position */ - offset_child(cpa, keys, state, part->childflat, part->childrad); + offset_child(cpa, keys, keys->rot, state, part->childflat, part->childrad); } par = keys; @@ -4212,7 +4222,7 @@ int psys_get_particle_state(ParticleSimulationData *sim, int p, ParticleKey *sta float t = (cfra - pa->time) / pa->lifetime; key1=&pa->state; - offset_child(cpa, key1, state, part->childflat, part->childrad); + offset_child(cpa, key1, key1->rot, state, part->childflat, part->childrad); CLAMP(t,0.0,1.0); diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 08fe931693e..71cb3128506 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -654,7 +654,7 @@ static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData DerivedMesh *dm= ctx->dm; ParticleData *tpa; /* ParticleSettings *part= ctx->sim.psys->part; */ - float *v1, *v2, *v3, *v4, nor[3], orco1[3], co1[3], co2[3], nor1[3], ornor1[3]; + float *v1, *v2, *v3, *v4, nor[3], orco1[3], co1[3], co2[3], nor1[3]; float cur_d, min_d, randu, randv; int from= ctx->from; int cfrom= ctx->cfrom; @@ -795,12 +795,9 @@ static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData int parent[10]; float pweight[10]; - /*do_seams= (part->flag&PART_CHILD_SEAMS && ctx->seams);*/ - - psys_particle_on_dm(dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,0,0,orco1,ornor1); + psys_particle_on_dm(dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,NULL,NULL,orco1,NULL); transform_mesh_orco_verts((Mesh*)ob->data, &orco1, 1, 1); - //maxw = BLI_kdtree_find_n_nearest(ctx->tree,(do_seams)?10:4,orco1,ornor1,ptn); - maxw = BLI_kdtree_find_n_nearest(ctx->tree,4,orco1,ornor1,ptn); + maxw = BLI_kdtree_find_n_nearest(ctx->tree,4,orco1,NULL,ptn); maxd=ptn[maxw-1].dist; mind=ptn[0].dist; @@ -815,63 +812,6 @@ static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData 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++){ - // sub_v3_v3v3(temp,co1,seam->v0); - // inp=dot_v3v3(temp,seam->dir)/seam->length2; - // if(inp<0.0f){ - // cur_len=len_v3v3(co1,seam->v0); - // } - // else if(inp>1.0f){ - // cur_len=len_v3v3(co1,seam->v1); - // } - // else{ - // copy_v3_v3(temp2,seam->dir); - // mul_v3_fl(temp2,inp); - // cur_len=len_v3v3(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; - // - // copy_v3_v3(temp,seam->v0); - // - // if(near_vert){ - // if(near_vert==-1) - // sub_v3_v3v3(tan,co1,seam->v0); - // else{ - // sub_v3_v3v3(tan,co1,seam->v1); - // copy_v3_v3(temp,seam->v1); - // } - - // normalize_v3(tan); - // } - // else{ - // copy_v3_v3(tan,seam->tan); - // sub_v3_v3v3(temp2,co1,temp); - // if(dot_v3v3(tan,temp2)<0.0f) - // negate_v3(tan); - // } - // for(w=0; w<maxw; w++){ - // sub_v3_v3v3(temp2,ptn[w].co,temp); - // if(dot_v3v3(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){ @@ -1007,6 +947,8 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene, if(from==PART_FROM_CHILD){ distr=PART_DISTR_RAND; + BLI_srandom(31415926 + psys->seed + psys->child_seed); + if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ dm= finaldm; children=1; @@ -1023,50 +965,6 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene, totpart=get_psys_tot_child(scene, psys); cfrom=from=PART_FROM_FACE; - - //if(part->flag&PART_CHILD_SEAMS){ - // MEdge *ed, *medge=dm->getEdgeDataArray(dm,CD_MEDGE); - // MVert *mvert=dm->getVertDataArray(dm,CD_MVERT); - // int totedge=dm->getNumEdges(dm); - - // for(p=0, ed=medge; p<totedge; p++,ed++) - // if(ed->flag&ME_SEAM) - // totseam++; - - // if(totseam){ - // ParticleSeam *cur_seam=seams=MEM_callocN(totseam*sizeof(ParticleSeam),"Child Distribution Seams"); - // float temp[3],temp2[3]; - - // for(p=0, ed=medge; p<totedge; p++,ed++){ - // if(ed->flag&ME_SEAM){ - // copy_v3_v3(cur_seam->v0,(mvert+ed->v1)->co); - // copy_v3_v3(cur_seam->v1,(mvert+ed->v2)->co); - - // sub_v3_v3v3(cur_seam->dir,cur_seam->v1,cur_seam->v0); - - // cur_seam->length2=len_v3(cur_seam->dir); - // cur_seam->length2*=cur_seam->length2; - - // temp[0]=(float)((mvert+ed->v1)->no[0]); - // temp[1]=(float)((mvert+ed->v1)->no[1]); - // temp[2]=(float)((mvert+ed->v1)->no[2]); - // temp2[0]=(float)((mvert+ed->v2)->no[0]); - // temp2[1]=(float)((mvert+ed->v2)->no[1]); - // temp2[2]=(float)((mvert+ed->v2)->no[2]); - - // add_v3_v3v3(cur_seam->nor,temp,temp2); - // normalize_v3(cur_seam->nor); - - // cross_v3_v3v3(cur_seam->tan,cur_seam->dir,cur_seam->nor); - - // normalize_v3(cur_seam->tan); - - // cur_seam++; - // } - // } - // } - // - //} } else{ /* no need to figure out distribution */ diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 4d96f0508d1..9ad00247dc2 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -2252,17 +2252,18 @@ int BKE_ptcache_object_reset(Scene *scene, Object *ob, int mode) } for(psys=ob->particlesystem.first; psys; psys=psys->next) { - /* Baked cloth hair has to be checked first, because we don't want to reset */ + /* children or just redo can be calculated without reseting anything */ + if(psys->recalc & PSYS_RECALC_REDO || psys->recalc & PSYS_RECALC_CHILD) + skip = 1; + /* Baked cloth hair has to be checked too, because we don't want to reset */ /* particles or cloth in that case -jahka */ - if(psys->clmd) { + else if(psys->clmd) { BKE_ptcache_id_from_cloth(&pid, ob, psys->clmd); if(mode == PSYS_RESET_ALL || !(psys->part->type == PART_HAIR && (pid.cache->flag & PTCACHE_BAKED))) reset |= BKE_ptcache_id_reset(scene, &pid, mode); else skip = 1; } - else if(psys->recalc & PSYS_RECALC_REDO || psys->recalc & PSYS_RECALC_CHILD) - skip = 1; if(skip == 0 && psys->part) { BKE_ptcache_id_from_particles(&pid, ob, psys); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 3d16242c428..7aca496d9ab 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -11181,6 +11181,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) part->boids->pitch = 1.0f; part->flag &= ~PART_HAIR_REGROW; /* this was a deprecated flag before */ + part->kink_amp_clump = 1.f; /* keep old files looking similar */ } for (sc= main->screen.first; sc; sc= sc->id.next) { diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index e336d3056ea..cbc9e0f1c29 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -163,7 +163,7 @@ typedef struct ParticleSettings { /* initial velocity factors */ float normfac, obfac, randfac, partfac, tanfac, tanphase, reactfac; - float ob_vel[3], rt; + float ob_vel[3]; float avefac, phasefac, randrotfac, randphasefac; /* physical properties */ float mass, size, randsize, reactshape; @@ -178,13 +178,17 @@ typedef struct ParticleSettings { /* clumping */ float clumpfac, clumppow; /* kink */ - float kink_amp, kink_freq, kink_shape; + float kink_amp, kink_freq, kink_shape, kink_flat; + float kink_amp_clump; /* rough */ float rough1, rough1_size; float rough2, rough2_size, rough2_thres; float rough_end, rough_end_shape; /* length */ float clength, clength_thres; + /* parting */ + float parting_fac; + float parting_min, parting_max; /* branching */ float branch_thres; /* drawing stuff */ @@ -232,7 +236,7 @@ typedef struct ParticleSystem{ /* note, make sure all (runtime) are NULL's in float imat[4][4]; /* used for duplicators */ float cfra, tree_frame; - int seed, rt; + int seed, child_seed; int flag, totpart, totchild, totcached, totchildcache; short recalc, target_psys, totkeyed, bakespace; @@ -294,18 +298,18 @@ typedef struct ParticleSystem{ /* note, make sure all (runtime) are NULL's in #define PART_BOIDS_2D (1<<19) -#define PART_BRANCHING (1<<20) -#define PART_ANIM_BRANCHING (1<<21) -#define PART_SYMM_BRANCHING (1<<24) +//#define PART_BRANCHING (1<<20) +//#define PART_ANIM_BRANCHING (1<<21) +//#define PART_SYMM_BRANCHING (1<<24) #define PART_HAIR_BSPLINE 1024 #define PART_GRID_INVERT (1<<26) -#define PART_CHILD_EFFECT (1<<27) -#define PART_CHILD_SEAMS (1<<28) -#define PART_CHILD_RENDER (1<<29) -#define PART_CHILD_GUIDE (1<<30) +#define PART_CHILD_EFFECT (1<<27) +#define PART_CHILD_LONG_HAIR (1<<28) +#define PART_CHILD_RENDER (1<<29) +#define PART_CHILD_GUIDE (1<<30) #define PART_SELF_EFFECT (1<<22) diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 90aeba65118..6a7b77df905 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -1103,8 +1103,8 @@ static void rna_def_particle_settings(BlenderRNA *brna) static EnumPropertyItem child_type_items[] = { {0, "NONE", 0, "None", ""}, - {PART_CHILD_PARTICLES, "PARTICLES", 0, "Particles", ""}, - {PART_CHILD_FACES, "FACES", 0, "Faces", ""}, + {PART_CHILD_PARTICLES, "SIMPLE", 0, "Simple", ""}, + {PART_CHILD_FACES, "INTERPOLATED", 0, "Interpolated", ""}, {0, NULL, 0, NULL, NULL} }; @@ -1239,21 +1239,6 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Boids 2D", "Constrain boids to a surface"); RNA_def_property_update(prop, 0, "rna_Particle_reset"); - prop= RNA_def_property(srna, "use_branching", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_BRANCHING); - RNA_def_property_ui_text(prop, "Branching", "Branch child paths from each other"); - RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); - - prop= RNA_def_property(srna, "use_animate_branching", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_ANIM_BRANCHING); - RNA_def_property_ui_text(prop, "Animated", "Animate branching"); - RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); - - prop= RNA_def_property(srna, "use_symmetric_branching", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_SYMM_BRANCHING); - RNA_def_property_ui_text(prop, "Symmetric", "Start and end points are the same"); - RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); - prop= RNA_def_property(srna, "use_hair_bspline", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_HAIR_BSPLINE); RNA_def_property_ui_text(prop, "B-Spline", "Interpolate hair using B-Splines"); @@ -1269,10 +1254,10 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Children", "Apply effectors to children"); RNA_def_property_update(prop, 0, "rna_Particle_redo"); - //prop= RNA_def_property(srna, "child_seams", PROP_BOOLEAN, PROP_NONE); - //RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_CHILD_SEAMS); - //RNA_def_property_ui_text(prop, "Use seams", "Use seams to determine parents"); - //RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop= RNA_def_property(srna, "create_long_hair_children", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_CHILD_LONG_HAIR); + RNA_def_property_ui_text(prop, "Long Hair", "Calculate children that suit long hair well"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); prop= RNA_def_property(srna, "apply_guide_to_children", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_CHILD_GUIDE); @@ -1863,6 +1848,12 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Amplitude", "The amplitude of the offset"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop= RNA_def_property(srna, "kink_amplitude_clump", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "kink_amp_clump"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Amplitude Clump", "How much clump effects kink amplitude"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop= RNA_def_property(srna, "kink_frequency", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "kink_freq"); RNA_def_property_range(prop, -100000.0f, 100000.0f); @@ -1875,6 +1866,10 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Shape", "Adjust the offset to the beginning/end"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop= RNA_def_property(srna, "kink_flat", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Flatness", "How flat the hairs are"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); /* rough */ prop= RNA_def_property(srna, "roughness_1", PROP_FLOAT, PROP_NONE); @@ -1936,6 +1931,25 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Threshold", "Amount of particles left untouched by child path length"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + /* parting */ + prop= RNA_def_property(srna, "child_parting_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "parting_fac"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Parting Factor", "Create parting in the children based on parent strands"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + + prop= RNA_def_property(srna, "child_parting_min", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "parting_min"); + RNA_def_property_range(prop, 0.0f, 180.0f); + RNA_def_property_ui_text(prop, "Parting Minimum", "Minimum root to tip angle (tip distance/root distance for long hair)"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + + prop= RNA_def_property(srna, "child_parting_max", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "parting_max"); + RNA_def_property_range(prop, 0.0f, 180.0f); + RNA_def_property_ui_text(prop, "Parting Maximum", "Maximum root to tip angle (tip distance/root distance for long hair)"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + /* branching */ prop= RNA_def_property(srna, "branch_threshold", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "branch_thres"); @@ -2152,6 +2166,10 @@ static void rna_def_particle_system(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Seed", "Offset in the random number table, to get a different randomized result"); RNA_def_property_update(prop, 0, "rna_Particle_reset"); + prop= RNA_def_property(srna, "child_seed", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text(prop, "Child Seed", "Offset in the random number table for child particles, to get a different randomized result"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + /* hair */ prop= RNA_def_property(srna, "is_global_hair", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PSYS_GLOBAL_HAIR); |