diff --git a/MUNGA/ENTITY.cpp b/MUNGA/ENTITY.cpp index 6ce16ec..89e1ff3 100644 --- a/MUNGA/ENTITY.cpp +++ b/MUNGA/ENTITY.cpp @@ -506,6 +506,13 @@ void { localOrigin = updateOrigin; localToWorld = localOrigin; + + // + // Set outside the step loop, so renderPreviousOrigin now + // describes a step that never happened. Draw plainly until + // a real one does. + // + renderStepTaken = False; } Simulation::ReadUpdateRecord(record); } @@ -1137,6 +1144,7 @@ Entity::Entity( // renderPreviousOrigin = localOrigin; renderStepFraction = (Scalar) 0; + renderStepTaken = False; // initialize camera stuff cameraOffset = Origin::Identity; @@ -1485,6 +1493,7 @@ void { Check(this); renderPreviousOrigin = localOrigin; + renderStepTaken = True; } void @@ -1501,11 +1510,31 @@ void Check_Pointer(out); // - // Nothing to blend towards: no fraction of a step outstanding, no - // fixed step at all, or interpolation switched off. Hand back exactly - // what every caller used before this existed. + // No fixed step at all, interpolation switched off, or this entity has + // not taken a step yet. Hand back exactly what every caller used before + // this existed. // - if (renderStepFraction <= (Scalar) 0 || !RenderInterpolationEnabled()) + // The test used to be on the fraction rather than on renderStepTaken, + // and that was wrong in a way that showed. Drawing at fraction f means + // drawing at the start of the step plus f of it, so f = 0 means the + // START of the step - while this early return hands back localToWorld, + // which is its END. The two are a whole step apart, about a metre at + // racing speed. + // + // behind is a whole number of milliseconds against a 20ms step, so it + // lands on exactly zero about one frame in twenty. On those frames a + // pod was drawn a full step ahead of itself and then snapped back on + // the next one: a jump three times a second at 59fps, regular because + // the beat between frame rate and step rate is regular, and worst when + // a pod crosses the view quickly. It never appeared in any simulation + // trace because the simulation was right - only the drawing was wrong. + // + // Interpolating at f = 0 is continuous with everything either side of + // it. Each frame advances the drawn position by frame_time / step + // whether or not a step boundary falls between the two, which is the + // entire point of doing it. + // + if (!RenderInterpolationEnabled() || !renderStepTaken) { *out = localToWorld; return; diff --git a/MUNGA/ENTITY.h b/MUNGA/ENTITY.h index ea7787d..115f94b 100644 --- a/MUNGA/ENTITY.h +++ b/MUNGA/ENTITY.h @@ -177,6 +177,15 @@ public: Origin renderPreviousOrigin; Scalar renderStepFraction; + // + // Whether renderPreviousOrigin describes a step this entity actually + // took. It has to be asked separately from the fraction, because a + // fraction of zero is a perfectly ordinary place to be drawing - see + // GetRenderToWorld for the one-step jump that testing the fraction + // instead used to produce. + // + Logical renderStepTaken; + // // The transform to DRAW with. Falls back to localToWorld verbatim when // interpolation is off, when there is no fixed step to interpolate diff --git a/MUNGA_L4/L4VIDRND.cpp b/MUNGA_L4/L4VIDRND.cpp index 89e63ff..72e29d7 100644 --- a/MUNGA_L4/L4VIDRND.cpp +++ b/MUNGA_L4/L4VIDRND.cpp @@ -5973,7 +5973,7 @@ void // whenever interpolation is off or idle, so the old behaviour is // untouched. See Entity::GetRenderToWorld. // - if((myEyepointRotation && *myEyepointRotation != oldEyepointRotation) || oldLocalToWorld != myEntity->localToWorld || mForceUpdate || myEntity->renderStepFraction > (Scalar) 0) + if((myEyepointRotation && *myEyepointRotation != oldEyepointRotation) || oldLocalToWorld != myEntity->localToWorld || mForceUpdate || myEntity->renderStepTaken) { mForceUpdate = false; oldLocalToWorld = myEntity->localToWorld;