Skip to content

Commit 7f74d4d

Browse files
authored
Merge pull request #250 from Architector4/patch-limbpath-fixups
2 parents 8261673 + 5dd6a40 commit 7f74d4d

File tree

4 files changed

+263
-108
lines changed

4 files changed

+263
-108
lines changed

Source/Entities/LimbPath.cpp

Lines changed: 175 additions & 50 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-
}
207-
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-
}
213221

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,138 @@ 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());
298-
float distance = distVec.GetMagnitude();
299-
float segMag = (*m_CurrentSegment * GetTotalScaleMultiplier()).GetMagnitude();
316+
float distanceSqr = distVec.GetSqrMagnitude();
317+
float segMagSqr = (*m_CurrentSegment * GetTotalScaleMultiplier()).GetSqrMagnitude();
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+
321+
if (distanceSqr > segMagSqr)
322+
// We're too far away from this target.
323+
m_SegProgress = 0.0;
324+
else
325+
m_SegProgress = (1.0F - (std::sqrt(distanceSqr) / std::sqrt(segMagSqr)));
326+
327+
m_Ended = distVec.MagnitudeIsLessThan(m_SegmentEndedThreshold);
328+
} else {
329+
// Presume we're not done with all path segments until proven otherwise.
330+
m_Ended = false;
331+
332+
// Check if we are sufficiently close to the current target, or any of the next ones,
333+
// to start going to whatever target is after that one.
334+
//
335+
// The limb might have been yanked and is closer to one of the future targets, so check them too.
336+
337+
// This rest of the code will be working in local space, so convert input limb pos to that.
338+
Vector limbPosLocal = ToLocalSpace(limbPos);
339+
340+
341+
// Iterate over all segments and find one whose target is closest to the limb position.
342+
343+
344+
// Segment positions are accumulative, so keep an accumulator.
345+
Vector currentSegmentStartPos = GetCurrentSegStartLocal(); // Will be needed later.
346+
Vector segmentPosAccumulator = currentSegmentStartPos;
347+
348+
Vector closestSegmentStartPos;
349+
float closestSegmentTargetDistanceSqr = std::numeric_limits<float>::max();
350+
std::deque<Vector>::iterator closestSegment = m_CurrentSegment;
351+
352+
for (std::deque<Vector>::iterator itr = m_CurrentSegment; itr != m_Segments.end(); ++itr) {
353+
354+
// We want to find a closest segment to work off of, but we don't want to
355+
// snap from collision-enabled segments to collision-disabled segments,
356+
// because doing so tends to produce erratic foot behavior.
357+
358+
// If the current segment of the limbpath is collision-enabled...
359+
if (!FootCollisionsShouldBeDisabled()) {
360+
// If the currently looked at segment is collision-disabled...
361+
if (m_FootCollisionsDisabledSegment >= 0 &&
362+
m_Segments.size() - (itr - m_Segments.begin()) <= m_FootCollisionsDisabledSegment) {
363+
364+
// ...Then break.
365+
366+
// Note: if the first of the above two checks has passed,
367+
// this means that the current segment is collision-enabled.
368+
// And, since this iterator starts with it, this means that
369+
// *at least* the current segment was picked as closest already.
370+
//
371+
// In other words, if this break was hit, then closest segment vars have
372+
// been properly initialized already. Therefore, it's safe to break.
373+
374+
break;
375+
}
307376
}
308-
// Next segment!
309-
else {
310-
m_SegProgress = 0.0F;
377+
378+
Vector thisSegmentStartPos = segmentPosAccumulator;
379+
segmentPosAccumulator += *itr; // The accumulator's value is now the target pos of this segment.
380+
float thisSegmentDistanceSqr = (segmentPosAccumulator - limbPosLocal).GetSqrMagnitude();
381+
382+
if (thisSegmentDistanceSqr < closestSegmentTargetDistanceSqr) {
383+
// This one's closer.
384+
closestSegmentStartPos = thisSegmentStartPos;
385+
closestSegmentTargetDistanceSqr = thisSegmentDistanceSqr;
386+
closestSegment = itr;
387+
}
388+
}
389+
390+
391+
// Branches below will determine the new current segment and write the distance to it here.
392+
// We need this distance to compute progress towards it, whatever it ends up being.
393+
float distanceToCurrentSegmentTargetSqr;
394+
395+
if (closestSegmentTargetDistanceSqr < m_SegmentEndedThreshold * m_SegmentEndedThreshold) {
396+
// We're sufficiently close to this segment's target to go on.
397+
// Either declare this path ended, or continue from the next segment.
398+
399+
if (closestSegment + 1 == m_Segments.end()) {
400+
// Closest segment is the last segment and we are at its target. Declare done.
401+
m_Ended = true;
402+
m_CurrentSegment = closestSegment;
403+
404+
distanceToCurrentSegmentTargetSqr = closestSegmentTargetDistanceSqr;
405+
406+
} else {
407+
// Time to switch to next segment!
311408
m_SegTimer.Reset();
312-
m_Ended = false;
409+
410+
m_CurrentSegment = closestSegment + 1;
411+
412+
Vector currentSegmentTarget = closestSegmentStartPos + *closestSegment + *m_CurrentSegment;
413+
distanceToCurrentSegmentTargetSqr = (currentSegmentTarget - limbPosLocal).GetSqrMagnitude();
313414
}
314415
} else {
315-
m_SegProgress = distance > segMag ? 0.0F : (1.0F - (distance / segMag));
316-
m_Ended = false;
416+
// We're not close enough to that closest segment's target, but we can still try to do better.
417+
418+
Vector currentSegmentTargetPos = currentSegmentStartPos + *m_CurrentSegment;
419+
float currentSegmentDistanceSqr = (currentSegmentTargetPos - limbPosLocal).GetSqrMagnitude();
420+
421+
if (closestSegmentTargetDistanceSqr < currentSegmentDistanceSqr) {
422+
// The target for this closest segment is closer than the current segment's.
423+
// Fast-forward to it.
424+
m_SegTimer.Reset();
425+
426+
m_CurrentSegment = closestSegment;
427+
428+
distanceToCurrentSegmentTargetSqr = closestSegmentTargetDistanceSqr;
429+
} else {
430+
// Just get the distance to current segment's target.
431+
Vector currentSegmentTarget = currentSegmentStartPos + *m_CurrentSegment;
432+
distanceToCurrentSegmentTargetSqr = (currentSegmentTarget - limbPosLocal).GetSqrMagnitude();
433+
}
317434
}
435+
436+
// Now compute a normalized progress measure towards the current segment.
437+
438+
float currentSegmentMagnitudeSqr = m_CurrentSegment->GetSqrMagnitude();
439+
440+
if (distanceToCurrentSegmentTargetSqr > currentSegmentMagnitudeSqr)
441+
// We're too far away from this target.
442+
m_SegProgress = 0.0;
443+
else
444+
m_SegProgress = (1.0F - (std::sqrt(distanceToCurrentSegmentTargetSqr) / std::sqrt(currentSegmentMagnitudeSqr)));
318445
}
319446
}
320447

