[fixed]Particle emitters use integer rand()

You discovered a bug in the engine, and you are sure that it is not a problem of your code? Just post it in here. Please read the bug posting guidelines first.
Post Reply
xDan
Competition winner
Posts: 673
Joined: Thu Mar 30, 2006 1:23 pm
Location: UK
Contact:

[fixed]Particle emitters use integer rand()

Post by xDan »

At least that could be the cause!

The problem being: a box emitter (maybe others, haven't looked), emits only in a limited number of locations.

I had something like a 40x40x1 box and particles were emitting in 5x5 different points. So for my rain effect the rain was coming down in 25 separate streams :(

In CParticleBoxEmitter this code selects position:

Code: Select all

			p.pos.X = Box.MinEdge.X + fmodf((f32)os::Randomizer::rand(), extent.X);
			p.pos.Y = Box.MinEdge.Y + fmodf((f32)os::Randomizer::rand(), extent.Y);
			p.pos.Z = Box.MinEdge.Z + fmodf((f32)os::Randomizer::rand(), extent.Z);
I'm not really sure why this doesn't work to be honest :oops:

But perhaps this sort of thing might work:

pos.X = MinEdge.X + extent.X * ((f32)(rand()%10000)) * 0.0001;
etc...

??

(I am temporarily getting around this problem by repositioning the particle system at a random offset every frame. So then the "streams" are never in the same position)

And while I'm at it, a feature request (sorry): axis aligned particles! (is that the correct term?!)As for rain, you can't have long droplets, as they turn to face the camera. And look quite silly from above... Perhaps the link here by bitplane is what I'm looking for, but would be nice to have it included in irrlicht. (and the gdlib.net link doesn't work for me at the moment)
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Well, the os::Randomizer::rand() exists so that the same pseudorandom sequence can be returned on all platforms. You would want to avoid using the C library rand() function for this reason.

Code: Select all

fmodf((f32)os::Randomizer::rand(), extent.X); 
Consider that rand() here returns an integer value. If the extent is a nice round integer value [say 10], no matter what value is returned by rand() a nice round integer value will be returned. If you change extent to have some fractional part, this problem goes away. Say extent.X is 10.3. Here is a quick table showing the results

Code: Select all

	 10.0	10.3 // extent
0	 0.0	 0.0
1	 1.0	 1.0
2	 2.0	 2.0
10	0.0	10.0
11	1.0	 0.7
12	2.0	 1.7
20	0.0	 9.7
21	1.0	 0.4
22	2.0	 1.4
30	0.0	 9.4
31	1.0	 0.1
32	2.0	 1.1
40	0.0	 9.1
41	1.0	10.1
42	2.0	 0.8
50	0.0	 8.8
51	1.0	 9.8
52	2.0	 0.5
...
So that makes for a simple workaround without modifying and rebuilding the library.

I don't have a compiler to verify, but it is also possible that that os::Randomizer::rand() doesn't return values that are statistically random. I know that this is a problem with many simple random number generators, even rand() on some platforms. You might write a testcase to see how random the values actually are.
pos.X = MinEdge.X + extent.X * ((f32)(rand()%10000)) * 0.0001;
One quick note about this. When dealing with rand(), you should never do an integer modulus. You should scale the result by RAND_MAX as a float. It will get a better distribution...

Code: Select all

pos.X = MinEdge.X + extent.X * ((1.f * rand()) / RAND_MAX);
And while I'm at it, a feature request (sorry): axis aligned particles!
Yeah, this has been mentioned before. If there was a particle renderer interface that could plug into the particle system, it would be possible to write a particle renderer to do this without having to modify or copy/paste the existing particle system code.

Travis
xDan
Competition winner
Posts: 673
Joined: Thu Mar 30, 2006 1:23 pm
Location: UK
Contact:

Post by xDan »

If you change extent to have some fractional part, this problem goes away.
That works. Thanks. But still, take note Irrdevs! Would be nice if this problem could not occur.
One quick note about this. When dealing with rand(), you should never do an integer modulus. You should scale the result by RAND_MAX as a float. It will get a better distribution...
Ok :)
Auria
Competition winner
Posts: 120
Joined: Wed Feb 18, 2009 1:11 am
Contact:

Post by Auria »

I'm bumping this because we (SuperTuxKart) met the exact same issue, except that in our case the box was not even a whole number.

I would appreciate if a patch like the one below could be applied, having emissions only at every unit is very visible and ugly

Code: Select all

