From 878774b5d3b5d9f4ea70ef184a4d6119d6bd3c32 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 24 Mar 2024 13:31:16 -0700 Subject: [PATCH 1/9] sound entities --- assignment-client/src/audio/AudioMixer.cpp | 1 + assignment-client/src/avatars/AvatarMixer.cpp | 1 + .../src/entities/EntityServer.cpp | 8 + .../src/scripts/EntityScriptServer.cpp | 1 + libraries/audio/src/AudioInjector.cpp | 3 + libraries/entities/CMakeLists.txt | 2 +- .../entities/src/EntityItemProperties.cpp | 139 +++++++ libraries/entities/src/EntityItemProperties.h | 13 + libraries/entities/src/EntityPropertyFlags.h | 10 + .../entities/src/EntityScriptingInterface.cpp | 26 ++ .../entities/src/EntityScriptingInterface.h | 21 + libraries/entities/src/EntityTree.h | 4 + libraries/entities/src/EntityTreeElement.cpp | 8 +- libraries/entities/src/EntityTypes.cpp | 2 + libraries/entities/src/EntityTypes.h | 3 + libraries/entities/src/SoundEntityItem.cpp | 371 ++++++++++++++++++ libraries/entities/src/SoundEntityItem.h | 100 +++++ libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/physics/CMakeLists.txt | 1 + scripts/system/assets/images/tools/sound.svg | 258 ++++++++++++ .../create/assets/data/createAppTooltips.json | 24 ++ .../create/assets/images/icon-sound.svg | 84 ++++ scripts/system/create/edit.js | 19 +- .../system/create/entityList/entityList.js | 4 +- .../create/entityList/html/js/entityList.js | 1 + .../html/js/entityProperties.js | 57 +++ .../entityProperties/html/tabs/sound.png | Bin 0 -> 684 bytes .../entitySelectionTool.js | 2 +- .../system/create/modules/brokenURLReport.js | 11 + scripts/system/create/qml/EditTabView.qml | 12 + .../system/create/qml/EditToolsTabView.qml | 12 + scripts/system/create/qml/icons/sound.svg | 59 +++ scripts/system/html/js/includes.js | 1 + 33 files changed, 1249 insertions(+), 10 deletions(-) create mode 100644 libraries/entities/src/SoundEntityItem.cpp create mode 100644 libraries/entities/src/SoundEntityItem.h create mode 100644 scripts/system/assets/images/tools/sound.svg create mode 100644 scripts/system/create/assets/images/icon-sound.svg create mode 100644 scripts/system/create/entityProperties/html/tabs/sound.png create mode 100644 scripts/system/create/qml/icons/sound.svg diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 92719abef6..83fe2cc08f 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -816,6 +816,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { void AudioMixer::setupEntityQuery() { _entityViewer.init(); EntityTreePointer entityTree = _entityViewer.getTree(); + entityTree->setIsServer(true); DependencyManager::registerInheritance(); DependencyManager::set(entityTree); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 8d322c36f2..81a9b89b0a 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -1081,6 +1081,7 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { void AvatarMixer::setupEntityQuery() { _entityViewer.init(); EntityTreePointer entityTree = _entityViewer.getTree(); + entityTree->setIsServer(true); DependencyManager::registerInheritance(); DependencyManager::set(entityTree); diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index d27a69ff7c..9cbaaa0ea1 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -16,9 +16,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -49,6 +51,9 @@ EntityServer::EntityServer(ReceivedMessage& message) : DependencyManager::set(); // ModelFormatRegistry must be defined before ModelCache. See the ModelCache ctor DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityClone, @@ -71,6 +76,8 @@ EntityServer::~EntityServer() { void EntityServer::aboutToFinish() { DependencyManager::get()->cleanup(); + DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); OctreeServer::aboutToFinish(); @@ -90,6 +97,7 @@ OctreePointer EntityServer::createTree() { EntityTreePointer tree = std::make_shared(true); tree->createRootElement(); tree->addNewlyCreatedHook(this); + tree->setIsEntityServer(true); if (!_entitySimulation) { SimpleEntitySimulationPointer simpleSimulation { new SimpleEntitySimulation() }; simpleSimulation->setEntityTree(tree); diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index b16e4561d6..3f49ecd889 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -313,6 +313,7 @@ void EntityScriptServer::run() { entityScriptingInterface->setEntityTree(_entityViewer.getTree()); auto treePtr = _entityViewer.getTree(); + treePtr->setIsServer(true); DependencyManager::set(treePtr); if (!_entitySimulation) { diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 2df766377f..8c3a6b118e 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -132,9 +132,11 @@ void AudioInjector::restart() { bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(const AudioInjectorPointer&)) { AudioInjectorOptions options; + uint32_t numBytes; withWriteLock([&] { _state = AudioInjectorState::NotFinished; options = _options; + numBytes = _audioData->getNumBytes(); }); int byteOffset = 0; @@ -142,6 +144,7 @@ bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(const AudioInj int numChannels = options.ambisonic ? 4 : (options.stereo ? 2 : 1); byteOffset = (int)(AudioConstants::SAMPLE_RATE * options.secondOffset * numChannels); byteOffset *= AudioConstants::SAMPLE_SIZE; + byteOffset = byteOffset % numBytes; } _currentSendOffset = byteOffset; diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index e04d9f9fa8..830cecd1ed 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -12,7 +12,7 @@ include_hifi_library_headers(image) include_hifi_library_headers(ktx) include_hifi_library_headers(material-networking) include_hifi_library_headers(procedural) -link_hifi_libraries(shared shaders networking octree avatars graphics model-networking script-engine) +link_hifi_libraries(audio shared shaders networking octree avatars graphics model-networking script-engine) if (WIN32) add_compile_definitions(_USE_MATH_DEFINES) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5db64d12e4..a32dcf78c0 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -690,6 +690,16 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_GIZMO_TYPE, gizmoType); changedProperties += _ring.getChangedProperties(); + // Sound + CHECK_PROPERTY_CHANGE(PROP_SOUND_URL, soundURL); + CHECK_PROPERTY_CHANGE(PROP_SOUND_VOLUME, volume); + CHECK_PROPERTY_CHANGE(PROP_SOUND_TIME_OFFSET, timeOffset); + CHECK_PROPERTY_CHANGE(PROP_SOUND_PITCH, pitch); + CHECK_PROPERTY_CHANGE(PROP_SOUND_PLAYING, playing); + CHECK_PROPERTY_CHANGE(PROP_SOUND_LOOP, loop); + CHECK_PROPERTY_CHANGE(PROP_SOUND_POSITIONAL, positional); + CHECK_PROPERTY_CHANGE(PROP_SOUND_LOCAL_ONLY, localOnly); + return changedProperties; } @@ -883,6 +893,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} * @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} + * @see {@link Entities.EntityProperties-Sound|EntityProperties-Sound} * @see {@link Entities.EntityProperties-Sphere|EntityProperties-Sphere} * @see {@link Entities.EntityProperties-Text|EntityProperties-Text} * @see {@link Entities.EntityProperties-Web|EntityProperties-Web} @@ -1340,6 +1351,34 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * }); */ +/*@jsdoc + * The "Sound" {@link Entities.EntityType|EntityType} plays a sound from a URL. It has properties in addition to + * the common {@link Entities.EntityProperties|EntityProperties}. + * + * @typedef {object} Entities.EntityProperties-Sound + * @property {string} soundURL="" - The URL of the sound to play, as a wav, mp3, or raw file. Supports stereo and ambisonic. + * @property {boolean} playing=true - Whether or not the sound should play. + * @property {number} volume=1.0 - The volume of the sound, from 0 to 1. + * @property {number} pitch=1.0 - The relative sample rate at which to resample the sound, within +/- 2 octaves. + * @property {number} timeOffset=0.0 - The time (in seconds) at which to start playback within the sound file. If looping, + * this only affects the first loop. + * @property {boolean} loop=true - Whether or not to loop the sound. + * @property {boolean} positional=true - Whether or not the volume of the sound should decay with distance. + * @property {boolean} localOnly=false - Whether or not the sound should play locally for everyone (unsynced), or synchronously + * for everyone via the Entity Mixer. + * @example Create a Sound entity. + * var entity = Entities.addEntity({ + * type: "Sound", + * soundURL: "https://themushroomkingdom.net/sounds/wav/lm/lm_gold_mouse.wav", + * positional: true, + * volume: 0.75, + * localOnly: true, + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -4 })), + * rotation: MyAvatar.orientation, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + /*@jsdoc * The "Sphere" {@link Entities.EntityType|EntityType} is the same as the "Shape" * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Sphere" @@ -1961,6 +2000,18 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s _ring.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); } + // Sound only + if (_type == EntityTypes::Sound) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_URL, soundURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_VOLUME, volume); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_TIME_OFFSET, timeOffset); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_PITCH, pitch); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_PLAYING, playing); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_LOOP, loop); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_POSITIONAL, positional); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOUND_LOCAL_ONLY, localOnly); + } + /*@jsdoc * The axis-aligned bounding box of an entity. * @typedef {object} Entities.BoundingBox @@ -2292,6 +2343,16 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(gizmoType, GizmoType); _ring.copyFromScriptValue(object, namesSet, _defaultSettings); + // Sound + COPY_PROPERTY_FROM_QSCRIPTVALUE(soundURL, QString, setSoundURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE(volume, float, setVolume); + COPY_PROPERTY_FROM_QSCRIPTVALUE(timeOffset, float, setTimeOffset); + COPY_PROPERTY_FROM_QSCRIPTVALUE(pitch, float, setPitch); + COPY_PROPERTY_FROM_QSCRIPTVALUE(playing, bool, setPlaying); + COPY_PROPERTY_FROM_QSCRIPTVALUE(loop, bool, setLoop); + COPY_PROPERTY_FROM_QSCRIPTVALUE(positional, bool, setPositional); + COPY_PROPERTY_FROM_QSCRIPTVALUE(localOnly, bool, setLocalOnly); + // Handle conversions from old 'textures' property to "imageURL" if (namesSet.contains("textures")) { ScriptValue V = object.property("textures"); @@ -2578,6 +2639,16 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(gizmoType); _ring.merge(other._ring); + // Sound + COPY_PROPERTY_IF_CHANGED(soundURL); + COPY_PROPERTY_IF_CHANGED(volume); + COPY_PROPERTY_IF_CHANGED(timeOffset); + COPY_PROPERTY_IF_CHANGED(pitch); + COPY_PROPERTY_IF_CHANGED(playing); + COPY_PROPERTY_IF_CHANGED(loop); + COPY_PROPERTY_IF_CHANGED(positional); + COPY_PROPERTY_IF_CHANGED(localOnly); + _lastEdited = usecTimestampNow(); } @@ -3002,6 +3073,16 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_GROUP_PROPERTY_TO_MAP(PROP_MAJOR_TICK_MARKS_COLOR, Ring, ring, MajorTickMarksColor, majorTickMarksColor); ADD_GROUP_PROPERTY_TO_MAP(PROP_MINOR_TICK_MARKS_COLOR, Ring, ring, MinorTickMarksColor, minorTickMarksColor); } + + // Sound + ADD_PROPERTY_TO_MAP(PROP_SOUND_URL, SoundURL, soundURL, QString); + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SOUND_VOLUME, Volume, volume, float, 0.0f, 1.0f); + ADD_PROPERTY_TO_MAP(PROP_SOUND_TIME_OFFSET, TimeOffset, timeOffset, float); + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SOUND_PITCH, Pitch, pitch, float, 1.0f / 16.0f, 16.0f); + ADD_PROPERTY_TO_MAP(PROP_SOUND_PLAYING, Playing, playing, bool); + ADD_PROPERTY_TO_MAP(PROP_SOUND_LOOP, Loop, loop, bool); + ADD_PROPERTY_TO_MAP(PROP_SOUND_POSITIONAL, Positional, positional, bool); + ADD_PROPERTY_TO_MAP(PROP_SOUND_LOCAL_ONLY, LocalOnly, localOnly, bool); }); auto iter = _propertyInfos.find(propertyName); @@ -3449,6 +3530,17 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticRing.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); } + + if (properties.getType() == EntityTypes::Sound) { + APPEND_ENTITY_PROPERTY(PROP_SOUND_URL, properties.getSoundURL()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_VOLUME, properties.getVolume()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_TIME_OFFSET, properties.getTimeOffset()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_PITCH, properties.getPitch()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_PLAYING, properties.getPlaying()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_LOOP, properties.getLoop()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_POSITIONAL, properties.getPositional()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_LOCAL_ONLY, properties.getLocalOnly()); + } } if (propertyCount > 0) { @@ -3911,6 +4003,17 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int properties.getRing().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); } + if (properties.getType() == EntityTypes::Sound) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_URL, QString, setSoundURL); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_VOLUME, float, setVolume); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_TIME_OFFSET, float, setTimeOffset); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_PITCH, float, setPitch); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_PLAYING, bool, setPlaying); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_LOOP, bool, setLoop); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_POSITIONAL, bool, setPositional); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOUND_LOCAL_ONLY, bool, setLocalOnly); + } + return valid; } @@ -4240,6 +4343,16 @@ void EntityItemProperties::markAllChanged() { // Gizmo _gizmoTypeChanged = true; _ring.markAllChanged(); + + // Sound + _soundURLChanged = true; + _volumeChanged = true; + _timeOffsetChanged = true; + _pitchChanged = true; + _playingChanged = true; + _loopChanged = true; + _positionalChanged = true; + _localOnlyChanged = true; } // The minimum bounding box for the entity. @@ -4945,6 +5058,32 @@ QList EntityItemProperties::listChangedProperties() { } getRing().listChangedProperties(out); + // Sound + if (soundURLChanged()) { + out += "soundURL"; + } + if (volumeChanged()) { + out += "volume"; + } + if (timeOffsetChanged()) { + out += "timeOffset"; + } + if (pitchChanged()) { + out += "pitch"; + } + if (playingChanged()) { + out += "playing"; + } + if (loopChanged()) { + out += "loop"; + } + if (positionalChanged()) { + out += "positional"; + } + if (localOnlyChanged()) { + out += "localOnly"; + } + return out; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6340313f2d..961049b1c7 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -52,6 +52,7 @@ #include "PolyVoxEntityItem.h" #include "GridEntityItem.h" #include "GizmoEntityItem.h" +#include "SoundEntityItem.h" #include "LightEntityItem.h" #include "ZoneEntityItem.h" @@ -124,6 +125,8 @@ class EntityItemProperties { friend class LightEntityItem; friend class ZoneEntityItem; friend class MaterialEntityItem; + friend class SoundEntityItem; + public: static bool blobToProperties(ScriptEngine& scriptEngine, const QByteArray& blob, EntityItemProperties& properties); static void propertiesToBlob(ScriptEngine& scriptEngine, const QUuid& myAvatarID, const EntityItemProperties& properties, @@ -407,6 +410,16 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_GIZMO_TYPE, GizmoType, gizmoType, GizmoType, GizmoType::RING); DEFINE_PROPERTY_GROUP(Ring, ring, RingGizmoPropertyGroup); + // Sound + DEFINE_PROPERTY_REF(PROP_SOUND_URL, SoundURL, soundURL, QString, ""); + DEFINE_PROPERTY(PROP_SOUND_VOLUME, Volume, volume, float, 1.0f); + DEFINE_PROPERTY(PROP_SOUND_TIME_OFFSET, TimeOffset, timeOffset, float, 0.0f); + DEFINE_PROPERTY(PROP_SOUND_PITCH, Pitch, pitch, float, 1.0f); + DEFINE_PROPERTY(PROP_SOUND_PLAYING, Playing, playing, bool, true); + DEFINE_PROPERTY(PROP_SOUND_LOOP, Loop, loop, bool, true); + DEFINE_PROPERTY(PROP_SOUND_POSITIONAL, Positional, positional, bool, true); + DEFINE_PROPERTY(PROP_SOUND_LOCAL_ONLY, LocalOnly, localOnly, bool, false); + static QString getComponentModeAsString(uint32_t mode); public: diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index ca96c04fd8..591725c2ea 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -394,6 +394,16 @@ enum EntityPropertyList { PROP_MAJOR_TICK_MARKS_COLOR = PROP_DERIVED_17, PROP_MINOR_TICK_MARKS_COLOR = PROP_DERIVED_18, + // Sound + PROP_SOUND_URL = PROP_DERIVED_0, + PROP_SOUND_VOLUME = PROP_DERIVED_1, + PROP_SOUND_TIME_OFFSET = PROP_DERIVED_2, + PROP_SOUND_PITCH = PROP_DERIVED_3, + PROP_SOUND_PLAYING = PROP_DERIVED_4, + PROP_SOUND_POSITIONAL = PROP_DERIVED_5, + PROP_SOUND_LOOP = PROP_DERIVED_6, + PROP_SOUND_LOCAL_ONLY = PROP_DERIVED_7, + // WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above }; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 4acf9b8d1b..f0da5f3cf3 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1871,6 +1871,7 @@ bool EntityScriptingInterface::setAllPoints(const QUuid& entityID, const QVector EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); if (!entity) { qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID; + return false; } EntityTypes::EntityType entityType = entity->getType(); @@ -1907,6 +1908,31 @@ bool EntityScriptingInterface::appendPoint(const QUuid& entityID, const glm::vec return false; } +bool EntityScriptingInterface::restartSound(const QUuid& entityID) { + PROFILE_RANGE(script_entities, __FUNCTION__); + + EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); + if (!entity) { + qCDebug(entities) << "EntityScriptingInterface::restartSound no entity with ID" << entityID; + // There is no entity + return false; + } + + EntityTypes::EntityType entityType = entity->getType(); + + if (entityType == EntityTypes::Sound) { + auto soundEntity = std::dynamic_pointer_cast(entity); + bool isPlaying = soundEntity->getPlaying(); + if (isPlaying) { + soundEntity->withWriteLock([&] { + soundEntity->restartSound(); + }); + } + return isPlaying; + } + + return false; +} bool EntityScriptingInterface::actionWorker(const QUuid& entityID, std::function actor) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index c677bdf0a1..59204bdd96 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -1237,6 +1237,27 @@ public slots: */ Q_INVOKABLE bool appendPoint(const QUuid& entityID, const glm::vec3& point); + /*@jsdoc + * Restart a {@link Entities.EntityProperties-Sound|Sound} entity, locally only. It must also be localOnly. + * @function Entities.restartSound + * @param {Uuid} entityID - The ID of the {@link Entities.EntityProperties-Sound|Sound} entity. + * @example Play a sound once and repeat it every 3 seconds. + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })); + * var sound = Entities.addEntity({ + * type: "Sound", + * position: position, + * soundURL: "https://themushroomkingdom.net/sounds/wav/lm/lm_gold_mouse.wav", + * positional: false, + * localOnly: true, + * loop: false, + * lifetime: 300 // Delete after 5 minutes. + * }); + * Script.setInterval(() => { + * Entities.restartSound(sound); + * }, 3000); + */ + Q_INVOKABLE bool restartSound(const QUuid& entityID); + /*@jsdoc * Dumps debug information about all entities in Interface's local in-memory tree of entities it knows about to the program * log. diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 1161bec6e9..a1f97ff621 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -257,6 +257,9 @@ public: void setIsServerlessMode(bool value) { _serverlessDomain = value; } bool isServerlessMode() const { return _serverlessDomain; } + void setIsEntityServer(bool value) { _entityServer = value; } + bool isEntityServer() const { return _entityServer; } + static void setGetEntityObjectOperator(std::function getEntityObjectOperator) { _getEntityObjectOperator = getEntityObjectOperator; } static QObject* getEntityObject(const QUuid& id); @@ -380,6 +383,7 @@ private: std::vector _staleProxies; bool _serverlessDomain { false }; + bool _entityServer { false }; std::map _namedPaths; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 32c791da33..b6f65cff65 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -257,8 +257,8 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori } } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results - // Never intersect with particle entities - if (localDistance < distance && entity->getType() != EntityTypes::ParticleEffect) { + // Never intersect with particle or sound entities + if (localDistance < distance && (entity->getType() != EntityTypes::ParticleEffect && entity->getType() != EntityTypes::Sound)) { distance = localDistance; face = localFace; surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f)); @@ -409,8 +409,8 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3 } } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results - // Never intersect with particle entities - if (localDistance < parabolicDistance && entity->getType() != EntityTypes::ParticleEffect) { + // Never intersect with particle or sound entities + if (localDistance < parabolicDistance && (entity->getType() != EntityTypes::ParticleEffect && entity->getType() != EntityTypes::Sound)) { parabolicDistance = localDistance; face = localFace; surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 0.0f)); diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 95057bedbc..49109fd5dd 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -34,6 +34,7 @@ #include "LightEntityItem.h" #include "ZoneEntityItem.h" #include "MaterialEntityItem.h" +#include "SoundEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; @@ -59,6 +60,7 @@ REGISTER_ENTITY_TYPE(Gizmo) REGISTER_ENTITY_TYPE(Light) REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Material) +REGISTER_ENTITY_TYPE(Sound) bool EntityTypes::typeIsValid(EntityType type) { return type > EntityType::Unknown && type <= EntityType::NUM_TYPES; diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 441e77fccd..30d59727d4 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -86,6 +86,8 @@ public: * {@link Entities.EntityProperties-Zone|EntityProperties-Zone} * "Material"Modifies the existing materials on entities and avatars. * {@link Entities.EntityProperties-Material|EntityProperties-Material} + * "Sound"Plays a sound. + * {@link Entities.EntityProperties-Material|EntityProperties-Sound} * * * @typedef {string} Entities.EntityType @@ -108,6 +110,7 @@ public: Light, Zone, Material, + Sound, NUM_TYPES } EntityType; diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp new file mode 100644 index 0000000000..575efabb2e --- /dev/null +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -0,0 +1,371 @@ +// +// Created by HifiExperiments on 12/30/2023 +// Copyright 2023 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "SoundEntityItem.h" + +#include + +#include "EntitiesLogging.h" +#include "EntityItemProperties.h" +#include "EntityTree.h" +#include "EntityTreeElement.h" + +EntityItemPointer SoundEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + std::shared_ptr entity(new SoundEntityItem(entityID), [](SoundEntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +// our non-pure virtual subclass for now... +SoundEntityItem::SoundEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _type = EntityTypes::Sound; +} + +SoundEntityItem::~SoundEntityItem() { + auto manager = DependencyManager::get(); + if (manager && _injector) { + manager->stop(_injector); + } +} + +EntityItemProperties SoundEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(soundURL, getURL); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(volume, getVolume); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(timeOffset, getTimeOffset); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(pitch, getPitch); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(playing, getPlaying); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(loop, getLoop); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(positional, getPositional); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(localOnly, getLocalOnly); + + return properties; +} + +bool SoundEntityItem::setSubClassProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(soundURL, setURL); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(volume, setVolume); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(timeOffset, setTimeOffset); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(pitch, setPitch); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(playing, setPlaying); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(loop, setLoop); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(positional, setPositional); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(localOnly, setLocalOnly); + + return somethingChanged; +} + +int SoundEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_SOUND_URL, QString, setURL); + READ_ENTITY_PROPERTY(PROP_SOUND_VOLUME, float, setVolume); + READ_ENTITY_PROPERTY(PROP_SOUND_TIME_OFFSET, float, setTimeOffset); + READ_ENTITY_PROPERTY(PROP_SOUND_PITCH, float, setPitch); + READ_ENTITY_PROPERTY(PROP_SOUND_PLAYING, bool, setPlaying); + READ_ENTITY_PROPERTY(PROP_SOUND_LOOP, bool, setLoop); + READ_ENTITY_PROPERTY(PROP_SOUND_POSITIONAL, bool, setPositional); + READ_ENTITY_PROPERTY(PROP_SOUND_LOCAL_ONLY, bool, setLocalOnly); + + return bytesRead; +} + +EntityPropertyFlags SoundEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + + requestedProperties += PROP_SOUND_URL; + requestedProperties += PROP_SOUND_VOLUME; + requestedProperties += PROP_SOUND_TIME_OFFSET; + requestedProperties += PROP_SOUND_PITCH; + requestedProperties += PROP_SOUND_PLAYING; + requestedProperties += PROP_SOUND_LOOP; + requestedProperties += PROP_SOUND_POSITIONAL; + requestedProperties += PROP_SOUND_LOCAL_ONLY; + + return requestedProperties; +} + +void SoundEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_SOUND_URL, getURL()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_VOLUME, getVolume()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_TIME_OFFSET, getTimeOffset()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_PITCH, getPitch()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_PLAYING, getPlaying()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_LOOP, getLoop()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_POSITIONAL, getPositional()); + APPEND_ENTITY_PROPERTY(PROP_SOUND_LOCAL_ONLY, getLocalOnly()); +} + +void SoundEntityItem::debugDump() const { + quint64 now = usecTimestampNow(); + qCDebug(entities) << "SOUND EntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " url:" << _url; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << "SOUND EntityItem Ptr:" << this; +} + +void SoundEntityItem::update(const quint64& now) { + withWriteLock([&] { + const auto tree = getTree(); + if (tree) { + _updateNeeded = false; + + if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + _sound = DependencyManager::get()->getSound(_url); + } + + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + } + } + } + }); +} + +void SoundEntityItem::setLocalPosition(const glm::vec3& value, bool tellPhysics) { + EntityItem::setLocalPosition(value, tellPhysics); + withWriteLock([&] { + updateSound(); + }); +} + +void SoundEntityItem::setLocalOrientation(const glm::quat& value) { + EntityItem::setLocalOrientation(value); + withWriteLock([&] { + updateSound(); + }); +} + +void SoundEntityItem::setURL(const QString& value) { + withWriteLock([&] { + if (value != _url) { + _url = value; + + const auto tree = getTree(); + if (!tree) { + _updateNeeded = true; + return; + } + + if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + _sound = DependencyManager::get()->getSound(_url); + } + + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + } + } + } + }); +} + +QString SoundEntityItem::getURL() const { + return resultWithReadLock([&] { + return _url; + }); +} + +void SoundEntityItem::setVolume(float value) { + withWriteLock([&] { + if (value != _volume) { + _volume = value; + updateSound(); + } + }); +} + +float SoundEntityItem::getVolume() const { + return resultWithReadLock([&] { + return _volume; + }); +} + +void SoundEntityItem::setTimeOffset(float value) { + withWriteLock([&] { + if (value != _timeOffset) { + _timeOffset = value; + updateSound(true); + } + }); +} + +float SoundEntityItem::getTimeOffset() const { + return resultWithReadLock([&] { + return _timeOffset; + }); +} + +void SoundEntityItem::setPitch(float value) { + withWriteLock([&] { + if (value != _pitch) { + _pitch = value; + updateSound(true); + } + }); +} + +float SoundEntityItem::getPitch() const { + return resultWithReadLock([&] { + return _pitch; + }); +} + +void SoundEntityItem::setPlaying(bool value) { + withWriteLock([&] { + if (value != _playing) { + _playing = value; + updateSound(); + } + }); +} + +bool SoundEntityItem::getPlaying() const { + return resultWithReadLock([&] { + return _playing; + }); +} + +void SoundEntityItem::setLoop(bool value) { + withWriteLock([&] { + if (value != _loop) { + _loop = value; + updateSound(true); + } + }); +} + +bool SoundEntityItem::getLoop() const { + return resultWithReadLock([&] { + return _loop; + }); +} + +void SoundEntityItem::setPositional(bool value) { + withWriteLock([&] { + if (value != _positional) { + _positional = value; + updateSound(); + } + }); +} + +bool SoundEntityItem::getPositional() const { + return resultWithReadLock([&] { + return _positional; + }); +} + +void SoundEntityItem::setLocalOnly(bool value) { + withWriteLock([&] { + if (value != _localOnly) { + _localOnly = value; + + const auto tree = getTree(); + if (!tree) { + _updateNeeded = true; + return; + } + + if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + _sound = DependencyManager::get()->getSound(_url); + } else { + _sound = nullptr; + + if (_injector) { + DependencyManager::get()->stop(_injector); + } + _injector = nullptr; + } + + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + } + } + } + }); +} + +bool SoundEntityItem::getLocalOnly() const { + return resultWithReadLock([&] { + return _localOnly; + }); +} + +bool SoundEntityItem::restartSound() { + if (!_sound) { + return false; + } + + AudioInjectorOptions options; + options.position = getWorldPosition(); + options.positionSet = _positional; + options.volume = _volume; + options.loop = _loop; + options.orientation = getWorldOrientation(); + options.localOnly = _localOnly; + options.secondOffset = _timeOffset; + options.pitch = _pitch; + + if (_injector) { + DependencyManager::get()->setOptionsAndRestart(_injector, options); + } else { + _injector = DependencyManager::get()->playSound(_sound, options); + } + + return true; +} + +void SoundEntityItem::updateSound(bool restart) { + if (!_sound) { + return; + } + + if (restart) { + if (_injector) { + DependencyManager::get()->stop(_injector); + } + _injector = nullptr; + } + + if (_playing) { + restartSound(); + } else { + if (_injector) { + DependencyManager::get()->stop(_injector); + } + } +} \ No newline at end of file diff --git a/libraries/entities/src/SoundEntityItem.h b/libraries/entities/src/SoundEntityItem.h new file mode 100644 index 0000000000..781c270d45 --- /dev/null +++ b/libraries/entities/src/SoundEntityItem.h @@ -0,0 +1,100 @@ +// +// Created by HifiExperiments on 12/30/2023 +// Copyright 2023 Overte e.V. +// +// 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_SoundEntityItem_h +#define hifi_SoundEntityItem_h + +#include "EntityItem.h" + +#include +#include + +class SoundEntityItem : public EntityItem { +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + SoundEntityItem(const EntityItemID& entityItemID); + ~SoundEntityItem(); + + ALLOW_INSTANTIATION // This class can be instantiated + + // methods for getting/setting all properties of an entity + EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; + bool setSubClassProperties(const EntityItemProperties& properties) override; + + EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + bool shouldBePhysical() const override { return false; } + + virtual void debugDump() const override; + + virtual bool supportsDetailedIntersection() const override { return false; } + + virtual void update(const quint64& now) override; + bool needsToCallUpdate() const override { return _updateNeeded; } + + void setLocalPosition(const glm::vec3& value, bool tellPhysics = true) override; + void setLocalOrientation(const glm::quat& value) override; + + void setURL(const QString& value); + QString getURL() const; + + void setVolume(float value); + float getVolume() const; + + void setTimeOffset(float value); + float getTimeOffset() const; + + void setPitch(float value); + float getPitch() const; + + void setPlaying(bool value); + bool getPlaying() const; + + void setLoop(bool value); + bool getLoop() const; + + void setPositional(bool value); + bool getPositional() const; + + void setLocalOnly(bool value); + bool getLocalOnly() const; + + bool restartSound(); + +protected: + void updateSound(bool restart = false); + + QString _url { "" }; + float _volume { 1.0f }; + float _timeOffset { 0.0f }; + float _pitch { 1.0f }; + bool _playing { true }; + bool _loop { true }; + bool _positional { true }; + bool _localOnly { false }; + + SharedSoundPointer _sound; + AudioInjectorPointer _injector; + bool _updateNeeded { false }; +}; + +#endif // hifi_SoundEntityItem_h diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 6f84f25e86..46e2fe61c7 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -294,6 +294,7 @@ enum class EntityVersion : PacketVersion { EntityTags, WantsKeyboardFocus, AudioZones, + SoundEntities, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt index 3237e712f9..ad6d5292b9 100644 --- a/libraries/physics/CMakeLists.txt +++ b/libraries/physics/CMakeLists.txt @@ -5,6 +5,7 @@ set(TARGET_NAME physics) setup_hifi_library() link_hifi_libraries(shared workload entities shaders) +include_hifi_library_headers(audio) include_hifi_library_headers(networking) include_hifi_library_headers(gpu) include_hifi_library_headers(avatars) diff --git a/scripts/system/assets/images/tools/sound.svg b/scripts/system/assets/images/tools/sound.svg new file mode 100644 index 0000000000..a71b449045 --- /dev/null +++ b/scripts/system/assets/images/tools/sound.svg @@ -0,0 +1,258 @@ + + + +SOUNDSOUNDSOUND + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index b4597e9141..efb33fd422 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -706,5 +706,29 @@ }, "zTextureURL": { "tooltip": "The URL of the texture to map to surfaces perpendicular to the entity's local z-axis. JPG or PNG format." + }, + "soundURL": { + "tooltip": "The URL of the sound, as a wav, mp3, or raw file." + }, + "playing": { + "tooltip": "Whether or not the sound should play." + }, + "volume": { + "tooltip": "The volume of the sound." + }, + "pitch": { + "tooltip": "Alter the pitch of the sound, within +/- 2 octaves. The value is the relative sample rate at which to resample the sound." + }, + "timeOffset": { + "tooltip": "Starts playback from a specified time (seconds) within the sound file." + }, + "loop": { + "tooltip": "Whether or not the sound is played repeatedly." + }, + "positional": { + "tooltip": "Whether or not the sound volume drops off with distance." + }, + "localOnly": { + "tooltip": "Whether the sound should play locally for everyone separately, or globally via the audio mixer." } } diff --git a/scripts/system/create/assets/images/icon-sound.svg b/scripts/system/create/assets/images/icon-sound.svg new file mode 100644 index 0000000000..cf89956c41 --- /dev/null +++ b/scripts/system/create/assets/images/icon-sound.svg @@ -0,0 +1,84 @@ + +image/svg+xml diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index dabdfe34f9..ade2133803 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -51,6 +51,7 @@ var DEFAULT_IMAGE = Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_image.jpg"); var DEFAULT_PARTICLE = Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_particle.png"); + var DEFAULT_SOUND = "TODO"; var createToolsWindow = new CreateWindow( Script.resolvePath("qml/EditTools.qml"), @@ -94,8 +95,9 @@ var SPOT_LIGHT_URL = Script.resolvePath("assets/images/icon-spot-light.svg"); var ZONE_URL = Script.resolvePath("assets/images/icon-zone.svg"); var MATERIAL_URL = Script.resolvePath("assets/images/icon-material.svg"); + var SOUND_URL = Script.resolvePath("assets/images/icon-sound.svg"); - var entityIconOverlayManager = new EntityIconOverlayManager(["Light", "ParticleEffect", "Zone", "Material"], function(entityID) { + var entityIconOverlayManager = new EntityIconOverlayManager(["Light", "ParticleEffect", "Zone", "Material", "Sound"], function(entityID) { var properties = Entities.getEntityProperties(entityID, ["type", "isSpotlight", "parentID", "name"]); if (properties.type === "Light") { return { @@ -109,6 +111,8 @@ } else { return { imageURL: "" }; } + } else if (properties.type === "Sound") { + return { imageURL: SOUND_URL, rotation: Quat.fromPitchYawRollDegrees(0, 0, 0) }; } else { return { imageURL: PARTICLE_SYSTEM_URL }; } @@ -492,6 +496,9 @@ exponent: 1.0, cutoff: 75.0, }, + Sound: { + soundURL: DEFAULT_SOUND + }, }; var toolBar = (function () { @@ -570,7 +577,7 @@ if (!properties.grab) { properties.grab = {}; if (Menu.isOptionChecked(MENU_CREATE_ENTITIES_GRABBABLE) && - !(properties.type === "Zone" || properties.type === "Light" + !(properties.type === "Zone" || properties.type === "Light" || properties.type === "Sound" || properties.type === "ParticleEffect" || properties.type === "Web")) { properties.grab.grabbable = true; } else { @@ -1056,6 +1063,12 @@ addButton("newPolyVoxButton", createNewEntityDialogButtonCallback("PolyVox")); + addButton("newSoundButton", function () { + createNewEntity({ + type: "Sound", + }); + }); + var deactivateCreateIfDesktopWindowsHidden = function() { if (!shouldUseEditTabletApp() && !entityListTool.isVisible() && !createToolsWindow.isVisible()) { that.setActive(false); @@ -2041,7 +2054,7 @@ var entityParentIDs = []; var propType = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]).type; - var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"]; + var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "Sound", "ParticleEffect"]; if (NO_ADJUST_ENTITY_TYPES.indexOf(propType) === -1) { var targetDirection; if (Camera.mode === "entity" || Camera.mode === "independent") { diff --git a/scripts/system/create/entityList/entityList.js b/scripts/system/create/entityList/entityList.js index 257f967852..17dd942e13 100644 --- a/scripts/system/create/entityList/entityList.js +++ b/scripts/system/create/entityList/entityList.js @@ -211,7 +211,7 @@ var EntityListTool = function(shouldUseEditTabletApp, selectionManager) { PROFILE("getMultipleProperties", function () { var multipleProperties = Entities.getMultipleEntityProperties(ids, ['position', 'name', 'type', 'locked', 'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'serverScripts', - 'skybox.url', 'ambientLight.url', 'created', 'lastEdited']); + 'skybox.url', 'ambientLight.url', 'soundURL', 'created', 'lastEdited']); for (var i = 0; i < multipleProperties.length; i++) { var properties = multipleProperties[i]; @@ -223,6 +223,8 @@ var EntityListTool = function(shouldUseEditTabletApp, selectionManager) { url = properties.materialURL; } else if (properties.type === "Image") { url = properties.imageURL; + } else if (properties.type === "Sound") { + url = properties.soundURL; } //print("Global object before getParentState call: " + JSON.stringify(globalThis)); var parentStatus = that.createApp.getParentState(ids[i]); diff --git a/scripts/system/create/entityList/html/js/entityList.js b/scripts/system/create/entityList/html/js/entityList.js index a64d95a64c..213e4ad09c 100644 --- a/scripts/system/create/entityList/html/js/entityList.js +++ b/scripts/system/create/entityList/html/js/entityList.js @@ -178,6 +178,7 @@ const FILTER_TYPES = [ "PolyVox", "Text", "Grid", + "Sound", ]; const DOUBLE_CLICK_TIMEOUT = 300; // ms diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 8a1540d411..797156081e 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -1432,6 +1432,62 @@ const GROUPS = [ }, ] }, + { + id: "sound", + label: "SOUND", + properties: [ + { + label: "Sound", + type: "string", + propertyID: "soundURL", + placeholder: "URL", + }, + { + label: "Playing", + type: "bool", + propertyID: "playing", + }, + { + label: "Loop", + type: "bool", + propertyID: "loop", + }, + { + label: "Volume", + type: "number-draggable", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "volume", + }, + { + label: "Positional", + type: "bool", + propertyID: "positional", + }, + { + label: "Pitch", + type: "number-draggable", + min: 0.0625, + max: 16, + step: 0.1, + decimals: 2, + propertyID: "pitch", + }, + { + label: "Time Offset", + type: "number-draggable", + step: 0.1, + propertyID: "timeOffset", + }, + { + label: "Local Only", + type: "bool", + propertyID: "localOnly", + } + ] + }, { id: "spatial", label: "SPATIAL", @@ -1811,6 +1867,7 @@ const GROUPS_PER_TYPE = { PolyLine: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], PolyVox: [ 'base', 'polyvox', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Grid: [ 'base', 'grid', 'spatial', 'behavior', 'scripts', 'physics' ], + Sound: [ 'base', 'sound', 'spatial', 'behavior', 'scripts', 'physics' ], Multiple: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], }; diff --git a/scripts/system/create/entityProperties/html/tabs/sound.png b/scripts/system/create/entityProperties/html/tabs/sound.png new file mode 100644 index 0000000000000000000000000000000000000000..218bfe7c4ffa41221ac8182017661d4136236684 GIT binary patch literal 684 zcmWlWTS!v@7=ZtCnLC@?JTmi!>G776wwkp<4Yy;?r9qx?0Lf1Qf~tG3&*=crs7!5Ye*L=f@aRZ`DG(YG5|)AiCqF8HplroJ(pC9)(^OjR#`}u65*vjjkLr`n?MirCR1TM4efkg8DkK^FV z!X_ZC;aZfgB)AIt8*on;S1naa+&HqV9MB~gH^O*Kvg1PWRV5HqdC)HEQcxaCU`(Im z;2h(T%QSwA&-mN@m`g)>d^w%64nc`EehRaCh-{vw5B;riSdH3H9&L%HMT>(u^amjq z#JUBlje^+#1Sj9{hM0yB@+3MIO8$)G36dvpZoIib5?Z2h7^yNW4=3q`z{9o`>QOc8 zJ|?k*I8+NSR_%?2!(Y;5RgydrNoNIAOm#6fzP^dp%gcbIvkFRd`n`GnV3WOLx-9`v zVhug-T-aiDVQMgpVrEj_AxzzqD$@aJt(?8@yercC&o@sE zpUvu9YM87&OMYrQusC`iMvR1u0JSoODnDDX23+y)209-HrESMWbz(Rf>6^&b`xY%H z__r4^xyv}$>s_PGs!`w^-!WD1!wqS7n@ubjo#sJ5l2(nLK~U>XAmG7p;d|iMo$19S P0D#Q&9PR6iWi9^!#`NqK literal 0 HcmV?d00001 diff --git a/scripts/system/create/entitySelectionTool/entitySelectionTool.js b/scripts/system/create/entitySelectionTool/entitySelectionTool.js index 7a188f3dc0..5238b2d374 100644 --- a/scripts/system/create/entitySelectionTool/entitySelectionTool.js +++ b/scripts/system/create/entitySelectionTool/entitySelectionTool.js @@ -2241,7 +2241,7 @@ SelectionDisplay = (function() { Entities.editEntity(selectionBox, selectionBoxGeometry); // UPDATE ICON TRANSLATE HANDLE - if (SelectionManager.entityType === "ParticleEffect" || SelectionManager.entityType === "Light") { + if (SelectionManager.entityType === "ParticleEffect" || SelectionManager.entityType === "Light" || SelectionManager.entityType === "Sound") { var iconSelectionBoxGeometry = { position: position, rotation: rotation diff --git a/scripts/system/create/modules/brokenURLReport.js b/scripts/system/create/modules/brokenURLReport.js index c04612bb27..4224826023 100644 --- a/scripts/system/create/modules/brokenURLReport.js +++ b/scripts/system/create/modules/brokenURLReport.js @@ -352,6 +352,17 @@ function brokenURLReport(entityIDs) { }; brokenURLReportUrlList.push(brokenURLReportUrlEntry); } + if (properties.type === "Sound" && properties.soundURL.toLowerCase().startsWith("http")) { + brokenURLReportUrlEntry = { + id: entityIDs[i], + name: properties.name, + type: properties.type, + urlType: "soundURL", + url: soundURL, + validity: "NOT_TESTED" + }; + brokenURLReportUrlList.push(brokenURLReportUrlEntry); + } } if (brokenURLReportUrlList.length === 0) { audioFeedback.confirmation(); diff --git a/scripts/system/create/qml/EditTabView.qml b/scripts/system/create/qml/EditTabView.qml index 96e66c109e..26cc90a357 100644 --- a/scripts/system/create/qml/EditTabView.qml +++ b/scripts/system/create/qml/EditTabView.qml @@ -190,6 +190,18 @@ TabBar { editTabView.currentIndex = 2 } } + + NewEntityButton { + icon: "icons/sound.svg" + text: "SOUND" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", + params: { buttonName: "newSoundButton" } + }); + editTabView.currentIndex = 2 + } + } } HifiControls.Button { diff --git a/scripts/system/create/qml/EditToolsTabView.qml b/scripts/system/create/qml/EditToolsTabView.qml index 998c3a3aac..445f084392 100644 --- a/scripts/system/create/qml/EditToolsTabView.qml +++ b/scripts/system/create/qml/EditToolsTabView.qml @@ -196,6 +196,18 @@ TabBar { editTabView.currentIndex = tabIndex.properties } } + + NewEntityButton { + icon: "icons/sound.svg" + text: "SOUND" + onClicked: { + editRoot.sendToScript({ + method: "newEntityButtonClicked", + params: { buttonName: "newSoundButton" } + }); + editTabView.currentIndex = tabIndex.properties + } + } } HifiControls.Button { diff --git a/scripts/system/create/qml/icons/sound.svg b/scripts/system/create/qml/icons/sound.svg new file mode 100644 index 0000000000..448156343d --- /dev/null +++ b/scripts/system/create/qml/icons/sound.svg @@ -0,0 +1,59 @@ + + + + + + + diff --git a/scripts/system/html/js/includes.js b/scripts/system/html/js/includes.js index c604115f91..69ae9bed2d 100644 --- a/scripts/system/html/js/includes.js +++ b/scripts/system/html/js/includes.js @@ -20,6 +20,7 @@ const ENTITY_TYPE_ICON = { PolyLine: "", Shape: "n", Sphere: "n", + Sound: "G", Text: "l", Web: "q", Zone: "o", From 9914d4d133807c832baaab9130f83d0bcdb10df4 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 30 Mar 2024 00:05:11 -0700 Subject: [PATCH 2/9] fix stereo sound speed --- libraries/entities/src/EntityItemProperties.cpp | 1 + libraries/entities/src/SoundEntityItem.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index a32dcf78c0..7d5c01fd25 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1357,6 +1357,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @typedef {object} Entities.EntityProperties-Sound * @property {string} soundURL="" - The URL of the sound to play, as a wav, mp3, or raw file. Supports stereo and ambisonic. + * Note: ambisonic sounds can only play as localOnly. * @property {boolean} playing=true - Whether or not the sound should play. * @property {number} volume=1.0 - The volume of the sound, from 0 to 1. * @property {number} pitch=1.0 - The relative sample rate at which to resample the sound, within +/- 2 octaves. diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp index 575efabb2e..997225515c 100644 --- a/libraries/entities/src/SoundEntityItem.cpp +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -336,10 +336,14 @@ bool SoundEntityItem::restartSound() { options.volume = _volume; options.loop = _loop; options.orientation = getWorldOrientation(); - options.localOnly = _localOnly; + options.localOnly = _localOnly || _sound->isAmbisonic(); // force localOnly when ambisonic options.secondOffset = _timeOffset; options.pitch = _pitch; + // stereo option isn't set from script, this comes from sound metadata or filename + options.stereo = _sound->isStereo(); + options.ambisonic = _sound->isAmbisonic(); + if (_injector) { DependencyManager::get()->setOptionsAndRestart(_injector, options); } else { From b7a3fb107295872fdd7b605446a705ecfbc4a3cd Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 7 Apr 2024 12:54:37 -0700 Subject: [PATCH 3/9] support non-localOnly sound avatar entities --- libraries/entities/src/SoundEntityItem.cpp | 12 +++++++++--- libraries/entities/src/SoundEntityItem.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp index 997225515c..57ce158ee0 100644 --- a/libraries/entities/src/SoundEntityItem.cpp +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -129,13 +129,19 @@ void SoundEntityItem::debugDump() const { qCDebug(entities) << "SOUND EntityItem Ptr:" << this; } +bool SoundEntityItem::shouldCreateSound(const EntityTreePointer& tree) const { + bool clientShouldMakeSound = _localOnly || isMyAvatarEntity() || tree->isServerlessMode(); + bool serverShouldMakeSound = !_localOnly; + return (clientShouldMakeSound && tree->getIsClient()) || (serverShouldMakeSound && tree->isEntityServer()); +} + void SoundEntityItem::update(const quint64& now) { withWriteLock([&] { const auto tree = getTree(); if (tree) { _updateNeeded = false; - if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } @@ -175,7 +181,7 @@ void SoundEntityItem::setURL(const QString& value) { return; } - if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } @@ -297,7 +303,7 @@ void SoundEntityItem::setLocalOnly(bool value) { return; } - if ((_localOnly && tree->getIsClient()) || (!_localOnly && (tree->isEntityServer() || tree->isServerlessMode()))) { + if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } else { _sound = nullptr; diff --git a/libraries/entities/src/SoundEntityItem.h b/libraries/entities/src/SoundEntityItem.h index 781c270d45..5cbd4bb814 100644 --- a/libraries/entities/src/SoundEntityItem.h +++ b/libraries/entities/src/SoundEntityItem.h @@ -81,6 +81,7 @@ public: bool restartSound(); protected: + bool shouldCreateSound(const EntityTreePointer& tree) const; void updateSound(bool restart = false); QString _url { "" }; From eb7d97064fc1937586cb9355d6c00b5864b12413 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 7 Apr 2024 13:40:42 -0700 Subject: [PATCH 4/9] add sound url prompt --- scripts/system/create/edit.js | 32 +++- scripts/system/create/qml/NewSoundDialog.qml | 159 +++++++++++++++++++ scripts/system/create/qml/NewSoundWindow.qml | 31 ++++ 3 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 scripts/system/create/qml/NewSoundDialog.qml create mode 100644 scripts/system/create/qml/NewSoundWindow.qml diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index ade2133803..27502af711 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -51,7 +51,6 @@ var DEFAULT_IMAGE = Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_image.jpg"); var DEFAULT_PARTICLE = Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_particle.png"); - var DEFAULT_SOUND = "TODO"; var createToolsWindow = new CreateWindow( Script.resolvePath("qml/EditTools.qml"), @@ -497,7 +496,11 @@ cutoff: 75.0, }, Sound: { - soundURL: DEFAULT_SOUND + volume: 1.0, + playing: true, + loop: true, + positional: true, + localOnly: false }, }; @@ -866,6 +869,18 @@ } } + function handleNewSoundDialogResult(result) { + if (result) { + var soundURL = result.textInput; + if (soundURL) { + createNewEntity({ + type: "Sound", + soundURL: soundURL + }); + } + } + } + function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml. var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet.popFromStack(); @@ -894,6 +909,13 @@ case "newPolyVoxDialogCancel": closeExistingDialogWindow(); break; + case "newSoundDialogAdd": + handleNewSoundDialogResult(message.params); + closeExistingDialogWindow(); + break; + case "newSoundDialogCancel": + closeExistingDialogWindow(); + break; } } @@ -1063,11 +1085,7 @@ addButton("newPolyVoxButton", createNewEntityDialogButtonCallback("PolyVox")); - addButton("newSoundButton", function () { - createNewEntity({ - type: "Sound", - }); - }); + addButton("newSoundButton", createNewEntityDialogButtonCallback("Sound")); var deactivateCreateIfDesktopWindowsHidden = function() { if (!shouldUseEditTabletApp() && !entityListTool.isVisible() && !createToolsWindow.isVisible()) { diff --git a/scripts/system/create/qml/NewSoundDialog.qml b/scripts/system/create/qml/NewSoundDialog.qml new file mode 100644 index 0000000000..d0b470109b --- /dev/null +++ b/scripts/system/create/qml/NewSoundDialog.qml @@ -0,0 +1,159 @@ +// +// NewSoundDialog.qml +// qml/hifi +// +// Created by HifiExperiments on 4/7/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Dialogs 1.2 as OriginalDialogs + +import stylesUit 1.0 +import controlsUit 1.0 +import hifi.dialogs 1.0 + +Rectangle { + id: newSoundDialog + // width: parent.width + // height: parent.height + HifiConstants { id: hifi } + color: hifi.colors.baseGray; + signal sendToScript(var message); + property bool keyboardEnabled: false + property bool punctuationMode: false + property bool keyboardRasied: false + + function errorMessageBox(message) { + try { + return desktop.messageBox({ + icon: hifi.icons.warning, + defaultButton: OriginalDialogs.StandardButton.Ok, + title: "Error", + text: message + }); + } catch(e) { + Window.alert(message); + } + } + + Item { + id: column1 + anchors.rightMargin: 10 + anchors.leftMargin: 10 + anchors.bottomMargin: 10 + anchors.topMargin: 10 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: keyboard.top + + Text { + id: text1 + text: qsTr("Sound URL") + color: "#ffffff" + font.pixelSize: 12 + } + + TextInput { + id: soundURL + height: 20 + text: qsTr("") + color: "white" + anchors.top: text1.bottom + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + font.pixelSize: 12 + + onAccepted: { + newSoundDialog.keyboardEnabled = false; + } + + MouseArea { + anchors.fill: parent + onClicked: { + newSoundDialog.keyboardEnabled = HMD.active + parent.focus = true; + parent.forceActiveFocus(); + soundURL.cursorPosition = soundURL.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters); + } + } + } + + Rectangle { + id: textInputBox + color: "white" + anchors.fill: soundURL + opacity: 0.1 + } + + Row { + id: row1 + height: 400 + spacing: 30 + anchors.top: soundURL.bottom + anchors.topMargin: 5 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + + Column { + id: column3 + height: 400 + spacing: 10 + + Row { + id: row3 + width: 200 + height: 400 + spacing: 5 + + anchors.horizontalCenter: column3.horizontalCenter + anchors.horizontalCenterOffset: 0 + + Button { + id: button1 + text: qsTr("Create") + z: -1 + onClicked: { + newSoundDialog.sendToScript({ + method: "newSoundDialogAdd", + params: { + textInput: soundURL.text + } + }); + } + } + + Button { + id: button2 + z: -1 + text: qsTr("Cancel") + onClicked: { + newSoundDialog.sendToScript({method: "newSoundDialogCancel"}) + } + } + } + } + } + } + + Keyboard { + id: keyboard + raised: parent.keyboardEnabled + numeric: parent.punctuationMode + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + } +} diff --git a/scripts/system/create/qml/NewSoundWindow.qml b/scripts/system/create/qml/NewSoundWindow.qml new file mode 100644 index 0000000000..9f78ade0b1 --- /dev/null +++ b/scripts/system/create/qml/NewSoundWindow.qml @@ -0,0 +1,31 @@ +// +// NewSoundWindow.qml +// qml/hifi +// +// Created by HifiExperiments on 4/7/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +StackView { + id: stackView + anchors.fill: parent + anchors.leftMargin: 10 + anchors.rightMargin: 10 + anchors.topMargin: 40 + + signal sendToScript(var message); + + NewSoundDialog { + id: dialog + anchors.fill: parent + Component.onCompleted:{ + dialog.sendToScript.connect(stackView.sendToScript); + } + } +} From 376be7b17eefdc29920ba7f4a85c3e069e68a691 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 12 Apr 2024 13:04:30 -0700 Subject: [PATCH 5/9] support registration point, improve locking --- libraries/entities/src/SoundEntityItem.cpp | 37 +++++++++++++--------- libraries/entities/src/SoundEntityItem.h | 4 +-- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp index 57ce158ee0..201592dc1f 100644 --- a/libraries/entities/src/SoundEntityItem.cpp +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -136,36 +136,42 @@ bool SoundEntityItem::shouldCreateSound(const EntityTreePointer& tree) const { } void SoundEntityItem::update(const quint64& now) { - withWriteLock([&] { - const auto tree = getTree(); - if (tree) { - _updateNeeded = false; + const auto tree = getTree(); + if (tree) { + _updateNeeded = false; + withWriteLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } + }); + withReadLock([&] { if (_sound) { if (_sound->isLoaded()) { updateSound(true); } else { - connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + connect(_sound.data(), &Resource::finished, this, [&] { + withReadLock([&] { + updateSound(true); + }); + }); } } - } - }); + }); + } } -void SoundEntityItem::setLocalPosition(const glm::vec3& value, bool tellPhysics) { - EntityItem::setLocalPosition(value, tellPhysics); - withWriteLock([&] { +void SoundEntityItem::locationChanged(bool tellPhysics, bool tellChildren) { + EntityItem::locationChanged(tellPhysics, tellChildren); + withReadLock([&] { updateSound(); }); } -void SoundEntityItem::setLocalOrientation(const glm::quat& value) { - EntityItem::setLocalOrientation(value); - withWriteLock([&] { +void SoundEntityItem::dimensionsChanged() { + EntityItem::dimensionsChanged(); + withReadLock([&] { updateSound(); }); } @@ -337,11 +343,12 @@ bool SoundEntityItem::restartSound() { } AudioInjectorOptions options; - options.position = getWorldPosition(); + const glm::quat orientation = getWorldOrientation(); + options.position = getWorldPosition() + orientation * (getScaledDimensions() * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); options.positionSet = _positional; options.volume = _volume; options.loop = _loop; - options.orientation = getWorldOrientation(); + options.orientation = orientation; options.localOnly = _localOnly || _sound->isAmbisonic(); // force localOnly when ambisonic options.secondOffset = _timeOffset; options.pitch = _pitch; diff --git a/libraries/entities/src/SoundEntityItem.h b/libraries/entities/src/SoundEntityItem.h index 5cbd4bb814..3d1815f557 100644 --- a/libraries/entities/src/SoundEntityItem.h +++ b/libraries/entities/src/SoundEntityItem.h @@ -51,8 +51,8 @@ public: virtual void update(const quint64& now) override; bool needsToCallUpdate() const override { return _updateNeeded; } - void setLocalPosition(const glm::vec3& value, bool tellPhysics = true) override; - void setLocalOrientation(const glm::quat& value) override; + void locationChanged(bool tellPhysics = true, bool tellChildren = true) override; + void dimensionsChanged() override; void setURL(const QString& value); QString getURL() const; From 6a180b14a1afd0acf31896d1a9de77c991e21f6a Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 12 Apr 2024 13:06:54 -0700 Subject: [PATCH 6/9] remove keyboardRasied --- scripts/system/create/qml/NewSoundDialog.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/create/qml/NewSoundDialog.qml b/scripts/system/create/qml/NewSoundDialog.qml index d0b470109b..870e8bb41d 100644 --- a/scripts/system/create/qml/NewSoundDialog.qml +++ b/scripts/system/create/qml/NewSoundDialog.qml @@ -26,7 +26,6 @@ Rectangle { signal sendToScript(var message); property bool keyboardEnabled: false property bool punctuationMode: false - property bool keyboardRasied: false function errorMessageBox(message) { try { From 98c11cef8283f6393b26ec620937fbc16c3724ae Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 14 Apr 2024 22:04:09 -0700 Subject: [PATCH 7/9] locking attempt #2 --- .../entities/src/EntityScriptingInterface.cpp | 4 +- libraries/entities/src/SoundEntityItem.cpp | 203 +++++++++++------- libraries/entities/src/SoundEntityItem.h | 3 +- 3 files changed, 133 insertions(+), 77 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f0da5f3cf3..10c25f545b 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1924,9 +1924,7 @@ bool EntityScriptingInterface::restartSound(const QUuid& entityID) { auto soundEntity = std::dynamic_pointer_cast(entity); bool isPlaying = soundEntity->getPlaying(); if (isPlaying) { - soundEntity->withWriteLock([&] { - soundEntity->restartSound(); - }); + soundEntity->restartSound(true); } return isPlaying; } diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp index 201592dc1f..71845920f9 100644 --- a/libraries/entities/src/SoundEntityItem.cpp +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -138,68 +138,68 @@ bool SoundEntityItem::shouldCreateSound(const EntityTreePointer& tree) const { void SoundEntityItem::update(const quint64& now) { const auto tree = getTree(); if (tree) { + std::lock_guard lock(_soundLock); + _updateNeeded = false; - withWriteLock([&] { + withReadLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } }); - withReadLock([&] { - if (_sound) { - if (_sound->isLoaded()) { - updateSound(true); - } else { - connect(_sound.data(), &Resource::finished, this, [&] { - withReadLock([&] { - updateSound(true); - }); - }); - } + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); } - }); + } } } void SoundEntityItem::locationChanged(bool tellPhysics, bool tellChildren) { EntityItem::locationChanged(tellPhysics, tellChildren); - withReadLock([&] { - updateSound(); - }); + updateSound(); } void SoundEntityItem::dimensionsChanged() { EntityItem::dimensionsChanged(); - withReadLock([&] { - updateSound(); - }); + updateSound(); } void SoundEntityItem::setURL(const QString& value) { + bool changed = false; withWriteLock([&] { if (value != _url) { _url = value; + changed = true; + } + }); - const auto tree = getTree(); - if (!tree) { - _updateNeeded = true; - return; - } + if (changed) { + const auto tree = getTree(); + if (!tree) { + _updateNeeded = true; + return; + } + std::lock_guard lock(_soundLock); + + withReadLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_url); } + }); - if (_sound) { - if (_sound->isLoaded()) { - updateSound(true); - } else { - connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); - } + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); } } - }); + } } QString SoundEntityItem::getURL() const { @@ -209,12 +209,17 @@ QString SoundEntityItem::getURL() const { } void SoundEntityItem::setVolume(float value) { + bool changed = false; withWriteLock([&] { if (value != _volume) { _volume = value; - updateSound(); + changed = true; } }); + + if (changed) { + updateSound(); + } } float SoundEntityItem::getVolume() const { @@ -224,12 +229,17 @@ float SoundEntityItem::getVolume() const { } void SoundEntityItem::setTimeOffset(float value) { + bool changed = false; withWriteLock([&] { if (value != _timeOffset) { _timeOffset = value; - updateSound(true); + changed = true; } }); + + if (changed) { + updateSound(true); + } } float SoundEntityItem::getTimeOffset() const { @@ -239,12 +249,17 @@ float SoundEntityItem::getTimeOffset() const { } void SoundEntityItem::setPitch(float value) { + bool changed = false; withWriteLock([&] { if (value != _pitch) { _pitch = value; - updateSound(true); + changed = true; } }); + + if (changed) { + updateSound(true); + } } float SoundEntityItem::getPitch() const { @@ -254,12 +269,17 @@ float SoundEntityItem::getPitch() const { } void SoundEntityItem::setPlaying(bool value) { + bool changed = false; withWriteLock([&] { if (value != _playing) { _playing = value; - updateSound(); + changed = true; } }); + + if (changed) { + updateSound(); + } } bool SoundEntityItem::getPlaying() const { @@ -269,12 +289,17 @@ bool SoundEntityItem::getPlaying() const { } void SoundEntityItem::setLoop(bool value) { + bool changed = false; withWriteLock([&] { if (value != _loop) { _loop = value; - updateSound(true); + changed = true; } }); + + if (changed) { + updateSound(true); + } } bool SoundEntityItem::getLoop() const { @@ -284,12 +309,17 @@ bool SoundEntityItem::getLoop() const { } void SoundEntityItem::setPositional(bool value) { + bool changed = false; withWriteLock([&] { if (value != _positional) { _positional = value; - updateSound(); + changed = true; } }); + + if (changed) { + updateSound(); + } } bool SoundEntityItem::getPositional() const { @@ -299,36 +329,48 @@ bool SoundEntityItem::getPositional() const { } void SoundEntityItem::setLocalOnly(bool value) { + bool changed = false; withWriteLock([&] { if (value != _localOnly) { _localOnly = value; - - const auto tree = getTree(); - if (!tree) { - _updateNeeded = true; - return; - } - - if (shouldCreateSound(tree)) { - _sound = DependencyManager::get()->getSound(_url); - } else { - _sound = nullptr; - - if (_injector) { - DependencyManager::get()->stop(_injector); - } - _injector = nullptr; - } - - if (_sound) { - if (_sound->isLoaded()) { - updateSound(true); - } else { - connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); - } - } + changed = true; } }); + + if (changed) { + const auto tree = getTree(); + if (!tree) { + _updateNeeded = true; + return; + } + + std::lock_guard lock(_soundLock); + + bool createdSound = false; + withReadLock([&] { + if (shouldCreateSound(tree)) { + _sound = DependencyManager::get()->getSound(_url); + createdSound = true; + } + }); + + if (!createdSound) { + _sound = nullptr; + + if (_injector) { + DependencyManager::get()->stop(_injector); + } + _injector = nullptr; + } + + if (_sound) { + if (_sound->isLoaded()) { + updateSound(true); + } else { + connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + } + } + } } bool SoundEntityItem::getLocalOnly() const { @@ -337,21 +379,30 @@ bool SoundEntityItem::getLocalOnly() const { }); } -bool SoundEntityItem::restartSound() { +bool SoundEntityItem::restartSound(bool lock) { + if (lock) { + _soundLock.lock(); + } + if (!_sound) { + if (lock) { + _soundLock.unlock(); + } return false; } AudioInjectorOptions options; - const glm::quat orientation = getWorldOrientation(); - options.position = getWorldPosition() + orientation * (getScaledDimensions() * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); - options.positionSet = _positional; - options.volume = _volume; - options.loop = _loop; - options.orientation = orientation; - options.localOnly = _localOnly || _sound->isAmbisonic(); // force localOnly when ambisonic - options.secondOffset = _timeOffset; - options.pitch = _pitch; + withReadLock([&] { + const glm::quat orientation = getWorldOrientation(); + options.position = getWorldPosition() + orientation * (getScaledDimensions() * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + options.positionSet = _positional; + options.volume = _volume; + options.loop = _loop; + options.orientation = orientation; + options.localOnly = _localOnly || _sound->isAmbisonic(); // force localOnly when ambisonic + options.secondOffset = _timeOffset; + options.pitch = _pitch; + }); // stereo option isn't set from script, this comes from sound metadata or filename options.stereo = _sound->isStereo(); @@ -363,10 +414,16 @@ bool SoundEntityItem::restartSound() { _injector = DependencyManager::get()->playSound(_sound, options); } + if (lock) { + _soundLock.unlock(); + } + return true; } void SoundEntityItem::updateSound(bool restart) { + std::lock_guard lock(_soundLock); + if (!_sound) { return; } @@ -385,4 +442,4 @@ void SoundEntityItem::updateSound(bool restart) { DependencyManager::get()->stop(_injector); } } -} \ No newline at end of file +} diff --git a/libraries/entities/src/SoundEntityItem.h b/libraries/entities/src/SoundEntityItem.h index 3d1815f557..bd590d29d2 100644 --- a/libraries/entities/src/SoundEntityItem.h +++ b/libraries/entities/src/SoundEntityItem.h @@ -78,7 +78,7 @@ public: void setLocalOnly(bool value); bool getLocalOnly() const; - bool restartSound(); + bool restartSound(bool lock = false); protected: bool shouldCreateSound(const EntityTreePointer& tree) const; @@ -93,6 +93,7 @@ protected: bool _positional { true }; bool _localOnly { false }; + std::recursive_mutex _soundLock; SharedSoundPointer _sound; AudioInjectorPointer _injector; bool _updateNeeded { false }; From 55907acd704bafcc6e9dbf6ee5fab74c066c6f1c Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 6 Jul 2024 16:39:46 -0700 Subject: [PATCH 8/9] remove attachments --- .../src/avatars/ScriptableAvatar.h | 2 - interface/src/Application.cpp | 75 ---- interface/src/Application.h | 5 - interface/src/AvatarBookmarks.cpp | 5 - interface/src/AvatarBookmarks.h | 7 +- interface/src/avatar/MyAvatar.cpp | 286 +-------------- interface/src/avatar/MyAvatar.h | 43 +-- interface/src/avatar/OtherAvatar.cpp | 3 +- interface/src/ui/DialogsManager.h | 8 - interface/src/ui/ModelsBrowser.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 165 --------- .../src/avatars-renderer/Avatar.h | 10 - .../src/avatars-renderer/ScriptAvatar.h | 2 - libraries/avatars/src/AvatarData.cpp | 329 +----------------- libraries/avatars/src/AvatarData.h | 191 ---------- libraries/avatars/src/ScriptAvatarData.cpp | 11 +- libraries/avatars/src/ScriptAvatarData.h | 14 +- .../src/RenderableEntityItem.cpp | 2 - .../src/RenderableModelEntityItem.cpp | 2 - libraries/entities/src/EntityItem.cpp | 4 - libraries/model-serializers/src/FSTReader.cpp | 4 - libraries/model-serializers/src/FSTReader.h | 3 +- .../networking/src/udt/PacketHeaders.cpp | 4 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- .../recording/RecordingScriptingInterface.cpp | 4 - .../recording/RecordingScriptingInterface.h | 17 - .../render-utils/src/SoftAttachmentModel.cpp | 73 ---- .../render-utils/src/SoftAttachmentModel.h | 38 -- libraries/shared/src/SpatiallyNestable.cpp | 16 - libraries/shared/src/SpatiallyNestable.h | 4 - 30 files changed, 19 insertions(+), 1313 deletions(-) delete mode 100644 libraries/render-utils/src/SoftAttachmentModel.cpp delete mode 100644 libraries/render-utils/src/SoftAttachmentModel.h diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 7e79d25cd0..97a5f44adc 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -67,8 +67,6 @@ * @property {boolean} lookAtSnappingEnabled=true - true if the avatar's eyes snap to look at another avatar's * eyes when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. * @property {string} skeletonModelURL - The avatar's FST file. - * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments. - *

Deprecated: This property is deprecated and will be removed. Use avatar entities instead.

* @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e756ca276a..67450f3dd7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -316,7 +316,6 @@ static const QString JS_EXTENSION = ".js"; static const QString FST_EXTENSION = ".fst"; static const QString FBX_EXTENSION = ".fbx"; static const QString OBJ_EXTENSION = ".obj"; -static const QString AVA_JSON_EXTENSION = ".ava.json"; static const QString WEB_VIEW_TAG = "noDownload=true"; static const QString ZIP_EXTENSION = ".zip"; static const QString CONTENT_ZIP_EXTENSION = ".content.zip"; @@ -365,7 +364,6 @@ static const QString TESTER_FILE = "/sdcard/_hifi_test_device.txt"; const std::vector> Application::_acceptedExtensions { { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, - { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }, { JSON_EXTENSION, &Application::importJSONFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, { FST_EXTENSION, &Application::askToSetAvatarUrl }, @@ -7838,74 +7836,6 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { return true; } -bool Application::askToWearAvatarAttachmentUrl(const QString& url) { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest = QNetworkRequest(url); - networkRequest.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::OVERTE_USER_AGENT); - QNetworkReply* reply = networkAccessManager.get(networkRequest); - int requestNumber = ++_avatarAttachmentRequest; - connect(reply, &QNetworkReply::finished, [this, reply, url, requestNumber]() { - - if (requestNumber != _avatarAttachmentRequest) { - // this request has been superseded by another more recent request - reply->deleteLater(); - return; - } - - QNetworkReply::NetworkError networkError = reply->error(); - if (networkError == QNetworkReply::NoError) { - // download success - QByteArray contents = reply->readAll(); - - QJsonParseError jsonError; - auto doc = QJsonDocument::fromJson(contents, &jsonError); - if (jsonError.error == QJsonParseError::NoError) { - - auto jsonObject = doc.object(); - - // retrieve optional name field from JSON - QString name = tr("Unnamed Attachment"); - auto nameValue = jsonObject.value("name"); - if (nameValue.isString()) { - name = nameValue.toString(); - } - - auto avatarAttachmentConfirmationTitle = tr("Avatar Attachment Confirmation"); - auto avatarAttachmentConfirmationMessage = tr("Would you like to wear '%1' on your avatar?").arg(name); - ModalDialogListener* dlg = OffscreenUi::asyncQuestion(avatarAttachmentConfirmationTitle, - avatarAttachmentConfirmationMessage, - QMessageBox::Ok | QMessageBox::Cancel); - QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) { - QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr); - if (static_cast(answer.toInt()) == QMessageBox::Yes) { - // add attachment to avatar - auto myAvatar = getMyAvatar(); - assert(myAvatar); - auto attachmentDataVec = myAvatar->getAttachmentData(); - AttachmentData attachmentData; - attachmentData.fromJson(jsonObject); - attachmentDataVec.push_back(attachmentData); - myAvatar->setAttachmentData(attachmentDataVec); - } else { - qCDebug(interfaceapp) << "User declined to wear the avatar attachment"; - } - }); - } else { - // json parse error - auto avatarAttachmentParseErrorString = tr("Error parsing attachment JSON from url: \"%1\""); - displayAvatarAttachmentWarning(avatarAttachmentParseErrorString.arg(url)); - } - } else { - // download failure - auto avatarAttachmentDownloadErrorString = tr("Error downloading attachment JSON from url: \"%1\""); - displayAvatarAttachmentWarning(avatarAttachmentDownloadErrorString.arg(url)); - } - reply->deleteLater(); - }); - return true; -} - static const QString CONTENT_SET_NAME_QUERY_PARAM = "name"; void Application::replaceDomainContent(const QString& url, const QString& itemName) { @@ -7984,11 +7914,6 @@ bool Application::askToReplaceDomainContent(const QString& url) { return true; } -void Application::displayAvatarAttachmentWarning(const QString& message) const { - auto avatarAttachmentWarningTitle = tr("Avatar Attachment Failure"); - OffscreenUi::asyncWarning(avatarAttachmentWarningTitle, message); -} - void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const { auto tablet = DependencyManager::get()->getTablet(SYSTEM_TABLET); auto hmd = DependencyManager::get(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 63a035dd45..e93a8338de 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -546,9 +546,6 @@ private slots: bool askToSetAvatarUrl(const QString& url); bool askToLoadScript(const QString& scriptFilenameOrURL); - bool askToWearAvatarAttachmentUrl(const QString& url); - void displayAvatarAttachmentWarning(const QString& message) const; - bool askToReplaceDomainContent(const QString& url); void setSessionUUID(const QUuid& sessionUUID) const; @@ -800,8 +797,6 @@ private: bool _reticleClickPressed { false }; bool _keyboardFocusWaitingOnRenderable { false }; - int _avatarAttachmentRequest = 0; - bool _settingsLoaded { false }; bool _captureMouse { false }; diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 461c55e64e..6485840c80 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -222,8 +222,6 @@ void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { * @property {number} avatarScale - The target scale of the avatar. * @property {Array>} [avatarEntites] - The avatar entities included with the * bookmark. - * @property {AttachmentData[]} [attachments] - The attachments included with the bookmark. - *

Deprecated: Use avatar entities instead. */ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { @@ -266,8 +264,6 @@ void AvatarBookmarks::loadBookmarkInternal(const QString& bookmarkName) { myAvatar->clearWornAvatarEntities(); const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat(); myAvatar->setAvatarScale(qScale); - QList attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList()).toList(); - myAvatar->setAttachmentsVariant(attachments); QVariantList avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList(); addAvatarEntities(avatarEntities); emit bookmarkLoaded(bookmarkName); @@ -335,7 +331,6 @@ QVariantMap AvatarBookmarks::getAvatarDataToBookmark() { const QString& avatarIcon = QString(""); const QVariant& avatarScale = myAvatar->getAvatarScale(); - // If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION QVariantMap bookmark; bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION); bookmark.insert(ENTRY_AVATAR_URL, avatarUrl); diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h index bf06743b3f..99421bb78b 100644 --- a/interface/src/AvatarBookmarks.h +++ b/interface/src/AvatarBookmarks.h @@ -66,8 +66,7 @@ public slots: void saveBookmark(const QString& bookmarkName); /*@jsdoc - * Loads an avatar bookmark, setting your avatar model, scale, and avatar entities (or attachments if an old bookmark) to - * those in the bookmark. + * Loads an avatar bookmark, setting your avatar model, scale, and avatar entities to those in the bookmark. * @function AvatarBookmarks.loadBookmark * @param {string} bookmarkName - The name of the avatar bookmark to load (case sensitive). */ @@ -104,8 +103,7 @@ public slots: signals: /*@jsdoc - * Triggered when an avatar bookmark is loaded, setting your avatar model, scale, and avatar entities (or attachments if an - * old bookmark) to those in the bookmark. + * Triggered when an avatar bookmark is loaded, setting your avatar model, scale, and avatar entities to those in the bookmark. * @function AvatarBookmarks.bookmarkLoaded * @param {string} bookmarkName - The name of the avatar bookmark loaded. * @returns {Signal} @@ -155,7 +153,6 @@ private: const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json"; const QString ENTRY_AVATAR_URL = "avatarUrl"; const QString ENTRY_AVATAR_ICON = "avatarIcon"; - const QString ENTRY_AVATAR_ATTACHMENTS = "attachments"; const QString ENTRY_AVATAR_ENTITIES = "avatarEntites"; const QString ENTRY_AVATAR_SCALE = "avatarScale"; const QString ENTRY_VERSION = "version"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0c61b0d01a..fc6fae5456 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -272,12 +272,6 @@ MyAvatar::MyAvatar(QThread* thread) : auto hfmModel = getSkeletonModel()->getHFMModel(); qApp->loadAvatarScripts(hfmModel.scripts); _shouldLoadScripts = false; - } - // Load and convert old attachments to avatar entities - if (_oldAttachmentData.size() > 0) { - setAttachmentData(_oldAttachmentData); - _oldAttachmentData.clear(); - _attachmentData.clear(); } }); connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); @@ -371,10 +365,6 @@ MyAvatar::MyAvatar(QThread* thread) : setWorldPosition(dummyAvatar.getWorldPosition()); setWorldOrientation(dummyAvatar.getWorldOrientation()); - if (!dummyAvatar.getAttachmentData().isEmpty()) { - setAttachmentData(dummyAvatar.getAttachmentData()); - } - auto headData = dummyAvatar.getHeadData(); if (headData && _headData) { // blendshapes @@ -501,11 +491,6 @@ glm::quat MyAvatar::getOrientationOutbound() const { return (slerp(_smoothOrientationInitial, _smoothOrientationTarget, interp)); } -// virtual -void MyAvatar::simulateAttachments(float deltaTime) { - // don't update attachments here, do it in harvestResultsFromPhysicsSimulation() -} - QByteArray MyAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) { CameraMode mode = qApp->getCamera().getMode(); _globalPosition = getWorldPosition(); @@ -982,8 +967,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) { } // we've achived our final adjusted position and rotation for the avatar - // and all of its joints, now update our attachements. - Avatar::simulateAttachments(deltaTime); + // and all of its joints, now update our children. relayJointDataToChildren(); if (applyGrabChanges()) { _cauterizationNeedsUpdate = true; @@ -2175,65 +2159,6 @@ void MyAvatar::loadAvatarEntityDataFromSettings() { }); } -void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { - Settings settings; - settings.beginGroup("savedAttachmentData"); - settings.beginGroup(_skeletonModel->getURL().toString()); - settings.beginGroup(attachment.modelURL.toString()); - settings.setValue("jointName", attachment.jointName); - - settings.beginGroup(attachment.jointName); - settings.setValue("translation_x", attachment.translation.x); - settings.setValue("translation_y", attachment.translation.y); - settings.setValue("translation_z", attachment.translation.z); - glm::vec3 eulers = safeEulerAngles(attachment.rotation); - settings.setValue("rotation_x", eulers.x); - settings.setValue("rotation_y", eulers.y); - settings.setValue("rotation_z", eulers.z); - settings.setValue("scale", attachment.scale); - - settings.endGroup(); - settings.endGroup(); - settings.endGroup(); - settings.endGroup(); -} - -AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString& jointName) const { - Settings settings; - settings.beginGroup("savedAttachmentData"); - settings.beginGroup(_skeletonModel->getURL().toString()); - settings.beginGroup(modelURL.toString()); - - AttachmentData attachment; - attachment.modelURL = modelURL; - if (jointName.isEmpty()) { - attachment.jointName = settings.value("jointName").toString(); - } else { - attachment.jointName = jointName; - } - settings.beginGroup(attachment.jointName); - if (settings.contains("translation_x")) { - attachment.translation.x = loadSetting(settings, "translation_x", 0.0f); - attachment.translation.y = loadSetting(settings, "translation_y", 0.0f); - attachment.translation.z = loadSetting(settings, "translation_z", 0.0f); - glm::vec3 eulers; - eulers.x = loadSetting(settings, "rotation_x", 0.0f); - eulers.y = loadSetting(settings, "rotation_y", 0.0f); - eulers.z = loadSetting(settings, "rotation_z", 0.0f); - attachment.rotation = glm::quat(eulers); - attachment.scale = loadSetting(settings, "scale", 1.0f); - } else { - attachment = AttachmentData(); - } - - settings.endGroup(); - settings.endGroup(); - settings.endGroup(); - settings.endGroup(); - - return attachment; -} - bool MyAvatar::isMyAvatarURLProtected() const { return !ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL); } @@ -2994,171 +2919,6 @@ SharedSoundPointer MyAvatar::getCollisionSound() { return _collisionSound; } -void MyAvatar::attach(const QString& modelURL, const QString& jointName, - const glm::vec3& translation, const glm::quat& rotation, - float scale, bool isSoft, - bool allowDuplicates, bool useSaved) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "attach", - Q_ARG(const QString&, modelURL), - Q_ARG(const QString&, jointName), - Q_ARG(const glm::vec3&, translation), - Q_ARG(const glm::quat&, rotation), - Q_ARG(float, scale), - Q_ARG(bool, isSoft), - Q_ARG(bool, allowDuplicates), - Q_ARG(bool, useSaved) - ); - return; - } - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) << "Ignoring attach() because don't have canRezAvatarEntities permission on domain"; - return; - } - - AttachmentData data; - data.modelURL = modelURL; - data.jointName = jointName; - data.translation = translation; - data.rotation = rotation; - data.scale = scale; - data.isSoft = isSoft; - EntityItemProperties properties; - attachmentDataToEntityProperties(data, properties); - DependencyManager::get()->addEntity(properties, true); - emit attachmentsChanged(); -} - -void MyAvatar::detachOne(const QString& modelURL, const QString& jointName) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "detachOne", - Q_ARG(const QString&, modelURL), - Q_ARG(const QString&, jointName) - ); - return; - } - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) << "Ignoring detachOne() because don't have canRezAvatarEntities permission on domain"; - return; - } - - QUuid entityID; - if (findAvatarEntity(modelURL, jointName, entityID)) { - DependencyManager::get()->deleteEntity(entityID); - } - emit attachmentsChanged(); -} - -void MyAvatar::detachAll(const QString& modelURL, const QString& jointName) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "detachAll", - Q_ARG(const QString&, modelURL), - Q_ARG(const QString&, jointName) - ); - return; - } - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) << "Ignoring detachAll() because don't have canRezAvatarEntities permission on domain"; - return; - } - - QUuid entityID; - while (findAvatarEntity(modelURL, jointName, entityID)) { - DependencyManager::get()->deleteEntity(entityID); - } - emit attachmentsChanged(); -} - -void MyAvatar::setAttachmentData(const QVector& attachmentData) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "setAttachmentData", - Q_ARG(const QVector&, attachmentData)); - return; - } - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) << "Ignoring setAttachmentData() because don't have canRezAvatarEntities permission on domain"; - return; - } - - std::vector newEntitiesProperties; - for (auto& data : attachmentData) { - QUuid entityID; - EntityItemProperties properties; - if (findAvatarEntity(data.modelURL.toString(), data.jointName, entityID)) { - properties = DependencyManager::get()->getEntityProperties(entityID); - } - attachmentDataToEntityProperties(data, properties); - newEntitiesProperties.push_back(properties); - } - - // clear any existing wearables - clearWornAvatarEntities(); - - for (auto& properties : newEntitiesProperties) { - DependencyManager::get()->addEntity(properties, true); - } - emit attachmentsChanged(); -} - -QVector MyAvatar::getAttachmentData() const { - QVector attachmentData; - - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) << "Ignoring getAttachmentData() because don't have canRezAvatarEntities permission on domain"; - return attachmentData; - } - - QList avatarEntityIDs; - _avatarEntitiesLock.withReadLock([&] { - avatarEntityIDs = _packedAvatarEntityData.keys(); - }); - for (const auto& entityID : avatarEntityIDs) { - auto properties = DependencyManager::get()->getEntityProperties(entityID); - AttachmentData data = entityPropertiesToAttachmentData(properties); - attachmentData.append(data); - } - return attachmentData; -} - -QVariantList MyAvatar::getAttachmentsVariant() const { - QVariantList result; - - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) - << "Ignoring getAttachmentsVariant() because don't have canRezAvatarEntities permission on domain"; - return result; - } - - for (const auto& attachment : getAttachmentData()) { - result.append(attachment.toVariant()); - } - return result; -} - -void MyAvatar::setAttachmentsVariant(const QVariantList& variant) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "setAttachmentsVariant", - Q_ARG(const QVariantList&, variant)); - return; - } - - if (!DependencyManager::get()->getThisNodeCanRezAvatarEntities()) { - qCDebug(interfaceapp) - << "Ignoring setAttachmentsVariant() because don't have canRezAvatarEntities permission on domain"; - return; - } - - QVector newAttachments; - newAttachments.reserve(variant.size()); - for (const auto& attachmentVar : variant) { - AttachmentData attachment; - if (attachment.fromVariant(attachmentVar)) { - newAttachments.append(attachment); - } - } - setAttachmentData(newAttachments); -} - bool MyAvatar::findAvatarEntity(const QString& modelURL, const QString& jointName, QUuid& entityID) { QList avatarEntityIDs; _avatarEntitiesLock.withReadLock([&] { @@ -3174,34 +2934,6 @@ bool MyAvatar::findAvatarEntity(const QString& modelURL, const QString& jointNam return false; } -AttachmentData MyAvatar::entityPropertiesToAttachmentData(const EntityItemProperties& properties) const { - AttachmentData data; - data.modelURL = properties.getModelURL(); - data.translation = properties.getLocalPosition(); - data.rotation = properties.getLocalRotation(); - data.isSoft = properties.getRelayParentJoints(); - int jointIndex = (int)properties.getParentJointIndex(); - if (jointIndex > -1 && jointIndex < getJointNames().size()) { - data.jointName = getJointNames()[jointIndex]; - } - return data; -} - -void MyAvatar::attachmentDataToEntityProperties(const AttachmentData& data, EntityItemProperties& properties) { - QString url = data.modelURL.toString(); - properties.setName(QFileInfo(url).baseName()); - properties.setType(EntityTypes::Model); - properties.setParentID(AVATAR_SELF_ID); - properties.setLocalPosition(data.translation); - properties.setLocalRotation(data.rotation); - if (!data.isSoft) { - properties.setParentJointIndex(getJointIndex(data.jointName)); - } else { - properties.setRelayParentJoints(true); - } - properties.setModelURL(url); -} - void MyAvatar::initHeadBones() { int neckJointIndex = -1; if (_skeletonModel->isLoaded()) { @@ -3444,22 +3176,6 @@ void MyAvatar::preDisplaySide(const RenderArgs* renderArgs) { if (shouldDrawHead != _prevShouldDrawHead) { _cauterizationNeedsUpdate = true; _skeletonModel->setEnableCauterization(!shouldDrawHead); - - for (int i = 0; i < _attachmentData.size(); i++) { - if (_attachmentData[i].jointName.compare("Head", Qt::CaseInsensitive) == 0 || - _attachmentData[i].jointName.compare("Neck", Qt::CaseInsensitive) == 0 || - _attachmentData[i].jointName.compare("LeftEye", Qt::CaseInsensitive) == 0 || - _attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 || - _attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 || - _attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) { - uint8_t modelRenderTagBits = shouldDrawHead ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_SECONDARY_VIEW; - - _attachmentModels[i]->setTagMask(modelRenderTagBits); - _attachmentModels[i]->setGroupCulled(false); - _attachmentModels[i]->setCanCastShadow(true); - _attachmentModels[i]->setVisibleInScene(true, qApp->getMain3DScene()); - } - } } _prevShouldDrawHead = shouldDrawHead; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 60c07ad42c..2777d2c82f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -141,8 +141,6 @@ class MyAvatar : public Avatar { * @property {boolean} lookAtSnappingEnabled=true - true if the avatar's eyes snap to look at another avatar's * eyes when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. * @property {string} skeletonModelURL - The avatar's FST file. - * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments. - *

Deprecated: This property is deprecated and will be removed. Use avatar entities instead.

* @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the @@ -326,17 +324,10 @@ class MyAvatar : public Avatar { * @borrows Avatar.getJointIndex as getJointIndex * @borrows Avatar.getJointNames as getJointNames * @borrows Avatar.setBlendshape as setBlendshape - * @borrows Avatar.getAttachmentsVariant as getAttachmentsVariant - * @borrows Avatar.setAttachmentsVariant as setAttachmentsVariant * @borrows Avatar.updateAvatarEntity as updateAvatarEntity * @borrows Avatar.clearAvatarEntity as clearAvatarEntity * @borrows Avatar.setForceFaceTrackerConnected as setForceFaceTrackerConnected * @borrows Avatar.setSkeletonModelURL as setSkeletonModelURL - * @borrows Avatar.getAttachmentData as getAttachmentData - * @borrows Avatar.setAttachmentData as setAttachmentData - * @borrows Avatar.attach as attach - * @borrows Avatar.detachOne as detachOne - * @borrows Avatar.detachAll as detachAll * @comment Avatar.getAvatarEntityData as getAvatarEntityData - Don't borrow because implementation is different. * @comment Avatar.setAvatarEntityData as setAvatarEntityData - Don't borrow because implementation is different. * @borrows Avatar.getSensorToWorldMatrix as getSensorToWorldMatrix @@ -590,8 +581,6 @@ public: static void registerMetaTypes(ScriptEnginePointer engine); void registerProperties(ScriptEnginePointer engine); - virtual void simulateAttachments(float deltaTime) override; - AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; } AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; } AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; } @@ -1073,9 +1062,6 @@ public: void loadData(); void loadAvatarEntityDataFromSettings(); - void saveAttachmentData(const AttachmentData& attachment) const; - AttachmentData loadAttachmentData(const QUrl& modelURL, const QString& jointName = QString()) const; - // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); void setDriveKey(DriveKeys key, float val); @@ -1822,12 +1808,6 @@ public: float computeStandingHeightMode(const controller::Pose& head); glm::quat computeAverageHeadRotation(const controller::Pose& head); - virtual void setAttachmentData(const QVector& attachmentData) override; - virtual QVector getAttachmentData() const override; - - virtual QVariantList getAttachmentsVariant() const override; - virtual void setAttachmentsVariant(const QVariantList& variant) override; - glm::vec3 getNextPosition() { return _goToPending ? _goToPosition : getWorldPosition(); } void prepareAvatarEntityDataForReload(); @@ -2610,16 +2590,6 @@ signals: */ void sensorToWorldScaleChanged(float sensorToWorldScale); - /*@jsdoc - * Triggered when the a model is attached to or detached from one of the avatar's joints using one of - * {@link MyAvatar.attach|attach}, {@link MyAvatar.detachOne|detachOne}, {@link MyAvatar.detachAll|detachAll}, or - * {@link MyAvatar.setAttachmentData|setAttachmentData}. - * @function MyAvatar.attachmentsChanged - * @returns {Signal} - * @deprecated This signal is deprecated and will be removed. Use avatar entities instead. - */ - void attachmentsChanged(); - /*@jsdoc * Triggered when the avatar's size changes. This can be due to the user changing the size of their avatar or the domain * limiting the size of their avatar. @@ -2701,18 +2671,7 @@ private: void setScriptedMotorFrame(QString frame); void setScriptedMotorMode(QString mode); - // Attachments - virtual void attach(const QString& modelURL, const QString& jointName = QString(), - const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), - float scale = 1.0f, bool isSoft = false, - bool allowDuplicates = false, bool useSaved = true) override; - - virtual void detachOne(const QString& modelURL, const QString& jointName = QString()) override; - virtual void detachAll(const QString& modelURL, const QString& jointName = QString()) override; - - // Attachments/Avatar Entity - void attachmentDataToEntityProperties(const AttachmentData& data, EntityItemProperties& properties); - AttachmentData entityPropertiesToAttachmentData(const EntityItemProperties& properties) const; + // Avatar Entities bool findAvatarEntity(const QString& modelURL, const QString& jointName, QUuid& entityID); void addAvatarEntitiesToTree(); diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index fe0e83dfa0..aacb6f56a9 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -316,7 +316,6 @@ void OtherAvatar::simulate(float deltaTime, bool inView) { { PROFILE_RANGE(simulation, "misc"); measureMotionDerivatives(deltaTime); - simulateAttachments(deltaTime); updatePalms(); } { @@ -384,7 +383,7 @@ void OtherAvatar::debugJointData() const { } void OtherAvatar::handleChangedAvatarEntityData() { - PerformanceTimer perfTimer("attachments"); + PerformanceTimer perfTimer("avatarEntities"); // AVATAR ENTITY UPDATE FLOW // - if queueEditEntityMessage() sees "AvatarEntity" HostType it calls _myAvatar->storeAvatarEntityDataPayload() diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 864174296e..6636c725c8 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -20,14 +20,10 @@ #include "HMDToolsDialog.h" #include "TestingDialog.h" -class AnimationsDialog; -class AttachmentsDialog; -class CachesSizeDialog; class LodToolsDialog; class OctreeStatsDialog; class ScriptEditorWindow; class TestingDialog; -class QMessageBox; class DomainConnectionDialog; class DialogsManager : public QObject, public Dependency { @@ -77,10 +73,6 @@ private: template void maybeCreateDialog(QPointer& member); - QPointer _animationsDialog; - QPointer _attachmentsDialog; - QPointer _cachesSizeDialog; - QPointer _ircInfoBox; QPointer _hmdToolsDialog; QPointer _lodToolsDialog; QPointer _octreeStatsDialog; diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 2f422129e3..cd706d0bcb 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -32,7 +32,7 @@ #include #include -const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "skeletons", "attachments" }; +const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "skeletons" }; static const QString S3_URL = NetworkingConstants::HF_PUBLIC_CDN_URL; static const QString PUBLIC_URL = "http://public.overte.org"; // Changed to Overte but not entirely sure what to do with this yet. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 790b45843c..25473bda90 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include "ModelEntityItem.h" @@ -52,7 +51,6 @@ const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); const float Avatar::MYAVATAR_LOADING_PRIORITY = (float)M_PI; // Entity priority is computed as atan2(maxDim, distance) which is <= PI / 2 const float Avatar::OTHERAVATAR_LOADING_PRIORITY = MYAVATAR_LOADING_PRIORITY - EPSILON; -const float Avatar::ATTACHMENT_LOADING_PRIORITY = OTHERAVATAR_LOADING_PRIORITY - EPSILON; namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { @@ -651,19 +649,6 @@ void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& sc _skeletonModel->setVisibleInScene(_isMeshVisible, scene); processMaterials(); - bool attachmentRenderingNeedsUpdate = false; - for (auto& attachmentModel : _attachmentModels) { - attachmentModel->addToScene(scene, transaction); - attachmentModel->setTagMask(render::hifi::TAG_ALL_VIEWS); - attachmentModel->setGroupCulled(true); - attachmentModel->setCanCastShadow(true); - attachmentModel->setVisibleInScene(_isMeshVisible, scene); - attachmentRenderingNeedsUpdate = true; - } - - if (attachmentRenderingNeedsUpdate) { - updateAttachmentRenderIDs(); - } _mustFadeIn = true; emit DependencyManager::get()->modelAddedToScene(getSessionUUID(), NestableType::Avatar, _skeletonModel); @@ -688,11 +673,6 @@ void Avatar::fadeOut(render::Transaction& transaction, KillAvatarReason reason) void Avatar::fade(render::Transaction& transaction, render::Transition::Type type) { transaction.resetTransitionOnItem(_renderItemID, type); - for (auto& attachmentModel : _attachmentModels) { - for (auto itemId : attachmentModel->fetchRenderItemIDs()) { - transaction.resetTransitionOnItem(itemId, type, _renderItemID); - } - } _lastFadeRequested = type; } @@ -704,9 +684,6 @@ void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointe transaction.removeItem(_renderItemID); render::Item::clearID(_renderItemID); _skeletonModel->removeFromScene(scene, transaction); - for (auto& attachmentModel : _attachmentModels) { - attachmentModel->removeFromScene(scene, transaction); - } emit DependencyManager::get()->modelRemovedFromScene(getSessionUUID(), NestableType::Avatar, _skeletonModel); } @@ -866,8 +843,6 @@ bool Avatar::getEnableMeshVisible() const { void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { bool canTryFade{ false }; - _attachmentsToDelete.clear(); - // 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::Transaction transaction; @@ -885,27 +860,9 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { canTryFade = true; _isAnimatingScale = true; } - bool attachmentRenderingNeedsUpdate = false; - for (auto attachmentModel : _attachmentModels) { - if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) { - attachmentModel->removeFromScene(scene, transaction); - attachmentModel->addToScene(scene, transaction); - - attachmentModel->setTagMask(render::hifi::TAG_ALL_VIEWS); - attachmentModel->setGroupCulled(true); - attachmentModel->setCanCastShadow(true); - attachmentModel->setVisibleInScene(_isMeshVisible, scene); - attachmentRenderingNeedsUpdate = true; - } - } if (_needMeshVisibleSwitch) { _skeletonModel->setVisibleInScene(_isMeshVisible, scene); - for (auto attachmentModel : _attachmentModels) { - if (attachmentModel->isRenderable()) { - attachmentModel->setVisibleInScene(_isMeshVisible, scene); - } - } updateRenderItem(transaction); _needMeshVisibleSwitch = false; } @@ -916,17 +873,6 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { _mustFadeIn = false; } - for (auto attachmentModelToRemove : _attachmentsToRemove) { - attachmentModelToRemove->removeFromScene(scene, transaction); - attachmentRenderingNeedsUpdate = true; - } - _attachmentsToDelete.insert(_attachmentsToDelete.end(), _attachmentsToRemove.begin(), _attachmentsToRemove.end()); - _attachmentsToRemove.clear(); - - if (attachmentRenderingNeedsUpdate) { - updateAttachmentRenderIDs(); - } - scene->enqueueTransaction(transaction); } @@ -934,48 +880,6 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { return true; } -void Avatar::simulateAttachments(float deltaTime) { - assert(_attachmentModels.size() == _attachmentModelsTexturesLoaded.size()); - PerformanceTimer perfTimer("attachments"); - for (int i = 0; i < (int)_attachmentModels.size(); i++) { - const AttachmentData& attachment = _attachmentData.at(i); - auto& model = _attachmentModels.at(i); - bool texturesLoaded = _attachmentModelsTexturesLoaded.at(i); - - // Watch for texture loading - if (!texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) { - _attachmentModelsTexturesLoaded[i] = true; - model->updateRenderItems(); - } - - int jointIndex = getJointIndex(attachment.jointName); - glm::vec3 jointPosition; - glm::quat jointRotation; - if (attachment.isSoft) { - // soft attachments do not have transform offsets - model->setTransformNoUpdateRenderItems(Transform(getWorldOrientation() * Quaternions::Y_180, glm::vec3(1.0), getWorldPosition())); - model->simulate(deltaTime); - model->updateRenderItems(); - } else { - if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPosition) && - _skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRotation)) { - model->setTransformNoUpdateRenderItems(Transform(jointRotation * attachment.rotation, glm::vec3(1.0), jointPosition + jointRotation * attachment.translation * getModelScale())); - float scale = getModelScale() * attachment.scale; - model->setScaleToFit(true, model->getNaturalDimensions() * scale, true); // hack to force rescale - model->setSnapModelToCenter(false); // hack to force resnap - model->setSnapModelToCenter(true); - model->simulate(deltaTime); - model->updateRenderItems(); - } - } - } - - if (_ancestorChainRenderableVersion != _lastAncestorChainRenderableVersion) { - _lastAncestorChainRenderableVersion = _ancestorChainRenderableVersion; - updateDescendantRenderIDs(); - } -} - float Avatar::getBoundingRadius() const { return getBounds().getLargestDimension() / 2.0f; } @@ -1631,58 +1535,6 @@ void Avatar::updateFitBoundingBox() { } } -// create new model, can return an instance of a SoftAttachmentModel rather then Model -static std::shared_ptr allocateAttachmentModel(bool isSoft, const Rig& rigOverride, bool isCauterized) { - if (isSoft) { - // cast to std::shared_ptr - std::shared_ptr softModel = std::make_shared(nullptr, rigOverride); - if (isCauterized) { - softModel->flagAsCauterized(); - } - return std::dynamic_pointer_cast(softModel); - } else { - return std::make_shared(); - } -} - -void Avatar::setAttachmentData(const QVector& attachmentData) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "setAttachmentData", - Q_ARG(const QVector, attachmentData)); - return; - } - - auto oldAttachmentData = _attachmentData; - AvatarData::setAttachmentData(attachmentData); - - // if number of attachments has been reduced, remove excess models. - while ((int)_attachmentModels.size() > attachmentData.size()) { - auto attachmentModel = _attachmentModels.back(); - _attachmentModels.pop_back(); - _attachmentModelsTexturesLoaded.pop_back(); - _attachmentsToRemove.push_back(attachmentModel); - } - - for (int i = 0; i < attachmentData.size(); i++) { - if (i == (int)_attachmentModels.size()) { - // if number of attachments has been increased, we need to allocate a new model - _attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar())); - _attachmentModelsTexturesLoaded.push_back(false); - } else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) { - // if the attachment has changed type, we need to re-allocate a new one. - _attachmentsToRemove.push_back(_attachmentModels[i]); - _attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar()); - _attachmentModelsTexturesLoaded[i] = false; - } - // If the model URL has changd, we need to wait for the textures to load - if (_attachmentModels[i]->getURL() != attachmentData[i].modelURL) { - _attachmentModelsTexturesLoaded[i] = false; - } - _attachmentModels[i]->setLoadingPriority(ATTACHMENT_LOADING_PRIORITY); - _attachmentModels[i]->setURL(attachmentData[i].modelURL); - } -} - int Avatar::parseDataFromBuffer(const QByteArray& buffer) { PerformanceTimer perfTimer("unpack"); if (!_initialized) { @@ -2102,11 +1954,6 @@ uint32_t Avatar::appendSubMetaItems(render::ItemIDs& subItems) { return _subItemLock.resultWithReadLock([&] { uint32_t total = 0; - if (_attachmentRenderIDs.size() > 0) { - subItems.insert(subItems.end(), _attachmentRenderIDs.begin(), _attachmentRenderIDs.end()); - total += (uint32_t)_attachmentRenderIDs.size(); - } - if (_descendantRenderIDs.size() > 0) { subItems.insert(subItems.end(), _descendantRenderIDs.begin(), _descendantRenderIDs.end()); total += (uint32_t)_descendantRenderIDs.size(); @@ -2116,18 +1963,6 @@ uint32_t Avatar::appendSubMetaItems(render::ItemIDs& subItems) { }); } -void Avatar::updateAttachmentRenderIDs() { - _subItemLock.withWriteLock([&] { - _attachmentRenderIDs.clear(); - for (auto& attachmentModel : _attachmentModels) { - if (attachmentModel && attachmentModel->isRenderable()) { - auto& metaSubItems = attachmentModel->fetchRenderItemIDs(); - _attachmentRenderIDs.insert(_attachmentRenderIDs.end(), metaSubItems.begin(), metaSubItems.end()); - } - } - }); -} - void Avatar::updateDescendantRenderIDs() { _subItemLock.withWriteLock([&] { auto oldRenderingDescendantEntityIDs = _renderingDescendantEntityIDs; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index c6112d74f2..af6b58a187 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -157,7 +157,6 @@ public: void init(); void removeAvatarEntitiesFromTree(); virtual void simulate(float deltaTime, bool inView) = 0; - virtual void simulateAttachments(float deltaTime); virtual void render(RenderArgs* renderArgs); @@ -344,7 +343,6 @@ public: Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const; Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; - virtual void setAttachmentData(const QVector& attachmentData) override; void updateDisplayNameAlpha(bool showDisplayName); virtual void setSessionDisplayName(const QString& sessionDisplayName) override { }; // no-op @@ -650,10 +648,6 @@ protected: mutable bool _modelJointsCached { false }; glm::vec3 _skeletonOffset; - std::vector> _attachmentModels; - std::vector _attachmentModelsTexturesLoaded; - std::vector> _attachmentsToRemove; - std::vector> _attachmentsToDelete; float _bodyYawDelta { 0.0f }; // degrees/sec float _seatedBodyYawDelta{ 0.0f }; // degrees/renderframe @@ -753,7 +747,6 @@ protected: static const float MYAVATAR_LOADING_PRIORITY; static const float OTHERAVATAR_LOADING_PRIORITY; - static const float ATTACHMENT_LOADING_PRIORITY; LoadingStatus _loadingStatus { LoadingStatus::NoModel }; @@ -773,12 +766,9 @@ protected: VectorOfIDs _grabsToDelete; // deleted grab IDs -- changes needed to entities or physics ReadWriteLockable _subItemLock; - void updateAttachmentRenderIDs(); - render::ItemIDs _attachmentRenderIDs; void updateDescendantRenderIDs(); render::ItemIDs _descendantRenderIDs; std::unordered_set _renderingDescendantEntityIDs; - uint32_t _lastAncestorChainRenderableVersion { 0 }; }; #endif // hifi_Avatar_h diff --git a/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h index a4595e9bde..bffd0561ab 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h @@ -58,8 +58,6 @@ * when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. * * @property {string} skeletonModelURL - The avatar's FST file. - * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments. - *

Deprecated: This property is deprecated and will be removed. Use avatar entities instead.

* @property {string[]} jointNames - The list of joints in the avatar model. * * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4068f7c547..d44890b1b8 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -73,17 +73,10 @@ static const float DEFAULT_AVATAR_DENSITY = 1000.0f; // density of water STATIC_SCRIPT_TYPES_INITIALIZER((+[](ScriptManager* manager) { auto scriptEngine = manager->engine().get(); - registerAvatarTypes(scriptEngine); scriptRegisterMetaType(scriptEngine); scriptRegisterMetaType(scriptEngine); })); -STATIC_SCRIPT_INITIALIZER(+[](ScriptManager* manager) { - auto scriptEngine = manager->engine().get(); - - registerAvatarPrototypes(scriptEngine); -}); - size_t AvatarDataPacket::maxFaceTrackerInfoSize(size_t numBlendshapeCoefficients) { return FACE_TRACKER_INFO_SIZE + numBlendshapeCoefficients * sizeof(float); } @@ -2027,7 +2020,6 @@ void AvatarData::processAvatarIdentity(QDataStream& packetStream, bool& identity Identity identity; packetStream - >> identity.attachmentData >> identity.displayName >> identity.sessionDisplayName >> identity.identityFlags @@ -2068,11 +2060,6 @@ void AvatarData::processAvatarIdentity(QDataStream& packetStream, bool& identity } }; - if (identity.attachmentData != _attachmentData) { - setAttachmentData(identity.attachmentData); - identityChanged = true; - } - #ifdef WANT_DEBUG qCDebug(avatars) << __FUNCTION__ << "identity.uuid:" << identity.uuid @@ -2319,7 +2306,6 @@ QByteArray AvatarData::identityByteArray(bool setIsReplicated) const { identityStream << getSessionUUID() << (udt::SequenceNumber::Type) _identitySequenceNumber - << _attachmentData << _displayName << getSessionDisplayNameForTransport() // depends on _sessionDisplayName << identityFlags; @@ -2353,86 +2339,6 @@ void AvatarData::setDisplayName(const QString& displayName) { markIdentityDataChanged(); } -QVector AvatarData::getAttachmentData() const { - if (QThread::currentThread() != thread()) { - QVector result; - BLOCKING_INVOKE_METHOD(const_cast(this), "getAttachmentData", - Q_RETURN_ARG(QVector, result)); - return result; - } - return _attachmentData; -} - -void AvatarData::setAttachmentData(const QVector& attachmentData) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setAttachmentData", Q_ARG(const QVector&, attachmentData)); - return; - } - _attachmentData = attachmentData; - markIdentityDataChanged(); -} - -void AvatarData::attach(const QString& modelURL, const QString& jointName, - const glm::vec3& translation, const glm::quat& rotation, - float scale, bool isSoft, - bool allowDuplicates, bool useSaved) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "attach", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName), - Q_ARG(const glm::vec3&, translation), Q_ARG(const glm::quat&, rotation), - Q_ARG(float, scale), Q_ARG(bool, isSoft), - Q_ARG(bool, allowDuplicates), Q_ARG(bool, useSaved)); - return; - } - QVector attachmentData = getAttachmentData(); - if (!allowDuplicates) { - foreach (const AttachmentData& data, attachmentData) { - if (data.modelURL == modelURL && (jointName.isEmpty() || data.jointName == jointName)) { - return; - } - } - } - AttachmentData data; - data.modelURL = modelURL; - data.jointName = jointName; - data.translation = translation; - data.rotation = rotation; - data.scale = scale; - data.isSoft = isSoft; - attachmentData.append(data); - setAttachmentData(attachmentData); -} - -void AvatarData::detachOne(const QString& modelURL, const QString& jointName) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "detachOne", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName)); - return; - } - QVector attachmentData = getAttachmentData(); - for (QVector::iterator it = attachmentData.begin(); it != attachmentData.end(); ++it) { - if (it->modelURL == modelURL && (jointName.isEmpty() || it->jointName == jointName)) { - attachmentData.erase(it); - setAttachmentData(attachmentData); - return; - } - } -} - -void AvatarData::detachAll(const QString& modelURL, const QString& jointName) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "detachAll", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName)); - return; - } - QVector attachmentData = getAttachmentData(); - for (QVector::iterator it = attachmentData.begin(); it != attachmentData.end(); ) { - if (it->modelURL == modelURL && (jointName.isEmpty() || it->jointName == jointName)) { - it = attachmentData.erase(it); - } else { - ++it; - } - } - setAttachmentData(attachmentData); -} - int AvatarData::sendAvatarDataPacket(bool sendAll) { auto nodeList = DependencyManager::get(); @@ -2495,149 +2401,6 @@ int AvatarData::sendIdentityPacket() { return identityData.size(); } -static const QString JSON_ATTACHMENT_URL = QStringLiteral("modelUrl"); -static const QString JSON_ATTACHMENT_JOINT_NAME = QStringLiteral("jointName"); -static const QString JSON_ATTACHMENT_TRANSFORM = QStringLiteral("transform"); -static const QString JSON_ATTACHMENT_IS_SOFT = QStringLiteral("isSoft"); - -QJsonObject AttachmentData::toJson() const { - QJsonObject result; - if (modelURL.isValid() && !modelURL.isEmpty()) { - result[JSON_ATTACHMENT_URL] = modelURL.toString(); - } - if (!jointName.isEmpty()) { - result[JSON_ATTACHMENT_JOINT_NAME] = jointName; - } - // FIXME the transform constructor that takes rot/scale/translation - // doesn't return the correct value for isIdentity() - Transform transform; - transform.setRotation(rotation); - transform.setScale(scale); - transform.setTranslation(translation); - if (!transform.isIdentity()) { - result[JSON_ATTACHMENT_TRANSFORM] = Transform::toJson(transform); - } - result[JSON_ATTACHMENT_IS_SOFT] = isSoft; - return result; -} - -void AttachmentData::fromJson(const QJsonObject& json) { - if (json.contains(JSON_ATTACHMENT_URL)) { - const QString modelURLTemp = json[JSON_ATTACHMENT_URL].toString(); - if (modelURLTemp != modelURL.toString()) { - modelURL = modelURLTemp; - } - } - - if (json.contains(JSON_ATTACHMENT_JOINT_NAME)) { - const QString jointNameTemp = json[JSON_ATTACHMENT_JOINT_NAME].toString(); - if (jointNameTemp != jointName) { - jointName = jointNameTemp; - } - } - - if (json.contains(JSON_ATTACHMENT_TRANSFORM)) { - Transform transform = Transform::fromJson(json[JSON_ATTACHMENT_TRANSFORM]); - translation = transform.getTranslation(); - rotation = transform.getRotation(); - scale = transform.getScale().x; - } - - if (json.contains(JSON_ATTACHMENT_IS_SOFT)) { - isSoft = json[JSON_ATTACHMENT_IS_SOFT].toBool(); - } -} - -bool AttachmentData::operator==(const AttachmentData& other) const { - return modelURL == other.modelURL && jointName == other.jointName && translation == other.translation && - rotation == other.rotation && scale == other.scale && isSoft == other.isSoft; -} - -QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment) { - return out << attachment.modelURL << attachment.jointName << - attachment.translation << attachment.rotation << attachment.scale << attachment.isSoft; -} - -QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) { - return in >> attachment.modelURL >> attachment.jointName >> - attachment.translation >> attachment.rotation >> attachment.scale >> attachment.isSoft; -} - -void AttachmentDataObject::setModelURL(const QString& modelURL) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.modelURL = modelURL; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -QString AttachmentDataObject::getModelURL() const { - return scriptvalue_cast(thisObject()).modelURL.toString(); -} - -void AttachmentDataObject::setJointName(const QString& jointName) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.jointName = jointName; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -QString AttachmentDataObject::getJointName() const { - return scriptvalue_cast(thisObject()).jointName; -} - -void AttachmentDataObject::setTranslation(const glm::vec3& translation) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.translation = translation; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -glm::vec3 AttachmentDataObject::getTranslation() const { - return scriptvalue_cast(thisObject()).translation; -} - -void AttachmentDataObject::setRotation(const glm::quat& rotation) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.rotation = rotation; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -glm::quat AttachmentDataObject::getRotation() const { - return scriptvalue_cast(thisObject()).rotation; -} - -void AttachmentDataObject::setScale(float scale) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.scale = scale; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -float AttachmentDataObject::getScale() const { - return scriptvalue_cast(thisObject()).scale; -} - -void AttachmentDataObject::setIsSoft(bool isSoft) { - AttachmentData data = scriptvalue_cast(thisObject()); - data.isSoft = isSoft; - Q_ASSERT(engine()); - thisObject() = engine()->toScriptValue(data); -} - -bool AttachmentDataObject::getIsSoft() const { - return scriptvalue_cast(thisObject()).isSoft; -} - -void registerAvatarTypes(ScriptEngine* engine) { - scriptRegisterSequenceMetaType >(engine); -} - -void registerAvatarPrototypes(ScriptEngine* engine) { - engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( - new AttachmentDataObject(), ScriptEngine::ScriptOwnership)); -} - void AvatarData::setRecordingBasis(std::shared_ptr recordingBasis) { if (!recordingBasis) { recordingBasis = std::make_shared(); @@ -2670,7 +2433,6 @@ static const QString JSON_AVATAR_HEAD_MODEL = QStringLiteral("headModel"); static const QString JSON_AVATAR_BODY_MODEL = QStringLiteral("bodyModel"); static const QString JSON_AVATAR_DISPLAY_NAME = QStringLiteral("displayName"); // It isn't meaningful to persist sessionDisplayName. -static const QString JSON_AVATAR_ATTACHMENTS = QStringLiteral("attachments"); static const QString JSON_AVATAR_ENTITIES = QStringLiteral("attachedEntities"); static const QString JSON_AVATAR_SCALE = QStringLiteral("scale"); static const QString JSON_AVATAR_VERSION = QStringLiteral("version"); @@ -2838,24 +2600,11 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { setTargetScale((float)json[JSON_AVATAR_SCALE].toDouble()); } - QVector attachments; - if (json.contains(JSON_AVATAR_ATTACHMENTS) && json[JSON_AVATAR_ATTACHMENTS].isArray()) { - QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHMENTS].toArray(); - for (auto attachmentJson : attachmentsJson) { - AttachmentData attachment; - attachment.fromJson(attachmentJson.toObject()); - attachments.push_back(attachment); - } - } - if (attachments != getAttachmentData()) { - setAttachmentData(attachments); - } - if (json.contains(JSON_AVATAR_ENTITIES) && json[JSON_AVATAR_ENTITIES].isArray()) { - QJsonArray attachmentsJson = json[JSON_AVATAR_ENTITIES].toArray(); - for (auto attachmentJson : attachmentsJson) { - if (attachmentJson.isObject()) { - QVariantMap entityData = attachmentJson.toObject().toVariantMap(); + QJsonArray avatarEntitiesJSON = json[JSON_AVATAR_ENTITIES].toArray(); + for (auto avatarEntityJSON : avatarEntitiesJSON) { + if (avatarEntityJSON.isObject()) { + QVariantMap entityData = avatarEntityJSON.toObject().toVariantMap(); QUuid id = entityData.value("id").toUuid(); QByteArray data = QByteArray::fromBase64(entityData.value("properties").toByteArray()); updateAvatarEntity(id, data); @@ -2970,30 +2719,6 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const return glm::vec3(); } -/*@jsdoc - * Information on an attachment worn by the avatar. - * @typedef {object} AttachmentData - * @property {string} modelUrl - The URL of the glTF, FBX, or OBJ model file. glTF models may be in JSON or binary format - * (".gltf" or ".glb" URLs respectively). - * @property {string} jointName - The name of the joint that the attachment is parented to. - * @property {Vec3} translation - The offset from the joint that the attachment is positioned at. - * @property {Vec3} rotation - The rotation applied to the model relative to the joint orientation. - * @property {number} scale - The scale applied to the attachment model. - * @property {boolean} soft - If true and the model has a skeleton, the bones of the attached model's skeleton are - * rotated to fit the avatar's current pose. If true, the translation, rotation, and - * scale parameters are ignored. - */ -QVariant AttachmentData::toVariant() const { - QVariantMap result; - result["modelUrl"] = modelURL; - result["jointName"] = jointName; - result["translation"] = vec3ToQMap(translation); - result["rotation"] = vec3ToQMap(glm::degrees(safeEulerAngles(rotation))); - result["scale"] = scale; - result["soft"] = isSoft; - return result; -} - glm::vec3 variantToVec3(const QVariant& var) { auto map = var.toMap(); glm::vec3 result; @@ -3003,52 +2728,6 @@ glm::vec3 variantToVec3(const QVariant& var) { return result; } -bool AttachmentData::fromVariant(const QVariant& variant) { - bool isValid = false; - auto map = variant.toMap(); - if (map.contains("modelUrl")) { - auto urlString = map["modelUrl"].toString(); - modelURL = urlString; - isValid = true; - } - if (map.contains("jointName")) { - jointName = map["jointName"].toString(); - } - if (map.contains("translation")) { - translation = variantToVec3(map["translation"]); - } - if (map.contains("rotation")) { - rotation = glm::quat(glm::radians(variantToVec3(map["rotation"]))); - } - if (map.contains("scale")) { - scale = map["scale"].toFloat(); - } - if (map.contains("soft")) { - isSoft = map["soft"].toBool(); - } - return isValid; -} - -QVariantList AvatarData::getAttachmentsVariant() const { - QVariantList result; - for (const auto& attachment : getAttachmentData()) { - result.append(attachment.toVariant()); - } - return result; -} - -void AvatarData::setAttachmentsVariant(const QVariantList& variant) { - QVector newAttachments; - newAttachments.reserve(variant.size()); - for (const auto& attachmentVar : variant) { - AttachmentData attachment; - if (attachment.fromVariant(attachmentVar)) { - newAttachments.append(attachment); - } - } - setAttachmentData(newAttachments); -} - void AvatarData::storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& data) { bool changed = false; _avatarEntitiesLock.withWriteLock([&] { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index d3bf8a3282..52f2878983 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -451,7 +451,6 @@ Q_DECLARE_METATYPE(KillAvatarReason); class QDataStream; -class AttachmentData; class Transform; using TransformPointer = std::shared_ptr; @@ -523,8 +522,6 @@ class AvatarData : public QObject, public SpatiallyNestable { * @property {boolean} lookAtSnappingEnabled=true - true if the avatar's eyes snap to look at another avatar's * eyes when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. * @property {string} skeletonModelURL - The avatar's FST file. - * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments. - *

Deprecated: This property is deprecated and will be removed. Use avatar entities instead.

* @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the @@ -580,7 +577,6 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName WRITE setSessionDisplayName NOTIFY sessionDisplayNameChanged) Q_PROPERTY(bool lookAtSnappingEnabled MEMBER _lookAtSnappingEnabled NOTIFY lookAtSnappingChanged) Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript NOTIFY skeletonModelURLChanged) - Q_PROPERTY(QVector attachmentData READ getAttachmentData WRITE setAttachmentData) Q_PROPERTY(QStringList jointNames READ getJointNames) @@ -1145,27 +1141,6 @@ public: */ Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); } - - /*@jsdoc - * Gets information about the models currently attached to your avatar. - * @function Avatar.getAttachmentsVariant - * @returns {AttachmentData[]} Information about all models attached to your avatar. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - */ - // FIXME: Can this name be improved? Can it be deprecated? - Q_INVOKABLE virtual QVariantList getAttachmentsVariant() const; - - /*@jsdoc - * Sets all models currently attached to your avatar. For example, if you retrieve attachment data using - * {@link MyAvatar.getAttachmentsVariant} or {@link Avatar.getAttachmentsVariant}, make changes to it, and then want to - * update your avatar's attachments per the changed data. - * @function Avatar.setAttachmentsVariant - * @param {AttachmentData[]} variant - The attachment data defining the models to have attached to your avatar. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - */ - // FIXME: Can this name be improved? Can it be deprecated? - Q_INVOKABLE virtual void setAttachmentsVariant(const QVariantList& variant); - virtual void storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& payload); /*@jsdoc @@ -1209,7 +1184,6 @@ public: const HeadData* getHeadData() const { return _headData; } struct Identity { - QVector attachmentData; QString displayName; QString sessionDisplayName; bool isReplicated; @@ -1254,109 +1228,6 @@ public: } virtual bool isCertifyFailed() const { return _verificationFailed; } - /*@jsdoc - * Gets information about the models currently attached to your avatar. - * @function Avatar.getAttachmentData - * @returns {AttachmentData[]} Information about all models attached to your avatar. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - * @example Report the URLs of all current attachments. - * var attachments = MyAvatar.getaAttachmentData(); - * for (var i = 0; i < attachments.length; i++) { - * print(attachments[i].modelURL); - * } - * - * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". - */ - Q_INVOKABLE virtual QVector getAttachmentData() const; - - /*@jsdoc - * Sets all models currently attached to your avatar. For example, if you retrieve attachment data using - * {@link MyAvatar.getAttachmentData} or {@link Avatar.getAttachmentData}, make changes to it, and then want to update your avatar's attachments per the - * changed data. You can also remove all attachments by using setting attachmentData to null. - * @function Avatar.setAttachmentData - * @param {AttachmentData[]} attachmentData - The attachment data defining the models to have attached to your avatar. Use - * null to remove all attachments. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - * @example Remove a hat attachment if your avatar is wearing it. - * var hatURL = "https://apidocs.overte.org/examples/cowboy-hat.fbx"; - * var attachments = MyAvatar.getAttachmentData(); - * - * for (var i = 0; i < attachments.length; i++) { - * if (attachments[i].modelURL === hatURL) { - * attachments.splice(i, 1); - * MyAvatar.setAttachmentData(attachments); - * break; - * } - * } - * - * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". - */ - Q_INVOKABLE virtual void setAttachmentData(const QVector& attachmentData); - - /*@jsdoc - * Attaches a model to your avatar. For example, you can give your avatar a hat to wear, a guitar to hold, or a surfboard to - * stand on. - * @function Avatar.attach - * @param {string} modelURL - The URL of the glTF, FBX, or OBJ model to attach. glTF models may be in JSON or binary format - * (".gltf" or ".glb" URLs respectively). - * @param {string} [jointName=""] - The name of the avatar joint (see {@link MyAvatar.getJointNames} or - * {@link Avatar.getJointNames}) to attach the model to. - * @param {Vec3} [translation=Vec3.ZERO] - The offset to apply to the model relative to the joint position. - * @param {Quat} [rotation=Quat.IDENTITY] - The rotation to apply to the model relative to the joint orientation. - * @param {number} [scale=1.0] - The scale to apply to the model. - * @param {boolean} [isSoft=false] - If the model has a skeleton, set this to true so that the bones of the - * attached model's skeleton are rotated to fit the avatar's current pose. isSoft is used, for example, - * to have clothing that moves with the avatar. - *

If true, the translation, rotation, and scale parameters are - * ignored.

- * @param {boolean} [allowDuplicates=false] - If true then more than one copy of any particular model may be - * attached to the same joint; if false then the same model cannot be attached to the same joint. - * @param {boolean} [useSaved=true] - Not used. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - * @example Attach a cowboy hat to your avatar's head. - * var attachment = { - * modelURL: "https://apidocs.overte.org/examples/cowboy-hat.fbx", - * jointName: "Head", - * translation: {"x": 0, "y": 0.25, "z": 0}, - * rotation: {"x": 0, "y": 0, "z": 0, "w": 1}, - * scale: 0.01, - * isSoft: false - * }; - * - * MyAvatar.attach(attachment.modelURL, - * attachment.jointName, - * attachment.translation, - * attachment.rotation, - * attachment.scale, - * attachment.isSoft); - * - * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". - */ - Q_INVOKABLE virtual void attach(const QString& modelURL, const QString& jointName = QString(), - const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), - float scale = 1.0f, bool isSoft = false, - bool allowDuplicates = false, bool useSaved = true); - - /*@jsdoc - * Detaches the most recently attached instance of a particular model from either a specific joint or any joint. - * @function Avatar.detachOne - * @param {string} modelURL - The URL of the model to detach. - * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the most - * recently attached model is removed from which ever joint it was attached to. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - */ - Q_INVOKABLE virtual void detachOne(const QString& modelURL, const QString& jointName = QString()); - - /*@jsdoc - * Detaches all instances of a particular model from either a specific joint or all joints. - * @function Avatar.detachAll - * @param {string} modelURL - The URL of the model to detach. - * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the model is - * detached from all joints. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - */ - Q_INVOKABLE virtual void detachAll(const QString& modelURL, const QString& jointName = QString()); - QString getSkeletonModelURLFromScript() const; void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); } @@ -1732,8 +1603,6 @@ protected: mutable HeadData* _headData { nullptr }; QUrl _skeletonModelURL; - QVector _attachmentData; - QVector _oldAttachmentData; QString _displayName; QString _sessionDisplayName { }; bool _lookAtSnappingEnabled { true }; @@ -1899,66 +1768,6 @@ Q_DECLARE_METATYPE(AvatarData*) QJsonValue toJsonValue(const JointData& joint); JointData jointDataFromJsonValue(const QJsonValue& q); -class AttachmentData { -public: - QUrl modelURL; - QString jointName; - glm::vec3 translation; - glm::quat rotation; - float scale { 1.0f }; - bool isSoft { false }; - - bool isValid() const { return modelURL.isValid(); } - - bool operator==(const AttachmentData& other) const; - - QJsonObject toJson() const; - void fromJson(const QJsonObject& json); - - QVariant toVariant() const; - bool fromVariant(const QVariant& variant); -}; - -QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment); -QDataStream& operator>>(QDataStream& in, AttachmentData& attachment); - -Q_DECLARE_METATYPE(AttachmentData) -Q_DECLARE_METATYPE(QVector) - -/// Scriptable wrapper for attachments. -class AttachmentDataObject : public QObject, protected Scriptable { - Q_OBJECT - Q_PROPERTY(QString modelURL READ getModelURL WRITE setModelURL) - Q_PROPERTY(QString jointName READ getJointName WRITE setJointName) - Q_PROPERTY(glm::vec3 translation READ getTranslation WRITE setTranslation) - Q_PROPERTY(glm::quat rotation READ getRotation WRITE setRotation) - Q_PROPERTY(float scale READ getScale WRITE setScale) - Q_PROPERTY(bool isSoft READ getIsSoft WRITE setIsSoft) - -public: - - Q_INVOKABLE void setModelURL(const QString& modelURL); - Q_INVOKABLE QString getModelURL() const; - - Q_INVOKABLE void setJointName(const QString& jointName); - Q_INVOKABLE QString getJointName() const; - - Q_INVOKABLE void setTranslation(const glm::vec3& translation); - Q_INVOKABLE glm::vec3 getTranslation() const; - - Q_INVOKABLE void setRotation(const glm::quat& rotation); - Q_INVOKABLE glm::quat getRotation() const; - - Q_INVOKABLE void setScale(float scale); - Q_INVOKABLE float getScale() const; - - Q_INVOKABLE void setIsSoft(bool scale); - Q_INVOKABLE bool getIsSoft() const; -}; - -void registerAvatarTypes(ScriptEngine* engine); -void registerAvatarPrototypes(ScriptEngine* engine); - class RayToAvatarIntersectionResult { public: bool intersects { false }; diff --git a/libraries/avatars/src/ScriptAvatarData.cpp b/libraries/avatars/src/ScriptAvatarData.cpp index 1d93a6e954..43dc5097ef 100644 --- a/libraries/avatars/src/ScriptAvatarData.cpp +++ b/libraries/avatars/src/ScriptAvatarData.cpp @@ -200,7 +200,7 @@ bool ScriptAvatarData::getLookAtSnappingEnabled() const { // // -// ATTACHMENT AND JOINT PROPERTIES +// JOINT PROPERTIES // START // QString ScriptAvatarData::getSkeletonModelURLFromScript() const { @@ -285,15 +285,8 @@ QStringList ScriptAvatarData::getJointNames() const { return QStringList(); } } -QVector ScriptAvatarData::getAttachmentData() const { - if (AvatarSharedPointer sharedAvatarData = _avatarData.lock()) { - return sharedAvatarData->getAttachmentData(); - } else { - return QVector(); - } -} // -// ATTACHMENT AND JOINT PROPERTIES +// JOINT PROPERTIES // END // diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 8f9b7b77b1..960423a1ee 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -50,10 +50,9 @@ class ScriptAvatarData : public QObject { Q_PROPERTY(bool lookAtSnappingEnabled READ getLookAtSnappingEnabled NOTIFY lookAtSnappingChanged) // - // ATTACHMENT AND JOINT PROPERTIES + // JOINT PROPERTIES // Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript NOTIFY skeletonModelURLChanged) - Q_PROPERTY(QVector attachmentData READ getAttachmentData) Q_PROPERTY(QStringList jointNames READ getJointNames) // @@ -104,7 +103,7 @@ public: bool getLookAtSnappingEnabled() const; // - // ATTACHMENT AND JOINT PROPERTIES + // JOINT PROPERTIES // QString getSkeletonModelURLFromScript() const; @@ -204,15 +203,6 @@ public: */ Q_INVOKABLE QStringList getJointNames() const; - /*@jsdoc - * Gets information about the models currently attached to the avatar. - * @function ScriptAvatar.getAttachmentData - * @returns {AttachmentData[]} Information about all models attached to the avatar, or [] if the avatar data - * aren't available. - * @deprecated This function is deprecated and will be removed. Use avatar entities instead. - */ - Q_INVOKABLE QVector getAttachmentData() const; - #if DEV_BUILD || PR_BUILD Q_INVOKABLE AvatarEntityMap getAvatarEntities() const; #endif diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index c68651d42c..da76961e7d 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -454,7 +454,6 @@ bool EntityRenderer::addToScene(const ScenePointer& scene, Transaction& transact transaction.resetItem(_renderItemID, renderPayload); onAddToScene(_entity); updateInScene(scene, transaction); - _entity->bumpAncestorChainRenderableVersion(); return true; } @@ -462,7 +461,6 @@ void EntityRenderer::removeFromScene(const ScenePointer& scene, Transaction& tra onRemoveFromScene(_entity); transaction.removeItem(_renderItemID); Item::clearID(_renderItemID); - _entity->bumpAncestorChainRenderableVersion(); } void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& transaction) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 9ea1d2f942..1b54d1b3b7 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1261,7 +1261,6 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint if (!_hasModel) { if (model) { model->removeFromScene(scene, transaction); - entity->bumpAncestorChainRenderableVersion(); emit DependencyManager::get()-> modelRemovedFromScene(entity->getEntityItemID(), NestableType::Entity, model); withWriteLock([&] { _model.reset(); }); @@ -1391,7 +1390,6 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint makeStatusGetters(entity, statusGetters); using namespace std::placeholders; model->addToScene(scene, transaction, statusGetters, std::bind(&ModelEntityRenderer::metaBlendshapeOperator, _renderItemID, _1, _2, _3, _4)); - entity->bumpAncestorChainRenderableVersion(); processMaterials(); } } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index cc0d93604a..7037785c48 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2969,10 +2969,6 @@ void EntityItem::setVisible(bool value) { _needsRenderUpdate |= changed; _visible = value; }); - - if (changed) { - bumpAncestorChainRenderableVersion(); - } } bool EntityItem::isVisibleInSecondaryCamera() const { diff --git a/libraries/model-serializers/src/FSTReader.cpp b/libraries/model-serializers/src/FSTReader.cpp index d7c4b73048..7e84f012a7 100644 --- a/libraries/model-serializers/src/FSTReader.cpp +++ b/libraries/model-serializers/src/FSTReader.cpp @@ -183,7 +183,6 @@ QString FSTReader::getNameFromType(ModelType modelType) { _typesToNames[HEAD_MODEL] = "head"; _typesToNames[BODY_ONLY_MODEL] = "body"; _typesToNames[HEAD_AND_BODY_MODEL] = "body+head"; - _typesToNames[ATTACHMENT_MODEL] = "attachment"; } return _typesToNames[modelType]; } @@ -195,9 +194,6 @@ FSTReader::ModelType FSTReader::getTypeFromName(const QString& name) { _namesToTypes["head"] = HEAD_MODEL ; _namesToTypes["body"] = BODY_ONLY_MODEL; _namesToTypes["body+head"] = HEAD_AND_BODY_MODEL; - - // NOTE: this is not yet implemented, but will be used to allow you to attach fully independent models to your avatar - _namesToTypes["attachment"] = ATTACHMENT_MODEL; } return _namesToTypes[name]; } diff --git a/libraries/model-serializers/src/FSTReader.h b/libraries/model-serializers/src/FSTReader.h index 4ba0428e83..e1b7405346 100644 --- a/libraries/model-serializers/src/FSTReader.h +++ b/libraries/model-serializers/src/FSTReader.h @@ -42,8 +42,7 @@ public: ENTITY_MODEL, HEAD_MODEL, BODY_ONLY_MODEL, - HEAD_AND_BODY_MODEL, - ATTACHMENT_MODEL + HEAD_AND_BODY_MODEL }; /// Reads an FST mapping from the supplied data. diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index e561bfe21e..943c8cce71 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -38,10 +38,10 @@ PacketVersion versionForPacketType(PacketType packetType) { return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: case PacketType::AvatarData: - return static_cast(AvatarMixerPacketVersion::ARKitBlendshapes); + return static_cast(AvatarMixerPacketVersion::RemoveAttachments); case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::ARKitBlendshapes); + return static_cast(AvatarMixerPacketVersion::RemoveAttachments); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); // ICE packets diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index f8d80660f4..86c8e8193a 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -363,7 +363,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { FBXJointOrderChange, HandControllerSection, SendVerificationFailed, - ARKitBlendshapes + ARKitBlendshapes, + RemoveAttachments, }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/libraries/recording/src/recording/RecordingScriptingInterface.cpp b/libraries/recording/src/recording/RecordingScriptingInterface.cpp index a05ee60604..a50453bb69 100644 --- a/libraries/recording/src/recording/RecordingScriptingInterface.cpp +++ b/libraries/recording/src/recording/RecordingScriptingInterface.cpp @@ -172,10 +172,6 @@ void RecordingScriptingInterface::setPlayerUseDisplayName(bool useDisplayName) { _useDisplayName = useDisplayName; } -void RecordingScriptingInterface::setPlayerUseAttachments(bool useAttachments) { - _useAttachments = useAttachments; -} - void RecordingScriptingInterface::setPlayerUseHeadModel(bool useHeadModel) { _useHeadModel = useHeadModel; } diff --git a/libraries/recording/src/recording/RecordingScriptingInterface.h b/libraries/recording/src/recording/RecordingScriptingInterface.h index 394c3e230d..2f84b9109a 100644 --- a/libraries/recording/src/recording/RecordingScriptingInterface.h +++ b/libraries/recording/src/recording/RecordingScriptingInterface.h @@ -162,14 +162,6 @@ public slots: */ void setPlayerUseDisplayName(bool useDisplayName); - /*@jsdoc - *

Not used.

- * @function Recording.setPlayerUseAttachments - * @param {boolean} useAttachments - Use attachments. - * @deprecated This method is deprecated and will be removed. - */ - void setPlayerUseAttachments(bool useAttachments); - /*@jsdoc *

Not used.

* @function Recording.setPlayerUseHeadModel @@ -203,14 +195,6 @@ public slots: */ bool getPlayerUseDisplayName() { return _useDisplayName; } - /*@jsdoc - *

Not used.

- * @function Recording.getPlayerUseAttachments - * @returns {boolean} Use attachments. - * @deprecated This method is deprecated and will be removed. - */ - bool getPlayerUseAttachments() { return _useAttachments; } - /*@jsdoc *

Not used.

* @function Recording.getPlayerUseHeadModel @@ -365,7 +349,6 @@ protected: Flag _playFromCurrentLocation { true }; Flag _useDisplayName { false }; Flag _useHeadModel { false }; - Flag _useAttachments { false }; Flag _useSkeletonModel { false }; recording::ClipPointer _lastClip; diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp deleted file mode 100644 index 24d6081743..0000000000 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// -// Created by Anthony J. Thibault on 12/17/15. -// Copyright 2013 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 "SoftAttachmentModel.h" - -SoftAttachmentModel::SoftAttachmentModel(QObject* parent, const Rig& rigOverride) : - CauterizedModel(parent), - _rigOverride(rigOverride) { -} - -SoftAttachmentModel::~SoftAttachmentModel() { -} - -// virtual -void SoftAttachmentModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - _needsUpdateClusterMatrices = true; -} - -int SoftAttachmentModel::getJointIndexOverride(int i) const { - QString name = _rig.nameOfJoint(i); - if (name.isEmpty()) { - return -1; - } - return _rigOverride.indexOfJoint(name); -} - -// virtual -// use the _rigOverride matrices instead of the Model::_rig -void SoftAttachmentModel::updateClusterMatrices() { - if (!_needsUpdateClusterMatrices) { - return; - } - if (!isLoaded()) { - return; - } - - _needsUpdateClusterMatrices = false; - - const HFMModel& hfmModel = getHFMModel(); - - for (int i = 0; i < (int) _meshStates.size(); i++) { - MeshState& state = _meshStates[i]; - const HFMMesh& mesh = hfmModel.meshes.at(i); - int meshIndex = i; - for (int j = 0; j < mesh.clusters.size(); j++) { - const HFMCluster& cluster = mesh.clusters.at(j); - - int clusterIndex = j; - // TODO: cache these look-ups as an optimization - int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); - glm::mat4 jointMatrix; - if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { - jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); - } else { - jointMatrix = _rig.getJointTransform(cluster.jointIndex); - } - if (_useDualQuaternionSkinning) { - glm::mat4 m; - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, m); - state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(m); - } else { - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); - } - } - } - - updateBlendshapes(); -} diff --git a/libraries/render-utils/src/SoftAttachmentModel.h b/libraries/render-utils/src/SoftAttachmentModel.h deleted file mode 100644 index 4335c1634e..0000000000 --- a/libraries/render-utils/src/SoftAttachmentModel.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// Created by Anthony J. Thibault on 12/17/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_SoftAttachmentModel_h -#define hifi_SoftAttachmentModel_h - -#include "CauterizedModel.h" - -// A model that allows the creator to specify a secondary rig instance. -// When the cluster matrices are created for rendering, the -// cluster matrices will use the secondary rig for the joint poses -// instead of the primary rig. -// -// This is used by Avatar instances to wear clothing that follows the same -// animated pose as the SkeletonModel. - -class SoftAttachmentModel : public CauterizedModel { - Q_OBJECT - -public: - SoftAttachmentModel(QObject* parent, const Rig& rigOverride); - ~SoftAttachmentModel(); - - void updateRig(float deltaTime, glm::mat4 parentTransform) override; - void updateClusterMatrices() override; - -protected: - int getJointIndexOverride(int i) const; - - const Rig& _rigOverride; -}; - -#endif // hifi_SoftAttachmentModel_h diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 7a02c2dbbb..d8395c8739 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -69,7 +69,6 @@ const QUuid SpatiallyNestable::getParentID() const { } void SpatiallyNestable::setParentID(const QUuid& parentID) { - bumpAncestorChainRenderableVersion(); bool success = false; auto parent = getParentPointer(success); bool parentChanged = false; @@ -89,7 +88,6 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) { success = false; parent = getParentPointer(success); if (success && parent) { - bumpAncestorChainRenderableVersion(); parent->updateQueryAACube(); parent->recalculateChildCauterization(); } @@ -1509,17 +1507,3 @@ QUuid SpatiallyNestable::getEditSenderID() { }); return editSenderID; } - -void SpatiallyNestable::bumpAncestorChainRenderableVersion(int depth) const { - if (depth > MAX_PARENTING_CHAIN_SIZE) { - // can't break the parent chain here, because it will call setParentID, which calls this - return; - } - - _ancestorChainRenderableVersion++; - bool success = false; - auto parent = getParentPointer(success); - if (success && parent) { - parent->bumpAncestorChainRenderableVersion(depth + 1); - } -} diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 29f23afdfb..a04ad62a3a 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -224,8 +224,6 @@ public: bool hasGrabs(); virtual QUuid getEditSenderID(); - void bumpAncestorChainRenderableVersion(int depth = 0) const; - protected: QUuid _id; mutable SpatiallyNestableWeakPointer _parent; @@ -248,8 +246,6 @@ protected: mutable ReadWriteLockable _grabsLock; QSet _grabs; // upon this thing - mutable std::atomic _ancestorChainRenderableVersion { 0 }; - private: SpatiallyNestable() = delete; const NestableType _nestableType; // EntityItem or an AvatarData From 9d2e03a5a4898f815fc67b700aa9b157462bf8b1 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 19 Jul 2024 17:31:51 -0700 Subject: [PATCH 9/9] fix non-localOnly positional sounds not updating --- libraries/entities/src/SimpleEntitySimulation.cpp | 4 ++-- libraries/entities/src/SoundEntityItem.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index d64efdf87f..ecdc37ebfb 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -62,7 +62,7 @@ void SimpleEntitySimulation::addEntityToInternalLists(EntityItemPointer entity) // we don't allow dynamic objects to move without an owner so nothing to do here } else if (entity->isMovingRelativeToParent()) { SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); - if (itr != _simpleKinematicEntities.end()) { + if (itr == _simpleKinematicEntities.end()) { _simpleKinematicEntities.insert(entity); entity->setLastSimulated(usecTimestampNow()); } @@ -73,7 +73,7 @@ void SimpleEntitySimulation::addEntityToInternalLists(EntityItemPointer entity) if (entity->isMovingRelativeToParent()) { SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity); - if (itr != _simpleKinematicEntities.end()) { + if (itr == _simpleKinematicEntities.end()) { _simpleKinematicEntities.insert(entity); entity->setLastSimulated(usecTimestampNow()); } diff --git a/libraries/entities/src/SoundEntityItem.cpp b/libraries/entities/src/SoundEntityItem.cpp index 71845920f9..d8c648a61a 100644 --- a/libraries/entities/src/SoundEntityItem.cpp +++ b/libraries/entities/src/SoundEntityItem.cpp @@ -359,8 +359,8 @@ void SoundEntityItem::setLocalOnly(bool value) { if (_injector) { DependencyManager::get()->stop(_injector); + _injector = nullptr; } - _injector = nullptr; } if (_sound) { @@ -409,7 +409,7 @@ bool SoundEntityItem::restartSound(bool lock) { options.ambisonic = _sound->isAmbisonic(); if (_injector) { - DependencyManager::get()->setOptionsAndRestart(_injector, options); + DependencyManager::get()->setOptions(_injector, options); } else { _injector = DependencyManager::get()->playSound(_sound, options); } @@ -431,8 +431,8 @@ void SoundEntityItem::updateSound(bool restart) { if (restart) { if (_injector) { DependencyManager::get()->stop(_injector); + _injector = nullptr; } - _injector = nullptr; } if (_playing) { @@ -440,6 +440,7 @@ void SoundEntityItem::updateSound(bool restart) { } else { if (_injector) { DependencyManager::get()->stop(_injector); + _injector = nullptr; } } }