1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9
10 #include "math/staticrand.h"
11 #include "math/vecmat.h"
12 #include "utils/Random.h"
13
14 bool Semirand_inited = false;
15 unsigned int Semirand[SEMIRAND_MAX];
16
17 /**
18 * @brief Initialize Semirand array. Doesn't have to be called.
19 */
init_semirand()20 void init_semirand()
21 {
22 Semirand_inited = true;
23
24 // Originally this made a 30-bit rand by sticking two 15-bit rands from myrand() together. Instead we trim Random::next() down to size.
25 for (auto & number : Semirand)
26 number = (unsigned) (util::Random::next() & STATIC_RAND_MAX);
27 }
28
29 // TODO: figure out what to do with these
30 /**
31 * @brief Return a pseudo random 32 bit value given a reasonably small number.
32 *
33 * @param num Seed input number
34 * @return Pseudo random 32 bit value
35 */
static_rand(int num)36 int static_rand(int num)
37 {
38 if (num < 0)
39 num *= -1;
40
41 if (!Semirand_inited)
42 init_semirand();
43
44 const unsigned int num_unsigned = num;
45 auto a = num_unsigned & (SEMIRAND_MAX - 1);
46 auto b = (num_unsigned >> SEMIRAND_MAX_LOG) & (SEMIRAND_MAX - 1);
47 auto c = (num_unsigned >> (2 * SEMIRAND_MAX_LOG)) & (SEMIRAND_MAX - 1);
48
49 return Semirand[a] ^ Semirand[b] ^ Semirand[c];
50 }
51
52 /**
53 * @brief Return a random float in 0.0f .. 1.0f- (ie, it will never return 1.0f).
54 *
55 * @param num Seed input number
56 * @return Random value in 0.0f .. 1.0f- (ie, it will never return 1.0f).
57 */
static_randf(int num)58 float static_randf(int num)
59 {
60 unsigned int a = static_rand(num);
61 return (a & 0xffff) / 65536.0f;
62 }
63
64 /**
65 * @brief Return a random integer within a range. Note: min and max are inclusive
66 *
67 * @param num Seed input number
68 * @param min Minimum range integer to return
69 * @param max Maximum range integer to return
70 * @return Random integer within the range
71 */
static_rand_range(int num,int min,int max)72 int static_rand_range(int num, int min, int max)
73 {
74 int rval = static_rand(num);
75 rval = (rval % (max - min + 1)) + min;
76 CLAMP(rval, min, max);
77 return rval;
78 }
79
80 /**
81 * @brief Return a random float within a range.
82 * Note: min and max are inclusive
83 *
84 * @param num Seed input number
85 * @param min Minimum range float to return
86 * @param max Maximum range float to return
87 * @return Random float within the range
88 */
static_randf_range(int num,float min,float max)89 float static_randf_range(int num, float min, float max)
90 {
91 float rval = static_randf(num);
92 rval = rval * (max - min) + min;
93
94 return rval;
95 }
96
97 /**
98 * @brief Create a random, normalized vector in unit sphere
99 *
100 * @param num Seed input number
101 * @param vp Vector
102 */
static_randvec(int num,vec3d * vp)103 void static_randvec(int num, vec3d *vp)
104 {
105 vp->xyz.x = static_randf(num) - 0.5f;
106 vp->xyz.y = static_randf(num+1) - 0.5f;
107 vp->xyz.z = static_randf(num+2) - 0.5f;
108
109 vm_vec_normalize_quick(vp);
110 }
111
112 /**
113 *
114 * @brief Create a random, unnormalized vector in the (half) unit cube
115 *
116 * @param num Seed input vector
117 * @param vp Vector
118 */
static_randvec_unnormalized(int num,vec3d * vp)119 void static_randvec_unnormalized(int num, vec3d* vp)
120 {
121 vp->xyz.x = static_randf(num) - 0.5f;
122 vp->xyz.y = static_randf(num + 1) - 0.5f;
123 vp->xyz.z = static_randf(num + 2) - 0.5f;
124 }
125
126 /**
127 * @brief Randomly perturb a vector around a given (normalized vector) or optional orientation matrix.
128 *
129 * @param num
130 * @param out
131 * @param in
132 * @param max_angle
133 * @param orient
134 */
static_rand_cone(int num,vec3d * out,const vec3d * const in,float max_angle,const matrix * const orient)135 void static_rand_cone(int num, vec3d *out, const vec3d* const in, float max_angle, const matrix* const orient)
136 {
137 vec3d t1, t2;
138 const matrix *rot;
139 matrix m;
140
141 // get an orientation matrix
142 if(orient != nullptr){
143 rot = orient;
144 } else {
145 vm_vector_2_matrix(&m, in, nullptr, nullptr);
146 rot = &m;
147 }
148
149 // axis 1
150 vm_rot_point_around_line(&t1, in, fl_radians(static_randf_range(num,-max_angle, max_angle)), &vmd_zero_vector, &rot->vec.fvec);
151
152 // axis 2
153 vm_rot_point_around_line(&t2, &t1, fl_radians(static_randf_range(num+1,-max_angle, max_angle)), &vmd_zero_vector, &rot->vec.rvec);
154
155 // axis 3
156 vm_rot_point_around_line(out, &t2, fl_radians(static_randf_range(num+2,-max_angle, max_angle)), &vmd_zero_vector, &rot->vec.uvec);
157 }
158
159 //generates a random vector in a cone, with a min amd max angle.
160 //Clone of vm_vec_random_cone overload of the same function, adapted to use static_randf_range
static_rand_cone(int num,vec3d * out,const vec3d * const in,float min_angle,float max_angle,const matrix * const orient)161 void static_rand_cone(int num, vec3d* out, const vec3d* const in, float min_angle, float max_angle, const matrix* const orient)
162 {
163 vec3d temp;
164 const matrix* rot;
165 matrix m;
166
167 if (max_angle < min_angle) {
168 std::swap(min_angle, max_angle);
169 }
170
171 // get an orientation matrix
172 if (orient != nullptr) {
173 rot = orient;
174 }
175 else {
176 vm_vector_2_matrix(&m, in, nullptr, nullptr);
177 rot = &m;
178 }
179
180 // Get properly distributed spherical coordinates (DahBlount)
181 // This might not seem intuitive, but the min_angle is the angle that will have a larger z coordinate
182 float z = static_randf_range(num, cosf(fl_radians(max_angle)), cosf(fl_radians(min_angle))); // Take a 2-sphere slice
183 float phi = static_randf_range(num+1, 0.0f, PI2);
184 vm_vec_make(&temp, sqrtf(1.0f - z * z) * cosf(phi), sqrtf(1.0f - z * z) * sinf(phi), z); // Using the z-vec as the starting point
185
186 vm_vec_unrotate(out, &temp, rot); // We find the final vector by rotating temp to the correct orientation
187 }
188
189
190 /////////////////////////////////////////////////////////////////////
191 // Alternate random number generator, that doesn't affect rand() sequence
192 /////////////////////////////////////////////////////////////////////
193 constexpr unsigned int RND_MASK = 0x6000;
194 constexpr int RND_MAX = 0x7fff;
195 unsigned int Rnd_seed = 1;
196
197 /**
198 * @brief Seed the alternative random number generator.
199 * Doesn't have to be called.
200 *
201 * @param seed Seed input number
202 */
init_static_rand_alt(int seed)203 void init_static_rand_alt(int seed)
204 {
205 Rnd_seed = seed;
206 }
207
208 /**
209 * @brief Get a random integer between 1 and RND_MAX.
210 *
211 * @return Random integer between 1 and RND_MAX
212 */
static_rand_alt()213 int static_rand_alt()
214 {
215 static unsigned int x=Rnd_seed;
216 unsigned int old_x = x;
217 x >>= 1u;
218 if ( old_x & 1u ) {
219 x ^= RND_MASK;
220 }
221 return x;
222 }
223
224 /**
225 * @brief Get a random integer between 0 and 1.0.
226 *
227 * @return Random float between 0 and 1.0
228 */
static_randf_alt()229 float static_randf_alt()
230 {
231 int r = static_rand_alt();
232 return i2fl(r)/RND_MAX;
233 }
234