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.
This commit is contained in:
Campbell Barton 2022-04-14 16:58:15 +10:00 committed by Philipp Oeser
parent 7dd15b7113
commit d0bcb6efea
Notes: blender-bot 2023-02-14 07:36:17 +01:00
Referenced by issue #88449: Blender LTS: Maintenance Task 2.93
Referenced by issue #88449, Blender LTS: Maintenance Task 2.93
Referenced by issue #68290, Baked particles dont render in final frame
3 changed files with 37 additions and 18 deletions

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -123,7 +123,12 @@ typedef struct ParticleData {
/** Die-time is not necessarily time+lifetime as. */
float time, lifetime;
/** Particles can die unnaturally (collision). */
/**
* Particles can die unnaturally (collision).
*
* \note Particles die on this frame, be sure to add 1 when clamping the lifetime of particles
* to inclusive ranges such as the scenes end frame. See: T68290.
*/
float dietime;
/**