From 8b34330079068495044a5d510cfbb169a0ae961d Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 26 Jul 2017 22:02:17 -0700 Subject: [PATCH] RandomGenerator; --- src/api/lovr.h | 3 ++ src/api/math.c | 14 +++++ src/api/types/randomGenerator.c | 95 +++++++++++++++++++++++++++++++++ src/math/randomGenerator.c | 92 +++++++++++++++++++++++++++++++ src/math/randomGenerator.h | 30 +++++++++++ 5 files changed, 234 insertions(+) create mode 100644 src/api/types/randomGenerator.c create mode 100644 src/math/randomGenerator.c create mode 100644 src/math/randomGenerator.h diff --git a/src/api/lovr.h b/src/api/lovr.h index d3ae67cd..9cc8e74a 100644 --- a/src/api/lovr.h +++ b/src/api/lovr.h @@ -2,6 +2,7 @@ #include "filesystem/blob.h" #include "graphics/mesh.h" #include "math/math.h" +#include "math/randomGenerator.h" #include "physics/physics.h" #include "lib/map/map.h" @@ -34,6 +35,7 @@ extern const luaL_Reg lovrMath[]; extern const luaL_Reg lovrMesh[]; extern const luaL_Reg lovrModel[]; extern const luaL_Reg lovrPhysics[]; +extern const luaL_Reg lovrRandomGenerator[]; extern const luaL_Reg lovrShader[]; extern const luaL_Reg lovrShape[]; extern const luaL_Reg lovrSkybox[]; @@ -71,3 +73,4 @@ int luax_readtransform(lua_State* L, int index, mat4 transform, int uniformScale Blob* luax_readblob(lua_State* L, int index, const char* debug); int luax_pushshape(lua_State* L, Shape* shape); int luax_pushjoint(lua_State* L, Joint* joint); +Seed luax_checkrandomseed(lua_State* L, int index); diff --git a/src/api/math.c b/src/api/math.c index 4f9b8765..a99335a6 100644 --- a/src/api/math.c +++ b/src/api/math.c @@ -1,15 +1,28 @@ #include "api/lovr.h" #include "math/mat4.h" #include "math/quat.h" +#include "math/randomGenerator.h" #include "math/transform.h" int l_lovrMathInit(lua_State* L) { lua_newtable(L); luaL_register(L, NULL, lovrMath); + luax_registertype(L, "RandomGenerator", lovrRandomGenerator); luax_registertype(L, "Transform", lovrTransform); return 1; } +int l_lovrMathNewRandomGenerator(lua_State* L) { + RandomGenerator* generator = lovrRandomGeneratorCreate(); + if (lua_gettop(L) > 0){ + Seed seed = luax_checkrandomseed(L, 1); + lovrRandomGeneratorSetSeed(generator, seed); + } + luax_pushtype(L, RandomGenerator, generator); + lovrRelease(&generator->ref); + return 1; +} + int l_lovrMathNewTransform(lua_State* L) { float matrix[16]; luax_readtransform(L, 1, matrix, 0); @@ -35,6 +48,7 @@ int l_lovrMathLookAt(lua_State* L) { } const luaL_Reg lovrMath[] = { + { "newRandomGenerator", l_lovrMathNewRandomGenerator }, { "newTransform", l_lovrMathNewTransform }, { "lookAt", l_lovrMathLookAt }, { NULL, NULL } diff --git a/src/api/types/randomGenerator.c b/src/api/types/randomGenerator.c new file mode 100644 index 00000000..1120b270 --- /dev/null +++ b/src/api/types/randomGenerator.c @@ -0,0 +1,95 @@ +#include "api/lovr.h" +#include "math/randomGenerator.h" + +static double luax_checkrandomseedpart(lua_State* L, int index) { + double x = luaL_checknumber(L, index); + + if (!isfinite(x)) { + luaL_argerror(L, index, "invalid random seed"); + } + + return x; +} + +Seed luax_checkrandomseed(lua_State* L, int index) { + Seed seed; + + if (lua_isnoneornil(L, index + 1)) { + seed.b64 = luax_checkrandomseedpart(L, index); + } else { + seed.b32.lo = luax_checkrandomseedpart(L, index); + seed.b32.hi = luax_checkrandomseedpart(L, index + 1); + } + + return seed; +} + +int l_lovrRandomGeneratorGetSeed(lua_State* L) { + RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); + Seed seed = lovrRandomGeneratorGetSeed(generator); + lua_pushnumber(L, seed.b32.lo); + lua_pushnumber(L, seed.b32.hi); + return 2; +} + +int l_lovrRandomGeneratorSetSeed(lua_State* L) { + RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); + Seed seed = luax_checkrandomseed(L, 2); + lovrRandomGeneratorSetSeed(generator, seed); + return 0; +} + +int l_lovrRandomGeneratorGetState(lua_State* L) { + RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); + size_t length = 32; + char state[32]; + lovrRandomGeneratorGetState(generator, state, length); + lua_pushstring(L, state); + return 1; +} + +int l_lovrRandomGeneratorSetState(lua_State* L) { + RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); + size_t length; + const char* state = luaL_checklstring(L, 2, &length); + if (lovrRandomGeneratorSetState(generator, state, length)) { + return luaL_error(L, "invalid random state %s", state); + } + return 0; +} + +int l_lovrRandomGeneratorRandom(lua_State* L) { + RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); + float r = lovrRandomGeneratorRandom(generator); + + if (lua_gettop(L) >= 3) { + float lower = luaL_checknumber(L, 2); + float upper = luaL_checknumber(L, 3); + lua_pushnumber(L, floor(r * (upper - lower + 1)) + lower); + } else if (lua_gettop(L) >= 2) { + float upper = luaL_checknumber(L, 2); + lua_pushnumber(L, floor(r * upper) + 1); + } else { + lua_pushnumber(L, r); + } + + return 1; +} + +int l_lovrRandomGeneratorRandomNormal(lua_State* L) { + RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); + float sigma = luaL_optnumber(L, 2, 1); + float mu = luaL_optnumber(L, 3, 0); + lua_pushnumber(L, mu + lovrRandomGeneratorRandomNormal(generator) * sigma); + return 1; +} + +const luaL_Reg lovrRandomGenerator[] = { + { "getSeed", l_lovrRandomGeneratorGetSeed }, + { "setSeed", l_lovrRandomGeneratorSetSeed }, + { "getState", l_lovrRandomGeneratorGetState }, + { "setState", l_lovrRandomGeneratorSetState }, + { "random", l_lovrRandomGeneratorRandom }, + { "randomNormal", l_lovrRandomGeneratorRandomNormal }, + { NULL, NULL } +}; diff --git a/src/math/randomGenerator.c b/src/math/randomGenerator.c new file mode 100644 index 00000000..9428ab92 --- /dev/null +++ b/src/math/randomGenerator.c @@ -0,0 +1,92 @@ +#include "math/randomGenerator.h" +#include +#include +#include + +// Thomas Wang's 64-bit integer hashing function: +// https://web.archive.org/web/20110807030012/http://www.cris.com/%7ETtwang/tech/inthash.htm +static uint64_t wangHash64(uint64_t key) { + key = (~key) + (key << 21); // key = (key << 21) - key - 1; + key = key ^ (key >> 24); + key = (key + (key << 3)) + (key << 8); // key * 265 + key = key ^ (key >> 14); + key = (key + (key << 2)) + (key << 4); // key * 21 + key = key ^ (key >> 28); + key = key + (key << 31); + return key; +} + +// 64 bit Xorshift implementation taken from the end of Sec. 3 (page 4) in +// George Marsaglia, "Xorshift RNGs", Journal of Statistical Software, Vol.8 (Issue 14), 2003 +// Use an 'Xorshift*' variant, as shown here: http://xorshift.di.unimi.it + +RandomGenerator* lovrRandomGeneratorCreate() { + RandomGenerator* generator = lovrAlloc(sizeof(RandomGenerator), lovrRandomGeneratorDestroy); + if (!generator) return NULL; + + Seed seed = { .b32 = { .lo = 0xCBBF7A44, .hi = 0x0139408D } }; + lovrRandomGeneratorSetSeed(generator, seed); + generator->lastRandomNormal = INFINITY; + + return generator; +} + +void lovrRandomGeneratorDestroy(const Ref* ref) { + RandomGenerator* generator = containerof(ref, RandomGenerator); + free(generator); +} + +Seed lovrRandomGeneratorGetSeed(RandomGenerator* generator) { + return generator->seed; +} + +void lovrRandomGeneratorSetSeed(RandomGenerator* generator, Seed seed) { + generator->seed = seed; + + do { + seed.b64 = wangHash64(seed.b64); + } while (seed.b64 == 0); + + generator->state = seed; +} + +void lovrRandomGeneratorGetState(RandomGenerator* generator, char* state, size_t length) { + snprintf(state, length, "0x%16llx", generator->state.b64); +} + +int lovrRandomGeneratorSetState(RandomGenerator* generator, const char* state, size_t length) { + char* end = NULL; + Seed newState; + newState.b64 = strtoull(state, &end, 16); + if (end != NULL && *end != 0) { + return 1; + } else { + generator->state = newState; + return 0; + } +} + +double lovrRandomGeneratorRandom(RandomGenerator* generator) { + generator->state.b64 ^= (generator->state.b64 >> 12); + generator->state.b64 ^= (generator->state.b64 << 25); + generator->state.b64 ^= (generator->state.b64 >> 27); + uint64_t r = generator->state.b64 * 2685821657736338717ULL; + union { uint64_t i; double d; } u; + u.i = ((0x3FFULL) << 52) | (r >> 12); + return u.d - 1.; +} + +double lovrRandomGeneratorRandomNormal(RandomGenerator* generator) { + if (generator->lastRandomNormal != INFINITY) { + double r = generator->lastRandomNormal; + generator->lastRandomNormal = INFINITY; + return r; + } + + double a = lovrRandomGeneratorRandom(generator); + double b = lovrRandomGeneratorRandom(generator); + double r = sqrt(-2. * log(1. - a)); + double phi = 2 * M_PI * (1. - b); + generator->lastRandomNormal = r * cos(phi); + return r * sin(phi); +} diff --git a/src/math/randomGenerator.h b/src/math/randomGenerator.h new file mode 100644 index 00000000..4acc765a --- /dev/null +++ b/src/math/randomGenerator.h @@ -0,0 +1,30 @@ +#include +#include "util.h" + +#pragma once + +// Direct port of LÖVE's RandomGenerator + +typedef union { + uint64_t b64; + struct { + uint32_t lo; + uint32_t hi; + } b32; +} Seed; + +typedef struct { + Ref ref; + Seed seed; + Seed state; + double lastRandomNormal; +} RandomGenerator; + +RandomGenerator* lovrRandomGeneratorCreate(); +void lovrRandomGeneratorDestroy(const Ref* ref); +Seed lovrRandomGeneratorGetSeed(RandomGenerator* generator); +void lovrRandomGeneratorSetSeed(RandomGenerator* generator, Seed seed); +void lovrRandomGeneratorGetState(RandomGenerator* generator, char* state, size_t length); +int lovrRandomGeneratorSetState(RandomGenerator* generator, const char* state, size_t length); +double lovrRandomGeneratorRandom(RandomGenerator* generator); +double lovrRandomGeneratorRandomNormal(RandomGenerator* generator);