diff options
Diffstat (limited to 'source/blender/blenkernel/intern/particle.c')
-rw-r--r-- | source/blender/blenkernel/intern/particle.c | 820 |
1 files changed, 303 insertions, 517 deletions
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 6c354c59d7a..a928ed69b9e 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -65,6 +65,7 @@ #include "BKE_boids.h" #include "BKE_cloth.h" #include "BKE_colortools.h" +#include "BKE_editstrands.h" #include "BKE_effect.h" #include "BKE_global.h" #include "BKE_group.h" @@ -362,6 +363,38 @@ int psys_uses_gravity(ParticleSimulationData *sim) { return sim->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY && sim->psys->part && sim->psys->part->effector_weights->global_gravity != 0.0f; } + +KeyBlock *BKE_psys_insert_shape_key(Scene *scene, Object *ob, ParticleSystem *psys, const char *name, const bool from_mix) +{ + Key *key = psys->key; + KeyBlock *kb; + int newkey = 0; + + if (key == NULL) { + key = psys->key = BKE_key_add_particles(ob, psys); + key->type = KEY_RELATIVE; + newkey = 1; + } + + if (newkey || !from_mix) { + /* create from particles */ + kb = BKE_keyblock_add_ctime(key, name, false); + BKE_keyblock_convert_from_hair_keys(ob, psys, kb); + } + else { + /* copy from current values */ + int totelem; + float *data = BKE_key_evaluate_particles(ob, psys, scene ? scene->r.cfra : 0.0f, &totelem); + + /* create new block with prepared data */ + kb = BKE_keyblock_add_ctime(key, name, false); + kb->data = data; + kb->totelem = totelem; + } + + return kb; +} + /************************************************/ /* Freeing stuff */ /************************************************/ @@ -546,6 +579,11 @@ void psys_free(Object *ob, ParticleSystem *psys) if (psys->edit && psys->free_edit) psys->free_edit(psys->edit); + if (psys->hairedit) { + BKE_editstrands_free(psys->hairedit); + MEM_freeN(psys->hairedit); + psys->hairedit = NULL; + } if (psys->child) { MEM_freeN(psys->child); @@ -570,6 +608,10 @@ void psys_free(Object *ob, ParticleSystem *psys) psys->part->id.us--; psys->part = NULL; } + if (psys->key) { + id_us_min(&psys->key->id); + psys->key = NULL; + } BKE_ptcache_free_list(&psys->ptcaches); psys->pointcache = NULL; @@ -769,400 +811,6 @@ bool psys_render_simplify_params(ParticleSystem *psys, ChildParticle *cpa, float } /************************************************/ -/* Interpolation */ -/************************************************/ -static float interpolate_particle_value(float v1, float v2, float v3, float v4, const float w[4], int four) -{ - float value; - - value = w[0] * v1 + w[1] * v2 + w[2] * v3; - if (four) - value += w[3] * v4; - - CLAMP(value, 0.f, 1.f); - - return value; -} - -void psys_interpolate_particle(short type, ParticleKey keys[4], float dt, ParticleKey *result, bool velocity) -{ - float t[4]; - - if (type < 0) { - interp_cubic_v3(result->co, result->vel, keys[1].co, keys[1].vel, keys[2].co, keys[2].vel, dt); - } - else { - key_curve_position_weights(dt, t, type); - - interp_v3_v3v3v3v3(result->co, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t); - - if (velocity) { - float temp[3]; - - if (dt > 0.999f) { - key_curve_position_weights(dt - 0.001f, t, type); - interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t); - sub_v3_v3v3(result->vel, result->co, temp); - } - else { - key_curve_position_weights(dt + 0.001f, t, type); - interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t); - sub_v3_v3v3(result->vel, temp, result->co); - } - } - } -} - - -typedef struct ParticleInterpolationData { - HairKey *hkey[2]; - - DerivedMesh *dm; - MVert *mvert[2]; - - int keyed; - ParticleKey *kkey[2]; - - PointCache *cache; - PTCacheMem *pm; - - PTCacheEditPoint *epoint; - PTCacheEditKey *ekey[2]; - - float birthtime, dietime; - int bspline; -} ParticleInterpolationData; -/* Assumes pointcache->mem_cache exists, so for disk cached particles call psys_make_temp_pointcache() before use */ -/* It uses ParticleInterpolationData->pm to store the current memory cache frame so it's thread safe. */ -static void get_pointcache_keys_for_time(Object *UNUSED(ob), PointCache *cache, PTCacheMem **cur, int index, float t, ParticleKey *key1, ParticleKey *key2) -{ - static PTCacheMem *pm = NULL; - int index1, index2; - - if (index < 0) { /* initialize */ - *cur = cache->mem_cache.first; - - if (*cur) - *cur = (*cur)->next; - } - else { - if (*cur) { - while (*cur && (*cur)->next && (float)(*cur)->frame < t) - *cur = (*cur)->next; - - pm = *cur; - - index2 = BKE_ptcache_mem_index_find(pm, index); - index1 = BKE_ptcache_mem_index_find(pm->prev, index); - - BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame); - if (index1 < 0) - copy_particle_key(key1, key2, 1); - else - BKE_ptcache_make_particle_key(key1, index1, pm->prev->data, (float)pm->prev->frame); - } - else if (cache->mem_cache.first) { - pm = cache->mem_cache.first; - index2 = BKE_ptcache_mem_index_find(pm, index); - BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame); - copy_particle_key(key1, key2, 1); - } - } -} -static int get_pointcache_times_for_particle(PointCache *cache, int index, float *start, float *end) -{ - PTCacheMem *pm; - int ret = 0; - - for (pm = cache->mem_cache.first; pm; pm = pm->next) { - if (BKE_ptcache_mem_index_find(pm, index) >= 0) { - *start = pm->frame; - ret++; - break; - } - } - - for (pm = cache->mem_cache.last; pm; pm = pm->prev) { - if (BKE_ptcache_mem_index_find(pm, index) >= 0) { - *end = pm->frame; - ret++; - break; - } - } - - return ret == 2; -} - -float psys_get_dietime_from_cache(PointCache *cache, int index) -{ - PTCacheMem *pm; - int dietime = 10000000; /* some max value so that we can default to pa->time+lifetime */ - - for (pm = cache->mem_cache.last; pm; pm = pm->prev) { - if (BKE_ptcache_mem_index_find(pm, index) >= 0) - return (float)pm->frame; - } - - return (float)dietime; -} - -static void init_particle_interpolation(Object *ob, ParticleSystem *psys, ParticleData *pa, ParticleInterpolationData *pind) -{ - - if (pind->epoint) { - PTCacheEditPoint *point = pind->epoint; - - pind->ekey[0] = point->keys; - pind->ekey[1] = point->totkey > 1 ? point->keys + 1 : NULL; - - pind->birthtime = *(point->keys->time); - pind->dietime = *((point->keys + point->totkey - 1)->time); - } - else if (pind->keyed) { - ParticleKey *key = pa->keys; - pind->kkey[0] = key; - pind->kkey[1] = pa->totkey > 1 ? key + 1 : NULL; - - pind->birthtime = key->time; - pind->dietime = (key + pa->totkey - 1)->time; - } - else if (pind->cache) { - float start = 0.0f, end = 0.0f; - get_pointcache_keys_for_time(ob, pind->cache, &pind->pm, -1, 0.0f, NULL, NULL); - pind->birthtime = pa ? pa->time : pind->cache->startframe; - pind->dietime = pa ? pa->dietime : pind->cache->endframe; - - if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &end)) { - pind->birthtime = MAX2(pind->birthtime, start); - pind->dietime = MIN2(pind->dietime, end); - } - } - else { - HairKey *key = pa->hair; - pind->hkey[0] = key; - pind->hkey[1] = key + 1; - - pind->birthtime = key->time; - pind->dietime = (key + pa->totkey - 1)->time; - - if (pind->dm) { - pind->mvert[0] = CDDM_get_vert(pind->dm, pa->hair_index); - pind->mvert[1] = pind->mvert[0] + 1; - } - } -} -static void edit_to_particle(ParticleKey *key, PTCacheEditKey *ekey) -{ - copy_v3_v3(key->co, ekey->co); - if (ekey->vel) { - copy_v3_v3(key->vel, ekey->vel); - } - key->time = *(ekey->time); -} -static void hair_to_particle(ParticleKey *key, HairKey *hkey) -{ - copy_v3_v3(key->co, hkey->co); - key->time = hkey->time; -} - -static void mvert_to_particle(ParticleKey *key, MVert *mvert, HairKey *hkey) -{ - copy_v3_v3(key->co, mvert->co); - key->time = hkey->time; -} - -static void do_particle_interpolation(ParticleSystem *psys, int p, ParticleData *pa, float t, ParticleInterpolationData *pind, ParticleKey *result) -{ - PTCacheEditPoint *point = pind->epoint; - ParticleKey keys[4]; - int point_vel = (point && point->keys->vel); - float real_t, dfra, keytime, invdt = 1.f; - - /* billboards wont fill in all of these, so start cleared */ - memset(keys, 0, sizeof(keys)); - - /* interpret timing and find keys */ - if (point) { - if (result->time < 0.0f) - real_t = -result->time; - else - real_t = *(pind->ekey[0]->time) + t * (*(pind->ekey[0][point->totkey - 1].time) - *(pind->ekey[0]->time)); - - while (*(pind->ekey[1]->time) < real_t) - pind->ekey[1]++; - - pind->ekey[0] = pind->ekey[1] - 1; - } - else if (pind->keyed) { - /* we have only one key, so let's use that */ - if (pind->kkey[1] == NULL) { - copy_particle_key(result, pind->kkey[0], 1); - return; - } - - if (result->time < 0.0f) - real_t = -result->time; - else - real_t = pind->kkey[0]->time + t * (pind->kkey[0][pa->totkey - 1].time - pind->kkey[0]->time); - - if (psys->part->phystype == PART_PHYS_KEYED && psys->flag & PSYS_KEYED_TIMING) { - ParticleTarget *pt = psys->targets.first; - - pt = pt->next; - - while (pt && pa->time + pt->time < real_t) - pt = pt->next; - - if (pt) { - pt = pt->prev; - - if (pa->time + pt->time + pt->duration > real_t) - real_t = pa->time + pt->time; - } - else - real_t = pa->time + ((ParticleTarget *)psys->targets.last)->time; - } - - CLAMP(real_t, pa->time, pa->dietime); - - while (pind->kkey[1]->time < real_t) - pind->kkey[1]++; - - pind->kkey[0] = pind->kkey[1] - 1; - } - else if (pind->cache) { - if (result->time < 0.0f) /* flag for time in frames */ - real_t = -result->time; - else - real_t = pa->time + t * (pa->dietime - pa->time); - } - else { - if (result->time < 0.0f) - real_t = -result->time; - else - real_t = pind->hkey[0]->time + t * (pind->hkey[0][pa->totkey - 1].time - pind->hkey[0]->time); - - while (pind->hkey[1]->time < real_t) { - pind->hkey[1]++; - pind->mvert[1]++; - } - - pind->hkey[0] = pind->hkey[1] - 1; - } - - /* set actual interpolation keys */ - if (point) { - edit_to_particle(keys + 1, pind->ekey[0]); - edit_to_particle(keys + 2, pind->ekey[1]); - } - else if (pind->dm) { - pind->mvert[0] = pind->mvert[1] - 1; - mvert_to_particle(keys + 1, pind->mvert[0], pind->hkey[0]); - mvert_to_particle(keys + 2, pind->mvert[1], pind->hkey[1]); - } - else if (pind->keyed) { - memcpy(keys + 1, pind->kkey[0], sizeof(ParticleKey)); - memcpy(keys + 2, pind->kkey[1], sizeof(ParticleKey)); - } - else if (pind->cache) { - get_pointcache_keys_for_time(NULL, pind->cache, &pind->pm, p, real_t, keys + 1, keys + 2); - } - else { - hair_to_particle(keys + 1, pind->hkey[0]); - hair_to_particle(keys + 2, pind->hkey[1]); - } - - /* set secondary interpolation keys for hair */ - if (!pind->keyed && !pind->cache && !point_vel) { - if (point) { - if (pind->ekey[0] != point->keys) - edit_to_particle(keys, pind->ekey[0] - 1); - else - edit_to_particle(keys, pind->ekey[0]); - } - else if (pind->dm) { - if (pind->hkey[0] != pa->hair) - mvert_to_particle(keys, pind->mvert[0] - 1, pind->hkey[0] - 1); - else - mvert_to_particle(keys, pind->mvert[0], pind->hkey[0]); - } - else { - if (pind->hkey[0] != pa->hair) - hair_to_particle(keys, pind->hkey[0] - 1); - else - hair_to_particle(keys, pind->hkey[0]); - } - - if (point) { - if (pind->ekey[1] != point->keys + point->totkey - 1) - edit_to_particle(keys + 3, pind->ekey[1] + 1); - else - edit_to_particle(keys + 3, pind->ekey[1]); - } - else if (pind->dm) { - if (pind->hkey[1] != pa->hair + pa->totkey - 1) - mvert_to_particle(keys + 3, pind->mvert[1] + 1, pind->hkey[1] + 1); - else - mvert_to_particle(keys + 3, pind->mvert[1], pind->hkey[1]); - } - else { - if (pind->hkey[1] != pa->hair + pa->totkey - 1) - hair_to_particle(keys + 3, pind->hkey[1] + 1); - else - hair_to_particle(keys + 3, pind->hkey[1]); - } - } - - dfra = keys[2].time - keys[1].time; - keytime = (real_t - keys[1].time) / dfra; - - /* convert velocity to timestep size */ - if (pind->keyed || pind->cache || point_vel) { - invdt = dfra * 0.04f * (psys ? psys->part->timetweak : 1.f); - mul_v3_fl(keys[1].vel, invdt); - mul_v3_fl(keys[2].vel, invdt); - interp_qt_qtqt(result->rot, keys[1].rot, keys[2].rot, keytime); - } - - /* now we should have in chronologiacl order k1<=k2<=t<=k3<=k4 with keytime between [0, 1]->[k2, k3] (k1 & k4 used for cardinal & bspline interpolation)*/ - psys_interpolate_particle((pind->keyed || pind->cache || point_vel) ? -1 /* signal for cubic interpolation */ - : (pind->bspline ? KEY_BSPLINE : KEY_CARDINAL), - keys, keytime, result, 1); - - /* the velocity needs to be converted back from cubic interpolation */ - if (pind->keyed || pind->cache || point_vel) - mul_v3_fl(result->vel, 1.f / invdt); -} - -static void interpolate_pathcache(ParticleCacheKey *first, float t, ParticleCacheKey *result) -{ - int i = 0; - ParticleCacheKey *cur = first; - - /* scale the requested time to fit the entire path even if the path is cut early */ - t *= (first + first->segments)->time; - - while (i < first->segments && cur->time < t) - cur++; - - if (cur->time == t) - *result = *cur; - else { - float dt = (t - (cur - 1)->time) / (cur->time - (cur - 1)->time); - interp_v3_v3v3(result->co, (cur - 1)->co, cur->co, dt); - interp_v3_v3v3(result->vel, (cur - 1)->vel, cur->vel, dt); - interp_qt_qtqt(result->rot, (cur - 1)->rot, cur->rot, dt); - result->time = t; - } - - /* first is actual base rotation, others are incremental from first */ - if (cur == first || cur - 1 == first) - copy_qt_qt(result->rot, first->rot); - else - mul_qt_qtqt(result->rot, first->rot, result->rot); -} - -/************************************************/ /* Particles on a dm */ /************************************************/ /* interpolate a location on a face based on face coordinates */ @@ -1333,6 +981,19 @@ void psys_interpolate_mcol(const MCol *mcol, int quad, const float w[4], MCol *m } } +static float interpolate_particle_value(float v1, float v2, float v3, float v4, const float w[4], int four) +{ + float value; + + value = w[0] * v1 + w[1] * v2 + w[2] * v3; + if (four) + value += w[3] * v4; + + CLAMP(value, 0.f, 1.f); + + return value; +} + static float psys_interpolate_value_from_verts(DerivedMesh *dm, short from, int index, const float fw[4], const float *values) { if (values == 0 || index == -1) @@ -1520,6 +1181,11 @@ static int psys_map_index_on_dm(DerivedMesh *dm, int from, int index, int index_ return 1; } +int psys_get_index_on_dm(ParticleSystem *psys, DerivedMesh *dm, ParticleData *pa, int *mapindex, float mapfw[4]) +{ + return psys_map_index_on_dm(dm, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, mapindex, mapfw); +} + /* interprets particle data to get a point on a mesh in object space */ void psys_particle_on_dm(DerivedMesh *dm, int from, int index, int index_dmcache, const float fw[4], float foffset, float vec[3], float nor[3], float utan[3], float vtan[3], @@ -1709,8 +1375,8 @@ void psys_particle_on_emitter(ParticleSystemModifierData *psmd, int from, int in extern void do_kink(ParticleKey *state, const float par_co[3], const float par_vel[3], const float par_rot[4], float time, float freq, float shape, float amplitude, float flat, short type, short axis, float obmat[4][4], int smooth_start); -extern float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump, - bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve); +extern float do_clump(ParticleKey *state, const float par_co[3], const float par_orco[3], float time, const float orco[3], float clumpfac, float clumppow, float pa_clump, + bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve, float clump_noise_random, float clump_noise_random_size, float mat[4][4]); void precalc_guides(ParticleSimulationData *sim, ListBase *effectors) { @@ -1834,12 +1500,14 @@ int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, i float par_co[3] = {0.0f, 0.0f, 0.0f}; float par_vel[3] = {0.0f, 0.0f, 0.0f}; float par_rot[4] = {1.0f, 0.0f, 0.0f, 0.0f}; - float orco_offset[3] = {0.0f, 0.0f, 0.0f}; + float par_orco[3] = {0.0f, 0.0f, 0.0f}; + float orco[3] = {0.0f, 0.0f, 0.0f}; copy_v3_v3(key.co, vec_to_point); do_kink(&key, par_co, par_vel, par_rot, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, 0.f, pd->kink, pd->kink_axis, 0, 0); - do_clump(&key, par_co, guidetime, orco_offset, pd->clump_fac, pd->clump_pow, 1.0f, - part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve); + do_clump(&key, par_co, par_orco, guidetime, orco, pd->clump_fac, pd->clump_pow, 1.0f, + part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve, + part->clump_noise_random, part->clump_noise_random_size, NULL); copy_v3_v3(vec_to_point, key.co); } @@ -1868,7 +1536,6 @@ int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, i } return 0; } - static void do_path_effectors(ParticleSimulationData *sim, int i, ParticleCacheKey *ca, int k, int steps, float *UNUSED(rootco), float effector, float UNUSED(dfra), float UNUSED(cfra), float *length, float *vec) { float force[3] = {0.0f, 0.0f, 0.0f}; @@ -2068,53 +1735,26 @@ static void psys_task_init_path(ParticleTask *task, ParticleSimulationData *sim) task->rng_path = BLI_rng_new(seed); } -/* note: this function must be thread safe, except for branching! */ -static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i) +static void psys_calc_child_parent_weights(ParticleTask *task, struct ChildParticle *cpa, + float orco[3], float ornor[3], float hairmat[4][4], int *cpa_num, float **cpa_fuv, short *cpa_from, + ParticleCacheKey *key[4], float weight[4], float offset[4][3]) { ParticleThreadContext *ctx = task->ctx; Object *ob = ctx->sim.ob; ParticleSystem *psys = ctx->sim.psys; ParticleSettings *part = psys->part; - ParticleCacheKey **cache = psys->childcache; ParticleCacheKey **pcache = psys_in_edit_mode(ctx->sim.scene, psys) && psys->edit ? psys->edit->pathcache : psys->pathcache; - ParticleCacheKey *child, *key[4]; - ParticleTexture ptex; - float *cpa_fuv = 0, *par_rot = 0, rot[4]; - float orco[3], ornor[3], hairmat[4][4], dvec[3], off1[4][3], off2[4][3]; - float eff_length, eff_vec[3], weight[4]; - int k, cpa_num; - short cpa_from; - - if (!pcache) - return; - + if (ctx->between) { ParticleData *pa = psys->particles + cpa->pa[0]; - int w, needupdate; - float foffset, wsum = 0.f; + int w; + float 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) { - if (psys->edit->points[cpa->pa[w]].flag & PEP_EDIT_RECALC) { - needupdate = 1; - break; - } - w++; - } - - if (!needupdate) - return; - else - memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1)); - } - /* get parent paths */ for (w = 0; w < 4; w++) { if (cpa->pa[w] >= 0) { @@ -2128,9 +1768,9 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp } /* modify weights to create parting */ - if (p_fac > 0.f) { + if (p_fac > 0.f && key[0]->segments != -1) { for (w = 0; w < 4; w++) { - if (w && weight[w] > 0.f) { + if (w && weight[w] > 0.f && key[w]->segments != -1) { 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. */ @@ -2168,46 +1808,83 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp } /* get the original coordinates (orco) for texture usage */ - cpa_num = cpa->num; - - foffset = cpa->foffset; - cpa_fuv = cpa->fuv; - cpa_from = PART_FROM_FACE; + *cpa_from = PART_FROM_FACE; + *cpa_num = cpa->num; + *cpa_fuv = cpa->fuv; - psys_particle_on_emitter(ctx->sim.psmd, cpa_from, cpa_num, DMCACHE_ISCHILD, cpa->fuv, foffset, co, ornor, 0, 0, orco, 0); + psys_particle_on_emitter(ctx->sim.psmd, *cpa_from, *cpa_num, DMCACHE_ISCHILD, *cpa_fuv, cpa->foffset, co, ornor, 0, 0, orco, 0); mul_m4_v3(ob->obmat, co); for (w = 0; w < 4; w++) - sub_v3_v3v3(off1[w], co, key[w]->co); + sub_v3_v3v3(offset[w], co, key[w]->co); psys_mat_hair_to_global(ob, ctx->sim.psmd->dm, psys->part->from, pa, hairmat); } else { ParticleData *pa = psys->particles + cpa->parent; float co[3]; - if (ctx->editupdate) { - if (!(psys->edit->points[cpa->parent].flag & PEP_EDIT_RECALC)) - return; - - memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1)); - } /* get the parent path */ key[0] = pcache[cpa->parent]; /* get the original coordinates (orco) for texture usage */ - cpa_from = part->from; - cpa_num = pa->num; + *cpa_from = part->from; + *cpa_num = pa->num; /* XXX hack to avoid messed up particle num and subsequent crash (#40733) */ - if (cpa_num > ctx->sim.psmd->dm->getNumTessFaces(ctx->sim.psmd->dm)) - cpa_num = 0; - cpa_fuv = pa->fuv; + if (*cpa_num > ctx->sim.psmd->dm->getNumTessFaces(ctx->sim.psmd->dm)) + *cpa_num = 0; + *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_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); } +} + +/* note: this function must be thread safe, except for branching! */ +static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i) +{ + ParticleThreadContext *ctx = task->ctx; + ParticleSystem *psys = ctx->sim.psys; + ParticleSettings *part = psys->part; + ParticleCacheKey **cache = psys->childcache; + ParticleCacheKey **pcache = psys_in_edit_mode(ctx->sim.scene, psys) && psys->edit ? psys->edit->pathcache : psys->pathcache; + ParticleCacheKey *child, *key[4]; + ParticleTexture ptex; + float *cpa_fuv = 0, *par_rot = 0, rot[4]; + float orco[3], ornor[3], hairmat[4][4], dvec[3], off1[4][3], off2[4][3]; + float eff_length, eff_vec[3], weight[4]; + int k, cpa_num; + short cpa_from; + + if (!pcache) + return; + + if (ctx->editupdate) { + bool need_update; + + if (ctx->between) { + int w; + need_update = false; + for (w = 0; w < 4 && cpa->pa[w] >= 0; ++w) { + if (psys->edit->points[cpa->pa[w]].flag & PEP_EDIT_RECALC) { + need_update = true; + break; + } + } + } + else { + need_update = psys->edit->points[cpa->parent].flag & PEP_EDIT_RECALC; + } + + if (need_update) + memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1)); + else + return; + } + + psys_calc_child_parent_weights(task, cpa, orco, ornor, hairmat, &cpa_num, &cpa_fuv, &cpa_from, key, weight, off1); child_keys->segments = ctx->segments; @@ -2221,6 +1898,8 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp /* create the child path */ for (k = 0, child = child_keys; k <= ctx->segments; k++, child++) { + child->hull_parent = cpa->hull_parent; + if (ctx->between) { int w = 0; @@ -2453,11 +2132,11 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) ParticleSystem *psys = sim->psys; ParticleSettings *part = psys->part; ParticleCacheKey *ca, **cache; + const bool keyed = psys->flag & PSYS_KEYED; + const bool baked = psys->pointcache->mem_cache.first && psys->part->type != PART_HAIR; DerivedMesh *hair_dm = (psys->part->type == PART_HAIR && psys->flag & PSYS_HAIR_DYNAMICS) ? psys->hair_out_dm : NULL; - ParticleKey result; - Material *ma; ParticleInterpolationData pind; ParticleTexture ptex; @@ -2475,7 +2154,6 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) float length, vec[3]; float *vg_effector = NULL; float *vg_length = NULL, pa_length = 1.0f; - int keyed, baked; /* we don't have anything valid to create paths from so let's quit here */ if ((psys->flag & PSYS_HAIR_DONE || psys->flag & PSYS_KEYED || psys->pointcache) == 0) @@ -2485,9 +2163,6 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) if (psys->renderdata == 0 && (psys->edit == NULL || pset->flag & PE_DRAW_PART) == 0) return; - keyed = psys->flag & PSYS_KEYED; - baked = psys->pointcache->mem_cache.first && psys->part->type != PART_HAIR; - /* clear out old and create new empty path cache */ psys_free_path_cache(psys, psys->edit); cache = psys->pathcache = psys_alloc_path_cache_buffers(&psys->pathcachebufs, totpart, segments + 1); @@ -2557,6 +2232,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) /*--interpolate actual path from data points--*/ for (k = 0, ca = cache[p]; k <= segments; k++, ca++) { + ParticleKey result; time = (float)k / (float)segments; t = birthtime + time * (dietime - birthtime); result.time = -t; @@ -2573,14 +2249,6 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) copy_v3_v3(ca->col, col); } - if (part->type == PART_HAIR) { - HairKey *hkey; - - for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) { - mul_v3_m4v3(hkey->world_co, hairmat, hkey->co); - } - } - /*--modify paths and calculate rotation & velocity--*/ if (!(psys->flag & PSYS_GLOBAL_HAIR)) { @@ -3011,6 +2679,25 @@ void psys_mat_hair_to_global(Object *ob, DerivedMesh *dm, short from, ParticleDa mul_m4_m4m4(hairmat, ob->obmat, facemat); } +void psys_child_mat_to_object(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, ChildParticle *cpa, float hairmat[4][4]) +{ + const bool between = (psys->part->childtype == PART_CHILD_FACES); + float co[3]; + + if (between) { + ParticleData *pa = &psys->particles[cpa->pa[0]]; + psys_particle_on_emitter(psmd, PART_FROM_FACE, cpa->num, DMCACHE_ISCHILD, cpa->fuv, cpa->foffset, co, NULL, NULL, NULL, NULL, NULL); + psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, pa, hairmat); + } + else { + ParticleData *pa = &psys->particles[cpa->parent]; + psys_particle_on_emitter(psmd, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, NULL, NULL, NULL, NULL, NULL); + psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, pa, hairmat); + } + + copy_v3_v3(hairmat[3], co); +} + /************************************************/ /* ParticleSettings handling */ /************************************************/ @@ -3032,6 +2719,8 @@ ModifierData *object_add_particle_system(Scene *scene, Object *ob, const char *n BLI_addtail(&ob->particlesystem, psys); psys->part = psys_new_settings(DATA_("ParticleSettings"), NULL); + psys->key = BKE_key_add_particles(ob, psys); + psys->key->type = KEY_RELATIVE; if (BLI_listbase_count_ex(&ob->particlesystem, 2) > 1) BLI_snprintf(psys->name, sizeof(psys->name), DATA_("ParticleSystem %i"), BLI_listbase_count(&ob->particlesystem)); @@ -3054,6 +2743,8 @@ ModifierData *object_add_particle_system(Scene *scene, Object *ob, const char *n psys->flag = PSYS_CURRENT; psys->cfra = BKE_scene_frame_get_from_ctime(scene, CFRA + 1); + psys->hair_preview_factor = 100.0f; + DAG_relations_tag_update(G.main); DAG_id_tag_update(&ob->id, OB_RECALC_DATA); @@ -3095,7 +2786,7 @@ void object_remove_particle_system(Scene *UNUSED(scene), Object *ob) if (ob->particlesystem.first) ((ParticleSystem *) ob->particlesystem.first)->flag |= PSYS_CURRENT; else - ob->mode &= ~OB_MODE_PARTICLE_EDIT; + ob->mode &= ~(OB_MODE_PARTICLE_EDIT | OB_MODE_HAIR_EDIT); DAG_relations_tag_update(G.main); DAG_id_tag_update(&ob->id, OB_RECALC_DATA); @@ -3372,6 +3063,11 @@ static int get_particle_uv(DerivedMesh *dm, ParticleData *pa, int face_index, co pvalue = texture_value_blend(def, pvalue, value, texfac, blend); \ } (void)0 +#define SET_PARTICLE_TEXTURE_RGB(type, prgb, texfac) \ + if ((event & mtex->mapto) & type) { \ + texture_rgb_blend(prgb, rgba, prgb, value, texfac, blend); \ + } (void)0 + #define CLAMP_PARTICLE_TEXTURE_POS(type, pvalue) \ if (event & type) { \ if (pvalue < 0.0f) \ @@ -3384,6 +3080,21 @@ static int get_particle_uv(DerivedMesh *dm, ParticleData *pa, int face_index, co CLAMP(pvalue, -1.0f, 1.0f); \ } (void)0 +#define CLAMP_PARTICLE_TEXTURE_RGB(type, prgb) \ + if (event & type) { \ + CLAMP3(prgb, 0.0f, 1.0f); \ + } (void)0 + +/* actual usable texco mode for particles */ +BLI_INLINE int particle_texco(ParticleSettings *part, MTex *mtex) +{ + short texco = mtex->texco; + if (ELEM(texco, TEXCO_UV, TEXCO_ORCO) && + (!ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME) || part->distr == PART_DISTR_GRID)) + texco = TEXCO_GLOB; + return texco; +} + static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSettings *part, ParticleData *par, int child_index, int face_index, const float fw[4], float *orco, ParticleTexture *ptex, int event, float cfra) { MTex *mtex, **mtexp = part->mtex; @@ -3393,6 +3104,7 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti ptex->ivel = ptex->life = ptex->exist = ptex->size = ptex->damp = ptex->gravity = ptex->field = ptex->time = ptex->clump = ptex->kink_freq = ptex->kink_amp = ptex->effector = ptex->rough1 = ptex->rough2 = ptex->roughe = 1.0f; + ptex->color[0] = ptex->color[1] = ptex->color[2] = 1.0f; ptex->length = 1.0f - part->randlength * psys_frand(psys, child_index + 26); ptex->length *= part->clength_thres < psys_frand(psys, child_index + 27) ? part->clength : 1.0f; @@ -3402,10 +3114,7 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti if (mtex && mtex->tex && mtex->mapto) { float def = mtex->def_var; short blend = mtex->blendtype; - short texco = mtex->texco; - - if (ELEM(texco, TEXCO_UV, TEXCO_ORCO) && (ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME) == 0 || part->distr == PART_DISTR_GRID)) - texco = TEXCO_GLOB; + short texco = particle_texco(part, mtex); switch (texco) { case TEXCO_GLOB: @@ -3441,6 +3150,7 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti SET_PARTICLE_TEXTURE(PAMAP_KINK_AMP, ptex->kink_amp, mtex->kinkampfac); SET_PARTICLE_TEXTURE(PAMAP_KINK_FREQ, ptex->kink_freq, mtex->kinkfac); SET_PARTICLE_TEXTURE(PAMAP_DENS, ptex->exist, mtex->padensfac); + SET_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color, mtex->pacolfac); } } @@ -3450,71 +3160,87 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti CLAMP_PARTICLE_TEXTURE_POS(PAMAP_KINK_FREQ, ptex->kink_freq); CLAMP_PARTICLE_TEXTURE_POS(PAMAP_ROUGH, ptex->rough1); CLAMP_PARTICLE_TEXTURE_POS(PAMAP_DENS, ptex->exist); + CLAMP_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color); } -void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTexture *ptex, int event, float cfra) + +bool particle_mtex_eval(ParticleSimulationData *sim, MTex *mtex, ParticleData *pa, float cfra, float *value, float rgba[4]) { Object *ob = sim->ob; Mesh *me = (Mesh *)ob->data; ParticleSettings *part = sim->psys->part; + short texco; + + float texvec[3]; + + if (!(mtex->tex && mtex->mapto)) + return false; + + texco = particle_texco(part, mtex); + switch (texco) { + case TEXCO_GLOB: + copy_v3_v3(texvec, pa->state.co); + break; + case TEXCO_OBJECT: + copy_v3_v3(texvec, pa->state.co); + if (mtex->object) + mul_m4_v3(mtex->object->imat, texvec); + break; + case TEXCO_UV: + if (get_particle_uv(sim->psmd->dm, pa, 0, pa->fuv, mtex->uvname, texvec)) + break; + /* no break, failed to get uv's, so let's try orco's */ + case TEXCO_ORCO: { + float co[3]; + + psys_particle_on_emitter(sim->psmd, sim->psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, texvec, 0); + + if (me->bb == NULL || (me->bb->flag & BOUNDBOX_DIRTY)) { + BKE_mesh_texspace_calc(me); + } + sub_v3_v3(texvec, me->loc); + if (me->size[0] != 0.0f) texvec[0] /= me->size[0]; + if (me->size[1] != 0.0f) texvec[1] /= me->size[1]; + if (me->size[2] != 0.0f) texvec[2] /= me->size[2]; + break; + } + case TEXCO_PARTICLE: + /* texture coordinates in range [-1, 1] */ + texvec[0] = 2.f * (cfra - pa->time) / (pa->dietime - pa->time) - 1.f; + if (sim->psys->totpart > 0) + texvec[1] = 2.f * (float)(pa - sim->psys->particles) / (float)sim->psys->totpart - 1.f; + else + texvec[1] = 0.0f; + texvec[2] = 0.f; + break; + } + + externtex(mtex, texvec, value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false); + + return true; +} + +void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTexture *ptex, int event, float cfra) +{ + ParticleSettings *part = sim->psys->part; MTex **mtexp = part->mtex; MTex *mtex; int m; - float value, rgba[4], co[3], texvec[3]; + float value, rgba[4]; int setvars = 0; /* initialize ptex */ ptex->ivel = ptex->life = ptex->exist = ptex->size = ptex->damp = ptex->gravity = ptex->field = ptex->length = ptex->clump = ptex->kink_freq = ptex->kink_amp = ptex->effector = ptex->rough1 = ptex->rough2 = ptex->roughe = 1.0f; + ptex->color[0] = ptex->color[1] = ptex->color[2] = 1.0f; ptex->time = (float)(pa - sim->psys->particles) / (float)sim->psys->totpart; for (m = 0; m < MAX_MTEX; m++, mtexp++) { mtex = *mtexp; - if (mtex && mtex->tex && mtex->mapto) { + if (mtex && particle_mtex_eval(sim, mtex, pa, cfra, &value, rgba)) { float def = mtex->def_var; short blend = mtex->blendtype; - short texco = mtex->texco; - - if (texco == TEXCO_UV && (ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME) == 0 || part->distr == PART_DISTR_GRID)) - texco = TEXCO_GLOB; - - switch (texco) { - case TEXCO_GLOB: - copy_v3_v3(texvec, pa->state.co); - break; - case TEXCO_OBJECT: - copy_v3_v3(texvec, pa->state.co); - if (mtex->object) - mul_m4_v3(mtex->object->imat, texvec); - break; - case TEXCO_UV: - if (get_particle_uv(sim->psmd->dm, pa, 0, pa->fuv, mtex->uvname, texvec)) - break; - /* no break, failed to get uv's, so let's try orco's */ - case TEXCO_ORCO: - psys_particle_on_emitter(sim->psmd, sim->psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, texvec, 0); - - if (me->bb == NULL || (me->bb->flag & BOUNDBOX_DIRTY)) { - BKE_mesh_texspace_calc(me); - } - sub_v3_v3(texvec, me->loc); - if (me->size[0] != 0.0f) texvec[0] /= me->size[0]; - if (me->size[1] != 0.0f) texvec[1] /= me->size[1]; - if (me->size[2] != 0.0f) texvec[2] /= me->size[2]; - break; - case TEXCO_PARTICLE: - /* texture coordinates in range [-1, 1] */ - texvec[0] = 2.f * (cfra - pa->time) / (pa->dietime - pa->time) - 1.f; - if (sim->psys->totpart > 0) - texvec[1] = 2.f * (float)(pa - sim->psys->particles) / (float)sim->psys->totpart - 1.f; - else - texvec[1] = 0.0f; - texvec[2] = 0.f; - break; - } - - externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false); if ((event & mtex->mapto) & PAMAP_TIME) { /* the first time has to set the base value for time regardless of blend mode */ @@ -3536,6 +3262,7 @@ void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTex SET_PARTICLE_TEXTURE(PAMAP_GRAVITY, ptex->gravity, mtex->gravityfac); SET_PARTICLE_TEXTURE(PAMAP_DAMP, ptex->damp, mtex->dampfac); SET_PARTICLE_TEXTURE(PAMAP_LENGTH, ptex->length, mtex->lengthfac); + SET_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color, mtex->pacolfac); } } @@ -3548,7 +3275,37 @@ void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTex CLAMP_PARTICLE_TEXTURE_POSNEG(PAMAP_GRAVITY, ptex->gravity); CLAMP_PARTICLE_TEXTURE_POS(PAMAP_DAMP, ptex->damp); CLAMP_PARTICLE_TEXTURE_POS(PAMAP_LENGTH, ptex->length); + CLAMP_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color); } + +/* specialized texture eval for shapekey influences */ +float psys_get_texture_shapefac(ParticleSimulationData *sim, ParticleData *pa, float cfra, const char *shapekey) +{ + ParticleSettings *part = sim->psys->part; + MTex **mtexp = part->mtex; + MTex *mtex; + int m; + float value, rgba[4]; + + float result = 1.0f; + + for (m = 0; m < MAX_MTEX; m++, mtexp++) { + mtex = *mtexp; + if (mtex && (mtex->mapto & PAMAP_SHAPEKEY) && STREQ(mtex->shapekey, shapekey)) { + float def = mtex->def_var; + short blend = mtex->blendtype; + + if (particle_mtex_eval(sim, mtex, pa, cfra, &value, rgba)) { + result = texture_value_blend(def, result, value, mtex->shapefac, blend); + } + } + } + + CLAMP(result, 0.0f, 1.0f); + + return result; +} + /************************************************/ /* Particle State */ /************************************************/ @@ -3628,6 +3385,35 @@ static void get_child_modifier_parameters(ParticleSettings *part, ParticleThread if (ctx->vg_effector) ptex->effector *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_effector); } + +static void interpolate_pathcache(ParticleCacheKey *first, float t, ParticleCacheKey *result) +{ + int i = 0; + ParticleCacheKey *cur = first; + + /* scale the requested time to fit the entire path even if the path is cut early */ + t *= (first + first->segments)->time; + + while (i < first->segments && cur->time < t) + cur++; + + if (cur->time == t) + *result = *cur; + else { + float dt = (t - (cur - 1)->time) / (cur->time - (cur - 1)->time); + interp_v3_v3v3(result->co, (cur - 1)->co, cur->co, dt); + interp_v3_v3v3(result->vel, (cur - 1)->vel, cur->vel, dt); + interp_qt_qtqt(result->rot, (cur - 1)->rot, cur->rot, dt); + result->time = t; + } + + /* first is actual base rotation, others are incremental from first */ + if (cur == first || cur - 1 == first) + copy_qt_qt(result->rot, first->rot); + else + mul_qt_qtqt(result->rot, first->rot, result->rot); +} + /* get's hair (or keyed) particles state at the "path time" specified in state->time */ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey *state, const bool vel) { |