Created
April 2, 2023 07:08
-
-
Save andanteyk/11967dd313c07136f542ba64b80411bb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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