Skip to content

Commit 8ad9f2f

Browse files
committed
Improve LimbPath.cpp - rewrite ReportProgress, etc
The rewrite checks all path segments from the current one to the last and finds the best one to continue from, instead of fixating on just the one. This helps in cases where feet might be yanked along the path by excessive horizontal acceleration and removes quite a bunch of stumbling. Also fix Vector/Matrix division, making it the proper inverse of multiplication and applicable to const matrix.
1 parent bc830c3 commit 8ad9f2f

File tree

4 files changed

+187
-54
lines changed

4 files changed

+187
-54
lines changed

Source/Entities/LimbPath.cpp

Lines changed: 154 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,23 @@ Vector LimbPath::RotatePoint(const Vector& point) const {
172172
return (((point - offset) * m_Rotation) + offset) + m_PositionOffset;
173173
}
174174

175+
Vector LimbPath::InverseRotatePoint(const Vector& point) const {
176+
Vector offset = (m_RotationOffset).GetXFlipped(m_HFlipped);
177+
return (((point - m_PositionOffset) - offset) / m_Rotation) + offset;
178+
}
179+
180+
Vector LimbPath::ToLocalSpace(const Vector& position) const {
181+
// The position might be on one side of a border of a wrapping scene while the joint is on another.
182+
// Account for that.
183+
Vector posWrapped = m_JointPos + g_SceneMan.ShortestDistance(m_JointPos, position);
184+
185+
return InverseRotatePoint(posWrapped - m_JointPos) / GetTotalScaleMultiplier();
186+
}
187+
188+
Vector LimbPath::ToWorldSpace(const Vector& position) const {
189+
return m_JointPos + (RotatePoint(position * GetTotalScaleMultiplier()));
190+
}
191+
175192
int LimbPath::Save(Writer& writer) const {
176193
Entity::Save(writer);
177194

@@ -199,43 +216,44 @@ void LimbPath::Destroy(bool notInherited) {
199216
Clear();
200217
}
201218

202-
Vector LimbPath::GetProgressPos() {
219+
Vector LimbPath::GetCurrentSegStartLocal() const {
203220
Vector returnVec(m_Start);
204-
if (IsStaticPoint()) {
205-
return m_JointPos + (RotatePoint(returnVec * GetTotalScaleMultiplier()));
206-
}
207221

208-
// Add all the segments before the current one
209-
std::deque<Vector>::const_iterator itr;
210-
for (itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) {
211-
returnVec += *itr;
212-
}
213-
214-
// Add any from the progress made on the current one
215-
if (itr != m_Segments.end()) {
216-
returnVec += *m_CurrentSegment * m_SegProgress;
222+
if (!IsStaticPoint()) {
223+
// Add all the segments before the current one
224+
std::deque<Vector>::const_iterator itr;
225+
for (itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) {
226+
returnVec += *itr;
227+
}
217228
}
218229

219-
return m_JointPos + (RotatePoint(returnVec * GetTotalScaleMultiplier()));
230+
return returnVec;
220231
}
221232

222-
Vector LimbPath::GetCurrentSegTarget() {
223-
Vector returnVec(m_Start);
224-
if (IsStaticPoint()) {
225-
return m_JointPos + (RotatePoint(returnVec * GetTotalScaleMultiplier()));
233+
Vector LimbPath::GetProgressPos() {
234+
Vector returnVec = GetCurrentSegStartLocal();
235+
236+
if (!IsStaticPoint()) {
237+
if (m_CurrentSegment != m_Segments.end()) {
238+
// Add approximation based on progress.
239+
returnVec += *m_CurrentSegment * m_SegProgress;
240+
}
226241
}
227242

228-
std::deque<Vector>::const_iterator itr;
243+
return ToWorldSpace(returnVec);
244+
}
229245

230-
for (itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) {
231-
returnVec += *itr;
232-
}
246+
Vector LimbPath::GetCurrentSegTarget() {
247+
Vector returnVec = GetCurrentSegStartLocal();
233248

234-
if (itr != m_Segments.end()) {
235-
returnVec += *m_CurrentSegment;
249+
if (!IsStaticPoint()) {
250+
if (m_CurrentSegment != m_Segments.end()) {
251+
// Add the current one as well.
252+
returnVec += *m_CurrentSegment;
253+
}
236254
}
237255

238-
return m_JointPos + (RotatePoint(returnVec * GetTotalScaleMultiplier()));
256+
return ToWorldSpace(returnVec);
239257
}
240258

241259
Vector LimbPath::GetCurrentVel(const Vector& limbPos) {
@@ -292,29 +310,119 @@ void LimbPath::ReportProgress(const Vector& limbPos) {
292310
if (IsStaticPoint()) {
293311
const float staticPointEndedThreshold = 1.0F;
294312
m_Ended = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget()).MagnitudeIsLessThan(staticPointEndedThreshold);
295-
} else {
296-
// Check if we are sufficiently close to the target to start going after the next one.
313+
} else if (m_CurrentSegment == m_Segments.end()) {
314+
// Current path has already come to an end. Compute progress and m_Ended based on last segment's target.
297315
Vector distVec = g_SceneMan.ShortestDistance(limbPos, GetCurrentSegTarget());
298316
float distance = distVec.GetMagnitude();
299317
float segMag = (*m_CurrentSegment * GetTotalScaleMultiplier()).GetMagnitude();
300318

301-
if (distance < m_SegmentEndedThreshold) {
302-
if (++(m_CurrentSegment) == m_Segments.end()) {
303-
--(m_CurrentSegment);
304-
// Get normalized progress measure toward the target.
305-
m_SegProgress = distance > segMag ? 0.0F : (1.0F - (distance / segMag));
306-
m_Ended = true;
319+
// Get normalized progress measure toward the target.
320+
m_SegProgress = distance > segMag ? 0.0F : (1.0F - (distance / segMag));
321+
322+
m_Ended = distVec.MagnitudeIsLessThan(m_SegmentEndedThreshold);
323+
} else {
324+
// Presume we're not done with all path segments until proven otherwise.
325+
m_Ended = false;
326+
327+
// Check if we are sufficiently close to the current target, or any of the next ones,
328+
// to start going to whatever target is after that one.
329+
//
330+
// The limb might have been yanked and is closer to one of the future targets, so check them too.
331+
332+
// This rest of the code will be working in local space, so convert input limb pos to that.
333+
Vector limbPosLocal = ToLocalSpace(limbPos);
334+
335+
//
336+
// Iterate over all segments and find one whose target is closest to the limb position.
337+
//
338+
339+
// Segment positions are accumulative, so keep an accumulator.
340+
Vector currentSegmentStartPos = GetCurrentSegStartLocal(); // Will be needed later.
341+
Vector segmentPosAccumulator = currentSegmentStartPos;
342+
343+
Vector closestSegmentStartPos;
344+
float closestSegmentTargetDistanceSqr = std::numeric_limits<float>::max();
345+
std::deque<Vector>::iterator closestSegment = m_CurrentSegment;
346+
347+
for (std::deque<Vector>::iterator itr = m_CurrentSegment; itr != m_Segments.end(); ++itr) {
348+
if (itr != m_CurrentSegment) {
349+
if (m_FootCollisionsDisabledSegment >= 0 && GetSegCount() - (itr - m_Segments.begin()) <= m_FootCollisionsDisabledSegment) {
350+
// We've already picked a segment, and the remaining ones are ones with collisions disabled.
351+
// Ignore these.
352+
break;
353+
}
354+
}
355+
356+
Vector thisSegmentStartPos = segmentPosAccumulator;
357+
segmentPosAccumulator += *itr; // The accumulator's value is now the target pos of this segment.
358+
float thisSegmentDistanceSqr = (segmentPosAccumulator - limbPosLocal).GetSqrMagnitude();
359+
360+
if (thisSegmentDistanceSqr < closestSegmentTargetDistanceSqr) {
361+
// This one's closer.
362+
closestSegmentStartPos = thisSegmentStartPos;
363+
closestSegmentTargetDistanceSqr = thisSegmentDistanceSqr;
364+
closestSegment = itr;
365+
} else {
366+
// This one is *farther* than the last one.
367+
// Assuming next segments will be only farther and farther, just break now.
368+
break;
307369
}
308-
// Next segment!
309-
else {
310-
m_SegProgress = 0.0F;
370+
}
371+
372+
// We will want to compute progress to whatever the new segment is.
373+
float distanceToCurrentSegmentTarget;
374+
375+
376+
if (closestSegmentTargetDistanceSqr < m_SegmentEndedThreshold * m_SegmentEndedThreshold) {
377+
// We're sufficiently close to this segment's target to go on.
378+
// Either declare this path ended, or continue from the next segment.
379+
380+
if (closestSegment + 1 == m_Segments.end()) {
381+
// Closest segment is the last segment and we are at its target. Declare done.
382+
m_Ended = true;
383+
m_CurrentSegment = closestSegment;
384+
385+
distanceToCurrentSegmentTarget = std::sqrt(closestSegmentTargetDistanceSqr);
386+
387+
} else {
388+
// Time to switch to next segment!
311389
m_SegTimer.Reset();
312-
m_Ended = false;
390+
391+
m_CurrentSegment = closestSegment + 1;
392+
393+
Vector currentSegmentTarget = closestSegmentStartPos + *closestSegment + *m_CurrentSegment;
394+
distanceToCurrentSegmentTarget = (currentSegmentTarget - limbPosLocal).GetMagnitude();
313395
}
314396
} else {
315-
m_SegProgress = distance > segMag ? 0.0F : (1.0F - (distance / segMag));
316-
m_Ended = false;
397+
// We're not close enough to that closest segment's target, but we can still try to do better.
398+
399+
Vector currentSegmentTargetPos = currentSegmentStartPos + *m_CurrentSegment;
400+
float currentSegmentDistanceSqr = (currentSegmentTargetPos - limbPosLocal).GetSqrMagnitude();
401+
402+
if (closestSegmentTargetDistanceSqr < currentSegmentDistanceSqr) {
403+
// The target for this closest segment is closer than the current segment's.
404+
// Fast-forward to it.
405+
m_SegTimer.Reset();
406+
407+
m_CurrentSegment = closestSegment;
408+
409+
distanceToCurrentSegmentTarget = std::sqrt(closestSegmentTargetDistanceSqr);
410+
} else {
411+
// Just get the distance to current segment's target.
412+
Vector currentSegmentTarget = currentSegmentStartPos + *m_CurrentSegment;
413+
distanceToCurrentSegmentTarget = (currentSegmentTarget - limbPosLocal).GetMagnitude();
414+
}
317415
}
416+
417+
// Now compute a normalized progress measure towards the current segment.
418+
419+
float currentSegmentMagnitude = m_CurrentSegment->GetMagnitude();
420+
421+
if (distanceToCurrentSegmentTarget > currentSegmentMagnitude)
422+
// We're too far away from this target.
423+
m_SegProgress = 0.0;
424+
else
425+
m_SegProgress = (1.0F - (distanceToCurrentSegmentTarget / currentSegmentMagnitude));
318426
}
319427
}
320428

@@ -343,13 +451,11 @@ float LimbPath::GetRegularProgress() const {
343451
}
344452

345453
int LimbPath::GetCurrentSegmentNumber() const {
346-
int progress = 0;
347-
if (!m_Ended && !IsStaticPoint()) {
348-
for (std::deque<Vector>::const_iterator itr = m_Segments.begin(); itr != m_CurrentSegment; ++itr) {
349-
progress++;
350-
}
454+
if (m_Ended || IsStaticPoint()) {
455+
return 0;
456+
} else {
457+
return m_CurrentSegment - m_Segments.begin();
351458
}
352-
return progress;
353459
}
354460

355461
void LimbPath::Terminate() {
@@ -379,7 +485,7 @@ bool LimbPath::RestartFree(Vector& limbPos, MOID MOIDToIgnore, int ignoreTeam) {
379485

380486
if (IsStaticPoint()) {
381487
Vector notUsed;
382-
Vector targetPos = m_JointPos + (RotatePoint(m_Start * GetTotalScaleMultiplier()));
488+
Vector targetPos = ToWorldSpace(m_Start);
383489
Vector beginPos = targetPos;
384490
// TODO: don't hardcode the beginpos
385491
beginPos.m_Y -= 24;
@@ -517,8 +623,8 @@ void LimbPath::Draw(BITMAP* pTargetBitmap,
517623
for (std::deque<Vector>::const_iterator itr = m_Segments.begin(); itr != m_Segments.end(); ++itr) {
518624
nextPoint += *itr;
519625

520-
Vector prevWorldPosition = m_JointPos + (RotatePoint(prevPoint * GetTotalScaleMultiplier()) - targetPos);
521-
Vector nextWorldPosition = m_JointPos + (RotatePoint(nextPoint * GetTotalScaleMultiplier()) - targetPos);
626+
Vector prevWorldPosition = ToWorldSpace(prevPoint) - targetPos;
627+
Vector nextWorldPosition = ToWorldSpace(nextPoint) - targetPos;
522628
line(pTargetBitmap, prevWorldPosition.m_X, prevWorldPosition.m_Y, nextWorldPosition.m_X, nextWorldPosition.m_Y, color);
523629

524630
Vector min(std::min(prevWorldPosition.m_X, nextWorldPosition.m_X), std::min(prevWorldPosition.m_Y, nextWorldPosition.m_Y));

Source/Entities/LimbPath.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ namespace RTE {
109109

110110
/// Gets the APPROXIMATE scene position that the limb was reported to be
111111
/// last frame. This really shouldn't be used by external clients.
112-
/// @return A Vector with the APPROXIAMTE scene/world coordinates of the limb as
112+
/// @return A Vector with the APPROXIMATE scene/world coordinates of the limb as
113113
/// reported last.
114114
Vector GetProgressPos();
115115

@@ -383,7 +383,9 @@ namespace RTE {
383383
// The iterator to the segment of the path that the limb ended up on the end of
384384
std::deque<Vector>::iterator m_CurrentSegment;
385385

386-
int m_FootCollisionsDisabledSegment; //!< The segment after which foot collisions will be disabled for this limbpath, if it's for legs.
386+
// Count of segments at the end of the segments list for which foot collisions should be disabled
387+
// for this limbpath, if it's for legs.
388+
int m_FootCollisionsDisabledSegment;
387389

388390
// Normalized measure of how far the limb has progressed toward the
389391
// current segment's target. 0.0 means its farther away than the
@@ -449,6 +451,26 @@ namespace RTE {
449451
/// @param point The point to rotate.
450452
/// @return The rotated point.
451453
Vector RotatePoint(const Vector& point) const;
454+
455+
/// Inverse of RotatePoint.
456+
/// @param point The rotated point
457+
/// @return The point pre-rotation.
458+
Vector InverseRotatePoint(const Vector& point) const;
459+
460+
/// Converts an input position, absolute and in scene/world space, to local space for this LimbPath,
461+
/// taking into account rotation and scaling.
462+
/// @param position World space position
463+
/// @returns Local space position
464+
Vector ToLocalSpace(const Vector& position) const;
465+
466+
/// Converts an input position, in local space for this LimbPath, into scene/world space,
467+
/// taking into account rotation and scaling.
468+
/// @param position Local space position
469+
/// @returns World space position
470+
Vector ToWorldSpace(const Vector& position) const;
471+
472+
// Gets the current segment's starting position (i.e. last segment's target) in local space.
473+
Vector GetCurrentSegStartLocal() const;
452474
};
453475

454476
} // namespace RTE

Source/System/Matrix.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,14 @@ Vector Matrix::operator/(const Vector& rhs) {
136136
}
137137

138138
Vector retVec = rhs;
139-
// Apply flipping as set.
140-
retVec.m_X = m_Flipped[X] ? -retVec.m_X : retVec.m_X;
141-
retVec.m_Y = m_Flipped[Y] ? -retVec.m_Y : retVec.m_Y;
142139

143140
// Do the matrix multiplication.
144141
retVec.SetXY(m_Elements[0][0] * retVec.m_X + m_Elements[1][0] * retVec.m_Y, m_Elements[0][1] * retVec.m_X + m_Elements[1][1] * retVec.m_Y);
145142

143+
// Apply flipping as set.
144+
retVec.m_X = m_Flipped[X] ? -retVec.m_X : retVec.m_X;
145+
retVec.m_Y = m_Flipped[Y] ? -retVec.m_Y : retVec.m_Y;
146+
146147
return retVec;
147148
}
148149

Source/System/Matrix.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ namespace RTE {
238238
}
239239

240240
/// Division operator overload for a Matrix and a Vector. The vector will be transformed according to the Matrix's elements.
241+
/// Flipping, if set, is performed after rotating.
241242
/// @param rhs A Vector reference as the right hand side operand.
242243
/// @return The resulting transformed Vector.
243244
Vector operator/(const Vector& rhs);
@@ -246,7 +247,10 @@ namespace RTE {
246247
/// @param lhs A Vector reference as the left hand side operand.
247248
/// @param rhs A Matrix reference as the right hand side operand.
248249
/// @return A reference to the resulting Vector.
249-
friend Vector operator/(const Vector& lhs, Matrix& rhs) { return rhs / lhs; }
250+
friend Vector operator/(const Vector& lhs, const Matrix& rhs) {
251+
Matrix m(rhs);
252+
return m / lhs;
253+
}
250254

251255
/// Self-multiplication operator overload for Vector with a Matrix.
252256
/// @param lhs A Vector reference as the left hand side operand.

0 commit comments

Comments
 (0)