1
0
mirror of https://github.com/moparisthebest/minetest synced 2024-12-22 23:58:48 -05:00

Add support for the PCG32 PRNG algo (and associated script APIs)

This commit is contained in:
kwolekr 2015-03-22 00:01:46 -04:00
parent 7679396ebb
commit 3993093f51
10 changed files with 415 additions and 179 deletions

View File

@ -2515,7 +2515,8 @@ an itemstring, a table or `nil`.
Returns taken `ItemStack`. Returns taken `ItemStack`.
### `PseudoRandom` ### `PseudoRandom`
A pseudorandom number generator. A 16-bit pseudorandom number generator.
Uses a well-known LCG algorithm introduced by K&R.
It can be created via `PseudoRandom(seed)`. It can be created via `PseudoRandom(seed)`.
@ -2525,6 +2526,19 @@ It can be created via `PseudoRandom(seed)`.
* `((max - min) == 32767) or ((max-min) <= 6553))` must be true * `((max - min) == 32767) or ((max-min) <= 6553))` must be true
due to the simple implementation making bad distribution otherwise. due to the simple implementation making bad distribution otherwise.
### `PcgRandom`
A 32-bit pseudorandom number generator.
Uses PCG32, an algorithm of the permuted congruential generator family, offering very strong randomness.
It can be created via `PcgRandom(seed)` or `PcgRandom(seed, sequence)`.
#### Methods
* `next()`: return next integer random number [`-2147483648`...`2147483647`]
* `next(min, max)`: return next integer random number [`min`...`max`]
* `rand_normal_dist(min, max, num_trials=6)`: return normally distributed random number [`min`...`max`]
* This is only a rough approximation of a normal distribution with mean=(max-min)/2 and variance=1
* Increasing num_trials improves accuracy of the approximation
### `PerlinNoise` ### `PerlinNoise`
A perlin noise generator. A perlin noise generator.
It can be created via `PerlinNoise(seed, octaves, persistence, scale)` It can be created via `PerlinNoise(seed, octaves, persistence, scale)`

View File

@ -515,14 +515,10 @@ void MapgenParams::load(const Settings &settings)
std::string seed_str; std::string seed_str;
const char *seed_name = (&settings == g_settings) ? "fixed_map_seed" : "seed"; const char *seed_name = (&settings == g_settings) ? "fixed_map_seed" : "seed";
if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty()) { if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty())
seed = read_seed(seed_str.c_str()); seed = read_seed(seed_str.c_str());
} else { else
seed = ((u64)(myrand() & 0xFFFF) << 0) | myrand_bytes(&seed, sizeof(seed));
((u64)(myrand() & 0xFFFF) << 16) |
((u64)(myrand() & 0xFFFF) << 32) |
((u64)(myrand() & 0xFFFF) << 48);
}
settings.getNoEx("mg_name", mg_name); settings.getNoEx("mg_name", mg_name);
settings.getS16NoEx("water_level", water_level); settings.getS16NoEx("water_level", water_level);

View File

@ -308,7 +308,7 @@ void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
} }
// randval ranges from -1..1 // randval ranges from -1..1
float randval = (float)pr.next() / (PSEUDORANDOM_MAX / 2) - 1.f; float randval = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f;
float noiseval = contour(noise->result[index]); float noiseval = contour(noise->result[index]);
float noiseval2 = contour(noise2->result[index]); float noiseval2 = contour(noise2->result[index]);
if (noiseval * noiseval2 + randval * random_factor < nthresh) if (noiseval * noiseval2 + randval * random_factor < nthresh)

View File

