Merge pull request #4860 from howard-stearns/entity-sound

Entity sound.
This commit is contained in:
Brad Hefta-Gaub 2015-05-13 20:07:44 -07:00
commit 5da0a8d7a3
12 changed files with 108 additions and 2 deletions

View file

@ -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 @@
</span>
</div>
<div class="property">
<div class="label">Collision Sound URL</div>
<div class="value">
<input id="property-collision-sound-url" class="url"></input>
</div>
</div>
<div class="property">
<div class="label">Lifetime</div>
<div class="value">

View file

@ -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++;

View file

@ -26,6 +26,7 @@
#include <SceneScriptingInterface.h>
#include <ScriptEngine.h>
#include <TextureCache.h>
#include <SoundCache.h>
#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<SoundCache>().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<EntityTree*>(_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<NodeList>()->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;

View file

@ -20,6 +20,7 @@
#include <MouseEvent.h>
#include <OctreeRenderer.h>
#include <ScriptCache.h>
#include <AbstractAudioInterface.h>
class AbstractScriptingServicesInterface;
class AbstractViewStateInterface;
@ -155,6 +156,9 @@ private:
QHash<EntityItemID, EntityScriptDetails> _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;

View file

@ -19,6 +19,7 @@
#include <PhysicsHelpers.h>
#include <RegisteredMetaTypes.h>
#include <SharedUtil.h> // usecTimestampNow()
#include <SoundCache.h>
#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, appendValue, getSimulatorID());
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, getMarketplaceID());
APPEND_ENTITY_PROPERTY(PROP_NAME, appendValue, getName());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, appendValue, getCollisionSoundURL());
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
requestedProperties,
@ -573,6 +577,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
}
READ_ENTITY_PROPERTY_STRING(PROP_NAME, setName);
READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_SOUND_URL, setCollisionSoundURL);
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
@ -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);

View file

@ -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;

View file

@ -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);
@ -367,6 +369,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);
@ -469,6 +472,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);
@ -735,6 +739,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, properties.getMarketplaceID());
APPEND_ENTITY_PROPERTY(PROP_NAME, appendValue, properties.getName());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, appendValue, properties.getCollisionSoundURL());
}
if (propertyCount > 0) {
int endOfEntityItemData = packetData->getUncompressedByteOffset();
@ -988,6 +993,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MARKETPLACE_ID, setMarketplaceID);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_NAME, setName);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COLLISION_SOUND_URL,setCollisionSoundURL);
return valid;
}
@ -1034,6 +1040,7 @@ void EntityItemProperties::markAllChanged() {
_userDataChanged = true;
_simulatorIDChanged = true;
_scriptChanged = true;
_collisionSoundURLChanged = true;
_registrationPointChanged = true;
_angularVelocityChanged = true;
_angularDampingChanged = true;

View file

@ -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, "");

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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