Skip to content

Instantly share code, notes, and snippets.

@andanteyk
Created April 2, 2023 07:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andanteyk/11967dd313c07136f542ba64b80411bb to your computer and use it in GitHub Desktop.
Save andanteyk/11967dd313c07136f542ba64b80411bb to your computer and use it in GitHub Desktop.
using System.Runtime.InteropServices;
using System.Security.Cryptography;
#if UNITY_2019_1_OR_NEWER
// in unity environment
using UnityEngine;
using System.Reflection;
#else
// in dotnet environment
using System.Numerics;
#endif
namespace RngLab.Rng
{
#if !UNITY_2019_1_OR_NEWER
// in dotnet environment
// placeholder
public record Color(float r, float g, float b, float a);
#endif
public class UnityRandom
{
private uint x, y, z, w;
public UnityRandom()
{
var buffer = (stackalloc uint[4]);
do
{
RandomNumberGenerator.Fill(MemoryMarshal.AsBytes(buffer));
} while (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0 && buffer[3] == 0);
x = buffer[0];
y = buffer[1];
z = buffer[2];
w = buffer[3];
}
public UnityRandom(uint x, uint y, uint z, uint w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
public uint Next()
{
uint t = x ^ x << 11;
x = y;
y = z;
z = w;
return w = w ^ w >> 19 ^ t ^ t >> 8;
}
public void Prev()
{
w ^= z ^ z >> 19; // t ^ t >> 8
w ^= w >> 8; w ^= w >> 16;
w ^= w << 11; w ^= w << 22; // w = x
uint tx = w;
w = z;
z = y;
y = x;
x = tx;
}
public void InitState(int seed)
{
x = (uint)seed;
y = x * 1812433253 + 1;
z = y * 1812433253 + 1;
w = z * 1812433253 + 1;
}
public float value => (Next() & 0x7fffffu) / 8388607.0f;
public int Range(int min, int max) => min + (int)(Next() % (uint)(max - min));
public float Range(float min, float max)
{
float t = value;
return t * min + (1 - t) * max;
}
public Vector2 insideUnitCircle
{
get
{
var angle = Range(0f, MathF.PI * 2);
var radius = (float)Math.Sqrt(Range(0f, 1f));
return new Vector2(radius * (float)Math.Cos(angle), radius * (float)Math.Sin(angle));
}
}
public Vector3 insideUnitSphere
{
get
{
var p = MathF.Asin(Range(-1f, 1f));
var t = Range(0, MathF.PI * 2);
var r = MathF.Cbrt(value);
return new Vector3(r * MathF.Cos(p) * MathF.Cos(t), r * MathF.Cos(p) * MathF.Sin(t), r * MathF.Sin(p));
}
}
public Vector3 onUnitSphere
{
get
{
var p = MathF.Asin(Range(-1f, 1f));
var t = Range(0, MathF.PI * 2);
return new Vector3(MathF.Cos(p) * MathF.Cos(t), MathF.Cos(p) * MathF.Sin(t), MathF.Sin(p));
}
}
public Quaternion rotation
{
get
{
float x = Range(-1f, 1f);
float y = Range(-1f, 1f);
float z = Range(-1f, 1f);
float w = Range(-1f, 1f);
var reciprocalLength = 1 / MathF.Sqrt(x * x + y * y + z * z + w * w);
reciprocalLength *= w < 0 ? -1 : 1;
return new Quaternion(x * reciprocalLength, y * reciprocalLength, z * reciprocalLength, w * reciprocalLength);
}
}
public Quaternion rotationUniform
{
get
{
float a = Range(0f, 1f);
float b = Range(0f, MathF.PI * 2);
float c = Range(0f, MathF.PI * 2);
float x = MathF.Sin(b) * MathF.Sqrt(1 - a);
float y = MathF.Cos(b) * MathF.Sqrt(1 - a);
float z = MathF.Sin(c) * MathF.Sqrt(a);
float w = MathF.Cos(c) * MathF.Sqrt(a);
float signOfW = w < 0 ? -1f : 1f;
return new Quaternion(x * signOfW, y * signOfW, z * signOfW, w * signOfW);
}
}
public Color ColorHsv(float hueMin, float hueMax, float saturationMin, float saturationMax, float valueMin, float valueMax, float alphaMin, float alphaMax)
{
// note: (max, min). maybe originally `min * (1 - value) + max * value`
float h = Range(hueMax, hueMin);
float s = Range(saturationMax, saturationMin);
float v = Range(valueMax, valueMin);
float a = Range(alphaMax, alphaMin);
Color HsvToRgb(float h, float s, float v, float a)
{
float hStar = h * 6;
float c = v * s;
float x = c * (1 - Math.Abs(hStar % 2f - 1f));
return hStar switch
{
>= 0 and
< 1 => new Color((v - c) + c, (v - c) + x, (v - c) + 0, a),
< 2 => new Color((v - c) + x, (v - c) + c, (v - c) + 0, a),
< 3 => new Color((v - c) + 0, (v - c) + c, (v - c) + x, a),
< 4 => new Color((v - c) + 0, (v - c) + x, (v - c) + c, a),
< 5 => new Color((v - c) + x, (v - c) + 0, (v - c) + c, a),
< 6 => new Color((v - c) + c, (v - c) + 0, (v - c) + x, a),
_ => throw new ArgumentException()
};
}
return HsvToRgb(h, s, v, a);
}
#if UNITY_2019_1_OR_NEWER
public static uint[] GetState(UnityEngine.Random.State state)
{
var flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField;
var values = new uint[4];
values[0] = (uint)state.GetType().GetField("s0", flags).GetValue(state);
values[1] = (uint)state.GetType().GetField("s1", flags).GetValue(state);
values[2] = (uint)state.GetType().GetField("s2", flags).GetValue(state);
values[3] = (uint)state.GetType().GetField("s3", flags).GetValue(state);
return values;
}
public static UnityEngine.Random.State SetState(uint[] values)
{
object state = new UnityEngine.Random.State();
var flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetField;
state.GetType().GetField("s0", flags).SetValue(state, (int)values[0]);
state.GetType().GetField("s1", flags).SetValue(state, (int)values[1]);
state.GetType().GetField("s2", flags).SetValue(state, (int)values[2]);
state.GetType().GetField("s3", flags).SetValue(state, (int)values[3]);
return (UnityEngine.Random.State)state;
}
public static uint[] HackState(Span<uint> values)
{
if (values.Length < 4)
throw new ArgumentException();
var emu = new UnityRandom() { x = values[0], y = values[1], z = values[2], w = values[3] };
emu.Prev();
emu.Prev();
emu.Prev();
emu.Prev();
return new uint[] { emu.x, emu.y, emu.z, emu.w };
}
#endif
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment