/// <summary> /// This is a hot path. Runs at the completion interpolation, and attempts to find/reconstruct the next suitable frame for interpolation. /// </summary> public Frame DetermineAndPrepareNextFrame(bool svrWaitingForTeleportConfirm) { // buffer is empty, no point looking for any frames - we need to extrapolate the next frame if (validFrameMask == 0) { ExtrapolateNextFrame(svrWaitingForTeleportConfirm); XDebug.Log(!XDebug.logInfo ? null : //Debug.Log( (Time.time + " NST:" + nst.NstId + " <b> Empty buffer</b>, (likely packetloss) copying current frame to " + NextFrame.frameid + " " + nst.name + "\nCurrentFrame: " + currentFrame.frameid + " scn:" + currentFrame.sceneIndex + " " + currentFrame.compPos + " " + currentFrame.rootPos + "\nNextFrame: " + NextFrame.frameid + " scn:" + NextFrame.sceneIndex + " " + NextFrame.compPos + " " + NextFrame.rootPos)); return(NextFrame); } extrapolationCount = 0; // First see if there is a future frame ready - ignoring late arrivles that may have backfilled behind the current frame Frame nextValid = GetFirstFutureValidFrame(); // if not see if there is an older frame that arrived late, if so we will jump back to that as current if (nextValid == null) { nextValid = GetOldestPastValidFrame() ?? GetOldestValidFrame(); // The only valid frames are only in the past, we need to jump back to the oldest to get our current frame in a better ballpark if (nextValid != null) { nextValid.CompletePosition(currentFrame); //Debug.Log( XDebug.Log(!XDebug.logInfo ? null : (Time.time + " NST:" + nst.NstId + " <b> Skipping back </b>(likely packetloss) to frame " + nextValid.frameid + " from current frame " + CurrentIndex + " " + nst.name + "\nOnly frames in buffer were in the past, so seems that we are getting ahead of the buffer. Should see these rarely." + "\nCurrentFrame: " + currentFrame.frameid + " scn:" + currentFrame.sceneIndex + " " + currentFrame.compPos + " " + currentFrame.rootPos + "\nNextValid: " + nextValid.frameid + " scn:" + nextValid.sceneIndex + " " + nextValid.compPos + " " + nextValid.rootPos)); return(nextValid); } } // Find out how far in the future the next valid frame is, need to know this for the reconstruction lerp. int stepsFromLast = CountFrames(CurrentIndex, nextValid.frameid); // The next frame is the next valid... not much thinking required... just use it. if (stepsFromLast == 1) { InvalidateOldFrames(NextFrame); // LIKELY UNEEDED NextFrame.CompletePosition(currentFrame); //Debug.Log( //XDebug.Log(!XDebug.logInfo ? null : // (Time.time + " NST:" + nst.NstId + " <b>Normal Next</b> from " + CurrentIndex + " to " + NextFrame.frameid + " (likely packetloss) from expected frame. " + nst.name + // "\nCurrentFrame: " + currentFrame.frameid + " scn:" + currentFrame.sceneIndex + " " + currentFrame.compPos + " " + currentFrame.rootPos + // "\nNextFrame: " + NextFrame.frameid + " scn:" + NextFrame.sceneIndex + " " + NextFrame.compPos + " " + NextFrame.rootPos)); return(NextFrame); } // if next frame on the buffer is a couple ahead of current, jump forward if (stepsFromLast > jumpForwardThreshold) { //Debug.Log( XDebug.Log(!XDebug.logInfo ? null : (Time.time + " NST:" + nst.NstId + " <b>Jumping forward</b> from " + CurrentIndex + " to " + nextValid.frameid + " (likely packetloss) from expected frame. " + nst.name + "\nCurrentFrame: " + currentFrame.frameid + " " + currentFrame.compPos + " " + currentFrame.rootPos + "\nNextValidFrame: " + nextValid.frameid + " " + nextValid.compPos + " " + nextValid.rootPos)); InvalidateOldFrames(nextValid); nextValid.CompletePosition(currentFrame); return(nextValid); } //All other cases we Reconstruct missing next frame using the current frame and a future frame NextFrame.state = currentFrame.state; float t = 1f / stepsFromLast; nextValid.CompletePosition(currentFrame); Vector3 lerpedPos = Vector3.Lerp(currentFrame.rootPos, nextValid.rootPos, t); float lerpedStartTime = Mathf.Lerp(currentFrame.packetArriveTime, nextValid.packetArriveTime, t); NextFrame.ModifyFrame(currentFrame.updateType, currentFrame.rootBitCullLevel, lerpedPos, GenericX.NULL, lerpedStartTime); //Debug.Log( XDebug.Log(!XDebug.logInfo ? null : (Time.time + " NST:" + nst.NstId + " <b>Reconstructing frame " + NextFrame.frameid + "</b> (likely packetloss) from current frame and future frame " + NextFrame.compPos + " <b>" + NextFrame.rootPos + "</b> " + nst.name + "\nCurrentFrame: " + currentFrame.frameid + " scn:" + currentFrame.sceneIndex + " " + currentFrame.compPos + " " + currentFrame.rootPos + "\nNextValidFrame: " + nextValid.frameid + " scn:" + nextValid.sceneIndex + " " + nextValid.compPos + " " + nextValid.rootPos) + " " + nextValid.rootBitCullLevel); //XDebug.Log(!XDebug.logInfo ? null : // (Time.time + "fid" + NextFrame.frameid + " <color=red><b> RECONSTRUCT ELEMENTS </b></color> " + NextFrame.RootRot + " " + currentFrame.RootRot + " " + nextValid.RootRot)); // Notify all interested components that they need to reconstruct a missing frame (elements and such) foreach (INstOnReconstructMissing callbacks in nst.iNstOnReconstructMissing) { callbacks.OnReconstructMissing(NextFrame, currentFrame, nextValid, t, svrWaitingForTeleportConfirm); } return(NextFrame); }
/// <summary> /// This is a hot path. Runs at the completion interpolation, and attempts to find/reconstruct the next suitable frame for interpolation. /// </summary> public Frame DetermineAndPrepareNextFrame(bool svrWaitingForTeleportConfirm) { // buffer is empty, no point looking for any frames - we need to extrapolate the next frame if (validFrameMask <= 1) { DebugX.Log(!DebugX.logInfo ? "" : //Debug.Log( (Time.time + " NST " + nst.NstId + " " + nst.name + " <color=red><b> empty buffer, copying current frame to </b></color>" + NextFrame.frameid + " \n curr: " + currentFrame)); ExtrapolateNextFrame(svrWaitingForTeleportConfirm); return(NextFrame); } extrapolationCount = 0; // First see if there is a future frame ready - ignoring late arrivles that may have backfilled behind the current frame Frame nextValid = GetFirstFutureValidFrame(); // if not see if there is an older frame that arrived late, if so we will jump back to that as current if (nextValid == null) { nextValid = GetOldestPastValidFrame() ?? GetOldestValidFrame(); // The only valid frames are only in the past, we need to jump back to the oldest to get our current frame in a better ballpark if (nextValid != null) { DebugX.Log(!DebugX.logInfo ? "" : //Debug.Log( (Time.time + " NST " + nst.NstId + " " + nst.name + " <color=red><b> Skipping back to frame </b></color> " + nextValid.frameid + " from current frame " + CurrentIndex)); nextValid.CompletePosition(currentFrame); return(nextValid); } } // Find out how far in the future the next valid frame is, need to know this for the reconstruction lerp. int stepsFromLast = CountFrames(CurrentIndex, nextValid.frameid); // The next frame is the next valid... not much thinking required... just use it. if (stepsFromLast == 1) { InvalidateOldFrames(NextFrame); // LIKELY UNEEDED NextFrame.CompletePosition(currentFrame); return(NextFrame); } // if next frame on the buffer is a couple ahead of current, jump forward if (stepsFromLast > jumpForwardThreshold) { //Debug.Log( DebugX.Log(!DebugX.logInfo ? "" : ("<color=red><b>Jumping forward frame(s) </b></color>")); InvalidateOldFrames(nextValid); nextValid.CompletePosition(currentFrame); return(nextValid); } //All other cases we Reconstruct missing next frame using the current frame and a future frame Frame next = NextFrame; //Debug.Log( DebugX.Log(!DebugX.logInfo ? "" : (Time.time + " NST:" + nst.NstId + " <color=black><b>Reconstructing missing packet " + next.frameid + " </b></color> \n" + currentFrame.compPos + "\n" + nextValid.compPos)); next.state = currentFrame.state; float t = 1f / stepsFromLast; nextValid.CompletePosition(currentFrame); Vector3 lerpedPos = Vector3.Lerp(currentFrame.rootPos, nextValid.rootPos, t); float lerpedStartTime = Mathf.Lerp(currentFrame.packetArriveTime, nextValid.packetArriveTime, t); next.ModifyFrame(currentFrame.updateType, currentFrame.rootSendType, lerpedPos, GenericX.NULL, lerpedStartTime); DebugX.Log(!DebugX.logInfo ? "" : (Time.time + "fid" + next.frameid + " <color=red><b> RECONSTRUCT ELEMENTS </b></color> " + next.RootRot + " " + currentFrame.RootRot + " " + nextValid.RootRot)); // Notify all interested components that they need to reconstruct a missing frame (elements and such) foreach (INstOnReconstructMissing callbacks in nst.iNstOnReconstructMissing) { callbacks.OnReconstructMissing(next, currentFrame, nextValid, t, svrWaitingForTeleportConfirm); } return(next); }
public Frame DetermineNextFrame() { // buffer is empty, no point looking for any frames if (validFrameMask <= 1) { DebugX.Log(Time.time + " NST " + nst.NstId + " " + nst.name + " <color=red><b> empty buffer, copying current frame </b></color>" + NextFrame.packetid); ExtrapolateNextFrame(); return(NextFrame); } // First see if there is a future frame ready Frame nextValid = GetFirstFutureValidFrame(); // if not see if there is an older frame that arrived late, if so we will jump back to that as current if (nextValid == null) { nextValid = GetOldestPastValidFrame(); // Valid frames are only in the past, we need to jump back to that packetindex if (nextValid != null) { DebugX.Log(Time.time + " NST " + nst.NstId + " " + nst.name + " <color=red><b> Skipping back to older frame </b></color> " + nextValid.packetid); nextValid.CompletePosition(currentFrame); return(nextValid); } // No future or past frames found - Look everywhere as a last ditch check in case we are way out of sync nextValid = GetOldestValidFrame(); if (nextValid != null) { DebugX.Log(Time.time + " NST " + nst.NstId + " " + nst.name + " <color=red><b> Skipping to out of sequence frame </b></color> " + nextValid.packetid); nextValid.CompletePosition(currentFrame); // of questionable value for a frame this out of sequence, but better than 0 position. return(nextValid); } } // Find out how far in the future the next valid frame is, need to know this for the reconstruction lerp. int stepsFromLast = CountFrames(CurrentIndex, nextValid.packetid); // The next frame is the next valid... not much thinking required... just use it. if (stepsFromLast == 1) { InvalidateOldFrames(NextFrame); NextFrame.CompletePosition(currentFrame); return(NextFrame); } if (stepsFromLast > 2) // arbitrary number... should refine this { InvalidateOldFrames(nextValid); nextValid.CompletePosition(currentFrame); return(nextValid); } //All other cases we Reconstruct missing next frame Frame next = NextFrame; DebugX.Log(Time.time + " NST:" + nst.NstId + " <color=black><b>Reconstructing missing packet " + next.packetid + " </b></color>"); float t = 1f / stepsFromLast; nextValid.CompletePosition(currentFrame); CompressedElement lerpedCompPos = Vector3.Lerp(currentFrame.pos, nextValid.pos, t).CompressPos(); // TODO: should this be v3 lerps instead to avoid the rounding error? // TEST //CompressedPos lerpedCompPos = nextValid.compPos; // TODO: should this be v3 lerps instead to avoid the rounding error? float lerpedStartTime = Mathf.Lerp(currentFrame.packetArriveTime, nextValid.packetArriveTime, t); next.ModifyFrame(currentFrame.msgType, lerpedCompPos, lerpedCompPos.Decompress(), lerpedStartTime); // Reconstruct missing Position Element for (int i = 0; i < next.positions.Count; i++) { GenericX currentTargetX = nst.positionElements[i].target; if (BitTools.GetBitInMask(nextValid.positionsMask, i)) { next.positions[i] = (Vector3.Lerp(currentTargetX, nextValid.positions[i], t)); i.SetBitInMask(ref next.positionsMask, true); } else { next.positions[i] = currentTargetX; i.SetBitInMask(ref next.rotationsMask, false); } } // Reconstruct missing Rotation Element for (int i = 0; i < next.rotations.Count; i++) { GenericX currentTargetX = nst.rotationElements[i].target; if (BitTools.GetBitInMask(nextValid.rotationsMask, i)) { if (currentTargetX.type == XType.NULL) { Debug.LogError(nst.name + " Cloning NULL GenX element " + i + " " + currentTargetX + " - which should never happen, this would be a bug."); } next.rotations[i] = (Quaternion.Slerp(currentTargetX, nextValid.rotations[i], t)); //TEST next.rotations[i] = currentTargetX; i.SetBitInMask(ref next.rotationsMask, true); } else { next.rotations[i] = nst.rotationElements[i].target; // maybe unneeded i.SetBitInMask(ref next.rotationsMask, false); } } return(next); }