Files
fs2open.github.com/code/particle/util/RandomRange.h
T
asarium bbeaaa1565 Add a new particle effect system
This is similar to the script I wrote a few years ago but unlike that
one, this solution doesn't kill the performance.
2016-08-04 11:08:39 +02:00

206 lines
4.6 KiB
C++

#ifndef RANDOM_RANGE_H
#define RANDOM_RANGE_H
#include <random>
#include <type_traits>
#include "globalincs/pstypes.h"
#include "parse/parselo.h"
namespace particle {
namespace util {
/**
* @defgroup particleUtils Particle Effect utilities
*
* @ingroup particleSystems
*/
namespace {
/**
* @brief Parses a generic list of numbers
* @param list The array where the numbers should be stored.
* @return The amount of parsed numbers
*
* @ingroup particleUtils
*/
template<typename T, size_t N>
size_t parse_number_list(T (& list)[N]) {
float helpList[N];
auto num = stuff_float_list(helpList, N);
for (size_t i = 0; i < num; ++i) {
list[i] = static_cast<T>(helpList[i]);
}
return num;
}
}
/**
* @brief Generic class for generating numbers in a specific range
*
* This allows to use a generic value, distribution and generator type. It's valid to only use one value for the range
* in which case the returned value is constant.
*
* @ingroup particleUtils
*/
template<typename Value, typename Distribution, typename Generator>
class RandomRange {
public:
typedef Distribution DistributionType;
typedef Generator GeneratorType;
typedef Value ValueType;
private:
GeneratorType m_generator;
DistributionType m_distribution;
bool m_constant;
ValueType m_constantValue;
public:
template<typename... Ts>
RandomRange(ValueType param1, ValueType param2, Ts&& ... distributionParameters) :
m_generator(std::random_device()()),
m_distribution(param1, param2, distributionParameters...) {
m_constantValue = static_cast<ValueType>(0.0);
m_constant = false;
}
explicit RandomRange(const ValueType& val) : RandomRange() {
m_constantValue = val;
m_constant = true;
}
RandomRange() :
m_generator(std::random_device()()),
m_distribution() {
m_constantValue = static_cast<ValueType>(0.0);
m_constant = true;
}
/**
* @brief Determines the next random number of this range
* @return The random number
*/
ValueType next() {
if (m_constant) {
return m_constantValue;
}
return m_distribution(m_generator);
}
};
/**
* @brief A random range with a normal distribution
*
* The range parameters are passed directly to std::normal_distribution
*
* @ingroup particleUtils
*/
template<typename Value>
using NormalRange = RandomRange<Value,
std::normal_distribution<Value>,
std::minstd_rand>;
/**
* @brief A normal range which uses floats
*
* @ingroup particleUtils
*/
typedef NormalRange<float> NormalFloatRange;
/**
* @brief A function for parsing a normal range
* @return The parsed normal range
*
* @ingroup particleUtils
*/
template<typename Value>
NormalRange<Value> parseNormalRange() {
Value valueList[2];
auto num = parse_number_list(valueList);
if (num == 0) {
error_display(0, "Need at least one value to form a random range!");
return NormalRange<Value>();
}
else if (num == 1) {
return NormalRange<Value>(valueList[0]);
}
return NormalRange<Value>(valueList[0], valueList[1]);
}
/**
* @brief A generic random range which uses a uniform distribution
*
* @ingroup particleUtils
*/
template<typename Value>
using UniformRange = RandomRange<Value,
typename std::conditional<std::is_integral<Value>::value,
std::uniform_int_distribution<Value>,
std::uniform_real_distribution<Value>>::type,
std::minstd_rand>;
/**
* @brief A uniform range which uses floats
*
* @ingroup particleUtils
*/
typedef UniformRange<float> UniformFloatRange;
/**
* @brief A uniform range which uses ints
*
* @ingroup particleUtils
*/
typedef UniformRange<int> UniformIntRange;
/**
* @brief A uniform range which uses uints
*
* @ingroup particleUtils
*/
typedef UniformRange<uint> UniformUIntRange;
/**
* @brief Parses a generic uniform range
* @param allowNegativ If @c true, negative values will be allowed
* @return The parsed uniform range
*
* @ingroup particleUtils
*/
template<typename Value>
UniformRange<Value> parseUniformRange(bool allowNegativ = false) {
Value valueList[2];
auto num = parse_number_list(valueList);
if (num == 0) {
error_display(0, "Need at least one value to form a random range!");
return UniformRange<Value>();
}
else if (num == 1) {
return UniformRange<Value>(valueList[0]);
}
if (valueList[0] > valueList[1]) {
error_display(0, "Minimum value %f is more than maximum value %f!", (float) valueList[0], (float) valueList[1]);
std::swap(valueList[0], valueList[1]);
}
if (!allowNegativ && valueList[0] < 0.0) {
error_display(0, "Minimum value may not be less than zero, got %f", (float) valueList[0]);
valueList[0] = static_cast<Value>(0.0);
}
return UniformRange<Value>(valueList[0], valueList[1]);
}
}
}
#endif // RANDOM_RANGE_H