/* * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2 * Copyright (C) 2008-2016 TrinityCore * Copyright (C) 2005-2009 MaNGOS */ #ifndef TRINITYSERVER_SPLINE_H #define TRINITYSERVER_SPLINE_H #include "MovementTypedefs.h" #include #include namespace Movement { class SplineBase { public: typedef int index_type; typedef std::vector ControlArray; enum EvaluationMode { ModeLinear, ModeCatmullrom, ModeBezier3_Unused, UninitializedMode, ModesEnd }; protected: ControlArray points; ControlArray pointsVisual; index_type index_lo{0}; index_type index_hi{0}; uint8 m_mode{UninitializedMode}; bool cyclic{false}; enum { // could be modified, affects segment length evaluation precision // lesser value saves more performance in cost of lover precision // minimal value is 1 // client's value is 20, blizzs use 2-3 steps to compute length STEPS_PER_SEGMENT = 3 }; static_assert(STEPS_PER_SEGMENT > 0, "shouldn't be lesser than 1"); protected: void EvaluateLinear(index_type, float, Vector3&) const; void EvaluateCatmullRom(index_type, float, Vector3&) const; void EvaluateBezier3(index_type, float, Vector3&) const; typedef void (SplineBase::*EvaluationMethtod)(index_type, float, Vector3&) const; static EvaluationMethtod evaluators[ModesEnd]; void EvaluateDerivativeLinear(index_type, float, Vector3&) const; void EvaluateDerivativeCatmullRom(index_type, float, Vector3&) const; void EvaluateDerivativeBezier3(index_type, float, Vector3&) const; static EvaluationMethtod derivative_evaluators[ModesEnd]; [[nodiscard]] float SegLengthLinear(index_type) const; [[nodiscard]] float SegLengthCatmullRom(index_type) const; [[nodiscard]] float SegLengthBezier3(index_type) const; typedef float (SplineBase::*SegLenghtMethtod)(index_type) const; static SegLenghtMethtod seglengths[ModesEnd]; void InitLinear(const Vector3*, index_type, bool, index_type); void InitCatmullRom(const Vector3*, index_type, bool, index_type); void InitBezier3(const Vector3*, index_type, bool, index_type); typedef void (SplineBase::*InitMethtod)(const Vector3*, index_type, bool, index_type); static InitMethtod initializers[ModesEnd]; void UninitializedSplineEvaluationMethod(index_type, float, Vector3&) const { ABORT(); } [[nodiscard]] float UninitializedSplineSegLenghtMethod(index_type) const { ABORT(); } void UninitializedSplineInitMethod(Vector3 const*, index_type, bool, index_type) { ABORT(); } public: explicit SplineBase() {} /** Caclulates the position for given segment Idx, and percent of segment length t @param t - percent of segment length, assumes that t in range [0, 1] @param Idx - spline segment index, should be in range [first, last) */ void evaluate_percent(index_type Idx, float u, Vector3& c) const {(this->*evaluators[m_mode])(Idx, u, c);} /** Caclulates derivation in index Idx, and percent of segment length t @param Idx - spline segment index, should be in range [first, last) @param t - percent of spline segment length, assumes that t in range [0, 1] */ void evaluate_derivative(index_type Idx, float u, Vector3& hermite) const {(this->*derivative_evaluators[m_mode])(Idx, u, hermite);} /** Bounds for spline indexes. All indexes should be in range [first, last). */ [[nodiscard]] index_type first() const { return index_lo;} [[nodiscard]] index_type last() const { return index_hi;} [[nodiscard]] bool empty() const { return index_lo == index_hi;} [[nodiscard]] EvaluationMode mode() const { return (EvaluationMode)m_mode;} [[nodiscard]] bool isCyclic() const { return cyclic;} // Xinef: DO NOT USE EXCEPT FOR SPLINE INITIALIZATION!!!!!! [[nodiscard]] const ControlArray* allocateVisualPoints() const { return &pointsVisual; } [[nodiscard]] const ControlArray& getPoints(bool visual) const { return visual ? pointsVisual : points;} [[nodiscard]] index_type getPointCount() const { return points.size();} [[nodiscard]] const Vector3& getPoint(index_type i, bool visual) const { return visual ? pointsVisual[i] : points[i];} /** Initializes spline. Don't call other methods while spline not initialized. */ void init_spline(const Vector3* controls, index_type count, EvaluationMode m); void init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point); /** As i can see there are a lot of ways how spline can be initialized would be no harm to have some custom initializers. */ template inline void init_spline_custom(Init& initializer) { initializer(m_mode, cyclic, points, index_lo, index_hi); } void clear(); /** Calculates distance between [i; i+1] points, assumes that index i is in bounds. */ [[nodiscard]] float SegLength(index_type i) const { return (this->*seglengths[m_mode])(i);} [[nodiscard]] std::string ToString() const; }; template class Spline : public SplineBase { public: typedef length_type LengthType; typedef std::vector LengthArray; protected: LengthArray lengths; [[nodiscard]] index_type computeIndexInBounds(length_type length) const; public: explicit Spline() = default; /** Calculates the position for given t @param t - percent of spline's length, assumes that t in range [0, 1]. */ void evaluate_percent(float t, Vector3& c) const; /** Calculates derivation for given t @param t - percent of spline's length, assumes that t in range [0, 1]. */ void evaluate_derivative(float t, Vector3& hermite) const; /** Calculates the position for given segment Idx, and percent of segment length t @param t = partial_segment_length / whole_segment_length @param Idx - spline segment index, should be in range [first, last). */ void evaluate_percent(index_type Idx, float u, Vector3& c) const { SplineBase::evaluate_percent(Idx, u, c);} /** Caclulates derivation for index Idx, and percent of segment length t @param Idx - spline segment index, should be in range [first, last) @param t - percent of spline segment length, assumes that t in range [0, 1]. */ void evaluate_derivative(index_type Idx, float u, Vector3& c) const { SplineBase::evaluate_derivative(Idx, u, c);} // Assumes that t in range [0, 1] [[nodiscard]] index_type computeIndexInBounds(float t) const; void computeIndex(float t, index_type& out_idx, float& out_u) const; /** Initializes spline. Don't call other methods while spline not initialized. */ void init_spline(const Vector3* controls, index_type count, EvaluationMode m) { SplineBase::init_spline(controls, count, m);} void init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point) { SplineBase::init_cyclic_spline(controls, count, m, cyclic_point);} /** Initializes lengths with SplineBase::SegLength method. */ void initLengths(); /** Initializes lengths in some custom way Note that value returned by cacher must be greater or equal to previous value. */ template inline void initLengths(T& cacher) { index_type i = index_lo; lengths.resize(index_hi + 1); length_type prev_length = 0, new_length = 0; while (i < index_hi) { new_length = cacher(*this, i); // length overflowed, assign to max positive value if (new_length < 0) new_length = std::numeric_limits::max(); lengths[++i] = new_length; ASSERT(prev_length <= new_length); prev_length = new_length; } } /** Returns length of the whole spline. */ [[nodiscard]] length_type length() const { return lengths[index_hi];} /** Returns length between given nodes. */ [[nodiscard]] length_type length(index_type first, index_type last) const { return lengths[last] - lengths[first];} [[nodiscard]] length_type length(index_type Idx) const { return lengths[Idx];} void set_length(index_type i, length_type length) { lengths[i] = length;} void clear(); }; } #include "SplineImpl.h" #endif // TRINITYSERVER_SPLINE_H