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 | |
15 | namespace 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 | } |