diff --git a/cmake/externals/hifiAudioCodec/CMakeLists.txt b/cmake/externals/hifiAudioCodec/CMakeLists.txt index 3a8c714d79..87b5044b42 100644 --- a/cmake/externals/hifiAudioCodec/CMakeLists.txt +++ b/cmake/externals/hifiAudioCodec/CMakeLists.txt @@ -1,19 +1,31 @@ include(ExternalProject) include(SelectLibraryConfigurations) -set(EXTERNAL_NAME HiFiAudioCodec) +set(EXTERNAL_NAME hifiAudioCodec) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://s3.amazonaws.com/hifi-public/dependencies/codecSDK-1.zip - URL_MD5 23ec3fe51eaa155ea159a4971856fc13 - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 -) +if (WIN32 OR APPLE) + ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-1.zip + URL_MD5 23ec3fe51eaa155ea159a4971856fc13 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + ) +elseif(NOT ANDROID) + ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux.zip + URL_MD5 7d37914a18aa4de971d2f45dd3043bde + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + ) +endif() # Hide this external target (for ide users) set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") @@ -23,11 +35,9 @@ ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL) if (WIN32) - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL) elseif(APPLE) set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL) elseif(NOT ANDROID) - # FIXME need to account for different architectures - #set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux64/audio.so CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL) endif() - diff --git a/cmake/macros/AddDependencyExternalProjects.cmake b/cmake/macros/AddDependencyExternalProjects.cmake index e35ca98959..99b8317fd7 100644 --- a/cmake/macros/AddDependencyExternalProjects.cmake +++ b/cmake/macros/AddDependencyExternalProjects.cmake @@ -43,4 +43,4 @@ macro(ADD_DEPENDENCY_EXTERNAL_PROJECTS) endforeach() -endmacro() \ No newline at end of file +endmacro() diff --git a/cmake/macros/SetupHifiClientServerPlugin.cmake b/cmake/macros/SetupHifiClientServerPlugin.cmake index 8ba38a09c3..74ceaeb0ab 100644 --- a/cmake/macros/SetupHifiClientServerPlugin.cmake +++ b/cmake/macros/SetupHifiClientServerPlugin.cmake @@ -37,7 +37,7 @@ macro(SETUP_HIFI_CLIENT_SERVER_PLUGIN) ${CLIENT_PLUGIN_FULL_PATH} ) # copy the client plugin binaries - add_custom_command(TARGET ${DIR} POST_BUILD + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy "$" ${CLIENT_PLUGIN_FULL_PATH} @@ -50,7 +50,7 @@ macro(SETUP_HIFI_CLIENT_SERVER_PLUGIN) ${SERVER_PLUGIN_FULL_PATH} ) # copy the server plugin binaries - add_custom_command(TARGET ${DIR} POST_BUILD + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy "$" ${SERVER_PLUGIN_FULL_PATH} diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index bac3b1e02f..5acee052f2 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -207,8 +207,10 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { } withWriteLock([&]{ - if (_previousSet) { + if (_previousSet && + _positionalTarget != _previousPositionalTarget) { // don't average in a zero velocity if we get the same data glm::vec3 oneFrameVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; + _measuredLinearVelocities[_measuredLinearVelocitiesIndex++] = oneFrameVelocity; if (_measuredLinearVelocitiesIndex >= AvatarActionHold::velocitySmoothFrames) { _measuredLinearVelocitiesIndex = 0; @@ -228,9 +230,9 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { // 3 -- ignore i of 0 1 2 // 4 -- ignore i of 1 2 3 // 5 -- ignore i of 2 3 4 - if ((i + 1) % 6 == _measuredLinearVelocitiesIndex || - (i + 2) % 6 == _measuredLinearVelocitiesIndex || - (i + 3) % 6 == _measuredLinearVelocitiesIndex) { + if ((i + 1) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex || + (i + 2) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex || + (i + 3) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex) { continue; } measuredLinearVelocity += _measuredLinearVelocities[i]; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index b5a7c8c0cf..7cf8574529 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -862,6 +862,9 @@ void AudioClient::mixLocalAudioInjectors(int16_t* inputBuffer) { static const float INT16_TO_FLOAT_SCALE_FACTOR = 1/32768.0f; bool injectorsHaveData = false; + + // lock the injector vector + Lock lock(_injectorsMutex); for (AudioInjector* injector : getActiveLocalAudioInjectors()) { if (injector->getLocalBuffer()) { @@ -871,6 +874,7 @@ void AudioClient::mixLocalAudioInjectors(int16_t* inputBuffer) { AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; // get one frame from the injector (mono or stereo) + memset(_scratchBuffer, 0, sizeof(_scratchBuffer)); if (0 < injector->getLocalBuffer()->readData((char*)_scratchBuffer, samplesToRead)) { injectorsHaveData = true; @@ -894,14 +898,14 @@ void AudioClient::mixLocalAudioInjectors(int16_t* inputBuffer) { } else { qDebug() << "injector has no more data, marking finished for removal"; - injector->finish(); + injector->finishLocalInjection(); injectorsToRemove.append(injector); } } else { qDebug() << "injector has no local buffer, marking as finished for removal"; - injector->finish(); + injector->finishLocalInjection(); injectorsToRemove.append(injector); } } @@ -1003,6 +1007,7 @@ void AudioClient::setIsStereoInput(bool isStereoInput) { bool AudioClient::outputLocalInjector(bool isStereo, AudioInjector* injector) { + Lock lock(_injectorsMutex); if (injector->getLocalBuffer() && _audioInput ) { // just add it to the vector of active local injectors, if // not already there. diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 3e4aa931a6..472092163b 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,9 @@ public: using AudioPositionGetter = std::function; using AudioOrientationGetter = std::function; + using Mutex = std::mutex; + using Lock = std::unique_lock; + class AudioOutputIODevice : public QIODevice { public: AudioOutputIODevice(MixedProcessedAudioStream& receivedAudioStream, AudioClient* audio) : @@ -219,6 +223,7 @@ private: float azimuthForSource(const glm::vec3& relativePosition); float gainForSource(float distance, float volume); + Mutex _injectorsMutex; QByteArray firstInputFrame; QAudioInput* _audioInput; QAudioFormat _desiredInputFormat; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 08143b8491..58122fee3c 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -28,6 +28,15 @@ int audioInjectorPtrMetaTypeId = qRegisterMetaType(); +AudioInjectorState operator& (AudioInjectorState lhs, AudioInjectorState rhs) { + return static_cast(static_cast(lhs) & static_cast(rhs)); +}; + +AudioInjectorState& operator|= (AudioInjectorState& lhs, AudioInjectorState rhs) { + lhs = static_cast(static_cast(lhs) | static_cast(rhs)); + return lhs; +}; + AudioInjector::AudioInjector(QObject* parent) : QObject(parent) { @@ -48,6 +57,10 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt } +bool AudioInjector::stateHas(AudioInjectorState state) const { + return (_state & state) == state; +} + void AudioInjector::setOptions(const AudioInjectorOptions& options) { // since options.stereo is computed from the audio stream, // we need to copy it from existing options just in case. @@ -56,10 +69,25 @@ void AudioInjector::setOptions(const AudioInjectorOptions& options) { _options.stereo = currentlyStereo; } +void AudioInjector::finishNetworkInjection() { + _state |= AudioInjectorState::NetworkInjectionFinished; + + // if we are already finished with local + // injection, then we are finished + if(stateHas(AudioInjectorState::LocalInjectionFinished)) { + finish(); + } +} + +void AudioInjector::finishLocalInjection() { + _state |= AudioInjectorState::LocalInjectionFinished; + if(_options.localOnly || stateHas(AudioInjectorState::NetworkInjectionFinished)) { + finish(); + } +} void AudioInjector::finish() { - bool shouldDelete = (_state == State::NotFinishedWithPendingDelete); - _state = State::Finished; + _state |= AudioInjectorState::Finished; emit finished(); @@ -69,7 +97,7 @@ void AudioInjector::finish() { _localBuffer = NULL; } - if (shouldDelete) { + if (stateHas(AudioInjectorState::PendingDelete)) { // we've been asked to delete after finishing, trigger a deleteLater here deleteLater(); } @@ -121,23 +149,27 @@ void AudioInjector::restart() { _hasSentFirstFrame = false; // check our state to decide if we need extra handling for the restart request - if (_state == State::Finished) { + if (stateHas(AudioInjectorState::Finished)) { // we finished playing, need to reset state so we can get going again _hasSetup = false; _shouldStop = false; - _state = State::NotFinished; + _state = AudioInjectorState::NotFinished; // call inject audio to start injection over again setupInjection(); - // if we're a local injector, just inject again - if (_options.localOnly) { - injectLocally(); - } else { - // wake the AudioInjectorManager back up if it's stuck waiting - if (!injectorManager->restartFinishedInjector(this)) { - _state = State::Finished; // we're not playing, so reset the state used by isPlaying. + // inject locally + if(injectLocally()) { + + // if not localOnly, wake the AudioInjectorManager back up if it is stuck waiting + if (!_options.localOnly) { + + if (!injectorManager->restartFinishedInjector(this)) { + _state = AudioInjectorState::Finished; // we're not playing, so reset the state used by isPlaying. + } } + } else { + _state = AudioInjectorState::Finished; // we failed to play, so we are finished again } } } @@ -183,7 +215,7 @@ static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1; static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0; int64_t AudioInjector::injectNextFrame() { - if (_state == AudioInjector::State::Finished) { + if (stateHas(AudioInjectorState::NetworkInjectionFinished)) { qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning."; return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } @@ -234,8 +266,10 @@ int64_t AudioInjector::injectNextFrame() { // pack the stereo/mono type of the stream audioPacketStream << _options.stereo; - // pack the flag for loopback - uchar loopbackFlag = (uchar)true; + // pack the flag for loopback. Now, we don't loopback + // and _always_ play locally, so loopbackFlag should be + // false always. + uchar loopbackFlag = (uchar)false; audioPacketStream << loopbackFlag; // pack the position for injected audio @@ -333,7 +367,7 @@ int64_t AudioInjector::injectNextFrame() { } if (_currentSendOffset >= _audioData.size() && !_options.loop) { - finish(); + finishNetworkInjection(); return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } @@ -372,10 +406,10 @@ void AudioInjector::triggerDeleteAfterFinish() { return; } - if (_state == State::Finished) { + if (_state == AudioInjectorState::Finished) { stopAndDeleteLater(); } else { - _state = State::NotFinishedWithPendingDelete; + _state |= AudioInjectorState::PendingDelete; } } @@ -421,7 +455,7 @@ AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjector* sound = playSound(buffer, options, localInterface); if (sound) { - sound->_state = AudioInjector::State::NotFinishedWithPendingDelete; + sound->_state |= AudioInjectorState::PendingDelete; } return sound; @@ -438,21 +472,23 @@ AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInj // setup parameters required for injection injector->setupInjection(); - if (options.localOnly) { - if (injector->injectLocally()) { - // local injection succeeded, return the pointer to injector - return injector; - } else { - // unable to inject locally, return a nullptr - return nullptr; - } - } else { - // attempt to thread the new injector - if (injectorManager->threadInjector(injector)) { - return injector; - } else { - // we failed to thread the new injector (we are at the max number of injector threads) - return nullptr; - } + // we always inject locally + // + if (!injector->injectLocally()) { + // failed, so don't bother sending to server + qDebug() << "AudioInjector::playSound failed to inject locally"; + return nullptr; } + // if localOnly, we are done, just return injector. + if (options.localOnly) { + return injector; + } + + // send off to server for everyone else + if (!injectorManager->threadInjector(injector)) { + // we failed to thread the new injector (we are at the max number of injector threads) + qDebug() << "AudioInjector::playSound failed to thread injector"; + } + return injector; + } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 1af733ace6..b872800e15 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -32,24 +32,29 @@ class AbstractAudioInterface; class AudioInjectorManager; + +enum class AudioInjectorState : uint8_t { + NotFinished = 0, + Finished = 1, + PendingDelete = 2, + LocalInjectionFinished = 4, + NetworkInjectionFinished = 8 +}; + +AudioInjectorState operator& (AudioInjectorState lhs, AudioInjectorState rhs); +AudioInjectorState& operator|= (AudioInjectorState& lhs, AudioInjectorState rhs); + // In order to make scripting cleaner for the AudioInjector, the script now holds on to the AudioInjector object // until it dies. - class AudioInjector : public QObject { Q_OBJECT public: - enum class State : uint8_t { - NotFinished, - NotFinishedWithPendingDelete, - Finished - }; - AudioInjector(QObject* parent); AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions); AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions); - bool isFinished() const { return _state == State::Finished; } + bool isFinished() const { return (stateHas(AudioInjectorState::Finished)); } int getCurrentSendOffset() const { return _currentSendOffset; } void setCurrentSendOffset(int currentSendOffset) { _currentSendOffset = currentSendOffset; } @@ -63,6 +68,7 @@ public: bool isStereo() const { return _options.stereo; } void setLocalAudioInterface(AbstractAudioInterface* localAudioInterface) { _localAudioInterface = localAudioInterface; } + bool stateHas(AudioInjectorState state) const ; static AudioInjector* playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface); static AudioInjector* playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface); static AudioInjector* playSound(SharedSoundPointer sound, const float volume, const float stretchFactor, const glm::vec3 position); @@ -78,8 +84,10 @@ public slots: void setOptions(const AudioInjectorOptions& options); float getLoudness() const { return _loudness; } - bool isPlaying() const { return _state == State::NotFinished || _state == State::NotFinishedWithPendingDelete; } + bool isPlaying() const { return !stateHas(AudioInjectorState::Finished); } void finish(); + void finishLocalInjection(); + void finishNetworkInjection(); signals: void finished(); @@ -92,7 +100,7 @@ private: QByteArray _audioData; AudioInjectorOptions _options; - State _state { State::NotFinished }; + AudioInjectorState _state { AudioInjectorState::NotFinished }; bool _hasSentFirstFrame { false }; bool _hasSetup { false }; bool _shouldStop { false }; @@ -111,4 +119,5 @@ private: friend class AudioInjectorManager; }; + #endif // hifi_AudioInjector_h diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index eec3d2bf6b..a6b5a03ff6 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -36,7 +36,15 @@ OffscreenGLCanvas::~OffscreenGLCanvas() { delete _logger; _logger = nullptr; } + _context->doneCurrent(); + delete _context; + _context = nullptr; + + _offscreenSurface->destroy(); + delete _offscreenSurface; + _offscreenSurface = nullptr; + } bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h index 69210f638d..8def09796d 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.h +++ b/libraries/gl/src/gl/OffscreenGLCanvas.h @@ -34,8 +34,8 @@ public: protected: std::once_flag _reportOnce; - QOpenGLContext* _context; - QOffscreenSurface* _offscreenSurface; + QOpenGLContext* _context{ nullptr }; + QOffscreenSurface* _offscreenSurface{ nullptr }; QOpenGLDebugLogger* _logger{ nullptr }; }; diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp index bc8afc3927..0b153a5ae8 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp @@ -28,16 +28,17 @@ QOpenGLContext* QOpenGLContextWrapper::currentContext() { return QOpenGLContext::currentContext(); } - QOpenGLContextWrapper::QOpenGLContextWrapper() : -_context(new QOpenGLContext) -{ -} - + _ownContext(true), _context(new QOpenGLContext) { } QOpenGLContextWrapper::QOpenGLContextWrapper(QOpenGLContext* context) : - _context(context) -{ + _context(context) { } + +QOpenGLContextWrapper::~QOpenGLContextWrapper() { + if (_ownContext) { + delete _context; + _context = nullptr; + } } void QOpenGLContextWrapper::setFormat(const QSurfaceFormat& format) { diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.h b/libraries/gl/src/gl/QOpenGLContextWrapper.h index d097284e68..b09ad1a4c3 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.h +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.h @@ -23,6 +23,7 @@ class QOpenGLContextWrapper { public: QOpenGLContextWrapper(); QOpenGLContextWrapper(QOpenGLContext* context); + virtual ~QOpenGLContextWrapper(); void setFormat(const QSurfaceFormat& format); bool create(); void swapBuffers(QSurface* surface); @@ -40,6 +41,7 @@ public: private: + bool _ownContext { false }; QOpenGLContext* _context { nullptr }; }; diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index ed931437b7..74428b53f5 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -187,7 +187,11 @@ GLTexture::~GLTexture() { } } - Backend::decrementTextureGPUCount(); + if (_id) { + glDeleteTextures(1, &_id); + const_cast(_id) = 0; + Backend::decrementTextureGPUCount(); + } Backend::updateTextureGPUMemoryUsage(_size, 0); Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); } diff --git a/plugins/hifiCodec/CMakeLists.txt b/plugins/hifiCodec/CMakeLists.txt index 0af7e42ea1..b278e839e4 100644 --- a/plugins/hifiCodec/CMakeLists.txt +++ b/plugins/hifiCodec/CMakeLists.txt @@ -6,15 +6,11 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -if (WIN32 OR APPLE) - set(TARGET_NAME hifiCodec) - setup_hifi_client_server_plugin() - - link_hifi_libraries(audio shared plugins) - - add_dependency_external_projects(HiFiAudioCodec) - target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAUDIOCODEC_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${HIFIAUDIOCODEC_LIBRARIES}) - install_beside_console() -endif() +set(TARGET_NAME hifiCodec) +setup_hifi_client_server_plugin() +link_hifi_libraries(audio shared plugins) +add_dependency_external_projects(hifiAudioCodec) +target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAUDIOCODEC_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${HIFIAUDIOCODEC_LIBRARIES}) +install_beside_console() diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 66c9e10795..cf8146fba9 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -202,8 +202,7 @@ CONTROLLER_STATE_MACHINE[STATE_NEAR_GRABBING] = { CONTROLLER_STATE_MACHINE[STATE_HOLD] = { name: "hold", enterMethod: "nearGrabbingEnter", - updateMethod: "nearGrabbing", - exitMethod: "holdExit" + updateMethod: "nearGrabbing" }; CONTROLLER_STATE_MACHINE[STATE_NEAR_TRIGGER] = { name: "trigger", @@ -228,7 +227,7 @@ function colorPow(color, power) { return { red: Math.pow(color.red / 255.0, power) * 255, green: Math.pow(color.green / 255.0, power) * 255, - blue: Math.pow(color.blue / 255.0, power) * 255, + blue: Math.pow(color.blue / 255.0, power) * 255 }; } @@ -271,14 +270,12 @@ function propsArePhysical(props) { return isPhysical; } -// currently disabled. -var USE_ATTACH_POINT_SETTINGS = false; +var USE_ATTACH_POINT_SETTINGS = true; var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints"; function getAttachPointSettings() { try { var str = Settings.getValue(ATTACH_POINT_SETTINGS); - print("getAttachPointSettings = " + str); if (str === "false") { return {}; } else { @@ -291,7 +288,6 @@ function getAttachPointSettings() { } function setAttachPointSettings(attachPointSettings) { var str = JSON.stringify(attachPointSettings); - print("setAttachPointSettings = " + str); Settings.setValue(ATTACH_POINT_SETTINGS, str); } function getAttachPointForHotspotFromSettings(hotspot, hand) { @@ -765,9 +761,8 @@ function MyController(hand) { } }; - var SEARCH_SPHERE_ALPHA = 0.5; this.searchSphereOn = function (location, size, color) { - + var rotation = Quat.lookAt(location, Camera.getPosition(), Vec3.UP); var brightColor = colorPow(color, 0.06); if (this.searchSphere === null) { @@ -790,7 +785,7 @@ function MyController(hand) { position: location, rotation: rotation, innerColor: brightColor, - outerColor: color, + outerColor: color, innerAlpha: 1.0, outerAlpha: 0.0, outerRadius: size * 1.2, @@ -1961,12 +1956,12 @@ function MyController(hand) { this.currentObjectRotation = grabbedProperties.rotation; this.currentVelocity = ZERO_VEC; this.currentAngularVelocity = ZERO_VEC; + + this.prevDropDetected = false; }; this.nearGrabbing = function (deltaTime, timestamp) { - var dropDetected = this.dropGestureProcess(deltaTime); - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); @@ -1975,6 +1970,16 @@ function MyController(hand) { if (this.state == STATE_HOLD) { + var dropDetected = this.dropGestureProcess(deltaTime); + + if (this.triggerSmoothedReleased()) { + this.waitForTriggerRelease = false; + } + + if (dropDetected && this.prevDropDetected != dropDetected) { + this.waitForTriggerRelease = true; + } + // highlight the grabbed hotspot when the dropGesture is detected. if (dropDetected) { entityPropertiesCache.addEntity(this.grabbedHotspot.entityID); @@ -1982,17 +1987,24 @@ function MyController(hand) { equipHotspotBuddy.highlightHotspot(this.grabbedHotspot); } - if (dropDetected && this.triggerSmoothedGrab()) { + if (dropDetected && !this.waitForTriggerRelease && this.triggerSmoothedGrab()) { this.callEntityMethodOnGrabbed("releaseEquip"); - this.setState(STATE_OFF, "drop gesture detected"); - return; - } - if (this.thumbPressed()) { - this.callEntityMethodOnGrabbed("releaseEquip"); - this.setState(STATE_OFF, "drop via thumb press"); + // store the offset attach points into preferences. + if (USE_ATTACH_POINT_SETTINGS && this.grabbedHotspot && this.grabbedEntity) { + var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]); + if (props && props.localPosition && props.localRotation) { + storeAttachPointForHotspotInSettings(this.grabbedHotspot, this.hand, props.localPosition, props.localRotation); + } + } + + var grabbedEntity = this.grabbedEntity; + this.release(); + this.grabbedEntity = grabbedEntity; + this.setState(STATE_NEAR_GRABBING, "drop gesture detected"); return; } + this.prevDropDetected = dropDetected; } this.heartBeat(this.grabbedEntity); @@ -2088,22 +2100,6 @@ function MyController(hand) { } }; - this.holdExit = function () { - // store the offset attach points into preferences. - if (USE_ATTACH_POINT_SETTINGS && this.grabbedHotspot && this.grabbedEntity) { - entityPropertiesCache.addEntity(this.grabbedEntity); - var props = entityPropertiesCache.getProps(this.grabbedEntity); - var entityXform = new Xform(props.rotation, props.position); - var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); - var handRot = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation(); - var avatarHandPos = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var palmXform = new Xform(handRot, avatarXform.xformPoint(avatarHandPos)); - var offsetXform = Xform.mul(palmXform.inv(), entityXform); - - storeAttachPointForHotspotInSettings(this.grabbedHotspot, this.hand, offsetXform.pos, offsetXform.rot); - } - }; - this.nearTriggerEnter = function () { this.clearEquipHaptics();