diff --git a/config/G2ME01/splits.txt b/config/G2ME01/splits.txt index d3d8b79..2e5257d 100644 --- a/config/G2ME01/splits.txt +++ b/config/G2ME01/splits.txt @@ -136,6 +136,9 @@ Kyoto/Input/CDolphinController.cpp: .sbss start:0x80419B90 end:0x80419B98 .sdata2 start:0x8041E9F0 end:0x8041EA28 +Kyoto/Math/CMayaSpline.cpp: + .text start:0x80328B0C end:0x8032A764 + Runtime/global_destructor_chain.c: .text start:0x80344DD8 end:0x80344E38 .sbss start:0x80419CC0 end:0x80419CC4 diff --git a/config/G2ME01/symbols.txt b/config/G2ME01/symbols.txt index a352570..8bb47d5 100644 --- a/config/G2ME01/symbols.txt +++ b/config/G2ME01/symbols.txt @@ -14828,27 +14828,27 @@ fn_803283B0 = .text:0x803283B0; // type:function size:0x220 align:4 fn_803285D0 = .text:0x803285D0; // type:function size:0x3D8 fn_803289A8 = .text:0x803289A8; // type:function size:0x164 fn_80328B0C = .text:0x80328B0C; // type:function size:0x22C -fn_80328D38 = .text:0x80328D38; // type:function size:0x144 -fn_80328E7C = .text:0x80328E7C; // type:function size:0x248 -fn_803290C4 = .text:0x803290C4; // type:function size:0x8 align:4 -fn_803290CC = .text:0x803290CC; // type:function size:0x50 align:4 -fn_8032911C = .text:0x8032911C; // type:function size:0x28 -fn_80329144 = .text:0x80329144; // type:function size:0x8 align:4 +CalculateHermiteCoefficients__11CMayaSplineFRCQ24rstl29reserved_vector<9CVector2f,4>Pf = .text:0x80328D38; // type:function size:0x144 +FindControlPoints__11CMayaSplineFiRQ24rstl29reserved_vector<9CVector2f,4> = .text:0x80328E7C; // type:function size:0x248 +GetKnotCount__11CMayaSplineCFv = .text:0x803290C4; // type:function size:0x8 align:4 +GetDuration__11CMayaSplineCFv = .text:0x803290CC; // type:function size:0x50 align:4 +GetMaxTime__11CMayaSplineCFv = .text:0x8032911C; // type:function size:0x28 +GetKnots__11CMayaSplineCFv = .text:0x80329144; // type:function size:0x8 align:4 fn_8032914C = .text:0x8032914C; // type:function size:0x1B8 -fn_80329304 = .text:0x80329304; // type:function size:0x2C4 -fn_803295C8 = .text:0x803295C8; // type:function size:0x158 -fn_80329720 = .text:0x80329720; // type:function size:0x6C -fn_8032978C = .text:0x8032978C; // type:function size:0x3AC -fn_80329B38 = .text:0x80329B38; // type:function size:0x28 -fn_80329B60 = .text:0x80329B60; // type:function size:0x40 +EvaluateAtUnclamped__11CMayaSplineFf = .text:0x80329304; // type:function size:0x2C4 +EvaluateAt__11CMayaSplineFf = .text:0x803295C8; // type:function size:0x158 +FindKnot__11CMayaSplineFfRi = .text:0x80329720; // type:function size:0x6C +EvaluateInfinities__11CMayaSplineFfb = .text:0x8032978C; // type:function size:0x3AC +EvaluateHermite__11CMayaSplineFf = .text:0x80329B38; // type:function size:0x28 +SLdrSpline_ct = .text:0x80329B60; // type:function size:0x40 fn_80329BA0 = .text:0x80329BA0; // type:function size:0xDC align:4 -fn_80329C7C = .text:0x80329C7C; // type:function size:0xC8 -fn_80329D44 = .text:0x80329D44; // type:function size:0xE0 -fn_80329E24 = .text:0x80329E24; // type:function size:0x5A4 align:4 -fn_8032A3C8 = .text:0x8032A3C8; // type:function size:0xA8 -fn_8032A470 = .text:0x8032A470; // type:function size:0x70 -fn_8032A4E0 = .text:0x8032A4E0; // type:function size:0x144 align:4 -fn_8032A624 = .text:0x8032A624; // type:function size:0x140 +SLdrSpline_Read = .text:0x80329C7C; // type:function size:0xC8 +vector_Knot_InputStream = .text:0x80329D44; // type:function size:0xE0 +CalculateTangents__15CMayaSplineKnotFP15CMayaSplineKnotP15CMayaSplineKnot = .text:0x80329E24; // type:function size:0x5A4 align:4 +ValidateTangent__FR9CVector2f = .text:0x8032A3C8; // type:function size:0xA8 +GetTangents__15CMayaSplineKnotFP15CMayaSplineKnotP15CMayaSplineKnotR9CVector2fR9CVector2f = .text:0x8032A470; // type:function size:0x70 +CMayaSplineKnot_ct = .text:0x8032A4E0; // type:function size:0x144 align:4 +__ct__15CMayaSplineKnotFR12CInputStream = .text:0x8032A624; // type:function size:0x140 fn_8032A764 = .text:0x8032A764; // type:function size:0x138 align:4 fn_8032A89C = .text:0x8032A89C; // type:function size:0x1DC align:4 fn_8032AA78 = .text:0x8032AA78; // type:function size:0xB0 diff --git a/configure.py b/configure.py index b229a30..6fcc9ac 100644 --- a/configure.py +++ b/configure.py @@ -304,6 +304,7 @@ config.libs = [ Object(Matching, "Kyoto/Math/CVector3i.cpp"), Object(Matching, "Kyoto/Input/DolphinIController.cpp"), Object(Matching, "Kyoto/Input/CDolphinController.cpp"), + Object(NonMatching, "Kyoto/Math/CMayaSpline.cpp"), ], }, { diff --git a/include/Kyoto/Math/CMayaSpline.hpp b/include/Kyoto/Math/CMayaSpline.hpp new file mode 100644 index 0000000..a636d15 --- /dev/null +++ b/include/Kyoto/Math/CMayaSpline.hpp @@ -0,0 +1,62 @@ +#ifndef _CMAYASPLINE +#define _CMAYASPLINE + +#include "Kyoto/Math/CVector2f.hpp" + +#include "rstl/vector.hpp" +#include "rstl/reserved_vector.hpp" + +class CMayaSplineKnot { + float x0_time; + float x4_amplitude; + u8 x8_; + u8 x9_; + bool xa_24_dirty : 1; + u8 xb_; + CVector2f xc_cachedTangentA; + CVector2f x14_cachedTangentB; + +public: + CMayaSplineKnot(CInputStream& in); + + float GetTime() const { return x0_time; } + float GetAmplitude() const { return x4_amplitude; } + u8 GetX8() const { return x8_; } + u8 GetX9() const { return x9_; } + void GetTangents(CMayaSplineKnot* prev, CMayaSplineKnot* next, CVector2f& tangentA, CVector2f& tangentB); + void CalculateTangents(CMayaSplineKnot* prev, CMayaSplineKnot* next); +}; + + +class CMayaSpline { + uint x0_preInfinity; + uint x4_postInfinity; + rstl::vector x8_knots; + uint x18_clampMode; + float x1c_minAmplitudeTime; + float x20_maxAmplitudeTime; + int x24_chachedKnotIndex; + int x28_; + bool x2c_24_dirty; + float x30_cachedMinTime; + float x34_cachedHermitCoefs[4]; + +public: + CMayaSpline(CInputStream& in, int count); + + size_t GetKnotCount() const; + const rstl::vector& GetKnots() const; + float GetMinTime() const; + float GetMaxTime() const; + float GetDuration() const; + + float EvaluateAt(float time); + float EvaluateAtUnclamped(float time); + float EvaluateInfinities(float time, bool Pre); + float EvaluateHermite(float time); + bool FindKnot(float time, int& knotIndex); + void FindControlPoints(int knotIndex, rstl::reserved_vector& controlPoints); + void CalculateHermiteCoefficients(const rstl::reserved_vector& controlPoits, float* coefs); +}; + +#endif // _CMAYASPLINE diff --git a/include/Kyoto/Math/CVector2f.hpp b/include/Kyoto/Math/CVector2f.hpp index be5ba66..5ad5601 100644 --- a/include/Kyoto/Math/CVector2f.hpp +++ b/include/Kyoto/Math/CVector2f.hpp @@ -11,6 +11,9 @@ public: float GetX() const { return mX; } float GetY() const { return mY; } + + void SetX(float x) { mX = x; } + void SetY(float y) { mY = y; } CVector2f& operator+=(const CVector2f& rhs); CVector2f& operator-=(const CVector2f& rhs); diff --git a/libc/math.h b/libc/math.h index 6057c1d..33c28b2 100644 --- a/libc/math.h +++ b/libc/math.h @@ -81,6 +81,8 @@ _MATH_INLINE float atan2f(float y, float x) { return (float)atan2((double)y, (do _MATH_INLINE float fmodf(float x, float m) { return (float)fmod((double)x, (double)m); } float tanf(float x); double acos(double x); + +double modf( double x, double * intptr ); _MATH_INLINE float acosf(float x) { return (float)acos((double)x); } double ldexp(double x, int exp); diff --git a/src/Kyoto/Math/CMayaSpline.cpp b/src/Kyoto/Math/CMayaSpline.cpp new file mode 100644 index 0000000..c3d1eb4 --- /dev/null +++ b/src/Kyoto/Math/CMayaSpline.cpp @@ -0,0 +1,460 @@ +#include "Kyoto/Math/CMayaSpline.hpp" + +#include "Kyoto/Math/CloseEnough.hpp" +#include "Kyoto/Streams/CInputStream.hpp" + + +#include "float.h" +#include "math.h" + +void ValidateTangent(CVector2f& tangent) { + if (tangent.GetX() < 0.f) { + tangent.SetX(0.f); + } + + const float mag = tangent.Magnitude(); + if (mag != 0.f) { + tangent /= mag; + } + + if (tangent.GetX() == 0.f && tangent.GetY() != 0.f) { + const float mul = tangent.GetY() >= 0.f ? 1.f : -1.f; + tangent.SetX(0.0001f); + tangent.SetY(5729578.0f * tangent.GetX() * mul); + } +} + +CMayaSplineKnot::CMayaSplineKnot(CInputStream& in) +: xc_cachedTangentA(0.0f, 0.0f), x14_cachedTangentB(0.0f, 0.0f) { + x0_time = in.ReadFloat(); + x4_amplitude = in.ReadFloat(); + x8_ = in.ReadChar(); + x9_ = in.ReadChar(); + if (x8_ == 5) { + float x = in.ReadFloat(); + float y = in.ReadFloat(); + xc_cachedTangentA = CVector2f(x, y); + } + + if (x9_ == 5) { + float x = in.ReadFloat(); + float y = in.ReadFloat(); + x14_cachedTangentB = CVector2f(x, y); + } +} + +void CMayaSplineKnot::GetTangents(CMayaSplineKnot* prev, CMayaSplineKnot* next, CVector2f& tangentA, + CVector2f& tangentB) { + if (xa_24_dirty) { + CalculateTangents(prev, next); + } + + tangentA = xc_cachedTangentA; + tangentB = x14_cachedTangentB; +} + +void CMayaSplineKnot::CalculateTangents(CMayaSplineKnot* prev, CMayaSplineKnot* next) { + xa_24_dirty = false; + bool calculateTangents = false; + if (x8_ == 4 && prev != nullptr) { + float fVar2 = abs(prev->GetAmplitude() - GetAmplitude()); + float fVar3 = fVar2; + if (next != nullptr) { + fVar3 = abs(next->GetAmplitude() - GetAmplitude()); + } + if (fVar3 <= 0.05f || fVar2 <= 0.05f) { + x8_ = 1; + } + } + + if (x8_ == 0) { + if (prev == nullptr) { + xc_cachedTangentA = CVector2f(1.f, 0.f); + } else { + xc_cachedTangentA = + CVector2f(GetTime() - prev->GetTime(), GetAmplitude() - prev->GetAmplitude()); + } + } else if (x8_ == 1) { + float fVar1 = 0.f; + if (prev != nullptr) { + fVar1 = GetTime() - prev->GetTime(); + } else if (next != nullptr) { + fVar1 = next->GetTime() - GetTime(); + } + xc_cachedTangentA = CVector2f(fVar1, 0.f); + } else if (x8_ == 2) { + calculateTangents = true; + } else if (x8_ == 3) { + xc_cachedTangentA = CVector2f(1.0f, 1.0f); + } else if (x8_ == 4) { + x8_ = 2; + calculateTangents = true; + } + + if (x9_ == 0) { + if (next == nullptr) { + x14_cachedTangentB = CVector2f(1.0f, 0.0f); + } else { + x14_cachedTangentB = + CVector2f(next->GetTime() - GetTime(), next->GetAmplitude() - GetAmplitude()); + } + } else if (x9_ == 1) { + float fVar1 = 0.f; + if (next != nullptr) { + fVar1 = next->GetTime() - GetTime(); + } else if (prev != nullptr) { + fVar1 = GetTime() - prev->GetTime(); + } + + x14_cachedTangentB = CVector2f(fVar1, 0.f); + } else if (x9_ == 2) { + calculateTangents = true; + } else if (x9_ == 3) { + x14_cachedTangentB = CVector2f(1.f, 0.f); + } else if (x9_ == 4 && next != nullptr) { + float fVar1 = next->GetAmplitude() - GetAmplitude(); + float fVar2 = fVar1; + if (prev != nullptr) { + fVar2 = prev->GetAmplitude() - GetAmplitude(); + } + + if (fVar1 <= 0.05f || fVar2 <= 0.05f) { + x9_ = 1; + } + calculateTangents = true; + } + + if (calculateTangents) { + CVector2f tangentA(0.0f, 0.0f); + CVector2f tangentB(0.0f, 0.0f); + + if (prev == nullptr && next != nullptr) { + tangentA = tangentB = + CVector2f(next->GetTime() - GetTime(), next->GetAmplitude() - GetAmplitude()); + } else if (prev != nullptr && next == nullptr) { + tangentA = tangentB = + CVector2f(GetTime() - prev->GetTime(), GetAmplitude() - prev->GetAmplitude()); + } else if (prev != nullptr && next != nullptr) { + float timeDiff = next->GetTime() - prev->GetTime(); + float ampDiff = next->GetAmplitude() - prev->GetAmplitude(); + float amp = + timeDiff >= 0.0001f ? ampDiff / timeDiff : (ampDiff <= 0.f ? -5729578.0f : 5729578.0f); + float nextTimeDiff = next->GetTime() - GetTime(); + float prevTimeDiff = GetTime() - prev->GetTime(); + float ampA = 0.f; + float ampB = 0.; + float timeA = 0.; + float timeB = 0.; + if (nextTimeDiff >= 0.f) { + ampA = nextTimeDiff * amp; + } else { + timeA = 0.f; + ampA = amp; + } + + if (prevTimeDiff >= 0.f) { + ampB = prevTimeDiff * amp; + } else { + timeB = 0.f; + } + tangentB = CVector2f(timeB, ampB); + tangentA = CVector2f(timeA, ampA); + + } else { + // TODO + // tangentA.zeroOut(); + // tangentB.zeroOut(); + } + + if (x8_ == 2) { + xc_cachedTangentA = tangentA; + } + if (x9_ == 2) { + x14_cachedTangentB = tangentB; + } + } + ValidateTangent(xc_cachedTangentA); + ValidateTangent(x14_cachedTangentB); +} + +// CMayaSpline::CMayaSpline(CInputStream& in, int count) : x0_preInfinity(in.ReadInt8()), +// x4_postInfinity(in.ReadInt8()) { + +// u32 knotCount = in.ReadLong(); +// x8_knots.reserve(knotCount); +// for (size_t i = 0; i < knotCount; ++i) { +// x8_knots.emplace_back(in); +// } +// x18_clampMode = in.ReadInt8(); +// x1c_minAmplitudeTime = in.ReadFloat(); +// x20_maxAmplitudeTime = in.ReadFloat(); +// } + +size_t CMayaSpline::GetKnotCount() const { return x8_knots.size(); } + +const rstl::vector< CMayaSplineKnot >& CMayaSpline::GetKnots() const { return x8_knots; } + +float CMayaSpline::GetMinTime() const { return x8_knots.empty() ? 0.f : x8_knots[0].GetTime(); } +float CMayaSpline::GetMaxTime() const { + return x8_knots.empty() ? 0.f : x8_knots[GetKnotCount() - 1].GetTime(); +} +float CMayaSpline::GetDuration() const { + return x8_knots.empty() ? 0.f : GetMaxTime() - GetMinTime(); +} + +float CMayaSpline::EvaluateAt(float time) { + float amplitude = EvaluateAtUnclamped(time); + if (x18_clampMode == 1) { + if (x1c_minAmplitudeTime > amplitude) { + return x1c_minAmplitudeTime; + } + if (x20_maxAmplitudeTime < amplitude) { + return x20_maxAmplitudeTime; + } + return amplitude; + } else if (x18_clampMode == 2) { + float center = x20_maxAmplitudeTime - x1c_minAmplitudeTime; + + if (center > 0.f) { + if (amplitude <= FLT_EPSILON + x20_maxAmplitudeTime) { + return amplitude - (center * static_cast< float >( + int((amplitude - x20_maxAmplitudeTime) / center) + 1)); + } + if (amplitude < x1c_minAmplitudeTime - FLT_EPSILON) { + return amplitude + (center * static_cast< float >( + abs(int((amplitude - x1c_minAmplitudeTime) / center)))); + } + } + } + + return amplitude; +} + +float CMayaSpline::EvaluateAtUnclamped(float time) { + if (x8_knots.empty()) { + return 0.f; + } + + u32 lastIdx = x8_knots.size() - 1; + bool bVar2 = false; + float retVal; + if (time < x8_knots[0].GetTime()) { + if (x0_preInfinity == 0) { + return x8_knots[0].GetAmplitude(); + } + return EvaluateInfinities(time, true); + } else if (x8_knots[lastIdx].GetTime() >= time) { + bVar2 = false; + int local_68 = -1; + int iVar1 = x24_chachedKnotIndex; + if (iVar1 != -1) { + if (lastIdx <= iVar1 || x8_knots[lastIdx].GetTime() >= time) { + if (iVar1 > 0 && x8_knots[iVar1].GetTime() > time) { + int iVar3 = iVar1 - 1; + bVar2 = x8_knots[iVar3].GetTime() < time; + if (bVar2) { + local_68 = iVar1; + } + if (x8_knots[iVar3].GetTime() == time) { + x24_chachedKnotIndex = iVar3; + return x8_knots[x24_chachedKnotIndex].GetAmplitude(); + } + } + } else { + retVal = x8_knots[iVar1 + 1].GetTime(); + if (retVal == time) { + x24_chachedKnotIndex = lastIdx; + return x8_knots[x24_chachedKnotIndex].GetAmplitude(); + } + + if (retVal > time) { + bVar2 = true; + local_68 = iVar1 + 1; + } + } + } + + if (!bVar2 && (FindKnot(time, local_68))) { + if (local_68 == 0) { + x24_chachedKnotIndex = 0; + return x8_knots[0].GetAmplitude(); + } + if (local_68 == x8_knots.size()) { + x24_chachedKnotIndex = 0; + return x8_knots[lastIdx].GetAmplitude(); + } + } + + lastIdx = local_68 - 1; + if (x28_ != lastIdx) { + x24_chachedKnotIndex = lastIdx; + x28_ = lastIdx; + if (x8_knots[x24_chachedKnotIndex].GetX9() == 3) { + x2c_24_dirty = true; + } else { + x2c_24_dirty = false; + rstl::reserved_vector< CVector2f, 4 > points; + FindControlPoints(x24_chachedKnotIndex, points); + CalculateHermiteCoefficients(points, x34_cachedHermitCoefs); + x30_cachedMinTime = points[0].GetX(); + } + } + + if (x2c_24_dirty) { + return x8_knots[x24_chachedKnotIndex].GetTime(); + } else { + return EvaluateHermite(time); + } + } + + if (x4_postInfinity == 0) { + return x8_knots[lastIdx].GetAmplitude(); + } + + return EvaluateInfinities(time, false); +} + +float CMayaSpline::EvaluateInfinities(float time, bool pre) { + if (x8_knots.empty()) { + return 0.f; + } + + int lastIdx = x8_knots.size() - 1; + CMayaSplineKnot* curKnot = &x8_knots[0]; + const float startTime = x8_knots[0].GetTime(); + const float endTime = x8_knots[lastIdx].GetAmplitude(); + float center = endTime - startTime; + + if (close_enough(center, 0)) { + return curKnot->GetAmplitude(); + } + + double tmp = 0.f; + float divTime = (time <= endTime) ? modf((time - startTime) / center, &tmp) + : modf((time - endTime) / center, &tmp); + + center = center * abs(divTime); + tmp = 1.f + abs(tmp); + + if (!pre) { + if (x4_postInfinity == 4) { + divTime = fmod(tmp, 2.f); + if (close_enough(divTime, 0.f)) { + center = startTime + center; + } else { + center = endTime - center; + } + } else if (x4_postInfinity == 2 || x4_postInfinity == 3) { + center = startTime + center; + } else if (x4_postInfinity == 1) { + center = time - endTime; + CVector2f tangentA(0.0f, 0.0f); + CVector2f tangentB(0.0f, 0.0f); + x8_knots[0].GetTangents((lastIdx < 1) ? nullptr : &x8_knots[lastIdx - 2], nullptr, tangentA, + tangentB); + if (!close_enough(tangentB.GetX(), 0.f)) { + return x8_knots[lastIdx].GetAmplitude() + (center * tangentB.GetY() / tangentB.GetX()); + } + return x8_knots[lastIdx].GetAmplitude(); + } + } else if (x0_preInfinity == 4) { + divTime = fmod(tmp, 2.f); + if (close_enough(divTime, 0.f)) { + center = endTime - center; + } else { + center = startTime + center; + } + } else if (x0_preInfinity == 2 || x0_preInfinity == 3) { + center = endTime - center; + } else if (x0_preInfinity == 1) { + center = (startTime - time); + CVector2f tangentA(0.0f, 0.0f); + CVector2f tangentB(0.0f, 0.0f); + x8_knots[0].GetTangents(nullptr, &x8_knots[1], tangentA, tangentB); + if (!close_enough(tangentA.GetX(), 0)) { + return (x8_knots[0].GetAmplitude() - (center * tangentA.GetY() / tangentA.GetX())); + } + return x8_knots[0].GetAmplitude(); + } + + float eval = EvaluateAt(center); + if (pre && x0_preInfinity == 3) { + return eval - (tmp * x8_knots[lastIdx].GetAmplitude() - x8_knots[0].GetAmplitude()); + } + + if (!pre && x4_postInfinity == 3) { + return eval + (tmp * x8_knots[lastIdx].GetAmplitude() - x8_knots[0].GetAmplitude()); + } + return eval; +} + +float CMayaSpline::EvaluateHermite(float time) { + const float timeDiff = time - x30_cachedMinTime; + return x34_cachedHermitCoefs[0] + (timeDiff * x34_cachedHermitCoefs[1]) + + (timeDiff * x34_cachedHermitCoefs[2]) + (timeDiff * x34_cachedHermitCoefs[3]); +} + +bool CMayaSpline::FindKnot(float time, int& knotIndex) { + if (x8_knots.empty()) { + return false; + } + + u32 lower = 0; + u32 upper = x8_knots.size(); + while (lower < upper) { + u32 index = (lower + upper) / 2; + const CMayaSplineKnot& knot = x8_knots[index]; + if (knot.GetTime() > time) { + upper = index - 1; + } else if (time > knot.GetTime()) { + lower = index + 1; + } else { + knotIndex = index; + return true; + } + } + + knotIndex = lower; + return false; +} + +void CMayaSpline::FindControlPoints(int knotIndex, + rstl::reserved_vector< CVector2f, 4 >& controlPoints) { + CMayaSplineKnot* knot = &x8_knots[knotIndex]; + controlPoints.push_back(CVector2f(knot->GetTime(), knot->GetAmplitude())); + + CVector2f tangentA(0.0f, 0.0f); + CVector2f tangentB(0.0f, 0.0f); + CMayaSplineKnot* next = (knotIndex + 1 < x8_knots.size()) ? &x8_knots[knotIndex + 1] : nullptr; + CMayaSplineKnot* prev = (knotIndex - 1 >= 0) ? &x8_knots[knotIndex - 1] : nullptr; + knot->GetTangents(prev, next, tangentA, tangentB); + + knot = &x8_knots[knotIndex + 1]; + controlPoints.push_back(CVector2f(controlPoints[0] + (tangentB * 1.f / 3.f))); + next = (knotIndex + 2 < x8_knots.size()) ? &x8_knots[knotIndex + 2] : nullptr; + prev = (knotIndex - 2 >= 0) ? &x8_knots[knotIndex - 2] : nullptr; + knot->GetTangents(prev, next, tangentA, tangentB); + CVector2f knotV(knot->GetTime(), knot->GetAmplitude()); + controlPoints.push_back(CVector2f(knotV - (tangentA * 1.f / 3.f))); + controlPoints.push_back(knotV); +} + +void CMayaSpline::CalculateHermiteCoefficients( + const rstl::reserved_vector< CVector2f, 4 >& controlPoints, float* coefs) { + const CVector2f point1 = controlPoints[3] - controlPoints[0]; + const CVector2f point2 = controlPoints[1] - controlPoints[0]; + const CVector2f point3 = controlPoints[3] - controlPoints[2]; + float dVar7 = point2.GetX() != 0.f ? point2.GetY() / point2.GetX() : 5729578.0f; + float dVar6 = point3.GetX() != 0.f ? point3.GetY() / point3.GetX() : 5729578.0f; + const float point1XSq = point1.GetX() * point1.GetX(); + coefs[0] = + ((1.f / (point1XSq)) * + (((dVar7 * point1.GetX()) + (dVar6 * point1.GetX()) - point1.GetY()) - point1.GetY())) / + point1.GetX(); + coefs[1] = ((1.f / (point1XSq)) * + ((((point1.GetY() + (point1.GetY() + point1.GetY())) - (dVar7 * point1.GetX())) - + (dVar7 * point1.GetX())) - + (dVar6 * point1.GetX()))); + coefs[2] = dVar7; + coefs[3] = controlPoints[0].GetY(); +}