1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#pragma once
4
5#include "Prerequisites/BsPrerequisitesUtil.h"
6#include "BsVector3.h"
7#include "BsVector2.h"
8
9namespace bs
10{
11 /** @addtogroup Math
12 * @{
13 */
14
15 /** Generates pseudo random numbers using the Xorshift128 algorithm. Suitable for high performance requirements. */
16 class BS_SCRIPT_EXPORT(m:Math) Random
17 {
18 public:
19 /** Initializes a new generator using the specified seed. */
20 BS_SCRIPT_EXPORT()
21 Random(uint32_t seed = 0)
22 {
23 setSeed(seed);
24 }
25
26 /** Changes the seed of the generator to the specified value. */
27 BS_SCRIPT_EXPORT()
28 void setSeed(uint32_t seed)
29 {
30 mSeed[0] = seed;
31 mSeed[1] = seed * 0x72e0447c + 1; // Arbitrary random numbers
32 mSeed[2] = seed * 0x352ad225 + 1;
33 mSeed[3] = seed * 0x03c3629f + 1;
34 }
35
36 /** Returns a random value in range [0, std::numeric_limits<uint32_t>::max()]. */
37 BS_SCRIPT_EXPORT()
38 uint32_t get() const
39 {
40 // Using xorshift128 algorithm
41 uint32_t t = mSeed[3];
42 t ^= t << 11;
43 t ^= t >> 8;
44
45 mSeed[3] = mSeed[2];
46 mSeed[2] = mSeed[1];
47 mSeed[1] = mSeed[0];
48
49 const uint32_t s = mSeed[0];
50 t ^= s;
51 t ^= s >> 19;
52
53 mSeed[0] = t;
54 return t;
55 }
56
57 /** Returns a random value in range [min, max]. */
58 BS_SCRIPT_EXPORT()
59 int32_t getRange(int32_t min, int32_t max) const
60 {
61 assert(max > min);
62
63 // Note: Not using modulo for performance
64 const int32_t range = max - min + 1;
65
66 constexpr static float DELTA = 0e-5f;
67 return min + (int32_t)(getUNorm() * ((float)range - DELTA));
68 }
69
70 /** Returns a random value in range [0, 1]. */
71 BS_SCRIPT_EXPORT()
72 float getUNorm() const
73 {
74 // Mask first 23 bits and divide by 2^23-1
75 return float(get() & 0x007FFFFF) / 8388607.0f;
76 }
77
78 /** Returns a random value in range [-1, 1]. */
79 BS_SCRIPT_EXPORT()
80 float getSNorm() const
81 {
82 return getUNorm() * 2.0f - 1.0f;
83 }
84
85 /** Returns a random unit vector in three dimensions. */
86 BS_SCRIPT_EXPORT()
87 Vector3 getUnitVector() const
88 {
89 // Pick a random number on a unit cube and use the result only if squared size less than 1. This is faster
90 // than most other methods, and generally not many iterations are required to get the required vector.
91
92 Vector3 output;
93 float sqrdSize;
94
95 do
96 {
97 output.x = getSNorm();
98 output.y = getSNorm();
99 output.z = getSNorm();
100 sqrdSize = output.squaredLength();
101
102 } while (sqrdSize > 1.0f || sqrdSize < 0.001f);
103
104 return Vector3::normalize(output);
105 }
106
107 /** Returns a random unit vector in two dimensions. */
108 BS_SCRIPT_EXPORT()
109 Vector2 getUnitVector2D() const
110 {
111 // Pick a random number on a unit square and use the result only if squared size less than 1. This is faster
112 // than most other methods, and generally not many iterations are required to get the required vector.
113
114 Vector2 output;
115 float sqrdSize;
116
117 do
118 {
119 output.x = getSNorm();
120 output.y = getSNorm();
121 sqrdSize = output.squaredLength();
122
123 } while (sqrdSize > 1.0f || sqrdSize < 0.001f);
124
125 return Vector2::normalize(output);
126 }
127
128 /** Returns a random point inside a unit sphere. */
129 BS_SCRIPT_EXPORT()
130 Vector3 getPointInSphere() const
131 {
132 const Vector3 dir = getUnitVector();
133 return dir * std::pow(getUNorm(), 1.0f / 3.0f);
134 }
135
136 /**
137 * Returns a random point inside the specified range in a sphere shell of unit radius, with the specified
138 * thickness, in range [0, 1]. Thickness of 0 will generate points on the sphere surface, while thickness of 1
139 * will generate points within the entire sphere volume. Intermediate values represent the shell, which is a volume
140 * between two concentric spheres.
141 */
142 BS_SCRIPT_EXPORT()
143 Vector3 getPointInSphereShell(float thickness) const
144 {
145 const float minRadius = 1.0f - thickness;
146
147 const Vector3 dir = getUnitVector();
148 return dir * (minRadius + thickness * std::pow(getUNorm(), 1.0f / 3.0f));
149 }
150
151 /** Returns a random point inside a unit circle. */
152 BS_SCRIPT_EXPORT()
153 Vector2 getPointInCircle() const
154 {
155 const Vector2 dir = getUnitVector2D();
156 return dir * std::pow(getUNorm(), 1.0f / 2.0f);
157 }
158
159 /**
160 * Returns a random point inside the specified range in a circle shell of unit radius, with the specified
161 * thickness, in range [0, 1]. Thickness of 0 will generate points on the circle edge, while thickness of 1 will
162 * generate points within the entire circle surface. Intermediate values represent the shell, which is the surface
163 * between two concentric circles.
164 */
165 BS_SCRIPT_EXPORT()
166 Vector2 getPointInCircleShell(float thickness) const
167 {
168 const float minRadius = 1.0f - thickness;
169
170 const Vector2 dir = getUnitVector2D();
171 return dir * (minRadius + thickness * std::pow(getUNorm(), 1.0f / 2.0f));
172 }
173
174 /** Returns a random point on a unit arc with the specified length (angle). Angle of 360 represents a circle. */
175 BS_SCRIPT_EXPORT()
176 Vector2 getPointInArc(Degree angle) const
177 {
178 float val = getUNorm() * angle.valueRadians();
179 return Vector2(Math::cos(val), Math::sin(val));
180 }
181
182 /**
183 * Returns a random point inside the specified range in an arc shell of unit radius, with the specified
184 * length (angle) and thickness in range [0, 1]. Angle of 360 represents a circle shell. Thickness of 0 will
185 * generate points on the arc edge, while thickness of 1 will generate points on the entire arc 'slice'.
186 * Intermediate vlaues represent the shell, which is the surface between two concentric circles.
187 */
188 BS_SCRIPT_EXPORT()
189 Vector2 getPointInArcShell(Degree angle, float thickness) const
190 {
191 const float minRadius = 1.0f - thickness;
192
193 const float val = getUNorm() * angle.valueRadians();
194 const Vector2 dir(Math::cos(val), Math::sin(val));
195
196 return dir * (minRadius + thickness * std::pow(getUNorm(), 1.0f / 2.0f));
197 }
198
199 /** Returns a random set of Barycentric coordinates that may be used for generating random points on a triangle. */
200 BS_SCRIPT_EXPORT()
201 Vector3 getBarycentric() const
202 {
203 float u = getUNorm();
204 float v = getUNorm();
205
206 if((u + v) > 1.0f)
207 {
208 u = 1.0f - u;
209 v = 1.0f - v;
210 }
211
212 const float w = 1.0f - u - v;
213 return Vector3(u, v, w);
214 }
215
216 private:
217 mutable uint32_t mSeed[4];
218 };
219
220 /** @} */
221}
222