New object/avatar interp.
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user