View Issue Details

IDProjectCategoryView StatusLast Update
0003161The Dark ModCodingpublic22.11.2020 11:01
Reportergrayman Assigned ToSteveL  
PrioritynormalSeveritynormalReproducibilityalways
Status resolvedResolutionfixed 
Product VersionSVN 
Target VersionTDM 2.03Fixed in VersionTDM 2.03 
Summary0003161: Dousing a fireplace causes smoke to bounce
DescriptionWhen 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.
Tagsparticle

Relationships

related to 0005138 resolvedstgatilov Refactor particle systems code 

Activities

Springheel

Springheel

27.06.2012 21:36

administrator   ~0004674

Last edited: 27.06.2012 21:40

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.

grayman

grayman

28.06.2012 02:30

viewer   ~0004678

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.
Springheel

Springheel

28.06.2012 20:45

administrator   ~0004680

Huh. I'll have to look then. I've noticed what I would call flickering, but not bouncing.
SteveL

SteveL

29.11.2014 09:55

reporter   ~0007176

Last edited: 29.11.2014 14:54

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.

SteveL

SteveL

29.11.2014 10:24

reporter   ~0007177

Last edited: 29.11.2014 10:25

How random numbers get consumed:

= idParticleStage::CreateParticle()
--- Always consume and discard one rand
=== idParticleStage::ParticleOrigin()
----| 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
=== idParticleStage::ParticleVerts()
----| 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.

SteveL

SteveL

29.11.2014 14:56

reporter   ~0007178

Last edited: 29.11.2014 14:56

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.

SteveL

SteveL

29.11.2014 15:00

reporter   ~0007179

At rev 6237

renderer/Model_prt.cpp
stgatilov

stgatilov

22.11.2020 11:00

administrator   ~0013009

Last edited: 22.11.2020 11:01

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)
Where:
  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.

Issue History

Date Modified Username Field Change
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
27.06.2012 21:40 Springheel Note Edited: 0004674
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
29.11.2014 10:11 SteveL Note Edited: 0007176
29.11.2014 10:24 SteveL Note Added: 0007177
29.11.2014 10:24 SteveL Note Edited: 0007177
29.11.2014 10:25 SteveL Note Edited: 0007177
29.11.2014 14:54 SteveL Note Edited: 0007176
29.11.2014 14:56 SteveL Note Added: 0007178
29.11.2014 14:56 SteveL Note Edited: 0007178
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