Merge pull request #9493 from AndrewMeadows/otherAvatars

optimize, prioritize, and timebox processing of avatar updates
This commit is contained in:
samcake 2017-01-26 13:30:52 -08:00 committed by GitHub
commit 19858bda78
14 changed files with 188 additions and 190 deletions

View file

@ -85,19 +85,6 @@ namespace render {
} }
} }
static uint64_t timeProcessingJoints = 0;
static int32_t numJointsProcessed = 0;
float Avatar::getNumJointsProcessedPerSecond() {
float rate = 0.0f;
if (timeProcessingJoints > 0) {
rate = (float)(numJointsProcessed * USECS_PER_SECOND) / (float)timeProcessingJoints;
}
timeProcessingJoints = 0;
numJointsProcessed = 0;
return rate;
}
Avatar::Avatar(RigPointer rig) : Avatar::Avatar(RigPointer rig) :
AvatarData(), AvatarData(),
_skeletonOffset(0.0f), _skeletonOffset(0.0f),
@ -127,6 +114,7 @@ Avatar::Avatar(RigPointer rig) :
_nameRectGeometryID = geometryCache->allocateID(); _nameRectGeometryID = geometryCache->allocateID();
_leftPointerGeometryID = geometryCache->allocateID(); _leftPointerGeometryID = geometryCache->allocateID();
_rightPointerGeometryID = geometryCache->allocateID(); _rightPointerGeometryID = geometryCache->allocateID();
_lastRenderUpdateTime = usecTimestampNow();
} }
Avatar::~Avatar() { Avatar::~Avatar() {
@ -187,25 +175,35 @@ AABox Avatar::getBounds() const {
} }
void Avatar::animateScaleChanges(float deltaTime) { void Avatar::animateScaleChanges(float deltaTime) {
float currentScale = getUniformScale(); if (_isAnimatingScale) {
auto desiredScale = getDomainLimitedScale(); float currentScale = getUniformScale();
if (currentScale != desiredScale) { float desiredScale = getDomainLimitedScale();
// use exponential decay toward the domain limit clamped scale // use exponential decay toward the domain limit clamped scale
const float SCALE_ANIMATION_TIMESCALE = 0.5f; const float SCALE_ANIMATION_TIMESCALE = 0.5f;
float blendFactor = glm::clamp(deltaTime / SCALE_ANIMATION_TIMESCALE, 0.0f, 1.0f); float blendFactor = glm::clamp(deltaTime / SCALE_ANIMATION_TIMESCALE, 0.0f, 1.0f);
float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * desiredScale; float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * desiredScale;
// snap to the end when we get close enough // snap to the end when we get close enough
const float MIN_RELATIVE_SCALE_ERROR = 0.03f; const float MIN_RELATIVE_ERROR = 0.03f;
if (fabsf(desiredScale - currentScale) / desiredScale < MIN_RELATIVE_SCALE_ERROR) { float relativeError = fabsf(desiredScale - currentScale) / desiredScale;
if (relativeError < MIN_RELATIVE_ERROR) {
animatedScale = desiredScale; animatedScale = desiredScale;
_isAnimatingScale = false;
} }
setScale(glm::vec3(animatedScale)); // avatar scale is uniform setScale(glm::vec3(animatedScale)); // avatar scale is uniform
// TODO: rebuilding the shape constantly is somehwat expensive.
// We should only rebuild after significant change.
rebuildCollisionShape(); rebuildCollisionShape();
} }
} }
void Avatar::setTargetScale(float targetScale) {
AvatarData::setTargetScale(targetScale);
_isAnimatingScale = true;
}
void Avatar::updateAvatarEntities() { void Avatar::updateAvatarEntities() {
PerformanceTimer perfTimer("attachments"); PerformanceTimer perfTimer("attachments");
// - if queueEditEntityMessage sees clientOnly flag it does _myAvatar->updateAvatarEntity() // - if queueEditEntityMessage sees clientOnly flag it does _myAvatar->updateAvatarEntity()
@ -302,63 +300,23 @@ void Avatar::updateAvatarEntities() {
} }
} }
void Avatar::setShouldDie() { bool Avatar::shouldDie() const {
// This will cause the avatar to be shrunk away and removed (the actual Avatar gets removed), but then it comes back. const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND;
_owningAvatarMixer.clear(); return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS;
} }
void Avatar::simulate(float deltaTime) { void Avatar::simulate(float deltaTime, bool inView) {
PROFILE_RANGE(simulation, "simulate");
PerformanceTimer perfTimer("simulate"); PerformanceTimer perfTimer("simulate");
{
if (!isDead() && !_motionState) { PROFILE_RANGE(simulation, "updateJoints");
DependencyManager::get<AvatarManager>()->addAvatarToSimulation(this); if (inView && _hasNewJointData) {
}
animateScaleChanges(deltaTime);
bool avatarInView = false;
{ // update the shouldAnimate flag to match whether or not we will render the avatar.
PerformanceTimer perfTimer("cull");
{
// simple frustum check
PerformanceTimer perfTimer("inView");
ViewFrustum viewFrustum;
qApp->copyDisplayViewFrustum(viewFrustum);
avatarInView = viewFrustum.sphereIntersectsFrustum(getPosition(), getBoundingRadius())
|| viewFrustum.boxIntersectsFrustum(_skeletonModel->getRenderableMeshBound());
}
PerformanceTimer lodPerfTimer("LOD");
if (avatarInView) {
const float MINIMUM_VISIBILITY_FOR_ON = 0.4f;
const float MAXIMUM_VISIBILITY_FOR_OFF = 0.6f;
ViewFrustum viewFrustum;
qApp->copyViewFrustum(viewFrustum);
float visibility = calculateRenderAccuracy(viewFrustum.getPosition(),
getBounds(), DependencyManager::get<LODManager>()->getOctreeSizeScale());
if (!_shouldAnimate) {
if (visibility > MINIMUM_VISIBILITY_FOR_ON) {
_shouldAnimate = true;
qCDebug(interfaceapp) << "Restoring" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for visibility" << visibility;
}
} else if (visibility < MAXIMUM_VISIBILITY_FOR_OFF) {
_shouldAnimate = false;
qCDebug(interfaceapp) << "Optimizing" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for visibility" << visibility;
}
}
}
uint64_t start = usecTimestampNow();
// CRUFT? _shouldSkipRender is never set 'true'
if (_shouldAnimate && avatarInView && !_shouldSkipRender) {
{
PerformanceTimer perfTimer("skeleton");
_skeletonModel->getRig()->copyJointsFromJointData(_jointData); _skeletonModel->getRig()->copyJointsFromJointData(_jointData);
_skeletonModel->simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations); _skeletonModel->simulate(deltaTime, true);
locationChanged(); // joints changed, so if there are any children, update them. locationChanged(); // joints changed, so if there are any children, update them.
_hasNewJointRotations = false; _hasNewJointData = false;
_hasNewJointTranslations = false;
}
{
PerformanceTimer perfTimer("head");
glm::vec3 headPosition = getPosition(); glm::vec3 headPosition = getPosition();
if (!_skeletonModel->getHeadPosition(headPosition)) { if (!_skeletonModel->getHeadPosition(headPosition)) {
headPosition = getPosition(); headPosition = getPosition();
@ -366,16 +324,12 @@ void Avatar::simulate(float deltaTime) {
Head* head = getHead(); Head* head = getHead();
head->setPosition(headPosition); head->setPosition(headPosition);
head->setScale(getUniformScale()); head->setScale(getUniformScale());
head->simulate(deltaTime, false, !_shouldAnimate); head->simulate(deltaTime, false);
} else {
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
_skeletonModel->simulate(deltaTime, false);
} }
} else {
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
getHead()->setPosition(getPosition());
PerformanceTimer perfTimer("skeleton");
_skeletonModel->simulate(deltaTime, false);
} }
timeProcessingJoints += usecTimestampNow() - start;
numJointsProcessed += _jointData.size();
// update animation for display name fade in/out // update animation for display name fade in/out
if ( _displayNameTargetAlpha != _displayNameAlpha) { if ( _displayNameTargetAlpha != _displayNameAlpha) {
@ -394,11 +348,13 @@ void Avatar::simulate(float deltaTime) {
_displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha; _displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha;
} }
measureMotionDerivatives(deltaTime); {
PROFILE_RANGE(simulation, "misc");
simulateAttachments(deltaTime); measureMotionDerivatives(deltaTime);
updatePalms(); simulateAttachments(deltaTime);
updateAvatarEntities(); updatePalms();
updateAvatarEntities();
}
} }
bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const { bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const {
@ -1106,7 +1062,7 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
if (_moving && _motionState) { if (_moving && _motionState) {
_motionState->addDirtyFlags(Simulation::DIRTY_POSITION); _motionState->addDirtyFlags(Simulation::DIRTY_POSITION);
} }
if (_moving || _hasNewJointRotations || _hasNewJointTranslations) { if (_moving || _hasNewJointData) {
locationChanged(); locationChanged();
} }

View file

@ -58,8 +58,6 @@ class Avatar : public AvatarData {
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
public: public:
static float getNumJointsProcessedPerSecond();
explicit Avatar(RigPointer rig = nullptr); explicit Avatar(RigPointer rig = nullptr);
~Avatar(); ~Avatar();
@ -68,7 +66,7 @@ public:
void init(); void init();
void updateAvatarEntities(); void updateAvatarEntities();
void simulate(float deltaTime); void simulate(float deltaTime, bool inView);
virtual void simulateAttachments(float deltaTime); virtual void simulateAttachments(float deltaTime);
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition); virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
@ -141,8 +139,6 @@ public:
Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; } Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
Q_INVOKABLE bool getShouldRender() const { return !_shouldSkipRender; }
/// Scales a world space position vector relative to the avatar position and scale /// Scales a world space position vector relative to the avatar position and scale
/// \param vector position to be scaled. Will store the result /// \param vector position to be scaled. Will store the result
void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const; void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const;
@ -179,7 +175,12 @@ public:
glm::vec3 getUncachedRightPalmPosition() const; glm::vec3 getUncachedRightPalmPosition() const;
glm::quat getUncachedRightPalmRotation() const; glm::quat getUncachedRightPalmRotation() const;
Q_INVOKABLE void setShouldDie(); uint64_t getLastRenderUpdateTime() const { return _lastRenderUpdateTime; }
void setLastRenderUpdateTime(uint64_t time) { _lastRenderUpdateTime = time; }
bool shouldDie() const;
void animateScaleChanges(float deltaTime);
void setTargetScale(float targetScale) override;
public slots: public slots:
@ -230,8 +231,6 @@ protected:
// protected methods... // protected methods...
bool isLookingAtMe(AvatarSharedPointer avatar) const; bool isLookingAtMe(AvatarSharedPointer avatar) const;
virtual void animateScaleChanges(float deltaTime);
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; } glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
@ -261,14 +260,14 @@ protected:
void ensureInScene(AvatarSharedPointer self); void ensureInScene(AvatarSharedPointer self);
private: private:
uint64_t _lastRenderUpdateTime { 0 };
int _leftPointerGeometryID { 0 }; int _leftPointerGeometryID { 0 };
int _rightPointerGeometryID { 0 }; int _rightPointerGeometryID { 0 };
int _nameRectGeometryID { 0 }; int _nameRectGeometryID { 0 };
bool _initialized; bool _initialized;
bool _shouldAnimate { true };
bool _shouldSkipRender { false };
bool _isLookAtTarget { false }; bool _isLookAtTarget { false };
bool _inScene { false }; bool _inScene { false };
bool _isAnimatingScale { false };
float getBoundingRadius() const; float getBoundingRadius() const;

View file

@ -132,53 +132,131 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
Q_LOGGING_CATEGORY(trace_simulation_avatar, "trace.simulation.avatar"); Q_LOGGING_CATEGORY(trace_simulation_avatar, "trace.simulation.avatar");
class AvatarPriority {
public:
AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {}
AvatarSharedPointer avatar;
float priority;
// NOTE: we invert the less-than operator to sort high priorities to front
bool operator<(const AvatarPriority& other) const { return priority > other.priority; }
};
void AvatarManager::updateOtherAvatars(float deltaTime) { void AvatarManager::updateOtherAvatars(float deltaTime) {
// lock the hash for read to check the size // lock the hash for read to check the size
QReadLocker lock(&_hashLock); QReadLocker lock(&_hashLock);
if (_avatarHash.size() < 2 && _avatarFades.isEmpty()) { if (_avatarHash.size() < 2 && _avatarFades.isEmpty()) {
return; return;
} }
lock.unlock(); lock.unlock();
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
PerformanceTimer perfTimer("otherAvatars"); PerformanceTimer perfTimer("otherAvatars");
render::PendingChanges pendingChanges; uint64_t startTime = usecTimestampNow();
// simulate avatars auto avatarMap = getHashCopy();
auto hashCopy = getHashCopy(); QList<AvatarSharedPointer> avatarList = avatarMap.values();
ViewFrustum cameraView;
qApp->copyDisplayViewFrustum(cameraView);
glm::vec3 frustumCenter = cameraView.getPosition();
uint64_t start = usecTimestampNow(); const float OUT_OF_VIEW_PENALTY = -10.0;
AvatarHash::iterator avatarIterator = hashCopy.begin();
while (avatarIterator != hashCopy.end()) {
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
if (avatar == _myAvatar || !avatar->isInitialized()) { std::priority_queue<AvatarPriority> sortedAvatars;
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop. {
// DO NOT update or fade out uninitialized Avatars PROFILE_RANGE(simulation, "sort");
++avatarIterator; for (int32_t i = 0; i < avatarList.size(); ++i) {
} else if (avatar->shouldDie()) { const auto& avatar = std::static_pointer_cast<Avatar>(avatarList.at(i));
removeAvatar(avatarIterator.key()); if (avatar == _myAvatar || !avatar->isInitialized()) {
++avatarIterator; // DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
} else { // DO NOT update or fade out uninitialized Avatars
avatar->ensureInScene(avatar); continue;
avatar->simulate(deltaTime); }
++avatarIterator; if (avatar->shouldDie()) {
removeAvatar(avatar->getID());
continue;
}
if (avatar->isDead()) {
continue;
}
avatar->updateRenderItem(pendingChanges); // priority = weighted linear combination of:
// (a) apparentSize
// (b) proximity to center of view
// (c) time since last update
// (d) TIME_PENALTY to help recently updated entries sort toward back
glm::vec3 avatarPosition = avatar->getPosition();
glm::vec3 offset = avatarPosition - frustumCenter;
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
float radius = avatar->getBoundingRadius();
const glm::vec3& forward = cameraView.getDirection();
float apparentSize = radius / distance;
float cosineAngle = glm::length(offset - glm::dot(offset, forward) * forward) / distance;
const float TIME_PENALTY = 0.080f; // seconds
float age = (float)(startTime - avatar->getLastRenderUpdateTime()) / (float)(USECS_PER_SECOND) - TIME_PENALTY;
// NOTE: we are adding values of different units to get a single measure of "priority".
// Thus we multiply each component by a conversion "weight" that scales its units
// relative to the others. These weights are pure magic tuning and are hard coded in the
// relation below: (hint: unitary weights are not explicityly shown)
float priority = apparentSize + 0.25f * cosineAngle + age;
// decrement priority of avatars outside keyhole
if (distance > cameraView.getCenterRadius()) {
if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) {
priority += OUT_OF_VIEW_PENALTY;
}
}
sortedAvatars.push(AvatarPriority(avatar, priority));
} }
} }
render::PendingChanges pendingChanges;
const uint64_t RENDER_UPDATE_BUDGET = 1500; // usec
const uint64_t MAX_UPDATE_BUDGET = 2000; // usec
uint64_t renderExpiry = startTime + RENDER_UPDATE_BUDGET;
uint64_t maxExpiry = startTime + MAX_UPDATE_BUDGET;
while (!sortedAvatars.empty()) {
const AvatarPriority& sortData = sortedAvatars.top();
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar);
// for ALL avatars...
avatar->ensureInScene(avatar);
if (!avatar->getMotionState()) {
ShapeInfo shapeInfo;
avatar->computeShapeInfo(shapeInfo);
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
if (shape) {
// don't add to the simulation now, instead put it on a list to be added later
AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape);
avatar->setMotionState(motionState);
_motionStatesToAddToPhysics.insert(motionState);
_motionStatesThatMightUpdate.insert(motionState);
}
}
avatar->animateScaleChanges(deltaTime);
uint64_t now = usecTimestampNow();
if (now < renderExpiry) {
// we're within budget
const float OUT_OF_VIEW_THRESHOLD = 0.5f * OUT_OF_VIEW_PENALTY;
bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD;
avatar->simulate(deltaTime, inView);
avatar->updateRenderItem(pendingChanges);
avatar->setLastRenderUpdateTime(startTime);
} else if (now < maxExpiry) {
// we've spent most of our time budget, but we still simulate() the avatar as it if were out of view
// --> some avatars may freeze until their priority trickles up
const bool inView = false;
avatar->simulate(deltaTime, inView);
} else {
// we've spent ALL of our time budget --> bail on the rest of the avatar updates
// --> some scale or fade animations may glitch
// --> some avatar velocity measurements may be a little off
break;
}
sortedAvatars.pop();
}
qApp->getMain3DScene()->enqueuePendingChanges(pendingChanges); qApp->getMain3DScene()->enqueuePendingChanges(pendingChanges);
// simulate avatar fades
simulateAvatarFades(deltaTime); simulateAvatarFades(deltaTime);
PROFILE_COUNTER(simulation_avatar, "NumAvatarsPerSec",
{ { "NumAvatarsPerSec", (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start) } });
PROFILE_COUNTER(simulation_avatar, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } });
} }
void AvatarManager::postUpdate(float deltaTime) { void AvatarManager::postUpdate(float deltaTime) {
@ -201,6 +279,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
while (fadingIterator != _avatarFades.end()) { while (fadingIterator != _avatarFades.end()) {
auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator); auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator);
avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE); avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
avatar->animateScaleChanges(deltaTime);
if (avatar->getTargetScale() <= MIN_FADE_SCALE) { if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
avatar->removeFromScene(*fadingIterator, scene, pendingChanges); avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
// only remove from _avatarFades if we're sure its motionState has been removed from PhysicsEngine // only remove from _avatarFades if we're sure its motionState has been removed from PhysicsEngine
@ -210,7 +289,8 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
++fadingIterator; ++fadingIterator;
} }
} else { } else {
avatar->simulate(deltaTime); const bool inView = true; // HACK
avatar->simulate(deltaTime, inView);
++fadingIterator; ++fadingIterator;
} }
} }
@ -386,21 +466,6 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
} }
} }
void AvatarManager::addAvatarToSimulation(Avatar* avatar) {
assert(!avatar->getMotionState());
ShapeInfo shapeInfo;
avatar->computeShapeInfo(shapeInfo);
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
if (shape) {
// we don't add to the simulation now, we put it on a list to be added later
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
avatar->setMotionState(motionState);
_motionStatesToAddToPhysics.insert(motionState);
_motionStatesThatMightUpdate.insert(motionState);
}
}
void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) { void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()) { if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()) {
for (auto avatarData : _avatarHash) { for (auto avatarData : _avatarHash) {

View file

@ -69,8 +69,6 @@ public:
void handleOutgoingChanges(const VectorOfMotionStates& motionStates); void handleOutgoingChanges(const VectorOfMotionStates& motionStates);
void handleCollisionEvents(const CollisionEvents& collisionEvents); void handleCollisionEvents(const CollisionEvents& collisionEvents);
void addAvatarToSimulation(Avatar* avatar);
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray, Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
const QScriptValue& avatarIdsToInclude = QScriptValue(), const QScriptValue& avatarIdsToInclude = QScriptValue(),
const QScriptValue& avatarIdsToDiscard = QScriptValue()); const QScriptValue& avatarIdsToDiscard = QScriptValue());

View file

@ -70,7 +70,7 @@ void Head::reset() {
_baseYaw = _basePitch = _baseRoll = 0.0f; _baseYaw = _basePitch = _baseRoll = 0.0f;
} }
void Head::simulate(float deltaTime, bool isMine, bool billboard) { void Head::simulate(float deltaTime, bool isMine) {
// Update audio trailing average for rendering facial animations // Update audio trailing average for rendering facial animations
const float AUDIO_AVERAGING_SECS = 0.05f; const float AUDIO_AVERAGING_SECS = 0.05f;
const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f; const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f;
@ -117,7 +117,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
} }
} }
if (!(_isFaceTrackerConnected || billboard)) { if (!_isFaceTrackerConnected) {
if (!_isEyeTrackerConnected) { if (!_isEyeTrackerConnected) {
// Update eye saccades // Update eye saccades
@ -220,7 +220,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
_leftEyePosition = _rightEyePosition = getPosition(); _leftEyePosition = _rightEyePosition = getPosition();
_eyePosition = getPosition(); _eyePosition = getPosition();
if (!billboard && _owningAvatar) { if (_owningAvatar) {
auto skeletonModel = static_cast<Avatar*>(_owningAvatar)->getSkeletonModel(); auto skeletonModel = static_cast<Avatar*>(_owningAvatar)->getSkeletonModel();
if (skeletonModel) { if (skeletonModel) {
skeletonModel->getEyePositions(_leftEyePosition, _rightEyePosition); skeletonModel->getEyePositions(_leftEyePosition, _rightEyePosition);
@ -378,10 +378,6 @@ glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {
return rotationBetween(orientation * IDENTITY_FRONT, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation; return rotationBetween(orientation * IDENTITY_FRONT, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation;
} }
glm::vec3 Head::getScalePivot() const {
return _position;
}
void Head::setFinalPitch(float finalPitch) { void Head::setFinalPitch(float finalPitch) {
_deltaPitch = glm::clamp(finalPitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH) - _basePitch; _deltaPitch = glm::clamp(finalPitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH) - _basePitch;
} }

View file

@ -31,7 +31,7 @@ public:
void init(); void init();
void reset(); void reset();
void simulate(float deltaTime, bool isMine, bool billboard = false); void simulate(float deltaTime, bool isMine);
void setScale(float scale); void setScale(float scale);
void setPosition(glm::vec3 position) { _position = position; } void setPosition(glm::vec3 position) { _position = position; }
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; } void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
@ -70,8 +70,6 @@ public:
bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected) bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
float getAverageLoudness() const { return _averageLoudness; } float getAverageLoudness() const { return _averageLoudness; }
/// \return the point about which scaling occurs.
glm::vec3 getScalePivot() const;
void setDeltaPitch(float pitch) { _deltaPitch = pitch; } void setDeltaPitch(float pitch) { _deltaPitch = pitch; }
float getDeltaPitch() const { return _deltaPitch; } float getDeltaPitch() const { return _deltaPitch; }

View file

@ -220,15 +220,19 @@ void SkeletonModel::updateAttitude() {
// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed), // Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
// but just before head has been simulated. // but just before head has been simulated.
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
updateAttitude(); if (fullUpdate) {
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients()); updateAttitude();
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
Model::simulate(deltaTime, fullUpdate); Model::simulate(deltaTime, fullUpdate);
// let rig compute the model offset // let rig compute the model offset
glm::vec3 registrationPoint; glm::vec3 registrationPoint;
if (_rig->getModelRegistrationPoint(registrationPoint)) { if (_rig->getModelRegistrationPoint(registrationPoint)) {
setOffset(registrationPoint); setOffset(registrationPoint);
}
} else {
Model::simulate(deltaTime, fullUpdate);
} }
if (!isActive() || !_owningAvatar->isMyAvatar()) { if (!isActive() || !_owningAvatar->isMyAvatar()) {

View file

@ -157,7 +157,7 @@ void setupPreferences() {
} }
{ {
auto getter = [=]()->float { return myAvatar->getUniformScale(); }; auto getter = [=]()->float { return myAvatar->getUniformScale(); };
auto setter = [=](float value) { myAvatar->setTargetScaleVerbose(value); }; // The hell? auto setter = [=](float value) { myAvatar->setTargetScale(value); };
auto preference = new SpinnerPreference(AVATAR_TUNING, "Avatar scale (default is 1.0)", getter, setter); auto preference = new SpinnerPreference(AVATAR_TUNING, "Avatar scale (default is 1.0)", getter, setter);
preference->setMin(0.01f); preference->setMin(0.01f);
preference->setMax(99.9f); preference->setMax(99.9f);

View file

@ -1269,6 +1269,7 @@ void Rig::copyJointsIntoJointData(QVector<JointData>& jointDataVec) const {
void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) { void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
PerformanceTimer perfTimer("copyJoints"); PerformanceTimer perfTimer("copyJoints");
PROFILE_RANGE(simulation_animation_detail, "copyJoints");
if (_animSkeleton && jointDataVec.size() == (int)_internalPoseSet._relativePoses.size()) { if (_animSkeleton && jointDataVec.size() == (int)_internalPoseSet._relativePoses.size()) {
// make a vector of rotations in absolute-geometry-frame // make a vector of rotations in absolute-geometry-frame
const AnimPoseVec& absoluteDefaultPoses = _animSkeleton->getAbsoluteDefaultPoses(); const AnimPoseVec& absoluteDefaultPoses = _animSkeleton->getAbsoluteDefaultPoses();

View file

@ -120,8 +120,7 @@ AvatarData::AvatarData() :
_handState(0), _handState(0),
_keyState(NO_KEY_DOWN), _keyState(NO_KEY_DOWN),
_forceFaceTrackerConnected(false), _forceFaceTrackerConnected(false),
_hasNewJointRotations(true), _hasNewJointData(true),
_hasNewJointTranslations(true),
_headData(NULL), _headData(NULL),
_displayNameTargetAlpha(1.0f), _displayNameTargetAlpha(1.0f),
_displayNameAlpha(1.0f), _displayNameAlpha(1.0f),
@ -180,11 +179,6 @@ void AvatarData::setTargetScale(float targetScale) {
_targetScale = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); _targetScale = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
} }
void AvatarData::setTargetScaleVerbose(float targetScale) {
setTargetScale(targetScale);
qCDebug(avatars) << "Changed scale to " << _targetScale;
}
glm::vec3 AvatarData::getHandPosition() const { glm::vec3 AvatarData::getHandPosition() const {
return getOrientation() * _handPosition + getPosition(); return getOrientation() * _handPosition + getPosition();
} }
@ -553,7 +547,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
glm::vec3 newEulerAngles(pitch, yaw, roll); glm::vec3 newEulerAngles(pitch, yaw, roll);
glm::quat newOrientation = glm::quat(glm::radians(newEulerAngles)); glm::quat newOrientation = glm::quat(glm::radians(newEulerAngles));
if (currentOrientation != newOrientation) { if (currentOrientation != newOrientation) {
_hasNewJointRotations = true; _hasNewJointData = true;
setLocalOrientation(newOrientation); setLocalOrientation(newOrientation);
} }
@ -680,7 +674,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
JointData& data = _jointData[i]; JointData& data = _jointData[i];
if (validRotations[i]) { if (validRotations[i]) {
sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, data.rotation); sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, data.rotation);
_hasNewJointRotations = true; _hasNewJointData = true;
data.rotationSet = true; data.rotationSet = true;
} }
} }
@ -715,7 +709,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
JointData& data = _jointData[i]; JointData& data = _jointData[i];
if (validTranslations[i]) { if (validTranslations[i]) {
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
_hasNewJointTranslations = true; _hasNewJointData = true;
data.translationSet = true; data.translationSet = true;
} }
} }

View file

@ -81,8 +81,6 @@ const quint32 AVATAR_MOTION_DEFAULTS =
const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND;
// Bitset of state flags - we store the key state, hand state, Faceshift, eye tracking, and existence of // Bitset of state flags - we store the key state, hand state, Faceshift, eye tracking, and existence of
// referential data in this bit set. The hand state is an octal, but is split into two sections to maintain // referential data in this bit set. The hand state is an octal, but is split into two sections to maintain
// backward compatibility. The bits are ordered as such (0-7 left to right). // backward compatibility. The bits are ordered as such (0-7 left to right).
@ -264,8 +262,7 @@ public:
// Scale // Scale
float getTargetScale() const; float getTargetScale() const;
void setTargetScale(float targetScale); virtual void setTargetScale(float targetScale);
void setTargetScaleVerbose(float targetScale);
float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); } float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); }
void setDomainMinimumScale(float domainMinimumScale) void setDomainMinimumScale(float domainMinimumScale)
@ -371,8 +368,6 @@ public:
const glm::vec3& getTargetVelocity() const { return _targetVelocity; } const glm::vec3& getTargetVelocity() const { return _targetVelocity; }
bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; }
void clearRecordingBasis(); void clearRecordingBasis();
TransformPointer getRecordingBasis() const; TransformPointer getRecordingBasis() const;
void setRecordingBasis(TransformPointer recordingBasis = TransformPointer()); void setRecordingBasis(TransformPointer recordingBasis = TransformPointer());
@ -427,8 +422,7 @@ protected:
KeyState _keyState; KeyState _keyState;
bool _forceFaceTrackerConnected; bool _forceFaceTrackerConnected;
bool _hasNewJointRotations; // set in AvatarData, cleared in Avatar bool _hasNewJointData; // set in AvatarData, cleared in Avatar
bool _hasNewJointTranslations; // set in AvatarData, cleared in Avatar
HeadData* _headData; HeadData* _headData;

