diff --git a/cmake/macros/AddResourcesToOSXBundle.cmake b/cmake/macros/AddResourcesToOSXBundle.cmake new file mode 100644 index 0000000000..113f751731 --- /dev/null +++ b/cmake/macros/AddResourcesToOSXBundle.cmake @@ -0,0 +1,40 @@ +# +# AddResourcesToOSXBundle.cmake +# cmake/macros +# +# Created by Stephen Birarda on 04/27/15. +# Copyright 2015 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html +# + +macro(_RECURSIVELY_SET_PACKAGE_LOCATION _PATH) + # enumerate the items + foreach(_ITEM ${ARGN}) + + if (IS_DIRECTORY "${_ITEM}") + # recurse into the directory and check for more resources + file(GLOB _DIR_CONTENTS "${_ITEM}/*") + + # figure out the path for this directory and then call ourselves to recurse into it + get_filename_component(_ITEM_PATH ${_ITEM} NAME) + _recursively_set_package_location("${_PATH}/${_ITEM_PATH}" ${_DIR_CONTENTS}) + else () + # add any files in the root to the resources folder directly + SET_SOURCE_FILES_PROPERTIES(${_ITEM} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources${_PATH}") + list(APPEND DISCOVERED_RESOURCES ${_ITEM}) + endif () + + endforeach() +endmacro(_RECURSIVELY_SET_PACKAGE_LOCATION _PATH) + +macro(ADD_RESOURCES_TO_OS_X_BUNDLE _RSRC_FOLDER) + + # GLOB the resource directory + file(GLOB _ROOT_ITEMS "${_RSRC_FOLDER}/*") + + # recursively enumerate from the root items + _recursively_set_package_location("" ${_ROOT_ITEMS}) + +endmacro(ADD_RESOURCES_TO_OS_X_BUNDLE _RSRC_FOLDER) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d7cd7ec47d..1ddf1cb7db 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -504,7 +504,7 @@ void DomainServer::populateStaticScriptedAssignmentsFromSettings() { Assignment::AgentType, scriptPool); scriptAssignment->setPayload(scriptURL.toUtf8()); - + // add it to static hash so we know we have to keep giving it back out addStaticAssignmentToAssignmentHash(scriptAssignment); } @@ -675,6 +675,10 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID()); nodeData->setWalletUUID(pendingAssigneeData->getWalletUUID()); + // always allow assignment clients to create and destroy entities + newNode->setCanAdjustLocks(true); + newNode->setCanRez(true); + // now that we've pulled the wallet UUID and added the node to our list, delete the pending assignee data delete pendingAssigneeData; } @@ -2119,10 +2123,10 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig Assignment* assignment = sharedAssignment->data(); bool requestIsAllTypes = requestAssignment.getType() == Assignment::AllTypes; bool assignmentTypesMatch = assignment->getType() == requestAssignment.getType(); - bool nietherHasPool = assignment->getPool().isEmpty() && requestAssignment.getPool().isEmpty(); + bool neitherHasPool = assignment->getPool().isEmpty() && requestAssignment.getPool().isEmpty(); bool assignmentPoolsMatch = assignment->getPool() == requestAssignment.getPool(); - if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) { + if ((requestIsAllTypes || assignmentTypesMatch) && (neitherHasPool || assignmentPoolsMatch)) { // remove the assignment from the queue SharedAssignmentPointer deployableAssignment = _unfulfilledAssignments.takeAt(sharedAssignment diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index b4e0e3a244..9e282f4725 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -88,18 +88,15 @@ if (APPLE) # set where in the bundle to put the resources file SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/icon/${ICON_FILENAME} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - # grab the directories in resources and put them in the right spot in Resources - file(GLOB RESOURCE_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/resources" "${CMAKE_CURRENT_SOURCE_DIR}/resources/*") - foreach(DIR ${RESOURCE_SUBDIRS}) - if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/resources/${DIR}") - FILE(GLOB DIR_CONTENTS "resources/${DIR}/*") - SET_SOURCE_FILES_PROPERTIES(${DIR_CONTENTS} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/${DIR}") + set(DISCOVERED_RESOURCES "") - SET(INTERFACE_SRCS ${INTERFACE_SRCS} "${DIR_CONTENTS}") - endif() - endforeach() + # use the add_resources_to_os_x_bundle macro to recurse into resources + add_resources_to_os_x_bundle("${CMAKE_CURRENT_SOURCE_DIR}/resources") - SET(INTERFACE_SRCS ${INTERFACE_SRCS} "${CMAKE_CURRENT_SOURCE_DIR}/icon/${ICON_FILENAME}") + # append the discovered resources to our list of interface sources + list(APPEND INTERFACE_SRCS ${DISCOVERED_RESOURCES}) + + set(INTERFACE_SRCS ${INTERFACE_SRCS} "${CMAKE_CURRENT_SOURCE_DIR}/icon/${ICON_FILENAME}") endif() # create the executable, make it a bundle on OS X diff --git a/interface/src/Application.h b/interface/src/Application.h index 688cf23876..9f87d05711 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -119,9 +119,9 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; #ifdef Q_OS_WIN static const UINT UWM_IDENTIFY_INSTANCES = - RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}"); + RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME")); static const UINT UWM_SHOW_APPLICATION = - RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}"); + RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}_" + qgetenv("USERNAME")); #endif class Application; diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index b84fd23614..41c2e9b54c 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -73,20 +73,20 @@ void Head::reset() { } 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.0f; + _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)); + } + if (isMine) { MyAvatar* myAvatar = static_cast(_owningAvatar); - // Update audio trailing average for rendering facial animations - const float AUDIO_AVERAGING_SECS = 0.05f; - const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f; - _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)); - } - // Only use face trackers when not playing back a recording. if (!myAvatar->isPlaying()) { FaceTracker* faceTracker = Application::getInstance()->getActiveFaceTracker(); diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index 8f1b070c02..be25c0794d 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -136,9 +136,6 @@ struct Packet { const float STARTING_DDE_MESSAGE_TIME = 0.033f; -const int FPS_TIMER_DELAY = 2000; // ms -const int FPS_TIMER_DURATION = 2000; // ms - DdeFaceTracker::DdeFaceTracker() : DdeFaceTracker(QHostAddress::Any, DDE_SERVER_PORT, DDE_CONTROL_PORT) { @@ -147,6 +144,7 @@ DdeFaceTracker::DdeFaceTracker() : DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, quint16 controlPort) : _ddeProcess(NULL), + _ddeStopping(false), _host(host), _serverPort(serverPort), _controlPort(controlPort), @@ -171,9 +169,7 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui _lastLeftEyeBlink(0.0f), _filteredLeftEyeBlink(0.0f), _lastRightEyeBlink(0.0f), - _filteredRightEyeBlink(0.0f), - _isCalculatingFPS(false), - _frameCount(0) + _filteredRightEyeBlink(0.0f) { _coefficients.resize(NUM_FACESHIFT_BLENDSHAPES); @@ -201,46 +197,50 @@ void DdeFaceTracker::setEnabled(bool enabled) { if (enabled && !_ddeProcess) { // Terminate any existing DDE process, perhaps left running after an Interface crash _udpSocket.writeDatagram(DDE_EXIT_COMMAND, DDE_SERVER_ADDR, _controlPort); + _ddeStopping = false; - qDebug() << "[Info] DDE Face Tracker Starting"; + qCDebug(interfaceapp) << "DDE Face Tracker: Starting"; _ddeProcess = new QProcess(qApp); connect(_ddeProcess, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus))); _ddeProcess->start(QCoreApplication::applicationDirPath() + DDE_PROGRAM_PATH, DDE_ARGUMENTS); } if (!enabled && _ddeProcess) { + _ddeStopping = true; _udpSocket.writeDatagram(DDE_EXIT_COMMAND, DDE_SERVER_ADDR, _controlPort); - delete _ddeProcess; - _ddeProcess = NULL; - qDebug() << "[Info] DDE Face Tracker Stopped"; + qCDebug(interfaceapp) << "DDE Face Tracker: Stopping"; } #endif } void DdeFaceTracker::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (_ddeProcess) { - // DDE crashed or was manually terminated - qDebug() << "[Info] DDE Face Tracker Stopped Unexpectedly"; + if (_ddeStopping) { + qCDebug(interfaceapp) << "DDE Face Tracker: Stopped"; + + } else { + qCWarning(interfaceapp) << "DDE Face Tracker: Stopped unexpectedly"; + Menu::getInstance()->setIsOptionChecked(MenuOption::NoFaceTracking, true); + } _udpSocket.close(); + delete _ddeProcess; _ddeProcess = NULL; - Menu::getInstance()->setIsOptionChecked(MenuOption::NoFaceTracking, true); } } void DdeFaceTracker::reset() { - _reset = true; + if (_udpSocket.state() == QAbstractSocket::BoundState) { + _reset = true; - qDebug() << "[Info] Reset DDE Tracking"; - const char* DDE_RESET_COMMAND = "reset"; - _udpSocket.writeDatagram(DDE_RESET_COMMAND, DDE_SERVER_ADDR, _controlPort); + qCDebug(interfaceapp) << "DDE Face Tracker: Reset"; - // Log camera FPS after a reset - if (!_isCalculatingFPS) { - QTimer::singleShot(FPS_TIMER_DELAY, this, SLOT(startFPSTimer())); - _isCalculatingFPS = true; + const char* DDE_RESET_COMMAND = "reset"; + _udpSocket.writeDatagram(DDE_RESET_COMMAND, DDE_SERVER_ADDR, _controlPort); + + FaceTracker::reset(); + + _reset = true; } - - _reset = true; } bool DdeFaceTracker::isActive() const { @@ -250,7 +250,7 @@ bool DdeFaceTracker::isActive() const { //private slots and methods void DdeFaceTracker::socketErrorOccurred(QAbstractSocket::SocketError socketError) { - qCDebug(interfaceapp) << "[Error] DDE Face Tracker Socket Error: " << _udpSocket.errorString(); + qCWarning(interfaceapp) << "DDE Face Tracker: Socket error: " << _udpSocket.errorString(); } void DdeFaceTracker::socketStateChanged(QAbstractSocket::SocketState socketState) { @@ -278,7 +278,7 @@ void DdeFaceTracker::socketStateChanged(QAbstractSocket::SocketState socketState state = "Unconnected"; break; } - qCDebug(interfaceapp) << "[Info] DDE Face Tracker Socket: " << state; + qCDebug(interfaceapp) << "DDE Face Tracker: Socket: " << state; } void DdeFaceTracker::readPendingDatagrams() { @@ -419,23 +419,10 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { } _lastMessageReceived = usecsNow; - // Count frames if timing - if (_isCalculatingFPS) { - _frameCount++; - } - + FaceTracker::countFrame(); + } else { - qCDebug(interfaceapp) << "[Error] DDE Face Tracker Decode Error"; + qCWarning(interfaceapp) << "DDE Face Tracker: Decode error"; } _lastReceiveTimestamp = usecTimestampNow(); } - -void DdeFaceTracker::startFPSTimer() { - _frameCount = 0; - QTimer::singleShot(FPS_TIMER_DURATION, this, SLOT(finishFPSTimer())); -} - -void DdeFaceTracker::finishFPSTimer() { - qDebug() << "[Info] DDE FPS =" << (float)_frameCount / ((float)FPS_TIMER_DURATION / 1000.0f); - _isCalculatingFPS = false; -} diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index 7cca1e31be..d9df5723cf 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -59,15 +59,13 @@ private slots: void readPendingDatagrams(); void socketStateChanged(QAbstractSocket::SocketState socketState); - void startFPSTimer(); - void finishFPSTimer(); - private: DdeFaceTracker(); DdeFaceTracker(const QHostAddress& host, quint16 serverPort, quint16 controlPort); - ~DdeFaceTracker(); + virtual ~DdeFaceTracker(); QProcess* _ddeProcess; + bool _ddeStopping; QHostAddress _host; quint16 _serverPort; @@ -111,9 +109,6 @@ private: float _filteredLeftEyeBlink; float _lastRightEyeBlink; float _filteredRightEyeBlink; - - bool _isCalculatingFPS; - int _frameCount; }; #endif // hifi_DdeFaceTracker_h \ No newline at end of file diff --git a/interface/src/devices/FaceTracker.cpp b/interface/src/devices/FaceTracker.cpp index 7c1c757ec1..0d40249c26 100644 --- a/interface/src/devices/FaceTracker.cpp +++ b/interface/src/devices/FaceTracker.cpp @@ -9,9 +9,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include "FaceTracker.h" +#include "InterfaceLogging.h" + +const int FPS_TIMER_DELAY = 2000; // ms +const int FPS_TIMER_DURATION = 2000; // ms + +FaceTracker::FaceTracker() : + _isCalculatingFPS(false), + _frameCount(0) +{ +} inline float FaceTracker::getBlendshapeCoefficient(int index) const { return isValidBlendshapeIndex(index) ? glm::mix(0.0f, _blendshapeCoefficients[index], getFadeCoefficient()) @@ -65,4 +77,27 @@ void FaceTracker::update(float deltaTime) { _relaxationStatus = glm::clamp(_relaxationStatus - deltaTime / RELAXATION_TIME, 0.0f, 1.0f); _fadeCoefficient = std::exp(-(1.0f - _relaxationStatus) * INVERSE_AT_EPSILON); } -} \ No newline at end of file +} + +void FaceTracker::reset() { + if (isActive() && !_isCalculatingFPS) { + QTimer::singleShot(FPS_TIMER_DELAY, this, SLOT(startFPSTimer())); + _isCalculatingFPS = true; + } +} + +void FaceTracker::startFPSTimer() { + _frameCount = 0; + QTimer::singleShot(FPS_TIMER_DURATION, this, SLOT(finishFPSTimer())); +} + +void FaceTracker::countFrame() { + if (_isCalculatingFPS) { + _frameCount++; + } +} + +void FaceTracker::finishFPSTimer() { + qCDebug(interfaceapp) << "Face tracker FPS =" << (float)_frameCount / ((float)FPS_TIMER_DURATION / 1000.0f); + _isCalculatingFPS = false; +} diff --git a/interface/src/devices/FaceTracker.h b/interface/src/devices/FaceTracker.h index db6fdd74b9..a0a434ee9e 100644 --- a/interface/src/devices/FaceTracker.h +++ b/interface/src/devices/FaceTracker.h @@ -28,7 +28,7 @@ public: virtual void init() {} virtual void update(float deltaTime); - virtual void reset() {} + virtual void reset(); float getFadeCoefficient() const; @@ -44,6 +44,9 @@ public: float getBlendshapeCoefficient(int index) const; protected: + FaceTracker(); + virtual ~FaceTracker() {}; + glm::vec3 _headTranslation = glm::vec3(0.0f); glm::quat _headRotation = glm::quat(); float _estimatedEyePitch = 0.0f; @@ -52,6 +55,16 @@ protected: float _relaxationStatus = 0.0f; // Between 0.0f and 1.0f float _fadeCoefficient = 0.0f; // Between 0.0f and 1.0f + + void countFrame(); + +private slots: + void startFPSTimer(); + void finishFPSTimer(); + +private: + bool _isCalculatingFPS; + int _frameCount; }; #endif // hifi_FaceTracker_h diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp index a2c1cd693a..8a0e5a324c 100644 --- a/interface/src/devices/Faceshift.cpp +++ b/interface/src/devices/Faceshift.cpp @@ -30,20 +30,16 @@ const QString DEFAULT_FACESHIFT_HOSTNAME = "localhost"; const quint16 FACESHIFT_PORT = 33433; const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f; -const int FPS_TIMER_DELAY = 2000; // ms -const int FPS_TIMER_DURATION = 2000; // ms - Faceshift::Faceshift() : _eyeDeflection("faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION), - _hostname("faceshiftHostname", DEFAULT_FACESHIFT_HOSTNAME), - _isCalculatingFPS(false), - _frameCount(0) + _hostname("faceshiftHostname", DEFAULT_FACESHIFT_HOSTNAME) { #ifdef HAVE_FACESHIFT connect(&_tcpSocket, SIGNAL(connected()), SLOT(noteConnected())); connect(&_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(noteError(QAbstractSocket::SocketError))); connect(&_tcpSocket, SIGNAL(readyRead()), SLOT(readFromSocket())); connect(&_tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SIGNAL(connectionStateChanged())); + connect(&_tcpSocket, SIGNAL(disconnected()), SLOT(noteDisconnected())); connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams())); @@ -83,15 +79,13 @@ void Faceshift::update(float deltaTime) { void Faceshift::reset() { if (_tcpSocket.state() == QAbstractSocket::ConnectedState) { + qCDebug(interfaceapp, "Faceshift: Reset"); + + FaceTracker::reset(); + string message; fsBinaryStream::encode_message(message, fsMsgCalibrateNeutral()); send(message); - - // Log camera FPS after a reset - if (!_isCalculatingFPS) { - QTimer::singleShot(FPS_TIMER_DELAY, this, SLOT(startFPSTimer())); - _isCalculatingFPS = true; - } } _longTermAverageInitialized = false; } @@ -138,6 +132,7 @@ void Faceshift::setTCPEnabled(bool enabled) { if ((_tcpEnabled = enabled)) { connectSocket(); } else { + qCDebug(interfaceapp, "Faceshift: Disconnecting..."); _tcpSocket.disconnectFromHost(); } #endif @@ -156,7 +151,7 @@ void Faceshift::connectSocket() { void Faceshift::noteConnected() { #ifdef HAVE_FACESHIFT - qCDebug(interfaceapp, "Faceshift: Connected."); + qCDebug(interfaceapp, "Faceshift: Connected"); // request the list of blendshape names string message; fsBinaryStream::encode_message(message, fsMsgSendBlendshapeNames()); @@ -164,10 +159,16 @@ void Faceshift::noteConnected() { #endif } +void Faceshift::noteDisconnected() { +#ifdef HAVE_FACESHIFT + qCDebug(interfaceapp, "Faceshift: Disconnected"); +#endif +} + void Faceshift::noteError(QAbstractSocket::SocketError error) { if (!_tcpRetryCount) { // Only spam log with fail to connect the first time, so that we can keep waiting for server - qCDebug(interfaceapp) << "Faceshift: " << _tcpSocket.errorString(); + qCWarning(interfaceapp) << "Faceshift: " << _tcpSocket.errorString(); } // retry connection after a 2 second delay if (_tcpEnabled) { @@ -294,10 +295,8 @@ void Faceshift::receive(const QByteArray& buffer) { } } #endif - // Count frames if timing - if (_isCalculatingFPS) { - _frameCount++; - } + + FaceTracker::countFrame(); } void Faceshift::setEyeDeflection(float faceshiftEyeDeflection) { @@ -308,12 +307,3 @@ void Faceshift::setHostname(const QString& hostname) { _hostname.set(hostname); } -void Faceshift::startFPSTimer() { - _frameCount = 0; - QTimer::singleShot(FPS_TIMER_DURATION, this, SLOT(finishFPSTimer())); -} - -void Faceshift::finishFPSTimer() { - qCDebug(interfaceapp) << "Faceshift: FPS =" << (float)_frameCount / ((float)FPS_TIMER_DURATION / 1000.0f); - _isCalculatingFPS = false; -} diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h index 18d88e6fe9..3d38af5654 100644 --- a/interface/src/devices/Faceshift.h +++ b/interface/src/devices/Faceshift.h @@ -95,8 +95,7 @@ private slots: void noteError(QAbstractSocket::SocketError error); void readPendingDatagrams(); void readFromSocket(); - void startFPSTimer(); - void finishFPSTimer(); + void noteDisconnected(); private: Faceshift(); @@ -154,9 +153,6 @@ private: int _mouthSmileRightIndex = 29; int _jawOpenIndex = 21; - - bool _isCalculatingFPS; - int _frameCount; }; #endif // hifi_Faceshift_h diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 7d80b077b8..7112944375 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -42,7 +42,7 @@ static BOOL CALLBACK enumWindowsCallback(HWND hWnd, LPARAM lParam) { int main(int argc, const char* argv[]) { #ifdef Q_OS_WIN // Run only one instance of Interface at a time. - HANDLE mutex = CreateMutex(NULL, FALSE, "High Fidelity Interface"); + HANDLE mutex = CreateMutex(NULL, FALSE, "High Fidelity Interface - " + qgetenv("USERNAME")); DWORD result = GetLastError(); if (result == ERROR_ALREADY_EXISTS || result == ERROR_ACCESS_DENIED) { // Interface is already running. diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index e4c56dc419..d61e3c02f7 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -18,39 +18,77 @@ EntityItem* RenderableZoneEntityItem::factory(const EntityItemID& entityID, cons return new RenderableZoneEntityItem(entityID, properties); } -bool RenderableZoneEntityItem::setProperties(const EntityItemProperties& properties) { +template +void RenderableZoneEntityItem::changeProperties(Lambda setNewProperties) { QString oldShapeURL = getCompoundShapeURL(); - bool somethingChanged = ZoneEntityItem::setProperties(properties); - if (somethingChanged && oldShapeURL != getCompoundShapeURL()) { - _compoundShapeModel = DependencyManager::get()->getGeometry(getCompoundShapeURL(), QUrl(), true); + glm::vec3 oldPosition = getPosition(), oldDimensions = getDimensions(); + glm::quat oldRotation = getRotation(); + + setNewProperties(); + + if (oldShapeURL != getCompoundShapeURL()) { + if (!_model) { + _model = getModel(); + _needsInitialSimulation = true; + } + _model->setURL(getCompoundShapeURL(), QUrl(), true, true); } + if (oldPosition != getPosition() || + oldRotation != getRotation() || + oldDimensions != getDimensions()) { + _needsInitialSimulation = true; + } +} + +bool RenderableZoneEntityItem::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + changeProperties([&]() { + somethingChanged = this->ZoneEntityItem::setProperties(properties); + }); return somethingChanged; } int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { - QString oldShapeURL = getCompoundShapeURL(); - int bytesRead = ZoneEntityItem::readEntitySubclassDataFromBuffer(data, bytesLeftToRead, - args, propertyFlags, overwriteLocalData); - if (oldShapeURL != getCompoundShapeURL()) { - _compoundShapeModel = DependencyManager::get()->getGeometry(getCompoundShapeURL(), QUrl(), true); - } + int bytesRead = 0; + changeProperties([&]() { + bytesRead = ZoneEntityItem::readEntitySubclassDataFromBuffer(data, bytesLeftToRead, + args, propertyFlags, overwriteLocalData); + }); return bytesRead; } +Model* RenderableZoneEntityItem::getModel() { + Model* model = new Model(); + model->init(); + return model; +} + +void RenderableZoneEntityItem::initialSimulation() { + _model->setScaleToFit(true, getDimensions()); + _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); + _model->setRotation(getRotation()); + _model->setTranslation(getPosition()); + _model->simulate(0.0f); + _needsInitialSimulation = false; +} + bool RenderableZoneEntityItem::contains(const glm::vec3& point) const { if (getShapeType() != SHAPE_TYPE_COMPOUND) { return EntityItem::contains(point); } - if (!_compoundShapeModel && hasCompoundShapeURL()) { - const_cast(this)->_compoundShapeModel = DependencyManager::get()->getGeometry(getCompoundShapeURL(), QUrl(), true); + + if (_model && !_model->isActive() && hasCompoundShapeURL()) { + // Since we have a delayload, we need to update the geometry if it has been downloaded + _model->setURL(getCompoundShapeURL(), QUrl(), true); } - if (EntityItem::contains(point) && _compoundShapeModel && _compoundShapeModel->isLoaded()) { - const FBXGeometry& geometry = _compoundShapeModel->getFBXGeometry(); - glm::vec3 meshDimensions = geometry.getUnscaledMeshExtents().maximum - geometry.getUnscaledMeshExtents().minimum; - return geometry.convexHullContains(worldToEntity(point) * meshDimensions); + if (_model && _model->isActive() && EntityItem::contains(point)) { + if (_needsInitialSimulation) { + const_cast(this)->initialSimulation(); + } + return _model->convexHullContains(point); } return false; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index 8d8d8b4b3f..8a75a048d1 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -12,6 +12,7 @@ #ifndef hifi_RenderableZoneEntityItem_h #define hifi_RenderableZoneEntityItem_h +#include #include class NetworkGeometry; @@ -21,7 +22,9 @@ public: static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableZoneEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - ZoneEntityItem(entityItemID, properties) + ZoneEntityItem(entityItemID, properties), + _model(NULL), + _needsInitialSimulation(true) { } virtual bool setProperties(const EntityItemProperties& properties); @@ -32,8 +35,14 @@ public: virtual bool contains(const glm::vec3& point) const; private: + Model* getModel(); + void initialSimulation(); - QSharedPointer _compoundShapeModel; + template + void changeProperties(Lambda functor); + + Model* _model; + bool _needsInitialSimulation; }; #endif // hifi_RenderableZoneEntityItem_h \ No newline at end of file diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 76f66420bf..1e24b3f1a9 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -58,6 +58,9 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // handle ICE signal from DS so connection is attempted immediately connect(&_domainHandler, &DomainHandler::requestICEConnectionAttempt, this, &NodeList::handleICEConnectionToDomainServer); + + // clear out NodeList when login is finished + connect(&AccountManager::getInstance(), &AccountManager::loginComplete , this, &NodeList::reset); // clear our NodeList when logout is requested connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset); diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 9c7a43b12d..210d074001 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -285,6 +285,7 @@ void OctreePersistThread::restoreFromMostRecentBackup() { qCDebug(octree) << "DONE restoring backup file " << mostRecentBackupFileName << "to" << _filename << "..."; } else { qCDebug(octree) << "ERROR while restoring backup file " << mostRecentBackupFileName << "to" << _filename << "..."; + perror("ERROR while restoring backup file"); } } else { qCDebug(octree) << "NO BEST backup file found."; @@ -366,6 +367,7 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) { qCDebug(octree) << "DONE rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; } else { qCDebug(octree) << "ERROR in rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; + perror("ERROR in rolling backup file"); } } } @@ -425,6 +427,7 @@ void OctreePersistThread::backup() { rule.lastBackup = now; // only record successful backup in this case. } else { qCDebug(octree) << "ERROR in backing up persist file..."; + perror("ERROR in backing up persist file"); } } else { qCDebug(octree) << "persist file " << _filename << " does not exist. " << diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 37156a6c71..8ccbb65dbb 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -324,7 +324,7 @@ public: const FBXGeometry& getFBXGeometry() const { return _geometry; } const QVector& getMeshes() const { return _meshes; } -// + QVector getJointMappings(const AnimationPointer& animation); virtual void setLoadPriority(const QPointer& owner, float priority); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 0114bc65f0..7528d1db4d 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -573,6 +573,61 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g return intersectedSomething; } +bool Model::convexHullContains(glm::vec3 point) { + // if we aren't active, we can't compute that yet... + if (!isActive()) { + return false; + } + + // extents is the entity relative, scaled, centered extents of the entity + glm::vec3 position = _translation; + glm::mat4 rotation = glm::mat4_cast(_rotation); + glm::mat4 translation = glm::translate(position); + glm::mat4 modelToWorldMatrix = translation * rotation; + glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix); + + Extents modelExtents = getMeshExtents(); // NOTE: unrotated + + glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum; + glm::vec3 corner = -(dimensions * _registrationPoint); + AABox modelFrameBox(corner, dimensions); + + glm::vec3 modelFramePoint = glm::vec3(worldToModelMatrix * glm::vec4(point, 1.0f)); + + // we can use the AABox's contains() by mapping our point into the model frame + // and testing there. + if (modelFrameBox.contains(modelFramePoint)){ + if (!_calculatedMeshTrianglesValid) { + recalculateMeshBoxes(true); + } + + // If we are inside the models box, then consider the submeshes... + int subMeshIndex = 0; + foreach(const AABox& subMeshBox, _calculatedMeshBoxes) { + if (subMeshBox.contains(point)) { + bool insideMesh = true; + // To be inside the sub mesh, we need to be behind every triangles' planes + const QVector& meshTriangles = _calculatedMeshTriangles[subMeshIndex]; + foreach (const Triangle& triangle, meshTriangles) { + if (!isPointBehindTrianglesPlane(point, triangle.v0, triangle.v1, triangle.v2)) { + // it's not behind at least one so we bail + insideMesh = false; + break; + } + + } + if (insideMesh) { + // It's inside this mesh, return true. + return true; + } + } + subMeshIndex++; + } + } + // It wasn't in any mesh, return false. + return false; +} + // TODO: we seem to call this too often when things haven't actually changed... look into optimizing this void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid; @@ -1047,7 +1102,7 @@ int Model::getLastFreeJointIndex(int jointIndex) const { void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bool delayLoad) { // don't recreate the geometry if it's the same URL - if (_url == url) { + if (_url == url && _geometry && _geometry->getURL() == url) { return; } _url = url; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index b91d2116f8..dba8f5277f 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -213,6 +213,7 @@ public: bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, QString& extraInfo, bool pickAgainstTriangles = false); + bool convexHullContains(glm::vec3 point); protected: QSharedPointer _geometry; diff --git a/libraries/script-engine/src/ScriptAudioInjector.cpp b/libraries/script-engine/src/ScriptAudioInjector.cpp index a9cf40558c..2ec30ad4dd 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.cpp +++ b/libraries/script-engine/src/ScriptAudioInjector.cpp @@ -13,8 +13,12 @@ #include "ScriptAudioInjector.h" QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in) { + // The AudioScriptingInterface::playSound method can return null, so we need to account for that. + if (!in) { + return QScriptValue(QScriptValue::NullValue); + } + // when the script goes down we want to cleanup the injector - QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately, Qt::DirectConnection);