From d0bcb6efead49a269d3b9b6f29ef07f5241fa137 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 14 Apr 2022 16:58:15 +1000 Subject: Fix T68290: Baked particles don't render in final frame Particles baked into memory would never load the final frame because of an off-by-one error calculating the particles `dietime`. This value indicates the frame which the particle ceases to exist but was being set to the end-frame which caused this bug as the scenes end-frame is inclusive. While the last frame was properly written and read from memory, the `dietime` was set to the last frame causing all the particles to be considered dead when calculating the cached particle system. --- source/blender/blenkernel/intern/particle.c | 25 +++++++++++++++---------- source/blender/blenkernel/intern/pointcache.c | 23 ++++++++++++++++------- 2 files changed, 31 insertions(+), 17 deletions(-) (limited to 'source/blender/blenkernel') diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index ae685357151..544c80ebc7b 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -1254,7 +1254,9 @@ typedef struct ParticleInterpolationData { PTCacheEditPoint *epoint; PTCacheEditKey *ekey[2]; - float birthtime, dietime; + float birthtime; + /** Die on this frame, see #ParticleData.dietime for details. */ + float dietime; int bspline; } ParticleInterpolationData; /** @@ -1316,15 +1318,15 @@ static void get_pointcache_keys_for_time(Object *UNUSED(ob), } static int get_pointcache_times_for_particle(PointCache *cache, int index, - float *start, - float *end) + float *r_start, + float *r_dietime) { 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; + *r_start = pm->frame; ret++; break; } @@ -1332,7 +1334,8 @@ static int get_pointcache_times_for_particle(PointCache *cache, for (pm = cache->mem_cache.last; pm; pm = pm->prev) { if (BKE_ptcache_mem_index_find(pm, index) >= 0) { - *end = pm->frame; + /* Die *after* the last available frame. */ + *r_dietime = pm->frame + 1; ret++; break; } @@ -1348,7 +1351,9 @@ float psys_get_dietime_from_cache(PointCache *cache, int index) for (pm = cache->mem_cache.last; pm; pm = pm->prev) { if (BKE_ptcache_mem_index_find(pm, index) >= 0) { - return (float)pm->frame; + /* Die *after* the last available frame. */ + dietime = pm->frame + 1; + break; } } @@ -1379,14 +1384,14 @@ static void init_particle_interpolation(Object *ob, pind->dietime = (key + pa->totkey - 1)->time; } else if (pind->cache) { - float start = 0.0f, end = 0.0f; + float start = 0.0f, dietime = 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; + pind->dietime = pa ? pa->dietime : (pind->cache->endframe + 1); - if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &end)) { + if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &dietime)) { pind->birthtime = MAX2(pind->birthtime, start); - pind->dietime = MIN2(pind->dietime, end); + pind->dietime = MIN2(pind->dietime, dietime + 1); } } else { diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 17434ee8023..98430bc422c 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -300,8 +300,10 @@ static int ptcache_particle_write(int index, void *psys_v, void **data, int cfra } } else { - /* Particles are only stored in their lifetime. */ - if (cfra < pa->time - step || cfra > pa->dietime + step) { + /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */ + const int pa_sfra = (int)pa->time - step; + const int pa_efra = ((int)pa->dietime - 1) + step; + if (!(cfra >= pa_sfra && cfra <= pa_efra)) { return 0; } } @@ -414,9 +416,12 @@ static void ptcache_particle_interpolate(int index, pa = psys->particles + index; - /* particle wasn't read from first cache so can't interpolate */ - if ((int)cfra1 < pa->time - psys->pointcache->step || - (int)cfra1 > pa->dietime + psys->pointcache->step) { + /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */ + const int pa_sfra = (int)pa->time - psys->pointcache->step; + const int pa_efra = ((int)pa->dietime - 1) + psys->pointcache->step; + + /* Particle wasn't read from first cache so can't interpolate. */ + if (!(cfra1 >= pa_sfra && cfra1 <= pa_efra)) { return; } @@ -497,12 +502,16 @@ static int ptcache_particle_totwrite(void *psys_v, int cfra) if (psys->part->flag & PART_DIED) { /* Also store dead particles when they are displayed. */ for (p = 0; p < psys->totpart; p++, pa++) { - totwrite += (cfra >= pa->time - step); + const int pa_sfra = (int)pa->time - step; + totwrite += (cfra >= pa_sfra); } } else { for (p = 0; p < psys->totpart; p++, pa++) { - totwrite += (cfra >= pa->time - step && cfra <= pa->dietime + step); + /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */ + const int pa_sfra = (int)pa->time - step; + const int pa_efra = ((int)pa->dietime - 1) + step; + totwrite += (cfra >= pa_sfra) && (cfra <= pa_efra); } } -- cgit v1.2.3