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 "BsCorePrerequisites.h"
6#include "Image/BsColor.h"
7#include "Image/BsColorGradient.h"
8#include "Math/BsVector3.h"
9#include "Math/BsRandom.h"
10#include "Animation/BsAnimationCurve.h"
11#include "Animation/BsAnimationUtility.h"
12#include "Utility/BsBitwise.h"
13#include "Utility/BsLookupTable.h"
14
15namespace bs
16{
17 /** @addtogroup Particles-Internal
18 * @{
19 */
20
21 /** Determines type of distribution used by distribution properties. */
22 enum BS_SCRIPT_EXPORT(m:Particles) PropertyDistributionType
23 {
24 /** The distribution is a costant value. */
25 PDT_Constant BS_SCRIPT_EXPORT(n:Constant),
26 /** The distribution is a random value in a specified constant range. */
27 PDT_RandomRange BS_SCRIPT_EXPORT(n:RandomRange),
28 /** The distribution is a time-varying value. */
29 PDT_Curve BS_SCRIPT_EXPORT(n:Curve),
30 /** The distribution is a random value in a specified time-varying range. */
31 PDT_RandomCurveRange BS_SCRIPT_EXPORT(n:RandomCurveRange)
32 };
33
34 /* @} */
35
36 /** @addtogroup Particles
37 * @{
38 */
39
40 /** Specifies a color as a distribution, which can include a constant color, random color range or a color gradient. */
41 struct BS_CORE_EXPORT BS_SCRIPT_EXPORT(m:Particles) ColorDistribution
42 {
43 /** Creates a new empty distribution. */
44 BS_SCRIPT_EXPORT()
45 ColorDistribution()
46 : mType(PDT_Constant)
47 , mMinGradient({ ColorGradientKey(Color::Black, 0.0f) })
48 , mMaxGradient({ ColorGradientKey(Color::Black, 0.0f) })
49 { }
50
51 /** Creates a new distribution that returns a constant color. */
52 BS_SCRIPT_EXPORT()
53 ColorDistribution(const Color& color)
54 : mType(PDT_Constant)
55 , mMinGradient({ ColorGradientKey(color, 0.0f) })
56 , mMaxGradient({ ColorGradientKey(color, 0.0f) })
57 { }
58
59 /** Creates a new distribution that returns a random color in the specified range. */
60 BS_SCRIPT_EXPORT()
61 ColorDistribution(const Color& minColor, const Color& maxColor)
62 : mType(PDT_RandomRange)
63 , mMinGradient({ ColorGradientKey(minColor, 0.0f) })
64 , mMaxGradient({ ColorGradientKey(maxColor, 0.0f) })
65 { }
66
67 /** Creates a new distribution that evaluates a color gradient. */
68 BS_SCRIPT_EXPORT()
69 ColorDistribution(const ColorGradient& gradient)
70 : mType(PDT_Curve), mMinGradient(gradient), mMaxGradient(gradient)
71 {
72 if(mMinGradient.getNumKeys() == 0)
73 mMinGradient = ColorGradient({ ColorGradientKey(Color::Black, 0.0f) });
74
75 if(mMaxGradient.getNumKeys() == 0)
76 mMaxGradient = ColorGradient({ ColorGradientKey(Color::Black, 0.0f) });
77 }
78
79 /** Creates a new distribution that returns a random color in a range determined by two gradients. */
80 BS_SCRIPT_EXPORT()
81 ColorDistribution(const ColorGradient& minGradient, const ColorGradient& maxGradient)
82 : mType(PDT_RandomCurveRange), mMinGradient(minGradient), mMaxGradient(maxGradient)
83 {
84 if(mMinGradient.getNumKeys() == 0)
85 mMinGradient = ColorGradient({ ColorGradientKey(Color::Black, 0.0f) });
86
87 if(mMaxGradient.getNumKeys() == 0)
88 mMaxGradient = ColorGradient({ ColorGradientKey(Color::Black, 0.0f) });
89 }
90
91 /** Returns the type of the represented distribution. */
92 BS_SCRIPT_EXPORT(pr:getter,n:DistributionType)
93 PropertyDistributionType getType() const { return mType; }
94
95 /**
96 * Returns the constant value of the distribution, or the minimal value of a constant range. Undefined if
97 * the distribution is represented by a gradient.
98 */
99 BS_SCRIPT_EXPORT()
100 Color getMinConstant() const { return mMinGradient.getKey(0).color; }
101
102 /**
103 * Returns the maximum value of a constant range. Only defined if the distribution represents a non-gradient range.
104 */
105 BS_SCRIPT_EXPORT()
106 Color getMaxConstant() const { return mMaxGradient.getKey(0).color; }
107
108 /**
109 * Returns the gradient representing the distribution, or the first gradient representing a gradient range.
110 * Undefined if the distribution is represented by a constant or a non-gradient range.
111 */
112 BS_SCRIPT_EXPORT()
113 const ColorGradient& getMinGradient() const { return mMinGradient; }
114
115 /**
116 * Returns the curve representing the second gradient of a gradient range. Only defined if the distribution
117 * represents a gradient range.
118 */
119 BS_SCRIPT_EXPORT()
120 const ColorGradient& getMaxGradient() const { return mMaxGradient; }
121
122 /**
123 * Evaluates the value of the distribution.
124 *
125 * @param[in] t Time at which to evaluate the distribution. This is only relevant if the distribution
126 * contains gradients.
127 * @param[in] factor Value in range [0, 1] that determines how to interpolate between min/max value, if the
128 * distribution represents a range. Value of 0 will return the minimum value, while value of 1
129 * will return the maximum value, and interpolate the values in-between.
130 * @return Evaluated color.
131 *
132 */
133 RGBA evaluate(float t, float factor) const
134 {
135 const UINT32 byteFactor = Bitwise::unormToUint<8>(factor);
136 switch(mType)
137 {
138 default:
139 case PDT_Constant:
140 return mMinGradient.evaluate(0.0f);
141 case PDT_RandomRange:
142 {
143 const RGBA minColor = mMinGradient.evaluate(0.0f);
144 const RGBA maxColor = mMaxGradient.evaluate(0.0f);
145
146 return Color::lerp(byteFactor, minColor, maxColor);
147 }
148 case PDT_Curve:
149 return mMinGradient.evaluate(t);
150 case PDT_RandomCurveRange:
151 {
152 const RGBA minColor = mMinGradient.evaluate(t);
153 const RGBA maxColor = mMaxGradient.evaluate(t);
154
155 return Color::lerp(byteFactor, minColor, maxColor);
156 }
157 }
158 }
159
160 /**
161 * Evaluates the value of the distribution.
162 *
163 * @param[in] t Time at which to evaluate the distribution. This is only relevant if the distribution
164 * contains gradients.
165 * @param[in] factor Random number generator that determines the factor. Factor determines how to interpolate
166 * between min/max value, if the distribution represents a range.
167 * @return Evaluated color.
168 *
169 */
170 RGBA evaluate(float t, const Random& factor) const
171 {
172 switch(mType)
173 {
174 default:
175 case PDT_Constant:
176 return mMinGradient.evaluate(0.0f);
177 case PDT_RandomRange:
178 {
179 const RGBA minColor = mMinGradient.evaluate(0.0f);
180 const RGBA maxColor = mMaxGradient.evaluate(0.0f);
181
182 const UINT32 byteFactor = Bitwise::unormToUint<8>(factor.getUNorm());
183 return Color::lerp(byteFactor, minColor, maxColor);
184 }
185 case PDT_Curve:
186 return mMinGradient.evaluate(t);
187 case PDT_RandomCurveRange:
188 {
189 const RGBA minColor = mMinGradient.evaluate(t);
190 const RGBA maxColor = mMaxGradient.evaluate(t);
191
192 const UINT32 byteFactor = Bitwise::unormToUint<8>(factor.getUNorm());
193 return Color::lerp(byteFactor, minColor, maxColor);
194 }
195 }
196 }
197
198 /**
199 * Converts the distribution into a lookup table that's faster to access. The distribution will be resampled
200 * using a fixed sample rate with equidistant samples.
201 *
202 * @param[in] numSamples Determines how many samples to output in the lookup table. This value is ignored
203 * for non-curve distributions in which case there is always just one sample.
204 * @param[in] ignoreRange If the curve represents a range (either between constants or curves), this
205 * determines should the other value of the range be included in the lookup table.
206 * If true, only the minimum constant/curve will be included, and if false then
207 * the maximum curve values will follow the minimum curve values of each sample.
208 * @return Resampled lookup table.
209 */
210 LookupTable toLookupTable(UINT32 numSamples = 128, bool ignoreRange = false) const;
211
212 bool operator== (const ColorDistribution& rhs) const
213 {
214 if(mType != rhs.mType)
215 return false;
216
217 if(mType == PDT_Constant || mType == PDT_Curve)
218 return mMinGradient == rhs.mMinGradient;
219 else
220 return mMinGradient == rhs.mMinGradient && mMaxGradient == rhs.mMaxGradient;
221 }
222
223 bool operator!= (const ColorDistribution& rhs) const { return !operator==(rhs); }
224 private:
225 friend struct RTTIPlainType<ColorDistribution>;
226
227 PropertyDistributionType mType;
228 ColorGradient mMinGradient;
229 ColorGradient mMaxGradient;
230 };
231
232 /** Specifies a value as a distribution, which can include a constant value, random range or a curve. */
233 template<class T>
234 struct TDistribution
235 {
236 /** Creates a new empty distribution. */
237 BS_SCRIPT_EXPORT()
238 TDistribution()
239 : mType(PDT_Constant)
240 , mMinCurve({ TKeyframe<T>{ T(), TCurveProperties<T>::getZero(), TCurveProperties<T>::getZero(), 0.0f} })
241 , mMaxCurve({ TKeyframe<T>{ T(), TCurveProperties<T>::getZero(), TCurveProperties<T>::getZero(), 0.0f} })
242 { }
243 /** Creates a new distribution that returns a constant value. */
244 BS_SCRIPT_EXPORT()
245 TDistribution(T value)
246 : mType(PDT_Constant)
247 , mMinCurve({ TKeyframe<T>{ value, TCurveProperties<T>::getZero(), TCurveProperties<T>::getZero(), 0.0f} })
248 , mMaxCurve({ TKeyframe<T>{ value, TCurveProperties<T>::getZero(), TCurveProperties<T>::getZero(), 0.0f} })
249 { }
250
251 /** Creates a new distribution that returns a random value in the specified range. */
252 BS_SCRIPT_EXPORT()
253 TDistribution(T minValue, T maxValue)
254 : mType(PDT_RandomRange)
255 , mMinCurve({ TKeyframe<T>{ minValue, TCurveProperties<T>::getZero(), TCurveProperties<T>::getZero(), 0.0f} })
256 , mMaxCurve({ TKeyframe<T>{ maxValue, TCurveProperties<T>::getZero(), TCurveProperties<T>::getZero(), 0.0f} })
257 { }
258
259 /** Creates a new distribution that evaluates a curve. */
260 BS_SCRIPT_EXPORT()
261 TDistribution(const TAnimationCurve<T>& curve)
262 : mType(PDT_Curve), mMinCurve(curve), mMaxCurve(curve)
263 {
264 if(mMinCurve.getKeyFrames().empty())
265 mMinCurve = TAnimationCurve<T>({ TKeyframe<T>{ T(), TCurveProperties<T>::getZero(), TCurveProperties<T>::getZero(), 0.0f} });
266
267 if(mMaxCurve.getKeyFrames().empty())
268 mMaxCurve = TAnimationCurve<T>({ TKeyframe<T>{ T(), TCurveProperties<T>::getZero(), TCurveProperties<T>::getZero(), 0.0f} });
269 }
270
271 /** Creates a new distribution that returns a random value in a range determined by two curves. */
272 BS_SCRIPT_EXPORT()
273 TDistribution(const TAnimationCurve<T>& minCurve, const TAnimationCurve<T>& maxCurve)
274 : mType(PDT_RandomCurveRange), mMinCurve(minCurve), mMaxCurve(maxCurve)
275 {
276 if(mMinCurve.getKeyFrames().empty())
277 mMinCurve = TAnimationCurve<T>({ TKeyframe<T>{ T(), TCurveProperties<T>::getZero(), TCurveProperties<T>::getZero(), 0.0f} });
278
279 if(mMaxCurve.getKeyFrames().empty())
280 mMaxCurve = TAnimationCurve<T>({ TKeyframe<T>{ T(), TCurveProperties<T>::getZero(), TCurveProperties<T>::getZero(), 0.0f} });
281 }
282
283 /** Returns the type of the represented distribution. */
284 BS_SCRIPT_EXPORT(pr:getter,n:DistributionType)
285 PropertyDistributionType getType() const { return mType; }
286
287 /**
288 * Returns the constant value of the distribution, or the minimal value of a constant range. Undefined if
289 * the distribution is represented by a curve.
290 */
291 BS_SCRIPT_EXPORT()
292 const T& getMinConstant() const { return mMinCurve.getKeyFrames()[0].value; }
293
294 /**
295 * Returns the maximum value of a constant range. Only defined if the distribution represents a non-curve range.
296 */
297 BS_SCRIPT_EXPORT()
298 const T& getMaxConstant() const { return mMaxCurve.getKeyFrames()[0].value; }
299
300 /**
301 * Returns the curve representing the distribution, or the first curve representing a curve range. Undefined if
302 * the distribution is represented by a constant or a non-curve range.
303 */
304 BS_SCRIPT_EXPORT()
305 const TAnimationCurve<T>& getMinCurve() const { return mMinCurve; }
306
307 /**
308 * Returns the curve representing the second curve of a curve range. Only defined if the distribution represents
309 * a curve range.
310 */
311 BS_SCRIPT_EXPORT()
312 const TAnimationCurve<T>& getMaxCurve() const { return mMaxCurve; }
313
314 /**
315 * Evaluates the value of the distribution.
316 *
317 * @param[in] t Time at which to evaluate the distribution. This is only relevant if the distribution
318 * contains curves.
319 * @param[in] factor Value in range [0, 1] that determines how to interpolate between min/max value, if the
320 * distribution represents a range. Value of 0 will return the minimum value, while value of 1
321 * will return the maximum value, and interpolate the values in-between.
322 * @return Evaluated value.
323 *
324 */
325 BS_SCRIPT_EXPORT()
326 T evaluate(float t, float factor) const
327 {
328 switch(mType)
329 {
330 default:
331 case PDT_Constant:
332 return getMinConstant();
333 case PDT_RandomRange:
334 return Math::lerp(factor, getMinConstant(), getMaxConstant());
335 case PDT_Curve:
336 return mMinCurve.evaluate(t);
337 case PDT_RandomCurveRange:
338 {
339 const T minValue = mMinCurve.evaluate(t);
340 const T maxValue = mMaxCurve.evaluate(t);
341
342 return Math::lerp(factor, minValue, maxValue);
343 }
344 }
345 }
346
347 /**
348 * Evaluates the value of the distribution.
349 *
350 * @param[in] t Time at which to evaluate the distribution. This is only relevant if the distribution
351 * contains curves.
352 * @param[in] factor Random number generator that determines the factor. Factor determines how to interpolate
353 * between min/max value, if the distribution represents a range.
354 * @return Evaluated value.
355 *
356 */
357 BS_SCRIPT_EXPORT()
358 T evaluate(float t, const Random& factor) const
359 {
360 switch(mType)
361 {
362 default:
363 case PDT_Constant:
364 return getMinConstant();
365 case PDT_RandomRange:
366 return Math::lerp(factor.getUNorm(), getMinConstant(), getMaxConstant());
367 case PDT_Curve:
368 return mMinCurve.evaluate(t);
369 case PDT_RandomCurveRange:
370 {
371 const T minValue = mMinCurve.evaluate(t);
372 const T maxValue = mMaxCurve.evaluate(t);
373
374 return Math::lerp(factor.getUNorm(), minValue, maxValue);
375 }
376 }
377 }
378
379 /**
380 * Converts the distribution into a lookup table that's faster to access. The distribution will be resampled
381 * using a fixed sample rate with equidistant samples.
382 *
383 * @param[in] numSamples Determines how many samples to output in the lookup table. This value is ignored
384 * for non-curve distributions in which case there is always just one sample.
385 * @param[in] ignoreRange If the curve represents a range (either between constants or curves), this
386 * determines should the other value of the range be included in the lookup table.
387 * If true, only the minimum constant/curve will be included, and if false then
388 * the maximum curve values will follow the minimum curve values of each sample.
389 * @return Resampled lookup table.
390 */
391 LookupTable toLookupTable(UINT32 numSamples = 128, bool ignoreRange = false) const;
392
393 bool operator== (const TDistribution<T>& rhs) const
394 {
395 if(mType != rhs.mType)
396 return false;
397
398 if(mType == PDT_Constant || mType == PDT_Curve)
399 return mMinCurve == rhs.mMinCurve;
400 else
401 return mMinCurve == rhs.mMinCurve && mMaxCurve == rhs.mMaxCurve;
402 }
403
404 bool operator!= (const TDistribution<T>& rhs) const { return !operator==(rhs); }
405 private:
406 friend struct RTTIPlainType<TDistribution<T>>;
407
408 PropertyDistributionType mType;
409 TAnimationCurve<T> mMinCurve;
410 TAnimationCurve<T> mMaxCurve;
411 };
412
413 using FloatDistribution = TDistribution<float>;
414 using Vector3Distribution = TDistribution<Vector3>;
415 using Vector2Distribution = TDistribution<Vector2>;
416
417#ifdef BS_SBGEN
418 template struct BS_SCRIPT_EXPORT(m:Particles,n:FloatDistribution) TDistribution<float>;
419 template struct BS_SCRIPT_EXPORT(m:Particles,n:Vector3Distribution) TDistribution<Vector3>;
420 template struct BS_SCRIPT_EXPORT(m:Particles,n:Vector2Distribution) TDistribution<Vector2>;
421#endif
422
423 /** @} */
424}