Add support for the PCG32 PRNG algo (and associated script APIs)
This commit is contained in:
parent
7679396ebb
commit
3993093f51
|
@ -2515,7 +2515,8 @@ an itemstring, a table or `nil`.
|
|||
Returns taken `ItemStack`.
|
||||
|
||||
### `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)`.
|
||||
|
||||
|
@ -2525,6 +2526,19 @@ It can be created via `PseudoRandom(seed)`.
|
|||
* `((max - min) == 32767) or ((max-min) <= 6553))` must be true
|
||||
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`
|
||||
A perlin noise generator.
|
||||
It can be created via `PerlinNoise(seed, octaves, persistence, scale)`
|
||||
|
|
|
@ -515,14 +515,10 @@ void MapgenParams::load(const Settings &settings)
|
|||
std::string seed_str;
|
||||
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());
|
||||
} else {
|
||||
seed = ((u64)(myrand() & 0xFFFF) << 0) |
|
||||
((u64)(myrand() & 0xFFFF) << 16) |
|
||||
((u64)(myrand() & 0xFFFF) << 32) |
|
||||
((u64)(myrand() & 0xFFFF) << 48);
|
||||
}
|
||||
else
|
||||
myrand_bytes(&seed, sizeof(seed));
|
||||
|
||||
settings.getNoEx("mg_name", mg_name);
|
||||
settings.getS16NoEx("water_level", water_level);
|
||||
|
|
|
@ -308,7 +308,7 @@ void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
|
|||
}
|
||||
|
||||
// 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 noiseval2 = contour(noise2->result[index]);
|
||||
if (noiseval * noiseval2 + randval * random_factor < nthresh)
|
||||
|
|
101
src/noise.cpp
101
src/noise.cpp
|
@ -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)
|
||||
{
|
||||
|
|
71
src/noise.h
71
src/noise.h
|
@ -30,47 +30,67 @@
|
|||
#include "irr_v3d.h"
|
||||
#include "util/string.h"
|
||||
|
||||
#define PSEUDORANDOM_MAX 32767
|
||||
|
||||
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:
|
||||
PseudoRandom(): m_next(0)
|
||||
const static u32 RANDOM_RANGE = 32767;
|
||||
|
||||
inline PseudoRandom(int seed=0):
|
||||
m_next(seed)
|
||||
{
|
||||
}
|
||||
PseudoRandom(int seed): m_next(seed)
|
||||
{
|
||||
}
|
||||
void seed(int seed)
|
||||
|
||||
inline void seed(int seed)
|
||||
{
|
||||
m_next = seed;
|
||||
}
|
||||
// Returns 0...PSEUDORANDOM_MAX
|
||||
int next()
|
||||
|
||||
inline int next()
|
||||
{
|
||||
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)
|
||||
{
|
||||
//dstream<<"WARNING: PseudoRandom::range: max > 32767"<<std::endl;
|
||||
assert("Something wrong with random number" == NULL);
|
||||
}
|
||||
if(min > max)
|
||||
{
|
||||
assert("Something wrong with random number" == NULL);
|
||||
//return max;
|
||||
}
|
||||
return (next()%(max-min+1))+min;
|
||||
assert(max >= min);
|
||||
/*
|
||||
Here, we ensure the range is not too large relative to RANDOM_MAX,
|
||||
as otherwise the effects of bias would become noticable. Unlike
|
||||
PcgRandom, we cannot modify this RNG's range as it would change the
|
||||
output of this RNG for reverse compatibility.
|
||||
*/
|
||||
assert((u32)(max - min) <= (RANDOM_RANGE + 1) / 10);
|
||||
|
||||
return (next() % (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
private:
|
||||
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_EASED 0x02
|
||||
#define NOISE_FLAG_ABSVALUE 0x04
|
||||
|
@ -89,7 +109,8 @@ struct NoiseParams {
|
|||
float lacunarity;
|
||||
u32 flags;
|
||||
|
||||
NoiseParams() {
|
||||
NoiseParams()
|
||||
{
|
||||
offset = 0.0f;
|
||||
scale = 1.0f;
|
||||
spread = v3f(250, 250, 250);
|
||||
|
|
|
@ -23,12 +23,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "common/c_content.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)
|
||||
{
|
||||
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;
|
||||
luaL_checktype(L, narg, LUA_TUSERDATA);
|
||||
void *ud = luaL_checkudata(L, narg, className);
|
||||
if (!ud)
|
||||
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_pushvalue(L, methodtable);
|
||||
lua_settable(L, metatable); // hide metatable from Lua getmetatable()
|
||||
lua_settable(L, metatable);
|
||||
|
||||
lua_pushliteral(L, "__index");
|
||||
lua_pushvalue(L, methodtable);
|
||||
|
@ -121,12 +123,11 @@ void LuaPerlinNoise::Register(lua_State *L)
|
|||
lua_pushcfunction(L, gc_object);
|
||||
lua_settable(L, metatable);
|
||||
|
||||
lua_pop(L, 1); // drop metatable
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_openlib(L, 0, methods, 0); // fill methodtable
|
||||
lua_pop(L, 1); // drop methodtable
|
||||
luaL_openlib(L, 0, methods, 0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Can be created from Lua (PerlinNoise(seed, octaves, persistence)
|
||||
lua_register(L, className, create_object);
|
||||
}
|
||||
|
||||
|
@ -138,16 +139,26 @@ const luaL_reg LuaPerlinNoise::methods[] = {
|
|||
{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));
|
||||
delete o;
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
luaL_checktype(L, narg, LUA_TUSERDATA);
|
||||
|
@ -294,7 +293,7 @@ LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg)
|
|||
if (!ud)
|
||||
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_pushvalue(L, methodtable);
|
||||
lua_settable(L, metatable); // hide metatable from Lua getmetatable()
|
||||
lua_settable(L, metatable);
|
||||
|
||||
lua_pushliteral(L, "__index");
|
||||
lua_pushvalue(L, methodtable);
|
||||
|
@ -317,12 +316,11 @@ void LuaPerlinNoiseMap::Register(lua_State *L)
|
|||
lua_pushcfunction(L, gc_object);
|
||||
lua_settable(L, metatable);
|
||||
|
||||
lua_pop(L, 1); // drop metatable
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_openlib(L, 0, methods, 0); // fill methodtable
|
||||
lua_pop(L, 1); // drop methodtable
|
||||
luaL_openlib(L, 0, methods, 0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Can be created from Lua (PerlinNoiseMap(np, size)
|
||||
lua_register(L, className, create_object);
|
||||
}
|
||||
|
||||
|
@ -336,32 +334,23 @@ const luaL_reg LuaPerlinNoiseMap::methods[] = {
|
|||
{0,0}
|
||||
};
|
||||
|
||||
///////////////////////////////////////
|
||||
/*
|
||||
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)
|
||||
{
|
||||
NO_MAP_LOCK_REQUIRED;
|
||||
LuaPseudoRandom *o = checkobject(L, 1);
|
||||
int min = 0;
|
||||
int max = 32767;
|
||||
lua_settop(L, 3); // Fill 2 and 3 with nil if they don't exist
|
||||
if(!lua_isnil(L, 2))
|
||||
lua_settop(L, 3);
|
||||
if (lua_isnumber(L, 2))
|
||||
min = luaL_checkinteger(L, 2);
|
||||
if(!lua_isnil(L, 3))
|
||||
if (lua_isnumber(L, 3))
|
||||
max = luaL_checkinteger(L, 3);
|
||||
if(max < min){
|
||||
if (max < min) {
|
||||
errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl;
|
||||
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 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);
|
||||
void *ud = luaL_checkudata(L, narg, className);
|
||||
if (!ud)
|
||||
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_pushvalue(L, methodtable);
|
||||
lua_settable(L, metatable); // hide metatable from Lua getmetatable()
|
||||
lua_settable(L, metatable);
|
||||
|
||||
lua_pushliteral(L, "__index");
|
||||
lua_pushvalue(L, methodtable);
|
||||
|
@ -442,12 +415,11 @@ void LuaPseudoRandom::Register(lua_State *L)
|
|||
lua_pushcfunction(L, gc_object);
|
||||
lua_settable(L, metatable);
|
||||
|
||||
lua_pop(L, 1); // drop metatable
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_openlib(L, 0, methods, 0); // fill methodtable
|
||||
lua_pop(L, 1); // drop methodtable
|
||||
luaL_openlib(L, 0, methods, 0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Can be created from Lua (LuaPseudoRandom(seed))
|
||||
lua_register(L, className, create_object);
|
||||
}
|
||||
|
||||
|
@ -457,3 +429,101 @@ const luaL_reg LuaPseudoRandom::methods[] = {
|
|||
luamethod(LuaPseudoRandom, next),
|
||||
{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}
|
||||
};
|
||||
|
|
|
@ -64,6 +64,9 @@ class LuaPerlinNoiseMap : public ModApiBase {
|
|||
static const char className[];
|
||||
static const luaL_reg methods[];
|
||||
|
||||
// Exported functions
|
||||
|
||||
// garbage collector
|
||||
static int gc_object(lua_State *L);
|
||||
|
||||
static int l_get2dMap(lua_State *L);
|
||||
|
@ -104,18 +107,51 @@ private:
|
|||
static int l_next(lua_State *L);
|
||||
|
||||
public:
|
||||
LuaPseudoRandom(int seed);
|
||||
|
||||
~LuaPseudoRandom();
|
||||
|
||||
const PseudoRandom& getItem() const;
|
||||
PseudoRandom& getItem();
|
||||
LuaPseudoRandom(int seed) :
|
||||
m_pseudo(seed) {}
|
||||
|
||||
// LuaPseudoRandom(seed)
|
||||
// Creates an LuaPseudoRandom and leaves it on top of stack
|
||||
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);
|
||||
};
|
||||
|
|
|
@ -92,6 +92,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top)
|
|||
LuaPerlinNoise::Register(L);
|
||||
LuaPerlinNoiseMap::Register(L);
|
||||
LuaPseudoRandom::Register(L);
|
||||
LuaPcgRandom::Register(L);
|
||||
LuaVoxelManip::Register(L);
|
||||
NodeMetaRef::Register(L);
|
||||
NodeTimerRef::Register(L);
|
||||
|
|
|
@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
#include "log.h"
|
||||
#include "../constants.h" // BS, MAP_BLOCKSIZE
|
||||
#include "../noise.h" // PseudoRandom, PcgRandom
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
|
||||
|
@ -115,36 +116,32 @@ void FacePositionCache::generateFacePosition(u16 d)
|
|||
myrand
|
||||
*/
|
||||
|
||||
static unsigned long next = 1;
|
||||
PcgRandom g_pcgrand;
|
||||
|
||||
/* RAND_MAX assumed to be 32767 */
|
||||
int myrand(void)
|
||||
u32 myrand()
|
||||
{
|
||||
next = next * 1103515245 + 12345;
|
||||
return((unsigned)(next/65536) % 32768);
|
||||
return g_pcgrand.next();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if(max-min > MYRAND_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;
|
||||
return g_pcgrand.range(min, max);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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));
|
||||
data++;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
h *= m;
|
||||
}
|
||||
|
||||
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];
|
||||
h *= m;
|
||||
}
|
||||
|
||||
|
||||
h ^= h >> r;
|
||||
h *= m;
|
||||
h ^= h >> r;
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE;
|
||||
|
||||
|
||||
// Block center position
|
||||
v3f blockpos(
|
||||
((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)
|
||||
*distance_ptr = d;
|
||||
|
||||
|
||||
// If block is far away, it's not in sight
|
||||
if(d > range)
|
||||
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
|
||||
// sqrt(3.0) / 2.0 in literal form.
|
||||
f32 block_max_radius = 0.866025403784 * MAP_BLOCKSIZE * BS;
|
||||
|
||||
|
||||
// If block is (nearly) touching the camera, don't
|
||||
// bother validating further (that is, render it anyway)
|
||||
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
|
||||
// and the block direction (camera_dir is an unit vector)
|
||||
f32 cosangle = dforward / blockpos_adj.getLength();
|
||||
|
||||
|
||||
// If block is not in the field of view, skip it
|
||||
if(cosangle < cos(camera_fov / 2))
|
||||
return false;
|
||||
|
|
|
@ -239,10 +239,10 @@ inline float wrapDegrees_180(float f)
|
|||
/*
|
||||
Pseudo-random (VC++ rand() sucks)
|
||||
*/
|
||||
int myrand(void);
|
||||
void mysrand(unsigned seed);
|
||||
#define MYRAND_MAX 32767
|
||||
|
||||
#define MYRAND_RANGE 0xffffffff
|
||||
u32 myrand();
|
||||
void mysrand(unsigned int seed);
|
||||
void myrand_bytes(void *out, size_t len);
|
||||
int myrand_range(int min, int max);
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue