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