Just putting a dedicated post here. Gonna be some stuff about random numbers here, because it’s such an important part of BioMonkey.
BioMonkey doesn’t use javas default random generator, it has an XORshift generator. It is many times faster. If you look in the FastRandom class in the forester lib you will find the link to a whitepaper and some info about the quality of the numbers etc.
This is some data. I copied it from some post I made last year.
Speed check:
compile-single:
run-single:
Sampling 100000000 random floating point numbers.
Time taken for fast: 415.36368
Time taken for slow (java): 1530.294
Sampling 100000000 random floating point numbers.
Time taken for fast: 404.29938
Time taken for slow (java): 1530.1299
Sampling 100000000 random floating point numbers.
Time taken for fast: 402.24176
Time taken for slow (java): 1524.5559
BUILD SUCCESSFUL (total time: 6 seconds)
Sampling 10000000 random numbers in [0,1].
Number of partitions: 10
Using fast generator
Partition 1: 0.0999716
Partition 2: 0.0999375
Partition 3: 0.1001207
Partition 4: 0.099938
Partition 5: 0.0998954
Partition 6: 0.1001462
Partition 7: 0.1000686
Partition 8: 0.1000338
Partition 9: 0.1000256
Partition 10: 0.0998626
Max deviation: 1.3740361E-4
Sampling 10000000 random numbers in [0,1].
Number of partitions: 10
Using standard generator (java)
Partition 1: 0.1001091
Partition 2: 0.0999581
Partition 3: 0.099842
Partition 4: 0.0997741
Partition 5: 0.099973
Partition 6: 0.1000415
Partition 7: 0.1000817
Partition 8: 0.0999945
Partition 9: 0.1000933
Partition 10: 0.1001327
Max deviation: 2.259016E-4
Speed is a lot higher with the XORshift generator, and precision is pretty much the same. The speed doesn't come from loss of precision. These tests are just confirmations that the implementation itself work, and that there are no noticable biases due to float precision etc. The values are pretty much as expected.
Here's a simple 64 bit generator, for the lazy.
[java]
public class Random {
protected long x;
public Random(){
x = System.nanoTime();
}
public long next()
{
x ^= (x << 21);
x ^= (x >>> 35);
x ^= (x << 4);
return x;
}
}
[/java]
There will be more stuff here. A 2d poisson disk sampling algorithm will be next, and it'll be added to the lib as well, but I'm gonna have to test it more to make sure there are no bugs.
Yes, one of the things high on my todo-list was a 2d poisson disk sampling algorithm for distribution of cruft on the terrain, now I’ll wait for your implementation and save myself the trouble
The XOR-thingy I implemented a while ago and ran some tests. If you need to synchronize access to it the speed difference between that and a java Math.Random was almost nothing (i.e. almost as fast/slow). I was very surprised that synchronizing in this day and age (JDK7) cost me that much (might be an artifact of the test also).
So something to take in to consideration if you want to reuse the XORRandom across threads (like loading tiles in the background) but still get the ability to recreate a level from only a seed value.
@kwando said:
One can use one generator per tile which are seeded by the tile coordinates multiplied by a master seed value. Hence no synchronization will be needed :)
That sounds like what I'm doing. I had to assign a value to each world tile so that grass and stuff gets the same distribution when regenerated, so it kind of solved itself. I love that it's such a small class you can just pop one out wherever you need it.
The poisson disk sampler is a bit of a mess because I didn't code it from scratch, but it was part of some terrain generation "sketch" thing that I translated to jME. Anyways, I have to clean it up and find out where I got the thing so I can attribute it properly before adding. Shouldn't take too long, considering it's a small class.
Here's an image of a 200x200 grid with min distance 10. I recently added a validate method too (to check that the produced numbers actually satisfy the initial conditions), and also a method to dump an image like this. Everything seems to work. :D
Interesting. Haven’t done any real comparisons with other generators except the default java myself. I guess the “smallness” of the class is the only thing separating it from LCG’s etc. It’s so easy to just use it everywhere without having to work with weird seeds and tables and stuff.
My current playground loop over the terrain quad with +10 on x and y, generate a random number and if it is smaller than the density map create a boulder or something (currently just placeholders). With the XOR rand and (even pre-calculated) poisson disk points I think it will be both fast and look good. The only thing lacking now is time to actually code the stuff
I think maybe that is a good start to some general placement design. First find a set of sample points taken from the height map (poisson disk to find the height from terrain or whatever). Then filter them according to different strategies like removing points according to a densitymap, remove some more them if they are at the wrong height and so on. Maybe not the most efficient but pretty easy to understand and should lend itself to tools. Almost like the nodes in NeoTexture editor.
There’s a poisson disk sampler in the biomonkey lib. it made the images in the posts above.
I don’t want to include colt in this lib. It is large, and most of the stuff is not needed. Normal, Poisson disk and regular uniform distributions is enough, and I’m using a good generator and good algorithms for those.
This is the poisson disk sampler from BioMonkey. Been using it for a month or so, and validation + printed images suggests it’s working well. No guarantees tho.
[java]
import com.jme3.math.FastMath;
import com.jme3.math.Vector2f;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import se.jod.biomonkey.random.FastRandom;
/**
Poisson disk sampling for semi-random distributions. Adapted from
The FastRandom class can be replaced with java random generator or w/e. It is only referenced in the two random(float f) and random(float min, float max) methods. Otherwise it will be available in BioMonkey (just as the poisson disk sampler).
Speaking of random number generation, I found that a long while ago, I don’t remember where, but it’s a random generator for GLSL.
I have no idea how fast it is though. But, if someone would like to test it, you technically could paint x number of pixels and manually count the time it took. Very dirty, I know, but result could be interesting.
The formula is simple, admittedly, you couldn’t outsource it to a shader to get and use the result externally.