diff --git a/examples/grab.js b/examples/grab.js index 6bdf218ce2..019ec2320f 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -76,7 +76,7 @@ function mousePressEvent(event) { return; } var pickRay = Camera.computePickRay(event.x, event.y); - var intersection = Entities.findRayIntersection(pickRay); + var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking if (intersection.intersects && intersection.properties.collisionsWillMove) { grabbedEntity = intersection.entityID; var props = Entities.getEntityProperties(grabbedEntity) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 4ce787d78a..25467f7573 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -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"); @@ -437,6 +438,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; @@ -622,6 +624,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')); @@ -999,6 +1003,13 @@ +
+
Collision Sound URL
+
+ +
+
+
Lifetime
diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index 5c6c688f0d..f9f8d57a51 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 cd2e339653..deba442c94 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" @@ -46,6 +47,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf _wantScripts(wantScripts), _entitiesScriptEngine(NULL), _sandboxScriptEngine(NULL), + _localAudioInterface(NULL), _lastMouseEventValid(false), _viewState(viewState), _scriptingServices(scriptingServices), @@ -1098,13 +1100,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(); + const 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; + const float COLLISION_ENERGY_AT_FULL_VOLUME = 10.0f; + const 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. + const 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 23d7608fe0..470426d55e 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; @@ -237,6 +240,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, getSimulatorID()); APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID()); APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); + APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL()); appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -573,7 +577,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } READ_ENTITY_PROPERTY(PROP_NAME, QString, setName); - + READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); //////////////////////////////////// @@ -898,6 +902,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 +935,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 de6a8e30b6..2d68a20022 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, ""), @@ -288,6 +289,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); @@ -405,6 +407,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(particleRadius); COPY_PROPERTY_TO_QSCRIPTVALUE(marketplaceID); COPY_PROPERTY_TO_QSCRIPTVALUE(name); + COPY_PROPERTY_TO_QSCRIPTVALUE(collisionSoundURL); COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightColor); COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightIntensity); @@ -507,6 +510,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE(particleRadius, float, setParticleRadius); COPY_PROPERTY_FROM_QSCRIPTVALUE(marketplaceID, QString, setMarketplaceID); COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName); + COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightColor, xColor, setKeyLightColor); COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightIntensity, float, setKeyLightIntensity); @@ -735,6 +739,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID()); APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); + APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL()); } if (propertyCount > 0) { int endOfEntityItemData = packetData->getUncompressedByteOffset(); @@ -988,7 +993,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); - + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); + return valid; } @@ -1034,6 +1040,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 a1ca4d5eff..7d8cbfe9b1 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -97,6 +97,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); @@ -244,6 +245,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 42032ec04d..66ec70ae60 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -106,6 +106,7 @@ enum EntityPropertyList { PROP_ACCELERATION, // all entities PROP_SIMULATOR_ID, // all entities PROP_NAME, // all entities + PROP_COLLISION_SOUND_URL, //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties ABOVE this line diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h index a5f39cb825..fc90b0c903 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/model/src/model/Stage.h @@ -71,14 +71,14 @@ public: EarthSunModel() { valid(); } protected: - double _scale = 1000.0; //Km + float _scale = 1000.0f; //Km double _earthRadius = 6360.0; Quat _surfaceOrientation; - double _longitude = 0.0; - double _latitude = 0.0; - double _altitude = 0.01; + float _longitude = 0.0f; + float _latitude = 0.0f; + float _altitude = 0.01f; mutable Vec3d _surfacePos; mutable Mat4d _worldToSurfaceMat; mutable Mat4d _surfaceToWorldMat; @@ -93,8 +93,8 @@ protected: mutable Mat4d _worldToEyeMat; mutable Mat4d _eyeToWorldMat; - double _sunLongitude = 0.0; - double _sunLatitude = 0.0; + float _sunLongitude = 0.0f; + float _sunLatitude = 0.0f; mutable Vec3d _sunDir; mutable Vec3d _surfaceSunDir; void updateSun() const; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 887634cf80..0dcd30a55c 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType packetType) { return 1; case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_HAVE_LINE_TYPE; + return VERSION_ENTITIES_HAVE_COLLISION_SOUND_URL; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 1dff5d7c79..01eb488c14 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -176,5 +176,6 @@ const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_SKYBOX = 21; const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_STAGE_HAS_AUTOMATIC_HOURDAY = 22; const PacketVersion VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES = 23; const PacketVersion VERSION_ENTITIES_HAVE_LINE_TYPE = 24; +const PacketVersion VERSION_ENTITIES_HAVE_COLLISION_SOUND_URL = 25; #endif // hifi_PacketHeaders_h