2019-11-23 20:27:39 +08:00
|
|
|
/****************************************************************************
|
|
|
|
Copyright (C) 2013 Henry van Merode. All rights reserved.
|
|
|
|
Copyright (c) 2015-2016 Chukong Technologies Inc.
|
|
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2022-10-01 16:24:52 +08:00
|
|
|
https://axmolengine.github.io/
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
THE SOFTWARE.
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "CCPUSimpleSpline.h"
|
|
|
|
#include "base/ccMacros.h"
|
|
|
|
|
2022-07-11 17:50:21 +08:00
|
|
|
NS_AX_BEGIN
|
2019-11-23 20:27:39 +08:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
PUSimpleSpline::PUSimpleSpline()
|
|
|
|
{
|
|
|
|
// Set up matrix
|
|
|
|
// Hermite polynomial
|
2021-12-25 10:04:45 +08:00
|
|
|
_coeffs.m[0] = 2;
|
|
|
|
_coeffs.m[1] = -2;
|
|
|
|
_coeffs.m[2] = 1;
|
|
|
|
_coeffs.m[3] = 1;
|
|
|
|
_coeffs.m[4] = -3;
|
|
|
|
_coeffs.m[5] = 3;
|
|
|
|
_coeffs.m[6] = -2;
|
|
|
|
_coeffs.m[7] = -1;
|
|
|
|
_coeffs.m[8] = 0;
|
|
|
|
_coeffs.m[9] = 0;
|
2019-11-23 20:27:39 +08:00
|
|
|
_coeffs.m[10] = 1;
|
|
|
|
_coeffs.m[11] = 0;
|
|
|
|
_coeffs.m[12] = 1;
|
|
|
|
_coeffs.m[13] = 0;
|
|
|
|
_coeffs.m[14] = 0;
|
|
|
|
_coeffs.m[15] = 0;
|
|
|
|
|
|
|
|
_autoCalc = true;
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
2021-12-25 10:04:45 +08:00
|
|
|
PUSimpleSpline::~PUSimpleSpline() {}
|
2019-11-23 20:27:39 +08:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
void PUSimpleSpline::addPoint(const Vec3& p)
|
|
|
|
{
|
2022-08-09 09:54:53 +08:00
|
|
|
_points.emplace_back(p);
|
2019-11-23 20:27:39 +08:00
|
|
|
if (_autoCalc)
|
|
|
|
{
|
|
|
|
recalcTangents();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
Vec3 PUSimpleSpline::interpolate(float t) const
|
|
|
|
{
|
|
|
|
// Currently assumes points are evenly spaced, will cause velocity
|
|
|
|
// change where this is not the case
|
|
|
|
// TODO: base on arclength?
|
|
|
|
|
|
|
|
// Work out which segment this is in
|
2021-12-25 10:04:45 +08:00
|
|
|
float fSeg = t * (_points.size() - 1);
|
2019-11-23 20:27:39 +08:00
|
|
|
unsigned int segIdx = (unsigned int)fSeg;
|
2021-12-25 10:04:45 +08:00
|
|
|
// Apportion t
|
2019-11-23 20:27:39 +08:00
|
|
|
t = fSeg - segIdx;
|
|
|
|
|
|
|
|
return interpolate(segIdx, t);
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
Vec3 PUSimpleSpline::interpolate(unsigned int fromIndex, float t) const
|
|
|
|
{
|
|
|
|
// Bounds check
|
2022-07-16 10:43:05 +08:00
|
|
|
AXASSERT(fromIndex < _points.size(), "fromIndex out of bounds");
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
if ((fromIndex + 1) == _points.size())
|
|
|
|
{
|
|
|
|
// Duff request, cannot blend to nothing
|
|
|
|
// Just return source
|
|
|
|
return _points[fromIndex];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fast special cases
|
|
|
|
if (t == 0.0f)
|
|
|
|
{
|
|
|
|
return _points[fromIndex];
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
else if (t == 1.0f)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
return _points[fromIndex + 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Real interpolation
|
|
|
|
// Form a vector of powers of t
|
|
|
|
float t2, t3;
|
|
|
|
t2 = t * t;
|
|
|
|
t3 = t2 * t;
|
|
|
|
Vec4 powers(t3, t2, t, 1);
|
|
|
|
|
|
|
|
// Algorithm is ret = powers * mCoeffs * Matrix4(point1, point2, tangent1, tangent2)
|
|
|
|
const Vec3& point1 = _points[fromIndex];
|
2021-12-25 10:04:45 +08:00
|
|
|
const Vec3& point2 = _points[fromIndex + 1];
|
|
|
|
const Vec3& tan1 = _tangents[fromIndex];
|
|
|
|
const Vec3& tan2 = _tangents[fromIndex + 1];
|
2019-11-23 20:27:39 +08:00
|
|
|
Mat4 pt;
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
pt.m[0] = point1.x;
|
|
|
|
pt.m[1] = point1.y;
|
|
|
|
pt.m[2] = point1.z;
|
|
|
|
pt.m[3] = 1.0f;
|
|
|
|
pt.m[4] = point2.x;
|
|
|
|
pt.m[5] = point2.y;
|
|
|
|
pt.m[6] = point2.z;
|
|
|
|
pt.m[7] = 1.0f;
|
|
|
|
pt.m[8] = tan1.x;
|
|
|
|
pt.m[9] = tan1.y;
|
2019-11-23 20:27:39 +08:00
|
|
|
pt.m[10] = tan1.z;
|
|
|
|
pt.m[11] = 1.0f;
|
|
|
|
pt.m[12] = tan2.x;
|
|
|
|
pt.m[13] = tan2.y;
|
|
|
|
pt.m[14] = tan2.z;
|
|
|
|
pt.m[15] = 1.0f;
|
|
|
|
|
|
|
|
Vec4 ret = pt * _coeffs * powers;
|
|
|
|
|
|
|
|
return Vec3(ret.x, ret.y, ret.z);
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
void PUSimpleSpline::recalcTangents()
|
|
|
|
{
|
|
|
|
// Catmull-Rom approach
|
2021-12-25 10:04:45 +08:00
|
|
|
//
|
2019-11-23 20:27:39 +08:00
|
|
|
// tangent[i] = 0.5 * (point[i+1] - point[i-1])
|
|
|
|
//
|
|
|
|
// Assume endpoint tangents are parallel with line with neighbour
|
|
|
|
|
|
|
|
size_t i, numPoints;
|
|
|
|
bool isClosed;
|
|
|
|
|
|
|
|
numPoints = _points.size();
|
|
|
|
if (numPoints < 2)
|
|
|
|
{
|
|
|
|
// Can't do anything yet
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Closed or open?
|
2021-12-25 10:04:45 +08:00
|
|
|
if (_points[0] == _points[numPoints - 1])
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
isClosed = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
isClosed = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
_tangents.resize(numPoints);
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
for (i = 0; i < numPoints; ++i)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
if (i == 0)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
// Special case start
|
|
|
|
if (isClosed)
|
|
|
|
{
|
|
|
|
// Use numPoints-2 since numPoints-1 is the last point and == [0]
|
2021-12-25 10:04:45 +08:00
|
|
|
_tangents[i] = 0.5 * (_points[1] - _points[numPoints - 2]);
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_tangents[i] = 0.5 * (_points[1] - _points[0]);
|
|
|
|
}
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
else if (i == numPoints - 1)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
// Special case end
|
|
|
|
if (isClosed)
|
|
|
|
{
|
|
|
|
// Use same tangent as already calculated for [0]
|
|
|
|
_tangents[i] = _tangents[0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
_tangents[i] = 0.5 * (_points[i] - _points[i - 1]);
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
_tangents[i] = 0.5 * (_points[i + 1] - _points[i - 1]);
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
const Vec3& PUSimpleSpline::getPoint(unsigned short index) const
|
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXASSERT(index < _points.size(), "Point index is out of bounds!!");
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
return _points[index];
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
unsigned short PUSimpleSpline::getNumPoints() const
|
|
|
|
{
|
|
|
|
return (unsigned short)_points.size();
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
void PUSimpleSpline::clear()
|
|
|
|
{
|
|
|
|
_points.clear();
|
|
|
|
_tangents.clear();
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
void PUSimpleSpline::updatePoint(unsigned short index, const Vec3& value)
|
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXASSERT(index < _points.size(), "Point index is out of bounds!!");
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
_points[index] = value;
|
|
|
|
if (_autoCalc)
|
|
|
|
{
|
|
|
|
recalcTangents();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
void PUSimpleSpline::setAutoCalculate(bool autoCalc)
|
|
|
|
{
|
|
|
|
_autoCalc = autoCalc;
|
|
|
|
}
|
2022-07-11 17:50:21 +08:00
|
|
|
NS_AX_END
|