@@ -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- }
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
241259Vector 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
345453int 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
355461void 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 ));
0 commit comments