@ -62,6 +62,107 @@ FlagDesc flagdesc_noiseparams[] = {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
PcgRandom::PcgRandom(u64 state, u64 seq)
{
seed(state, seq);
}
void PcgRandom::seed(u64 state, u64 seq)
{
m_state = 0U;
m_inc = (seq << 1u) | 1u;
next();
m_state += state;
next();
}
u32 PcgRandom::next()
{
u64 oldstate = m_state;
m_state = oldstate * 6364136223846793005ULL + m_inc;
u32 xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
u32 rot = oldstate >> 59u;
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
}
u32 PcgRandom::range(u32 bound)
{
/*
If the bound is not a multiple of the RNG's range, it may cause bias,
e.g. a RNG has a range from 0 to 3 and we take want a number 0 to 2.
Using rand() % 3, the number 0 would be twice as likely to appear.
With a very large RNG range, the effect becomes less prevalent but
still present. This can be solved by modifying the range of the RNG
to become a multiple of bound by dropping values above the a threshhold.
In our example, threshhold == 4 - 3 = 1 % 3 == 1, so reject 0, thus
making the range 3 with no bias.
This loop looks dangerous, but will always terminate due to the
RNG's property of uniformity.
*/
u32 threshhold = -bound % bound;
u32 r;
while ((r = next()) < threshhold);
return r % bound;
}
s32 PcgRandom::range(s32 min, s32 max)
{
assert(max >= min);
u32 bound = max - min + 1;
return range(bound) + min;
}
void PcgRandom::bytes(void *out, size_t len)
{
u32 r;
u8 *outb = (u8 *)out;
size_t len_alignment = (uintptr_t)out % sizeof(u32);
if (len_alignment) {
r = next();
while (len_alignment--) {
*outb = r & 0xFF;
outb++;
r >>= 8;
}
}
size_t len_dwords = len / sizeof(u32);
while (len_dwords--) {
r = next();
*(u32 *)outb = next();
outb += sizeof(u32);
}
size_t len_remaining = len % sizeof(u32);
if (len_remaining) {
r = next();
while (len_remaining--) {
*outb = r & 0xFF;
outb++;
r >>= 8;
}
}
}
s32 PcgRandom::randNormalDist(s32 min, s32 max, int num_trials)
{
u32 accum = 0;
for (int i = 0; i != num_trials; i++)
accum += range(min, max);
return ((float)accum / num_trials) + 0.5f;
}
///////////////////////////////////////////////////////////////////////////////
float noise2d(int x, int y, int seed) float noise2d(int x, int y, int seed)
{ {

View File

@ -30,47 +30,67 @@
#include "irr_v3d.h" #include "irr_v3d.h"
#include "util/string.h" #include "util/string.h"
#define PSEUDORANDOM_MAX 32767
extern FlagDesc flagdesc_noiseparams[]; extern FlagDesc flagdesc_noiseparams[];
class PseudoRandom // Note: this class is not polymorphic so that its high level of
{ // optimizability may be preserved in the common use case
class PseudoRandom {
public: public:
PseudoRandom(): m_next(0) const static u32 RANDOM_RANGE = 32767;
inline PseudoRandom(int seed=0):
m_next(seed)
{ {
} }
PseudoRandom(int seed): m_next(seed)
{ inline void seed(int seed)
}
void seed(int seed)
{ {
m_next = seed; m_next = seed;
} }
// Returns 0...PSEUDORANDOM_MAX
int next() inline int next()
{ {
m_next = m_next * 1103515245 + 12345; m_next = m_next * 1103515245 + 12345;
return((unsigned)(m_next/65536) % (PSEUDORANDOM_MAX + 1)); return (unsigned)(m_next / 65536) % (RANDOM_RANGE + 1);
} }
int range(int min, int max)
inline int range(int min, int max)
{ {
if (max-min > (PSEUDORANDOM_MAX + 1) / 10) assert(max >= min);
{ /*
//dstream<<"WARNING: PseudoRandom::range: max > 32767"<<std::endl; Here, we ensure the range is not too large relative to RANDOM_MAX,
assert("Something wrong with random number" == NULL); as otherwise the effects of bias would become noticable. Unlike
} PcgRandom, we cannot modify this RNG's range as it would change the
if(min > max) output of this RNG for reverse compatibility.
{ */
assert("Something wrong with random number" == NULL); assert((u32)(max - min) <= (RANDOM_RANGE + 1) / 10);
//return max;
} return (next() % (max - min + 1)) + min;
return (next()%(max-min+1))+min;
} }
private: private:
int m_next; int m_next;
}; };
class PcgRandom {
public:
const static s32 RANDOM_MIN = -0x7fffffff - 1;
const static s32 RANDOM_MAX = 0x7fffffff;
const static u32 RANDOM_RANGE = 0xffffffff;
PcgRandom(u64 state=0x853c49e6748fea9bULL, u64 seq=0xda3e39cb94b95bdbULL);
void seed(u64 state, u64 seq=0xda3e39cb94b95bdbULL);
u32 next();
u32 range(u32 bound);
s32 range(s32 min, s32 max);
void bytes(void *out, size_t len);
s32 randNormalDist(s32 min, s32 max, int num_trials=6);
private:
u64 m_state;
u64 m_inc;
};
#define NOISE_FLAG_DEFAULTS 0x01 #define NOISE_FLAG_DEFAULTS 0x01
#define NOISE_FLAG_EASED 0x02 #define NOISE_FLAG_EASED 0x02
#define NOISE_FLAG_ABSVALUE 0x04 #define NOISE_FLAG_ABSVALUE 0x04
@ -89,7 +109,8 @@ struct NoiseParams {
float lacunarity; float lacunarity;
u32 flags; u32 flags;
NoiseParams() { NoiseParams()
{
offset = 0.0f; offset = 0.0f;
scale = 1.0f; scale = 1.0f;
spread = v3f(250, 250, 250); spread = v3f(250, 250, 250);

View File

@ -23,12 +23,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common/c_content.h" #include "common/c_content.h"
#include "log.h" #include "log.h"
// garbage collector ///////////////////////////////////////
int LuaPerlinNoise::gc_object(lua_State *L) /*
LuaPerlinNoise
*/
LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) :
np(*params)
{
}
LuaPerlinNoise::~LuaPerlinNoise()
{ {
LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1));
delete o;
return 0;
} }
@ -54,19 +61,6 @@ int LuaPerlinNoise::l_get3d(lua_State *L)
} }
LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) :
np(*params)
{
}
LuaPerlinNoise::~LuaPerlinNoise()
{
}
// LuaPerlinNoise(seed, octaves, persistence, scale)
// Creates an LuaPerlinNoise and leaves it on top of stack
int LuaPerlinNoise::create_object(lua_State *L) int LuaPerlinNoise::create_object(lua_State *L)
{ {
NO_MAP_LOCK_REQUIRED; NO_MAP_LOCK_REQUIRED;
@ -91,14 +85,22 @@ int LuaPerlinNoise::create_object(lua_State *L)
} }
LuaPerlinNoise* LuaPerlinNoise::checkobject(lua_State *L, int narg) int LuaPerlinNoise::gc_object(lua_State *L)
{
LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1));
delete o;
return 0;
}
LuaPerlinNoise *LuaPerlinNoise::checkobject(lua_State *L, int narg)
{ {
NO_MAP_LOCK_REQUIRED; NO_MAP_LOCK_REQUIRED;
luaL_checktype(L, narg, LUA_TUSERDATA); luaL_checktype(L, narg, LUA_TUSERDATA);
void *ud = luaL_checkudata(L, narg, className); void *ud = luaL_checkudata(L, narg, className);
if (!ud) if (!ud)
luaL_typerror(L, narg, className); luaL_typerror(L, narg, className);
return *(LuaPerlinNoise**)ud; // unbox pointer return *(LuaPerlinNoise **)ud;
} }
@ -111,7 +113,7 @@ void LuaPerlinNoise::Register(lua_State *L)
lua_pushliteral(L, "__metatable"); lua_pushliteral(L, "__metatable");
lua_pushvalue(L, methodtable); lua_pushvalue(L, methodtable);
lua_settable(L, metatable); // hide metatable from Lua getmetatable() lua_settable(L, metatable);
lua_pushliteral(L, "__index"); lua_pushliteral(L, "__index");
lua_pushvalue(L, methodtable); lua_pushvalue(L, methodtable);
@ -121,12 +123,11 @@ void LuaPerlinNoise::Register(lua_State *L)
lua_pushcfunction(L, gc_object); lua_pushcfunction(L, gc_object);
lua_settable(L, metatable); lua_settable(L, metatable);
lua_pop(L, 1); // drop metatable lua_pop(L, 1);
luaL_openlib(L, 0, methods, 0); // fill methodtable luaL_openlib(L, 0, methods, 0);
lua_pop(L, 1); // drop methodtable lua_pop(L, 1);
// Can be created from Lua (PerlinNoise(seed, octaves, persistence)
lua_register(L, className, create_object); lua_register(L, className, create_object);
} }
@ -138,16 +139,26 @@ const luaL_reg LuaPerlinNoise::methods[] = {
{0,0} {0,0}
}; };
///////////////////////////////////////
/* /*
PerlinNoiseMap LuaPerlinNoiseMap
*/ */
int LuaPerlinNoiseMap::gc_object(lua_State *L) LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size)
{ {
LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1)); m_is3d = size.Z > 1;
delete o; np = *params;
return 0; try {
noise = new Noise(&np, seed, size.X, size.Y, size.Z);
} catch (InvalidNoiseParamsException &e) {
throw LuaError(e.what());
}
}
LuaPerlinNoiseMap::~LuaPerlinNoiseMap()
{
delete noise;
} }
@ -251,26 +262,6 @@ int LuaPerlinNoiseMap::l_get3dMap_flat(lua_State *L)
} }
LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size)
{
m_is3d = size.Z > 1;
np = *params;
try {
noise = new Noise(&np, seed, size.X, size.Y, size.Z);
} catch (InvalidNoiseParamsException &e) {
throw LuaError(e.what());
}
}
LuaPerlinNoiseMap::~LuaPerlinNoiseMap()
{
delete noise;
}
// LuaPerlinNoiseMap(np, size)
// Creates an LuaPerlinNoiseMap and leaves it on top of stack
int LuaPerlinNoiseMap::create_object(lua_State *L) int LuaPerlinNoiseMap::create_object(lua_State *L)
{ {
NoiseParams np; NoiseParams np;
@ -286,6 +277,14 @@ int LuaPerlinNoiseMap::create_object(lua_State *L)
} }
int LuaPerlinNoiseMap::gc_object(lua_State *L)
{
LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1));
delete o;
return 0;
}
LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg) LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg)
{ {
luaL_checktype(L, narg, LUA_TUSERDATA); luaL_checktype(L, narg, LUA_TUSERDATA);
@ -294,7 +293,7 @@ LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg)
if (!ud) if (!ud)
luaL_typerror(L, narg, className); luaL_typerror(L, narg, className);
return *(LuaPerlinNoiseMap **)ud; // unbox pointer return *(LuaPerlinNoiseMap **)ud;
} }
@ -307,7 +306,7 @@ void LuaPerlinNoiseMap::Register(lua_State *L)
lua_pushliteral(L, "__metatable"); lua_pushliteral(L, "__metatable");
lua_pushvalue(L, methodtable); lua_pushvalue(L, methodtable);
lua_settable(L, metatable); // hide metatable from Lua getmetatable() lua_settable(L, metatable);
lua_pushliteral(L, "__index"); lua_pushliteral(L, "__index");
lua_pushvalue(L, methodtable); lua_pushvalue(L, methodtable);
@ -317,12 +316,11 @@ void LuaPerlinNoiseMap::Register(lua_State *L)
lua_pushcfunction(L, gc_object); lua_pushcfunction(L, gc_object);
lua_settable(L, metatable); lua_settable(L, metatable);
lua_pop(L, 1); // drop metatable lua_pop(L, 1);
luaL_openlib(L, 0, methods, 0); // fill methodtable luaL_openlib(L, 0, methods, 0);
lua_pop(L, 1); // drop methodtable lua_pop(L, 1);
// Can be created from Lua (PerlinNoiseMap(np, size)
lua_register(L, className, create_object); lua_register(L, className, create_object);
} }
@ -336,32 +334,23 @@ const luaL_reg LuaPerlinNoiseMap::methods[] = {
{0,0} {0,0}
}; };
///////////////////////////////////////
/* /*
LuaPseudoRandom LuaPseudoRandom
*/ */
// garbage collector
int LuaPseudoRandom::gc_object(lua_State *L)
{
LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1));
delete o;
return 0;
}
// next(self, min=0, max=32767) -> get next value
int LuaPseudoRandom::l_next(lua_State *L) int LuaPseudoRandom::l_next(lua_State *L)
{ {
NO_MAP_LOCK_REQUIRED; NO_MAP_LOCK_REQUIRED;
LuaPseudoRandom *o = checkobject(L, 1); LuaPseudoRandom *o = checkobject(L, 1);
int min = 0; int min = 0;
int max = 32767; int max = 32767;
lua_settop(L, 3); // Fill 2 and 3 with nil if they don't exist lua_settop(L, 3);
if(!lua_isnil(L, 2)) if (lua_isnumber(L, 2))
min = luaL_checkinteger(L, 2); min = luaL_checkinteger(L, 2);
if(!lua_isnil(L, 3)) if (lua_isnumber(L, 3))
max = luaL_checkinteger(L, 3); max = luaL_checkinteger(L, 3);
if(max < min){ if (max < min) {
errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl; errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl;
throw LuaError("PseudoRandom.next(): max < min"); throw LuaError("PseudoRandom.next(): max < min");
} }
@ -378,30 +367,6 @@ int LuaPseudoRandom::l_next(lua_State *L)
} }
LuaPseudoRandom::LuaPseudoRandom(int seed):
m_pseudo(seed)
{
}
LuaPseudoRandom::~LuaPseudoRandom()
{
}
const PseudoRandom& LuaPseudoRandom::getItem() const
{
return m_pseudo;
}
PseudoRandom& LuaPseudoRandom::getItem()
{
return m_pseudo;
}
// LuaPseudoRandom(seed)
// Creates an LuaPseudoRandom and leaves it on top of stack
int LuaPseudoRandom::create_object(lua_State *L) int LuaPseudoRandom::create_object(lua_State *L)
{ {
int seed = luaL_checknumber(L, 1); int seed = luaL_checknumber(L, 1);
@ -413,13 +378,21 @@ int LuaPseudoRandom::create_object(lua_State *L)
} }
LuaPseudoRandom* LuaPseudoRandom::checkobject(lua_State *L, int narg) int LuaPseudoRandom::gc_object(lua_State *L)
{
LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1));
delete o;
return 0;
}
LuaPseudoRandom *LuaPseudoRandom::checkobject(lua_State *L, int narg)
{ {
luaL_checktype(L, narg, LUA_TUSERDATA); luaL_checktype(L, narg, LUA_TUSERDATA);
void *ud = luaL_checkudata(L, narg, className); void *ud = luaL_checkudata(L, narg, className);
if (!ud) if (!ud)
luaL_typerror(L, narg, className); luaL_typerror(L, narg, className);
return *(LuaPseudoRandom**)ud; // unbox pointer return *(LuaPseudoRandom **)ud;
} }
@ -432,7 +405,7 @@ void LuaPseudoRandom::Register(lua_State *L)
lua_pushliteral(L, "__metatable"); lua_pushliteral(L, "__metatable");
lua_pushvalue(L, methodtable); lua_pushvalue(L, methodtable);
lua_settable(L, metatable); // hide metatable from Lua getmetatable() lua_settable(L, metatable);
lua_pushliteral(L, "__index"); lua_pushliteral(L, "__index");
lua_pushvalue(L, methodtable); lua_pushvalue(L, methodtable);
@ -442,12 +415,11 @@ void LuaPseudoRandom::Register(lua_State *L)
lua_pushcfunction(L, gc_object); lua_pushcfunction(L, gc_object);
lua_settable(L, metatable); lua_settable(L, metatable);
lua_pop(L, 1); // drop metatable lua_pop(L, 1);
luaL_openlib(L, 0, methods, 0); // fill methodtable luaL_openlib(L, 0, methods, 0);
lua_pop(L, 1); // drop methodtable lua_pop(L, 1);
// Can be created from Lua (LuaPseudoRandom(seed))
lua_register(L, className, create_object); lua_register(L, className, create_object);
} }
@ -457,3 +429,101 @@ const luaL_reg LuaPseudoRandom::methods[] = {
luamethod(LuaPseudoRandom, next), luamethod(LuaPseudoRandom, next),
{0,0} {0,0}
}; };
///////////////////////////////////////
/*
LuaPcgRandom
*/
int LuaPcgRandom::l_next(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
LuaPcgRandom *o = checkobject(L, 1);
u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN;
u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX;
lua_pushinteger(L, o->m_rnd.range(min, max));
return 1;
}
int LuaPcgRandom::l_rand_normal_dist(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
LuaPcgRandom *o = checkobject(L, 1);
u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN;
u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX;
int num_trials = lua_isnumber(L, 4) ? lua_tointeger(L, 4) : 6;
lua_pushinteger(L, o->m_rnd.randNormalDist(min, max, num_trials));
return 1;
}
int LuaPcgRandom::create_object(lua_State *L)
{
lua_Integer seed = luaL_checknumber(L, 1);
LuaPcgRandom *o = lua_isnumber(L, 2) ?
new LuaPcgRandom(seed, lua_tointeger(L, 2)) :
new LuaPcgRandom(seed);
*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
luaL_getmetatable(L, className);
lua_setmetatable(L, -2);
return 1;
}
int LuaPcgRandom::gc_object(lua_State *L)
{
LuaPcgRandom *o = *(LuaPcgRandom **)(lua_touserdata(L, 1));
delete o;
return 0;
}
LuaPcgRandom *LuaPcgRandom::checkobject(lua_State *L, int narg)
{
luaL_checktype(L, narg, LUA_TUSERDATA);
void *ud = luaL_checkudata(L, narg, className);
if (!ud)
luaL_typerror(L, narg, className);
return *(LuaPcgRandom **)ud;
}
void LuaPcgRandom::Register(lua_State *L)
{
lua_newtable(L);
int methodtable = lua_gettop(L);
luaL_newmetatable(L, className);
int metatable = lua_gettop(L);
lua_pushliteral(L, "__metatable");
lua_pushvalue(L, methodtable);
lua_settable(L, metatable);
lua_pushliteral(L, "__index");
lua_pushvalue(L, methodtable);
lua_settable(L, metatable);
lua_pushliteral(L, "__gc");
lua_pushcfunction(L, gc_object);
lua_settable(L, metatable);
lua_pop(L, 1);
luaL_openlib(L, 0, methods, 0);
lua_pop(L, 1);
lua_register(L, className, create_object);
}
const char LuaPcgRandom::className[] = "PcgRandom";
const luaL_reg LuaPcgRandom::methods[] = {
luamethod(LuaPcgRandom, next),
luamethod(LuaPcgRandom, rand_normal_dist),
{0,0}
};

