From 117a868bcf5d1a4413a2a93b3de0ee590fadd915 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Mon, 15 Sep 2014 17:49:48 -0700 Subject: [PATCH 01/19] head penumbra filter tuning with Freddy / filter defaults to on --- assignment-client/src/audio/AudioMixer.cpp | 34 +++++++++---------- .../resources/web/settings/describe.json | 2 +- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index bfe2f7e249..714cd0e3f3 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -321,9 +321,14 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* const float TWO_OVER_PI = 2.0f / PI; const float ZERO_DB = 1.0f; - const float NEGATIVE_ONE_DB = 0.891f; +// const float NEGATIVE_ONE_DB = 0.891f; const float NEGATIVE_THREE_DB = 0.708f; - + const float NEGATIVE_SIX_DB = 0.501f; + + const float FILTER_GAIN_AT_0 = ZERO_DB; // source is in front + const float FILTER_GAIN_AT_90 = NEGATIVE_SIX_DB; // source is incident to left or right ear + const float FILTER_GAIN_AT_180 = NEGATIVE_SIX_DB; // source is behind + const float FILTER_CUTOFF_FREQUENCY_HZ = 1000.0f; const float penumbraFilterFrequency = FILTER_CUTOFF_FREQUENCY_HZ; // constant frequency @@ -334,32 +339,25 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* // variable gain calculation broken down by quadrent if (bearingAngleToSource < -PI_OVER_TWO && bearingAngleToSource > -PI) { - // gainL(-pi/2,0b)->(-pi,-1db) penumbraFilterGainL = TWO_OVER_PI * - (ZERO_DB - NEGATIVE_ONE_DB) * (bearingAngleToSource + PI_OVER_TWO) + ZERO_DB; - // gainR(-pi/2,-3db)->(-pi,-1db) + (FILTER_GAIN_AT_0 - FILTER_GAIN_AT_180) * (bearingAngleToSource + PI_OVER_TWO) + FILTER_GAIN_AT_0; penumbraFilterGainR = TWO_OVER_PI * - (NEGATIVE_THREE_DB - NEGATIVE_ONE_DB) * (bearingAngleToSource + PI_OVER_TWO) + NEGATIVE_THREE_DB; + (FILTER_GAIN_AT_90 - FILTER_GAIN_AT_180) * (bearingAngleToSource + PI_OVER_TWO) + FILTER_GAIN_AT_90; } else if (bearingAngleToSource <= PI && bearingAngleToSource > PI_OVER_TWO) { - // gainL(+pi,-1db)->(pi/2,-3db) penumbraFilterGainL = TWO_OVER_PI * - (NEGATIVE_ONE_DB - NEGATIVE_THREE_DB) * (bearingAngleToSource - PI) + NEGATIVE_ONE_DB; - // gainR(+pi,-1db)->(pi/2,0db) + (FILTER_GAIN_AT_180 - FILTER_GAIN_AT_90) * (bearingAngleToSource - PI) + FILTER_GAIN_AT_180; penumbraFilterGainR = TWO_OVER_PI * - (NEGATIVE_ONE_DB - ZERO_DB) * (bearingAngleToSource - PI) + NEGATIVE_ONE_DB; + (FILTER_GAIN_AT_180 - FILTER_GAIN_AT_0) * (bearingAngleToSource - PI) + FILTER_GAIN_AT_180; } else if (bearingAngleToSource <= PI_OVER_TWO && bearingAngleToSource > 0) { - // gainL(+pi/2,-3db)->(0,0db) penumbraFilterGainL = TWO_OVER_PI * - (NEGATIVE_THREE_DB - ZERO_DB) * (bearingAngleToSource - PI_OVER_TWO) + NEGATIVE_THREE_DB; - // gainR(+p1/2,0db)->(0,0db) - penumbraFilterGainR = ZERO_DB; + (FILTER_GAIN_AT_90 - FILTER_GAIN_AT_0) * (bearingAngleToSource - PI_OVER_TWO) + FILTER_GAIN_AT_90; + penumbraFilterGainR = FILTER_GAIN_AT_0; } else { - // gainL(0,0db)->(-pi/2,0db) - penumbraFilterGainL = ZERO_DB; - // gainR(0,0db)->(-pi/2,-3db) + penumbraFilterGainL = FILTER_GAIN_AT_0; penumbraFilterGainR = TWO_OVER_PI * - (ZERO_DB - NEGATIVE_THREE_DB) * (bearingAngleToSource) + ZERO_DB; + (FILTER_GAIN_AT_0 - FILTER_GAIN_AT_90) * (bearingAngleToSource) + FILTER_GAIN_AT_0; } + #if 0 qDebug() << "avatar=" << listeningNodeStream diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index fee7ff21fc..2ea0aec0c7 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -67,7 +67,7 @@ "type": "checkbox", "label": "Enable Positional Filter", "help": "If enabled, positional audio stream uses lowpass filter", - "default": false + "default": true } } } From dbcb778ea9d84d242640ab09a4ae0a79f8c04c77 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 18 Sep 2014 08:33:41 -0700 Subject: [PATCH 02/19] Drive mouth with loudness relative to long term average --- examples/headMove.js | 2 +- interface/src/avatar/Head.cpp | 22 ++++++++++++++++------ interface/src/avatar/Head.h | 1 + 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/examples/headMove.js b/examples/headMove.js index 0a7686c569..f8a1895a08 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -79,7 +79,7 @@ function moveWithHead(deltaTime) { if (Math.abs(headDelta.z) > HEAD_MOVE_DEAD_ZONE) { if (Math.abs(Vec3.dot(velocity, forward)) < maxVelocity) { thrust = Vec3.sum(thrust, Vec3.multiply(forward, -headDelta.z * HEAD_THRUST_FWD_SCALE * deltaTime)); - } + } } if (Math.abs(headDelta.x) > HEAD_STRAFE_DEAD_ZONE) { if (Math.abs(Vec3.dot(velocity, right)) < maxVelocity) { diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index b226b8ed31..d1fcc0f33f 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -32,6 +32,7 @@ Head::Head(Avatar* owningAvatar) : _eyePosition(0.0f, 0.0f, 0.0f), _scale(1.0f), _lastLoudness(0.0f), + _longTermAverageLoudness(-1.0f), _audioAttack(0.0f), _angularVelocity(0,0,0), _renderLookatVectors(false), @@ -62,7 +63,7 @@ void Head::reset() { } void Head::simulate(float deltaTime, bool isMine, bool billboard) { - // Update audio trailing average for rendering facial animations + if (isMine) { MyAvatar* myAvatar = static_cast(_owningAvatar); @@ -78,6 +79,18 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { } } } + // Update audio trailing average for rendering facial animations + const float AUDIO_AVERAGING_SECS = 0.05f; + const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.f; + _averageLoudness = glm::mix(_averageLoudness, _audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f)); + + if (_longTermAverageLoudness == -1.0) { + _longTermAverageLoudness = _averageLoudness; + } else { + _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); + } + float deltaLoudness = glm::max(0.0f, _averageLoudness - _longTermAverageLoudness); + qDebug() << "deltaLoudness: " << deltaLoudness; if (!(_isFaceshiftConnected || billboard)) { // Update eye saccades @@ -92,9 +105,6 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { _saccadeTarget = SACCADE_MAGNITUDE * randVector(); } _saccade += (_saccadeTarget - _saccade) * 0.50f; - - const float AUDIO_AVERAGING_SECS = 0.05f; - _averageLoudness = glm::mix(_averageLoudness, _audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f)); // Detect transition from talking to not; force blink after that and a delay bool forceBlink = false; @@ -108,8 +118,8 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { } // Update audio attack data for facial animation (eyebrows and mouth) - _audioAttack = 0.9f * _audioAttack + 0.1f * fabs(_audioLoudness - _lastLoudness); - _lastLoudness = _audioLoudness; + _audioAttack = 0.9f * _audioAttack + 0.1f * fabs((_audioLoudness - _longTermAverageLoudness) - _lastLoudness); + _lastLoudness = (_audioLoudness - _longTermAverageLoudness); const float BROW_LIFT_THRESHOLD = 100.0f; if (_audioAttack > BROW_LIFT_THRESHOLD) { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 1cdfdaf5a3..e99a7f5c7b 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -121,6 +121,7 @@ private: float _scale; float _lastLoudness; + float _longTermAverageLoudness; float _audioAttack; glm::vec3 _angularVelocity; bool _renderLookatVectors; From 8c71ed3f103b6106438d7633ff35e8cf5fa3eafa Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 18 Sep 2014 10:48:36 -0700 Subject: [PATCH 03/19] Improving headMove, added JS call for neckPosition --- examples/headMove.js | 29 +++++++++++++++++++++-------- interface/src/avatar/Avatar.cpp | 6 ++++++ interface/src/avatar/Avatar.h | 2 ++ interface/src/avatar/Head.cpp | 2 +- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/examples/headMove.js b/examples/headMove.js index 359dfd8751..68a9a7833d 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -45,7 +45,7 @@ var maxVelocity = 1.25; var noFly = true; //var roomLimits = { xMin: 618, xMax: 635.5, zMin: 528, zMax: 552.5 }; -var roomLimits = { xMin: -1, xMax: 0, zMin: 0, zMax: 0 }; +var roomLimits = { xMin: 142.6, xMax: 153.7, zMin: 177.5, zMax: 192.0 }; function isInRoom(position) { var BUFFER = 2.0; @@ -71,19 +71,29 @@ function moveWithHead(deltaTime) { var deltaPitch = MyAvatar.getHeadDeltaPitch() - headStartDeltaPitch; var deltaRoll = MyAvatar.getHeadFinalRoll() - headStartRoll; var velocity = MyAvatar.getVelocity(); - var bodyLocalCurrentHeadVector = Vec3.subtract(MyAvatar.getHeadPosition(), MyAvatar.position); - bodyLocalCurrentHeadVector = Vec3.multiplyQbyV(Quat.angleAxis(-deltaYaw, {x:0, y: 1, z:0}), bodyLocalCurrentHeadVector); + var position = MyAvatar.position; + var neckPosition = MyAvatar.getNeckPosition(); + var bodyLocalCurrentHeadVector = Vec3.subtract(neckPosition, position); + //bodyLocalCurrentHeadVector = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), bodyLocalCurrentHeadVector); var headDelta = Vec3.subtract(bodyLocalCurrentHeadVector, headStartPosition); - headDelta = Vec3.multiplyQbyV(Quat.inverse(Camera.getOrientation()), headDelta); + //headDelta = Vec3.multiplyQbyV(Quat.inverse(Camera.getOrientation()), headDelta); headDelta.y = 0.0; // Don't respond to any of the vertical component of head motion + if (Vec3.length(headDelta) > 0.005) { + Vec3.print("headDelta = ", headDelta); + Vec3.print("headStartPosition = ", headStartPosition); + Vec3.print("MyAvatar.position = ", position); + Vec3.print("MyAvatar.getNeckPosition() = ", neckPosition); + + } + // Thrust based on leaning forward and side-to-side var targetVelocity = {x:0.0, y:0.0, z:0.0}; if (Math.abs(headDelta.z) > HEAD_MOVE_DEAD_ZONE) { - targetVelocity = Vec3.multiply(zAxis, -headDelta.z * HEAD_VELOCITY_FWD_FACTOR); + targetVelocity = Vec3.multiply(zAxis, headDelta.z * HEAD_VELOCITY_FWD_FACTOR); } if (Math.abs(headDelta.x) > HEAD_STRAFE_DEAD_ZONE) { - var deltaVelocity = Vec3.multiply(xAxis, -headDelta.x * HEAD_VELOCITY_LEFT_FACTOR); + var deltaVelocity = Vec3.multiply(xAxis, headDelta.x * HEAD_VELOCITY_LEFT_FACTOR); targetVelocity = Vec3.sum(targetVelocity, deltaVelocity); } if (Math.abs(deltaYaw) > HEAD_ROTATE_DEAD_ZONE) { @@ -130,7 +140,10 @@ function moveWithHead(deltaTime) { Controller.keyPressEvent.connect(function(event) { if (event.text == "SPACE" && !movingWithHead) { movingWithHead = true; - headStartPosition = Vec3.subtract(MyAvatar.getHeadPosition(), MyAvatar.position); + Vec3.print("getNeckPosition() = ", MyAvatar.getNeckPosition()); + headStartPosition = Vec3.subtract(MyAvatar.getNeckPosition(), MyAvatar.position); + //headStartPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), headStartPosition); + Vec3.print("First HeadStartPosition = ", headStartPosition); headStartDeltaPitch = MyAvatar.getHeadDeltaPitch(); headStartFinalPitch = MyAvatar.getHeadFinalPitch(); headStartRoll = MyAvatar.getHeadFinalRoll(); @@ -138,7 +151,7 @@ Controller.keyPressEvent.connect(function(event) { // start with disabled motor -- it will be updated shortly MyAvatar.motorTimescale = VERY_LARGE_TIMESCALE; MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0}; - MyAvatar.motorReferenceFrame = "camera"; // alternatives are: "avatar" and "world" + MyAvatar.motorReferenceFrame = "avatar"; // alternatives are: "avatar" and "world" } }); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b62c744fa2..bd05f7c4c0 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -97,6 +97,12 @@ glm::vec3 Avatar::getChestPosition() const { return _skeletonModel.getNeckPosition(neckPosition) ? (_position + neckPosition) * 0.5f : _position; } +glm::vec3 Avatar::getNeckPosition() const { + glm::vec3 neckPosition; + return _skeletonModel.getNeckPosition(neckPosition) ? neckPosition : _position; +} + + glm::quat Avatar::getWorldAlignedOrientation () const { return computeRotationFromBodyToWorldUp() * getOrientation(); } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 921722504d..c449a0d1b9 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -154,6 +154,8 @@ public: Q_INVOKABLE void setJointModelPositionAndOrientation(int index, const glm::vec3 position, const glm::quat& rotation); Q_INVOKABLE void setJointModelPositionAndOrientation(const QString& name, const glm::vec3 position, const glm::quat& rotation); + + Q_INVOKABLE glm::vec3 getNeckPosition() const; Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; } Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; } diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 3f0770428b..4c394377a3 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -90,7 +90,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); } float deltaLoudness = glm::max(0.0f, _averageLoudness - _longTermAverageLoudness); - qDebug() << "deltaLoudness: " << deltaLoudness; + //qDebug() << "deltaLoudness: " << deltaLoudness; if (!(_isFaceshiftConnected || billboard)) { // Update eye saccades From 256bd627fa9dedcbbf548ec67ef8d9f28b4f57f4 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Thu, 18 Sep 2014 13:12:53 -0700 Subject: [PATCH 04/19] clamp to 0.0f when within EPSILON neighborhood of 0.0f --- libraries/audio/src/AudioFilter.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/audio/src/AudioFilter.h b/libraries/audio/src/AudioFilter.h index c9458f280e..c792b5fec4 100644 --- a/libraries/audio/src/AudioFilter.h +++ b/libraries/audio/src/AudioFilter.h @@ -75,6 +75,8 @@ public: - (_b1 * _ym1) - (_b2 * _ym2); + y = (y >= -EPSILON && y < EPSILON) ? 0.0f : y; // clamp to 0 + // update delay line _xm2 = _xm1; _xm1 = x; From 2383379a356386ff33b3fec7363b2ee15663552e Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Thu, 18 Sep 2014 13:52:53 -0700 Subject: [PATCH 05/19] default sine gain set to -6db --- libraries/audio/src/AudioSourceTone.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioSourceTone.cpp b/libraries/audio/src/AudioSourceTone.cpp index da6eea6a9e..d1cd14be96 100644 --- a/libraries/audio/src/AudioSourceTone.cpp +++ b/libraries/audio/src/AudioSourceTone.cpp @@ -40,8 +40,8 @@ void AudioSourceTone::updateCoefficients() { void AudioSourceTone::initialize() { const float32_t FREQUENCY_220_HZ = 220.0f; - const float32_t GAIN_MINUS_3DB = 0.708f; - setParameters(SAMPLE_RATE, FREQUENCY_220_HZ, GAIN_MINUS_3DB); + const float32_t GAIN_MINUS_6DB = 0.501f; + setParameters(SAMPLE_RATE, FREQUENCY_220_HZ, GAIN_MINUS_6DB); } void AudioSourceTone::setParameters(const float32_t sampleRate, const float32_t frequency, const float32_t amplitude) { From 864c1c88a703c4552cf31bb61a098615172ff5dd Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 18 Sep 2014 14:58:44 -0700 Subject: [PATCH 06/19] fix the filter with delay and add per listener source pair data --- assignment-client/src/audio/AudioMixer.cpp | 26 +++++++++++++------ assignment-client/src/audio/AudioMixer.h | 7 +++-- .../src/audio/AudioMixerClientData.cpp | 14 ++++++++++ .../src/audio/AudioMixerClientData.h | 20 ++++++++++++++ 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 9d56e02a67..7f1825084b 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -96,7 +96,6 @@ AudioMixer::AudioMixer(const QByteArray& packet) : { // constant defined in AudioMixer.h. However, we don't want to include this here // we will soon find a better common home for these audio-related constants - _penumbraFilter.initialize(SAMPLE_RATE, (NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)) / 2); } AudioMixer::~AudioMixer() { @@ -108,8 +107,10 @@ const float ATTENUATION_BEGINS_AT_DISTANCE = 1.0f; const float ATTENUATION_AMOUNT_PER_DOUBLING_IN_DISTANCE = 0.18f; const float RADIUS_OF_HEAD = 0.076f; -int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* streamToAdd, - AvatarAudioStream* listeningNodeStream) { +int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* listenerNodeData, + const QUuid& streamUUID, + PositionalAudioStream* streamToAdd, + AvatarAudioStream* listeningNodeStream) { // If repetition with fade is enabled: // If streamToAdd could not provide a frame (it was starved), then we'll mix its previously-mixed frame // This is preferable to not mixing it at all since that's equivalent to inserting silence. @@ -413,11 +414,13 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* << -bearingRelativeAngleToSource; #endif + // Get our per listener/source data so we can get our filter + AudioFilterHSF1s& penumbraFilter = listenerNodeData->getListenerSourcePairData(streamUUID)->getPenumbraFilter(); + // set the gain on both filter channels - _penumbraFilter.reset(); - _penumbraFilter.setParameters(0, 0, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainL, penumbraFilterSlope); - _penumbraFilter.setParameters(0, 1, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainR, penumbraFilterSlope); - _penumbraFilter.render(_preMixSamples, _preMixSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2); + penumbraFilter.setParameters(0, 0, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainL, penumbraFilterSlope); + penumbraFilter.setParameters(0, 1, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainR, penumbraFilterSlope); + penumbraFilter.render(_preMixSamples, _preMixSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2); } // Actually mix the _preMixSamples into the _mixSamples here. @@ -430,6 +433,7 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* int AudioMixer::prepareMixForListeningNode(Node* node) { AvatarAudioStream* nodeAudioStream = ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioStream(); + AudioMixerClientData* listenerNodeData = (AudioMixerClientData*)node->getLinkedData(); // zero out the client mix for this node memset(_preMixSamples, 0, sizeof(_preMixSamples)); @@ -447,9 +451,15 @@ int AudioMixer::prepareMixForListeningNode(Node* node) { QHash::ConstIterator i; for (i = otherNodeAudioStreams.constBegin(); i != otherNodeAudioStreams.constEnd(); i++) { PositionalAudioStream* otherNodeStream = i.value(); + QUuid streamUUID = i.key(); + if (otherNodeStream->getType() == PositionalAudioStream::Microphone) { + streamUUID = otherNode->getUUID(); + } + if (*otherNode != *node || otherNodeStream->shouldLoopbackForNode()) { - streamsMixed += addStreamToMixForListeningNodeWithStream(otherNodeStream, nodeAudioStream); + streamsMixed += addStreamToMixForListeningNodeWithStream(listenerNodeData, streamUUID, + otherNodeStream, nodeAudioStream); } } } diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index afc59d3641..1594dbda9a 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -22,6 +22,7 @@ class PositionalAudioStream; class AvatarAudioStream; +class AudioMixerClientData; const int SAMPLE_PHASE_DELAY_AT_90 = 20; @@ -46,8 +47,10 @@ public slots: private: /// adds one stream to the mix for a listening node - int addStreamToMixForListeningNodeWithStream(PositionalAudioStream* streamToAdd, - AvatarAudioStream* listeningNodeStream); + int addStreamToMixForListeningNodeWithStream(AudioMixerClientData* listenerNodeData, + const QUuid& streamUUID, + PositionalAudioStream* streamToAdd, + AvatarAudioStream* listeningNodeStream); /// prepares and sends a mix to one Node int prepareMixForListeningNode(Node* node); diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 68ab7d74e1..6c326b3d28 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -33,6 +33,11 @@ AudioMixerClientData::~AudioMixerClientData() { // delete this attached InboundAudioStream delete i.value(); } + + // clean up our pair data... + foreach(PerListenerSourcePairData* pairData, _listenerSourcePairData) { + delete pairData; + } } AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() const { @@ -302,3 +307,12 @@ void AudioMixerClientData::printAudioStreamStats(const AudioStreamStats& streamS formatUsecTime(streamStats._timeGapWindowMax).toLatin1().data(), formatUsecTime(streamStats._timeGapWindowAverage).toLatin1().data()); } + + +PerListenerSourcePairData* AudioMixerClientData::getListenerSourcePairData(const QUuid& sourceUUID) { + if (!_listenerSourcePairData.contains(sourceUUID)) { + PerListenerSourcePairData* newData = new PerListenerSourcePairData(); + _listenerSourcePairData[sourceUUID] = newData; + } + return _listenerSourcePairData[sourceUUID]; +} diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 0ce4ecc36a..65a65f3d77 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -13,10 +13,26 @@ #define hifi_AudioMixerClientData_h #include +#include // For AudioFilterHSF1s and _penumbraFilter +#include // For AudioFilterHSF1s and _penumbraFilter +#include // For AudioFilterHSF1s and _penumbraFilter +#include // For AudioFilterHSF1s and _penumbraFilter #include "PositionalAudioStream.h" #include "AvatarAudioStream.h" +class PerListenerSourcePairData { +public: + PerListenerSourcePairData() { + const int SAMPLE_PHASE_DELAY_AT_90 = 20; + _penumbraFilter.initialize(SAMPLE_RATE, (NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)) / 2); + }; + AudioFilterHSF1s& getPenumbraFilter() { return _penumbraFilter; } + +private: + AudioFilterHSF1s _penumbraFilter; +}; + class AudioMixerClientData : public NodeData { public: AudioMixerClientData(); @@ -40,12 +56,16 @@ public: void printUpstreamDownstreamStats() const; + PerListenerSourcePairData* getListenerSourcePairData(const QUuid& sourceUUID); private: void printAudioStreamStats(const AudioStreamStats& streamStats) const; private: QHash _audioStreams; // mic stream stored under key of null UUID + // TODO: how can we prune this hash when a stream is no longer present? + QHash _listenerSourcePairData; + quint16 _outgoingMixedAudioSequenceNumber; AudioStreamStats _downstreamAudioStreamStats; From bd23b8b8b95f3160093921bf4da7c25b9111c147 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 18 Sep 2014 15:01:06 -0700 Subject: [PATCH 07/19] completely remove _penumbraFilter from AudioMixer class --- assignment-client/src/audio/AudioMixer.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 1594dbda9a..6c25fe21d9 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -13,10 +13,6 @@ #define hifi_AudioMixer_h #include -#include // For AudioFilterHSF1s and _penumbraFilter -#include // For AudioFilterHSF1s and _penumbraFilter -#include // For AudioFilterHSF1s and _penumbraFilter -#include // For AudioFilterHSF1s and _penumbraFilter #include #include @@ -63,8 +59,6 @@ private: // we are MMX adding 4 samples at a time so we need client samples to have an extra 4 int16_t _mixSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)]; - AudioFilterHSF1s _penumbraFilter; - void perSecondActions(); QString getReadPendingDatagramsCallsPerSecondsStatsString() const; From 6f73da7d44107efaf427fddd62c69b981a70d2c4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 18 Sep 2014 15:19:03 -0700 Subject: [PATCH 08/19] CR feedback --- assignment-client/src/audio/AudioMixerClientData.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 65a65f3d77..61d2b54e71 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -24,8 +24,7 @@ class PerListenerSourcePairData { public: PerListenerSourcePairData() { - const int SAMPLE_PHASE_DELAY_AT_90 = 20; - _penumbraFilter.initialize(SAMPLE_RATE, (NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)) / 2); + _penumbraFilter.initialize(SAMPLE_RATE, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2); }; AudioFilterHSF1s& getPenumbraFilter() { return _penumbraFilter; } From 8e6d5da8fd7ea48fbfcc453f14bf3f1ac11df2cf Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Thu, 18 Sep 2014 16:05:15 -0700 Subject: [PATCH 09/19] disable positional filter by default --- domain-server/resources/web/settings/describe.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index 2ea0aec0c7..fee7ff21fc 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -67,7 +67,7 @@ "type": "checkbox", "label": "Enable Positional Filter", "help": "If enabled, positional audio stream uses lowpass filter", - "default": true + "default": false } } } From 58b1db4f0921f2ed92a957fd5604fe8f8bb0619b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 18 Sep 2014 16:49:13 -0700 Subject: [PATCH 10/19] Added JS hooks for new Player options --- examples/Recorder.js | 13 ++++++++++--- libraries/avatars/src/AvatarData.cpp | 24 ++++++++++++++++++++++++ libraries/avatars/src/AvatarData.h | 4 ++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/examples/Recorder.js b/examples/Recorder.js index 657e2d5206..8efa9408a9 100644 --- a/examples/Recorder.js +++ b/examples/Recorder.js @@ -12,7 +12,14 @@ Script.include("toolBars.js"); var recordingFile = "recording.rec"; -var playFromCurrentLocation = true; + +function setPlayerOptions() { + MyAvatar.setPlayFromCurrentLocation(true); + MyAvatar.setPlayerUseDisplayName(false); + MyAvatar.setPlayerUseAttachments(false); + MyAvatar.setPlayerUseHeadModel(false); + MyAvatar.setPlayerUseSkeletonModel(false); +} var windowDimensions = Controller.getViewportDimensions(); var TOOL_ICON_URL = "http://s3-us-west-1.amazonaws.com/highfidelity-public/images/tools/"; @@ -186,7 +193,7 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); } else if (MyAvatar.playerLength() > 0) { - MyAvatar.setPlayFromCurrentLocation(playFromCurrentLocation); + setPlayerOptions(); MyAvatar.setPlayerLoop(false); MyAvatar.startPlaying(); toolBar.setAlpha(ALPHA_OFF, recordIcon); @@ -201,7 +208,7 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); } else if (MyAvatar.playerLength() > 0) { - MyAvatar.setPlayFromCurrentLocation(playFromCurrentLocation); + setPlayerOptions(); MyAvatar.setPlayerLoop(true); MyAvatar.startPlaying(); toolBar.setAlpha(ALPHA_OFF, recordIcon); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 200a6af6c7..5ac0c69864 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -661,6 +661,30 @@ void AvatarData::setPlayerLoop(bool loop) { } } +void AvatarData::setPlayerUseDisplayName(bool useDisplayName) { + if(_player) { + _player->useDisplayName(useDisplayName); + } +} + +void AvatarData::setPlayerUseAttachments(bool useAttachments) { + if(_player) { + _player->useAttachements(useAttachments); + } +} + +void AvatarData::setPlayerUseHeadModel(bool useHeadModel) { + if(_player) { + _player->useHeadModel(useHeadModel); + } +} + +void AvatarData::setPlayerUseSkeletonModel(bool useSkeletonModel) { + if(_player) { + _player->useSkeletonModel(useSkeletonModel); + } +} + void AvatarData::play() { if (isPlaying()) { if (QThread::currentThread() != thread()) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 3c373a0eda..bc68103ca6 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -302,6 +302,10 @@ public slots: void startPlaying(); void setPlayFromCurrentLocation(bool playFromCurrentLocation); void setPlayerLoop(bool loop); + void setPlayerUseDisplayName(bool useDisplayName); + void setPlayerUseAttachments(bool useAttachments); + void setPlayerUseHeadModel(bool useHeadModel); + void setPlayerUseSkeletonModel(bool useSkeletonModel); void play(); void stopPlaying(); From 3ba178022eb84ec7841eaea3ec6fb4aa0aab447d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 18 Sep 2014 17:02:11 -0700 Subject: [PATCH 11/19] Added new options to ControlledAC.js --- examples/ControlledAC.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/ControlledAC.js b/examples/ControlledAC.js index 84a6c36082..33fa629ff8 100644 --- a/examples/ControlledAC.js +++ b/examples/ControlledAC.js @@ -12,6 +12,10 @@ // Set the following variables to the values needed var filename = "http://s3-us-west-1.amazonaws.com/highfidelity-public/ozan/bartender.rec"; var playFromCurrentLocation = true; +var useDisplayName = true; +var useAttachments = true; +var useHeadModel = true; +var useSkeletonModel = true; // ID of the agent. Two agents can't have the same ID. var id = 0; @@ -44,11 +48,15 @@ COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 }; COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 }; controlVoxelPosition.x += id * controlVoxelSize; - -Avatar.setPlayFromCurrentLocation(playFromCurrentLocation); Avatar.loadRecording(filename); +Avatar.setPlayFromCurrentLocation(playFromCurrentLocation); +Avatar.setPlayerUseDisplayName(useDisplayName); +Avatar.setPlayerUseAttachments(useAttachments); +Avatar.setPlayerUseHeadModel(useHeadModel); +Avatar.setPlayerUseSkeletonModel(useSkeletonModel); + function setupVoxelViewer() { var voxelViewerOffset = 10; var voxelViewerPosition = JSON.parse(JSON.stringify(controlVoxelPosition)); From c6d6c347cd94f9fa38bb46c4c029bb40c2950167 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 19 Sep 2014 08:48:55 -0700 Subject: [PATCH 12/19] head move using neck measurement for movement rather than head. --- examples/headMove.js | 57 +++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/examples/headMove.js b/examples/headMove.js index 68a9a7833d..834c63c3ab 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -16,24 +16,21 @@ var debug = false; var movingWithHead = false; var headStartPosition, headStartDeltaPitch, headStartFinalPitch, headStartRoll, headStartYaw; -var HEAD_MOVE_DEAD_ZONE = 0.10; -var HEAD_STRAFE_DEAD_ZONE = 0.0; -var HEAD_ROTATE_DEAD_ZONE = 0.0; -//var HEAD_THRUST_FWD_SCALE = 12000.0; -//var HEAD_THRUST_STRAFE_SCALE = 0.0; -var HEAD_YAW_RATE = 1.0; +var HEAD_MOVE_DEAD_ZONE = 0.03; +var HEAD_STRAFE_DEAD_ZONE = 0.03; +var HEAD_ROTATE_DEAD_ZONE = 10.0; +var HEAD_YAW_RATE = 2.0; var HEAD_PITCH_RATE = 1.0; -//var HEAD_ROLL_THRUST_SCALE = 75.0; -//var HEAD_PITCH_LIFT_THRUST = 3.0; var WALL_BOUNCE = 4000.0; +var FIXED_WALK_VELOCITY = 1.5; // Modify these values to tweak the strength of the motion. // A larger *FACTOR increases the speed. // A lower SHORT_TIMESCALE makes the motor achieve full speed faster. var HEAD_VELOCITY_FWD_FACTOR = 20.0; -var HEAD_VELOCITY_LEFT_FACTOR = 20.0; +var HEAD_VELOCITY_LEFT_FACTOR = 0.0; var HEAD_VELOCITY_UP_FACTOR = 20.0; -var SHORT_TIMESCALE = 0.125; +var SHORT_TIMESCALE = 0.01; var VERY_LARGE_TIMESCALE = 1000000.0; var xAxis = {x:1.0, y:0.0, z:0.0 }; @@ -43,9 +40,10 @@ var zAxis = {x:0.0, y:0.0, z:1.0 }; // If these values are set to something var maxVelocity = 1.25; var noFly = true; +var fixedWalkVelocity = true; //var roomLimits = { xMin: 618, xMax: 635.5, zMin: 528, zMax: 552.5 }; -var roomLimits = { xMin: 142.6, xMax: 153.7, zMin: 177.5, zMax: 192.0 }; +var roomLimits = { xMin: 193.0, xMax: 206.5, zMin: 251.4, zMax: 269.5 }; function isInRoom(position) { var BUFFER = 2.0; @@ -74,32 +72,43 @@ function moveWithHead(deltaTime) { var position = MyAvatar.position; var neckPosition = MyAvatar.getNeckPosition(); var bodyLocalCurrentHeadVector = Vec3.subtract(neckPosition, position); - //bodyLocalCurrentHeadVector = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), bodyLocalCurrentHeadVector); + bodyLocalCurrentHeadVector = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), bodyLocalCurrentHeadVector); var headDelta = Vec3.subtract(bodyLocalCurrentHeadVector, headStartPosition); - //headDelta = Vec3.multiplyQbyV(Quat.inverse(Camera.getOrientation()), headDelta); headDelta.y = 0.0; // Don't respond to any of the vertical component of head motion - if (Vec3.length(headDelta) > 0.005) { - Vec3.print("headDelta = ", headDelta); - Vec3.print("headStartPosition = ", headStartPosition); - Vec3.print("MyAvatar.position = ", position); - Vec3.print("MyAvatar.getNeckPosition() = ", neckPosition); + headDelta = Vec3.multiplyQbyV(MyAvatar.orientation, headDelta); + headDelta = Vec3.multiplyQbyV(Quat.inverse(Camera.getOrientation()), headDelta); + + var length = Vec3.length(headDelta); + if (length > 1.0) { + // Needs fixed! Right now sometimes reported neck position jumps to a bad value + headDelta.x = headDelta.y = headDelta.z = 0.0; + length = 0.0; } - // Thrust based on leaning forward and side-to-side var targetVelocity = {x:0.0, y:0.0, z:0.0}; + if (length > HEAD_MOVE_DEAD_ZONE) { + targetVelocity = Vec3.multiply(headDelta, HEAD_VELOCITY_FWD_FACTOR); + } + /* if (Math.abs(headDelta.z) > HEAD_MOVE_DEAD_ZONE) { - targetVelocity = Vec3.multiply(zAxis, headDelta.z * HEAD_VELOCITY_FWD_FACTOR); + if (fixedWalkVelocity) { + targetVelocity = Vec3.multiply(zAxis, headDelta.z > 0 ? FIXED_WALK_VELOCITY : -FIXED_WALK_VELOCITY); + } else { + targetVelocity = Vec3.multiply(zAxis, headDelta.z * HEAD_VELOCITY_FWD_FACTOR); + } } if (Math.abs(headDelta.x) > HEAD_STRAFE_DEAD_ZONE) { var deltaVelocity = Vec3.multiply(xAxis, headDelta.x * HEAD_VELOCITY_LEFT_FACTOR); targetVelocity = Vec3.sum(targetVelocity, deltaVelocity); } + */ if (Math.abs(deltaYaw) > HEAD_ROTATE_DEAD_ZONE) { var orientation = Quat.multiply(Quat.angleAxis((deltaYaw + deltaRoll) * HEAD_YAW_RATE * deltaTime, yAxis), MyAvatar.orientation); MyAvatar.orientation = orientation; } + // Thrust Up/Down based on head pitch if (!noFly) { var deltaVelocity = Vec3.multiply(yAxis, headDelta.y * HEAD_VELOCITY_UP_FACTOR); @@ -140,10 +149,8 @@ function moveWithHead(deltaTime) { Controller.keyPressEvent.connect(function(event) { if (event.text == "SPACE" && !movingWithHead) { movingWithHead = true; - Vec3.print("getNeckPosition() = ", MyAvatar.getNeckPosition()); - headStartPosition = Vec3.subtract(MyAvatar.getNeckPosition(), MyAvatar.position); - //headStartPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), headStartPosition); - Vec3.print("First HeadStartPosition = ", headStartPosition); + headStartPosition = Vec3.subtract(MyAvatar.getNeckPosition(), MyAvatar.position); + headStartPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), headStartPosition); headStartDeltaPitch = MyAvatar.getHeadDeltaPitch(); headStartFinalPitch = MyAvatar.getHeadFinalPitch(); headStartRoll = MyAvatar.getHeadFinalRoll(); @@ -151,7 +158,7 @@ Controller.keyPressEvent.connect(function(event) { // start with disabled motor -- it will be updated shortly MyAvatar.motorTimescale = VERY_LARGE_TIMESCALE; MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0}; - MyAvatar.motorReferenceFrame = "avatar"; // alternatives are: "avatar" and "world" + MyAvatar.motorReferenceFrame = "camera"; // alternatives are: "avatar" and "world" } }); From 6c808f2979cd324c0c563af225549365dbfcfe2c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 19 Sep 2014 09:22:27 -0700 Subject: [PATCH 13/19] final tweaks to movement script --- examples/headMove.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/headMove.js b/examples/headMove.js index 834c63c3ab..4d432c28b3 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -16,18 +16,18 @@ var debug = false; var movingWithHead = false; var headStartPosition, headStartDeltaPitch, headStartFinalPitch, headStartRoll, headStartYaw; -var HEAD_MOVE_DEAD_ZONE = 0.03; +var HEAD_MOVE_DEAD_ZONE = 0.05; var HEAD_STRAFE_DEAD_ZONE = 0.03; var HEAD_ROTATE_DEAD_ZONE = 10.0; -var HEAD_YAW_RATE = 2.0; +var HEAD_YAW_RATE = 1.5; var HEAD_PITCH_RATE = 1.0; -var WALL_BOUNCE = 4000.0; +var WALL_BOUNCE = 10000.0; var FIXED_WALK_VELOCITY = 1.5; // Modify these values to tweak the strength of the motion. // A larger *FACTOR increases the speed. // A lower SHORT_TIMESCALE makes the motor achieve full speed faster. -var HEAD_VELOCITY_FWD_FACTOR = 20.0; +var HEAD_VELOCITY_FWD_FACTOR = 10.0; var HEAD_VELOCITY_LEFT_FACTOR = 0.0; var HEAD_VELOCITY_UP_FACTOR = 20.0; var SHORT_TIMESCALE = 0.01; @@ -84,11 +84,14 @@ function moveWithHead(deltaTime) { // Needs fixed! Right now sometimes reported neck position jumps to a bad value headDelta.x = headDelta.y = headDelta.z = 0.0; length = 0.0; + return; } // Thrust based on leaning forward and side-to-side var targetVelocity = {x:0.0, y:0.0, z:0.0}; if (length > HEAD_MOVE_DEAD_ZONE) { + //headDelta = Vec3.normalize(headDelta); + //targetVelocity = Vec3.multiply(headDelta, FIXED_WALK_VELOCITY); targetVelocity = Vec3.multiply(headDelta, HEAD_VELOCITY_FWD_FACTOR); } /* @@ -140,7 +143,7 @@ function moveWithHead(deltaTime) { if (movingWithHead && Vec3.length(thrust) > 0.0) { // reduce the timescale of the motor so that it won't defeat the thrust code Vec3.print("adebug room containment thrust = ", thrust); - motorTimescale = 4.0 * SHORT_TIMESCALE; + motorTimescale = 1000000.0; } } MyAvatar.motorTimescale = motorTimescale; From 3d8f3ad9fa05ced16999fe89a877dba14b1421e4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 19 Sep 2014 13:21:24 -0700 Subject: [PATCH 14/19] Fixes concurrency issue with scriptengine timers --- libraries/script-engine/src/ScriptEngine.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 29423b7116..cfea0d6b86 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -605,18 +605,18 @@ void ScriptEngine::stop() { void ScriptEngine::timerFired() { QTimer* callingTimer = reinterpret_cast(sender()); - - // call the associated JS function, if it exists QScriptValue timerFunction = _timerFunctionMap.value(callingTimer); - if (timerFunction.isValid()) { - timerFunction.call(); - } - + if (!callingTimer->isActive()) { // this timer is done, we can kill it _timerFunctionMap.remove(callingTimer); delete callingTimer; } + + // call the associated JS function, if it exists + if (timerFunction.isValid()) { + timerFunction.call(); + } } QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot) { From c570931f351f3b86b53655d13816e0dab309a0dd Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 19 Sep 2014 15:43:22 -0700 Subject: [PATCH 15/19] Fix attachemnt getting into wrong state If you play a recording containing attachement with different attachemnts already on, those attachement will sometime get into a wrong state. --- interface/src/avatar/Avatar.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6f421965db..d43bc07118 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -877,7 +877,9 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { void Avatar::setAttachmentData(const QVector& attachmentData) { AvatarData::setAttachmentData(attachmentData); - if (QThread::currentThread() != thread()) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setAttachmentData", Qt::DirectConnection, + Q_ARG(const QVector, attachmentData)); return; } // make sure we have as many models as attachments @@ -892,9 +894,9 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { // update the urls for (int i = 0; i < attachmentData.size(); i++) { + _attachmentModels[i]->setURL(attachmentData.at(i).modelURL); _attachmentModels[i]->setSnapModelToCenter(true); _attachmentModels[i]->setScaleToFit(true, _scale * _attachmentData.at(i).scale); - _attachmentModels[i]->setURL(attachmentData.at(i).modelURL); } } From 36a3372f582170c8fe2496b28873728b4d761577 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 19 Sep 2014 15:47:13 -0700 Subject: [PATCH 16/19] Same fix for MyAvatar class --- interface/src/avatar/MyAvatar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 27f74f185d..1e086d86a9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1029,7 +1029,9 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { void MyAvatar::setAttachmentData(const QVector& attachmentData) { Avatar::setAttachmentData(attachmentData); - if (QThread::currentThread() != thread()) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setAttachmentData", Qt::DirectConnection, + Q_ARG(const QVector, attachmentData)); return; } _billboardValid = false; From 09d3c511205364a69c89f4dd5df16614e20dafab Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 19 Sep 2014 16:54:03 -0700 Subject: [PATCH 17/19] add multiply(float, Vec3) because multiply commutes --- libraries/script-engine/src/Vec3.cpp | 4 ++++ libraries/script-engine/src/Vec3.h | 1 + 2 files changed, 5 insertions(+) diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index 0cbb43f89a..7589ade3b6 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -25,6 +25,10 @@ glm::vec3 Vec3::multiply(const glm::vec3& v1, float f) { return v1 * f; } +glm::vec3 Vec3::multiply(float f, const glm::vec3& v1) { + return v1 * f; +} + glm::vec3 Vec3::multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; } diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 5a3eeca7be..598f9be432 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -28,6 +28,7 @@ public slots: glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2); float dot(const glm::vec3& v1, const glm::vec3& v2); glm::vec3 multiply(const glm::vec3& v1, float f); + glm::vec3 multiply(float, const glm::vec3& v1); glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v); glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2); glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2); From 57dd9fba285f7849bb0c3cf8accc5d8813d1ce84 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 19 Sep 2014 16:55:04 -0700 Subject: [PATCH 18/19] adding move.js, removing swissArmyJetpack.js --- examples/move.js | 171 ++++++++++++++++++++ examples/swissArmyJetpack.js | 294 ----------------------------------- 2 files changed, 171 insertions(+), 294 deletions(-) create mode 100755 examples/move.js delete mode 100644 examples/swissArmyJetpack.js diff --git a/examples/move.js b/examples/move.js new file mode 100755 index 0000000000..e3fc5806b6 --- /dev/null +++ b/examples/move.js @@ -0,0 +1,171 @@ +// +// move.js +// +// Created by AndrewMeadows, 2014.09.17 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +// +// The avatar can be controlled by setting two motor parameters: motorVelocity and motorTimescale. +// Once the motorVelocity is set the avatar will try to move in that direction and speed. The +// motorTimescale is the approximate amount of time it takes for the avatar to reach within 1/e of its +// motorVelocity, so a short timescale makes it ramp up fast, and a long timescale makes it slow. + + +// These parameters control the motor's speed and strength. +var MAX_MOTOR_TIMESCALE = 0.5; +var PUSHING_MOTOR_TIMESCALE = 0.25; +var BRAKING_MOTOR_TIMESCALE = 0.125; +var VERY_LONG_TIME = 1000000.0; + +var AVATAR_SPEED = 4.0; +var MIN_BRAKING_SPEED = 0.2; + + +var motorAccumulator = {x:0.0, y:0.0, z:0.0}; +var isBraking = false; + +// There is a bug in Qt-5.3.0 (and below) that prevents QKeyEvent.isAutoRepeat from being correctly set. +// This means we can't tell when a held button is actually released -- all we get are the repeated +// keyPress- (and keyRelease-) events. So what we have to do is keep a list of last press timestamps +// for buttons of interest, and then periodically scan that list for old timestamps and drop any that +// have expired (at which point we actually remove that buttons effect on the motor). As long as the +// check period is longer than the time between key repeats then things will be smooth, and as long +// as the expiry time is short enough then the stop won't feel too laggy. + +var MAX_AUTO_REPEAT_DELAY = 3; +var KEY_RELEASE_EXPIRY_MSEC = 100; + +// KeyInfo class contructor: +function KeyInfo(contribution) { + this.motorContribution = contribution; // Vec3 contribution of this key toward motorVelocity + this.releaseTime = new Date(); // time when this button was released + this.pressTime = new Date(); // time when this button was pressed + this.isPressed = false; +} + +// NOTE: the avatar's default orientation is such that "forward" is along the -zAxis, and "left" is along -xAxis. +var controlKeys = { + "UP" : new KeyInfo({x: 0.0, y: 0.0, z:-1.0}), + "DOWN" : new KeyInfo({x: 0.0, y: 0.0, z: 1.0}), + "SHIFT+LEFT" : new KeyInfo({x:-1.0, y: 0.0, z: 0.0}), + "SHIFT+RIGHT": new KeyInfo({x: 1.0, y: 0.0, z: 0.0}), + "w" : new KeyInfo({x: 0.0, y: 0.0, z:-1.0}), + "s" : new KeyInfo({x: 0.0, y: 0.0, z: 1.0}), + "e" : new KeyInfo({x: 0.0, y: 1.0, z: 0.0}), + "c" : new KeyInfo({x: 0.0, y:-1.0, z: 0.0}) +}; + +// list of last timestamps when various buttons were last pressed +var pressTimestamps = {}; + +function keyPressEvent(event) { + // NOTE: we're harvesting some of the same keyboard controls that are used by the default (scriptless) + // avatar control. The scriptless control can be disabled via the Menu, thereby allowing this script + // to be the ONLY controller of the avatar position. + + var keyName = event.text; + if (event.isShifted) { + keyName = "SHIFT+" + keyName; + } + + var key = controlKeys[keyName]; + if (key != undefined) { + key.pressTime = new Date(); + // set the last pressTimestap element to undefined (MUCH faster than removing from the list) + pressTimestamps[keyName] = undefined; + var msec = key.pressTime.valueOf() - key.releaseTime.valueOf(); + if (!key.isPressed) { + // add this key's effect to the motorAccumulator + motorAccumulator = Vec3.sum(motorAccumulator, key.motorContribution); + key.isPressed = true; + } + } +} + +function keyReleaseEvent(event) { + var keyName = event.text; + if (event.isShifted) { + keyName = "SHIFT+" + keyName; + } + + var key = controlKeys[keyName]; + if (key != undefined) { + // add key to pressTimestamps + pressTimestamps[keyName] = new Date(); + key.releaseTime = new Date(); + var msec = key.releaseTime.valueOf() - key.pressTime.valueOf(); + } +} + +function updateMotor(deltaTime) { + // remove expired pressTimestamps + var now = new Date(); + for (var keyName in pressTimestamps) { + var t = pressTimestamps[keyName]; + if (t != undefined) { + var msec = now.valueOf() - t.valueOf(); + if (msec > KEY_RELEASE_EXPIRY_MSEC) { + // the release of this key is now official, and we remove it from the motorAccumulator + motorAccumulator = Vec3.subtract(motorAccumulator, controlKeys[keyName].motorContribution); + controlKeys[keyName].isPressed = false; + // set the last pressTimestap element to undefined (MUCH faster than removing from the list) + pressTimestamps[keyName] = undefined; + } + } + } + + var motorVelocity = {x:0.0, y:0.0, z:0.0}; + + // figure out if we're pushing or braking + var accumulatorLength = Vec3.length(motorAccumulator); + var isPushing = false; + if (accumulatorLength == 0.0) { + if (!isBraking) { + isBraking = true; + } + isPushing = false; + } else { + isPushing = true; + motorVelocity = Vec3.multiply(AVATAR_SPEED / accumulatorLength, motorAccumulator); + } + + // compute the timescale + var motorTimescale = MAX_MOTOR_TIMESCALE; + if (isBraking) { + var speed = Vec3.length(MyAvatar.getVelocity()); + if (speed < MIN_BRAKING_SPEED) { + // we're going slow enough to turn off braking + // --> we'll drift to a halt, but not so stiffly that we can't be bumped + isBraking = false; + motorTimescale = MAX_MOTOR_TIMESCALE; + } else { + // we're still braking + motorTimescale = BRAKING_MOTOR_TIMESCALE; + } + } else if (isPushing) { + motorTimescale = PUSHING_MOTOR_TIMESCALE; + } + + // apply the motor parameters + MyAvatar.motorVelocity = motorVelocity; + MyAvatar.motorTimescale = motorTimescale; +} + +function scriptEnding() { + // disable the motor + MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0} + MyAvatar.motorTimescale = VERY_LONG_TIME; +} + + +// init stuff +MyAvatar.motorReferenceFrame = "camera"; // "camera" is default, other options are "avatar" and "world" +MyAvatar.motorTimescale = VERY_LONG_TIME; + +// connect callbacks +Controller.keyPressEvent.connect(keyPressEvent); +Controller.keyReleaseEvent.connect(keyReleaseEvent); +Script.update.connect(updateMotor); +Script.scriptEnding.connect(scriptEnding) diff --git a/examples/swissArmyJetpack.js b/examples/swissArmyJetpack.js deleted file mode 100644 index 4cb68e9b64..0000000000 --- a/examples/swissArmyJetpack.js +++ /dev/null @@ -1,294 +0,0 @@ -// -// swissArmyJetpack.js -// examples -// -// Created by Andrew Meadows 2014.04.24 -// Copyright 2014 High Fidelity, Inc. -// -// This is a work in progress. It will eventually be able to move the avatar around, -// toggle collision groups, modify avatar movement options, and other stuff (maybe trigger animations). -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// misc global constants -var NUMBER_OF_COLLISION_BUTTONS = 3; -var NUMBER_OF_BUTTONS = 4; -var DOWN = { x: 0.0, y: -1.0, z: 0.0 }; -var MAX_VOXEL_SCAN_DISTANCE = 30.0; - -// behavior transition thresholds -var MIN_FLYING_SPEED = 3.0; -var MIN_COLLISIONLESS_SPEED = 5.0; -var MAX_WALKING_SPEED = 30.0; -var MAX_COLLIDABLE_SPEED = 35.0; - -// button URL and geometry/UI tuning -var BUTTON_IMAGE_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg"; -var DISABLED_OFFSET_Y = 12; -var ENABLED_OFFSET_Y = 55 + 12; -var UI_BUFFER = 1; -var OFFSET_X = UI_BUFFER; -var OFFSET_Y = 200; -var BUTTON_WIDTH = 30; -var BUTTON_HEIGHT = 30; -var TEXT_OFFSET_X = OFFSET_X + BUTTON_WIDTH + UI_BUFFER; -var TEXT_HEIGHT = BUTTON_HEIGHT; -var TEXT_WIDTH = 210; - -var MSEC_PER_SECOND = 1000; -var RAYCAST_EXPIRY_PERIOD = MSEC_PER_SECOND / 16; -var COLLISION_EXPIRY_PERIOD = 2 * MSEC_PER_SECOND; -var GRAVITY_ON_EXPIRY_PERIOD = MSEC_PER_SECOND / 2; -var GRAVITY_OFF_EXPIRY_PERIOD = MSEC_PER_SECOND / 8; - -var dater = new Date(); -var raycastExpiry = dater.getTime() + RAYCAST_EXPIRY_PERIOD; -var gravityOnExpiry = dater.getTime() + GRAVITY_ON_EXPIRY_PERIOD; -var gravityOffExpiry = dater.getTime() + GRAVITY_OFF_EXPIRY_PERIOD; -var collisionOnExpiry = dater.getTime() + COLLISION_EXPIRY_PERIOD; - -// avatar state -var velocity = { x: 0.0, y: 0.0, z: 0.0 }; -var standing = false; - -// speedometer globals -var speed = 0.0; -var lastPosition = MyAvatar.position; -var speedometer = Overlays.addOverlay("text", { - x: OFFSET_X, - y: OFFSET_Y - BUTTON_HEIGHT, - width: BUTTON_WIDTH + UI_BUFFER + TEXT_WIDTH, - height: TEXT_HEIGHT, - color: { red: 0, green: 0, blue: 0 }, - textColor: { red: 255, green: 0, blue: 0}, - topMargin: 4, - leftMargin: 4, - text: "Speed: 0.0" - }); - -// collision group buttons -var buttons = new Array(); -var labels = new Array(); - -var labelContents = new Array(); -labelContents[0] = "Collide with Avatars"; -labelContents[1] = "Collide with Voxels"; -labelContents[2] = "Collide with Particles"; -labelContents[3] = "Use local gravity"; -var groupBits = 0; - -var enabledColors = new Array(); -enabledColors[0] = { red: 255, green: 0, blue: 0}; -enabledColors[1] = { red: 0, green: 255, blue: 0}; -enabledColors[2] = { red: 0, green: 0, blue: 255}; -enabledColors[3] = { red: 255, green: 255, blue: 0}; - -var disabledColors = new Array(); -disabledColors[0] = { red: 90, green: 75, blue: 75}; -disabledColors[1] = { red: 75, green: 90, blue: 75}; -disabledColors[2] = { red: 75, green: 75, blue: 90}; -disabledColors[3] = { red: 90, green: 90, blue: 75}; - -var buttonStates = new Array(); - -for (i = 0; i < NUMBER_OF_BUTTONS; i++) { - var offsetS = 12 - var offsetT = DISABLED_OFFSET_Y; - - buttons[i] = Overlays.addOverlay("image", { - x: OFFSET_X, - y: OFFSET_Y + (BUTTON_HEIGHT * i), - width: BUTTON_WIDTH, - height: BUTTON_HEIGHT, - subImage: { x: offsetS, y: offsetT, width: BUTTON_WIDTH, height: BUTTON_HEIGHT }, - imageURL: BUTTON_IMAGE_URL, - color: disabledColors[i], - alpha: 1, - }); - - labels[i] = Overlays.addOverlay("text", { - x: TEXT_OFFSET_X, - y: OFFSET_Y + (BUTTON_HEIGHT * i), - width: TEXT_WIDTH, - height: TEXT_HEIGHT, - color: { red: 0, green: 0, blue: 0}, - textColor: { red: 255, green: 0, blue: 0}, - topMargin: 4, - leftMargin: 4, - text: labelContents[i] - }); - - buttonStates[i] = false; -} - - -// functions - -function updateButton(i, enabled) { - var offsetY = DISABLED_OFFSET_Y; - var buttonColor = disabledColors[i]; - if (enabled) { - offsetY = ENABLED_OFFSET_Y; - buttonColor = enabledColors[i]; - if (i == 0) { - groupBits |= COLLISION_GROUP_AVATARS; - } else if (i == 1) { - groupBits |= COLLISION_GROUP_VOXELS; - } else if (i == 2) { - groupBits |= COLLISION_GROUP_PARTICLES; - } - } else { - if (i == 0) { - groupBits &= ~COLLISION_GROUP_AVATARS; - } else if (i == 1) { - groupBits &= ~COLLISION_GROUP_VOXELS; - } else if (i == 2) { - groupBits &= ~COLLISION_GROUP_PARTICLES; - } - } - if (groupBits != MyAvatar.collisionGroups) { - MyAvatar.collisionGroups = groupBits; - } - - Overlays.editOverlay(buttons[i], { subImage: { y: offsetY } } ); - Overlays.editOverlay(buttons[i], { color: buttonColor } ); - buttonStates[i] = enabled; -} - - -// When our script shuts down, we should clean up all of our overlays -function scriptEnding() { - for (i = 0; i < NUMBER_OF_BUTTONS; i++) { - Overlays.deleteOverlay(buttons[i]); - Overlays.deleteOverlay(labels[i]); - } - Overlays.deleteOverlay(speedometer); -} -Script.scriptEnding.connect(scriptEnding); - - -function updateSpeedometerDisplay() { - Overlays.editOverlay(speedometer, { text: "Speed: " + speed.toFixed(2) }); -} -Script.setInterval(updateSpeedometerDisplay, 100); - -function disableArtificialGravity() { - MyAvatar.motionBehaviors = MyAvatar.motionBehaviors & ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY; - updateButton(3, false); -} -// call this immediately so that avatar doesn't fall before voxel data arrives -// Ideally we would only do this on LOGIN, not when starting the script -// in the middle of a session. -disableArtificialGravity(); - -function enableArtificialGravity() { - // NOTE: setting the gravity automatically sets the AVATAR_MOTION_OBEY_LOCAL_GRAVITY behavior bit. - MyAvatar.gravity = DOWN; - updateButton(3, true); - // also enable collisions with voxels - groupBits |= COLLISION_GROUP_VOXELS; - updateButton(1, groupBits & COLLISION_GROUP_VOXELS); -} - -// Our update() function is called at approximately 60fps, and we will use it to animate our various overlays -function update(deltaTime) { - if (groupBits != MyAvatar.collisionGroups) { - groupBits = MyAvatar.collisionGroups; - updateButton(0, groupBits & COLLISION_GROUP_AVATARS); - updateButton(1, groupBits & COLLISION_GROUP_VOXELS); - updateButton(2, groupBits & COLLISION_GROUP_PARTICLES); - } - - // measure speed - var distance = Vec3.distance(MyAvatar.position, lastPosition); - speed = 0.8 * speed + 0.2 * distance / deltaTime; - lastPosition = MyAvatar.position; - - dater = new Date(); - var now = dater.getTime(); - - // transition gravity - if (raycastExpiry < now) { - // scan for landing platform - ray = { origin: MyAvatar.position, direction: DOWN }; - var intersection = Voxels.findRayIntersection(ray); - // NOTE: it is possible for intersection.intersects to be false when it should be true - // (perhaps the raycast failed to lock the octree thread?). To workaround this problem - // we only transition on repeated failures. - - if (intersection.intersects) { - // compute distance to voxel - var v = intersection.voxel; - var maxCorner = Vec3.sum({ x: v.x, y: v.y, z: v.z }, {x: v.s, y: v.s, z: v.s }); - var distance = lastPosition.y - maxCorner.y; - - if (distance < MAX_VOXEL_SCAN_DISTANCE) { - if (speed < MIN_FLYING_SPEED && - gravityOnExpiry < now && - !(MyAvatar.motionBehaviors & AVATAR_MOTION_OBEY_LOCAL_GRAVITY)) { - enableArtificialGravity(); - } - if (speed < MAX_WALKING_SPEED) { - gravityOffExpiry = now + GRAVITY_OFF_EXPIRY_PERIOD; - } else if (gravityOffExpiry < now && MyAvatar.motionBehaviors & AVATAR_MOTION_OBEY_LOCAL_GRAVITY) { - disableArtificialGravity(); - } - } else { - // distance too far - if (gravityOffExpiry < now && MyAvatar.motionBehaviors & AVATAR_MOTION_OBEY_LOCAL_GRAVITY) { - disableArtificialGravity(); - } - gravityOnExpiry = now + GRAVITY_ON_EXPIRY_PERIOD; - } - } else { - // no intersection - if (gravityOffExpiry < now && MyAvatar.motionBehaviors & AVATAR_MOTION_OBEY_LOCAL_GRAVITY) { - disableArtificialGravity(); - } - gravityOnExpiry = now + GRAVITY_ON_EXPIRY_PERIOD; - } - } - if (speed > MAX_WALKING_SPEED && gravityOffExpiry < now) { - if (MyAvatar.motionBehaviors & AVATAR_MOTION_OBEY_LOCAL_GRAVITY) { - // turn off gravity - MyAvatar.motionBehaviors = MyAvatar.motionBehaviors & ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY; - updateButton(3, false); - } - gravityOnExpiry = now + GRAVITY_ON_EXPIRY_PERIOD; - } - - // transition collidability with voxels - if (speed < MIN_COLLISIONLESS_SPEED) { - if (collisionOnExpiry < now && !(MyAvatar.collisionGroups & COLLISION_GROUP_VOXELS)) { - // TODO: check to make sure not already colliding - // enable collision with voxels - groupBits |= COLLISION_GROUP_VOXELS; - updateButton(1, groupBits & COLLISION_GROUP_VOXELS); - } - } else { - collisionOnExpiry = now + COLLISION_EXPIRY_PERIOD; - } - if (speed > MAX_COLLIDABLE_SPEED) { - if (MyAvatar.collisionGroups & COLLISION_GROUP_VOXELS) { - // disable collisions with voxels - groupBits &= ~COLLISION_GROUP_VOXELS; - updateButton(1, groupBits & COLLISION_GROUP_VOXELS); - } - } -} -Script.update.connect(update); - -// we also handle click detection in our mousePressEvent() -function mousePressEvent(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - for (i = 0; i < NUMBER_OF_COLLISION_BUTTONS; i++) { - if (clickedOverlay == buttons[i]) { - var enabled = !(buttonStates[i]); - updateButton(i, enabled); - } - } -} -Controller.mousePressEvent.connect(mousePressEvent); - From 49e3f4bdb0a6e25b9aa799e7cc0546e8299d17c9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 22 Sep 2014 09:20:34 -0700 Subject: [PATCH 19/19] use 10.8 only in deployment target, base SDK should be latest --- CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 28ff00cc96..4c0bfb0892 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,8 +35,7 @@ endif () set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_CMAKE_PREFIX_PATH}) -# set our Base SDK to 10.8 -set(CMAKE_OSX_SYSROOT /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk) +# set our OS X deployment target to set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8) # Find includes in corresponding build directories