Merge pull request #375 from Areloch/LoopmodePathManager

Adds handling to the path manager so it can deal with both looping and non-looping paths
This commit is contained in:
Brian Roberts 2020-12-21 03:05:00 -06:00 committed by GitHub
commit e9dba74891
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 128 additions and 12 deletions

View file

@ -102,6 +102,7 @@ void PathManagerEvent::pack(NetConnection*, BitStream* stream)
stream->write(modifiedPath);
stream->writeFlag(clearPaths);
stream->write(path.totalTime);
stream->write(path.looping);
stream->write(path.positions.size());
@ -131,6 +132,7 @@ void PathManagerEvent::unpack(NetConnection*, BitStream* stream)
stream->read(&modifiedPath);
clearPaths = stream->readFlag();
stream->read(&path.totalTime);
stream->read(&path.looping);
U32 numPoints;
stream->read(&numPoints);
@ -231,7 +233,8 @@ void PathManager::updatePath(const U32 id,
const Vector<Point3F>& positions,
const Vector<QuatF>& rotations,
const Vector<U32>& times,
const Vector<U32>& smoothingTypes)
const Vector<U32>& smoothingTypes,
const bool looping)
{
AssertFatal(mIsServer == true, "PathManager::updatePath: Error, must be called on the server side");
AssertFatal(id < mPaths.size(), "PathManager::updatePath: error, id out of range");
@ -243,6 +246,7 @@ void PathManager::updatePath(const U32 id,
rEntry.rotations = rotations;
rEntry.msToNext = times;
rEntry.smoothingType = smoothingTypes;
rEntry.looping = looping;
rEntry.totalTime = 0;
for (S32 i = 0; i < S32(rEntry.msToNext.size()); i++)
@ -299,15 +303,33 @@ void PathManager::getPathPosition(const U32 id,
// Ok, query holds our path information...
F64 ms = msPosition;
if (ms > mPaths[id]->totalTime)
ms = mPaths[id]->totalTime;
//Looping vs. non-looping splines
F32 msTotal;
if (mPaths[id]->looping)
msTotal = mPaths[id]->totalTime;
else
//total time minus last nodes time
msTotal = mPaths[id]->totalTime - mPaths[id]->msToNext[mPaths[id]->msToNext.size() - 1];
if (ms > msTotal)
ms = msTotal;
S32 startNode = 0;
while (ms > mPaths[id]->msToNext[startNode]) {
ms -= mPaths[id]->msToNext[startNode];
startNode++;
}
S32 endNode = (startNode + 1) % mPaths[id]->positions.size();
S32 endNode;
//Looping splines
if (mPaths[id]->looping)
endNode = (startNode + 1) % mPaths[id]->positions.size();
//Non-looping splines
else
endNode = getMin(startNode + 1, mPaths[id]->positions.size() - 1);
Point3F& rStart = mPaths[id]->positions[startNode];
Point3F& rEnd = mPaths[id]->positions[endNode];
@ -326,10 +348,24 @@ void PathManager::getPathPosition(const U32 id,
{
S32 preStart = startNode - 1;
S32 postEnd = endNode + 1;
if(postEnd >= mPaths[id]->positions.size())
postEnd = 0;
if(preStart < 0)
preStart = mPaths[id]->positions.size() - 1;
//Looping splines
if (mPaths[id]->looping)
{
if (postEnd >= mPaths[id]->positions.size())
postEnd = 0;
if (preStart < 0)
preStart = mPaths[id]->positions.size() - 1;
}
//Non-looping splines
else
{
if (postEnd >= mPaths[id]->positions.size())
postEnd = mPaths[id]->positions.size() - 1;
if (preStart < 0)
preStart = 0;
}
Point3F p0 = mPaths[id]->positions[preStart];
Point3F p1 = rStart;
Point3F p2 = rEnd;
@ -430,5 +466,83 @@ bool PathManager::readState(BitStream* stream)
return stream->getStatus() == Stream::Ok;
}
F64 PathManager::getClosestTimeToPoint(const U32 id, const Point3F p)
{
//Ubiq: Ideally this algorithm would work directly by finding roots. However, it's a 5th order
//polynomial (cannot be solved by radicals), so we're doing it iteratively instead!
//Steps:
//1) Termination condition: if the segment is shorter than L, return the midpoint
//2) Otherwise, divide the spline-segment into S sub-segments (S+1 points to test)
//2) Test these points against the given point and find the closest of them
//4) Recurse within the 2 segments surrounding the closest point
//NOTE: In a case where the spline comes near the point multiple times, the wrong local
//minima of the spline may be chosen early on and then refined, like polishing a turd :)
PROFILE_START(PathManager_getClosestTimeToPoint);
F64 totalTime;
if (mPaths[id]->looping)
totalTime = mPaths[id]->totalTime;
else
totalTime = mPaths[id]->totalTime - mPaths[id]->msToNext[mPaths[id]->msToNext.size() - 1];
F64 ret = getClosestTimeToPoint(id, p, 0.0f, totalTime);
PROFILE_END();
return ret;
}
F64 PathManager::getClosestTimeToPoint(const U32 id, const Point3F p, const F64 tMin, const F64 tMax)
{
F64 totalSize = tMax - tMin;
//termination condition
if (totalSize <= 25.0f)
return (tMin + tMax) / 2.0f;
U32 steps = getMax((F32)totalSize / 500.0f, 8.0f);
F64 stepSize = totalSize / steps;
F64 distBest = F32_MAX;
F64 tBest = 0;
for (U32 i = 0; i <= steps; i++)
{
F64 tTest = tMin + (stepSize * i);
Point3F pTest; QuatF dummy;
getPathPosition(id, tTest, pTest, dummy);
F64 dist = (pTest - p).lenSquared(); //no need for square root
if (dist < distBest)
{
tBest = tTest;
distBest = dist;
}
}
F64 tMinNew = tBest - stepSize;
F64 tMaxNew = tBest + stepSize;
if (mPaths[id]->looping)
{
while (tMinNew < 0)
{
tMinNew += getPathTotalTime(id);
tMaxNew += getPathTotalTime(id);
}
while (tMaxNew > getPathTotalTime(id))
{
tMinNew -= getPathTotalTime(id);
tMaxNew -= getPathTotalTime(id);
}
}
else
{
tMinNew = getMax(tMin, tMinNew);
tMaxNew = getMin(tMax, tMaxNew);
}
return getClosestTimeToPoint(id, p, tMinNew, tMaxNew);
}

View file

@ -49,6 +49,7 @@ class PathManager
private:
struct PathEntry {
U32 totalTime;
bool looping;
Vector<Point3F> positions;
Vector<QuatF> rotations;
@ -85,6 +86,8 @@ class PathManager
U32 getPathTotalTime(const U32 id) const;
U32 getPathNumWaypoints(const U32 id) const;
U32 getWaypointTime(const U32 id, const U32 wayPoint) const;
F64 getClosestTimeToPoint(const U32 id, const Point3F p);
F64 getClosestTimeToPoint(const U32 id, const Point3F p, const F64 tMin, const F64 tMax);
U32 getPathTimeBits(const U32 id);
U32 getPathWaypointBits(const U32 id);
@ -97,7 +100,7 @@ class PathManager
void transmitPath(U32);
U32 allocatePathId();
void updatePath(const U32 id, const Vector<Point3F>&, const Vector<QuatF>&, const Vector<U32> &, const Vector<U32>&);
void updatePath(const U32 id, const Vector<Point3F>&, const Vector<QuatF>&, const Vector<U32>&, const Vector<U32>&, const bool looping);
//-------------------------------------- State dumping/reading
public:

View file

@ -248,8 +248,7 @@ void Path::updatePath()
}
}
// DMMTODO: Looping paths.
gServerPathManager->updatePath(mPathIndex, positions, rotations, times, smoothingTypes);
gServerPathManager->updatePath(mPathIndex, positions, rotations, times, smoothingTypes, mIsLooping);
}
void Path::addObject(SimObject* obj)
@ -543,4 +542,4 @@ void Marker::unpackUpdate(NetConnection* con, BitStream* stream)
stream->readAffineTransform(&otow);
setTransform(otow);
}
}