@@ -343,13 +470,11 @@ float LimbPath::GetRegularProgress() const {
343470
}
344471

345472
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-
}
473+
if (m_Ended || IsStaticPoint()) {
474+
return 0;
475+
} else {
476+
return m_CurrentSegment - m_Segments.begin();
351477
}
352-
return progress;
353478
}
354479

355480
void LimbPath::Terminate() {
@@ -379,7 +504,7 @@ bool LimbPath::RestartFree(Vector& limbPos, MOID MOIDToIgnore, int ignoreTeam) {
379504

380505
if (IsStaticPoint()) {
381506
Vector notUsed;
382-
Vector targetPos = m_JointPos + (RotatePoint(m_Start * GetTotalScaleMultiplier()));
507+
Vector targetPos = ToWorldSpace(m_Start);
383508
Vector beginPos = targetPos;
384509
// TODO: don't hardcode the beginpos
385510
beginPos.m_Y -= 24;
@@ -517,8 +642,8 @@ void LimbPath::Draw(BITMAP* pTargetBitmap,
517642
for (std::deque<Vector>::const_iterator itr = m_Segments.begin(); itr != m_Segments.end(); ++itr) {
518643
nextPoint += *itr;
519644

520-
Vector prevWorldPosition = m_JointPos + (RotatePoint(prevPoint * GetTotalScaleMultiplier()) - targetPos);
521-
Vector nextWorldPosition = m_JointPos + (RotatePoint(nextPoint * GetTotalScaleMultiplier()) - targetPos);
645+
Vector prevWorldPosition = ToWorldSpace(prevPoint) - targetPos;
646+
Vector nextWorldPosition = ToWorldSpace(nextPoint) - targetPos;
522647
line(pTargetBitmap, prevWorldPosition.m_X, prevWorldPosition.m_Y, nextWorldPosition.m_X, nextWorldPosition.m_Y, color);
523648

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

0 commit comments

Comments
 (0)