@@ -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+
175192int 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
241259Vector 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
345472int 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
355480void 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