mirror of https://github.com/axmolengine/axmol.git
292 lines
8.5 KiB
C++
292 lines
8.5 KiB
C++
#include "GPath.h"
|
|
|
|
NS_FGUI_BEGIN
|
|
USING_NS_AX;
|
|
|
|
static std::vector<ax::Vec3> splinePoints;
|
|
|
|
GPathPoint::GPathPoint(const Vec3& pos)
|
|
{
|
|
this->pos = pos;
|
|
this->control1 = Vec3::ZERO;
|
|
this->control2 = Vec3::ZERO;
|
|
this->curveType = CurveType::CRSpline;
|
|
}
|
|
|
|
GPathPoint::GPathPoint(const Vec3& pos, const Vec3& control)
|
|
{
|
|
this->pos = pos;
|
|
this->control1 = control;
|
|
this->control2 = Vec3::ZERO;
|
|
this->curveType = CurveType::Bezier;
|
|
}
|
|
|
|
GPathPoint::GPathPoint(const Vec3& pos, const Vec3& control1, const Vec3& control2)
|
|
{
|
|
this->pos = pos;
|
|
this->control1 = control1;
|
|
this->control2 = control2;
|
|
this->curveType = CurveType::CubicBezier;
|
|
}
|
|
|
|
GPathPoint::GPathPoint(const Vec3& pos, CurveType curveType)
|
|
{
|
|
this->pos = pos;
|
|
this->control1 = Vec3::ZERO;
|
|
this->control2 = Vec3::ZERO;
|
|
this->curveType = curveType;
|
|
}
|
|
|
|
GPath::GPath()
|
|
: _fullLength(0)
|
|
{
|
|
}
|
|
|
|
void GPath::create(GPathPoint* points, int count)
|
|
{
|
|
_segments.clear();
|
|
_points.clear();
|
|
splinePoints.clear();
|
|
_fullLength = 0;
|
|
|
|
if (count == 0)
|
|
return;
|
|
|
|
const GPathPoint* prev = points;
|
|
if (prev->curveType == GPathPoint::CurveType::CRSpline)
|
|
splinePoints.push_back(prev->pos);
|
|
|
|
for (int i = 1; i < count; i++)
|
|
{
|
|
const GPathPoint* current = points + i;
|
|
|
|
if (prev->curveType != GPathPoint::CurveType::CRSpline)
|
|
{
|
|
Segment seg;
|
|
seg.type = prev->curveType;
|
|
seg.ptStart = (int)_points.size();
|
|
if (prev->curveType == GPathPoint::CurveType::Straight)
|
|
{
|
|
seg.ptCount = 2;
|
|
_points.push_back(prev->pos);
|
|
_points.push_back(current->pos);
|
|
}
|
|
else if (prev->curveType == GPathPoint::CurveType::Bezier)
|
|
{
|
|
seg.ptCount = 3;
|
|
_points.push_back(prev->pos);
|
|
_points.push_back(current->pos);
|
|
_points.push_back(prev->control1);
|
|
}
|
|
else if (prev->curveType == GPathPoint::CurveType::CubicBezier)
|
|
{
|
|
seg.ptCount = 4;
|
|
_points.push_back(prev->pos);
|
|
_points.push_back(current->pos);
|
|
_points.push_back(prev->control1);
|
|
_points.push_back(prev->control2);
|
|
}
|
|
seg.length = prev->pos.distance(current->pos);
|
|
_fullLength += seg.length;
|
|
_segments.push_back(seg);
|
|
}
|
|
|
|
if (current->curveType != GPathPoint::CurveType::CRSpline)
|
|
{
|
|
if (splinePoints.size() > 0)
|
|
{
|
|
splinePoints.push_back(current->pos);
|
|
createSplineSegment();
|
|
}
|
|
}
|
|
else
|
|
splinePoints.push_back(current->pos);
|
|
|
|
prev = current;
|
|
}
|
|
|
|
if (splinePoints.size() > 1)
|
|
createSplineSegment();
|
|
}
|
|
|
|
void GPath::createSplineSegment()
|
|
{
|
|
int cnt = (int)splinePoints.size();
|
|
splinePoints.insert(splinePoints.begin(), splinePoints[0]);
|
|
splinePoints.push_back(splinePoints[cnt]);
|
|
splinePoints.push_back(splinePoints[cnt]);
|
|
cnt += 3;
|
|
|
|
Segment seg;
|
|
seg.type = GPathPoint::CurveType::CRSpline;
|
|
seg.ptStart = (int)_points.size();
|
|
seg.ptCount = cnt;
|
|
for (auto& it : splinePoints)
|
|
_points.push_back(it);
|
|
|
|
seg.length = 0;
|
|
for (int i = 1; i < cnt; i++)
|
|
seg.length += splinePoints[i - 1].distance(splinePoints[i]);
|
|
_fullLength += seg.length;
|
|
_segments.push_back(seg);
|
|
splinePoints.clear();
|
|
}
|
|
|
|
void GPath::clear()
|
|
{
|
|
_segments.clear();
|
|
_points.clear();
|
|
}
|
|
|
|
Vec3 GPath::getPointAt(float t)
|
|
{
|
|
t = clampf(t, 0, 1);
|
|
int cnt = (int)_segments.size();
|
|
if (cnt == 0)
|
|
return Vec3::ZERO;
|
|
|
|
Segment seg;
|
|
if (t == 1)
|
|
{
|
|
seg = _segments[cnt - 1];
|
|
|
|
if (seg.type == GPathPoint::CurveType::Straight)
|
|
return _points[seg.ptStart].lerp(_points[seg.ptStart + 1], t);
|
|
else if (seg.type == GPathPoint::CurveType::Bezier || seg.type == GPathPoint::CurveType::CubicBezier)
|
|
return onBezierCurve(seg.ptStart, seg.ptCount, t);
|
|
else
|
|
return onCRSplineCurve(seg.ptStart, seg.ptCount, t);
|
|
}
|
|
|
|
float len = t * _fullLength;
|
|
Vec3 pt;
|
|
for (int i = 0; i < cnt; i++)
|
|
{
|
|
seg = _segments[i];
|
|
|
|
len -= seg.length;
|
|
if (len < 0)
|
|
{
|
|
t = 1 + len / seg.length;
|
|
|
|
if (seg.type == GPathPoint::CurveType::Straight)
|
|
pt = _points[seg.ptStart].lerp(_points[seg.ptStart + 1], t);
|
|
else if (seg.type == GPathPoint::CurveType::Bezier || seg.type == GPathPoint::CurveType::CubicBezier)
|
|
pt = onBezierCurve(seg.ptStart, seg.ptCount, t);
|
|
else
|
|
pt = onCRSplineCurve(seg.ptStart, seg.ptCount, t);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pt;
|
|
}
|
|
|
|
float GPath::getSegmentLength(int segmentIndex)
|
|
{
|
|
return _segments[segmentIndex].length;
|
|
}
|
|
|
|
void GPath::getPointsInSegment(int segmentIndex, float t0, float t1,
|
|
std::vector<ax::Vec3>& points, std::vector<float>* ts, float pointDensity)
|
|
{
|
|
if (ts != nullptr)
|
|
ts->push_back(t0);
|
|
Segment seg = _segments[segmentIndex];
|
|
if (seg.type == GPathPoint::CurveType::Straight)
|
|
{
|
|
points.push_back(_points[seg.ptStart].lerp(_points[seg.ptStart + 1], t0));
|
|
points.push_back(_points[seg.ptStart].lerp(_points[seg.ptStart + 1], t1));
|
|
}
|
|
else if (seg.type == GPathPoint::CurveType::Bezier || seg.type == GPathPoint::CurveType::CubicBezier)
|
|
{
|
|
points.push_back(onBezierCurve(seg.ptStart, seg.ptCount, t0));
|
|
int SmoothAmount = (int)MIN(seg.length * pointDensity, 50);
|
|
for (int j = 0; j <= SmoothAmount; j++)
|
|
{
|
|
float t = (float)j / SmoothAmount;
|
|
if (t > t0 && t < t1)
|
|
{
|
|
points.push_back(onBezierCurve(seg.ptStart, seg.ptCount, t));
|
|
if (ts != nullptr)
|
|
ts->push_back(t);
|
|
}
|
|
}
|
|
points.push_back(onBezierCurve(seg.ptStart, seg.ptCount, t1));
|
|
}
|
|
else
|
|
{
|
|
points.push_back(onCRSplineCurve(seg.ptStart, seg.ptCount, t0));
|
|
int SmoothAmount = (int)MIN(seg.length * pointDensity, 50);
|
|
for (int j = 0; j <= SmoothAmount; j++)
|
|
{
|
|
float t = (float)j / SmoothAmount;
|
|
if (t > t0 && t < t1)
|
|
{
|
|
points.push_back(onCRSplineCurve(seg.ptStart, seg.ptCount, t));
|
|
if (ts != nullptr)
|
|
ts->push_back(t);
|
|
}
|
|
}
|
|
points.push_back(onCRSplineCurve(seg.ptStart, seg.ptCount, t1));
|
|
}
|
|
|
|
if (ts != nullptr)
|
|
ts->push_back(t1);
|
|
}
|
|
|
|
void GPath::getAllPoints(std::vector<ax::Vec3>& points, float pointDensity)
|
|
{
|
|
int cnt = (int)_segments.size();
|
|
for (int i = 0; i < cnt; i++)
|
|
getPointsInSegment(i, 0, 1, points, nullptr, pointDensity);
|
|
}
|
|
|
|
static float repeat(float t, float length)
|
|
{
|
|
return t - floor(t / length) * length;
|
|
}
|
|
|
|
Vec3 GPath::onCRSplineCurve(int ptStart, int ptCount, float t)
|
|
{
|
|
int adjustedIndex = floor(t * (ptCount - 4)) + ptStart; //Since the equation works with 4 points, we adjust the starting point depending on t to return a point on the specific segment
|
|
|
|
Vec3 result;
|
|
|
|
Vec3 p0 = _points[adjustedIndex];
|
|
Vec3 p1 = _points[adjustedIndex + 1];
|
|
Vec3 p2 = _points[adjustedIndex + 2];
|
|
Vec3 p3 = _points[adjustedIndex + 3];
|
|
|
|
float adjustedT = (t == 1.f) ? 1.f : repeat(t * (ptCount - 4), 1.f); // Then we adjust t to be that value on that new piece of segment... for t == 1f don't use repeat (that would return 0f);
|
|
|
|
float t0 = ((-adjustedT + 2.f) * adjustedT - 1.f) * adjustedT * 0.5f;
|
|
float t1 = (((3.f * adjustedT - 5.f) * adjustedT) * adjustedT + 2.f) * 0.5f;
|
|
float t2 = ((-3.f * adjustedT + 4.f) * adjustedT + 1.f) * adjustedT * 0.5f;
|
|
float t3 = ((adjustedT - 1.f) * adjustedT * adjustedT) * 0.5f;
|
|
|
|
result.x = p0.x * t0 + p1.x * t1 + p2.x * t2 + p3.x * t3;
|
|
result.y = p0.y * t0 + p1.y * t1 + p2.y * t2 + p3.y * t3;
|
|
result.z = p0.z * t0 + p1.z * t1 + p2.z * t2 + p3.z * t3;
|
|
|
|
return result;
|
|
}
|
|
|
|
Vec3 GPath::onBezierCurve(int ptStart, int ptCount, float t)
|
|
{
|
|
float t2 = 1.0f - t;
|
|
Vec3 p0 = _points[ptStart];
|
|
Vec3 p1 = _points[ptStart + 1];
|
|
Vec3 cp0 = _points[ptStart + 2];
|
|
|
|
if (ptCount == 4)
|
|
{
|
|
Vec3 cp1 = _points[ptStart + 3];
|
|
return t2 * t2 * t2 * p0 + 3.f * t2 * t2 * t * cp0 + 3.f * t2 * t * t * cp1 + t * t * t * p1;
|
|
}
|
|
else
|
|
return t2 * t2 * p0 + 2.f * t2 * t * cp0 + t * t * p1;
|
|
}
|
|
|
|
NS_FGUI_END |