public bool GetInterpolatedAvatars(ref AvatarState[] avatar, out int count, out ushort resetId) { count = 0; resetId = 0; var frame = (long)Math.Floor(packetFrame + time * PhysicsFrameRate); if (frame < 0.0) { return(false); //if interpolation frame is negative, it's too early to display anything(reset) } const int n = 16; if (isInterpolating && frame - startFrame > n) { isInterpolating = false; //if we are interpolating but the interpolation start frame is too old, go back to the not interpolating state, so we can find a new start point. } //if not interpolating, attempt to find an interpolation start point. //if start point exists, go into interpolating mode and set end point to start point //so we can reuse code below to find a suitable end point on first time through. //if no interpolation start point is found, return. if (!isInterpolating) { for (var i = frame + 1; i > frame - n && i >= 0; i--) { var entry = GetEntry((uint)i); if (entry == null) { continue; } var sampleTime = (i - packetFrame) / PhysicsFrameRate + entry.header.timeOffset; if (time < sampleTime || time > sampleTime + (1.0f / PhysicsFrameRate)) { continue; } startFrame = i; endFrame = i; startTime = sampleTime; endTime = sampleTime; isInterpolating = true; } } if (!isInterpolating) { return(false); } Assert.IsTrue(time >= startTime); //if current time is >= end time, we need to start a new interpolation //from the previous end time to the next sample that exists up to n samples ahead. if (time >= endTime) { startFrame = endFrame; startTime = endTime; for (int i = 0; i < n; ++i) { var entry = GetEntry((uint)(startFrame + 1 + i)); if (entry == null) { continue; } var sampleTime = (startFrame + 1 + i - packetFrame) / PhysicsFrameRate + entry.header.timeOffset; if (sampleTime < time) { continue; } endFrame = startFrame + 1 + i; endTime = sampleTime + (1.0 / PhysicsFrameRate); break; } } if (time > endTime) { return(false); //if current time is still > end time, we couldn't start a new interpolation so return. } //we are in a valid interpolation, calculate t by looking at current time //relative to interpolation start/end times and perform the interpolation. var t = (float)Clamp((time - startTime) / (endTime - startTime), 0.0, 1.0); var a = GetEntry((uint)(startFrame)); var b = GetEntry((uint)(endFrame)); for (int i = 0; i < a.avatarCount; ++i) { for (int j = 0; j < b.avatarCount; ++j) { if (a.avatars[i].clientId != b.avatars[j].clientId) { continue; } AvatarState.Interpolate(ref a.avatars[i], ref b.avatars[j], out avatar[count], t); count++; } } resetId = a.header.resetId; return(true); }
public bool GetInterpolatedAvatarState(ref AvatarState[] output, out int numOutputAvatarStates, out ushort resetSequence) { numOutputAvatarStates = 0; resetSequence = 0; // if interpolation frame is negative, it's too early to display anything double interpolation_frame = initial_frame + time * Constants.PhysicsFrameRate; if (interpolation_frame < 0.0) { return(false); } // if we are interpolating but the interpolation start frame is too old, // go back to the not interpolating state, so we can find a new start point. const int n = 16; if (interpolating) { long frame = (long)Math.Floor(interpolation_frame); if (frame - interpolation_start_frame > n) { interpolating = false; } } // if not interpolating, attempt to find an interpolation start point. // if start point exists, go into interpolating mode and set end point to start point // so we can reuse code below to find a suitable end point on first time through. // if no interpolation start point is found, return. if (!interpolating) { long current_frame = (uint)Math.Floor(interpolation_frame); for (long frame = current_frame + 1; (frame > current_frame - n) && (frame >= 0); frame--) { JitterBufferEntry entry = GetEntry((uint)frame); if (entry != null) { double avatar_sample_time = (frame - initial_frame) * (1.0 / Constants.PhysicsFrameRate) + entry.packetHeader.avatarSampleTimeOffset; if (time >= avatar_sample_time && time <= avatar_sample_time + (1.0f / Constants.PhysicsFrameRate)) { interpolation_start_frame = frame; interpolation_end_frame = frame; interpolation_start_time = avatar_sample_time; interpolation_end_time = avatar_sample_time; interpolating = true; } } } } if (!interpolating) { return(false); } Assert.IsTrue(time >= interpolation_start_time); // if current time is >= end time, we need to start a new interpolation // from the previous end time to the next sample that exists up to n samples ahead. if (time >= interpolation_end_time) { interpolation_start_frame = interpolation_end_frame; interpolation_start_time = interpolation_end_time; for (int i = 0; i < n; ++i) { JitterBufferEntry entry = GetEntry((uint)(interpolation_start_frame + 1 + i)); if (entry != null) { double avatar_sample_time = (interpolation_start_frame + 1 + i - initial_frame) * (1.0 / Constants.PhysicsFrameRate) + entry.packetHeader.avatarSampleTimeOffset; if (avatar_sample_time >= time) { interpolation_end_frame = interpolation_start_frame + 1 + i; interpolation_end_time = avatar_sample_time + (1.0 / Constants.PhysicsFrameRate); break; } } } } // if current time is still > end time, we couldn't start a new interpolation so return. if (time > interpolation_end_time) { return(false); } // we are in a valid interpolation, calculate t by looking at current time // relative to interpolation start/end times and perform the interpolation. float t = (float)Clamp((time - interpolation_start_time) / (interpolation_end_time - interpolation_start_time), 0.0, 1.0); JitterBufferEntry a = GetEntry((uint)(interpolation_start_frame)); JitterBufferEntry b = GetEntry((uint)(interpolation_end_frame)); for (int i = 0; i < a.numAvatarStates; ++i) { for (int j = 0; j < b.numAvatarStates; ++j) { if (a.avatarState[i].client_index == b.avatarState[j].client_index) { AvatarState.Interpolate(ref a.avatarState[i], ref b.avatarState[j], out output[numOutputAvatarStates], t); numOutputAvatarStates++; } } } resetSequence = a.packetHeader.resetSequence; return(true); }