New object/avatar interp.

This commit is contained in:
Shyotl
2011-08-19 01:36:01 -05:00
parent 7f2e96f0a3
commit 9d40820937
4 changed files with 240 additions and 60 deletions

View File

@@ -13395,6 +13395,28 @@
<key>Value</key>
<integer>1</integer>
</map>
<key>InterpolationTime</key>
<map>
<key>Comment</key>
<string>How long to extrapolate object motion after last packet received</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<integer>3</integer>
</map>
<key>InterpolationPhaseOut</key>
<map>
<key>Comment</key>
<string>Seconds to phase out interpolated motion</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>VerboseLogs</key>
<map>
<key>Comment</key>

View File

@@ -106,8 +106,8 @@
//#define DEBUG_UPDATE_TYPE
BOOL gVelocityInterpolate = TRUE;
BOOL gPingInterpolate = TRUE;
BOOL LLViewerObject::sVelocityInterpolate = TRUE;
BOOL LLViewerObject::sPingInterpolate = TRUE;
U32 LLViewerObject::sNumZombieObjects = 0;
S32 LLViewerObject::sNumObjects = 0;
@@ -118,6 +118,10 @@ S32 LLViewerObject::sAxisArrowLength(50);
BOOL LLViewerObject::sPulseEnabled(FALSE);
BOOL LLViewerObject::sUseSharedDrawables(FALSE); // TRUE
// sMaxUpdateInterpolationTime must be greater than sPhaseOutUpdateInterpolationTime
F64 LLViewerObject::sMaxUpdateInterpolationTime = 3.0; // For motion interpolation: after X seconds with no updates, don't predict object motion
F64 LLViewerObject::sPhaseOutUpdateInterpolationTime = 2.0; // For motion interpolation: after Y seconds with no updates, taper off motion prediction
// static
LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
{
@@ -1899,9 +1903,9 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
}
}
new_rot.normalize();
new_rot.normQuat();
if (gPingInterpolate)
if (sPingInterpolate)
{
LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mesgsys->getSender());
if (cdp)
@@ -2064,7 +2068,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
// U32 ping_delay = mesgsys->mCircuitInfo.getPingDelay();
mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds();
mLastMessageUpdateSecs = LLFrameTimer::getElapsedSeconds();
mLastMessageUpdateSecs = mLastInterpUpdateSecs;
if (mDrawable.notNull())
{
// Don't clear invisibility flag on update if still orphaned!
@@ -2101,7 +2105,7 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
// CRO - don't velocity interp linked objects!
// Leviathan - but DO velocity interp joints
if (!mStatic && gVelocityInterpolate && !isSelected())
if (!mStatic && sVelocityInterpolate && !isSelected())
{
// calculate dt from last update
F32 dt_raw = (F32)(time - mLastInterpUpdateSecs);
@@ -2191,48 +2195,170 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
return TRUE;
}
else
{
// linear motion
// PHYSICS_TIMESTEP is used below to correct for the fact that the velocity in object
// updates represents the average velocity of the last timestep, rather than the final velocity.
// the time dilation above should guarantee that dt is never less than PHYSICS_TIMESTEP, theoretically
//
// There is a problem here if dt is negative. . .
// *TODO: should also wrap linear accel/velocity in check
// to see if object is selected, instead of explicitly
// zeroing it out
LLVector3 accel = getAcceleration();
LLVector3 vel = getVelocity();
if (!(accel.isExactlyZero() && vel.isExactlyZero()))
{
LLVector3 pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt;
// region local
setPositionRegion(pos + getPositionRegion());
setVelocity(vel + accel*dt);
// for objects that are spinning but not translating, make sure to flag them as having moved
setChanged(MOVED | SILHOUETTE);
}
mLastInterpUpdateSecs = time;
{ // Move object based on it's velocity and rotation
interpolateLinearMotion(time, dt);
}
}
if (gNoRender)
{
// Skip drawable stuff if not rendering.
return TRUE;
}
updateDrawable(FALSE);
return TRUE;
}
// Move an object due to idle-time viewer side updates by iterpolating motion
void LLViewerObject::interpolateLinearMotion(const F64 & time, const F32 & dt)
{
// linear motion
// PHYSICS_TIMESTEP is used below to correct for the fact that the velocity in object
// updates represents the average velocity of the last timestep, rather than the final velocity.
// the time dilation above should guarantee that dt is never less than PHYSICS_TIMESTEP, theoretically
//
// *TODO: should also wrap linear accel/velocity in check
// to see if object is selected, instead of explicitly
// zeroing it out
F64 time_since_last_update = time - mLastMessageUpdateSecs;
if (time_since_last_update <= 0.0 || dt <= 0.f)
{
return;
}
LLVector3 accel = getAcceleration();
LLVector3 vel = getVelocity();
if (sMaxUpdateInterpolationTime <= 0.0)
{ // Old code path ... unbounded, simple interpolation
if (!(accel.isExactlyZero() && vel.isExactlyZero()))
{
LLVector3 pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt;
// region local
setPositionRegion(pos + getPositionRegion());
setVelocity(vel + accel*dt);
// for objects that are spinning but not translating, make sure to flag them as having moved
setChanged(MOVED | SILHOUETTE);
}
}
else if (!accel.isExactlyZero() || !vel.isExactlyZero()) // object is moving
{ // Object is moving, and hasn't been too long since we got an update from the server
// Calculate predicted position and velocity
LLVector3 new_pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt;
LLVector3 new_v = accel * dt;
if (time_since_last_update > sPhaseOutUpdateInterpolationTime &&
sPhaseOutUpdateInterpolationTime > 0.0)
{ // Haven't seen a viewer update in a while, check to see if the ciruit is still active
if (mRegionp)
{ // The simulator will NOT send updates if the object continues normally on the path
// predicted by the velocity and the acceleration (often gravity) sent to the viewer
// So check to see if the circuit is blocked, which means the sim is likely in a long lag
LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit( mRegionp->getHost() );
if (cdp)
{
// Find out how many seconds since last packet arrived on the circuit
F64 time_since_last_packet = LLMessageSystem::getMessageTimeSeconds() - cdp->getLastPacketInTime();
if (!cdp->isAlive() || // Circuit is dead or blocked
cdp->isBlocked() || // or doesn't seem to be getting any packets
(time_since_last_packet > sPhaseOutUpdateInterpolationTime))
{
// Start to reduce motion interpolation since we haven't seen a server update in a while
F64 time_since_last_interpolation = time - mLastInterpUpdateSecs;
F64 phase_out = 1.0;
if (time_since_last_update > sMaxUpdateInterpolationTime)
{ // Past the time limit, so stop the object
phase_out = 0.0;
//llinfos << "Motion phase out to zero" << llendl;
// Kill angular motion as well. Note - not adding this due to paranoia
// about stopping rotation for llTargetOmega objects and not having it restart
// setAngularVelocity(LLVector3::zero);
}
else if (mLastInterpUpdateSecs - mLastMessageUpdateSecs > sPhaseOutUpdateInterpolationTime)
{ // Last update was already phased out a bit
phase_out = (sMaxUpdateInterpolationTime - time_since_last_update) /
(sMaxUpdateInterpolationTime - time_since_last_interpolation);
//llinfos << "Continuing motion phase out of " << (F32) phase_out << llendl;
}
else
{ // Phase out from full value
phase_out = (sMaxUpdateInterpolationTime - time_since_last_update) /
(sMaxUpdateInterpolationTime - sPhaseOutUpdateInterpolationTime);
//llinfos << "Starting motion phase out of " << (F32) phase_out << llendl;
}
phase_out = llclamp(phase_out, 0.0, 1.0);
new_pos = new_pos * ((F32) phase_out);
new_v = new_v * ((F32) phase_out);
}
}
}
}
new_pos = new_pos + getPositionRegion();
new_v = new_v + vel;
// Clamp interpolated position to minimum underground and maximum region height
LLVector3d new_pos_global = mRegionp->getPosGlobalFromRegion(new_pos);
F32 min_height;
if (isAvatar())
{ // Make a better guess about AVs not going underground
min_height = LLWorld::getInstance()->resolveLandHeightGlobal(new_pos_global);
min_height += (0.5f * getScale().mV[VZ]);
}
else
{ // This will put the object underground, but we can't tell if it will stop
// at ground level or not
min_height = LLWorld::getInstance()->getMinAllowedZ(this, new_pos_global);
}
new_pos.mV[VZ] = llmax(min_height, new_pos.mV[VZ]);
new_pos.mV[VZ] = llmin(LLWorld::getInstance()->getRegionMaxHeight(), new_pos.mV[VZ]);
// Check to see if it's going off the region
LLVector3 temp(new_pos);
if (temp.clamp(0.f, mRegionp->getWidth()))
{ // Going off this region, so see if we might end up on another region
LLVector3d old_pos_global = mRegionp->getPosGlobalFromRegion(getPositionRegion());
new_pos_global = mRegionp->getPosGlobalFromRegion(new_pos); // Re-fetch in case it got clipped above
// Clip the positions to known regions
LLVector3d clip_pos_global = LLWorld::getInstance()->clipToVisibleRegions(old_pos_global, new_pos_global);
if (clip_pos_global != new_pos_global)
{ // Was clipped, so this means we hit a edge where there is no region to enter
//llinfos << "Hit empty region edge, clipped predicted position to " << mRegionp->getPosRegionFromGlobal(clip_pos_global)
// << " from " << new_pos << llendl;
new_pos = mRegionp->getPosRegionFromGlobal(clip_pos_global);
// Stop motion and get server update for bouncing on the edge
new_v.clear();
setAcceleration(LLVector3::zero);
}
else
{ // Let predicted movement cross into another region
//llinfos << "Predicting region crossing to " << new_pos << llendl;
}
}
// Set new position and velocity
setPositionRegion(new_pos);
setVelocity(new_v);
// for objects that are spinning but not translating, make sure to flag them as having moved
setChanged(MOVED | SILHOUETTE);
}
// Update the last time we did anything
mLastInterpUpdateSecs = time;
}
BOOL LLViewerObject::setData(const U8 *datap, const U32 data_size)
{
LLMemType mt(LLMemType::MTYPE_OBJECT);
@@ -2930,6 +3056,8 @@ void LLViewerObject::setScale(const LLVector3 &scale, BOOL damped)
{
if (!mOnMap)
{
llassert_always(LLWorld::getInstance()->getRegionFromHandle(getRegion()->getHandle()));
gObjectList.addToMap(this);
mOnMap = TRUE;
}

View File

@@ -545,6 +545,9 @@ private:
ExtraParameter* getExtraParameterEntryCreate(U16 param_type);
bool unpackParameterEntry(U16 param_type, LLDataPacker *dp);
// Motion prediction between updates
void interpolateLinearMotion(const F64 & time, const F32 & dt);
public:
//
// Viewer-side only types - use the LL_PCODE_APP mask.
@@ -735,8 +738,24 @@ protected:
mutable LLVector3 mPositionRegion;
mutable LLVector3 mPositionAgent;
static void setPhaseOutUpdateInterpolationTime(F32 value) { sPhaseOutUpdateInterpolationTime = (F64) value; }
static void setMaxUpdateInterpolationTime(F32 value) { sMaxUpdateInterpolationTime = (F64) value; }
static void setVelocityInterpolate(BOOL value) { sVelocityInterpolate = value; }
static void setPingInterpolate(BOOL value) { sPingInterpolate = value; }
private:
static S32 sNumObjects;
static F64 sPhaseOutUpdateInterpolationTime; // For motion interpolation
static F64 sMaxUpdateInterpolationTime; // For motion interpolation
static BOOL sVelocityInterpolate;
static BOOL sPingInterpolate;
//--------------------------------------------------------------------
// For objects that are attachments
//--------------------------------------------------------------------
public:
// <edit>
S32 getAttachmentPoint();
@@ -816,7 +835,4 @@ public:
virtual void updateDrawable(BOOL force_damped);
};
extern BOOL gVelocityInterpolate;
extern BOOL gPingInterpolate;
#endif

View File

@@ -597,33 +597,30 @@ void LLViewerObjectList::dirtyAllObjectInventory()
void LLViewerObjectList::updateApparentAngles(LLAgent &agent)
{
S32 i;
S32 const objects_size = mObjects.size();
S32 num_objects = 0;
LLViewerObject *objectp;
S32 num_updates, max_value;
// The list can have shrinked since mCurLazyUpdateIndex was last updated.
if (mCurLazyUpdateIndex >= objects_size)
{
mCurLazyUpdateIndex = 0;
}
if (NUM_BINS - 1 == mCurBin)
{
num_updates = objects_size - mCurLazyUpdateIndex;
max_value = objects_size;
num_updates = (S32) mObjects.size() - mCurLazyUpdateIndex;
max_value = (S32) mObjects.size();
gTextureList.setUpdateStats(TRUE);
}
else
{
num_updates = (objects_size / NUM_BINS) + 1;
max_value = llmin(objects_size, mCurLazyUpdateIndex + num_updates);
num_updates = ((S32) mObjects.size() / NUM_BINS) + 1;
max_value = llmin((S32) mObjects.size(), mCurLazyUpdateIndex + num_updates);
}
if (!gNoRender)
// Slam priorities for textures that we care about (hovered, selected, and focused)
// Hovered
// Assumes only one level deep of parenting
LLSelectNode* nodep = LLSelectMgr::instance().getHoverNode();
if (nodep)
{
// Slam priorities for textures that we care about (hovered, selected, and focused)
// Hovered
// Assumes only one level deep of parenting
objectp = gHoverView->getLastHoverObject();
objectp = nodep->getObject();
if (objectp)
{
objectp->boostTexturePriority();
@@ -654,6 +651,8 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent)
objectp = mObjects[i];
if (!objectp->isDead())
{
num_objects++;
// Update distance & gpw
objectp->setPixelAreaAndAngle(agent); // Also sets the approx. pixel area
objectp->updateTextures(); // Update the image levels of textures for this object.
@@ -661,7 +660,7 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent)
}
mCurLazyUpdateIndex = max_value;
if (mCurLazyUpdateIndex == objects_size)
if (mCurLazyUpdateIndex == mObjects.size())
{
mCurLazyUpdateIndex = 0;
}
@@ -861,9 +860,24 @@ private:
void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
{
LLMemType mt(LLMemType::MTYPE_OBJECT);
// Update globals
gVelocityInterpolate = gSavedSettings.getBOOL("VelocityInterpolate");
gPingInterpolate = gSavedSettings.getBOOL("PingInterpolate");
LLViewerObject::setVelocityInterpolate( gSavedSettings.getBOOL("VelocityInterpolate") );
LLViewerObject::setPingInterpolate( gSavedSettings.getBOOL("PingInterpolate") );
F32 interp_time = gSavedSettings.getF32("InterpolationTime");
F32 phase_out_time = gSavedSettings.getF32("InterpolationPhaseOut");
if (interp_time < 0.0 ||
phase_out_time < 0.0 ||
phase_out_time > interp_time)
{
llwarns << "Invalid values for InterpolationTime or InterpolationPhaseOut, resetting to defaults" << llendl;
interp_time = 3.0f;
phase_out_time = 1.0f;
}
LLViewerObject::setPhaseOutUpdateInterpolationTime( interp_time );
LLViewerObject::setMaxUpdateInterpolationTime( phase_out_time );
gAnimateTextures = gSavedSettings.getBOOL("AnimateTextures");
// update global timer