View File

@ -64,6 +64,9 @@ class LuaPerlinNoiseMap : public ModApiBase {
static const char className[]; static const char className[];
static const luaL_reg methods[]; static const luaL_reg methods[];
// Exported functions
// garbage collector
static int gc_object(lua_State *L); static int gc_object(lua_State *L);
static int l_get2dMap(lua_State *L); static int l_get2dMap(lua_State *L);
@ -104,18 +107,51 @@ private:
static int l_next(lua_State *L); static int l_next(lua_State *L);
public: public:
LuaPseudoRandom(int seed); LuaPseudoRandom(int seed) :
m_pseudo(seed) {}
~LuaPseudoRandom();
const PseudoRandom& getItem() const;
PseudoRandom& getItem();
// LuaPseudoRandom(seed) // LuaPseudoRandom(seed)
// Creates an LuaPseudoRandom and leaves it on top of stack // Creates an LuaPseudoRandom and leaves it on top of stack
static int create_object(lua_State *L); static int create_object(lua_State *L);
static LuaPseudoRandom* checkobject(lua_State *L, int narg); static LuaPseudoRandom *checkobject(lua_State *L, int narg);
static void Register(lua_State *L);
};
/*
LuaPcgRandom
*/
class LuaPcgRandom : public ModApiBase {
private:
PcgRandom m_rnd;
static const char className[];
static const luaL_reg methods[];
// Exported functions
// garbage collector
static int gc_object(lua_State *L);
// next(self, min=-2147483648, max=2147483647) -> get next value
static int l_next(lua_State *L);
// rand_normal_dist(self, min=-2147483648, max=2147483647, num_trials=6) ->
// get next normally distributed random value
static int l_rand_normal_dist(lua_State *L);
public:
LuaPcgRandom(u64 seed) :
m_rnd(seed) {}
LuaPcgRandom(u64 seed, u64 seq) :
m_rnd(seed, seq) {}
// LuaPcgRandom(seed)
// Creates an LuaPcgRandom and leaves it on top of stack
static int create_object(lua_State *L);
static LuaPcgRandom *checkobject(lua_State *L, int narg);
static void Register(lua_State *L); static void Register(lua_State *L);
}; };

