diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 7e6c72712c..776fedeab6 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -74,7 +74,7 @@ function createEmitTextPropertyUpdateFunction(propertyName) { return function() { var properties = {}; - properties[propertyName] = this.value; + properties[propertyName] = this.value; EventBridge.emitWebEvent( JSON.stringify({ type: "update", @@ -244,6 +244,7 @@ var elDensity = document.getElementById("property-density"); var elIgnoreForCollisions = document.getElementById("property-ignore-for-collisions"); var elCollisionsWillMove = document.getElementById("property-collisions-will-move"); + var elCollisionSoundURL = document.getElementById("property-collision-sound-url"); var elLifetime = document.getElementById("property-lifetime"); var elScriptURL = document.getElementById("property-script-url"); var elUserData = document.getElementById("property-user-data"); @@ -433,6 +434,7 @@ elDensity.value = properties.density.toFixed(2); elIgnoreForCollisions.checked = properties.ignoreForCollisions; elCollisionsWillMove.checked = properties.collisionsWillMove; + elCollisionSoundURL.value = properties.collisionSoundURL; elLifetime.value = properties.lifetime; elScriptURL.value = properties.script; elUserData.value = properties.userData; @@ -612,6 +614,8 @@ elDensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('density')); elIgnoreForCollisions.addEventListener('change', createEmitCheckedPropertyUpdateFunction('ignoreForCollisions')); elCollisionsWillMove.addEventListener('change', createEmitCheckedPropertyUpdateFunction('collisionsWillMove')); + elCollisionSoundURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionSoundURL')); + elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime')); elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script')); elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); @@ -987,6 +991,13 @@ +
+
Collision Sound URL
+
+ +
+
+
Lifetime
diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index 5c6c688f0d..06d674d2e7 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -185,6 +185,8 @@ EntityPropertyDialogBox = (function () { index++; array.push({ label: "Collisions Will Move:", type: "checkbox", value: properties.collisionsWillMove }); index++; + array.push({ label: "Collision Sound URL:", value: properties.collisionSoundURL }); + index++; array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) }); index++; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index beb8b27e48..9dbf37282a 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "EntityTreeRenderer.h" @@ -45,6 +46,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf _wantScripts(wantScripts), _entitiesScriptEngine(NULL), _sandboxScriptEngine(NULL), + _localAudioInterface(NULL), _lastMouseEventValid(false), _viewState(viewState), _scriptingServices(scriptingServices), @@ -1096,13 +1098,78 @@ void EntityTreeRenderer::changingEntityID(const EntityItemID& oldEntityID, const } } -void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, +void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision) { + EntityItem* entity = entityTree->findEntityByEntityItemID(id); + QUuid simulatorID = entity->getSimulatorID(); + if (simulatorID.isNull() || (simulatorID != myNodeID)) { + return; // Only one injector per simulation, please. + } + const QString& collisionSoundURL = entity->getCollisionSoundURL(); + if (collisionSoundURL.isEmpty()) { + return; + } + SharedSoundPointer sound = DependencyManager::get().data()->getSound(QUrl(collisionSoundURL)); + if (!sound->isReady()) { + return; + } + + const float mass = entity->computeMass(); + constexpr float COLLISION_PENTRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity() + const float linearVelocity = glm::length(collision.penetration) * COLLISION_PENTRATION_TO_VELOCITY; + const float energy = mass * linearVelocity * linearVelocity / 2.0f; + const glm::vec3 position = collision.contactPoint; + constexpr float COLLISION_ENERGY_AT_FULL_VOLUME = 10.0f; + constexpr float COLLISION_MINIMUM_VOLUME = 0.01f; + const float energyPercentOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); + //qCDebug(entitiesrenderer) << energyPercentOfFull << energy << " " << " " << linearVelocity << " " << mass; + if (energyPercentOfFull < COLLISION_MINIMUM_VOLUME) { + return; + } + // This is a hack. Quiet sound aren't really heard at all, so we compress everything to the range 0.5-1.0, if we play it all. + constexpr float COLLISION_SOUND_COMPRESSION = 0.5f; + const float volume = (energyPercentOfFull * COLLISION_SOUND_COMPRESSION) + (1.0f - COLLISION_SOUND_COMPRESSION); + //qCDebug(entitiesrenderer) << collisionSoundURL << " " << volume << " " << position << " " << sound->isStereo(); + + // This is quite similar to AudioScriptingInterface::playSound() and should probably be refactored. + AudioInjectorOptions options; + options.stereo = sound->isStereo(); + options.position = position; + options.volume = volume; + AudioInjector* injector = new AudioInjector(sound.data(), options); + injector->setLocalAudioInterface(_localAudioInterface); + QThread* injectorThread = new QThread(); + injectorThread->setObjectName("Audio Injector Thread"); + injector->moveToThread(injectorThread); + // start injecting when the injector thread starts + connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio); + // connect the right slots and signals for AudioInjector and thread cleanup + connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit); + connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater); + injectorThread->start(); +} + +void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { // If we don't have a tree, or we're in the process of shutting down, then don't // process these events. if (!_tree || _shuttingDown) { return; } + + // See if we should play sounds + EntityTree* entityTree = static_cast(_tree); + if (!entityTree->tryLockForRead()) { + // I don't know why this can happen, but if it does, + // the consequences are a deadlock, so bail. + qCDebug(entitiesrenderer) << "NOTICE: skipping collision."; + return; + } + const QUuid& myNodeID = DependencyManager::get()->getSessionUUID(); + playEntityCollisionSound(myNodeID, entityTree, idA, collision); + playEntityCollisionSound(myNodeID, entityTree, idB, collision); + entityTree->unlock(); + + // And now the entity scripts QScriptValue entityScriptA = loadEntityScript(idA); if (entityScriptA.property("collisionWithEntity").isValid()) { QScriptValueList args; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 20534c3e2b..eb8b44f5eb 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -20,6 +20,7 @@ #include #include #include +#include class AbstractScriptingServicesInterface; class AbstractViewStateInterface; @@ -155,6 +156,9 @@ private: QHash _entityScripts; + void playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision); + AbstractAudioInterface* _localAudioInterface; // So we can render collision sounds + bool _lastMouseEventValid; MouseEvent _lastMouseEvent; AbstractViewStateInterface* _viewState; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index ee7ca54a98..33908316d6 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -19,6 +19,7 @@ #include #include #include // usecTimestampNow() +#include #include "EntityScriptingInterface.h" #include "EntityItem.h" @@ -52,6 +53,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _damping(ENTITY_ITEM_DEFAULT_DAMPING), _lifetime(ENTITY_ITEM_DEFAULT_LIFETIME), _script(ENTITY_ITEM_DEFAULT_SCRIPT), + _collisionSoundURL(ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL), _registrationPoint(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT), _angularVelocity(ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY), _angularDamping(ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING), @@ -100,6 +102,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_DAMPING; requestedProperties += PROP_LIFETIME; requestedProperties += PROP_SCRIPT; + requestedProperties += PROP_COLLISION_SOUND_URL; requestedProperties += PROP_REGISTRATION_POINT; requestedProperties += PROP_ANGULAR_VELOCITY; requestedProperties += PROP_ANGULAR_DAMPING; @@ -226,6 +229,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, getDamping()); APPEND_ENTITY_PROPERTY(PROP_LIFETIME, appendValue, getLifetime()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, getScript()); + APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, appendValue, getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, appendValue, getRegistrationPoint()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, getAngularVelocity()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, getAngularDamping()); @@ -551,6 +555,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping); READ_ENTITY_PROPERTY_SETTER(PROP_LIFETIME, float, updateLifetime); READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT, setScript); + READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_SOUND_URL, setCollisionSoundURL); READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint); if (useMeters) { READ_ENTITY_PROPERTY_SETTER(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity); @@ -898,6 +903,7 @@ EntityItemProperties EntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(damping, getDamping); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime); COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionSoundURL, getCollisionSoundURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(registrationPoint, getRegistrationPoint); COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularVelocity, getAngularVelocity); COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularDamping, getAngularDamping); @@ -930,6 +936,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, updateDamping); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime); SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint); SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, updateAngularVelocity); SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, updateAngularDamping); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index d9096bf429..b9dc14251c 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -246,6 +246,8 @@ public: const QString& getScript() const { return _script; } void setScript(const QString& value) { _script = value; } + const QString& getCollisionSoundURL() const { return _collisionSoundURL; } + void setCollisionSoundURL(const QString& value) { _collisionSoundURL = value; } const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } /// registration point as ratio of entity @@ -375,6 +377,7 @@ protected: float _damping; float _lifetime; QString _script; + QString _collisionSoundURL; glm::vec3 _registrationPoint; glm::vec3 _angularVelocity; float _angularDamping; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index ff54ef88fe..ea8694ecb5 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -46,6 +46,7 @@ EntityItemProperties::EntityItemProperties() : CONSTRUCT_PROPERTY(damping, ENTITY_ITEM_DEFAULT_DAMPING), CONSTRUCT_PROPERTY(lifetime, ENTITY_ITEM_DEFAULT_LIFETIME), CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT), + CONSTRUCT_PROPERTY(collisionSoundURL, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL), CONSTRUCT_PROPERTY(color, ), CONSTRUCT_PROPERTY(modelURL, ""), CONSTRUCT_PROPERTY(compoundShapeURL, ""), @@ -287,6 +288,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_DAMPING, damping); CHECK_PROPERTY_CHANGE(PROP_LIFETIME, lifetime); CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script); + CHECK_PROPERTY_CHANGE(PROP_COLLISION_SOUND_URL, collisionSoundURL); CHECK_PROPERTY_CHANGE(PROP_COLOR, color); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); @@ -365,6 +367,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable } COPY_PROPERTY_TO_QSCRIPTVALUE(script); + COPY_PROPERTY_TO_QSCRIPTVALUE(collisionSoundURL); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(registrationPoint); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(angularVelocity); COPY_PROPERTY_TO_QSCRIPTVALUE(angularDamping); @@ -466,6 +469,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(damping, setDamping); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(lifetime, setLifetime); COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(script, setScript); + COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(collisionSoundURL, setCollisionSoundURL); COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(registrationPoint, setRegistrationPoint); COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(angularVelocity, setAngularVelocity); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(angularDamping, setAngularDamping); @@ -656,6 +660,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, properties.getDamping()); APPEND_ENTITY_PROPERTY(PROP_LIFETIME, appendValue, properties.getLifetime()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, properties.getScript()); + APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, appendValue, properties.getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, appendValue, properties.getRegistrationPoint()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, properties.getAngularVelocity()); @@ -911,6 +916,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DAMPING, float, setDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_SCRIPT,setScript); + READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COLLISION_SOUND_URL,setCollisionSoundURL); READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_COLOR, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity); @@ -1022,6 +1028,7 @@ void EntityItemProperties::markAllChanged() { _userDataChanged = true; _simulatorIDChanged = true; _scriptChanged = true; + _collisionSoundURLChanged = true; _registrationPointChanged = true; _angularVelocityChanged = true; _angularDampingChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 956065d069..4c3ceefa9a 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -96,6 +96,7 @@ public: DEFINE_PROPERTY(PROP_DAMPING, Damping, damping, float); DEFINE_PROPERTY(PROP_LIFETIME, Lifetime, lifetime, float); DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString); + DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString); DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor); DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString); DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); @@ -242,6 +243,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, Damping, damping, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifetime, lifetime, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionSoundURL, collisionSoundURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, CompoundShapeURL, compoundShapeURL, ""); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index bdc1fb37e6..ae44322377 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -30,6 +30,7 @@ const float ENTITY_ITEM_DEFAULT_GLOW_LEVEL = 0.0f; const bool ENTITY_ITEM_DEFAULT_VISIBLE = true; const QString ENTITY_ITEM_DEFAULT_SCRIPT = QString(""); +const QString ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL = QString(""); const glm::vec3 ENTITY_ITEM_DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f, 0.5f, 0.5f); // center const float ENTITY_ITEM_IMMORTAL_LIFETIME = -1.0f; /// special lifetime which means the entity lives for ever diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 93b8c76216..0489fd5eac 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -55,6 +55,7 @@ enum EntityPropertyList { PROP_DAMPING, PROP_LIFETIME, PROP_SCRIPT, + PROP_COLLISION_SOUND_URL, // these properties are supported by some derived classes PROP_COLOR,