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,