View file

@ -272,8 +272,6 @@ void Model::reset() {
} }
bool Model::updateGeometry() { bool Model::updateGeometry() {
PROFILE_RANGE(render_detail, __FUNCTION__);
PerformanceTimer perfTimer("Model::updateGeometry");
bool needFullUpdate = false; bool needFullUpdate = false;
if (!isLoaded()) { if (!isLoaded()) {
@ -1128,7 +1126,9 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
if (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint) { if (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint) {
snapToRegistrationPoint(); snapToRegistrationPoint();
} }
simulateInternal(deltaTime); // update the world space transforms for all joints
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
updateRig(deltaTime, parentTransform);
} }
} }
@ -1138,12 +1138,6 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
_rig->updateAnimations(deltaTime, parentTransform); _rig->updateAnimations(deltaTime, parentTransform);
} }
void Model::simulateInternal(float deltaTime) {
// update the world space transforms for all joints
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
updateRig(deltaTime, parentTransform);
}
// virtual // virtual
void Model::updateClusterMatrices() { void Model::updateClusterMatrices() {
PerformanceTimer perfTimer("Model::updateClusterMatrices"); PerformanceTimer perfTimer("Model::updateClusterMatrices");

View file

@ -312,7 +312,6 @@ protected:
void scaleToFit(); void scaleToFit();
void snapToRegistrationPoint(); void snapToRegistrationPoint();
void simulateInternal(float deltaTime);
virtual void updateRig(float deltaTime, glm::mat4 parentTransform); virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
/// Restores the indexed joint to its default position. /// Restores the indexed joint to its default position.

View file

@ -729,7 +729,7 @@ void ViewFrustum::evalProjectionMatrix(glm::mat4& proj) const {
glm::mat4 ViewFrustum::evalProjectionMatrixRange(float rangeNear, float rangeFar) const { glm::mat4 ViewFrustum::evalProjectionMatrixRange(float rangeNear, float rangeFar) const {
// make sure range near far make sense // make sure range near far make sense
assert(rangeNear > 0.0); assert(rangeNear > 0.0f);
assert(rangeFar > rangeNear); assert(rangeFar > rangeNear);
// recreate a projection matrix for only a range of depth of this frustum. // recreate a projection matrix for only a range of depth of this frustum.
@ -738,7 +738,7 @@ glm::mat4 ViewFrustum::evalProjectionMatrixRange(float rangeNear, float rangeFar
glm::mat4 rangeProj = _projection; glm::mat4 rangeProj = _projection;
float A = -(rangeFar + rangeNear) / (rangeFar - rangeNear); float A = -(rangeFar + rangeNear) / (rangeFar - rangeNear);
float B = -2 * rangeFar*rangeNear / ((rangeFar - rangeNear)); float B = -2.0f * rangeFar*rangeNear / ((rangeFar - rangeNear));
rangeProj[2][2] = A; rangeProj[2][2] = A;
rangeProj[3][2] = B; rangeProj[3][2] = B;