Index: source/Irrlicht/CParticleBoxEmitter.cpp
===================================================================
--- source/Irrlicht/CParticleBoxEmitter.cpp	(revision 3516)
+++ source/Irrlicht/CParticleBoxEmitter.cpp	(working copy)
@@ -56,9 +56,10 @@
 
 		for (u32 i=0; i<amount; ++i)
 		{
-			p.pos.X = Box.MinEdge.X + fmodf((f32)os::Randomizer::rand(), extent.X);
-			p.pos.Y = Box.MinEdge.Y + fmodf((f32)os::Randomizer::rand(), extent.Y);
-			p.pos.Z = Box.MinEdge.Z + fmodf((f32)os::Randomizer::rand(), extent.Z);
+            
+			p.pos.X = Box.MinEdge.X + ((f32)(os::Randomizer::rand()%10000)) * 0.0001 * extent.X;
+			p.pos.Y = Box.MinEdge.Y + ((f32)(os::Randomizer::rand()%10000)) * 0.0001 * extent.Y;
+			p.pos.Z = Box.MinEdge.Z + ((f32)(os::Randomizer::rand()%10000)) * 0.0001 * extent.Z;
 
 			p.startTime = now;
 			p.vector = Direction;
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Post by CuteAlien »

Having Randomizer::randf or frand might be even better. I think I even run into the same problem with particles already, but didn't figure it out back then. Unfortunately I probably won't have time next few days (not until I finished doing taxes *sigh*). But if no-one else fixes it I'll get back to it in a week or two.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Auria
Competition winner
Posts: 120
Joined: Wed Feb 18, 2009 1:11 am
Contact:

Post by Auria »

CuteAlien wrote:Having Randomizer::randf or frand might be even better. I think I even run into the same problem with particles already, but didn't figure it out back then. Unfortunately I probably won't have time next few days (not until I finished doing taxes *sigh*). But if no-one else fixes it I'll get back to it in a week or two.
+1, randf() would be very useful :D
I'll check if maybe I can provide a patch that adds randf()

EDIT : alright, there we go :)

Code: Select all

Index: source/Irrlicht/CParticleBoxEmitter.cpp
===================================================================
--- source/Irrlicht/CParticleBoxEmitter.cpp	(revision 3516)
+++ source/Irrlicht/CParticleBoxEmitter.cpp	(working copy)
@@ -56,9 +56,10 @@
 
 		for (u32 i=0; i<amount; ++i)
 		{
-			p.pos.X = Box.MinEdge.X + fmodf((f32)os::Randomizer::rand(), extent.X);
-			p.pos.Y = Box.MinEdge.Y + fmodf((f32)os::Randomizer::rand(), extent.Y);
-			p.pos.Z = Box.MinEdge.Z + fmodf((f32)os::Randomizer::rand(), extent.Z);
+            
+			p.pos.X = Box.MinEdge.X + os::Randomizer::randf() * extent.X;
+			p.pos.Y = Box.MinEdge.Y + os::Randomizer::randf() * extent.Y;
+			p.pos.Z = Box.MinEdge.Z + os::Randomizer::randf() * extent.Z;
 
 			p.startTime = now;
 			p.vector = Direction;
@@ -75,10 +76,11 @@
 			if (MaxLifeTime - MinLifeTime == 0)
 				p.endTime = now + MinLifeTime;
 			else
-				p.endTime = now + MinLifeTime + (os::Randomizer::rand() % (MaxLifeTime - MinLifeTime));
+				p.endTime = now + MinLifeTime + (os::Randomizer::rand() % (MaxLifeTime - MinLifeTime));
 
 			p.color = MinStartColor.getInterpolated(
-				MaxStartColor, (os::Randomizer::rand() % 100) / 100.0f);
+				MaxStartColor, os::Randomizer::randf());
 
 			p.startColor = p.color;
 			p.startVector = p.vector;
@@ -87,7 +89,7 @@
 				p.startSize = MinStartSize;
 			else
 				p.startSize = MinStartSize.getInterpolated(
-					MaxStartSize, (os::Randomizer::rand() % 100) / 100.0f);
+					MaxStartSize, os::Randomizer::randf());
 			p.size = p.startSize;
 
 			Particles.push_back(p);
Index: source/Irrlicht/os.cpp
===================================================================
--- source/Irrlicht/os.cpp	(revision 3516)
+++ source/Irrlicht/os.cpp	(working copy)
@@ -207,6 +207,11 @@
 		return seed;
 	}
 
+    f32 Randomizer::randf()
+    {
+        return ((f32)(Randomizer::rand()%10000)) * 0.0001;
+    }
+    
 	//! resets the randomizer
 	void Randomizer::reset()
 	{
Index: source/Irrlicht/os.h
===================================================================
--- source/Irrlicht/os.h	(revision 3516)
+++ source/Irrlicht/os.h	(working copy)
@@ -51,6 +51,9 @@
 
 		//! generates a pseudo random number
 		static s32 rand();
+        
+        //! generates a pseudo random number in range [0 .. 1]
+        static f32 randf();
 
 	private:
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Well, I named it frand if you don't mind. With some similarity to drand from C-lib. I had to search a little longer until I knew what happened inside rand() and to get a proper calculation inside frand without disturbing the good distribution properties of rand(). Moreover, the stupid particle effect implementation in the examples bugged me once more. But this patch seems to be fine. Check it out in trunk/
Auria
Competition winner
Posts: 120
Joined: Wed Feb 18, 2009 1:11 am
Contact:

Post by Auria »

Nice, thanks :)
Post Reply