diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index 21c4e1629..a1cb8ec98 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -55,6 +55,10 @@ uint32_t Math::rand() { return default_rand.rand(); } +double Math::randfn(double mean, double deviation) { + return default_rand.randfn(mean, deviation); +} + int Math::step_decimals(double p_step) { static const int maxn = 10; static const double sd[maxn] = { @@ -184,3 +188,11 @@ double Math::random(double from, double to) { float Math::random(float from, float to) { return default_rand.random(from, to); } + +real_t Math::randomr(real_t from, real_t to) { + return default_rand.randomr(from, to); +} + +int Math::random(int from, int to) { + return default_rand.random(from, to); +} diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index ea1179419..f545f137e 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -104,6 +104,12 @@ public: static _ALWAYS_INLINE_ double log(double p_x) { return ::log(p_x); } static _ALWAYS_INLINE_ float log(float p_x) { return ::logf(p_x); } + static _ALWAYS_INLINE_ double log1p(double p_x) { return ::log1p(p_x); } + static _ALWAYS_INLINE_ float log1p(float p_x) { return ::log1pf(p_x); } + + static _ALWAYS_INLINE_ double log2(double p_x) { return ::log2(p_x); } + static _ALWAYS_INLINE_ float log2(float p_x) { return ::log2f(p_x); } + static _ALWAYS_INLINE_ double exp(double p_x) { return ::exp(p_x); } static _ALWAYS_INLINE_ float exp(float p_x) { return ::expf(p_x); } @@ -211,6 +217,24 @@ public: value += 0.0f; return value; } + + static _ALWAYS_INLINE_ float fposmodp(float p_x, float p_y) { + float value = Math::fmod(p_x, p_y); + if (value < 0) { + value += p_y; + } + value += 0.0f; + return value; + } + static _ALWAYS_INLINE_ double fposmodp(double p_x, double p_y) { + double value = Math::fmod(p_x, p_y); + if (value < 0) { + value += p_y; + } + value += 0.0; + return value; + } + static _ALWAYS_INLINE_ int64_t posmod(int64_t p_x, int64_t p_y) { int64_t value = p_x % p_y; if (((value < 0) && (p_y > 0)) || ((value > 0) && (p_y < 0))) { @@ -265,6 +289,42 @@ public: return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); } + static _ALWAYS_INLINE_ double cubic_interpolate(double p_from, double p_to, double p_pre, double p_post, double p_weight) { + return 0.5 * + ((p_from * 2.0) + + (-p_pre + p_to) * p_weight + + (2.0 * p_pre - 5.0 * p_from + 4.0 * p_to - p_post) * (p_weight * p_weight) + + (-p_pre + 3.0 * p_from - 3.0 * p_to + p_post) * (p_weight * p_weight * p_weight)); + } + static _ALWAYS_INLINE_ float cubic_interpolate(float p_from, float p_to, float p_pre, float p_post, float p_weight) { + return 0.5f * + ((p_from * 2.0f) + + (-p_pre + p_to) * p_weight + + (2.0f * p_pre - 5.0f * p_from + 4.0f * p_to - p_post) * (p_weight * p_weight) + + (-p_pre + 3.0f * p_from - 3.0f * p_to + p_post) * (p_weight * p_weight * p_weight)); + } + + static _ALWAYS_INLINE_ double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { + /* Formula from Wikipedia article on Bezier curves. */ + double omt = (1.0 - p_t); + double omt2 = omt * omt; + double omt3 = omt2 * omt; + double t2 = p_t * p_t; + double t3 = t2 * p_t; + + return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3; + } + static _ALWAYS_INLINE_ float bezier_interpolate(float p_start, float p_control_1, float p_control_2, float p_end, float p_t) { + /* Formula from Wikipedia article on Bezier curves. */ + float omt = (1.0f - p_t); + float omt2 = omt * omt; + float omt3 = omt2 * omt; + float t2 = p_t * p_t; + float t3 = t2 * p_t; + + return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0f + p_control_2 * omt * t2 * 3.0f + p_end * t3; + } + static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_s) { if (is_equal_approx(p_from, p_to)) { return p_from; @@ -328,6 +388,19 @@ public: return result; } + static _ALWAYS_INLINE_ float fract(float value) { + return value - floor(value); + } + static _ALWAYS_INLINE_ double fract(double value) { + return value - floor(value); + } + static _ALWAYS_INLINE_ float pingpong(float value, float length) { + return (length != 0.0f) ? abs(fract((value - length) / (length * 2.0f)) * length * 2.0f - length) : 0.0f; + } + static _ALWAYS_INLINE_ double pingpong(double value, double length) { + return (length != 0.0) ? abs(fract((value - length) / (length * 2.0)) * length * 2.0 - length) : 0.0; + } + // double only, as these functions are mainly used by the editor and not performance-critical, static double ease(double p_x, double p_c); static int step_decimals(double p_step); @@ -347,12 +420,12 @@ public: static _ALWAYS_INLINE_ float randf() { return (float)rand() / (float)Math::RANDOM_32BIT_MAX; } + static double randfn(double mean, double deviation); static double random(double from, double to); static float random(float from, float to); - static real_t random(int from, int to) { - return (real_t)random((real_t)from, (real_t)to); - } + static real_t randomr(real_t from, real_t to); + static int random(int from, int to); static _ALWAYS_INLINE_ bool is_equal_approx_ratio(real_t a, real_t b, real_t epsilon = CMP_EPSILON, real_t min_epsilon = CMP_EPSILON) { // this is an approximate way to check that numbers are close, as a ratio of their average size diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h index aa673e6aa..2e200f704 100644 --- a/core/math/random_number_generator.h +++ b/core/math/random_number_generator.h @@ -43,30 +43,20 @@ protected: public: _FORCE_INLINE_ void set_seed(uint64_t seed) { randbase.seed(seed); } - _FORCE_INLINE_ uint64_t get_seed() { return randbase.get_seed(); } _FORCE_INLINE_ void set_state(uint64_t p_state) { randbase.set_state(p_state); } - _FORCE_INLINE_ uint64_t get_state() const { return randbase.get_state(); } _FORCE_INLINE_ void randomize() { randbase.randomize(); } _FORCE_INLINE_ uint32_t randi() { return randbase.rand(); } - _FORCE_INLINE_ real_t randf() { return randbase.randf(); } - _FORCE_INLINE_ real_t randf_range(real_t from, real_t to) { return randbase.random(from, to); } - _FORCE_INLINE_ real_t randfn(real_t mean = 0.0, real_t deviation = 1.0) { return randbase.randfn(mean, deviation); } _FORCE_INLINE_ int randi_range(int from, int to) { - unsigned int ret = randbase.rand(); - if (to < from) { - return ret % (from - to + 1) + to; - } else { - return ret % (to - from + 1) + from; - } + return randbase.random(from, to); } RandomNumberGenerator(); diff --git a/core/math/random_pcg.cpp b/core/math/random_pcg.cpp index 2a8c0dcac..dede02433 100644 --- a/core/math/random_pcg.cpp +++ b/core/math/random_pcg.cpp @@ -49,3 +49,10 @@ double RandomPCG::random(double p_from, double p_to) { float RandomPCG::random(float p_from, float p_to) { return randf() * (p_to - p_from) + p_from; } + +int RandomPCG::random(int p_from, int p_to) { + if (p_from == p_to) { + return p_from; + } + return rand(abs(p_from - p_to) + 1) + MIN(p_from, p_to); +} diff --git a/core/math/random_pcg.h b/core/math/random_pcg.h index d741183b9..b579deb9f 100644 --- a/core/math/random_pcg.h +++ b/core/math/random_pcg.h @@ -80,9 +80,11 @@ public: void randomize(); _FORCE_INLINE_ uint32_t rand() { - current_seed = pcg.state; return pcg32_random_r(&pcg); } + _FORCE_INLINE_ uint32_t rand(uint32_t bounds) { + return pcg32_boundedrand_r(&pcg, bounds); + } // Obtaining floating point numbers in [0, 1] range with "good enough" uniformity. // These functions sample the output of rand() as the fraction part of an infinite binary number, @@ -131,7 +133,8 @@ public: double random(double p_from, double p_to); float random(float p_from, float p_to); - real_t random(int p_from, int p_to) { return (real_t)random((real_t)p_from, (real_t)p_to); } + real_t randomr(real_t p_from, real_t p_to) { return random(p_from, p_to); } + int random(int p_from, int p_to); }; #endif // RANDOM_PCG_H diff --git a/thirdparty/misc/pcg.cpp b/thirdparty/misc/pcg.cpp index c421e16f8..914a35387 100644 --- a/thirdparty/misc/pcg.cpp +++ b/thirdparty/misc/pcg.cpp @@ -23,3 +23,36 @@ void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq) rng->state += initstate; pcg32_random_r(rng); } + +// Source from https://github.com/imneme/pcg-c-basic/blob/master/pcg_basic.c +// pcg32_boundedrand_r(rng, bound): +// Generate a uniformly distributed number, r, where 0 <= r < bound +uint32_t pcg32_boundedrand_r(pcg32_random_t *rng, uint32_t bound) { + // To avoid bias, we need to make the range of the RNG a multiple of + // bound, which we do by dropping output less than a threshold. + // A naive scheme to calculate the threshold would be to do + // + // uint32_t threshold = 0x100000000ull % bound; + // + // but 64-bit div/mod is slower than 32-bit div/mod (especially on + // 32-bit platforms). In essence, we do + // + // uint32_t threshold = (0x100000000ull-bound) % bound; + // + // because this version will calculate the same modulus, but the LHS + // value is less than 2^32. + uint32_t threshold = -bound % bound; + + // Uniformity guarantees that this loop will terminate. In practice, it + // should usually terminate quickly; on average (assuming all bounds are + // equally likely), 82.25% of the time, we can expect it to require just + // one iteration. In the worst case, someone passes a bound of 2^31 + 1 + // (i.e., 2147483649), which invalidates almost 50% of the range. In + // practice, bounds are typically small and only a tiny amount of the range + // is eliminated. + for (;;) { + uint32_t r = pcg32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} diff --git a/thirdparty/misc/pcg.h b/thirdparty/misc/pcg.h index 1e7421f67..0faab73e6 100644 --- a/thirdparty/misc/pcg.h +++ b/thirdparty/misc/pcg.h @@ -1,10 +1,8 @@ -#ifndef RANDOM_H -#define RANDOM_H // *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org // Licensed under Apache License 2.0 (NO WARRANTY, etc. see website) - - +#ifndef RANDOM_H +#define RANDOM_H #include "core/typedefs.h" @@ -13,5 +11,6 @@ typedef struct { uint64_t state; uint64_t inc; } pcg32_random_t; uint32_t pcg32_random_r(pcg32_random_t* rng); void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq); +uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound); #endif // RANDOM_H