diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 04acae6f05..4f123a6a8f 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -316,6 +316,10 @@ void AudioMixer::sendStatsPacket() { addTiming(_mixTiming, "mix"); addTiming(_eventsTiming, "events"); +#ifdef HIFI_AUDIO_THROTTLE_DEBUG + timingStats["ns_per_throttle"] = (_stats.totalMixes > 0) ? (float)(_stats.throttleTime / _stats.totalMixes) : 0; +#endif + // call it "avg_..." to keep it higher in the display, sorted alphabetically statsObject["avg_timing_stats"] = timingStats; diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 4b02ca1567..adc6413316 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -46,10 +46,12 @@ void sendMutePacket(const SharedNodePointer& node, AudioMixerClientData&); void sendEnvironmentPacket(const SharedNodePointer& node, AudioMixerClientData& data); // mix helpers -bool shouldIgnoreNode(const SharedNodePointer& listener, const SharedNodePointer& node); -float gainForSource(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, +inline bool shouldIgnoreNode(const SharedNodePointer& listener, const SharedNodePointer& node); +inline float approximateGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, + const glm::vec3& relativePosition); +inline float computeGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, bool isEcho); -float azimuthForSource(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, +inline float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition); void AudioMixerSlave::configure(ConstIter begin, ConstIter end, unsigned int frame, float throttlingRatio) { @@ -126,9 +128,10 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { AudioMixerClientData&, const QUuid&, const AvatarAudioStream&, const PositionalAudioStream&); auto allStreams = [&](const SharedNodePointer& node, MixFunctor mixFunctor) { AudioMixerClientData* nodeData = static_cast(node->getLinkedData()); + auto nodeID = node->getUUID(); for (auto& streamPair : nodeData->getAudioStreams()) { auto nodeStream = streamPair.second; - (this->*mixFunctor)(*listenerData, node->getUUID(), *listenerAudioStream, *nodeStream); + (this->*mixFunctor)(*listenerData, nodeID, *listenerAudioStream, *nodeStream); } }; @@ -147,14 +150,28 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { if (!isThrottling) { allStreams(node, &AudioMixerSlave::mixStream); } else { +#ifdef HIFI_AUDIO_THROTTLE_DEBUG + auto throttleStart = p_high_resolution_clock::now(); +#endif + AudioMixerClientData* nodeData = static_cast(node->getLinkedData()); + auto nodeID = node->getUUID(); // compute the node's max relative volume float nodeVolume; for (auto& streamPair : nodeData->getAudioStreams()) { auto nodeStream = streamPair.second; - float distance = glm::length(nodeStream->getPosition() - listenerAudioStream->getPosition()); - nodeVolume = std::max(nodeStream->getLastPopOutputTrailingLoudness() / distance, nodeVolume); + + // approximate the gain + glm::vec3 relativePosition = nodeStream->getPosition() - listenerAudioStream->getPosition(); + float gain = approximateGain(*listenerAudioStream, *nodeStream, relativePosition); + + // modify by hrtf gain adjustment + auto& hrtf = listenerData->hrtfForStream(nodeID, nodeStream->getStreamIdentifier()); + gain *= hrtf.getGainAdjustment(); + + auto streamVolume = nodeStream->getLastPopOutputTrailingLoudness() * gain; + nodeVolume = std::max(streamVolume, nodeVolume); } // max-heapify the nodes by relative volume @@ -162,6 +179,13 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { if (!throttledNodes.empty()) { std::push_heap(throttledNodes.begin(), throttledNodes.end()); } + +#ifdef HIFI_AUDIO_THROTTLE_DEBUG + auto throttleEnd = p_high_resolution_clock::now(); + uint64_t throttleTime = + std::chrono::duration_cast(throttleEnd - throttleStart).count(); + stats.throttleTime += throttleTime; +#endif } } }); @@ -227,9 +251,9 @@ void AudioMixerSlave::addStream(AudioMixerClientData& listenerNodeData, const QU glm::vec3 relativePosition = streamToAdd.getPosition() - listeningNodeStream.getPosition(); float distance = glm::max(glm::length(relativePosition), EPSILON); - float gain = gainForSource(listeningNodeStream, streamToAdd, relativePosition, isEcho); - float azimuth = isEcho ? 0.0f : azimuthForSource(listeningNodeStream, listeningNodeStream, relativePosition); - static const int HRTF_DATASET_INDEX = 1; + float gain = computeGain(listeningNodeStream, streamToAdd, relativePosition, isEcho); + float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition); + const int HRTF_DATASET_INDEX = 1; if (!streamToAdd.lastPopSucceeded()) { bool forceSilentBlock = true; @@ -330,7 +354,7 @@ std::unique_ptr createAudioPacket(PacketType type, int size, quint16 s } void sendMixPacket(const SharedNodePointer& node, AudioMixerClientData& data, QByteArray& buffer) { - static const int MIX_PACKET_SIZE = + const int MIX_PACKET_SIZE = sizeof(quint16) + AudioConstants::MAX_CODEC_NAME_LENGTH_ON_WIRE + AudioConstants::NETWORK_FRAME_BYTES_STEREO; quint16 sequence = data.getOutgoingSequenceNumber(); QString codec = data.getCodecName(); @@ -345,7 +369,7 @@ void sendMixPacket(const SharedNodePointer& node, AudioMixerClientData& data, QB } void sendSilentPacket(const SharedNodePointer& node, AudioMixerClientData& data) { - static const int SILENT_PACKET_SIZE = + const int SILENT_PACKET_SIZE = sizeof(quint16) + AudioConstants::MAX_CODEC_NAME_LENGTH_ON_WIRE + sizeof(quint16); quint16 sequence = data.getOutgoingSequenceNumber(); QString codec = data.getCodecName(); @@ -475,40 +499,54 @@ bool shouldIgnoreNode(const SharedNodePointer& listener, const SharedNodePointer return ignore; } -float gainForSource(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, - const glm::vec3& relativePosition, bool isEcho) { +static const float ATTENUATION_START_DISTANCE = 1.0f; + +float approximateGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, + const glm::vec3& relativePosition) { float gain = 1.0f; - float distanceBetween = glm::length(relativePosition); - - if (distanceBetween < EPSILON) { - distanceBetween = EPSILON; - } - + // injector: apply attenuation if (streamToAdd.getType() == PositionalAudioStream::Injector) { gain *= reinterpret_cast(&streamToAdd)->getAttenuationRatio(); } - if (!isEcho && (streamToAdd.getType() == PositionalAudioStream::Microphone)) { - // source is another avatar, apply fixed off-axis attenuation to make them quieter as they turn away from listener - glm::vec3 rotatedListenerPosition = glm::inverse(streamToAdd.getOrientation()) * relativePosition; + // avatar: skip attenuation - it is too costly to approximate + // distance attenuation: approximate, ignore zone-specific attenuations + // this is a good approximation for streams further than ATTENUATION_START_DISTANCE + // those streams closer will be amplified; amplifying close streams is acceptable + // when throttling, as close streams are expected to be heard by a user + float distance = glm::length(relativePosition); + return gain / distance; +} + +float computeGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, + const glm::vec3& relativePosition, bool isEcho) { + float gain = 1.0f; + + // injector: apply attenuation + if (streamToAdd.getType() == PositionalAudioStream::Injector) { + gain *= reinterpret_cast(&streamToAdd)->getAttenuationRatio(); + + // avatar: apply fixed off-axis attenuation to make them quieter as they turn away + } else if (!isEcho && (streamToAdd.getType() == PositionalAudioStream::Microphone)) { + glm::vec3 rotatedListenerPosition = glm::inverse(streamToAdd.getOrientation()) * relativePosition; float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedListenerPosition)); const float MAX_OFF_AXIS_ATTENUATION = 0.2f; - const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f; - + const float OFF_AXIS_ATTENUATION_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f; float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + - (OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / PI_OVER_TWO)); + (angleOfDelivery * (OFF_AXIS_ATTENUATION_STEP / PI_OVER_TWO)); - // multiply the current attenuation coefficient by the calculated off axis coefficient gain *= offAxisCoefficient; } - float attenuationPerDoublingInDistance = AudioMixer::getAttenuationPerDoublingInDistance(); - auto& zoneSettings = AudioMixer::getZoneSettings(); auto& audioZones = AudioMixer::getAudioZones(); + auto& zoneSettings = AudioMixer::getZoneSettings(); + + // find distance attenuation coefficient + float attenuationPerDoublingInDistance = AudioMixer::getAttenuationPerDoublingInDistance(); for (int i = 0; i < zoneSettings.length(); ++i) { if (audioZones[zoneSettings[i].source].contains(streamToAdd.getPosition()) && audioZones[zoneSettings[i].listener].contains(listeningNodeStream.getPosition())) { @@ -517,16 +555,17 @@ float gainForSource(const AvatarAudioStream& listeningNodeStream, const Position } } - const float ATTENUATION_BEGINS_AT_DISTANCE = 1.0f; - if (distanceBetween >= ATTENUATION_BEGINS_AT_DISTANCE) { + // distance attenuation + float distance = glm::length(relativePosition); + assert(ATTENUATION_START_DISTANCE > EPSILON); + if (distance >= ATTENUATION_START_DISTANCE) { // translate the zone setting to gain per log2(distance) float g = 1.0f - attenuationPerDoublingInDistance; - g = (g < EPSILON) ? EPSILON : g; - g = (g > 1.0f) ? 1.0f : g; + g = glm::clamp(g, EPSILON, 1.0f); // calculate the distance coefficient using the distance to this node - float distanceCoefficient = fastExp2f(fastLog2f(g) * fastLog2f(distanceBetween/ATTENUATION_BEGINS_AT_DISTANCE)); + float distanceCoefficient = fastExp2f(fastLog2f(g) * fastLog2f(distance/ATTENUATION_START_DISTANCE)); // multiply the current attenuation coefficient by the distance coefficient gain *= distanceCoefficient; @@ -535,7 +574,7 @@ float gainForSource(const AvatarAudioStream& listeningNodeStream, const Position return gain; } -float azimuthForSource(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, +float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition) { glm::quat inverseOrientation = glm::inverse(listeningNodeStream.getOrientation()); diff --git a/assignment-client/src/audio/AudioMixerStats.cpp b/assignment-client/src/audio/AudioMixerStats.cpp index a50c0d26c1..a3a3a215bc 100644 --- a/assignment-client/src/audio/AudioMixerStats.cpp +++ b/assignment-client/src/audio/AudioMixerStats.cpp @@ -20,6 +20,9 @@ void AudioMixerStats::reset() { hrtfThrottleRenders = 0; manualStereoMixes = 0; manualEchoMixes = 0; +#ifdef HIFI_AUDIO_THROTTLE_DEBUG + throttleTime = 0; +#endif } void AudioMixerStats::accumulate(const AudioMixerStats& otherStats) { @@ -31,4 +34,7 @@ void AudioMixerStats::accumulate(const AudioMixerStats& otherStats) { hrtfThrottleRenders += otherStats.hrtfThrottleRenders; manualStereoMixes += otherStats.manualStereoMixes; manualEchoMixes += otherStats.manualEchoMixes; +#ifdef HIFI_AUDIO_THROTTLE_DEBUG + throttleTime += otherStats.throttleTime; +#endif } diff --git a/assignment-client/src/audio/AudioMixerStats.h b/assignment-client/src/audio/AudioMixerStats.h index cb85006061..f7e3ed1525 100644 --- a/assignment-client/src/audio/AudioMixerStats.h +++ b/assignment-client/src/audio/AudioMixerStats.h @@ -12,6 +12,10 @@ #ifndef hifi_AudioMixerStats_h #define hifi_AudioMixerStats_h +#ifdef HIFI_AUDIO_THROTTLE_DEBUG +#include +#endif + struct AudioMixerStats { int sumStreams { 0 }; int sumListeners { 0 }; @@ -25,6 +29,10 @@ struct AudioMixerStats { int manualStereoMixes { 0 }; int manualEchoMixes { 0 }; +#ifdef HIFI_AUDIO_THROTTLE_DEBUG + uint64_t throttleTime { 0 }; +#endif + void reset(); void accumulate(const AudioMixerStats& otherStats); }; diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index dc1a693590..02dc552dae 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -34,7 +34,7 @@ EntityServer::EntityServer(ReceivedMessage& message) : DependencyManager::set(); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase }, + packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase, PacketType::EntityPhysics }, this, "handleEntityPacket"); } diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 20d2711743..d27d068f84 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -141,218 +141,6 @@ "can_set": true } ] - }, - { - "label": "Operating Hours", - "help": "\"Open\" domains can be searched using their operating hours. Hours are entered in the local timezone, selected below.", - - "name": "weekday_hours", - "caption": "Weekday Hours (Monday-Friday)", - "type": "table", - "can_add_new_rows": false, - "columns": [ - { - "name": "open", - "label": "Opening Time", - "type": "time", - "default": "00:00", - "editable": true - }, - { - "name": "close", - "label": "Closing Time", - "type": "time", - "default": "23:59", - "editable": true - } - ] - }, - { - "name": "weekend_hours", - "label": "Weekend Hours (Saturday/Sunday)", - "type": "table", - "can_add_new_rows": false, - "columns": [ - { - "name": "open", - "label": "Opening Time", - "type": "time", - "default": "00:00", - "editable": true - }, - { - "name": "close", - "label": "Closing Time", - "type": "time", - "default": "23:59", - "editable": true - } - ] - }, - { - "label": "Time Zone", - "name": "utc_offset", - "caption": "Time Zone", - "help": "This server's time zone. Used to define your server's operating hours.", - "type": "select", - "options": [ - { - "value": "-12", - "label": "UTC-12:00" - }, - { - "value": "-11", - "label": "UTC-11:00" - }, - { - "value": "-10", - "label": "UTC-10:00" - }, - { - "value": "-9.5", - "label": "UTC-09:30" - }, - { - "value": "-9", - "label": "UTC-09:00" - }, - { - "value": "-8", - "label": "UTC-08:00" - }, - { - "value": "-7", - "label": "UTC-07:00" - }, - { - "value": "-6", - "label": "UTC-06:00" - }, - { - "value": "-5", - "label": "UTC-05:00" - }, - { - "value": "-4", - "label": "UTC-04:00" - }, - { - "value": "-3.5", - "label": "UTC-03:30" - }, - { - "value": "-3", - "label": "UTC-03:00" - }, - { - "value": "-2", - "label": "UTC-02:00" - }, - { - "value": "-1", - "label": "UTC-01:00" - }, - { - "value": "", - "label": "UTC±00:00" - }, - { - "value": "1", - "label": "UTC+01:00" - }, - { - "value": "2", - "label": "UTC+02:00" - }, - { - "value": "3", - "label": "UTC+03:00" - }, - { - "value": "3.5", - "label": "UTC+03:30" - }, - { - "value": "4", - "label": "UTC+04:00" - }, - { - "value": "4.5", - "label": "UTC+04:30" - }, - { - "value": "5", - "label": "UTC+05:00" - }, - { - "value": "5.5", - "label": "UTC+05:30" - }, - { - "value": "5.75", - "label": "UTC+05:45" - }, - { - "value": "6", - "label": "UTC+06:00" - }, - { - "value": "6.5", - "label": "UTC+06:30" - }, - { - "value": "7", - "label": "UTC+07:00" - }, - { - "value": "8", - "label": "UTC+08:00" - }, - { - "value": "8.5", - "label": "UTC+08:30" - }, - { - "value": "8.75", - "label": "UTC+08:45" - }, - { - "value": "9", - "label": "UTC+09:00" - }, - { - "value": "9.5", - "label": "UTC+09:30" - }, - { - "value": "10", - "label": "UTC+10:00" - }, - { - "value": "10.5", - "label": "UTC+10:30" - }, - { - "value": "11", - "label": "UTC+11:00" - }, - { - "value": "12", - "label": "UTC+12:00" - }, - { - "value": "12.75", - "label": "UTC+12:45" - }, - { - "value": "13", - "label": "UTC+13:00" - }, - { - "value": "14", - "label": "UTC+14:00" - } - ] } ] }, diff --git a/domain-server/src/DomainMetadata.cpp b/domain-server/src/DomainMetadata.cpp index d614b1bbd3..c19cefa397 100644 --- a/domain-server/src/DomainMetadata.cpp +++ b/domain-server/src/DomainMetadata.cpp @@ -35,12 +35,6 @@ const QString DomainMetadata::Descriptors::RESTRICTION = "restriction"; // parse const QString DomainMetadata::Descriptors::MATURITY = "maturity"; const QString DomainMetadata::Descriptors::HOSTS = "hosts"; const QString DomainMetadata::Descriptors::TAGS = "tags"; -const QString DomainMetadata::Descriptors::HOURS = "hours"; -const QString DomainMetadata::Descriptors::Hours::WEEKDAY = "weekday"; -const QString DomainMetadata::Descriptors::Hours::WEEKEND = "weekend"; -const QString DomainMetadata::Descriptors::Hours::UTC_OFFSET = "utc_offset"; -const QString DomainMetadata::Descriptors::Hours::OPEN = "open"; -const QString DomainMetadata::Descriptors::Hours::CLOSE = "close"; // descriptors metadata will appear as (JSON): // { "description": String, // capped description // "capacity": Number, @@ -48,11 +42,6 @@ const QString DomainMetadata::Descriptors::Hours::CLOSE = "close"; // "maturity": String, // enum corresponding to ESRB ratings // "hosts": [ String ], // capped list of usernames // "tags": [ String ], // capped list of tags -// "hours": { -// "utc_offset": Number, -// "weekday": [ [ Time, Time ] ], -// "weekend": [ [ Time, Time ] ], -// } // } // metadata will appear as (JSON): @@ -60,52 +49,10 @@ const QString DomainMetadata::Descriptors::Hours::CLOSE = "close"; // // it is meant to be sent to and consumed by an external API -// merge delta into target -// target should be of the form [ OpenTime, CloseTime ], -// delta should be of the form [ { open: Time, close: Time } ] -void parseHours(QVariant delta, QVariant& target) { - using Hours = DomainMetadata::Descriptors::Hours; - static const QVariantList DEFAULT_HOURS{ - { QVariantList{ "00:00", "23:59" } } - }; - target.setValue(DEFAULT_HOURS); - - if (!delta.canConvert()) { - return; - } - - auto& deltaList = *static_cast(delta.data()); - if (deltaList.isEmpty()) { - return; - } - - auto& deltaHours = *static_cast(deltaList.first().data()); - auto open = deltaHours.find(Hours::OPEN); - auto close = deltaHours.find(Hours::CLOSE); - if (open == deltaHours.end() || close == deltaHours.end()) { - return; - } - - // merge delta into new hours - static const int OPEN_INDEX = 0; - static const int CLOSE_INDEX = 1; - auto& hours = *static_cast(static_cast(target.data())->first().data()); - hours[OPEN_INDEX] = open.value(); - hours[CLOSE_INDEX] = close.value(); - - assert(hours[OPEN_INDEX].canConvert()); - assert(hours[CLOSE_INDEX].canConvert()); -} - DomainMetadata::DomainMetadata(QObject* domainServer) : QObject(domainServer) { - // set up the structure necessary for casting during parsing (see parseHours, esp.) + // set up the structure necessary for casting during parsing _metadata[USERS] = QVariantMap {}; - _metadata[DESCRIPTORS] = QVariantMap { { - Descriptors::HOURS, QVariantMap { - { Descriptors::Hours::WEEKDAY, QVariant{} }, - { Descriptors::Hours::WEEKEND, QVariant{} } - } - } }; + _metadata[DESCRIPTORS] = QVariantMap {}; assert(dynamic_cast(domainServer)); DomainServer* server = static_cast(domainServer); @@ -154,16 +101,6 @@ void DomainMetadata::descriptorsChanged() { unsigned int capacity = capacityVariant ? capacityVariant->toUInt() : 0; state[Descriptors::CAPACITY] = capacity; - // parse operating hours - static const QString WEEKDAY_HOURS = "weekday_hours"; - static const QString WEEKEND_HOURS = "weekend_hours"; - static const QString UTC_OFFSET = "utc_offset"; - assert(state[Descriptors::HOURS].canConvert()); - auto& hours = *static_cast(state[Descriptors::HOURS].data()); - hours[Descriptors::Hours::UTC_OFFSET] = descriptors.take(UTC_OFFSET); - parseHours(descriptors[WEEKDAY_HOURS], hours[Descriptors::Hours::WEEKDAY]); - parseHours(descriptors[WEEKEND_HOURS], hours[Descriptors::Hours::WEEKEND]); - #if DEV_BUILD || PR_BUILD qDebug() << "Domain metadata descriptors set:" << QJsonObject::fromVariantMap(_metadata[DESCRIPTORS].toMap()); #endif diff --git a/domain-server/src/DomainMetadata.h b/domain-server/src/DomainMetadata.h index 41f3a60832..ed4e324464 100644 --- a/domain-server/src/DomainMetadata.h +++ b/domain-server/src/DomainMetadata.h @@ -39,15 +39,6 @@ public: static const QString MATURITY; static const QString HOSTS; static const QString TAGS; - static const QString HOURS; - class Hours { - public: - static const QString WEEKDAY; - static const QString WEEKEND; - static const QString UTC_OFFSET; - static const QString OPEN; - static const QString CLOSE; - }; }; DomainMetadata(QObject* domainServer); diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index a0b80875b0..31d6845972 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -270,11 +269,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList _agentPermissions.clear(); } - if (oldVersion < 1.5) { - // This was prior to operating hours, so add default hours - validateDescriptorsMap(); - } - if (oldVersion < 1.6) { unpackPermissions(); @@ -305,46 +299,10 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList } QVariantMap& DomainServerSettingsManager::getDescriptorsMap() { - validateDescriptorsMap(); - static const QString DESCRIPTORS{ "descriptors" }; return *static_cast(getSettingsMap()[DESCRIPTORS].data()); } -void DomainServerSettingsManager::validateDescriptorsMap() { - static const QString WEEKDAY_HOURS{ "descriptors.weekday_hours" }; - static const QString WEEKEND_HOURS{ "descriptors.weekend_hours" }; - static const QString UTC_OFFSET{ "descriptors.utc_offset" }; - - QVariant* weekdayHours = _configMap.valueForKeyPath(WEEKDAY_HOURS, true); - QVariant* weekendHours = _configMap.valueForKeyPath(WEEKEND_HOURS, true); - QVariant* utcOffset = _configMap.valueForKeyPath(UTC_OFFSET, true); - - static const QString OPEN{ "open" }; - static const QString CLOSE{ "close" }; - static const QString DEFAULT_OPEN{ "00:00" }; - static const QString DEFAULT_CLOSE{ "23:59" }; - bool wasMalformed = false; - if (weekdayHours->isNull()) { - *weekdayHours = QVariantList{ QVariantMap{ { OPEN, QVariant(DEFAULT_OPEN) }, { CLOSE, QVariant(DEFAULT_CLOSE) } } }; - wasMalformed = true; - } - if (weekendHours->isNull()) { - *weekendHours = QVariantList{ QVariantMap{ { OPEN, QVariant(DEFAULT_OPEN) }, { CLOSE, QVariant(DEFAULT_CLOSE) } } }; - wasMalformed = true; - } - if (utcOffset->isNull()) { - *utcOffset = QVariant(QTimeZone::systemTimeZone().offsetFromUtc(QDateTime::currentDateTime()) / (float)SECS_PER_HOUR); - wasMalformed = true; - } - - if (wasMalformed) { - // write the new settings to file - persistToFile(); - } -} - - void DomainServerSettingsManager::initializeGroupPermissions(NodePermissionsMap& permissionsRows, QString groupName, NodePermissionsPointer perms) { // this is called when someone has used the domain-settings webpage to add a group. They type the group's name diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 2b5f9518a0..d56a786d4b 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -138,8 +138,6 @@ private: friend class DomainServer; - void validateDescriptorsMap(); - // these cause calls to metaverse's group api void apiGetGroupID(const QString& groupName); void apiGetGroupRanks(const QUuid& groupID); diff --git a/interface/resources/qml/hifi/dialogs/AvatarPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/AvatarPreferencesDialog.qml index 45414cfaf8..86f195612c 100644 --- a/interface/resources/qml/hifi/dialogs/AvatarPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/AvatarPreferencesDialog.qml @@ -7,7 +7,7 @@ PreferencesDialog { id: root objectName: "AvatarPreferencesDialog" title: "Avatar Settings" - showCategories: [ "Avatar Basics", "Snapshots", "Avatar Tuning", "Avatar Camera" ] + showCategories: [ "Avatar Basics", "Avatar Tuning", "Avatar Camera" ] property var settings: Settings { category: root.objectName property alias x: root.x diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index d68d9b4531..6377cda281 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -104,7 +104,7 @@ void setupPreferences() { { auto getter = []()->bool { return SnapshotAnimated::alsoTakeAnimatedSnapshot.get(); }; auto setter = [](bool value) { SnapshotAnimated::alsoTakeAnimatedSnapshot.set(value); }; - preferences->addPreference(new CheckPreference(SNAPSHOTS, "Take Animated GIF Snapshot with HUD Button", getter, setter)); + preferences->addPreference(new CheckPreference(SNAPSHOTS, "Take Animated GIF Snapshot", getter, setter)); } { auto getter = []()->float { return SnapshotAnimated::snapshotAnimatedDuration.get(); }; diff --git a/libraries/audio/src/AudioHRTF.h b/libraries/audio/src/AudioHRTF.h index 6a17a2d3cc..a197264994 100644 --- a/libraries/audio/src/AudioHRTF.h +++ b/libraries/audio/src/AudioHRTF.h @@ -48,6 +48,7 @@ public: // HRTF local gain adjustment in amplitude (1.0 == unity) // void setGainAdjustment(float gain) { _gainAdjust = HRTF_GAIN * gain; }; + float getGainAdjustment() { return _gainAdjust; } private: AudioHRTF(const AudioHRTF&) = delete; diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 36a6079546..57c344adaf 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -131,12 +131,16 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { // handle this packet based on its arrival status. switch (arrivalInfo._status) { + case SequenceNumberStats::Unreasonable: { + lostAudioData(1); + break; + } case SequenceNumberStats::Early: { // Packet is early; write droppable silent samples for each of the skipped packets. // NOTE: we assume that each dropped packet contains the same number of samples // as the packet we just received. int packetsDropped = arrivalInfo._seqDiffFromExpected; - writeFramesForDroppedPackets(packetsDropped * networkFrames); + lostAudioData(packetsDropped); // fall through to OnTime case } @@ -208,6 +212,21 @@ int InboundAudioStream::parseStreamProperties(PacketType type, const QByteArray& } } +int InboundAudioStream::lostAudioData(int numPackets) { + QByteArray decodedBuffer; + + while (numPackets--) { + if (_decoder) { + _decoder->lostFrame(decodedBuffer); + } else { + decodedBuffer.resize(AudioConstants::NETWORK_FRAME_BYTES_STEREO); + memset(decodedBuffer.data(), 0, decodedBuffer.size()); + } + _ringBuffer.writeData(decodedBuffer.data(), decodedBuffer.size()); + } + return 0; +} + int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) { QByteArray decodedBuffer; if (_decoder) { @@ -220,9 +239,6 @@ int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packet } int InboundAudioStream::writeDroppableSilentFrames(int silentFrames) { - if (_decoder) { - _decoder->trackLostFrames(silentFrames); - } // calculate how many silent frames we should drop. int silentSamples = silentFrames * _numChannels; @@ -416,29 +432,6 @@ void InboundAudioStream::packetReceivedUpdateTimingStats() { _lastPacketReceivedTime = now; } -int InboundAudioStream::writeFramesForDroppedPackets(int networkFrames) { - return writeLastFrameRepeatedWithFade(networkFrames); -} - -int InboundAudioStream::writeLastFrameRepeatedWithFade(int frames) { - AudioRingBuffer::ConstIterator frameToRepeat = _ringBuffer.lastFrameWritten(); - int frameSize = _ringBuffer.getNumFrameSamples(); - int samplesToWrite = frames * _numChannels; - int indexOfRepeat = 0; - do { - int samplesToWriteThisIteration = std::min(samplesToWrite, frameSize); - float fade = calculateRepeatedFrameFadeFactor(indexOfRepeat); - if (fade == 1.0f) { - samplesToWrite -= _ringBuffer.writeSamples(frameToRepeat, samplesToWriteThisIteration); - } else { - samplesToWrite -= _ringBuffer.writeSamplesWithFade(frameToRepeat, samplesToWriteThisIteration, fade); - } - indexOfRepeat++; - } while (samplesToWrite > 0); - - return frames; -} - AudioStreamStats InboundAudioStream::getAudioStreamStats() const { AudioStreamStats streamStats; diff --git a/libraries/audio/src/InboundAudioStream.h b/libraries/audio/src/InboundAudioStream.h index f7b79ab136..9494b2f204 100644 --- a/libraries/audio/src/InboundAudioStream.h +++ b/libraries/audio/src/InboundAudioStream.h @@ -115,8 +115,6 @@ public slots: private: void packetReceivedUpdateTimingStats(); - int writeFramesForDroppedPackets(int networkFrames); - void popSamplesNoCheck(int samples); void framesAvailableChanged(); @@ -134,12 +132,11 @@ protected: /// default implementation assumes packet contains raw audio samples after stream properties virtual int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties); + /// produces audio data for lost network packets. + virtual int lostAudioData(int numPackets); + /// writes silent frames to the buffer that may be dropped to reduce latency caused by the buffer virtual int writeDroppableSilentFrames(int silentFrames); - - /// writes the last written frame repeatedly, gradually fading to silence. - /// used for writing samples for dropped packets. - virtual int writeLastFrameRepeatedWithFade(int frames); protected: diff --git a/libraries/audio/src/MixedProcessedAudioStream.cpp b/libraries/audio/src/MixedProcessedAudioStream.cpp index 671d3a9d60..082977246b 100644 --- a/libraries/audio/src/MixedProcessedAudioStream.cpp +++ b/libraries/audio/src/MixedProcessedAudioStream.cpp @@ -31,11 +31,26 @@ int MixedProcessedAudioStream::writeDroppableSilentFrames(int silentFrames) { return deviceSilentFramesWritten; } -int MixedProcessedAudioStream::writeLastFrameRepeatedWithFade(int frames) { - int deviceFrames = networkToDeviceFrames(frames); - int deviceFramesWritten = InboundAudioStream::writeLastFrameRepeatedWithFade(deviceFrames); - emit addedLastFrameRepeatedWithFade(deviceToNetworkFrames(deviceFramesWritten)); - return deviceFramesWritten; +int MixedProcessedAudioStream::lostAudioData(int numPackets) { + QByteArray decodedBuffer; + QByteArray outputBuffer; + + while (numPackets--) { + if (_decoder) { + _decoder->lostFrame(decodedBuffer); + } else { + decodedBuffer.resize(AudioConstants::NETWORK_FRAME_BYTES_STEREO); + memset(decodedBuffer.data(), 0, decodedBuffer.size()); + } + + emit addedStereoSamples(decodedBuffer); + + emit processSamples(decodedBuffer, outputBuffer); + + _ringBuffer.writeData(outputBuffer.data(), outputBuffer.size()); + qCDebug(audiostream, "Wrote %d samples to buffer (%d available)", outputBuffer.size() / (int)sizeof(int16_t), getSamplesAvailable()); + } + return 0; } int MixedProcessedAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) { diff --git a/libraries/audio/src/MixedProcessedAudioStream.h b/libraries/audio/src/MixedProcessedAudioStream.h index d536163d2d..14da1d45af 100644 --- a/libraries/audio/src/MixedProcessedAudioStream.h +++ b/libraries/audio/src/MixedProcessedAudioStream.h @@ -34,8 +34,8 @@ public: protected: int writeDroppableSilentFrames(int silentFrames) override; - int writeLastFrameRepeatedWithFade(int frames) override; int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) override; + int lostAudioData(int numPackets) override; private: int networkToDeviceFrames(int networkFrames); diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index e05db07d0d..c8a14c40be 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -29,7 +29,7 @@ void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer(); + const QUuid myNodeID = nodeList->getSessionUUID(); + propertiesCopy.setParentID(myNodeID); + success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut); + } else { + success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, properties, bufferOut); + } + + if (success) { #ifdef WANT_DEBUG qCDebug(entities) << "calling queueOctreeEditMessage()..."; qCDebug(entities) << " id:" << entityItemID; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 233ce7d88e..6543af5355 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -828,7 +828,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef { // parentID and parentJointIndex are also protected by simulation ownership bool oldOverwrite = overwriteLocalData; overwriteLocalData = overwriteLocalData && !weOwnSimulation; - READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, setParentID); + READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, updateParentID); READ_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex); overwriteLocalData = oldOverwrite; } @@ -1823,28 +1823,6 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } uint8_t userMask = getCollisionMask(); - if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { - // if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the - // "bootstrapping" problem where you can shoot yourself across the room by grabbing something - // and holding it against your own avatar. - QUuid ancestorID = findAncestorOfType(NestableType::Avatar); - if (!ancestorID.isNull() && ancestorID == Physics::getSessionUUID()) { - userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; - } - } - if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { - // also, don't bootstrap our own avatar with a hold action - QList holdActions = getActionsOfType(ACTION_TYPE_HOLD); - QList::const_iterator i = holdActions.begin(); - while (i != holdActions.end()) { - EntityActionPointer action = *i; - if (action->isMine()) { - userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; - break; - } - i++; - } - } if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) != (bool)(userMask & USER_COLLISION_GROUP_OTHER_AVATAR)) { @@ -1854,6 +1832,33 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask userMask ^= USER_COLLISION_MASK_AVATARS | ~userMask; } } + + if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { + bool iAmHoldingThis = false; + // if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the + // "bootstrapping" problem where you can shoot yourself across the room by grabbing something + // and holding it against your own avatar. + QUuid ancestorID = findAncestorOfType(NestableType::Avatar); + if (!ancestorID.isNull() && + (ancestorID == Physics::getSessionUUID() || ancestorID == AVATAR_SELF_ID)) { + iAmHoldingThis = true; + } + // also, don't bootstrap our own avatar with a hold action + QList holdActions = getActionsOfType(ACTION_TYPE_HOLD); + QList::const_iterator i = holdActions.begin(); + while (i != holdActions.end()) { + EntityActionPointer action = *i; + if (action->isMine()) { + iAmHoldingThis = true; + break; + } + i++; + } + + if (iAmHoldingThis) { + userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; + } + } mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b7a8841772..4e92b2a572 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -104,6 +104,7 @@ bool EntityTree::handlesEditPacketType(PacketType packetType) const { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityErase: + case PacketType::EntityPhysics: return true; default: return false; @@ -931,10 +932,15 @@ void EntityTree::initEntityEditFilterEngine(QScriptEngine* engine, std::function qCDebug(entities) << "Filter function specified but not found. Will reject all edits."; _entityEditFilterEngine = nullptr; // So that we don't try to call it. See filterProperties. } + auto entitiesObject = _entityEditFilterEngine->newObject(); + entitiesObject.setProperty("ADD_FILTER_TYPE", FilterType::Add); + entitiesObject.setProperty("EDIT_FILTER_TYPE", FilterType::Edit); + entitiesObject.setProperty("PHYSICS_FILTER_TYPE", FilterType::Physics); + global.setProperty("Entities", entitiesObject); _hasEntityEditFilter = true; } -bool EntityTree::filterProperties(EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, bool isAdd) { +bool EntityTree::filterProperties(EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, FilterType filterType) { if (!_entityEditFilterEngine) { propertiesOut = propertiesIn; wasChanged = false; // not changed @@ -953,7 +959,7 @@ bool EntityTree::filterProperties(EntityItemProperties& propertiesIn, EntityItem auto in = QJsonValue::fromVariant(inputValues.toVariant()); // grab json copy now, because the inputValues might be side effected by the filter. QScriptValueList args; args << inputValues; - args << isAdd; + args << filterType; QScriptValue result = _entityEditFilterFunction.call(_nullObjectForFilter, args); if (_entityEditFilterHadUncaughtExceptions()) { @@ -1001,6 +1007,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c case PacketType::EntityAdd: isAdd = true; // fall through to next case + case PacketType::EntityPhysics: case PacketType::EntityEdit: { quint64 startDecode = 0, endDecode = 0; quint64 startLookup = 0, endLookup = 0; @@ -1010,6 +1017,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c quint64 startLogging = 0, endLogging = 0; bool suppressDisallowedScript = false; + bool isPhysics = message.getType() == PacketType::EntityPhysics; _totalEditMessages++; @@ -1021,6 +1029,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c entityItemID, properties); endDecode = usecTimestampNow(); + if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) { bool passedWhiteList = false; @@ -1053,8 +1062,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } } - if ((isAdd || - (message.getType() == PacketType::EntityEdit && properties.lifetimeChanged())) && + if ((isAdd || properties.lifetimeChanged()) && !senderNode->getCanRez() && senderNode->getCanRezTmp()) { // this node is only allowed to rez temporary entities. if need be, cap the lifetime. if (properties.getLifetime() == ENTITY_ITEM_IMMORTAL_LIFETIME || @@ -1070,8 +1078,9 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c startFilter = usecTimestampNow(); bool wasChanged = false; - // Having (un)lock rights bypasses the filter. - bool allowed = senderNode->isAllowedEditor() || filterProperties(properties, properties, wasChanged, isAdd); + // Having (un)lock rights bypasses the filter, unless it's a physics result. + FilterType filterType = isPhysics ? FilterType::Physics : (isAdd ? FilterType::Add : FilterType::Edit); + bool allowed = (!isPhysics && senderNode->isAllowedEditor()) || filterProperties(properties, properties, wasChanged, filterType); if (!allowed) { auto timestamp = properties.getLastEdited(); properties = EntityItemProperties(); @@ -1088,7 +1097,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c startLookup = usecTimestampNow(); EntityItemPointer existingEntity = findEntityByEntityItemID(entityItemID); endLookup = usecTimestampNow(); - if (existingEntity && message.getType() == PacketType::EntityEdit) { + if (existingEntity && !isAdd) { if (suppressDisallowedScript) { bumpTimestamp(properties); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 9b30096be5..5dad282d3b 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -60,6 +60,11 @@ public: class EntityTree : public Octree, public SpatialParentTree { Q_OBJECT public: + enum FilterType { + Add, + Edit, + Physics + }; EntityTree(bool shouldReaverage = false); virtual ~EntityTree(); @@ -357,7 +362,7 @@ protected: float _maxTmpEntityLifetime { DEFAULT_MAX_TMP_ENTITY_LIFETIME }; - bool filterProperties(EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, bool isAdd); + bool filterProperties(EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, FilterType filterType); bool _hasEntityEditFilter{ false }; QScriptEngine* _entityEditFilterEngine{}; QScriptValue _entityEditFilterFunction{}; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 917009956e..2e6544fbdb 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1468,25 +1468,26 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // HACK: until we get proper LOD management we're going to cap model textures // according to how many unique textures the model uses: - // 1 - 7 textures --> 2048 - // 8 - 31 textures --> 1024 - // 32 - 127 textures --> 512 + // 1 - 8 textures --> 2048 + // 8 - 32 textures --> 1024 + // 33 - 128 textures --> 512 // etc... QSet uniqueTextures; for (auto& material : _fbxMaterials) { material.getTextureNames(uniqueTextures); } int numTextures = uniqueTextures.size(); - const int MAX_NUM_TEXTURES_AT_MAX_RESOLUTION = 7; + const int MAX_NUM_TEXTURES_AT_MAX_RESOLUTION = 8; + int maxWidth = sqrt(MAX_NUM_PIXELS_FOR_FBX_TEXTURE); if (numTextures > MAX_NUM_TEXTURES_AT_MAX_RESOLUTION) { - int maxWidth = sqrt(MAX_NUM_PIXELS_FOR_FBX_TEXTURE + 1); - int t = numTextures; - t /= MAX_NUM_TEXTURES_AT_MAX_RESOLUTION; - while (t > 0) { + int numTextureThreshold = MAX_NUM_TEXTURES_AT_MAX_RESOLUTION; + const int MIN_MIP_TEXTURE_WIDTH = 64; + do { maxWidth /= 2; - t /= 4; - } - qCDebug(modelformat) << "max square texture width =" << maxWidth << " for model" << url; + numTextureThreshold *= 4; + } while (numTextureThreshold < numTextures && maxWidth > MIN_MIP_TEXTURE_WIDTH); + + qCDebug(modelformat) << "Capped square texture width =" << maxWidth << "for model" << url << "with" << numTextures << "textures"; for (auto& material : _fbxMaterials) { material.setMaxNumPixelsPerTexture(maxWidth * maxWidth); } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 7bfe1d1845..f4a02ad805 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -827,18 +827,26 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { }); if (ignoreEnabled) { - QReadLocker ignoredSetLocker{ &_ignoredSetLock }; // read lock for insert - QReadLocker personalMutedSetLocker{ &_personalMutedSetLock }; // read lock for insert - // add this nodeID to our set of ignored IDs - _ignoredNodeIDs.insert(nodeID); - // add this nodeID to our set of personal muted IDs - _personalMutedNodeIDs.insert(nodeID); + { + QReadLocker ignoredSetLocker{ &_ignoredSetLock }; // read lock for insert + // add this nodeID to our set of ignored IDs + _ignoredNodeIDs.insert(nodeID); + } + { + QReadLocker personalMutedSetLocker{ &_personalMutedSetLock }; // read lock for insert + // add this nodeID to our set of personal muted IDs + _personalMutedNodeIDs.insert(nodeID); + } emit ignoredNode(nodeID, true); } else { - QWriteLocker ignoredSetLocker{ &_ignoredSetLock }; // write lock for unsafe_erase - QWriteLocker personalMutedSetLocker{ &_personalMutedSetLock }; // write lock for unsafe_erase - _ignoredNodeIDs.unsafe_erase(nodeID); - _personalMutedNodeIDs.unsafe_erase(nodeID); + { + QWriteLocker ignoredSetLocker{ &_ignoredSetLock }; // write lock for unsafe_erase + _ignoredNodeIDs.unsafe_erase(nodeID); + } + { + QWriteLocker personalMutedSetLocker{ &_personalMutedSetLock }; // write lock for unsafe_erase + _personalMutedNodeIDs.unsafe_erase(nodeID); + } emit ignoredNode(nodeID, false); } @@ -850,10 +858,14 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { void NodeList::removeFromIgnoreMuteSets(const QUuid& nodeID) { // don't remove yourself, or nobody if (!nodeID.isNull() && _sessionUUID != nodeID) { - QWriteLocker ignoredSetLocker{ &_ignoredSetLock }; - QWriteLocker personalMutedSetLocker{ &_personalMutedSetLock }; - _ignoredNodeIDs.unsafe_erase(nodeID); - _personalMutedNodeIDs.unsafe_erase(nodeID); + { + QWriteLocker ignoredSetLocker{ &_ignoredSetLock }; + _ignoredNodeIDs.unsafe_erase(nodeID); + } + { + QWriteLocker personalMutedSetLocker{ &_personalMutedSetLock }; + _personalMutedNodeIDs.unsafe_erase(nodeID); + } } } diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index 02d1711230..f38d24c31f 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -16,6 +16,14 @@ void UserActivityLoggerScriptingInterface::enabledEdit() { logAction("enabled_edit"); } +void UserActivityLoggerScriptingInterface::openedTablet() { + logAction("opened_tablet"); +} + +void UserActivityLoggerScriptingInterface::closedTablet() { + logAction("closed_tablet"); +} + void UserActivityLoggerScriptingInterface::openedMarketplace() { logAction("opened_marketplace"); } diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index a202858a1c..b827b2262a 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -21,6 +21,8 @@ class UserActivityLoggerScriptingInterface : public QObject, public Dependency { Q_OBJECT public: Q_INVOKABLE void enabledEdit(); + Q_INVOKABLE void openedTablet(); + Q_INVOKABLE void closedTablet(); Q_INVOKABLE void openedMarketplace(); Q_INVOKABLE void toggledAway(bool isAway); Q_INVOKABLE void tutorialProgress(QString stepName, int stepNumber, float secondsToComplete, diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 7cb02010f8..e2dc8d73e6 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -48,7 +48,8 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_ENTITIES_SERVER_SCRIPTS; + case PacketType::EntityPhysics: + return VERSION_ENTITIES_PHYSICS_PACKET; case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::JsonFilter); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index d695bde62a..88b5ec19ad 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -110,7 +110,8 @@ public: EntityScriptGetStatus, EntityScriptGetStatusReply, ReloadEntityServerScript, - LAST_PACKET_TYPE = ReloadEntityServerScript + EntityPhysics, + LAST_PACKET_TYPE = EntityPhysics }; }; @@ -201,6 +202,7 @@ const PacketVersion VERSION_WEB_ENTITIES_SUPPORT_DPI = 63; const PacketVersion VERSION_ENTITIES_ARROW_ACTION = 64; const PacketVersion VERSION_ENTITIES_LAST_EDITED_BY = 65; const PacketVersion VERSION_ENTITIES_SERVER_SCRIPTS = 66; +const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67; enum class EntityQueryPacketVersion: PacketVersion { JsonFilter = 18 diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index e9891020b3..c175a836cc 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -199,15 +199,12 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { return; } assert(entityTreeIsLocked()); - if (_motionType == MOTION_TYPE_KINEMATIC) { + if (_motionType == MOTION_TYPE_KINEMATIC && !_entity->hasAncestorOfType(NestableType::Avatar)) { BT_PROFILE("kinematicIntegration"); // This is physical kinematic motion which steps strictly by the subframe count // of the physics simulation and uses full gravity for acceleration. - if (_entity->hasAncestorOfType(NestableType::Avatar)) { - _entity->setAcceleration(glm::vec3(0.0f)); - } else { - _entity->setAcceleration(_entity->getGravity()); - } + _entity->setAcceleration(_entity->getGravity()); + uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); float dt = (thisStep - _lastKinematicStep) * PHYSICS_ENGINE_FIXED_SUBSTEP; _entity->stepKinematicMotion(dt); @@ -614,7 +611,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.setClientOnly(_entity->getClientOnly()); properties.setOwningAvatarID(_entity->getOwningAvatarID()); - entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, tree, id, properties); + entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree, id, properties); _entity->setLastBroadcast(now); // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server @@ -630,7 +627,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ newQueryCubeProperties.setClientOnly(entityDescendant->getClientOnly()); newQueryCubeProperties.setOwningAvatarID(entityDescendant->getOwningAvatarID()); - entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, tree, + entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree, descendant->getID(), newQueryCubeProperties); entityDescendant->setLastBroadcast(now); } diff --git a/libraries/plugins/src/plugins/CodecPlugin.h b/libraries/plugins/src/plugins/CodecPlugin.h index 404f05e860..cb5b857be8 100644 --- a/libraries/plugins/src/plugins/CodecPlugin.h +++ b/libraries/plugins/src/plugins/CodecPlugin.h @@ -23,8 +23,7 @@ public: virtual ~Decoder() { } virtual void decode(const QByteArray& encodedBuffer, QByteArray& decodedBuffer) = 0; - // numFrames - number of samples (mono) or sample-pairs (stereo) - virtual void trackLostFrames(int numFrames) = 0; + virtual void lostFrame(QByteArray& decodedBuffer) = 0; }; class CodecPlugin : public Plugin { diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index 4a74fb4033..89d5ce709d 100644 --- a/libraries/shared/src/AABox.cpp +++ b/libraries/shared/src/AABox.cpp @@ -360,7 +360,7 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec3& point, BoxFace face) con case MIN_Z_FACE: return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z), - glm::vec3(_corner.x + _scale.z, _corner.y + _scale.y, _corner.z)); + glm::vec3(_corner.x + _scale.x, _corner.y + _scale.y, _corner.z)); default: //quiet windows warnings case MAX_Z_FACE: diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 35e574bf06..ddc3f416e0 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1034,6 +1034,13 @@ AACube SpatiallyNestable::getQueryAACube() const { bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const { bool success; + if (nestableType == NestableType::Avatar) { + QUuid parentID = getParentID(); + if (parentID == AVATAR_SELF_ID) { + return true; + } + } + SpatiallyNestablePointer parent = getParentPointer(success); if (!success || !parent) { return false; @@ -1048,6 +1055,14 @@ bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const { const QUuid SpatiallyNestable::findAncestorOfType(NestableType nestableType) const { bool success; + + if (nestableType == NestableType::Avatar) { + QUuid parentID = getParentID(); + if (parentID == AVATAR_SELF_ID) { + return AVATAR_SELF_ID; // TODO -- can we put nodeID here? + } + } + SpatiallyNestablePointer parent = getParentPointer(success); if (!success || !parent) { return QUuid(); diff --git a/plugins/hifiCodec/src/HiFiCodec.cpp b/plugins/hifiCodec/src/HiFiCodec.cpp index 77c369dcae..2c7151fe59 100644 --- a/plugins/hifiCodec/src/HiFiCodec.cpp +++ b/plugins/hifiCodec/src/HiFiCodec.cpp @@ -65,12 +65,10 @@ public: AudioDecoder::process((const int16_t*)encodedBuffer.constData(), (int16_t*)decodedBuffer.data(), AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL, true); } - virtual void trackLostFrames(int numFrames) override { - QByteArray encodedBuffer; - QByteArray decodedBuffer; + virtual void lostFrame(QByteArray& decodedBuffer) override { decodedBuffer.resize(_decodedSize); - // NOTE: we don't actually use the results of this decode, we just do it to keep the state of the codec clean - AudioDecoder::process((const int16_t*)encodedBuffer.constData(), (int16_t*)decodedBuffer.data(), AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL, false); + // this performs packet loss interpolation + AudioDecoder::process(nullptr, (int16_t*)decodedBuffer.data(), AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL, false); } private: int _decodedSize; diff --git a/plugins/pcmCodec/src/PCMCodecManager.h b/plugins/pcmCodec/src/PCMCodecManager.h index d58a219fef..608e9a1556 100644 --- a/plugins/pcmCodec/src/PCMCodecManager.h +++ b/plugins/pcmCodec/src/PCMCodecManager.h @@ -38,11 +38,14 @@ public: virtual void encode(const QByteArray& decodedBuffer, QByteArray& encodedBuffer) override { encodedBuffer = decodedBuffer; } + virtual void decode(const QByteArray& encodedBuffer, QByteArray& decodedBuffer) override { decodedBuffer = encodedBuffer; } - virtual void trackLostFrames(int numFrames) override { } + virtual void lostFrame(QByteArray& decodedBuffer) override { + memset(decodedBuffer.data(), 0, decodedBuffer.size()); + } private: static const char* NAME; @@ -77,7 +80,9 @@ public: decodedBuffer = qUncompress(encodedBuffer); } - virtual void trackLostFrames(int numFrames) override { } + virtual void lostFrame(QByteArray& decodedBuffer) override { + memset(decodedBuffer.data(), 0, decodedBuffer.size()); + } private: static const char* NAME; diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index d2c9fdc05a..109d5171c4 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2882,7 +2882,6 @@ function MyController(hand) { this.touchingEnterTimer += dt; if (this.state == STATE_OVERLAY_STYLUS_TOUCHING && this.triggerSmoothedSqueezed()) { - this.setState(STATE_OFF, "trigger squeezed"); return; } diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index eab3d85adc..dc1d71f402 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global Script, HMD, WebTablet, UIWebTablet */ +/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, MyAvatar */ (function() { // BEGIN LOCAL_SCOPE var tabletShown = false; @@ -65,8 +65,10 @@ hideTabletUI(); HMD.closeTablet(); } else if (HMD.showTablet && !tabletShown) { + UserActivityLogger.openedTablet(); showTabletUI(); } else if (!HMD.showTablet && tabletShown) { + UserActivityLogger.closedTablet(); hideTabletUI(); } } @@ -86,7 +88,6 @@ var accumulatedLevel = 0.0; // Note: Might have to tweak the following two based on the rate we're getting the data var AVERAGING_RATIO = 0.05; - var MIC_LEVEL_UPDATE_INTERVAL_MS = 100; // Calculate microphone level with the same scaling equation (log scale, exponentially averaged) in AvatarInputs and pal.js function getMicLevel() { diff --git a/tests/shared/src/AABoxTests.cpp b/tests/shared/src/AABoxTests.cpp index b9ab95bb09..2e9dfab497 100644 --- a/tests/shared/src/AABoxTests.cpp +++ b/tests/shared/src/AABoxTests.cpp @@ -169,3 +169,17 @@ void AABoxTests::testScale() { box3 += glm::vec3(-1.0f, -1.0f, -1.0f); QCOMPARE(box3.contains(glm::vec3(0.5f, 0.5f, 0.5f)), true); } + +void AABoxTests::testFindSpherePenetration() { + vec3 searchPosition(-0.0141186f, 0.0640736f, -0.116081f); + float searchRadius = 0.5f; + + vec3 boxMin(-0.800014f, -0.450025f, -0.00503815f); + vec3 boxDim(1.60003f, 0.900049f, 0.0100763f); + AABox testBox(boxMin, boxDim); + + vec3 penetration; + bool hit = testBox.findSpherePenetration(searchPosition, searchRadius, penetration); + QCOMPARE(hit, true); +} + diff --git a/tests/shared/src/AABoxTests.h b/tests/shared/src/AABoxTests.h index c777f8e94f..605db7d3ca 100644 --- a/tests/shared/src/AABoxTests.h +++ b/tests/shared/src/AABoxTests.h @@ -24,6 +24,7 @@ private slots: void testContainsPoint(); void testTouchesSphere(); void testScale(); + void testFindSpherePenetration(); }; #endif // hifi_AABoxTests_h