diff options
Diffstat (limited to 'source/blender/blenkernel/intern/particle_system.c')
-rw-r--r-- | source/blender/blenkernel/intern/particle_system.c | 391 |
1 files changed, 344 insertions, 47 deletions
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 89db2dd45a9..cf4bf32fd77 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -84,6 +84,7 @@ #include "BKE_object.h" #include "BKE_material.h" #include "BKE_cloth.h" +#include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_pointcache.h" #include "BKE_mesh.h" @@ -1201,7 +1202,7 @@ void psys_make_temp_pointcache(Object *ob, ParticleSystem *psys) PTCacheID pid; BKE_ptcache_id_from_particles(&pid, ob, psys); cache->flag &= ~PTCACHE_DISK_CACHE; - BKE_ptcache_disk_to_mem(&pid); + BKE_ptcache_disk_to_mem(&pid, false); cache->flag |= PTCACHE_DISK_CACHE; } } @@ -1291,7 +1292,7 @@ static void psys_update_effectors(ParticleSimulationData *sim) { pdEndEffectors(&sim->psys->effectors); sim->psys->effectors = pdInitEffectors(sim->scene, sim->ob, sim->psys, - sim->psys->part->effector_weights, true); + sim->psys->part->effector_weights); precalc_guides(sim, sim->psys->effectors); } @@ -2896,79 +2897,93 @@ static void collision_check(ParticleSimulationData *sim, int p, float dfra, floa /************************************************/ /* Hair */ /************************************************/ -/* check if path cache or children need updating and do it if needed */ -static void psys_update_path_cache(ParticleSimulationData *sim, float cfra) + +static bool psys_needs_path_cache(ParticleSimulationData *sim) { ParticleSystem *psys = sim->psys; ParticleSettings *part = psys->part; ParticleEditSettings *pset = &sim->scene->toolsettings->particle; Base *base; - int distr=0, alloc=0, skip=0; - - if ((psys->part->childtype && psys->totchild != psys_get_tot_child(sim->scene, psys)) || psys->recalc&PSYS_RECALC_RESET) - alloc=1; - - if (alloc || psys->recalc&PSYS_RECALC_CHILD || (psys->vgroup[PSYS_VG_DENSITY] && (sim->ob && sim->ob->mode & OB_MODE_WEIGHT_PAINT))) - distr=1; - - if (distr) { - if (alloc) - realloc_particles(sim, sim->psys->totpart); - - if (psys_get_tot_child(sim->scene, psys)) { - /* don't generate children while computing the hair keys */ - if (!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) { - distribute_particles(sim, PART_FROM_CHILD); - - if (part->childtype==PART_CHILD_FACES && part->parents != 0.0f) - psys_find_parents(sim); + + /* particle instance modifier with "path" option need cached paths even if particle system doesn't */ + for (base = sim->scene->base.first; base; base= base->next) { + ModifierData *md = modifiers_findByType(base->object, eModifierType_ParticleInstance); + if (md) { + ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md; + if (pimd->flag & eParticleInstanceFlag_Path && pimd->ob == sim->ob && pimd->psys == (psys - (ParticleSystem*)sim->ob->particlesystem.first)) { + return true; } } - else - psys_free_children(psys); } - + if ((part->type==PART_HAIR || psys->flag&PSYS_KEYED || psys->pointcache->flag & PTCACHE_BAKED)==0) - skip = 1; /* only hair, keyed and baked stuff can have paths */ + return false; /* only hair, keyed and baked stuff can have paths */ else if (part->ren_as != PART_DRAW_PATH && !(part->type==PART_HAIR && ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR))) - skip = 1; /* particle visualization must be set as path */ + return false; /* particle visualization must be set as path */ else if (!psys->renderdata) { - if (part->draw_as != PART_DRAW_REND) - skip = 1; /* draw visualization */ + if (!ELEM(part->draw_as, PART_DRAW_REND, PART_DRAW_HULL)) + return false; /* not a mode that requires paths */ else if (psys->pointcache->flag & PTCACHE_BAKING) - skip = 1; /* no need to cache paths while baking dynamics */ + return false; /* no need to cache paths while baking dynamics */ else if (psys_in_edit_mode(sim->scene, psys)) { if ((pset->flag & PE_DRAW_PART)==0) - skip = 1; + return false; else if (part->childtype==0 && (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED)==0) - skip = 1; /* in edit mode paths are needed for child particles and dynamic hair */ + return false; /* in edit mode paths are needed for child particles and dynamic hair */ } } + + return true; +} +/* check if path cache or children need updating and do it if needed */ +static void psys_update_path_cache(ParticleSimulationData *sim, float cfra) +{ + ParticleSystem *psys = sim->psys; + ParticleSettings *part = psys->part; - /* particle instance modifier with "path" option need cached paths even if particle system doesn't */ - for (base = sim->scene->base.first; base; base= base->next) { - ModifierData *md = modifiers_findByType(base->object, eModifierType_ParticleInstance); - if (md) { - ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md; - if (pimd->flag & eParticleInstanceFlag_Path && pimd->ob == sim->ob && pimd->psys == (psys - (ParticleSystem*)sim->ob->particlesystem.first)) { - skip = 0; - break; + /* check if particles need to be reallocated or redistributed */ + { + bool alloc = false, distr = false; + + if ((psys->part->childtype && psys->totchild != psys_get_tot_child(sim->scene, psys)) || psys->recalc&PSYS_RECALC_RESET) + alloc = true; + + if (alloc || psys->recalc&PSYS_RECALC_CHILD || (psys->vgroup[PSYS_VG_DENSITY] && (sim->ob && sim->ob->mode & OB_MODE_WEIGHT_PAINT))) + distr = true; + + if (distr) { + if (alloc) + realloc_particles(sim, sim->psys->totpart); + + if (psys_get_tot_child(sim->scene, psys)) { + /* don't generate children while computing the hair keys */ + if (!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) { + distribute_particles(sim, PART_FROM_CHILD); + + if (part->childtype==PART_CHILD_FACES && part->parents != 0.0f) + psys_find_parents(sim); + } } + else + psys_free_children(psys); } } - if (!skip) { + if (psys_needs_path_cache(sim)) { + psys_cache_paths(sim, cfra); /* for render, child particle paths are computed on the fly */ if (part->childtype) { + bool do_child_paths = true; + if (!psys->totchild) - skip = 1; + do_child_paths = false; else if (psys->part->type == PART_HAIR && (psys->flag & PSYS_HAIR_DONE)==0) - skip = 1; + do_child_paths = false; - if (!skip) + if (do_child_paths) psys_cache_child_paths(sim, cfra, 0); } } @@ -3015,6 +3030,120 @@ static MDeformVert *hair_set_pinning(MDeformVert *dvert, float weight) return dvert; } +bool psys_hair_update_preview(ParticleSimulationData *sim) +{ +#ifdef USE_PARTICLE_PREVIEW + ParticleSystem *psys = sim->psys; + ParticleSettings *part = psys->part; + DerivedMesh *dm = sim->psmd->dm; + const float ratio = psys->hair_preview_factor * 0.01f; + /* target number of simulated hairs + * NOTE: this has to be reached exactly, in order to allow + * comparison with the psys->hair_num_simulated value! + */ + const int num_simulated = psys->totpart * ratio; + + if (!(part->type == PART_HAIR)) + return false; + if (num_simulated == psys->hair_num_simulated) + return false; + + { /* Random hair selection method */ + KDTree *tree = BLI_kdtree_new(num_simulated); /* kdtree for finding nearest simulated hairs for blending */ + RNG *rng = BLI_rng_new(98250 + psys->seed); + ParticleData *pa; + int cur_simulated = 0; + int i; + + /* construct a kd-tree of all simulated hairs */ + pa = psys->particles; + for (i = 0; i < psys->totpart; ++i, ++pa) { + bool simulate = true; + if (cur_simulated == num_simulated) { + /* don't simulate more than the total number */ + simulate = false; + } + else if (num_simulated - cur_simulated <= psys->totpart - i) { + /* only allow disabling if the target sim number + * can be reached with the remaining hairs + */ + simulate = BLI_rng_get_float(rng) < ratio; + } + + if (simulate) { + float co[3]; + + pa->flag &= ~PARS_HAIR_BLEND; + + if (pa->totkey >= 2) { + psys_particle_on_dm(dm, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, 0, 0); + BLI_kdtree_insert(tree, i, co); + } + + ++cur_simulated; + } + else { + pa->flag |= PARS_HAIR_BLEND; + } + } + + BLI_kdtree_balance(tree); + + /* look up nearest simulated hairs for preview hairs and calculate blending weights */ + pa = psys->particles; + for (i = 0; i < psys->totpart; ++i, ++pa) { + if (pa->flag & PARS_HAIR_BLEND) { + float co[3]; + int maxw, w; + KDTreeNearest nearest[4]; + + psys_particle_on_dm(dm, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, 0, 0); + maxw = BLI_kdtree_find_nearest_n(tree, co, nearest, 4); + if (maxw == 1) { + pa->blend_index[0] = nearest[0].index; + pa->blend_weight[0] = 1.0f; + } + else if (maxw > 1) { + float norm, totdist = 0.0f; + for (w = 0; w < maxw; ++w) + totdist += nearest[w].dist; + norm = totdist > 0.0f ? 1.0f / (totdist * (float)(maxw - 1)) : 0.0f; + + for (w = 0; w < maxw; ++w) { + pa->blend_index[w] = nearest[w].index; + pa->blend_weight[w] = (totdist - nearest[w].dist) * norm; + } + } + /* clear unused weights */ + for (w = maxw; w < 4; ++w) { + pa->blend_index[w] = -1; + pa->blend_weight[w] = 0.0f; + } + } + else { + pa->blend_index[0] = -1; + pa->blend_index[1] = -1; + pa->blend_index[2] = -1; + pa->blend_index[3] = -1; + pa->blend_weight[0] = 0.0f; + pa->blend_weight[1] = 0.0f; + pa->blend_weight[2] = 0.0f; + pa->blend_weight[3] = 0.0f; + } + } + + BLI_kdtree_free(tree); + BLI_rng_free(rng); + } + + psys->hair_num_simulated = num_simulated; + return true; +#else + (void)sim; + return false; +#endif +} + static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int totedge, DerivedMesh **r_dm, ClothHairData **r_hairdata) { ParticleSystem *psys = sim->psys; @@ -3030,6 +3159,8 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int float hairmat[4][4]; float max_length; float hair_radius; + float *shapekey_data, *shapekey; + int totshapekey; dm = *r_dm; if (!dm) { @@ -3060,6 +3191,8 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int /* XXX placeholder for more flexible future hair settings */ hair_radius = part->size; + shapekey = shapekey_data = BKE_key_evaluate_particles(sim->ob, psys, sim->scene ? sim->scene->r.cfra : 0.0f, &totshapekey); + /* make vgroup for pin roots etc.. */ hair_index = 1; LOOP_PARTICLES { @@ -3080,8 +3213,15 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int ClothHairData *hair; float *co, *co_next; - co = key->co; - co_next = (key+1)->co; + if (shapekey) { + co = shapekey; + co_next = shapekey + 3; + shapekey += 3; + } + else { + co = key->co; + co_next = (key+1)->co; + } /* create fake root before actual root to resist bending */ if (k==0) { @@ -3101,6 +3241,14 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int dvert = hair_set_pinning(dvert, 1.0f); +#ifdef USE_PARTICLE_PREVIEW + /* tag vert if hair is not simulated */ + if (pa->flag & PARS_HAIR_BLEND) + mvert->flag |= ME_VERT_TMP_TAG; + else + mvert->flag &= ~ME_VERT_TMP_TAG; +#endif + mvert++; medge++; } @@ -3127,6 +3275,14 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int else dvert = hair_set_pinning(dvert, 1.0f); +#ifdef USE_PARTICLE_PREVIEW + /* tag vert if hair is not simulated */ + if (pa->flag & PARS_HAIR_BLEND) + mvert->flag |= ME_VERT_TMP_TAG; + else + mvert->flag &= ~ME_VERT_TMP_TAG; +#endif + mvert++; if (k) medge++; @@ -3136,6 +3292,105 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int } } +#ifdef USE_PARTICLE_PREVIEW +static void hair_deform_preview_curve(ParticleSystem *psys, ParticleData *pa, float (*deformedVerts)[3], ClothHairData *hairdata) +{ + ParticleData *particles = psys->particles; + HairKey *hkey; + float (*vert)[3]; + ClothHairData *root; + int k; + float totlen, norm; + + /* first key is root, no blending for them */ + if (pa->totkey < 2) + return; + + /* calculate normalization factor to equally parameterize hairs */ + totlen = 0.0f; + hkey = pa->hair; + for (k = 0; k < pa->totkey - 1; ++k, ++hkey) + totlen += len_v3v3((hkey+1)->co, hkey->co); + norm = totlen > 0.0f ? 1.0f / totlen : 0.0f; + + totlen = 0.0f; + hkey = pa->hair; + vert = deformedVerts + pa->hair_index; + root = hairdata + pa->hair_index; + for (k = 0; k < pa->totkey; ++k, ++hkey, ++vert, ++root) { + float param; + int w; + + if (k == 0) /* skip root vertex */ + continue; + param = totlen * norm; + totlen += len_v3v3(hkey->co, (hkey-1)->co); + + zero_v3(vert[0]); + for (w = 0; w < 4; ++w) { + ParticleData *blend_pa; + float (*blend_vert)[3]; + ClothHairData *blend_hair; + float blend_key, blend_factor; + int blend_totkey, blend_index; + float co[3]; + + if (pa->blend_index[w] < 0) + continue; + + blend_pa = particles + pa->blend_index[w]; + blend_totkey = blend_pa->totkey; + + blend_key = param * (float)blend_totkey; + blend_index = (int)blend_key; + if (blend_index >= blend_totkey - 1) { + blend_index = blend_totkey - 2; + blend_factor = 1.0f; + } + else { + blend_factor = blend_key - floorf(blend_key); + } + + /* pa->hair_index is set when creating input_dm, + * use it here to map to output mvert index + */ + blend_vert = deformedVerts + blend_pa->hair_index + blend_index; + blend_hair = hairdata + blend_pa->hair_index + blend_index; + + interp_v3_v3v3(co, blend_vert[0], blend_vert[1], blend_factor); + + /* transform parent vector from world to root space, then back into root space of the blended hair */ + sub_v3_v3(co, blend_hair->loc); + /* note: rotation transform disabled, this does not work nicely with global force directions (gravity, wind etc.) + * these forces would also get rotated, giving movement in a different direction than the force would actually incur. + * would have to split internal (stretch, bend) and external forces somehow to make this plausible + */ +#if 0 + mul_transposed_m3_v3(blend_hair->rot, co); + mul_m3_v3(root->rot, co); +#endif + add_v3_v3(co, root->loc); + + madd_v3_v3fl(vert[0], co, pa->blend_weight[w]); + } + } +} + +static void hair_deform_preview_hairs(ParticleSimulationData *sim, float (*deformedVerts)[3], ClothHairData *roots) +{ + ParticleSystem *psys = sim->psys; + ParticleData *pa; + int p; + + pa = psys->particles; + for (p = 0; p < psys->totpart; ++p, ++pa) { + if (pa->flag & PARS_HAIR_BLEND) { + hair_deform_preview_curve(psys, pa, deformedVerts, roots); + } + } +} +#endif + static void do_hair_dynamics(ParticleSimulationData *sim) { ParticleSystem *psys = sim->psys; @@ -3146,6 +3401,11 @@ static void do_hair_dynamics(ParticleSimulationData *sim) float (*deformedVerts)[3]; bool realloc_roots; + if (psys_hair_update_preview(sim)) { + if (psys->clmd) + cloth_free_modifier(psys->clmd); + } + if (!psys->clmd) { psys->clmd = (ClothModifierData*)modifier_new(eModifierType_Cloth); psys->clmd->sim_parms->goalspring = 0.0f; @@ -3197,6 +3457,9 @@ static void do_hair_dynamics(ParticleSimulationData *sim) psys->hair_out_dm->getVertCos(psys->hair_out_dm, deformedVerts); clothModifier_do(psys->clmd, sim->scene, sim->ob, psys->hair_in_dm, deformedVerts); +#ifdef USE_PARTICLE_PREVIEW + hair_deform_preview_hairs(sim, deformedVerts, psys->clmd->roots); +#endif CDDM_apply_vert_coords(psys->hair_out_dm, deformedVerts); @@ -3210,7 +3473,14 @@ static void hair_step(ParticleSimulationData *sim, float cfra) ParticleSystem *psys = sim->psys; ParticleSettings *part = psys->part; PARTICLE_P; + PARTICLE_PSMD; float disp = psys_get_current_display_percentage(psys); + float *shapekey_data = NULL, *shapekey; + int totshapekey; + + if (part->type == PART_HAIR) { + shapekey = shapekey_data = BKE_key_evaluate_particles(sim->ob, sim->psys, sim->scene ? sim->scene->r.cfra : 0.0f, &totshapekey); + } LOOP_PARTICLES { pa->size = part->size; @@ -3221,8 +3491,35 @@ static void hair_step(ParticleSimulationData *sim, float cfra) pa->flag |= PARS_NO_DISP; else pa->flag &= ~PARS_NO_DISP; + + if (part->type == PART_HAIR) { + float hairmat[4][4]; + HairKey *hkey; + int k; + + psys_mat_hair_to_global(sim->ob, psmd->dm, psys->part->from, pa, hairmat); + + /* update world coordinates and calculate shapekey result if needed */ + for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) { + const float *co; + if (shapekey) { + co = shapekey; + shapekey += 3; + + copy_v3_v3(hkey->co, co); + } + else { + co = hkey->co; + } + + mul_v3_m4v3(hkey->world_co, hairmat, co); + } + } } + if (shapekey_data) + MEM_freeN(shapekey_data); + if (psys->recalc & PSYS_RECALC_RESET) { /* need this for changing subsurf levels */ psys_calc_dmcache(sim->ob, sim->psmd->dm, psys); |