View File

@ -92,6 +92,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top)
LuaPerlinNoise::Register(L); LuaPerlinNoise::Register(L);
LuaPerlinNoiseMap::Register(L); LuaPerlinNoiseMap::Register(L);
LuaPseudoRandom::Register(L); LuaPseudoRandom::Register(L);
LuaPcgRandom::Register(L);
LuaVoxelManip::Register(L); LuaVoxelManip::Register(L);
NodeMetaRef::Register(L); NodeMetaRef::Register(L);
NodeTimerRef::Register(L); NodeTimerRef::Register(L);

View File

@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h" #include "log.h"
#include "../constants.h" // BS, MAP_BLOCKSIZE #include "../constants.h" // BS, MAP_BLOCKSIZE
#include "../noise.h" // PseudoRandom, PcgRandom
#include <string.h> #include <string.h>
#include <iostream> #include <iostream>
@ -115,36 +116,32 @@ void FacePositionCache::generateFacePosition(u16 d)
myrand myrand
*/ */
static unsigned long next = 1; PcgRandom g_pcgrand;
/* RAND_MAX assumed to be 32767 */ u32 myrand()
int myrand(void)
{ {
next = next * 1103515245 + 12345; return g_pcgrand.next();
return((unsigned)(next/65536) % 32768);
} }
void mysrand(unsigned seed) void mysrand(unsigned int seed)
{ {
next = seed; g_pcgrand.seed(seed);
}
void myrand_bytes(void *out, size_t len)
{
g_pcgrand.bytes(out, len);
} }
int myrand_range(int min, int max) int myrand_range(int min, int max)
{ {
if(max-min > MYRAND_MAX) return g_pcgrand.range(min, max);
{
errorstream<<"WARNING: myrand_range: max-min > MYRAND_MAX"<<std::endl;
max = min + MYRAND_MAX;
}
if(min > max)
{
errorstream<<"WARNING: myrand_range: min > max"<<std::endl;
return max;
}
return (myrand()%(max-min+1))+min;
} }
// 64-bit unaligned version of MurmurHash
/*
64-bit unaligned version of MurmurHash
*/
u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed) u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
{ {
const u64 m = 0xc6a4a7935bd1e995ULL; const u64 m = 0xc6a4a7935bd1e995ULL;
@ -159,12 +156,12 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
memcpy(&k, data, sizeof(u64)); memcpy(&k, data, sizeof(u64));
data++; data++;
k *= m; k *= m;
k ^= k >> r; k ^= k >> r;
k *= m; k *= m;
h ^= k; h ^= k;
h *= m; h *= m;
} }
const unsigned char *data2 = (const unsigned char *)data; const unsigned char *data2 = (const unsigned char *)data;
@ -178,13 +175,13 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
case 1: h ^= (u64)data2[0]; case 1: h ^= (u64)data2[0];
h *= m; h *= m;
} }
h ^= h >> r; h ^= h >> r;
h *= m; h *= m;
h ^= h >> r; h ^= h >> r;
return h; return h;
} }
/* /*
@ -197,7 +194,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
f32 camera_fov, f32 range, f32 *distance_ptr) f32 camera_fov, f32 range, f32 *distance_ptr)
{ {
v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE; v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE;
// Block center position // Block center position
v3f blockpos( v3f blockpos(
((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS, ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
@ -213,7 +210,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
if(distance_ptr) if(distance_ptr)
*distance_ptr = d; *distance_ptr = d;
// If block is far away, it's not in sight // If block is far away, it's not in sight
if(d > range) if(d > range)
return false; return false;
@ -221,7 +218,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
// Maximum radius of a block. The magic number is // Maximum radius of a block. The magic number is
// sqrt(3.0) / 2.0 in literal form. // sqrt(3.0) / 2.0 in literal form.
f32 block_max_radius = 0.866025403784 * MAP_BLOCKSIZE * BS; f32 block_max_radius = 0.866025403784 * MAP_BLOCKSIZE * BS;
// If block is (nearly) touching the camera, don't // If block is (nearly) touching the camera, don't
// bother validating further (that is, render it anyway) // bother validating further (that is, render it anyway)
if(d < block_max_radius) if(d < block_max_radius)
@ -242,7 +239,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
// Cosine of the angle between the camera direction // Cosine of the angle between the camera direction
// and the block direction (camera_dir is an unit vector) // and the block direction (camera_dir is an unit vector)
f32 cosangle = dforward / blockpos_adj.getLength(); f32 cosangle = dforward / blockpos_adj.getLength();
// If block is not in the field of view, skip it // If block is not in the field of view, skip it
if(cosangle < cos(camera_fov / 2)) if(cosangle < cos(camera_fov / 2))
return false; return false;

View File

@ -239,10 +239,10 @@ inline float wrapDegrees_180(float f)
/* /*
Pseudo-random (VC++ rand() sucks) Pseudo-random (VC++ rand() sucks)
*/ */
int myrand(void); #define MYRAND_RANGE 0xffffffff
void mysrand(unsigned seed); u32 myrand();
#define MYRAND_MAX 32767 void mysrand(unsigned int seed);
void myrand_bytes(void *out, size_t len);
int myrand_range(int min, int max); int myrand_range(int min, int max);
/* /*