struct DistanceTableEntry
{
f32 t;
f32 distance;
};
//The base Catmull-Rom evaluation function which requires 4 points
void CatmullRom_Evaluate(vec3* pOut_result, const vec3* p0, const vec3* p1, const vec3* p2, const vec3* p3, f32 t)
{
const f32 c0 = ((-t + 2.0f) * t - 1.0f) * t * 0.5f;
const f32 c1 = (((3.0f * t - 5.0f) * t) * t + 2.0f) * 0.5f;
const f32 c2 = ((-3.0f * t + 4.0f) * t + 1.0f) * t * 0.5f;
const f32 c3 = ((t - 1.0f) * t * t) * 0.5f;
ScaleVec3(pOut_result, p0, c0);
AddScaledVec3_Self(pOut_result, p1, c1);
AddScaledVec3_Self(pOut_result, p2, c2);
AddScaledVec3_Self(pOut_result, p3, c3);
}
//Evaluates a curve with any number of points using the Catmull-Rom method
void CatmullRom_EvaluateCurve(vec3* pOut_result, const vec3* pPoints, u32 numPoints, f32 t)
{
const f32 index1F = t * (f32)(numPoints-1);
const u32 index1 = (u32)index1F;
const f32 remainder = index1F - (f32)index1;
const u32 index0 = MaxS32(0,index1 - 1);
const u32 index2 = index1 + 1;
const u32 index3 = MinS32(numPoints-1,index1 + 2);
const vec3* p0 = &pPoints[index0];
const vec3* p1 = &pPoints[index1];
const vec3* p2 = &pPoints[index2];
const vec3* p3 = &pPoints[index3];
CatmullRom_Evaluate(pOut_result, p0, p1, p2, p3, remainder);
}
//Creates the specified number of points along a Catmull-Rom spline (non-uniform)
void CatmullRom_CreatePoints(vec3* pOut_pointArray, u32 numPointsDesired, const vec3* pCurvePoints, u32 numCurvePoints)
{
const f32 numPointsDesiredMin1 = (f32)(numPointsDesired-1);
for(u32 i=0; i<numPointsDesired; ++i)
{
const f32 t = (f32)i/numPointsDesiredMin1;
CatmullRom_EvaluateCurve(&pOut_pointArray[i],pCurvePoints,numCurvePoints,t);
}
}
//Creates the specified number of points along a Catmull-Rom spline (uniformly spaced)
void CatmullRom_CreatePoints_Uniform(vec3* pOut_pointArray, u32 numPointsDesired, const vec3* pCurvePoints, u32 numCurvePoints, DistanceTableEntry* pDistTable, u32 numDistTableEntries)
{
const f32 numPointsDesiredMin1 = (f32)(numPointsDesired-1);
const f32 totalLength = pDistTable[numDistTableEntries-1].distance;
for(u32 i=0; i<numPointsDesired; ++i)
{
const f32 distT = (f32)i/(f32)numPointsDesiredMin1;
const f32 distance = distT * totalLength;
const f32 t = Curve_TValueFromDist(distance,pDistTable,numDistTableEntries);
CatmullRom_EvaluateCurve(&pOut_pointArray[i],pCurvePoints,numCurvePoints,t);
}
}
//Creates a distance table used by Curve_TValueFromDist below
void Curve_CreateDistanceTable(DistanceTableEntry* pOut_result, const vec3* pPoints, u32 numPoints)
{
const u32 numPointsMin1 = numPoints-1;
f32 distSoFar = 0.0f;
pOut_result[0].distance = 0.0f;
pOut_result[0].t = 0.0f;
for(u32 i=1; i<numPoints; ++i)
{
const f32 dist = DistVec3(&pPoints[i-1], &pPoints[i]);
distSoFar += dist;
DistanceTableEntry* pEntry = &pOut_result[i];
pEntry->distance = distSoFar;
pEntry->t = (f32)i/(f32)numPointsMin1;
}
}
//When evaluating a spline, pass in a dist (0 to max) to get a T value to pass in
f32 Curve_TValueFromDist(f32 dist, DistanceTableEntry* pDistTable, u32 numDistTableEntries)
{
for(s32 i=numDistTableEntries-2; i != -1; --i)
{
DistanceTableEntry* pEntry = &pDistTable[i];
if(dist >= pEntry->distance)
{
if(i == numDistTableEntries-1)
{
return 1.0f;
}
else
{
DistanceTableEntry* pNextEntry = &pDistTable[i+1];
const f32 lerpT = (dist-pEntry->distance)/(pNextEntry->distance-pEntry->distance);
const f32 t = Lerp(pEntry->t, pNextEntry->t, lerpT);
return t;
}
}
}
return 0.0f;
}