View Issue Details
|ID||Project||Category||View Status||Date Submitted||Last Update|
|0003161||The Dark Mod||Coding||public||27.06.2012 16:02||22.11.2020 11:01|
|Target Version||TDM 2.03||Fixed in Version||TDM 2.03|
|Summary||0003161: Dousing a fireplace causes smoke to bounce|
|Description||When you put out a fire in a fireplace with a water arrow, the flames die, the light dies, and the dissipating smoke bounces up and down until it's gone.|
Do you mean a flickering effect? I'm seeing that a lot lately.
I'm curious about whether there has been a change to the smoke particle.
|The smoke isn't flickering, it's bouncing as it dissipates, as if the particle origin is being recalculated each frame and the origin is moving up and down.|
|Huh. I'll have to look then. I've noticed what I would call flickering, but not bouncing.|
From this discussion: http://forums.thedarkmod.com/topic/16710-particle-request-candle-smoke/page__view__findpost__p__359471
There's a problem with the way the engine handles the randomizing of particles.
The engine wants to vary particles so that chimneys etc don't puff exactly in sync or exactly the same every time, but it needs to make sure that from frame to frame individual particle quads don't jump around the place randomly -- they need to appear to move smoothly. Like the rest of the renderer, it's also a stateless system -- no information is carried from one frame to the next. The way it gets to have all those qualities is that it uses a random number generator that spews out the same list of numbers for a given particle stage each frame. When a particle emitter starts up a random number sequence is initialized (seeded) using a few numbers: the particle's time offset, how many cycles it's gone through, and (optionally) shaderparm 5, which lets mappers start 2 identical particles in the same frame without them looking identical. For func emitters, the mapper doesn't have to remember to do it: the game code sets a random time offset before it passes the particle to the renderer for the first time. The starting position, path, and rotation speed of each particle quad are determined by numbers from that sequence, and then the age of the particle quad is used to determine how far along the "path" the quad is. When the next frame comes around, the parameters will be the same so the same sequence of "random" numbers gets generated and the particle quad moves smoothly.
How many of the random numbers from the sequence are used for each quad's calculations varies depending on its setup. Particles in a rectangular distribution use 3 random numbers in deciding their starting position. Particles in a cylindrical distribution use 5 for that. And aimed particles use more random numbers to work out their movements than view-aligned particles. But any given particle quad always uses the same number of random numbers each frame. So if the 11th number was used in generating "rotation speed" for a particle quad last frame, the 11th number will be used this frame too. When there are multiple quads in a particle stage (nearly always the case), the extra quads just carry on taking numbers from the same sequence. The second quad might use the 23rd number for rotation speed every frame.
The problem starts when the first particle quad dies. It stops consuming numbers from the random number generator, so suddenly quad # 2 starts to get its numbers. Quad # 2's age is still measured correctly, so it remains in approximately the right place, the right distance from the emitter, but its starting position, angle, and other characteristics are suddenly calculated from quad # 1's random number set. And quad # 3 will inherit quad # 2 old numbers, etc etc.
I'm not sure how to fix it yet. It'd be tempting to simply have the code throw away the required number of random numbers when it decides that quad # 1 doesn't need to be drawn, but it'd be unacceptable to break the "stateless" design pattern by having it remember how many numbers each quad needed last frame. Likewise it'd be unacceptable to put a list of the counts somewhere, which would mean remembering to update that list if ever we tweak the way particles are drawn.
How random numbers get consumed:
--- Always consume and discard one rand
----| If path is standard:
------| If distribution is random:
--------| If distribution is rectangular: Consume 3 rands
--------| If distribution is cylindrical: Consume 2 rands
--------| If distribution is spherical: Consume 3 rands
------| Else if distribution is conical: Consume 2 rands
----| Else if path is custom:
------| If path is Helix: Consume 3 rands
------| If path is Flies: Consume 4 rands
------| If path is Orbit: Consume 1 rand
------| If path is drip: 0 rands
----| Consume 1 rand for Angle.
ParticelVerts() calls ParticleOrigin() a second time for aimed particles, but it resets the random number sequence afterwards so that counts as 0.
Fortunately, there's no need to know how the numbers are consumed, or to take account of the decision tree above. From the forum discussion:
There's no need to solve that problem, because there's no need for all the different quads in the same stage to share a random number sequence in the first place. They can each have their own sequence of random numbers. Instead of initialising the random number generators for each stage, using stage properites like time offset etc, we initialise it for each quad using those same stage properties plus the quad number! That way each quad in each stage gets a repeatable set of numbers of its own.
No repeated code, no flags to be passed to complicated functions, no need to touch any code other than the bit that sets up the random number sequence.
This will even simplify the existing setup in the engine. Right now the particle system maintains two random number sequences for each particle stage, because for looping/cycling particles there can be quads on screen from two different cycles at once, and we want successive cycles to look different. But if each quad has its own sequence, we don't need special case code for quads that happen to be in a different cycle.
There's no peformance cost for having more random number sequences. Each one is just a single number (int) that uses a fixed function to work out the next number.
At rev 6237
I think the easiest fix was to have:
* an outer random which is always stepped once per particle, regardless of whether it is dead or alive
* use its output to seed inner random, which is used to compute particle parameters.
I believe that's how it was done in the "particle deform" code.
Anyway, I'm now at the end of The Great Refactoring of Particle Systems (0005138), and I'm getting rid of the approach that I described just above in favor of the approach you implemented.
Basically, random seed for a particle is computed as:
randSeed = Hash(index, particleCycle, randomizer)
index --- index of particle
particleCycle --- number of the cycle the particle lives through right now
randomizer --- whatever external sources of randomness (aka diversity)
The only fun fact is that if I make hash function depend linearly on index, then I see obvious regularities in rain, so I made the dependency quadratic.
|27.06.2012 16:02||grayman||New Issue|
|27.06.2012 21:36||Springheel||Note Added: 0004674|
|27.06.2012 21:40||Springheel||Note Edited: 0004674||View Revisions|
|27.06.2012 21:40||Springheel||Note Edited: 0004674||View Revisions|
|28.06.2012 02:30||grayman||Note Added: 0004678|
|28.06.2012 20:45||Springheel||Note Added: 0004680|
|29.11.2014 09:55||SteveL||Note Added: 0007176|
|29.11.2014 09:55||SteveL||Assigned To||=> SteveL|
|29.11.2014 09:55||SteveL||Status||new => assigned|
|29.11.2014 09:56||SteveL||Note Edited: 0007176||View Revisions|
|29.11.2014 10:11||SteveL||Note Edited: 0007176||View Revisions|
|29.11.2014 10:24||SteveL||Note Added: 0007177|
|29.11.2014 10:24||SteveL||Note Edited: 0007177||View Revisions|
|29.11.2014 10:25||SteveL||Note Edited: 0007177||View Revisions|
|29.11.2014 14:54||SteveL||Note Edited: 0007176||View Revisions|
|29.11.2014 14:56||SteveL||Note Added: 0007178|
|29.11.2014 14:56||SteveL||Note Edited: 0007178||View Revisions|
|29.11.2014 15:00||SteveL||Note Added: 0007179|
|29.11.2014 15:01||SteveL||Status||assigned => resolved|
|29.11.2014 15:01||SteveL||Resolution||open => fixed|
|29.11.2014 15:01||SteveL||Fixed in Version||=> TDM 2.03|
|29.11.2014 15:01||SteveL||Target Version||=> TDM 2.03|
|01.02.2020 05:04||stgatilov||Tag Attached: particle|
|15.11.2020 17:38||stgatilov||Relationship added||related to 0005138|
|22.11.2020 11:00||stgatilov||Note Added: 0013009|
|22.11.2020 11:01||stgatilov||Note Edited: 0013009||View Revisions|