// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
// Random numbers which also allow creation of random values in the shaders and are deterministic
// Based on this article:
// http://martindevans.me/game-development/2015/02/22/Random-Gibberish/
using System;
namespace math
{
///
/// The is a structure for deterministically acquiring random values.
/// One should be able to reproduce the same pseudo-random value for a fixed offset, but
/// provide enough random distribution for different offsets or different random seeds
/// Although other methods exist, the current implementation can easily be replicated in the shaders if required
///
public struct RandomSeed
{
private const double GelfondConst = 23.1406926327792690; // e to the power of Pi = (-1) to the power of -i
private const double GelfondSchneiderConst = 2.6651441426902251; // 2 to the power of sqrt(2)
private const double Numerator = 123456789;
// When casting uint to double it works fine, but when casting it to float it might cause underflow errors (loss of precision)
// We want to limit the maximum settable value to prevent such errors.
private const uint UnderflowGuard = 0xFFFF;
private readonly uint seed;
///
/// Initializes a new instance of the struct from a target uint.
///
/// The seed value to initialize the deterministic random generator.
public RandomSeed(uint seed)
{
this.seed = (seed & UnderflowGuard);
}
///
/// Get a deterministic double value between 0 and 1 based on the seed
///
/// Deterministic pseudo-random value between 0 and 1
public double GetDouble(uint offset)
{
var dRand = (double)(unchecked(seed + offset)); // We want it to overflow
var dotProduct = Math.Cos(dRand) * GelfondConst + Math.Sin(dRand) * GelfondSchneiderConst;
var denominator = 1e-7 + 256 * dotProduct;
var remainder = Numerator % denominator;
return (remainder - Math.Floor(remainder));
}
///
/// Get a deterministic float value between 0 and 1 based on the seed
/// The calculations are still made as doubles to prevent underflow errors.
///
/// Deterministic pseudo-random value between 0 and 1
public float GetFloat(uint offset) => (float)GetDouble(offset);
}
}