From fee13d4375c53688f3b3beee2f09b641244e86d4 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 17 Nov 2015 20:06:25 -0800 Subject: [PATCH 01/20] Rebuild on a new branch after original stopped building. --- interface/resources/qml/Stats.qml | 2 +- interface/src/Application.cpp | 19 +++++++++++- interface/src/Application.h | 6 ++++ interface/src/avatar/Avatar.cpp | 34 ++++++++++++++++++-- interface/src/avatar/Avatar.h | 3 ++ interface/src/avatar/AvatarManager.cpp | 43 ++++++++++++++++++++++++++ interface/src/avatar/AvatarManager.h | 19 ++++++++++++ interface/src/ui/Stats.cpp | 2 ++ interface/src/ui/Stats.h | 4 +++ 9 files changed, 127 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index c98d4741b0..336b8ff577 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -40,7 +40,7 @@ Item { Text { color: root.fontColor; font.pixelSize: root.fontSize - text: "Avatars: " + root.avatarCount + text: "Avatars: " + root.avatarCount + ", " + root.avatarRenderedCount + " w/in " + root.avatarRenderDistance + "m" } Text { color: root.fontColor; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 96b8ab74a8..f33cd763ca 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1061,8 +1061,10 @@ void Application::paintGL() { uint64_t now = usecTimestampNow(); static uint64_t lastPaintBegin{ now }; uint64_t diff = now - lastPaintBegin; + float instantaneousFps = 0.0f; if (diff != 0) { - _framesPerSecond.updateAverage((float)USECS_PER_SECOND / (float)diff); + instantaneousFps = (float)USECS_PER_SECOND / (float)diff; + _framesPerSecond.updateAverage(_lastInstantaneousFps); } lastPaintBegin = now; @@ -1326,6 +1328,7 @@ void Application::paintGL() { // Ensure all operations from the previous context are complete before we try to read the fbo glWaitSync(sync, 0, GL_TIMEOUT_IGNORED); glDeleteSync(sync); + uint64_t displayStart = usecTimestampNow(); { PROFILE_RANGE(__FUNCTION__ "/pluginDisplay"); @@ -1338,6 +1341,20 @@ void Application::paintGL() { PerformanceTimer perfTimer("bufferSwap"); displayPlugin->finishFrame(); } + uint64_t displayEnd = usecTimestampNow(); + // Store together, without Application::idle happening in between setting fps and period. + _lastInstantaneousFps = instantaneousFps; + const float displayPeriodUsec = (float)(displayEnd - displayStart); // usecs + _lastPaintWait = displayPeriodUsec / (float)USECS_PER_SECOND; + const float targetPeriod = isHMDMode() ? 1.0f/75.0f : 1.0f/60.0f; + const float actualPeriod = 1.0f / instantaneousFps; + const float nSyncsByFrameRate = round(actualPeriod / targetPeriod); + const float accuracyAllowance = 0.0005; + const float nSyncsByPaintWait = floor((_lastPaintWait + accuracyAllowance) / targetPeriod); // sometimes paint goes over and it isn't reflected in actualPeriod + const float modularPeriod = (nSyncsByFrameRate + nSyncsByPaintWait) * targetPeriod; + const float deducedNonVSyncPeriod = modularPeriod - _lastPaintWait; + _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; + } { diff --git a/interface/src/Application.h b/interface/src/Application.h index 39e3879707..01d0c72645 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -159,6 +159,9 @@ public: bool isForeground() const { return _isForeground; } float getFps() const { return _fps; } + float getLastInstanteousFps() const { return _lastInstantaneousFps; } + float getLastPaintWait() const { return _lastPaintWait; }; + float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; } float getFieldOfView() { return _fieldOfView.get(); } void setFieldOfView(float fov); @@ -430,6 +433,9 @@ private: float _fps; QElapsedTimer _timerStart; QElapsedTimer _lastTimeUpdated; + float _lastInstantaneousFps { 0.0f }; + float _lastPaintWait { 0.0f }; + float _lastDeducedNonVSyncFps { 0.0f }; ShapeManager _shapeManager; PhysicalEntitySimulation _entitySimulation; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b979334383..14824ad118 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -97,6 +97,7 @@ Avatar::Avatar(RigPointer rig) : _moving(false), _initialized(false), _shouldRenderBillboard(true), + _shouldSkipRender(true), _voiceSphereID(GeometryCache::UNKNOWN_ID) { // we may have been created in the network thread, but we live in the main thread @@ -114,7 +115,7 @@ Avatar::~Avatar() { } } -const float BILLBOARD_LOD_DISTANCE = 40.0f; +/*const fixme*/ float BILLBOARD_LOD_DISTANCE = 40.0f; void Avatar::init() { getHead()->init(); @@ -181,12 +182,36 @@ void Avatar::simulate(float deltaTime) { // update the billboard render flag const float BILLBOARD_HYSTERESIS_PROPORTION = 0.1f; + BILLBOARD_LOD_DISTANCE = DependencyManager::get()->getFIXMEupdate(); if (_shouldRenderBillboard) { if (getLODDistance() < BILLBOARD_LOD_DISTANCE * (1.0f - BILLBOARD_HYSTERESIS_PROPORTION)) { _shouldRenderBillboard = false; + qCDebug(interfaceapp) << "Unbillboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); } } else if (getLODDistance() > BILLBOARD_LOD_DISTANCE * (1.0f + BILLBOARD_HYSTERESIS_PROPORTION)) { _shouldRenderBillboard = true; + qCDebug(interfaceapp) << "Billboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); + } +#define PID_TUNING 1 +#ifdef PID_TUNING + const float SKIP_HYSTERESIS_PROPORTION = 0.0f; +#else + const float SKIP_HYSTERESIS_PROPORTION = BILLBOARD_HYSTERESIS_PROPORTION; +#endif + float renderDistance = DependencyManager::get()->getRenderDistance(); + float distance = glm::distance(qApp->getCamera()->getPosition(), _position); + if (_shouldSkipRender) { + if (distance < renderDistance * (1.0f - SKIP_HYSTERESIS_PROPORTION)) { + _shouldSkipRender = false; +#ifndef PID_TUNING + qCDebug(interfaceapp) << "Rerendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); +#endif + } + } else if (distance > renderDistance * (1.0f + SKIP_HYSTERESIS_PROPORTION)) { + _shouldSkipRender = true; +#ifndef PID_TUNING + qCDebug(interfaceapp) << "Unrendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); +#endif } // simple frustum check @@ -199,7 +224,7 @@ void Avatar::simulate(float deltaTime) { getHand()->simulate(deltaTime, false); } - if (!_shouldRenderBillboard && inViewFrustum) { + if (!_shouldRenderBillboard && !_shouldSkipRender && inViewFrustum) { { PerformanceTimer perfTimer("skeleton"); for (int i = 0; i < _jointData.size(); i++) { @@ -585,10 +610,13 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::fixupModelsInScene() { - // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene render::ScenePointer scene = qApp->getMain3DScene(); + _skeletonModel.setVisibleInScene(!_shouldSkipRender, scene); + if (_shouldSkipRender) { + return; + } render::PendingChanges pendingChanges; if (_skeletonModel.isRenderable() && _skeletonModel.needsFixupInScene()) { _skeletonModel.removeFromScene(scene, pendingChanges); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 44b5d91015..cb238dc82c 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -140,6 +140,8 @@ public: Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; } Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; } + Q_INVOKABLE bool getShouldSkipRendering() const { return _shouldSkipRender; } + /// Scales a world space position vector relative to the avatar position and scale /// \param vector position to be scaled. Will store the result void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const; @@ -226,6 +228,7 @@ private: bool _initialized; NetworkTexturePointer _billboardTexture; bool _shouldRenderBillboard; + bool _shouldSkipRender { false }; bool _isLookAtTarget; void renderBillboard(RenderArgs* renderArgs); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 4b9bfd21a3..b2a2e27999 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -61,12 +61,34 @@ void AvatarManager::registerMetaTypes(QScriptEngine* engine) { qScriptRegisterSequenceMetaType >(engine); } +#define TARGET_FPS 60.0f +#define TARGET_PERIOD_MS (1000.0f / TARGET_FPS) AvatarManager::AvatarManager(QObject* parent) : _avatarFades() { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); _myAvatar = std::make_shared(std::make_shared()); + _renderDistanceController.setMeasuredValueSetpoint(TARGET_FPS); //FIXME + const float TREE_SCALE = 32768.0f; // Not in shared library, alas. + const float SMALLEST_REASONABLE_HORIZON = 0.5f; // FIXME 5 + _renderDistanceController.setControlledValueHighLimit(1.0f/SMALLEST_REASONABLE_HORIZON); + _renderDistanceController.setControlledValueLowLimit(1.0f/TREE_SCALE); + + // Advice for tuning parameters: + // See PIDController.h. There's a sectionon tuning in the reference. + // Turn off HYSTERESIS_PROPORTION and extra logging by defining PID_TUNING in Avatar.cpp. + // Turn on logging with the following: + _renderDistanceController.setHistorySize("avatar render", TARGET_FPS * 4); // FIXME + // KP is usually tuned by setting the other constants to zero, finding the maximum value that doesn't oscillate, + // and taking about 0.6 of that. A typical osciallation would be with error=37fps with avatars 10m away, so + // KP*37=1/10 => KP(oscillating)=0.1/37 = 0.0027 + _renderDistanceController.setKP(0.0012f); + // alt: + // Our anti-windup limits accumulated error to 10*targetFrameRate, so the sanity check on KI is + // KI*750=controlledValueHighLimit=1 => KI=1/750. + _renderDistanceController.setKI(0.001); + _renderDistanceController.setKD(0.0001); // a touch of kd increases the speed by which we get there auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); @@ -117,9 +139,26 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); PerformanceTimer perfTimer("otherAvatars"); + const float FEED_FORWARD_RANGE = 2; + const float fps = qApp->getLastInstanteousFps(); + const float paintWait = qApp->getLastPaintWait(); + //const float modularizedPeriod = floor((1000.0f / std::min(fps, TARGET_FPS)) / TARGET_PERIOD_MS) * TARGET_PERIOD_MS; + // measured value: 1) bigger => more desirable plant activity (i.e., more rendering), 2) setpoint=TARGET_PERIOD_MS=13.333 + // single vsync: no load=>1or2. high load=>12or13 + // over vsync: just over: 13. way over: 14...15...16 + //const float effective = ((1000.0f / fps) < TARGET_PERIOD_MS) ? (TARGET_PERIOD_MS - paintWait) : ((2.0f * TARGET_PERIOD_MS) - paintWait); + const float deduced = qApp->getLastDeducedNonVSyncFps(); + const bool isAtSetpoint = false; //FIXME fabsf(effectiveFps - _renderDistanceController.getMeasuredValueSetpoint()) < FEED_FORWARD_RANGE; + const float distance = 1.0f / _renderDistanceController.update(deduced + (isAtSetpoint ? _renderFeedForward : 0.0f), deltaTime, isAtSetpoint, fps, paintWait); + + const float RENDER_DISTANCE_DEADBAND = 0.0f; //FIXME 0.3f; // meters + if (fabsf(distance - _renderDistance) > RENDER_DISTANCE_DEADBAND) { + _renderDistance = distance; + } // simulate avatars AvatarHash::iterator avatarIterator = _avatarHash.begin(); + int renderableCount = 0; while (avatarIterator != _avatarHash.end()) { auto avatar = std::dynamic_pointer_cast(avatarIterator.value()); @@ -135,10 +174,14 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } else { avatar->startUpdate(); avatar->simulate(deltaTime); + if (!avatar->getShouldSkipRendering()) { + renderableCount++; + } avatar->endUpdate(); ++avatarIterator; } } + _renderedAvatarCount = renderableCount; // simulate avatar fades simulateAvatarFades(deltaTime); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index fa0593368b..92f9958962 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -18,6 +18,7 @@ #include #include +#include #include "Avatar.h" #include "AvatarMotionState.h" @@ -43,6 +44,7 @@ public: void clearOtherAvatars(); bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; } + PIDController& getRenderDistanceController() { return _renderDistanceController; } class LocalLight { public: @@ -64,6 +66,17 @@ public: void handleCollisionEvents(const CollisionEvents& collisionEvents); void updateAvatarPhysicsShape(const QUuid& id); + Q_INVOKABLE int getNumberRendered() { return _renderedAvatarCount; } + Q_INVOKABLE float getRenderDistance() { return _renderDistance; } + Q_INVOKABLE float getFIXMEupdate() { return _fixmeUpdate; } + Q_INVOKABLE void setFIXMEupdate(float f) { _fixmeUpdate = f; } + Q_INVOKABLE void setFIXMEkp(float f) { _renderDistanceController.setKP(f); } + Q_INVOKABLE void setFIXMEki(float f) { _renderDistanceController.setKI(f); } + Q_INVOKABLE void setFIXMEkd(float f) { _renderDistanceController.setKD(f); } + Q_INVOKABLE void setFIXMEbias(float f) { _renderDistanceController.setBias(f); } + Q_INVOKABLE void setFIXMElow(float f) { _renderDistanceController.setControlledValueLowLimit(f); } + Q_INVOKABLE void setFIXMEhigh(float f) { _renderDistanceController.setControlledValueHighLimit(f); } + Q_INVOKABLE void setFIXMEfeedForward(float f) { _renderFeedForward = f; } public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } @@ -88,6 +101,12 @@ private: QVector _localLights; bool _shouldShowReceiveStats = false; + float _fixmeUpdate = 40.0f; + float _renderDistance { 40.0f }; + float _renderFeedForward { 5.0f }; + int _renderedAvatarCount {0}; + PIDController _renderDistanceController {}; + SetOfAvatarMotionStates _avatarMotionStates; SetOfMotionStates _motionStatesToAdd; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 1c0c03e16c..3f27052f60 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -115,6 +115,8 @@ void Stats::updateStats(bool force) { auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves STAT_UPDATE(avatarCount, avatarManager->size() - 1); + STAT_UPDATE(avatarRenderedCount, avatarManager->getNumberRendered()); + STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating STAT_UPDATE(serverCount, nodeList->size()); STAT_UPDATE(framerate, (int)qApp->getFps()); STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 39e3c6b24a..12caf19d18 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -36,6 +36,8 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, simrate, 0) STATS_PROPERTY(int, avatarSimrate, 0) STATS_PROPERTY(int, avatarCount, 0) + STATS_PROPERTY(int, avatarRenderedCount, 0) + STATS_PROPERTY(int, avatarRenderDistance, 0) STATS_PROPERTY(int, packetInCount, 0) STATS_PROPERTY(int, packetOutCount, 0) STATS_PROPERTY(float, mbpsIn, 0) @@ -117,6 +119,8 @@ signals: void simrateChanged(); void avatarSimrateChanged(); void avatarCountChanged(); + void avatarRenderedCountChanged(); + void avatarRenderDistanceChanged(); void packetInCountChanged(); void packetOutCountChanged(); void mbpsInChanged(); From 97898a1ad1a4ce6972c52fe5dbf1736a5b34acc6 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 17 Nov 2015 20:43:40 -0800 Subject: [PATCH 02/20] forgot new files --- libraries/shared/src/PIDController.cpp | 71 ++++++++++++++++++++ libraries/shared/src/PIDController.h | 91 ++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 libraries/shared/src/PIDController.cpp create mode 100644 libraries/shared/src/PIDController.h diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp new file mode 100644 index 0000000000..42447e9809 --- /dev/null +++ b/libraries/shared/src/PIDController.cpp @@ -0,0 +1,71 @@ +// +// PIDController.cpp +// libraries/shared/src +// +// Created by Howard Stearns 11/13/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include "SharedLogging.h" +#include "PIDController.h" + +float PIDController::update(float measuredValue, float dt, bool resetAccumulator, float FIXME1, float FIXME2) { + const float error = getMeasuredValueSetpoint() - measuredValue; // Sign is the direction we want measuredValue to go. Positive means go higher. + + const float p = getKP() * error; // term is Proportional to error + + const float accumulatedError = glm::clamp(error * dt + (resetAccumulator ? 0 : _lastAccumulation), // integrate error + getAccumulatedValueLowLimit(), // but clamp by anti-windup limits + getAccumulatedValueHighLimit()); + const float i = getKI() * accumulatedError; // term is Integral of error + + const float changeInError = (error - _lastError) / dt; // positive value denotes increasing deficit + const float d = getKD() * changeInError; // term is Derivative of Error + + const float computedValue = glm::clamp(p + i + d + getBias(), + getControlledValueLowLimit(), + getControlledValueHighLimit()); + + if (_history.capacity()) { // THIS SECTION ONLY FOR LOGGING + // Don't report each update(), as the I/O messes with the results a lot. + // Instead, add to history, and then dump out at once when full. + // Typically, the first few values reported in each batch should be ignored. + const int n = _history.size(); + _history.resize(n + 1); + Row& next = _history[n]; + next.measured = measuredValue; + next.FIXME1 = FIXME1; + next.FIXME2 = FIXME2; + next.dt = dt; + next.error = error; + next.accumulated = accumulatedError; + next.changed = changeInError; + next.p = p; + next.i = i; + next.d = d; + next.computed = computedValue; + if (_history.size() == _history.capacity()) { // report when buffer is full + qCDebug(shared) << _label << "measured dt FIXME || error accumulated changed || p i d controlled"; + for (int i = 0; i < _history.size(); i++) { + Row& row = _history[i]; + qCDebug(shared) << row.measured << row.dt << row.FIXME1 << row.FIXME2 << + "||" << row.error << row.accumulated << row.changed << + "||" << row.p << row.i << row.d << row.computed; + } + qCDebug(shared) << "Limits: accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() << + "controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() << + "kp/ki/kd/bias" << getKP() << getKI() << getKD() << getBias(); + _history.resize(0); + } + } + + // update state for next time + _lastError = error; + _lastAccumulation = accumulatedError; + return computedValue; +} \ No newline at end of file diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h new file mode 100644 index 0000000000..dd0c4a8528 --- /dev/null +++ b/libraries/shared/src/PIDController.h @@ -0,0 +1,91 @@ +// +// PIDController.h +// libraries/shared/src +// +// Given a measure of system performance (such as frame rate, where bigger denotes more system work), +// compute a value that the system can take as input to control the amount of work done (such as an 1/LOD-distance, +// where bigger tends to give a higher measured system performance value). The controller's job is to compute a +// controlled value such that the measured value stays near the specified setpoint, even as system load changes. +// See http://www.wetmachine.com/inventing-the-future/mostly-reliable-performance-of-software-processes-by-dynamic-control-of-quality-parameters/ +// +// Created by Howard Stearns 11/13/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_PIDController_h +#define hifi_PIDController_h + +#include +#include + +// Although our coding standard shuns abbreviations, the control systems literature pretty uniformly uses p, i, d, and dt rather than +// proportionalTerm, integralTerm, derivativeTerm, and deltaTime. Here we will be consistent with the literature. +class PIDController { + +public: + // These three are the main interface: + void setMeasuredValueSetpoint(float newValue) { _measuredValueSetpoint = newValue; } + float update(float measuredValue, float dt, bool resetAccumulator = false, float FIXME1 = 0.0f, float FIXME2 = 0.0f); // returns the new computedValue + void setHistorySize(QString label = QString(""), int size = 0) { _history.reserve(size); _history.resize(0); _label = label; } // non-empty does logging + + // There are several values that rarely change and might be thought of as "constants", but which do change during tuning, debugging, or other + // special-but-expected circumstances. Thus the instance vars are not const. + float getMeasuredValueSetpoint() const { return _measuredValueSetpoint; } + // In normal operation (where we can easily reach setpoint), controlledValue is typcially pinned at max. + // Defaults to [0, max float], but for 1/LODdistance, it might be, say, [0, 0.2 or 0.1] + float getControlledValueLowLimit() const { return _controlledValueLowLimit; } + float getControlledValueHighLimit() const { return _controlledValueHighLimit; } + float getAntiWindupFactor() const { return _antiWindupFactor; } // default 10 + float getKP() const { return _kp; } + float getKI() const { return _ki; } + float getKD() const { return _kd; } + float getBias() const { return _bias; } + float getAccumulatedValueHighLimit() const { return getAntiWindupFactor() * getMeasuredValueSetpoint(); } + float getAccumulatedValueLowLimit() const { return -getAntiWindupFactor() * getMeasuredValueSetpoint(); } + + void setControlledValueLowLimit(float newValue) { _controlledValueLowLimit = newValue; } + void setControlledValueHighLimit(float newValue) { _controlledValueHighLimit = newValue; } + void setAntiWindupFactor(float newValue) { _antiWindupFactor = newValue; } + void setKP(float newValue) { _kp = newValue; } + void setKI(float newValue) { _ki = newValue; } + void setKD(float newValue) { _kd = newValue; } + void setBias(float newValue) { _bias = newValue; } + + class Row { // one row of accumulated history, used only for logging (if at all) + public: + float FIXME1; + float FIXME2; + float measured; + float dt; + float error; + float accumulated; + float changed; + float p; + float i; + float d; + float bias; + float computed; + }; +protected: + float _measuredValueSetpoint { 0.0f }; + float _controlledValueLowLimit { 0.0f }; + float _controlledValueHighLimit { std::numeric_limits::max() }; + float _antiWindupFactor { 10.0f }; + float _kp { 0.0f }; + float _ki { 0.0f }; + float _kd { 0.0f }; + float _bias { 0.0f }; + + // Controller state + float _lastError{ 0.0f }; + float _lastAccumulation{ 0.0f }; + // reporting + QVector _history{}; + QString _label{ "" }; + +}; + +#endif // hifi_PIDController_h From 42a1ee353e2acddf174ba057c65a9aa299e9646b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 18 Nov 2015 14:09:33 -0800 Subject: [PATCH 03/20] checkpoint --- interface/src/Application.cpp | 25 +++++---- interface/src/avatar/AvatarManager.cpp | 53 +++++++++---------- libraries/shared/src/PIDController.cpp | 72 ++++++++++++++------------ libraries/shared/src/PIDController.h | 2 + 4 files changed, 82 insertions(+), 70 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f33cd763ca..2eaf898e72 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1095,6 +1095,21 @@ void Application::paintGL() { _inPaint = true; Finally clearFlagLambda([this] { _inPaint = false; }); + _lastInstantaneousFps = instantaneousFps; + // Time between previous paintGL call and this one, which can vary not only with vSync misses, but also with QT timing. + // This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second. + // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. + const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize + const float targetPeriod = isHMDMode() ? 1.0f / 75.0f : 1.0f / 60.0f; + const float nSyncsByFrameRate = round(actualPeriod / targetPeriod); + const float accuracyAllowance = 0.0005f; // sometimes paint goes over and it isn't reflected in actualPeriod + const float nSyncsByPaintWait = floor((_lastPaintWait + accuracyAllowance) / targetPeriod); + const float nSyncs = nSyncsByFrameRate + nSyncsByPaintWait; + const float modularPeriod = ((nSyncs - 1) * targetPeriod) + actualPeriod; + const float deducedNonVSyncPeriod = modularPeriod - _lastPaintWait; + _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; + + auto displayPlugin = getActiveDisplayPlugin(); displayPlugin->preRender(); _offscreenContext->makeCurrent(); @@ -1342,18 +1357,8 @@ void Application::paintGL() { displayPlugin->finishFrame(); } uint64_t displayEnd = usecTimestampNow(); - // Store together, without Application::idle happening in between setting fps and period. - _lastInstantaneousFps = instantaneousFps; const float displayPeriodUsec = (float)(displayEnd - displayStart); // usecs _lastPaintWait = displayPeriodUsec / (float)USECS_PER_SECOND; - const float targetPeriod = isHMDMode() ? 1.0f/75.0f : 1.0f/60.0f; - const float actualPeriod = 1.0f / instantaneousFps; - const float nSyncsByFrameRate = round(actualPeriod / targetPeriod); - const float accuracyAllowance = 0.0005; - const float nSyncsByPaintWait = floor((_lastPaintWait + accuracyAllowance) / targetPeriod); // sometimes paint goes over and it isn't reflected in actualPeriod - const float modularPeriod = (nSyncsByFrameRate + nSyncsByPaintWait) * targetPeriod; - const float deducedNonVSyncPeriod = modularPeriod - _lastPaintWait; - _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index b2a2e27999..18e4dd40be 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -61,34 +61,12 @@ void AvatarManager::registerMetaTypes(QScriptEngine* engine) { qScriptRegisterSequenceMetaType >(engine); } -#define TARGET_FPS 60.0f -#define TARGET_PERIOD_MS (1000.0f / TARGET_FPS) AvatarManager::AvatarManager(QObject* parent) : _avatarFades() { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); _myAvatar = std::make_shared(std::make_shared()); - _renderDistanceController.setMeasuredValueSetpoint(TARGET_FPS); //FIXME - const float TREE_SCALE = 32768.0f; // Not in shared library, alas. - const float SMALLEST_REASONABLE_HORIZON = 0.5f; // FIXME 5 - _renderDistanceController.setControlledValueHighLimit(1.0f/SMALLEST_REASONABLE_HORIZON); - _renderDistanceController.setControlledValueLowLimit(1.0f/TREE_SCALE); - - // Advice for tuning parameters: - // See PIDController.h. There's a sectionon tuning in the reference. - // Turn off HYSTERESIS_PROPORTION and extra logging by defining PID_TUNING in Avatar.cpp. - // Turn on logging with the following: - _renderDistanceController.setHistorySize("avatar render", TARGET_FPS * 4); // FIXME - // KP is usually tuned by setting the other constants to zero, finding the maximum value that doesn't oscillate, - // and taking about 0.6 of that. A typical osciallation would be with error=37fps with avatars 10m away, so - // KP*37=1/10 => KP(oscillating)=0.1/37 = 0.0027 - _renderDistanceController.setKP(0.0012f); - // alt: - // Our anti-windup limits accumulated error to 10*targetFrameRate, so the sanity check on KI is - // KI*750=controlledValueHighLimit=1 => KI=1/750. - _renderDistanceController.setKI(0.001); - _renderDistanceController.setKD(0.0001); // a touch of kd increases the speed by which we get there auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); @@ -112,6 +90,29 @@ void AvatarManager::init() { _myAvatar->addToScene(_myAvatar, scene, pendingChanges); } scene->enqueuePendingChanges(pendingChanges); + + const float target_fps = 75.0f; // qApp->isHMDMode() ? 75.0f : 60.0f; + _renderDistanceController.setMeasuredValueSetpoint(target_fps); + const float TREE_SCALE = 32768.0f; // Not in shared library, alas. + const float SMALLEST_REASONABLE_HORIZON = 0.5f; // FIXME 5 + _renderDistanceController.setControlledValueHighLimit(1.0f / SMALLEST_REASONABLE_HORIZON); + _renderDistanceController.setControlledValueLowLimit(1.0f / TREE_SCALE); + + // Advice for tuning parameters: + // See PIDController.h. There's a sectionon tuning in the reference. + // Turn off HYSTERESIS_PROPORTION and extra logging by defining PID_TUNING in Avatar.cpp. + // Turn on logging with the following: + _renderDistanceController.setHistorySize("avatar render", target_fps * 4); // FIXME + // KP is usually tuned by setting the other constants to zero, finding the maximum value that doesn't oscillate, + // and taking about 0.6 of that. A typical osciallation would be with error=37fps with avatars 10m away, so + // KP*37=1/10 => KP(oscillating)=0.1/37 = 0.0027 + _renderDistanceController.setKP(0.0015f); + // alt: + // Our anti-windup limits accumulated error to 10*targetFrameRate, so the sanity check on KI is + // KI*750=controlledValueHighLimit=1 => KI=1/750. + _renderDistanceController.setKI(0.001f); + _renderDistanceController.setKD(0.0001f); // a touch of kd increases the speed by which we get there + } void AvatarManager::updateMyAvatar(float deltaTime) { @@ -142,14 +143,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { const float FEED_FORWARD_RANGE = 2; const float fps = qApp->getLastInstanteousFps(); const float paintWait = qApp->getLastPaintWait(); - //const float modularizedPeriod = floor((1000.0f / std::min(fps, TARGET_FPS)) / TARGET_PERIOD_MS) * TARGET_PERIOD_MS; - // measured value: 1) bigger => more desirable plant activity (i.e., more rendering), 2) setpoint=TARGET_PERIOD_MS=13.333 - // single vsync: no load=>1or2. high load=>12or13 - // over vsync: just over: 13. way over: 14...15...16 - //const float effective = ((1000.0f / fps) < TARGET_PERIOD_MS) ? (TARGET_PERIOD_MS - paintWait) : ((2.0f * TARGET_PERIOD_MS) - paintWait); const float deduced = qApp->getLastDeducedNonVSyncFps(); const bool isAtSetpoint = false; //FIXME fabsf(effectiveFps - _renderDistanceController.getMeasuredValueSetpoint()) < FEED_FORWARD_RANGE; - const float distance = 1.0f / _renderDistanceController.update(deduced + (isAtSetpoint ? _renderFeedForward : 0.0f), deltaTime, isAtSetpoint, fps, paintWait); + //const float distance = 1.0f / _renderDistanceController.update(deduced + (isAtSetpoint ? _renderFeedForward : 0.0f), deltaTime, isAtSetpoint, fps, paintWait); + const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, isAtSetpoint, fps, paintWait); const float RENDER_DISTANCE_DEADBAND = 0.0f; //FIXME 0.3f; // meters if (fabsf(distance - _renderDistance) > RENDER_DISTANCE_DEADBAND) { diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp index 42447e9809..2756ab615f 100644 --- a/libraries/shared/src/PIDController.cpp +++ b/libraries/shared/src/PIDController.cpp @@ -31,41 +31,49 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator getControlledValueLowLimit(), getControlledValueHighLimit()); - if (_history.capacity()) { // THIS SECTION ONLY FOR LOGGING - // Don't report each update(), as the I/O messes with the results a lot. - // Instead, add to history, and then dump out at once when full. - // Typically, the first few values reported in each batch should be ignored. - const int n = _history.size(); - _history.resize(n + 1); - Row& next = _history[n]; - next.measured = measuredValue; - next.FIXME1 = FIXME1; - next.FIXME2 = FIXME2; - next.dt = dt; - next.error = error; - next.accumulated = accumulatedError; - next.changed = changeInError; - next.p = p; - next.i = i; - next.d = d; - next.computed = computedValue; - if (_history.size() == _history.capacity()) { // report when buffer is full - qCDebug(shared) << _label << "measured dt FIXME || error accumulated changed || p i d controlled"; - for (int i = 0; i < _history.size(); i++) { - Row& row = _history[i]; - qCDebug(shared) << row.measured << row.dt << row.FIXME1 << row.FIXME2 << - "||" << row.error << row.accumulated << row.changed << - "||" << row.p << row.i << row.d << row.computed; - } - qCDebug(shared) << "Limits: accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() << - "controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() << - "kp/ki/kd/bias" << getKP() << getKI() << getKD() << getBias(); - _history.resize(0); - } - } + if (_history.capacity()) { // if logging/reporting + updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue, FIXME1, FIXME2); + } // update state for next time _lastError = error; _lastAccumulation = accumulatedError; return computedValue; +} + +// Just for logging/reporting. Used when picking/verifying the operational parameters. +void PIDController::updateHistory(float measuredValue, float dt, float error, float accumulatedError, float changeInError, float p, float i, float d, float computedValue, float FIXME1, float FIXME2) { + // Don't report each update(), as the I/O messes with the results a lot. + // Instead, add to history, and then dump out at once when full. + // Typically, the first few values reported in each batch should be ignored. + const int n = _history.size(); + _history.resize(n + 1); + Row& next = _history[n]; + next.measured = measuredValue; + next.FIXME1 = FIXME1; + next.FIXME2 = FIXME2; + next.dt = dt; + next.error = error; + next.accumulated = accumulatedError; + next.changed = changeInError; + next.p = p; + next.i = i; + next.d = d; + next.computed = computedValue; + if (_history.size() == _history.capacity()) { // report when buffer is full + reportHistory(); + _history.resize(0); + } +} +void PIDController::reportHistory() { + qCDebug(shared) << _label << "measured dt FIXME || error accumulated changed || p i d controlled"; + for (int i = 0; i < _history.size(); i++) { + Row& row = _history[i]; + qCDebug(shared) << row.measured << (row.dt * 1000) << row.FIXME1 << (row.FIXME2 * 1000) << + "||" << row.error << row.accumulated << row.changed << + "||" << row.p << row.i << row.d << row.computed; + } + qCDebug(shared) << "Limits: setpoint" << getMeasuredValueSetpoint() << "accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() << + "controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() << + "kp/ki/kd/bias" << getKP() << getKI() << getKD() << getBias(); } \ No newline at end of file diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h index dd0c4a8528..a2adbf98ab 100644 --- a/libraries/shared/src/PIDController.h +++ b/libraries/shared/src/PIDController.h @@ -70,6 +70,8 @@ public: float computed; }; protected: + void reportHistory(); + void updateHistory(float measured, float dt, float error, float accumulatedError, float changeInErro, float p, float i, float d, float computedValue, float FIXME1, float FIXME2); float _measuredValueSetpoint { 0.0f }; float _controlledValueLowLimit { 0.0f }; float _controlledValueHighLimit { std::numeric_limits::max() }; From 7d30cd0159bb8692514cdfb235c0e5814451e9c0 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 19 Nov 2015 09:18:40 -0800 Subject: [PATCH 04/20] checkpoint: it works! --- interface/src/Application.cpp | 22 +++++++++++++--------- interface/src/avatar/Avatar.cpp | 2 +- interface/src/avatar/AvatarManager.cpp | 26 +++++++------------------- interface/src/avatar/AvatarManager.h | 3 +++ libraries/shared/src/PIDController.cpp | 2 +- 5 files changed, 25 insertions(+), 30 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2eaf898e72..84b14497cd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1095,20 +1095,24 @@ void Application::paintGL() { _inPaint = true; Finally clearFlagLambda([this] { _inPaint = false; }); - _lastInstantaneousFps = instantaneousFps; + // Some LOD-like controls need to know a smoothly varying "potential" frame rate that doesn't + // include time waiting for vsync, and which can report a number above target if we've got the headroom. + // For example, if we're shooting for 75fps and paintWait is 3.3333ms (= 75% * 13.33ms), our deducedNonVSyncFps + // would be 100fps. In principle, a paintWait of zero would have deducedNonVSyncFps=75. + // Here we make a guess for deducedNonVSyncFps = 1 / deducedNonVSyncPeriod. + // // Time between previous paintGL call and this one, which can vary not only with vSync misses, but also with QT timing. // This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second. - // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize + // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. + float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait; // plus a some non-zero time for machinery we can't measure + // We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion + // of paintWait up to the next vSync. const float targetPeriod = isHMDMode() ? 1.0f / 75.0f : 1.0f / 60.0f; - const float nSyncsByFrameRate = round(actualPeriod / targetPeriod); - const float accuracyAllowance = 0.0005f; // sometimes paint goes over and it isn't reflected in actualPeriod - const float nSyncsByPaintWait = floor((_lastPaintWait + accuracyAllowance) / targetPeriod); - const float nSyncs = nSyncsByFrameRate + nSyncsByPaintWait; - const float modularPeriod = ((nSyncs - 1) * targetPeriod) + actualPeriod; - const float deducedNonVSyncPeriod = modularPeriod - _lastPaintWait; + const float minumumMachinery = glm::max(0.0f, (floorf(_lastPaintWait / targetPeriod) * targetPeriod) - _lastPaintWait); + deducedNonVSyncPeriod += minumumMachinery; _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; - + _lastInstantaneousFps = instantaneousFps; auto displayPlugin = getActiveDisplayPlugin(); displayPlugin->preRender(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 14824ad118..6a634bcc90 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -192,7 +192,7 @@ void Avatar::simulate(float deltaTime) { _shouldRenderBillboard = true; qCDebug(interfaceapp) << "Billboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); } -#define PID_TUNING 1 +//#define PID_TUNING 1 #ifdef PID_TUNING const float SKIP_HYSTERESIS_PROPORTION = 0.0f; #else diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 18e4dd40be..132ddb9dfa 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -102,16 +102,10 @@ void AvatarManager::init() { // See PIDController.h. There's a sectionon tuning in the reference. // Turn off HYSTERESIS_PROPORTION and extra logging by defining PID_TUNING in Avatar.cpp. // Turn on logging with the following: - _renderDistanceController.setHistorySize("avatar render", target_fps * 4); // FIXME - // KP is usually tuned by setting the other constants to zero, finding the maximum value that doesn't oscillate, - // and taking about 0.6 of that. A typical osciallation would be with error=37fps with avatars 10m away, so - // KP*37=1/10 => KP(oscillating)=0.1/37 = 0.0027 - _renderDistanceController.setKP(0.0015f); - // alt: - // Our anti-windup limits accumulated error to 10*targetFrameRate, so the sanity check on KI is - // KI*750=controlledValueHighLimit=1 => KI=1/750. - _renderDistanceController.setKI(0.001f); - _renderDistanceController.setKD(0.0001f); // a touch of kd increases the speed by which we get there + //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); // FIXME + _renderDistanceController.setKP(0.0003f); //Usually about 0.6 of largest that doesn't oscillate, with other constants 0. + _renderDistanceController.setKI(0.001f); // Big enough to bring us to target with the above KP. + _renderDistanceController.setKD(0.00001f); // a touch of kd increases the speed by which we get there } @@ -140,18 +134,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); PerformanceTimer perfTimer("otherAvatars"); - const float FEED_FORWARD_RANGE = 2; const float fps = qApp->getLastInstanteousFps(); const float paintWait = qApp->getLastPaintWait(); const float deduced = qApp->getLastDeducedNonVSyncFps(); - const bool isAtSetpoint = false; //FIXME fabsf(effectiveFps - _renderDistanceController.getMeasuredValueSetpoint()) < FEED_FORWARD_RANGE; - //const float distance = 1.0f / _renderDistanceController.update(deduced + (isAtSetpoint ? _renderFeedForward : 0.0f), deltaTime, isAtSetpoint, fps, paintWait); - const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, isAtSetpoint, fps, paintWait); - - const float RENDER_DISTANCE_DEADBAND = 0.0f; //FIXME 0.3f; // meters - if (fabsf(distance - _renderDistance) > RENDER_DISTANCE_DEADBAND) { - _renderDistance = distance; - } + const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, false, fps, paintWait); + _renderDistanceAverage.updateAverage(distance); + _renderDistance = _renderDistanceAverage.getAverage(); // simulate avatars AvatarHash::iterator avatarIterator = _avatarHash.begin(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 92f9958962..7a4dd2632e 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "Avatar.h" #include "AvatarMotionState.h" @@ -106,6 +107,8 @@ private: float _renderFeedForward { 5.0f }; int _renderedAvatarCount {0}; PIDController _renderDistanceController {}; + SimpleMovingAverage _renderDistanceAverage {10}; + SetOfAvatarMotionStates _avatarMotionStates; diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp index 2756ab615f..d5697538d5 100644 --- a/libraries/shared/src/PIDController.cpp +++ b/libraries/shared/src/PIDController.cpp @@ -71,7 +71,7 @@ void PIDController::reportHistory() { Row& row = _history[i]; qCDebug(shared) << row.measured << (row.dt * 1000) << row.FIXME1 << (row.FIXME2 * 1000) << "||" << row.error << row.accumulated << row.changed << - "||" << row.p << row.i << row.d << row.computed; + "||" << row.p << row.i << row.d << row.computed << 1.0f/row.computed; } qCDebug(shared) << "Limits: setpoint" << getMeasuredValueSetpoint() << "accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() << "controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() << From 0a8fd9cbad939cf82668cd6457cafd629e63ae22 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 19 Nov 2015 09:22:41 -0800 Subject: [PATCH 05/20] Spread avatars around (not up to) a larger area. --- examples/acScripts/animatedAvatarAgent.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/acScripts/animatedAvatarAgent.js b/examples/acScripts/animatedAvatarAgent.js index 4e550e9789..3e5c90ed1a 100644 --- a/examples/acScripts/animatedAvatarAgent.js +++ b/examples/acScripts/animatedAvatarAgent.js @@ -14,16 +14,17 @@ // An assignment client script that animates one avatar at random location within 'spread' meters of 'origin'. // In Domain Server Settings, go to scripts and give the url of this script. Press '+', and then 'Save and restart'. -var origin = {x: 500, y: 502, z: 500}; -var spread = 10; // meters +var origin = {x: 500, y: 500, z: 500}; +var spread = 20; // meters var animationData = {url: "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", lastFrame: 35}; Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst"; //lovejoy Avatar.displayName = "'Bot"; var millisecondsToWaitBeforeStarting = 10 * 1000; // To give the various servers a chance to start. Agent.isAvatar = true; +function coord() { return (Math.random() * spread) - (spread / 2); } // randomly distribute a coordinate zero += spread/2. Script.setTimeout(function () { - Avatar.position = Vec3.sum(origin, {x: Math.random() * spread, y: 0, z: Math.random() * spread}); + Avatar.position = Vec3.sum(origin, {x: coord(), y: 0, z: coord()}); print("Starting at", JSON.stringify(Avatar.position)); Avatar.startAnimation(animationData.url, animationData.fps || 30, 1, true, false, animationData.firstFrame || 0, animationData.lastFrame); }, millisecondsToWaitBeforeStarting); From f6286201f46daa296004c6528417241e557198ee Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 19 Nov 2015 11:55:22 -0800 Subject: [PATCH 06/20] cleanup --- interface/resources/qml/Stats.qml | 8 +++++++- interface/src/avatar/Avatar.cpp | 24 ++++++++++-------------- interface/src/avatar/AvatarManager.cpp | 12 ++++++------ interface/src/avatar/AvatarManager.h | 22 ++++++++++------------ interface/src/ui/Stats.cpp | 2 +- interface/src/ui/Stats.h | 4 ++-- libraries/shared/src/PIDController.cpp | 6 +++--- libraries/shared/src/PIDController.h | 11 ++++------- 8 files changed, 43 insertions(+), 46 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 336b8ff577..84381cc754 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -40,7 +40,7 @@ Item { Text { color: root.fontColor; font.pixelSize: root.fontSize - text: "Avatars: " + root.avatarCount + ", " + root.avatarRenderedCount + " w/in " + root.avatarRenderDistance + "m" + text: "Avatars: " + root.avatarCount } Text { color: root.fontColor; @@ -256,6 +256,12 @@ Item { visible: root.expanded text: "LOD: " + root.lodStatus; } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Renderable avatars: " + root.avatarRenderableCount + " w/in " + root.avatarRenderDistance + "m"; + } } } } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6a634bcc90..bfee330700 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -115,7 +115,7 @@ Avatar::~Avatar() { } } -/*const fixme*/ float BILLBOARD_LOD_DISTANCE = 40.0f; +const float BILLBOARD_LOD_DISTANCE = 40.0f; void Avatar::init() { getHead()->init(); @@ -182,7 +182,6 @@ void Avatar::simulate(float deltaTime) { // update the billboard render flag const float BILLBOARD_HYSTERESIS_PROPORTION = 0.1f; - BILLBOARD_LOD_DISTANCE = DependencyManager::get()->getFIXMEupdate(); if (_shouldRenderBillboard) { if (getLODDistance() < BILLBOARD_LOD_DISTANCE * (1.0f - BILLBOARD_HYSTERESIS_PROPORTION)) { _shouldRenderBillboard = false; @@ -192,26 +191,23 @@ void Avatar::simulate(float deltaTime) { _shouldRenderBillboard = true; qCDebug(interfaceapp) << "Billboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); } -//#define PID_TUNING 1 -#ifdef PID_TUNING - const float SKIP_HYSTERESIS_PROPORTION = 0.0f; -#else - const float SKIP_HYSTERESIS_PROPORTION = BILLBOARD_HYSTERESIS_PROPORTION; -#endif + + const bool isControllerLogging = DependencyManager::get()->getRenderDistanceControllerIsLogging(); float renderDistance = DependencyManager::get()->getRenderDistance(); + const float SKIP_HYSTERESIS_PROPORTION = isControllerLogging ? 0.0f : BILLBOARD_HYSTERESIS_PROPORTION; float distance = glm::distance(qApp->getCamera()->getPosition(), _position); if (_shouldSkipRender) { if (distance < renderDistance * (1.0f - SKIP_HYSTERESIS_PROPORTION)) { _shouldSkipRender = false; -#ifndef PID_TUNING - qCDebug(interfaceapp) << "Rerendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); -#endif + if (!isControllerLogging) { // Test for isMyAvatar is prophylactic. Never occurs in current code. + qCDebug(interfaceapp) << "Rerendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for distance" << renderDistance; + } } } else if (distance > renderDistance * (1.0f + SKIP_HYSTERESIS_PROPORTION)) { _shouldSkipRender = true; -#ifndef PID_TUNING - qCDebug(interfaceapp) << "Unrendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); -#endif + if (!isControllerLogging) { + qCDebug(interfaceapp) << "Unrendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for distance" << renderDistance; + } } // simple frustum check diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 132ddb9dfa..d2765e0122 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -91,18 +91,17 @@ void AvatarManager::init() { } scene->enqueuePendingChanges(pendingChanges); - const float target_fps = 75.0f; // qApp->isHMDMode() ? 75.0f : 60.0f; + const float target_fps = 75.0f; // qApp->isHMDMode() ? 75.0f : 60.0f; FIXME _renderDistanceController.setMeasuredValueSetpoint(target_fps); const float TREE_SCALE = 32768.0f; // Not in shared library, alas. - const float SMALLEST_REASONABLE_HORIZON = 0.5f; // FIXME 5 + const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters _renderDistanceController.setControlledValueHighLimit(1.0f / SMALLEST_REASONABLE_HORIZON); _renderDistanceController.setControlledValueLowLimit(1.0f / TREE_SCALE); - // Advice for tuning parameters: // See PIDController.h. There's a sectionon tuning in the reference. - // Turn off HYSTERESIS_PROPORTION and extra logging by defining PID_TUNING in Avatar.cpp. - // Turn on logging with the following: - //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); // FIXME + // Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300)) + //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); + // Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on. _renderDistanceController.setKP(0.0003f); //Usually about 0.6 of largest that doesn't oscillate, with other constants 0. _renderDistanceController.setKI(0.001f); // Big enough to bring us to target with the above KP. _renderDistanceController.setKD(0.00001f); // a touch of kd increases the speed by which we get there @@ -134,6 +133,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); PerformanceTimer perfTimer("otherAvatars"); + const float fps = qApp->getLastInstanteousFps(); const float paintWait = qApp->getLastPaintWait(); const float deduced = qApp->getLastDeducedNonVSyncFps(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 7a4dd2632e..9f8b6dc646 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -67,17 +67,17 @@ public: void handleCollisionEvents(const CollisionEvents& collisionEvents); void updateAvatarPhysicsShape(const QUuid& id); - Q_INVOKABLE int getNumberRendered() { return _renderedAvatarCount; } + + // Expose results and parameter-tuning operations to other systems, such as stats and javascript. Q_INVOKABLE float getRenderDistance() { return _renderDistance; } - Q_INVOKABLE float getFIXMEupdate() { return _fixmeUpdate; } - Q_INVOKABLE void setFIXMEupdate(float f) { _fixmeUpdate = f; } - Q_INVOKABLE void setFIXMEkp(float f) { _renderDistanceController.setKP(f); } - Q_INVOKABLE void setFIXMEki(float f) { _renderDistanceController.setKI(f); } - Q_INVOKABLE void setFIXMEkd(float f) { _renderDistanceController.setKD(f); } - Q_INVOKABLE void setFIXMEbias(float f) { _renderDistanceController.setBias(f); } - Q_INVOKABLE void setFIXMElow(float f) { _renderDistanceController.setControlledValueLowLimit(f); } - Q_INVOKABLE void setFIXMEhigh(float f) { _renderDistanceController.setControlledValueHighLimit(f); } - Q_INVOKABLE void setFIXMEfeedForward(float f) { _renderFeedForward = f; } + Q_INVOKABLE int getNumberInRenderRange() { return _renderedAvatarCount; } + Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); } + Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); } + Q_INVOKABLE void setRenderDistanceKP(float f) { _renderDistanceController.setKP(f); } + Q_INVOKABLE void setRenderDistanceKI(float f) { _renderDistanceController.setKI(f); } + Q_INVOKABLE void setRenderDistanceKD(float f) { _renderDistanceController.setKD(f); } + Q_INVOKABLE void setRenderDistanceLowLimit(float f) { _renderDistanceController.setControlledValueLowLimit(f); } + Q_INVOKABLE void setRenderDistanceHighLimit(float f) { _renderDistanceController.setControlledValueHighLimit(f); } public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } @@ -102,9 +102,7 @@ private: QVector _localLights; bool _shouldShowReceiveStats = false; - float _fixmeUpdate = 40.0f; float _renderDistance { 40.0f }; - float _renderFeedForward { 5.0f }; int _renderedAvatarCount {0}; PIDController _renderDistanceController {}; SimpleMovingAverage _renderDistanceAverage {10}; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 3f27052f60..663cf144b1 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -115,7 +115,7 @@ void Stats::updateStats(bool force) { auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves STAT_UPDATE(avatarCount, avatarManager->size() - 1); - STAT_UPDATE(avatarRenderedCount, avatarManager->getNumberRendered()); + STAT_UPDATE(avatarRenderableCount, avatarManager->getNumberInRenderRange()); STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating STAT_UPDATE(serverCount, nodeList->size()); STAT_UPDATE(framerate, (int)qApp->getFps()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 12caf19d18..d1c0dd19d7 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -36,7 +36,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, simrate, 0) STATS_PROPERTY(int, avatarSimrate, 0) STATS_PROPERTY(int, avatarCount, 0) - STATS_PROPERTY(int, avatarRenderedCount, 0) + STATS_PROPERTY(int, avatarRenderableCount, 0) STATS_PROPERTY(int, avatarRenderDistance, 0) STATS_PROPERTY(int, packetInCount, 0) STATS_PROPERTY(int, packetOutCount, 0) @@ -119,7 +119,7 @@ signals: void simrateChanged(); void avatarSimrateChanged(); void avatarCountChanged(); - void avatarRenderedCountChanged(); + void avatarRenderableCountChanged(); void avatarRenderDistanceChanged(); void packetInCountChanged(); void packetOutCountChanged(); diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp index d5697538d5..6080b878b8 100644 --- a/libraries/shared/src/PIDController.cpp +++ b/libraries/shared/src/PIDController.cpp @@ -27,11 +27,11 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator const float changeInError = (error - _lastError) / dt; // positive value denotes increasing deficit const float d = getKD() * changeInError; // term is Derivative of Error - const float computedValue = glm::clamp(p + i + d + getBias(), + const float computedValue = glm::clamp(p + i + d, getControlledValueLowLimit(), getControlledValueHighLimit()); - if (_history.capacity()) { // if logging/reporting + if (getIsLogging()) { // if logging/reporting updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue, FIXME1, FIXME2); } @@ -75,5 +75,5 @@ void PIDController::reportHistory() { } qCDebug(shared) << "Limits: setpoint" << getMeasuredValueSetpoint() << "accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() << "controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() << - "kp/ki/kd/bias" << getKP() << getKI() << getKD() << getBias(); + "kp/ki/kd" << getKP() << getKI() << getKD(); } \ No newline at end of file diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h index a2adbf98ab..7c63206406 100644 --- a/libraries/shared/src/PIDController.h +++ b/libraries/shared/src/PIDController.h @@ -26,13 +26,12 @@ class PIDController { public: - // These three are the main interface: + // These four the main interface: void setMeasuredValueSetpoint(float newValue) { _measuredValueSetpoint = newValue; } float update(float measuredValue, float dt, bool resetAccumulator = false, float FIXME1 = 0.0f, float FIXME2 = 0.0f); // returns the new computedValue void setHistorySize(QString label = QString(""), int size = 0) { _history.reserve(size); _history.resize(0); _label = label; } // non-empty does logging - // There are several values that rarely change and might be thought of as "constants", but which do change during tuning, debugging, or other - // special-but-expected circumstances. Thus the instance vars are not const. + bool getIsLogging() { return _history.capacity(); } float getMeasuredValueSetpoint() const { return _measuredValueSetpoint; } // In normal operation (where we can easily reach setpoint), controlledValue is typcially pinned at max. // Defaults to [0, max float], but for 1/LODdistance, it might be, say, [0, 0.2 or 0.1] @@ -42,17 +41,17 @@ public: float getKP() const { return _kp; } float getKI() const { return _ki; } float getKD() const { return _kd; } - float getBias() const { return _bias; } float getAccumulatedValueHighLimit() const { return getAntiWindupFactor() * getMeasuredValueSetpoint(); } float getAccumulatedValueLowLimit() const { return -getAntiWindupFactor() * getMeasuredValueSetpoint(); } + // There are several values that rarely change and might be thought of as "constants", but which do change during tuning, debugging, or other + // special-but-expected circumstances. Thus the instance vars are not const. void setControlledValueLowLimit(float newValue) { _controlledValueLowLimit = newValue; } void setControlledValueHighLimit(float newValue) { _controlledValueHighLimit = newValue; } void setAntiWindupFactor(float newValue) { _antiWindupFactor = newValue; } void setKP(float newValue) { _kp = newValue; } void setKI(float newValue) { _ki = newValue; } void setKD(float newValue) { _kd = newValue; } - void setBias(float newValue) { _bias = newValue; } class Row { // one row of accumulated history, used only for logging (if at all) public: @@ -66,7 +65,6 @@ public: float p; float i; float d; - float bias; float computed; }; protected: @@ -79,7 +77,6 @@ protected: float _kp { 0.0f }; float _ki { 0.0f }; float _kd { 0.0f }; - float _bias { 0.0f }; // Controller state float _lastError{ 0.0f }; From 431a8c95848a2599743e70cf4034a0970549ae94 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 19 Nov 2015 17:12:50 -0800 Subject: [PATCH 07/20] cleanup --- interface/src/Application.cpp | 11 +++++++---- interface/src/Application.h | 1 + interface/src/avatar/AvatarManager.cpp | 8 ++++---- libraries/shared/src/PIDController.cpp | 5 +++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 84b14497cd..1e970828ec 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1102,15 +1102,18 @@ void Application::paintGL() { // Here we make a guess for deducedNonVSyncFps = 1 / deducedNonVSyncPeriod. // // Time between previous paintGL call and this one, which can vary not only with vSync misses, but also with QT timing. + // We're using this as a proxy for the time between vsync and displayEnd, below. (Not exact, but tends to be the same over time.) // This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second. const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait; // plus a some non-zero time for machinery we can't measure // We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion - // of paintWait up to the next vSync. - const float targetPeriod = isHMDMode() ? 1.0f / 75.0f : 1.0f / 60.0f; - const float minumumMachinery = glm::max(0.0f, (floorf(_lastPaintWait / targetPeriod) * targetPeriod) - _lastPaintWait); - deducedNonVSyncPeriod += minumumMachinery; + // of paintWait up to the next vSync. This gives us enough of a penalty so that when actualPeriod crosses two cycles, + // the key part (and not an exagerated part) of _lastPaintWait is accounted for. + const float targetPeriod = getTargetFramePeriod(); + if (_lastPaintWait > EPSILON && actualPeriod > targetPeriod) { + deducedNonVSyncPeriod += remainder(actualPeriod, _lastPaintWait); + } _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; _lastInstantaneousFps = instantaneousFps; diff --git a/interface/src/Application.h b/interface/src/Application.h index 01d0c72645..0222f585cf 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -159,6 +159,7 @@ public: bool isForeground() const { return _isForeground; } float getFps() const { return _fps; } + float getTargetFramePeriod() { return isHMDMode() ? 1.0f / 75.0f : 1.0f / 60.0f; } float getLastInstanteousFps() const { return _lastInstantaneousFps; } float getLastPaintWait() const { return _lastPaintWait; }; float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index d2765e0122..6c1097a42b 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -91,7 +91,7 @@ void AvatarManager::init() { } scene->enqueuePendingChanges(pendingChanges); - const float target_fps = 75.0f; // qApp->isHMDMode() ? 75.0f : 60.0f; FIXME + const float target_fps = 1.0f / qApp->getTargetFramePeriod(); _renderDistanceController.setMeasuredValueSetpoint(target_fps); const float TREE_SCALE = 32768.0f; // Not in shared library, alas. const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters @@ -102,9 +102,9 @@ void AvatarManager::init() { // Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300)) //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); // Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on. - _renderDistanceController.setKP(0.0003f); //Usually about 0.6 of largest that doesn't oscillate, with other constants 0. - _renderDistanceController.setKI(0.001f); // Big enough to bring us to target with the above KP. - _renderDistanceController.setKD(0.00001f); // a touch of kd increases the speed by which we get there + _renderDistanceController.setKP(0.0005f); // Usually about 0.6 of largest that doesn't oscillate when other constants 0. + _renderDistanceController.setKI(0.0004f); // Big enough to bring us to target with the above KP. + _renderDistanceController.setKD(0.00001f); // A touch of kd increases the speed by which we get there. } diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp index 6080b878b8..fc75b91fb2 100644 --- a/libraries/shared/src/PIDController.cpp +++ b/libraries/shared/src/PIDController.cpp @@ -33,8 +33,9 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator if (getIsLogging()) { // if logging/reporting updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue, FIXME1, FIXME2); - } - + } + Q_ASSERT(!isnan(computedValue)); + // update state for next time _lastError = error; _lastAccumulation = accumulatedError; From 0f6a37f0f7de9223419eb4d7954bb5b7b7367388 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 20 Nov 2015 09:49:14 -0800 Subject: [PATCH 08/20] remainder->fmod! --- interface/src/Application.cpp | 3 ++- interface/src/avatar/AvatarManager.cpp | 11 +++++++---- interface/src/avatar/AvatarManager.h | 2 +- libraries/shared/src/PIDController.h | 7 ++++--- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1e970828ec..5640827982 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1112,7 +1112,8 @@ void Application::paintGL() { // the key part (and not an exagerated part) of _lastPaintWait is accounted for. const float targetPeriod = getTargetFramePeriod(); if (_lastPaintWait > EPSILON && actualPeriod > targetPeriod) { - deducedNonVSyncPeriod += remainder(actualPeriod, _lastPaintWait); + // Don't use C++ remainder(). It's authors are mathematically insane. + deducedNonVSyncPeriod += fmod(actualPeriod, _lastPaintWait); } _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; _lastInstantaneousFps = instantaneousFps; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6c1097a42b..789865679d 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -93,16 +93,15 @@ void AvatarManager::init() { const float target_fps = 1.0f / qApp->getTargetFramePeriod(); _renderDistanceController.setMeasuredValueSetpoint(target_fps); - const float TREE_SCALE = 32768.0f; // Not in shared library, alas. const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters _renderDistanceController.setControlledValueHighLimit(1.0f / SMALLEST_REASONABLE_HORIZON); - _renderDistanceController.setControlledValueLowLimit(1.0f / TREE_SCALE); + _renderDistanceController.setControlledValueLowLimit(1.0f / (float) TREE_SCALE); // Advice for tuning parameters: - // See PIDController.h. There's a sectionon tuning in the reference. + // See PIDController.h. There's a section on tuning in the reference. // Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300)) //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); // Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on. - _renderDistanceController.setKP(0.0005f); // Usually about 0.6 of largest that doesn't oscillate when other constants 0. + _renderDistanceController.setKP(0.0005f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. _renderDistanceController.setKI(0.0004f); // Big enough to bring us to target with the above KP. _renderDistanceController.setKD(0.00001f); // A touch of kd increases the speed by which we get there. @@ -136,6 +135,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { const float fps = qApp->getLastInstanteousFps(); const float paintWait = qApp->getLastPaintWait(); + // The PID controller raises the controlled value when the measured value goes up. + // The measured value is frame rate. When the controlled value (1 / render cutoff distance) + // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate + // goes up. const float deduced = qApp->getLastDeducedNonVSyncFps(); const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, false, fps, paintWait); _renderDistanceAverage.updateAverage(distance); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 9f8b6dc646..f17a34da35 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -102,7 +102,7 @@ private: QVector _localLights; bool _shouldShowReceiveStats = false; - float _renderDistance { 40.0f }; + float _renderDistance { (float) TREE_SCALE }; int _renderedAvatarCount {0}; PIDController _renderDistanceController {}; SimpleMovingAverage _renderDistanceAverage {10}; diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h index 7c63206406..55519063bc 100644 --- a/libraries/shared/src/PIDController.h +++ b/libraries/shared/src/PIDController.h @@ -21,12 +21,12 @@ #include #include -// Although our coding standard shuns abbreviations, the control systems literature pretty uniformly uses p, i, d, and dt rather than +// Although our coding standard shuns abbreviations, the control systems literature uniformly uses p, i, d, and dt rather than // proportionalTerm, integralTerm, derivativeTerm, and deltaTime. Here we will be consistent with the literature. class PIDController { public: - // These four the main interface: + // These are the main interfaces: void setMeasuredValueSetpoint(float newValue) { _measuredValueSetpoint = newValue; } float update(float measuredValue, float dt, bool resetAccumulator = false, float FIXME1 = 0.0f, float FIXME2 = 0.0f); // returns the new computedValue void setHistorySize(QString label = QString(""), int size = 0) { _history.reserve(size); _history.resize(0); _label = label; } // non-empty does logging @@ -78,9 +78,10 @@ protected: float _ki { 0.0f }; float _kd { 0.0f }; - // Controller state + // Controller operating state float _lastError{ 0.0f }; float _lastAccumulation{ 0.0f }; + // reporting QVector _history{}; QString _label{ "" }; From b5e5adc41e9485e6e2a02c7634c2df0e095edf35 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 20 Nov 2015 09:51:57 -0800 Subject: [PATCH 09/20] Update for changed target frame period. --- interface/src/avatar/AvatarManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 789865679d..5c9796b1e4 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -135,6 +135,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { const float fps = qApp->getLastInstanteousFps(); const float paintWait = qApp->getLastPaintWait(); + _renderDistanceController.setMeasuredValueSetpoint(1.0f / qApp->getTargetFramePeriod()); // No problem updating in flight. // The PID controller raises the controlled value when the measured value goes up. // The measured value is frame rate. When the controlled value (1 / render cutoff distance) // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate From a3665985986cb98103f40a8084fc8270ad0364c5 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 20 Nov 2015 11:48:44 -0800 Subject: [PATCH 10/20] snapshot. It works. --- interface/src/Application.cpp | 2 +- interface/src/Application.h | 2 ++ interface/src/avatar/AvatarManager.cpp | 8 ++++---- interface/src/avatar/AvatarManager.h | 18 ++++++++++-------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5640827982..9a3f3b071e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1106,7 +1106,7 @@ void Application::paintGL() { // This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second. const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. - float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait; // plus a some non-zero time for machinery we can't measure + float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait + 0.002f; // plus a some non-zero time for machinery we can't measure // We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion // of paintWait up to the next vSync. This gives us enough of a penalty so that when actualPeriod crosses two cycles, // the key part (and not an exagerated part) of _lastPaintWait is accounted for. diff --git a/interface/src/Application.h b/interface/src/Application.h index 0222f585cf..7d9efb34af 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -163,6 +163,7 @@ public: float getLastInstanteousFps() const { return _lastInstantaneousFps; } float getLastPaintWait() const { return _lastPaintWait; }; float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; } + void setMarginForDeducedFramePeriod(float newValue) { _marginForDeducedFramePeriod = newValue; } float getFieldOfView() { return _fieldOfView.get(); } void setFieldOfView(float fov); @@ -437,6 +438,7 @@ private: float _lastInstantaneousFps { 0.0f }; float _lastPaintWait { 0.0f }; float _lastDeducedNonVSyncFps { 0.0f }; + float _marginForDeducedFramePeriod { 1.0f }; ShapeManager _shapeManager; PhysicalEntitySimulation _entitySimulation; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 5c9796b1e4..80e35174bd 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -101,9 +101,9 @@ void AvatarManager::init() { // Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300)) //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); // Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on. - _renderDistanceController.setKP(0.0005f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. - _renderDistanceController.setKI(0.0004f); // Big enough to bring us to target with the above KP. - _renderDistanceController.setKD(0.00001f); // A touch of kd increases the speed by which we get there. + _renderDistanceController.setKP(0.0006f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. + _renderDistanceController.setKI(0.0005f); // Big enough to bring us to target with the above KP. + _renderDistanceController.setKD(0.000001f); // A touch of kd increases the speed by which we get there. } @@ -140,7 +140,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // The measured value is frame rate. When the controlled value (1 / render cutoff distance) // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate // goes up. - const float deduced = qApp->getLastDeducedNonVSyncFps(); + const float deduced = qApp->getLastDeducedNonVSyncFps() - _renderPresentationAllowance; const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, false, fps, paintWait); _renderDistanceAverage.updateAverage(distance); _renderDistance = _renderDistanceAverage.getAverage(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index f17a34da35..8be7df43f9 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -73,11 +73,12 @@ public: Q_INVOKABLE int getNumberInRenderRange() { return _renderedAvatarCount; } Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); } Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); } - Q_INVOKABLE void setRenderDistanceKP(float f) { _renderDistanceController.setKP(f); } - Q_INVOKABLE void setRenderDistanceKI(float f) { _renderDistanceController.setKI(f); } - Q_INVOKABLE void setRenderDistanceKD(float f) { _renderDistanceController.setKD(f); } - Q_INVOKABLE void setRenderDistanceLowLimit(float f) { _renderDistanceController.setControlledValueLowLimit(f); } - Q_INVOKABLE void setRenderDistanceHighLimit(float f) { _renderDistanceController.setControlledValueHighLimit(f); } + Q_INVOKABLE void setRenderDistanceKP(float newValue) { _renderDistanceController.setKP(newValue); } + Q_INVOKABLE void setRenderDistanceKI(float newValue) { _renderDistanceController.setKI(newValue); } + Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); } + Q_INVOKABLE void setRenderDistanceLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); } + Q_INVOKABLE void setRenderDistanceHighLimit(float newValue) { _renderDistanceController.setControlledValueHighLimit(newValue); } + // Q_INVOKABLE void setRenderPresentationAllowance(float newValue) { qApp->setMarginForDeducedFramePeriod(newValue); } public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } @@ -103,9 +104,10 @@ private: bool _shouldShowReceiveStats = false; float _renderDistance { (float) TREE_SCALE }; - int _renderedAvatarCount {0}; - PIDController _renderDistanceController {}; - SimpleMovingAverage _renderDistanceAverage {10}; + int _renderedAvatarCount { 0 }; + float _renderPresentationAllowance { 0.0f }; + PIDController _renderDistanceController { }; + SimpleMovingAverage _renderDistanceAverage { 10 }; From 6c784256cb9eae3269d943e8e3053d20f458219b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 20 Nov 2015 13:51:12 -0800 Subject: [PATCH 11/20] final(?) cleanup --- interface/src/Application.cpp | 2 +- interface/src/Application.h | 2 +- interface/src/avatar/AvatarManager.cpp | 10 ++++------ interface/src/avatar/AvatarManager.h | 2 -- libraries/shared/src/PIDController.cpp | 10 ++++------ libraries/shared/src/PIDController.h | 6 ++---- 6 files changed, 12 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9a3f3b071e..9863248cf8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1106,7 +1106,7 @@ void Application::paintGL() { // This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second. const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. - float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait + 0.002f; // plus a some non-zero time for machinery we can't measure + float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait + _marginForDeducedFramePeriod; // plus a some non-zero time for machinery we can't measure // We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion // of paintWait up to the next vSync. This gives us enough of a penalty so that when actualPeriod crosses two cycles, // the key part (and not an exagerated part) of _lastPaintWait is accounted for. diff --git a/interface/src/Application.h b/interface/src/Application.h index 7d9efb34af..b90ad43d60 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -438,7 +438,7 @@ private: float _lastInstantaneousFps { 0.0f }; float _lastPaintWait { 0.0f }; float _lastDeducedNonVSyncFps { 0.0f }; - float _marginForDeducedFramePeriod { 1.0f }; + float _marginForDeducedFramePeriod{ 0.002f }; // 2ms, adjustable ShapeManager _shapeManager; PhysicalEntitySimulation _entitySimulation; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 80e35174bd..eea4edafc9 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -101,8 +101,8 @@ void AvatarManager::init() { // Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300)) //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); // Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on. - _renderDistanceController.setKP(0.0006f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. - _renderDistanceController.setKI(0.0005f); // Big enough to bring us to target with the above KP. + _renderDistanceController.setKP(0.0008f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. + _renderDistanceController.setKI(0.0006f); // Big enough to bring us to target with the above KP. _renderDistanceController.setKD(0.000001f); // A touch of kd increases the speed by which we get there. } @@ -133,15 +133,13 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceTimer perfTimer("otherAvatars"); - const float fps = qApp->getLastInstanteousFps(); - const float paintWait = qApp->getLastPaintWait(); _renderDistanceController.setMeasuredValueSetpoint(1.0f / qApp->getTargetFramePeriod()); // No problem updating in flight. // The PID controller raises the controlled value when the measured value goes up. // The measured value is frame rate. When the controlled value (1 / render cutoff distance) // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate // goes up. - const float deduced = qApp->getLastDeducedNonVSyncFps() - _renderPresentationAllowance; - const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, false, fps, paintWait); + const float deduced = qApp->getLastDeducedNonVSyncFps(); + const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime); _renderDistanceAverage.updateAverage(distance); _renderDistance = _renderDistanceAverage.getAverage(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 8be7df43f9..52741970aa 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -78,7 +78,6 @@ public: Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); } Q_INVOKABLE void setRenderDistanceLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); } Q_INVOKABLE void setRenderDistanceHighLimit(float newValue) { _renderDistanceController.setControlledValueHighLimit(newValue); } - // Q_INVOKABLE void setRenderPresentationAllowance(float newValue) { qApp->setMarginForDeducedFramePeriod(newValue); } public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } @@ -105,7 +104,6 @@ private: bool _shouldShowReceiveStats = false; float _renderDistance { (float) TREE_SCALE }; int _renderedAvatarCount { 0 }; - float _renderPresentationAllowance { 0.0f }; PIDController _renderDistanceController { }; SimpleMovingAverage _renderDistanceAverage { 10 }; diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp index fc75b91fb2..a853553b89 100644 --- a/libraries/shared/src/PIDController.cpp +++ b/libraries/shared/src/PIDController.cpp @@ -14,7 +14,7 @@ #include "SharedLogging.h" #include "PIDController.h" -float PIDController::update(float measuredValue, float dt, bool resetAccumulator, float FIXME1, float FIXME2) { +float PIDController::update(float measuredValue, float dt, bool resetAccumulator) { const float error = getMeasuredValueSetpoint() - measuredValue; // Sign is the direction we want measuredValue to go. Positive means go higher. const float p = getKP() * error; // term is Proportional to error @@ -32,7 +32,7 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator getControlledValueHighLimit()); if (getIsLogging()) { // if logging/reporting - updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue, FIXME1, FIXME2); + updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue); } Q_ASSERT(!isnan(computedValue)); @@ -43,7 +43,7 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator } // Just for logging/reporting. Used when picking/verifying the operational parameters. -void PIDController::updateHistory(float measuredValue, float dt, float error, float accumulatedError, float changeInError, float p, float i, float d, float computedValue, float FIXME1, float FIXME2) { +void PIDController::updateHistory(float measuredValue, float dt, float error, float accumulatedError, float changeInError, float p, float i, float d, float computedValue) { // Don't report each update(), as the I/O messes with the results a lot. // Instead, add to history, and then dump out at once when full. // Typically, the first few values reported in each batch should be ignored. @@ -51,8 +51,6 @@ void PIDController::updateHistory(float measuredValue, float dt, float error, fl _history.resize(n + 1); Row& next = _history[n]; next.measured = measuredValue; - next.FIXME1 = FIXME1; - next.FIXME2 = FIXME2; next.dt = dt; next.error = error; next.accumulated = accumulatedError; @@ -70,7 +68,7 @@ void PIDController::reportHistory() { qCDebug(shared) << _label << "measured dt FIXME || error accumulated changed || p i d controlled"; for (int i = 0; i < _history.size(); i++) { Row& row = _history[i]; - qCDebug(shared) << row.measured << (row.dt * 1000) << row.FIXME1 << (row.FIXME2 * 1000) << + qCDebug(shared) << row.measured << row.dt << "||" << row.error << row.accumulated << row.changed << "||" << row.p << row.i << row.d << row.computed << 1.0f/row.computed; } diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h index 55519063bc..e89ff2a693 100644 --- a/libraries/shared/src/PIDController.h +++ b/libraries/shared/src/PIDController.h @@ -28,7 +28,7 @@ class PIDController { public: // These are the main interfaces: void setMeasuredValueSetpoint(float newValue) { _measuredValueSetpoint = newValue; } - float update(float measuredValue, float dt, bool resetAccumulator = false, float FIXME1 = 0.0f, float FIXME2 = 0.0f); // returns the new computedValue + float update(float measuredValue, float dt, bool resetAccumulator = false); // returns the new computedValue void setHistorySize(QString label = QString(""), int size = 0) { _history.reserve(size); _history.resize(0); _label = label; } // non-empty does logging bool getIsLogging() { return _history.capacity(); } @@ -55,8 +55,6 @@ public: class Row { // one row of accumulated history, used only for logging (if at all) public: - float FIXME1; - float FIXME2; float measured; float dt; float error; @@ -69,7 +67,7 @@ public: }; protected: void reportHistory(); - void updateHistory(float measured, float dt, float error, float accumulatedError, float changeInErro, float p, float i, float d, float computedValue, float FIXME1, float FIXME2); + void updateHistory(float measured, float dt, float error, float accumulatedError, float changeInErro, float p, float i, float d, float computedValue); float _measuredValueSetpoint { 0.0f }; float _controlledValueLowLimit { 0.0f }; float _controlledValueHighLimit { std::numeric_limits::max() }; From 9673133bf4d76aade057a5a0d3d50e7cf2d33af7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 20 Nov 2015 14:26:37 -0800 Subject: [PATCH 12/20] whitespace (reduce diffs) --- interface/src/avatar/Avatar.cpp | 1 + interface/src/avatar/AvatarManager.h | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 26fb9c1b2e..d3f47703fc 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -606,6 +606,7 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::fixupModelsInScene() { + // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene render::ScenePointer scene = qApp->getMain3DScene(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 9918fc9374..96383b7e60 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -65,6 +65,7 @@ public: void getObjectsToChange(VectorOfMotionStates& motionStates); void handleOutgoingChanges(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); + void updateAvatarPhysicsShape(Avatar* avatar); // Expose results and parameter-tuning operations to other systems, such as stats and javascript. @@ -108,8 +109,6 @@ private: PIDController _renderDistanceController { }; SimpleMovingAverage _renderDistanceAverage { 10 }; - - SetOfAvatarMotionStates _avatarMotionStates; SetOfMotionStates _motionStatesToAdd; VectorOfMotionStates _motionStatesToDelete; From f1e0742aeb6aa5e65de04c96b974bd197082e023 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 23 Nov 2015 10:20:59 -0800 Subject: [PATCH 13/20] Add getTargetFrameRate(). --- interface/src/Application.h | 5 ++++- interface/src/avatar/AvatarManager.cpp | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 356fabce71..730158c689 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -159,7 +159,10 @@ public: bool isForeground() const { return _isForeground; } float getFps() const { return _fps; } - float getTargetFramePeriod() { return isHMDMode() ? 1.0f / 75.0f : 1.0f / 60.0f; } + float const HMD_TARGET_FRAME_RATE = 75.0f; + float const DESKTOP_TARGET_FRAME_RATE = 60.0f; + float getTargetFrameRate() { return isHMDMode() ? HMD_TARGET_FRAME_RATE : DESKTOP_TARGET_FRAME_RATE; } + float getTargetFramePeriod() { return isHMDMode() ? 1.0f / HMD_TARGET_FRAME_RATE : 1.0f / DESKTOP_TARGET_FRAME_RATE; } // same as 1/getTargetFrameRate, but w/compile-time division float getLastInstanteousFps() const { return _lastInstantaneousFps; } float getLastPaintWait() const { return _lastPaintWait; }; float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index e7701dcb24..6d8182dfc0 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -91,7 +91,7 @@ void AvatarManager::init() { } scene->enqueuePendingChanges(pendingChanges); - const float target_fps = 1.0f / qApp->getTargetFramePeriod(); + const float target_fps = qApp->getTargetFrameRate(); _renderDistanceController.setMeasuredValueSetpoint(target_fps); const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters _renderDistanceController.setControlledValueHighLimit(1.0f / SMALLEST_REASONABLE_HORIZON); @@ -139,7 +139,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceTimer perfTimer("otherAvatars"); - _renderDistanceController.setMeasuredValueSetpoint(1.0f / qApp->getTargetFramePeriod()); // No problem updating in flight. + _renderDistanceController.setMeasuredValueSetpoint(qApp->getTargetFrameRate()); // No problem updating in flight. // The PID controller raises the controlled value when the measured value goes up. // The measured value is frame rate. When the controlled value (1 / render cutoff distance) // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate From 591280807795d9e6e05478475a1d58beb0c95b63 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 23 Nov 2015 10:27:49 -0800 Subject: [PATCH 14/20] getShouldSkipRendering() => !getShouldRender(). --- interface/src/avatar/Avatar.h | 2 +- interface/src/avatar/AvatarManager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index cb238dc82c..64ef321f1c 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -140,7 +140,7 @@ public: Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; } Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; } - Q_INVOKABLE bool getShouldSkipRendering() const { return _shouldSkipRender; } + Q_INVOKABLE bool getShouldRender() const { return !_shouldSkipRender; } /// Scales a world space position vector relative to the avatar position and scale /// \param vector position to be scaled. Will store the result diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6d8182dfc0..40f469961c 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -167,7 +167,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } else { avatar->startUpdate(); avatar->simulate(deltaTime); - if (!avatar->getShouldSkipRendering()) { + if (avatar->getShouldRender()) { renderableCount++; } avatar->endUpdate(); From c94a5389a0af9144182e90659c0807d61a91d22b Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 23 Nov 2015 10:31:37 -0800 Subject: [PATCH 15/20] comments --- libraries/shared/src/PIDController.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h index e89ff2a693..0b2411530a 100644 --- a/libraries/shared/src/PIDController.h +++ b/libraries/shared/src/PIDController.h @@ -38,9 +38,9 @@ public: float getControlledValueLowLimit() const { return _controlledValueLowLimit; } float getControlledValueHighLimit() const { return _controlledValueHighLimit; } float getAntiWindupFactor() const { return _antiWindupFactor; } // default 10 - float getKP() const { return _kp; } - float getKI() const { return _ki; } - float getKD() const { return _kd; } + float getKP() const { return _kp; } // proportional to error. See comment above class. + float getKI() const { return _ki; } // to time integral of error + float getKD() const { return _kd; } // to time derivative of error float getAccumulatedValueHighLimit() const { return getAntiWindupFactor() * getMeasuredValueSetpoint(); } float getAccumulatedValueLowLimit() const { return -getAntiWindupFactor() * getMeasuredValueSetpoint(); } From 1409ed2d1892a8bd659a937efb24c0a6b9f97933 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 23 Nov 2015 12:28:44 -0800 Subject: [PATCH 16/20] moved logic to build vertices buffer to renderable class, recalculating vertices anytime any of the normals, point, or strokeWidths data changes --- .../painting/whiteboard/whiteboardSpawner.js | 2 +- .../src/RenderablePolyLineEntityItem.cpp | 49 ++++++++++++++++++- .../src/RenderablePolyLineEntityItem.h | 2 + libraries/entities/src/PolyLineEntityItem.cpp | 41 ++-------------- libraries/entities/src/PolyLineEntityItem.h | 3 +- 5 files changed, 56 insertions(+), 41 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 56ead143e4..75d5be8967 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -247,4 +247,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -// Script.scriptEnding.connect(cleanup); \ No newline at end of file +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 76cf4fac3d..036d37a95b 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -31,6 +31,7 @@ EntityItemPointer RenderablePolyLineEntityItem::factory(const EntityItemID& enti RenderablePolyLineEntityItem::RenderablePolyLineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : PolyLineEntityItem(entityItemID, properties) { _numVertices = 0; + _vertices = QVector(0.0f); } @@ -114,13 +115,56 @@ void RenderablePolyLineEntityItem::updateGeometry() { _numVertices += 2; } _pointsChanged = false; + _normalsChanged = false; + _strokeWidthsChanged = false; +} + +void RenderablePolyLineEntityItem::updateVertices() { + // Calculate the minimum vector size out of normals, points, and stroke widths + int minVectorSize = _normals.size(); + if (_points.size() < minVectorSize) { + minVectorSize = _points.size(); + } + if (_strokeWidths.size() < minVectorSize) { + minVectorSize = _strokeWidths.size(); + } + + _vertices.clear(); + glm::vec3 v1, v2, tangent, binormal, point; + + int finalIndex = minVectorSize - 1; + for (int i = 0; i < finalIndex; i++) { + float width = _strokeWidths.at(i); + point = _points.at(i); + + tangent = _points.at(i); + + tangent = _points.at(i + 1) - point; + glm::vec3 normal = _normals.at(i); + binormal = glm::normalize(glm::cross(tangent, normal)) * width; + + // Check to make sure binormal is not a NAN. If it is, don't add to vertices vector + if (binormal.x != binormal.x) { + continue; + } + v1 = point + binormal; + v2 = point - binormal; + _vertices << v1 << v2; + } + + // For last point we can assume binormals are the same since it represents the last two vertices of quad + point = _points.at(finalIndex); + v1 = point + binormal; + v2 = point - binormal; + _vertices << v1 << v2; + } void RenderablePolyLineEntityItem::render(RenderArgs* args) { QWriteLocker lock(&_quadReadWriteLock); - if (_points.size() < 2 || _normals.size () < 2 || _vertices.size() < 2) { + if (_points.size() < 2 || _normals.size () < 2 || _strokeWidths.size() < 2) { return; } @@ -139,7 +183,8 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { Q_ASSERT(getType() == EntityTypes::PolyLine); Q_ASSERT(args->_batch); - if (_pointsChanged) { + if (_pointsChanged || _strokeWidthsChanged || _normalsChanged) { + updateVertices(); updateGeometry(); } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 618f8c66a6..c49777cfa3 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -40,8 +40,10 @@ public: protected: void updateGeometry(); + void updateVertices(); gpu::BufferPointer _verticesBuffer; unsigned int _numVertices; + QVector _vertices; }; diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index 45c967f78d..012ec3ca4a 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -34,8 +34,9 @@ PolyLineEntityItem::PolyLineEntityItem(const EntityItemID& entityItemID, const E EntityItem(entityItemID), _lineWidth(DEFAULT_LINE_WIDTH), _pointsChanged(true), +_normalsChanged(true), +_strokeWidthsChanged(true), _points(QVector(0.0f)), -_vertices(QVector(0.0f)), _normals(QVector(0.0f)), _strokeWidths(QVector(0.0f)), _textures("") @@ -106,47 +107,13 @@ bool PolyLineEntityItem::appendPoint(const glm::vec3& point) { bool PolyLineEntityItem::setStrokeWidths(const QVector& strokeWidths) { _strokeWidths = strokeWidths; + _strokeWidthsChanged = true; return true; } bool PolyLineEntityItem::setNormals(const QVector& normals) { _normals = normals; - if (_points.size() < 2 || _normals.size() < 2 || _strokeWidths.size() < 2) { - return false; - } - - int minVectorSize = _normals.size(); - if (_points.size() < minVectorSize) { - minVectorSize = _points.size(); - } - if (_strokeWidths.size() < minVectorSize) { - minVectorSize = _strokeWidths.size(); - } - - _vertices.clear(); - glm::vec3 v1, v2, tangent, binormal, point; - - int finalIndex = minVectorSize -1; - for (int i = 0; i < finalIndex; i++) { - float width = _strokeWidths.at(i); - point = _points.at(i); - - tangent = _points.at(i + 1) - point; - glm::vec3 normal = normals.at(i); - binormal = glm::normalize(glm::cross(tangent, normal)) * width; - - //This checks to make sure binormal is not a NAN - assert(binormal.x == binormal.x); - v1 = point + binormal; - v2 = point - binormal; - _vertices << v1 << v2; - } - //for last point we can just assume binormals are same since it represents last two vertices of quad - point = _points.at(finalIndex); - v1 = point + binormal; - v2 = point - binormal; - _vertices << v1 << v2; - + _normalsChanged = true; return true; } diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index 9e9d3f9013..4da52f3f21 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -93,8 +93,9 @@ class PolyLineEntityItem : public EntityItem { rgbColor _color; float _lineWidth; bool _pointsChanged; + bool _normalsChanged; + bool _strokeWidthsChanged; QVector _points; - QVector _vertices; QVector _normals; QVector _strokeWidths; QString _textures; From 40be9ae1211409c9c34997c544c73201ab163281 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Mon, 23 Nov 2015 17:15:26 -0800 Subject: [PATCH 17/20] Commented out whiteboard cleanup --- examples/painting/whiteboard/whiteboardSpawner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 75d5be8967..f3005495ec 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -247,4 +247,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -Script.scriptEnding.connect(cleanup); \ No newline at end of file +//Script.scriptEnding.connect(cleanup); From 23f10659b0e84e2f254440f46e6eac8a8425a38e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 24 Nov 2015 14:00:23 -0800 Subject: [PATCH 18/20] update to new trigger method --- examples/controllers/squeezeHands.js | 6 ++---- examples/example/games/sword.js | 4 ++-- examples/painting/closePaint.js | 7 ++++--- examples/toybox/flashlight/createFlashlight.js | 5 ++--- examples/toybox/flashlight/flashlight.js | 11 +++++++---- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/controllers/squeezeHands.js b/examples/controllers/squeezeHands.js index 29965f77eb..3bf13d8646 100644 --- a/examples/controllers/squeezeHands.js +++ b/examples/controllers/squeezeHands.js @@ -25,8 +25,6 @@ var LAST_FRAME = 15.0; // What is the number of the last frame we want to us var SMOOTH_FACTOR = 0.75; var MAX_FRAMES = 30.0; -var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK"); -var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK"); var CONTROLLER_DEAD_SPOT = 0.25; @@ -45,8 +43,8 @@ function normalizeControllerValue(val) { } Script.update.connect(function(deltaTime) { - var leftTrigger = normalizeControllerValue(Controller.getActionValue(LEFT_HAND_CLICK)); - var rightTrigger = normalizeControllerValue(Controller.getActionValue(RIGHT_HAND_CLICK)); + var leftTrigger = normalizeControllerValue(Controller.getValue(Controller.Standard.LT)); + var rightTrigger = normalizeControllerValue(Controller.getValue(Controller.Standard.RT)); // Average last few trigger values together for a bit of smoothing var smoothLeftTrigger = leftTrigger * (1.0 - SMOOTH_FACTOR) + lastLeftTrigger * SMOOTH_FACTOR; diff --git a/examples/example/games/sword.js b/examples/example/games/sword.js index abd94b5319..31a9a82d87 100644 --- a/examples/example/games/sword.js +++ b/examples/example/games/sword.js @@ -361,8 +361,8 @@ function update() { } function updateControllerState() { - rightTriggerValue = Controller.getActionValue(rightHandClick); - leftTriggerValue = Controller.getActionValue(leftHandClick); + rightTriggerValue = Controller.getValue(Controller.Standard.RT); + leftTriggerValue =Controller.getValue(Controller.Standard.LT); if (rightTriggerValue > TRIGGER_THRESHOLD && !swordHeld) { grabSword("right") diff --git a/examples/painting/closePaint.js b/examples/painting/closePaint.js index 563ed1dafb..60b4ac2e25 100644 --- a/examples/painting/closePaint.js +++ b/examples/painting/closePaint.js @@ -159,7 +159,8 @@ function MyController(hand, triggerAction) { } this.updateControllerState = function() { - this.triggerValue = Controller.getActionValue(this.triggerAction); + this.triggerValue = Controller.getValue(this.triggerAction); + if (this.triggerValue > TRIGGER_ON_VALUE && this.prevTriggerValue <= TRIGGER_ON_VALUE) { this.squeeze(); } else if (this.triggerValue < TRIGGER_ON_VALUE && this.prevTriggerValue >= TRIGGER_ON_VALUE) { @@ -256,8 +257,8 @@ function MyController(hand, triggerAction) { } } -var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK")); -var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK")); +var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT); +var leftController = new MyController(LEFT_HAND, Controller.Standard.LT); Controller.actionEvent.connect(function(action, state) { if (state === 0) { diff --git a/examples/toybox/flashlight/createFlashlight.js b/examples/toybox/flashlight/createFlashlight.js index 490792027a..b049d2632e 100644 --- a/examples/toybox/flashlight/createFlashlight.js +++ b/examples/toybox/flashlight/createFlashlight.js @@ -12,10 +12,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ -Script.include("https://hifi-public.s3.amazonaws.com/scripts/utilities.js"); +Script.include("../../libraries/utils.js"); - -var scriptURL = Script.resolvePath('flashlight.js?123123'); +var scriptURL = Script.resolvePath('flashlight.js'); var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; diff --git a/examples/toybox/flashlight/flashlight.js b/examples/toybox/flashlight/flashlight.js index 722a5c1bfc..8911c0ecd8 100644 --- a/examples/toybox/flashlight/flashlight.js +++ b/examples/toybox/flashlight/flashlight.js @@ -183,11 +183,14 @@ }, changeLightWithTriggerPressure: function(flashLightHand) { - var handClickString = flashLightHand + "_HAND_CLICK"; - var handClick = Controller.findAction(handClickString); + if (flashLightHand === 'LEFT') { + this.triggerValue = Controller.getValue(Controller.Standard.LT); + } + if (flashLightHand === 'RIGHT') { + this.triggerValue = Controller.getValue(Controller.Standard.RT); - this.triggerValue = Controller.getActionValue(handClick); + } if (this.triggerValue < DISABLE_LIGHT_THRESHOLD && this.lightOn === true) { this.turnLightOff(); @@ -266,4 +269,4 @@ // entity scripts always need to return a newly constructed object of our type return new Flashlight(); -}); +}); \ No newline at end of file From cb0b5b5d02a10ff04b5bdd53fe0bb3bf129ac17c Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Tue, 24 Nov 2015 18:30:31 -0800 Subject: [PATCH 19/20] added prompt --- .../entityScripts/recordingEntityScript.js | 6 ++-- examples/entityScripts/recordingMaster.js | 36 ++++++++++++------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/examples/entityScripts/recordingEntityScript.js b/examples/entityScripts/recordingEntityScript.js index 0694ff431e..dedfbf809e 100644 --- a/examples/entityScripts/recordingEntityScript.js +++ b/examples/entityScripts/recordingEntityScript.js @@ -84,7 +84,7 @@ overlay = null; }, - startRecording: function (entityID) { + startRecording: function () { if (!isAvatarRecording) { print("RECORDING STARTED"); Messages.sendMessage(CLIENTS_TO_MASTER_CHANNEL, PARTICIPATING_MESSAGE); //tell to master that I'm participating @@ -94,7 +94,7 @@ } }, - stopRecording: function (entityID) { + stopRecording: function () { if (isAvatarRecording) { print("RECORDING ENDED"); Recording.stopRecording(); @@ -109,7 +109,7 @@ _this.stopRecording(); Messages.unsubscribe(MASTER_TO_CLIENTS_CHANNEL); Messages.messageReceived.disconnect(receivingMessage); - if(overlay !== null){ + if (overlay !== null) { Overlays.deleteOverlay(overlay); overlay = null; } diff --git a/examples/entityScripts/recordingMaster.js b/examples/entityScripts/recordingMaster.js index 51149991c2..0aa3e6f866 100644 --- a/examples/entityScripts/recordingMaster.js +++ b/examples/entityScripts/recordingMaster.js @@ -29,11 +29,14 @@ var STOP_MESSAGE = "recordingEnded"; var PARTICIPATING_MESSAGE = "participatingToRecording"; var TIMEOUT = 20; + var toolBar = null; var recordIcon; var isRecording = false; var performanceJSON = { "avatarClips" : [] }; var responsesExpected = 0; +var readyToPrintInfo = false; +var performanceFileURL = null; var waitingForPerformanceFile = true; var totalWaitingTime = 0; var extension = "txt"; @@ -71,9 +74,9 @@ function mousePressEvent(event) { print("I'm the master. I want to start recording"); Messages.sendMessage(MASTER_TO_CLIENTS_CHANNEL, START_MESSAGE); isRecording = true; + waitingForPerformanceFile = true; } else { print("I want to stop recording"); - waitingForPerformanceFile = true; Script.update.connect(update); Messages.sendMessage(MASTER_TO_CLIENTS_CHANNEL, STOP_MESSAGE); isRecording = false; @@ -108,29 +111,38 @@ function update(deltaTime) { } //clean things after upload performance file to asset waitingForPerformanceFile = false; - responsesExpected = 0; totalWaitingTime = 0; Script.update.disconnect(update); - performanceJSON = { "avatarClips" : [] }; } + } else if (readyToPrintInfo == true){ + Window.prompt("Performance file and clips: ", getUtilityString()); + responsesExpected = 0; + performanceJSON = { "avatarClips" : [] }; + Script.update.disconnect(update); } } +function getUtilityString() { + var resultString = "JSON:\n" + performanceFileURL + "\n" + responsesExpected + " avatar clips:\n"; + var avatarClips = performanceJSON.avatarClips; + avatarClips.forEach(function(param) { + resultString += param + "\n"; + }); + return resultString; +} + function uploadFinished(url){ //need to print somehow the url here this way the master can copy the url - print("PERFORMANCE FILE URL: " + url); - Assets.downloadData(url, function (data) { - printPerformanceJSON(JSON.parse(data)); - }); -} - -function printPerformanceJSON(obj) { print("some info:"); - print("downloaded performance file from asset and examinating its content..."); - var avatarClips = obj.avatarClips; + performanceFileURL = url; + print("PERFORMANCE FILE URL: " + performanceFileURL); + print("number of clips obtained:" + responsesExpected); + var avatarClips = performanceJSON.avatarClips; avatarClips.forEach(function(param) { print("clip url obtained: " + param); }); + readyToPrintInfo = true; + Script.update.connect(update); } function cleanup() { From 386dad7aff4357abfe39cd32bfbb1c9bd52e5b04 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 24 Nov 2015 18:57:35 -0800 Subject: [PATCH 20/20] Fixes hand IK for some avatars Specifically: https://hifi-content.s3.amazonaws.com/ozan/dev/avatars/hifi_team/ryan/_test/ryan.fst https://hifi-content.s3.amazonaws.com/ozan/dev/avatars/hifi_team/brad/brad.fst https://s3.amazonaws.com/hifi-public/tony/blackmery/blackmery.fst These avatars have "Hips" joints that are NOT the root of the skeleton. This would cause the getRootAbsoluteBindPoseByChildName() to return (0,0,0). Causing the IK targets to be lower then they should have. --- libraries/animation/src/AnimSkeleton.cpp | 17 ----------------- libraries/animation/src/AnimSkeleton.h | 1 - libraries/animation/src/Rig.cpp | 10 +++++----- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 7ec8db1490..b87d649e31 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -49,23 +49,6 @@ const AnimPose& AnimSkeleton::getAbsoluteBindPose(int jointIndex) const { return _absoluteBindPoses[jointIndex]; } -AnimPose AnimSkeleton::getRootAbsoluteBindPoseByChildName(const QString& childName) const { - AnimPose pose = AnimPose::identity; - int jointIndex = nameToJointIndex(childName); - if (jointIndex >= 0) { - int numJoints = (int)(_absoluteBindPoses.size()); - if (jointIndex < numJoints) { - int parentIndex = getParentIndex(jointIndex); - while (parentIndex != -1 && parentIndex < numJoints) { - jointIndex = parentIndex; - parentIndex = getParentIndex(jointIndex); - } - pose = _absoluteBindPoses[jointIndex]; - } - } - return pose; -} - const AnimPose& AnimSkeleton::getRelativeBindPose(int jointIndex) const { return _relativeBindPoses[jointIndex]; } diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 9dda313528..98e2b5882f 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -31,7 +31,6 @@ public: // absolute pose, not relative to parent const AnimPose& getAbsoluteBindPose(int jointIndex) const; - AnimPose getRootAbsoluteBindPoseByChildName(const QString& childName) const; // relative to parent pose const AnimPose& getRelativeBindPose(int jointIndex) const; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 046603d660..029fd20e44 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1306,10 +1306,10 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { // TODO: figure out how to obtain the yFlip from where it is actually stored glm::quat yFlipHACK = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - AnimPose rootBindPose = _animSkeleton->getRootAbsoluteBindPoseByChildName("LeftHand"); + AnimPose hipsBindPose = _animSkeleton->getAbsoluteBindPose(_animSkeleton->nameToJointIndex("Hips")); if (params.isLeftEnabled) { - _animVars.set("leftHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.leftPosition); - _animVars.set("leftHandRotation", rootBindPose.rot * yFlipHACK * params.leftOrientation); + _animVars.set("leftHandPosition", hipsBindPose.trans + hipsBindPose.rot * yFlipHACK * params.leftPosition); + _animVars.set("leftHandRotation", hipsBindPose.rot * yFlipHACK * params.leftOrientation); _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition); } else { _animVars.unset("leftHandPosition"); @@ -1317,8 +1317,8 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { _animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition); } if (params.isRightEnabled) { - _animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition); - _animVars.set("rightHandRotation", rootBindPose.rot * yFlipHACK * params.rightOrientation); + _animVars.set("rightHandPosition", hipsBindPose.trans + hipsBindPose.rot * yFlipHACK * params.rightPosition); + _animVars.set("rightHandRotation", hipsBindPose.rot * yFlipHACK * params.rightOrientation); _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); } else { _animVars.unset("rightHandPosition");