diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index cb94990037..92c08152f7 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -261,6 +261,12 @@ int OctreeInboundPacketProcessor::sendNackPackets() { } const SharedNodePointer& destinationNode = DependencyManager::get()->nodeWithUUID(nodeUUID); + // If the node no longer exists, wait until the ReceivedPacketProcessor has cleaned up the node + // to remove it from our stats list. + // FIXME Is it safe to clean it up here before ReceivedPacketProcess has? + if (!destinationNode) { + continue; + } // retrieve sequence number stats of node, prune its missing set SequenceNumberStats& sequenceNumberStats = nodeStats.getIncomingEditSequenceNumberStats(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7baa6cf66f..22816e9001 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -80,12 +80,10 @@ const float MyAvatar::ZOOM_DEFAULT = 1.5f; MyAvatar::MyAvatar(RigPointer rig) : Avatar(rig), - _gravity(0.0f, 0.0f, 0.0f), _wasPushing(false), _isPushing(false), _isBraking(false), _boomLength(ZOOM_DEFAULT), - _trapDuration(0.0f), _thrust(0.0f), _keyboardMotorVelocity(0.0f), _keyboardMotorTimescale(DEFAULT_KEYBOARD_MOTOR_TIMESCALE), @@ -111,7 +109,8 @@ MyAvatar::MyAvatar(RigPointer rig) : _goToOrientation(), _rig(rig), _prevShouldDrawHead(true), - _audioListenerMode(FROM_HEAD) + _audioListenerMode(FROM_HEAD), + _hmdAtRestDetector(glm::vec3(0), glm::quat()) { for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; @@ -142,24 +141,46 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { } void MyAvatar::reset() { - _skeletonModel.reset(); - getHead()->reset(); - - _targetVelocity = glm::vec3(0.0f); - setThrust(glm::vec3(0.0f)); - // Reset the pitch and roll components of the avatar's orientation, preserve yaw direction - glm::vec3 eulers = safeEulerAngles(getOrientation()); - eulers.x = 0.0f; - eulers.z = 0.0f; - setOrientation(glm::quat(eulers)); - + // Gather animation mode... // This should be simpler when we have only graph animations always on. bool isRig = _rig->getEnableRig(); // seting rig animation to true, below, will clear the graph animation menu item, so grab it now. bool isGraph = _rig->getEnableAnimGraph() || Menu::getInstance()->isOptionChecked(MenuOption::EnableAnimGraph); + // ... and get to sane configuration where other activity won't bother us. qApp->setRawAvatarUpdateThreading(false); _rig->disableHands = true; setEnableRigAnimations(true); + + // Reset dynamic state. + _wasPushing = _isPushing = _isBraking = _billboardValid = _goToPending = _straighteningLean = false; + _skeletonModel.reset(); + getHead()->reset(); + _targetVelocity = glm::vec3(0.0f); + setThrust(glm::vec3(0.0f)); + + // Get fresh data, in case we're really slow and out of wack. + _hmdSensorMatrix = qApp->getHMDSensorPose(); + _hmdSensorPosition = extractTranslation(_hmdSensorMatrix); + _hmdSensorOrientation = glm::quat_cast(_hmdSensorMatrix); + + // Reset body position/orientation under the head. + auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. + auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; + glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix); + glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); + + // FIXME: Hack to retain the previous behavior wrt height. + // I'd like to make the body match head height, but that will have to wait for separate PR. + worldBodyPos.y = getPosition().y; + + setPosition(worldBodyPos); + setOrientation(worldBodyRot); + // If there is any discrepency between positioning and the head (as there is in initial deriveBodyFromHMDSensor), + // we can make that right by setting _bodySensorMatrix = newBodySensorMatrix. + // However, doing so will make the head want to point to the previous body orientation, as cached above. + //_bodySensorMatrix = newBodySensorMatrix; + //updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes + _skeletonModel.simulate(0.1f); // non-zero setEnableRigAnimations(false); _skeletonModel.simulate(0.1f); @@ -311,37 +332,39 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { _hmdSensorPosition = extractTranslation(hmdSensorMatrix); _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); - const float STRAIGHTING_LEAN_DURATION = 0.5f; // seconds + bool hmdIsAtRest = _hmdAtRestDetector.update(deltaTime, _hmdSensorPosition, _hmdSensorOrientation); + + const float STRAIGHTENING_LEAN_DURATION = 0.5f; // seconds // define a vertical capsule - const float STRAIGHTING_LEAN_CAPSULE_RADIUS = 0.2f; // meters - const float STRAIGHTING_LEAN_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. + const float STRAIGHTENING_LEAN_CAPSULE_RADIUS = 0.2f; // meters + const float STRAIGHTENING_LEAN_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); glm::vec3 diff = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); - if (!_straightingLean && capsuleCheck(diff, STRAIGHTING_LEAN_CAPSULE_LENGTH, STRAIGHTING_LEAN_CAPSULE_RADIUS)) { + if (!_straighteningLean && (capsuleCheck(diff, STRAIGHTENING_LEAN_CAPSULE_LENGTH, STRAIGHTENING_LEAN_CAPSULE_RADIUS) || hmdIsAtRest)) { // begin homing toward derived body position. - _straightingLean = true; - _straightingLeanAlpha = 0.0f; + _straighteningLean = true; + _straighteningLeanAlpha = 0.0f; - } else if (_straightingLean) { + } else if (_straighteningLean) { auto newBodySensorMatrix = deriveBodyFromHMDSensor(); auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix); glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); - _straightingLeanAlpha += (1.0f / STRAIGHTING_LEAN_DURATION) * deltaTime; + _straighteningLeanAlpha += (1.0f / STRAIGHTENING_LEAN_DURATION) * deltaTime; - if (_straightingLeanAlpha >= 1.0f) { - _straightingLean = false; + if (_straighteningLeanAlpha >= 1.0f) { + _straighteningLean = false; nextAttitude(worldBodyPos, worldBodyRot); _bodySensorMatrix = newBodySensorMatrix; } else { // interp position toward the desired pos - glm::vec3 pos = lerp(getPosition(), worldBodyPos, _straightingLeanAlpha); - glm::quat rot = glm::normalize(safeMix(getOrientation(), worldBodyRot, _straightingLeanAlpha)); + glm::vec3 pos = lerp(getPosition(), worldBodyPos, _straighteningLeanAlpha); + glm::quat rot = glm::normalize(safeMix(getOrientation(), worldBodyRot, _straighteningLeanAlpha)); nextAttitude(pos, rot); // interp sensor matrix toward desired @@ -349,13 +372,13 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { glm::quat nextBodyRot = glm::normalize(glm::quat_cast(newBodySensorMatrix)); glm::vec3 prevBodyPos = extractTranslation(_bodySensorMatrix); glm::quat prevBodyRot = glm::normalize(glm::quat_cast(_bodySensorMatrix)); - pos = lerp(prevBodyPos, nextBodyPos, _straightingLeanAlpha); - rot = glm::normalize(safeMix(prevBodyRot, nextBodyRot, _straightingLeanAlpha)); + pos = lerp(prevBodyPos, nextBodyPos, _straighteningLeanAlpha); + rot = glm::normalize(safeMix(prevBodyRot, nextBodyRot, _straighteningLeanAlpha)); _bodySensorMatrix = createMatFromQuatAndPos(rot, pos); } } } -// + // best called at end of main loop, just before rendering. // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 5d87737dd7..cca0c4152f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -17,6 +17,7 @@ #include #include "Avatar.h" +#include "AtRestDetector.h" class ModelItemID; @@ -282,8 +283,6 @@ private: // results are in sensor space glm::mat4 deriveBodyFromHMDSensor() const; - glm::vec3 _gravity; - float _driveKeys[MAX_DRIVE_KEYS]; bool _wasPushing; bool _isPushing; @@ -291,7 +290,6 @@ private: float _boomLength; - float _trapDuration; // seconds that avatar has been trapped by collisions glm::vec3 _thrust; // impulse accumulator for outside sources glm::vec3 _keyboardMotorVelocity; // target local-frame velocity of avatar (keyboard) @@ -363,10 +361,11 @@ private: glm::vec3 _customListenPosition; glm::quat _customListenOrientation; - bool _straightingLean = false; - float _straightingLeanAlpha = 0.0f; + bool _straighteningLean = false; + float _straighteningLeanAlpha = 0.0f; quint64 _lastUpdateFromHMDTime = usecTimestampNow(); + AtRestDetector _hmdAtRestDetector; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/controllers/CMakeLists.txt b/libraries/controllers/CMakeLists.txt new file mode 100644 index 0000000000..fbabbe1463 --- /dev/null +++ b/libraries/controllers/CMakeLists.txt @@ -0,0 +1,14 @@ +set(TARGET_NAME controllers) + +# set a default root dir for each of our optional externals if it was not passed +setup_hifi_library(Script) + +# use setup_hifi_library macro to setup our project and link appropriate Qt modules +link_hifi_libraries(shared plugins input-plugins) + +GroupSources("src/controllers") + +add_dependency_external_projects(glm) +find_package(GLM REQUIRED) +target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) + diff --git a/libraries/controllers/src/controllers/ControllerMapping.cpp b/libraries/controllers/src/controllers/ControllerMapping.cpp new file mode 100644 index 0000000000..59f8789d31 --- /dev/null +++ b/libraries/controllers/src/controllers/ControllerMapping.cpp @@ -0,0 +1,190 @@ +#include +#include + +#include +#include + +extern float currentTime(); + +namespace Controllers { + + /* + * Encapsulates a particular input / output, + * i.e. Hydra.Button0, Standard.X, Action.Yaw + */ + class Endpoint { + public: + virtual float value() = 0; + virtual void apply(float newValue, float oldValue, const Endpoint& source) = 0; + }; + + using EndpointList = std::list; + + const EndpointList& getHardwareEndpoints(); + + // Ex: xbox.RY, xbox.A .... + class HardwareEndpoint : public Endpoint { + public: + virtual float value() override { + // ... + } + + virtual void apply(float newValue, float oldValue, const Endpoint& source) override { + // Default does nothing, but in theory this could be something like vibration + // mapping.from(xbox.X).to(xbox.Vibrate) + } + }; + + class VirtualEndpoint : public Endpoint { + public: + virtual void apply(float newValue) { + if (newValue != _lastValue) { + _lastValue = newValue; + } + } + + virtual float value() { + return _lastValue; + } + + float _lastValue; + }; + + /* + * A function which provides input + */ + class FunctionEndpoint : public Endpoint { + public: + + virtual float value() override { + float now = currentTime(); + float delta = now - _lastCalled; + float result = _inputFunction.call(_object, QScriptValue(delta)).toNumber(); + _lastCalled = now; + return result; + } + + virtual void apply(float newValue, float oldValue, const Endpoint& source) override { + if (newValue != oldValue) { + //_outputFunction.call(newValue, oldValue, source); + } + } + + float _lastValue{ NAN }; + float _lastCalled{ 0 }; + QScriptValue _outputFunction; + QScriptValue _inputFunction; + QScriptValue _object; + }; + + + // Encapsulates part of a filter chain + class Filter { + public: + virtual float apply(float newValue, float oldValue) = 0; + }; + + class ScaleFilter : public Filter { + public: + virtual float apply(float newValue, float oldValue) { + return newValue * _scale; + } + + float _scale{ 1.0 }; + }; + + class PulseFilter : public Filter { + public: + virtual float apply(float newValue, float oldValue) { + // ??? + } + + float _lastEmitValue{ 0 }; + float _lastEmitTime{ 0 }; + float _interval{ -1.0f }; + }; + + using FilterList = std::list; + + /* + * encapsulates a source, destination and filters to apply + */ + class Route { + public: + Endpoint* _source; + Endpoint* _destination; + FilterList _filters; + }; + + using ValueMap = std::map; + + class Mapping { + public: + // List of routes + using List = std::list; + // Map of source channels to route lists + using Map = std::map; + + Map _channelMappings; + ValueMap _lastValues; + }; + + class MappingsStack { + std::list _stack; + ValueMap _lastValues; + + void update() { + EndpointList hardwareInputs = getHardwareEndpoints(); + ValueMap currentValues; + + for (auto input : hardwareInputs) { + currentValues[input] = input->value(); + } + + // Now process the current values for each level of the stack + for (auto& mapping : _stack) { + update(mapping, currentValues); + } + + _lastValues = currentValues; + } + + void update(Mapping& mapping, ValueMap& values) { + ValueMap updates; + EndpointList consumedEndpoints; + for (const auto& entry : values) { + Endpoint* endpoint = entry.first; + if (!mapping._channelMappings.count(endpoint)) { + continue; + } + + const Mapping::List& routes = mapping._channelMappings[endpoint]; + consumedEndpoints.push_back(endpoint); + for (const auto& route : routes) { + float lastValue = 0; + if (mapping._lastValues.count(endpoint)) { + lastValue = mapping._lastValues[endpoint]; + } + float value = entry.second; + for (const auto& filter : route._filters) { + value = filter->apply(value, lastValue); + } + updates[route._destination] = value; + } + } + + // Update the last seen values + mapping._lastValues = values; + + // Remove all the consumed inputs + for (auto endpoint : consumedEndpoints) { + values.erase(endpoint); + } + + // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) + for (const auto& entry : updates) { + values[entry.first] = entry.second; + } + } + }; +} diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index b72fadafa0..71c5a83331 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -339,8 +339,6 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) { GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat); - auto semantic = texture.getTexelFormat().getSemantic(); - glTexImage2D(GL_TEXTURE_2D, 0, texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0, texelFormat.format, texelFormat.type, bytes); diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index bc64ec4ccc..f542e63668 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -68,10 +68,10 @@ const float DEFAULT_REACH_LENGTH = 1.5f; SixenseManager::SixenseManager() : InputDevice("Hydra"), -#ifdef __APPLE__ - _sixenseLibrary(NULL), -#endif _reachLength(DEFAULT_REACH_LENGTH), +#ifdef __APPLE__ + _sixenseLibrary(nullptr), +#endif _hydrasConnected(false) { } diff --git a/libraries/networking/src/AssetResourceRequest.h b/libraries/networking/src/AssetResourceRequest.h index aee26a5d5a..0024426be0 100644 --- a/libraries/networking/src/AssetResourceRequest.h +++ b/libraries/networking/src/AssetResourceRequest.h @@ -30,7 +30,7 @@ private slots: void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); private: - AssetRequest* _assetRequest; + AssetRequest* _assetRequest { nullptr }; }; #endif diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index 96d73676f0..639b2990ec 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -91,6 +91,7 @@ SendQueue& Connection::getSendQueue() { // set defaults on the send queue from our congestion control object and estimatedTimeout() _sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod); + _sendQueue->setSyncInterval(_synInterval); _sendQueue->setEstimatedTimeout(estimatedTimeout()); _sendQueue->setFlowWindowSize(std::min(_flowWindowSize, (int) _congestionControl->_congestionWindowSize)); } diff --git a/libraries/networking/src/udt/Packet.cpp b/libraries/networking/src/udt/Packet.cpp index d06ff9707e..e068727e6f 100644 --- a/libraries/networking/src/udt/Packet.cpp +++ b/libraries/networking/src/udt/Packet.cpp @@ -13,6 +13,8 @@ using namespace udt; +static int packetMetaTypeId = qRegisterMetaType("Packet*"); + int Packet::localHeaderSize(bool isPartOfMessage) { return sizeof(Packet::SequenceNumberAndBitField) + (isPartOfMessage ? sizeof(Packet::MessageNumberAndBitField) + sizeof(MessagePartNumber) : 0); diff --git a/libraries/networking/src/udt/Packet.h b/libraries/networking/src/udt/Packet.h index 24b9144672..02d2c3d9bd 100644 --- a/libraries/networking/src/udt/Packet.h +++ b/libraries/networking/src/udt/Packet.h @@ -88,7 +88,9 @@ private: mutable PacketPosition _packetPosition { PacketPosition::ONLY }; mutable MessagePartNumber _messagePartNumber { 0 }; }; - + } // namespace udt +Q_DECLARE_METATYPE(udt::Packet*); + #endif // hifi_Packet_h diff --git a/libraries/networking/src/udt/PacketList.cpp b/libraries/networking/src/udt/PacketList.cpp index 09d8628a1c..332603a8b9 100644 --- a/libraries/networking/src/udt/PacketList.cpp +++ b/libraries/networking/src/udt/PacketList.cpp @@ -15,6 +15,8 @@ using namespace udt; +static int packetListMetaTypeId = qRegisterMetaType("PacketList*"); + std::unique_ptr PacketList::create(PacketType packetType, QByteArray extendedHeader, bool isReliable, bool isOrdered) { auto packetList = std::unique_ptr(new PacketList(packetType, extendedHeader, diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h index 5337094d1f..a6bee9f5eb 100644 --- a/libraries/networking/src/udt/PacketList.h +++ b/libraries/networking/src/udt/PacketList.h @@ -118,4 +118,6 @@ template std::unique_ptr PacketList::takeFront() { } +Q_DECLARE_METATYPE(udt::PacketList*); + #endif // hifi_PacketList_h diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index 01175d0a94..fd11b0f41e 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -439,7 +439,8 @@ bool SendQueue::isInactive(bool sentAPacket) { } else { // We think the client is still waiting for data (based on the sequence number gap) // Let's wait either for a response from the client or until the estimated timeout - auto waitDuration = std::chrono::microseconds(_estimatedTimeout); + // (plus the sync interval to allow the client to respond) has elapsed + auto waitDuration = std::chrono::microseconds(_estimatedTimeout + _syncInterval); // use our condition_variable_any to wait auto cvStatus = _emptyCondition.wait_for(locker, waitDuration); diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index 96de12a971..3fe4006139 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -63,6 +63,7 @@ public: void setPacketSendPeriod(int newPeriod) { _packetSendPeriod = newPeriod; } void setEstimatedTimeout(int estimatedTimeout) { _estimatedTimeout = estimatedTimeout; } + void setSyncInterval(int syncInterval) { _syncInterval = syncInterval; } public slots: void stop(); @@ -114,6 +115,7 @@ private: std::atomic _state { State::NotStarted }; std::atomic _estimatedTimeout { 0 }; // Estimated timeout, set from CC + std::atomic _syncInterval { udt::DEFAULT_SYN_INTERVAL_USECS }; // Sync interval, set from CC std::atomic _timeoutExpiryCount { 0 }; // The number of times the timeout has expired without response from client std::atomic _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK) diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index bc34c6e294..6d4a834879 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -24,16 +24,10 @@ using namespace udt; -Q_DECLARE_METATYPE(Packet*); -Q_DECLARE_METATYPE(PacketList*); - Socket::Socket(QObject* parent) : QObject(parent), _synTimer(new QTimer(this)) { - qRegisterMetaType("Packet*"); - qRegisterMetaType("PacketList*"); - connect(&_udpSocket, &QUdpSocket::readyRead, this, &Socket::readPendingDatagrams); // make sure our synchronization method is called every SYN interval diff --git a/libraries/shared/src/AtRestDetector.cpp b/libraries/shared/src/AtRestDetector.cpp new file mode 100644 index 0000000000..5e623a005c --- /dev/null +++ b/libraries/shared/src/AtRestDetector.cpp @@ -0,0 +1,41 @@ +#include "AtRestDetector.h" +#include "SharedLogging.h" + +AtRestDetector::AtRestDetector(const glm::vec3& startPosition, const glm::quat& startRotation) { + reset(startPosition, startRotation); +} + +void AtRestDetector::reset(const glm::vec3& startPosition, const glm::quat& startRotation) { + _positionAverage = startPosition; + _positionVariance = 0.0f; + + glm::quat ql = glm::log(startRotation); + _quatLogAverage = glm::vec3(ql.x, ql.y, ql.z); + _quatLogVariance = 0.0f; +} + +bool AtRestDetector::update(float dt, const glm::vec3& position, const glm::quat& rotation) { + const float TAU = 1.0f; + float delta = glm::min(dt / TAU, 1.0f); + + // keep a running average of position. + _positionAverage = position * delta + _positionAverage * (1 - delta); + + // keep a running average of position variances. + glm::vec3 dx = position - _positionAverage; + _positionVariance = glm::dot(dx, dx) * delta + _positionVariance * (1 - delta); + + // keep a running average of quaternion logarithms. + glm::quat quatLogAsQuat = glm::log(rotation); + glm::vec3 quatLog(quatLogAsQuat.x, quatLogAsQuat.y, quatLogAsQuat.z); + _quatLogAverage = quatLog * delta + _quatLogAverage * (1 - delta); + + // keep a running average of quatLog variances. + glm::vec3 dql = quatLog - _quatLogAverage; + _quatLogVariance = glm::dot(dql, dql) * delta + _quatLogVariance * (1 - delta); + + const float POSITION_VARIANCE_THRESHOLD = 0.001f; + const float QUAT_LOG_VARIANCE_THRESHOLD = 0.00002f; + + return _positionVariance < POSITION_VARIANCE_THRESHOLD && _quatLogVariance < QUAT_LOG_VARIANCE_THRESHOLD; +} diff --git a/libraries/shared/src/AtRestDetector.h b/libraries/shared/src/AtRestDetector.h new file mode 100644 index 0000000000..d82e54a692 --- /dev/null +++ b/libraries/shared/src/AtRestDetector.h @@ -0,0 +1,34 @@ +// +// AtRestDetector.h +// libraries/shared/src +// +// Created by Anthony Thibault on 10/6/2015 +// 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_AtRestDetector_h +#define hifi_AtRestDetector_h + +#include +#include + +class AtRestDetector { +public: + AtRestDetector(const glm::vec3& startPosition, const glm::quat& startRotation); + void reset(const glm::vec3& startPosition, const glm::quat& startRotation); + + // returns true if object is at rest, dt in assumed to be seconds. + bool update(float dt, const glm::vec3& position, const glm::quat& startRotation); + +protected: + glm::vec3 _positionAverage; + float _positionVariance; + + glm::vec3 _quatLogAverage; + float _quatLogVariance; +}; + +#endif diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 9b443fc1ae..d480c0ac74 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -979,7 +979,7 @@ resetMe: { resetMe: true, }, - grabbaleKey: { + grabbableKey: { grabbable: false } }) @@ -1165,7 +1165,7 @@ resetMe: true, }, grabbableKey: { - grabbale: false + grabbable: false } }) }); @@ -1207,7 +1207,7 @@ resetMe: { resetMe: true, }, - grabbaleKey: { + grabbableKey: { grabbable: false } }) diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 6b4a2528cf..693e2eb28b 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -946,7 +946,7 @@ MasterReset = function() { resetMe: { resetMe: true, }, - grabbaleKey: { + grabbableKey: { grabbable: false } }) @@ -1132,7 +1132,7 @@ MasterReset = function() { resetMe: true, }, grabbableKey: { - grabbale: false + grabbable: false } }) }); @@ -1174,7 +1174,7 @@ MasterReset = function() { resetMe: { resetMe: true, }, - grabbaleKey: { + grabbableKey: { grabbable: false } })