From b75e053e9deb3d92d54abb719870d13d0f363bd4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 23 Oct 2015 13:37:49 -0700 Subject: [PATCH 01/41] Add automatic CCD activation to the physics engine --- libraries/physics/src/ObjectMotionState.cpp | 18 ++++++++++++++++++ libraries/physics/src/ObjectMotionState.h | 1 + 2 files changed, 19 insertions(+) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index be0edafff5..6f1b928456 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -115,6 +115,21 @@ void ObjectMotionState::setMotionType(MotionType motionType) { _motionType = motionType; } +void ObjectMotionState::updateCCDConfiguration() { + if (_body) { + if (_shape) { + btVector3 center; + btScalar radius; + _shape->getBoundingSphere(center, radius); + _body->setCcdMotionThreshold(radius * 2.0f); + _body->setCcdSweptSphereRadius(radius); + } else { + // Disable CCD + _body->setCcdMotionThreshold(0); + } + } +} + void ObjectMotionState::setRigidBody(btRigidBody* body) { // give the body a (void*) back-pointer to this ObjectMotionState if (_body != body) { @@ -125,6 +140,7 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) { if (_body) { _body->setUserPointer(this); } + updateCCDConfiguration(); } } @@ -187,6 +203,8 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* if (_shape != newShape) { _shape = newShape; _body->setCollisionShape(_shape); + + updateCCDConfiguration(); } else { // huh... the shape didn't actually change, so we clear the DIRTY_SHAPE flag flags &= ~Simulation::DIRTY_SHAPE; diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 992bdd11d7..8c3f3aaca1 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -151,6 +151,7 @@ protected: virtual bool isReadyToComputeShape() = 0; virtual btCollisionShape* computeNewShape() = 0; void setMotionType(MotionType motionType); + void updateCCDConfiguration(); // clearObjectBackPointer() overrrides should call the base method, then actually clear the object back pointer. virtual void clearObjectBackPointer() { _type = MOTIONSTATE_TYPE_INVALID; } From b410604475a606a53efede906cabc22f1ddacbe1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 3 Nov 2015 11:18:38 -0800 Subject: [PATCH 02/41] Add comments to updateCCDConfiguration --- libraries/physics/src/ObjectMotionState.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 6f1b928456..5101eabd6a 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -118,10 +118,18 @@ void ObjectMotionState::setMotionType(MotionType motionType) { void ObjectMotionState::updateCCDConfiguration() { if (_body) { if (_shape) { + // If this object moves faster than its bounding radius * RADIUS_MOTION_THRESHOLD_MULTIPLIER, + // CCD will be enabled for this object. + const auto RADIUS_MOTION_THRESHOLD_MULTIPLIER = 2.0f; + btVector3 center; btScalar radius; _shape->getBoundingSphere(center, radius); - _body->setCcdMotionThreshold(radius * 2.0f); + _body->setCcdMotionThreshold(radius * RADIUS_MOTION_THRESHOLD_MULTIPLIER); + + // TODO: Ideally the swept sphere radius would be contained by the object. Using the bounding sphere + // radius works well for spherical objects, but may cause issues with other shapes. For arbitrary + // objects we may want to consider a different approach, such as grouping rigid bodies together. _body->setCcdSweptSphereRadius(radius); } else { // Disable CCD From c0a7da0b88b8903fa1e0aedac464b7b77ac0b35b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 3 Nov 2015 11:22:15 -0800 Subject: [PATCH 03/41] Add comment to ObjectMotionState::updateCCDConfiguration --- libraries/physics/src/ObjectMotionState.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 5101eabd6a..4f2f2b8184 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -115,6 +115,8 @@ void ObjectMotionState::setMotionType(MotionType motionType) { _motionType = motionType; } +// Update the Continuous Collision Detection (CCD) configuration settings of our RigidBody so that +// CCD will be enabled automatically when its speed surpasses a certain threshold. void ObjectMotionState::updateCCDConfiguration() { if (_body) { if (_shape) { From 7c5f0bb62058283579bfc615c4cc53b0358a6e40 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 08:51:22 -0800 Subject: [PATCH 04/41] Update RADIUS_MOTION_THRESHOLD_MULTIPLIER --- libraries/physics/src/ObjectMotionState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 4f2f2b8184..24acb3358c 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -122,7 +122,7 @@ void ObjectMotionState::updateCCDConfiguration() { if (_shape) { // If this object moves faster than its bounding radius * RADIUS_MOTION_THRESHOLD_MULTIPLIER, // CCD will be enabled for this object. - const auto RADIUS_MOTION_THRESHOLD_MULTIPLIER = 2.0f; + const auto RADIUS_MOTION_THRESHOLD_MULTIPLIER = 0.5f; btVector3 center; btScalar radius; From fee13d4375c53688f3b3beee2f09b641244e86d4 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 17 Nov 2015 20:06:25 -0800 Subject: [PATCH 05/41] 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 06/41] 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 07/41] 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 08/41] 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 09/41] 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 10/41] 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 11/41] 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 12/41] 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 13/41] 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 14/41] 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 15/41] 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 784df60a9750ae2ad32ec4ae9d9d2321e5024abb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 20 Nov 2015 13:56:20 -0800 Subject: [PATCH 16/41] Update bullet archive to include CCD+Compound improvements --- cmake/externals/bullet/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/externals/bullet/CMakeLists.txt b/cmake/externals/bullet/CMakeLists.txt index efe5b694fa..3322779272 100644 --- a/cmake/externals/bullet/CMakeLists.txt +++ b/cmake/externals/bullet/CMakeLists.txt @@ -18,8 +18,8 @@ if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} # URL https://bullet.googlecode.com/files/bullet-2.82-r2704.zip - URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-r2704.zip - URL_MD5 f5e8914fc9064ad32e0d62d19d33d977 + URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.zip + URL_MD5 d95b07eb120de7dd7786361c0b5a8d9f CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 -DUSE_DX11=0 LOG_DOWNLOAD 1 LOG_CONFIGURE 1 @@ -30,8 +30,8 @@ else () ExternalProject_Add( ${EXTERNAL_NAME} #URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz - URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-r2704.tgz - URL_MD5 70b3c8d202dee91a0854b4cbc88173e8 + URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.tgz + URL_MD5 eae10a936e100ddf9fe7b1645018f561 CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 LOG_DOWNLOAD 1 LOG_CONFIGURE 1 @@ -80,4 +80,4 @@ endif () if (DEFINED ${EXTERNAL_NAME_UPPER}_DYNAMICS_LIBRARY_RELEASE) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/bullet CACHE PATH "Path to bullet include directory") -endif () \ No newline at end of file +endif () From 9673133bf4d76aade057a5a0d3d50e7cf2d33af7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 20 Nov 2015 14:26:37 -0800 Subject: [PATCH 17/41] 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 7e87ad5114febc0105e1155b62940271f7dff23d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 20 Nov 2015 14:50:31 -0800 Subject: [PATCH 18/41] Update bullet tgz md5sum --- cmake/externals/bullet/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/externals/bullet/CMakeLists.txt b/cmake/externals/bullet/CMakeLists.txt index 3322779272..867ff85ea2 100644 --- a/cmake/externals/bullet/CMakeLists.txt +++ b/cmake/externals/bullet/CMakeLists.txt @@ -31,7 +31,7 @@ else () ${EXTERNAL_NAME} #URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.tgz - URL_MD5 eae10a936e100ddf9fe7b1645018f561 + URL_MD5 3ffdde88230ddf3c5125a6b25373a2be CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 LOG_DOWNLOAD 1 LOG_CONFIGURE 1 From 67d07921736a05deb5d7be780269b00ee5ef909d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 20 Nov 2015 15:09:06 -0800 Subject: [PATCH 19/41] Update bullet tgz md5sum --- cmake/externals/bullet/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/externals/bullet/CMakeLists.txt b/cmake/externals/bullet/CMakeLists.txt index 867ff85ea2..402cfbb3dc 100644 --- a/cmake/externals/bullet/CMakeLists.txt +++ b/cmake/externals/bullet/CMakeLists.txt @@ -31,7 +31,7 @@ else () ${EXTERNAL_NAME} #URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.tgz - URL_MD5 3ffdde88230ddf3c5125a6b25373a2be + URL_MD5 8082947ba8b07480ea785e5e6b2bab23 CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 LOG_DOWNLOAD 1 LOG_CONFIGURE 1 From 3aa271d688c87ba4cea77190ae9505214f33a515 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 20 Nov 2015 15:21:53 -0800 Subject: [PATCH 20/41] Update bullet tgz md5sum --- cmake/externals/bullet/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/externals/bullet/CMakeLists.txt b/cmake/externals/bullet/CMakeLists.txt index 402cfbb3dc..8990167e1c 100644 --- a/cmake/externals/bullet/CMakeLists.txt +++ b/cmake/externals/bullet/CMakeLists.txt @@ -31,7 +31,7 @@ else () ${EXTERNAL_NAME} #URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.tgz - URL_MD5 8082947ba8b07480ea785e5e6b2bab23 + URL_MD5 8d9daf5a46409120fa02c77d02c23c56 CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 LOG_DOWNLOAD 1 LOG_CONFIGURE 1 From d1e9e28522c2075da9bb2e8a580cc5c3859ce7d3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 20 Nov 2015 16:11:12 -0800 Subject: [PATCH 21/41] Update bullet tgz md5sum --- cmake/externals/bullet/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/externals/bullet/CMakeLists.txt b/cmake/externals/bullet/CMakeLists.txt index 8990167e1c..ff0eb22241 100644 --- a/cmake/externals/bullet/CMakeLists.txt +++ b/cmake/externals/bullet/CMakeLists.txt @@ -31,7 +31,7 @@ else () ${EXTERNAL_NAME} #URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.tgz - URL_MD5 8d9daf5a46409120fa02c77d02c23c56 + URL_MD5 fb140a4983b4109aa1c825a162aa8d64 CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 LOG_DOWNLOAD 1 LOG_CONFIGURE 1 From f1e0742aeb6aa5e65de04c96b974bd197082e023 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 23 Nov 2015 10:20:59 -0800 Subject: [PATCH 22/41] 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 23/41] 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 24/41] 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 25/41] 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 26/41] 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 27/41] 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 f4ba2eb4c42e38842d6cce6f7d5f03b6e3bfb60b Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 24 Nov 2015 20:25:17 -0600 Subject: [PATCH 28/41] Update BUILD_OSX for CMake env vars. - openssl requires OPENSSL_ROOT_DIR to be set. - qt5 QT_CMAKE_PREFIX_PATH was outdated. --- BUILD_OSX.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 54360ad4b8..c8f19710ca 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -7,11 +7,18 @@ Please read the [general build guide](BUILD.md) for information on dependencies We no longer require install of qt5 via our [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas). Versions of Qt that are 5.5.x and above provide a mechanism to disable the wireless scanning we previously had a custom patch for. -###Qt +###OpenSSL and Qt -Assuming you've installed Qt 5 using the homebrew instructions above, you'll need to set QT_CMAKE_PREFIX_PATH so CMake can find your installation of Qt. For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows. +Assuming you've installed OpenSSL or Qt 5 using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR and QT_CMAKE_PREFIX_PATH so CMake can find your installations. +For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR: - export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1/lib/cmake + export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2d_1 + +For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows. + + export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1_2/lib/cmake + +Not that these use the versions from homebrew formulae at the time of this writing, and the version in the path will likely change. ###Xcode If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. From cb0b5b5d02a10ff04b5bdd53fe0bb3bf129ac17c Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Tue, 24 Nov 2015 18:30:31 -0800 Subject: [PATCH 29/41] 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 30/41] 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"); From 546f09c0b1248d909ab5e7fd46fdf2bc4fcd1a28 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 25 Nov 2015 13:30:54 -0800 Subject: [PATCH 31/41] Restore rendering of one's own avatar. --- interface/src/avatar/Avatar.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f6d73e3beb..17ecae1330 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -96,7 +96,6 @@ 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 From 6c7099a5f7b23a0a1dbdb10cff9653de18b670d1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 25 Nov 2015 15:18:32 -0800 Subject: [PATCH 32/41] Coordinate with other uses of setVisibleInScene. --- interface/src/avatar/Avatar.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 17ecae1330..a52123b3d5 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -197,12 +197,14 @@ void Avatar::simulate(float deltaTime) { if (_shouldSkipRender) { if (distance < renderDistance * (1.0f - SKIP_HYSTERESIS_PROPORTION)) { _shouldSkipRender = false; + _skeletonModel.setVisibleInScene(true, qApp->getMain3DScene()); 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; + _skeletonModel.setVisibleInScene(false, qApp->getMain3DScene()); if (!isControllerLogging) { qCDebug(interfaceapp) << "Unrendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for distance" << renderDistance; } @@ -608,10 +610,6 @@ 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); From 9ec2a01475c60f6653c2e69dcf08f81b9e63dbe1 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 27 Nov 2015 12:13:53 -0800 Subject: [PATCH 33/41] isolate grab script changes from bow branch --- examples/controllers/handControllerGrab.js | 92 +++++++++++++--------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f8a2eeefa5..0a6aec6e7d 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -37,6 +37,7 @@ var BUMPER_ON_VALUE = 0.5; var DISTANCE_HOLDING_RADIUS_FACTOR = 5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did + var NO_INTERSECT_COLOR = { red: 10, green: 10, @@ -86,6 +87,7 @@ var ZERO_VEC = { y: 0, z: 0 }; + var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; var MSEC_PER_SEC = 1000.0; @@ -95,7 +97,8 @@ var ACTION_TTL = 15; // seconds var ACTION_TTL_REFRESH = 5; var PICKS_PER_SECOND_PER_HAND = 5; var MSECS_PER_SEC = 1000.0; -var GRABBABLE_PROPERTIES = ["position", +var GRABBABLE_PROPERTIES = [ + "position", "rotation", "gravity", "ignoreForCollisions", @@ -104,17 +107,15 @@ var GRABBABLE_PROPERTIES = ["position", "name" ]; - var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js +var BEAM_DISABLER_KEY = 'beamDisablerKey' var DEFAULT_GRABBABLE_DATA = { grabbable: true, invertSolidWhileHeld: false }; -var disabledHand = 'none'; - // states for the state machine var STATE_OFF = 0; @@ -307,7 +308,14 @@ function MyController(hand) { position: closePoint, linePoints: [ZERO_VEC, farPoint], color: color, - lifetime: 0.1 + lifetime: 0.1, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) }); } @@ -322,7 +330,14 @@ function MyController(hand) { position: closePoint, linePoints: [ZERO_VEC, farPoint], color: color, - lifetime: LIFETIME + lifetime: LIFETIME, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) }); } else { var age = Entities.getEntityProperties(this.pointer, "age").age; @@ -396,11 +411,6 @@ function MyController(hand) { this.search = function() { this.grabbedEntity = null; - // if this hand is the one that's disabled, we don't want to search for anything at all - if (this.hand === disabledHand) { - return; - } - if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { this.setState(STATE_RELEASE); return; @@ -445,17 +455,7 @@ function MyController(hand) { // the ray is intersecting something we can move. var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); - //this code will disabled the beam for the opposite hand of the one that grabbed it if the entity says so var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); - if (grabbableData["turnOffOppositeBeam"]) { - if (this.hand === RIGHT_HAND) { - disabledHand = LEFT_HAND; - } else { - disabledHand = RIGHT_HAND; - } - } else { - disabledHand = 'none'; - } if (intersection.properties.name == "Grab Debug Entity") { continue; @@ -526,7 +526,14 @@ function MyController(hand) { green: 255, blue: 0 }, - lifetime: 0.1 + lifetime: 0.1, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) }); } @@ -540,8 +547,7 @@ function MyController(hand) { if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) { continue; } - var propsForCandidate = - Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES); + var propsForCandidate = Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES); if (propsForCandidate.type == 'Unknown') { continue; @@ -737,15 +743,8 @@ function MyController(hand) { this.nearGrabbing = function() { var now = Date.now(); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - var turnOffOtherHand = grabbableData["turnOffOtherHand"]; - if (turnOffOtherHand) { - //don't activate the second hand grab because the script is handling the second hand logic - return; - } - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); @@ -1094,10 +1093,6 @@ function MyController(hand) { this.release = function() { - if (this.hand !== disabledHand) { - //release the disabled hand when we let go with the main one - disabledHand = 'none'; - } this.lineOff(); if (this.grabbedEntity !== null) { @@ -1218,12 +1213,35 @@ mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); Controller.enableMapping(MAPPING_NAME); +var handToDisable = 'none'; function update() { - rightController.update(); - leftController.update(); + if (handToDisable !== 0) { + leftController.update(); + } + if (handToDisable !== 1) { + rightController.update(); + } } +Messages.subscribe('Hifi-Beam-Disabler'); + +handleBeamDisablerMessages = function(channel, message, sender) { + + if (sender === MyAvatar.sessionUUID) { + handToDisable = message; + if (message === 'left') { + handToDisable = 1; + } + if (message === 'right') { + handToDisable = 0; + } + } + +} + +Messages.messageReceived.connect(handleBeamDisablerMessages); + function cleanup() { rightController.cleanup(); leftController.cleanup(); From a9e25c51e52a4f81faeadc7efde82863f795efcd Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 27 Nov 2015 19:47:12 -0800 Subject: [PATCH 34/41] Fix for linker warning LNK4075 on Windows Specifcally, this warning: LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification The fix is to set /OPT:NOREF and /OPT:NOICF on debug builds. --- cmake/macros/LinkHifiLibraries.cmake | 2 +- cmake/macros/SetupHifiProject.cmake | 7 +++++++ interface/CMakeLists.txt | 7 +++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cmake/macros/LinkHifiLibraries.cmake b/cmake/macros/LinkHifiLibraries.cmake index 70399bedb4..ed37256b10 100644 --- a/cmake/macros/LinkHifiLibraries.cmake +++ b/cmake/macros/LinkHifiLibraries.cmake @@ -21,7 +21,7 @@ macro(LINK_HIFI_LIBRARIES) include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src") add_dependencies(${TARGET_NAME} ${HIFI_LIBRARY}) - + # link the actual library - it is static so don't bubble it up target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY}) diff --git a/cmake/macros/SetupHifiProject.cmake b/cmake/macros/SetupHifiProject.cmake index 79ca0122c9..77df280d1a 100644 --- a/cmake/macros/SetupHifiProject.cmake +++ b/cmake/macros/SetupHifiProject.cmake @@ -34,6 +34,13 @@ macro(SETUP_HIFI_PROJECT) # find these Qt modules and link them to our own target find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED) + # disable /OPT:REF and /OPT:ICF for the Debug builds + # This will prevent the following linker warnings + # LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification + if (WIN32) + set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG "/OPT:NOREF /OPT:NOICF") + endif() + foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES}) target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE}) endforeach() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 3357b57858..67ce3f3df8 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -100,6 +100,13 @@ else() add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM}) endif() +# disable /OPT:REF and /OPT:ICF for the Debug builds +# This will prevent the following linker warnings +# LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification +if (WIN32) + set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG "/OPT:NOREF /OPT:NOICF") +endif() + # link required hifi libraries link_hifi_libraries(shared octree environment gpu gl procedural model render recording fbx networking model-networking entities avatars From 63cef9c367eb2c2f3085b07828737a2538a0205c Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 27 Nov 2015 20:57:35 -0800 Subject: [PATCH 35/41] Configure fix for quazip 23> CMake Warning (dev) at C:/msys64/home/Anthony/code/hifi/build/ext/vc12/quazip/project/src/quazip-stamp/quazip-configure-Debug.cmake:3 (set): 23> Syntax error in cmake code at 23> 23> C:/msys64/home/Anthony/code/hifi/build/ext/vc12/quazip/project/src/quazip-stamp/quazip-configure-Debug.cmake:3 23> 23> when parsing string 23> 23> C:/Program Files (x86)/CMake/bin/cmake.exe;-DCMAKE_INSTALL_PREFIX:PATH=C:/msys64/home/Anthony/code/hifi/build/ext/vc12/quazip/project;-DCMAKE_PREFIX_PATH=C:\Qt\Qt5.5.1\5.5\msvc2013\lib\cmake;-DCMAKE_INSTALL_NAME_DIR:PATH=C:/msys64/home/Anthony/code/hifi/build/ext/vc12/quazip/project/lib;-DZLIB_ROOT=C:/msys64/home/Anthony/code/hifi/build/ext/vc12/zlib/project;-GVisual Studio 12 2013;C:/msys64/home/Anthony/code/hifi/build/ext/vc12/quazip/project/src/quazip 23> 23> Invalid escape sequence \Q 23> 23> Policy CMP0010 is not set: Bad variable reference syntax is an error. Run 23> "cmake --help-policy CMP0010" for policy details. Use the cmake_policy 23> command to set the policy and suppress this warning. 23> This warning is for project developers. Use -Wno-dev to suppress it. By converting backslashes to slashes before using the QT_CMAKE_PREFIX_PATH environment variable on the command line. --- cmake/externals/quazip/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index db0e218835..ddac942692 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -3,12 +3,14 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) cmake_policy(SET CMP0046 OLD) include(ExternalProject) + +string(REPLACE \\ / QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH}) ExternalProject_Add( ${EXTERNAL_NAME} URL http://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.6.2.zip URL_MD5 514851970f1a14d815bdc3ad6267af4d BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=$ENV{QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/lib -DZLIB_ROOT=${ZLIB_ROOT} + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/lib -DZLIB_ROOT=${ZLIB_ROOT} LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 From 8f51ee6f580b65b1a5ad45cf7b2fd3311e645fcc Mon Sep 17 00:00:00 2001 From: James Pollack Date: Sat, 28 Nov 2015 22:29:12 -0800 Subject: [PATCH 36/41] rename hand disabler --- examples/controllers/handControllerGrab.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 0a6aec6e7d..158e67b5db 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1224,17 +1224,17 @@ function update() { } } -Messages.subscribe('Hifi-Beam-Disabler'); +Messages.subscribe('Hifi-Hand-Disabler'); handleBeamDisablerMessages = function(channel, message, sender) { if (sender === MyAvatar.sessionUUID) { handToDisable = message; if (message === 'left') { - handToDisable = 1; + handToDisable = LEFT_HAND; } if (message === 'right') { - handToDisable = 0; + handToDisable = RIGHT_HAND; } } From 1315201bdcb564434ff508e6a464e3d7ca764014 Mon Sep 17 00:00:00 2001 From: James Pollack Date: Sat, 28 Nov 2015 22:55:44 -0800 Subject: [PATCH 37/41] update name of function --- examples/controllers/handControllerGrab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 158e67b5db..eb07ec4133 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1226,7 +1226,7 @@ function update() { Messages.subscribe('Hifi-Hand-Disabler'); -handleBeamDisablerMessages = function(channel, message, sender) { +handleHandDisablerMessages = function(channel, message, sender) { if (sender === MyAvatar.sessionUUID) { handToDisable = message; @@ -1240,7 +1240,7 @@ handleBeamDisablerMessages = function(channel, message, sender) { } -Messages.messageReceived.connect(handleBeamDisablerMessages); +Messages.messageReceived.connect(handleHandDisablerMessages); function cleanup() { rightController.cleanup(); From 1e2398cbea6b499c56b852ad07c331b1e375a88f Mon Sep 17 00:00:00 2001 From: James Pollack Date: Sat, 28 Nov 2015 23:04:52 -0800 Subject: [PATCH 38/41] use constants --- examples/controllers/handControllerGrab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index eb07ec4133..bd63ab55e2 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1216,10 +1216,10 @@ Controller.enableMapping(MAPPING_NAME); var handToDisable = 'none'; function update() { - if (handToDisable !== 0) { + if (handToDisable !== LEFT_HAND) { leftController.update(); } - if (handToDisable !== 1) { + if (handToDisable !== RIGHT_HAND) { rightController.update(); } } From 55f797838f4e45004dbacebe87d9119ca98fe990 Mon Sep 17 00:00:00 2001 From: James Pollack Date: Sat, 28 Nov 2015 23:22:00 -0800 Subject: [PATCH 39/41] remove unused key --- examples/controllers/handControllerGrab.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index bd63ab55e2..0812dc8980 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -109,7 +109,6 @@ var GRABBABLE_PROPERTIES = [ var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js -var BEAM_DISABLER_KEY = 'beamDisablerKey' var DEFAULT_GRABBABLE_DATA = { grabbable: true, From ce100dbbc5e6b7fd0682bddce7b1b2dcf0363bc6 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 30 Nov 2015 15:10:51 +1300 Subject: [PATCH 40/41] Fix crash in WebWindowClass in debug build setTitle on open WebWindow --- interface/src/scripting/WebWindowClass.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index 69523f5f9d..a5ce817f87 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -196,5 +196,9 @@ QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* } void WebWindowClass::setTitle(const QString& title) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setTitle", Qt::AutoConnection, Q_ARG(QString, title)); + return; + } _windowWidget->setWindowTitle(title); } From 49a6f63663401df9583f3b6b537d39f8ed042600 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 30 Nov 2015 10:00:05 -0800 Subject: [PATCH 41/41] remove whitespace and fix warning (unused var) --- assignment-client/src/Agent.cpp | 74 ++++++++++++++-------------- assignment-client/src/Agent.h | 10 ++-- interface/src/Application.cpp | 4 +- libraries/audio/src/AudioConstants.h | 4 +- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index de9d7e2453..cc63f4450e 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -67,7 +67,7 @@ Agent::Agent(NLPacket& packet) : DependencyManager::set(); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - + packetReceiver.registerListenerForTypes( { PacketType::MixedAudio, PacketType::SilentAudioFrame }, this, "handleAudioPacket"); @@ -86,7 +86,7 @@ void Agent::handleOctreePacket(QSharedPointer packet, SharedNodePointe if (packet->getPayloadSize() > statsMessageLength) { // pull out the piggybacked packet and create a new QSharedPointer for it int piggyBackedSizeWithHeader = packet->getPayloadSize() - statsMessageLength; - + auto buffer = std::unique_ptr(new char[piggyBackedSizeWithHeader]); memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader); @@ -126,11 +126,11 @@ void Agent::handleAudioPacket(QSharedPointer packet) { const QString AGENT_LOGGING_NAME = "agent"; void Agent::run() { - + // make sure we request our script once the agent connects to the domain auto nodeList = DependencyManager::get(); connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript); - + ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); // Setup MessagesClient @@ -140,7 +140,7 @@ void Agent::run() { messagesClient->moveToThread(messagesThread); connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init); messagesThread->start(); - + nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer }); @@ -149,7 +149,7 @@ void Agent::run() { void Agent::requestScript() { auto nodeList = DependencyManager::get(); disconnect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript); - + // figure out the URL for the script for this agent assignment QUrl scriptURL; if (_payload.isEmpty()) { @@ -160,24 +160,24 @@ void Agent::requestScript() { } else { scriptURL = QUrl(_payload); } - + // setup a network access manager and QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - + QNetworkDiskCache* cache = new QNetworkDiskCache(); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache"); networkAccessManager.setCache(cache); - + QNetworkRequest networkRequest = QNetworkRequest(scriptURL); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - + // setup a timeout for script request static const int SCRIPT_TIMEOUT_MS = 10000; _scriptRequestTimeout = new QTimer(this); connect(_scriptRequestTimeout, &QTimer::timeout, this, &Agent::scriptRequestFinished); _scriptRequestTimeout->start(SCRIPT_TIMEOUT_MS); - + qDebug() << "Downloading script at" << scriptURL.toString(); QNetworkReply* reply = networkAccessManager.get(networkRequest); connect(reply, &QNetworkReply::finished, this, &Agent::scriptRequestFinished); @@ -187,11 +187,11 @@ void Agent::scriptRequestFinished() { auto reply = qobject_cast(sender()); _scriptRequestTimeout->stop(); - + if (reply && reply->error() == QNetworkReply::NoError) { _scriptContents = reply->readAll(); qDebug() << "Downloaded script:" << _scriptContents; - + // we could just call executeScript directly - we use a QueuedConnection to allow scriptRequestFinished // to return before calling executeScript QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection); @@ -202,38 +202,38 @@ void Agent::scriptRequestFinished() { } else { qDebug() << "Failed to download script - request timed out. Bailing on assignment."; } - + setFinished(true); } - + reply->deleteLater(); } void Agent::executeScript() { _scriptEngine = std::unique_ptr(new ScriptEngine(_scriptContents, _payload)); _scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do - + // setup an Avatar for the script to use auto scriptedAvatar = DependencyManager::get(); connect(_scriptEngine.get(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection); scriptedAvatar->setForceFaceTrackerConnected(true); - + // call model URL setters with empty URLs so our avatar, if user, will have the default models scriptedAvatar->setFaceModelURL(QUrl()); scriptedAvatar->setSkeletonModelURL(QUrl()); // give this AvatarData object to the script engine _scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data()); - - + + using namespace recording; static const FrameType AVATAR_FRAME_TYPE = Frame::registerFrameType(AvatarData::FRAME_NAME); // FIXME how to deal with driving multiple avatars locally? Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this, scriptedAvatar](Frame::ConstPointer frame) { AvatarData::fromFrame(frame->data, *scriptedAvatar); }); - + using namespace recording; - static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::AUDIO_FRAME_NAME); + static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::getAudioFrameName()); Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) { const QByteArray& audio = frame->data; static quint16 audioSequenceNumber{ 0 }; @@ -242,49 +242,49 @@ void Agent::executeScript() { audioTransform.setRotation(scriptedAvatar->getOrientation()); AbstractAudioInterface::emitAudioPacket(audio.data(), audio.size(), audioSequenceNumber, audioTransform, PacketType::MicrophoneAudioNoEcho); }); - + auto avatarHashMap = DependencyManager::set(); _scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data()); - + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket"); packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar"); packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::AvatarBillboard, avatarHashMap.data(), "processAvatarBillboardPacket"); - + // register ourselves to the script engine _scriptEngine->registerGlobalObject("Agent", this); - + // FIXME -we shouldn't be calling this directly, it's normally called by run(), not sure why // viewers would need this called. //_scriptEngine->init(); // must be done before we set up the viewers - + _scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); - + QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor); _scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); - + auto entityScriptingInterface = DependencyManager::get(); - + _scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer); - + // we need to make sure that init has been called for our EntityScriptingInterface // so that it actually has a jurisdiction listener when we ask it for it next entityScriptingInterface->init(); _entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener()); - + _entityViewer.init(); - + entityScriptingInterface->setEntityTree(_entityViewer.getTree()); - + // wire up our additional agent related processing to the update signal QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio); - + _scriptEngine->run(); - + Frame::clearFrameHandler(AUDIO_FRAME_TYPE); Frame::clearFrameHandler(AVATAR_FRAME_TYPE); - + setFinished(true); } @@ -317,7 +317,7 @@ void Agent::setIsAvatar(bool isAvatar) { delete _avatarIdentityTimer; _avatarIdentityTimer = nullptr; } - + if (_avatarBillboardTimer) { _avatarBillboardTimer->stop(); delete _avatarBillboardTimer; diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 274e5f3b55..6d3ce2b3b1 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -31,7 +31,7 @@ class Agent : public ThreadedAssignment { Q_OBJECT - + Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar) Q_PROPERTY(bool isPlayingAvatarSound READ isPlayingAvatarSound) Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream) @@ -40,7 +40,7 @@ class Agent : public ThreadedAssignment { public: Agent(NLPacket& packet); - + void setIsAvatar(bool isAvatar); bool isAvatar() const { return _isAvatar; } @@ -53,7 +53,7 @@ public: QUuid getSessionUUID() const; virtual void aboutToFinish(); - + public slots: void run(); void playAvatarSound(Sound* avatarSound) { setAvatarSound(avatarSound); } @@ -62,7 +62,7 @@ private slots: void requestScript(); void scriptRequestFinished(); void executeScript(); - + void handleAudioPacket(QSharedPointer packet); void handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer packet, SharedNodePointer senderNode); @@ -72,7 +72,7 @@ private: std::unique_ptr _scriptEngine; EntityEditPacketSender _entityEditSender; EntityTreeHeadlessViewer _entityViewer; - + MixedAudioStream _receivedAudioStream; float _lastReceivedAudioLoudness; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 22822bdf74..2afa8cead5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -454,14 +454,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : audioIO->setOrientationGetter([this]{ return getMyAvatar()->getOrientationForAudio(); }); audioIO->moveToThread(audioThread); - recording::Frame::registerFrameHandler(AudioConstants::AUDIO_FRAME_NAME, [=](recording::Frame::ConstPointer frame) { + recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) { audioIO->handleRecordedAudioInput(frame->data); }); connect(audioIO.data(), &AudioClient::inputReceived, [](const QByteArray& audio){ static auto recorder = DependencyManager::get(); if (recorder->isRecording()) { - static const recording::FrameType AUDIO_FRAME_TYPE = recording::Frame::registerFrameType(AudioConstants::AUDIO_FRAME_NAME); + static const recording::FrameType AUDIO_FRAME_TYPE = recording::Frame::registerFrameType(AudioConstants::getAudioFrameName()); recorder->recordFrame(AUDIO_FRAME_TYPE, audio); } }); diff --git a/libraries/audio/src/AudioConstants.h b/libraries/audio/src/AudioConstants.h index 785060d364..b1c810710e 100644 --- a/libraries/audio/src/AudioConstants.h +++ b/libraries/audio/src/AudioConstants.h @@ -18,10 +18,10 @@ namespace AudioConstants { const int SAMPLE_RATE = 24000; - + typedef int16_t AudioSample; - static const char* AUDIO_FRAME_NAME = "com.highfidelity.recording.Audio"; + inline const char* getAudioFrameName() { return "com.highfidelity.recording.Audio"; } const int NETWORK_FRAME_BYTES_STEREO = 1024; const int NETWORK_FRAME_SAMPLES_STEREO = NETWORK_FRAME_BYTES_STEREO / sizeof(AudioSample);