diff --git a/animation-server/CMakeLists.txt b/animation-server/CMakeLists.txt index 42516d2f86..31ed5d98df 100644 --- a/animation-server/CMakeLists.txt +++ b/animation-server/CMakeLists.txt @@ -12,6 +12,9 @@ set(MACRO_DIR "${ROOT_DIR}/cmake/macros") # setup for find modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") +find_package(Qt5 COMPONENTS Script) +include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}") + # set up the external glm library include("${MACRO_DIR}/IncludeGLM.cmake") include_glm(${TARGET_NAME} "${ROOT_DIR}") @@ -35,4 +38,4 @@ link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") # add a definition for ssize_t so that windows doesn't bail if (WIN32) add_definitions(-Dssize_t=long) -endif () \ No newline at end of file +endif () diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 5720ecaaf5..fcd983873d 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -13,13 +13,13 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -208,12 +208,14 @@ void Agent::run() { scriptURL = QUrl(_payload); } - QNetworkAccessManager *networkManager = new QNetworkAccessManager(this); - QNetworkReply *reply = networkManager->get(QNetworkRequest(scriptURL)); - QNetworkDiskCache* cache = new QNetworkDiskCache(networkManager); + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkReply *reply = networkAccessManager.get(QNetworkRequest(scriptURL)); + QNetworkDiskCache* cache = new QNetworkDiskCache(&networkAccessManager); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache"); - networkManager->setCache(cache); + QMetaObject::invokeMethod(&networkAccessManager, "setCache", + Qt::BlockingQueuedConnection, + Q_ARG(QAbstractNetworkCache*, cache)); qDebug() << "Downloading script at" << scriptURL.toString(); @@ -222,10 +224,6 @@ void Agent::run() { loop.exec(); - // let the AvatarData and ResourceCache classes use our QNetworkAccessManager - AvatarData::setNetworkAccessManager(networkManager); - ResourceCache::setNetworkAccessManager(networkManager); - QString scriptContents(reply->readAll()); qDebug() << "Downloaded script:" << scriptContents; diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 663aef81a7..1c3046b9b1 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -38,11 +38,11 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -482,8 +482,8 @@ void AudioMixer::run() { nodeList->linkedDataCreateCallback = attachNewBufferToNode; - // setup a QNetworkAccessManager to ask the domain-server for our settings - QNetworkAccessManager *networkManager = new QNetworkAccessManager(this); + // setup a NetworkAccessManager to ask the domain-server for our settings + NetworkAccessManager& networkManager = NetworkAccessManager::getInstance(); QUrl settingsJSONURL; settingsJSONURL.setScheme("http"); @@ -500,7 +500,7 @@ void AudioMixer::run() { qDebug() << "Requesting settings for assignment from domain-server at" << settingsJSONURL.toString(); while (!reply || reply->error() != QNetworkReply::NoError) { - reply = networkManager->get(QNetworkRequest(settingsJSONURL)); + reply = networkManager.get(QNetworkRequest(settingsJSONURL)); QEventLoop loop; QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 23719b86cf..551bed795d 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -10,7 +10,6 @@ // #include -#include #include #include diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 7a2d5f4f99..366a5016f9 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -51,8 +51,6 @@ DomainServer::DomainServer(int argc, char* argv[]) : _argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); - _networkAccessManager = new QNetworkAccessManager(this); - if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) { // we either read a certificate and private key or were not passed one // and completed login or did not need to @@ -1196,7 +1194,7 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u QNetworkRequest tokenRequest(tokenRequestUrl); tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - QNetworkReply* tokenReply = _networkAccessManager->post(tokenRequest, tokenPostBody.toLocal8Bit()); + QNetworkReply* tokenReply = NetworkAccessManager::getInstance().post(tokenRequest, tokenPostBody.toLocal8Bit()); qDebug() << "Requesting a token for user with session UUID" << uuidStringWithoutCurlyBraces(stateUUID); @@ -1233,7 +1231,7 @@ void DomainServer::handleTokenRequestFinished() { profileURL.setPath("/api/v1/users/profile"); profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken)); - QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL)); + QNetworkReply* profileReply = NetworkAccessManager::getInstance().get(QNetworkRequest(profileURL)); qDebug() << "Requesting access token for user with session UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 01f44b698e..cc44bd95a8 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -103,8 +103,6 @@ private: bool _isUsingDTLS; - QNetworkAccessManager* _networkAccessManager; - QUrl _oauthProviderURL; QString _oauthClientID; QString _oauthClientSecret; diff --git a/interface/external/oculus/readme.txt b/interface/external/oculus/readme.txt index f689f81478..f68818d1ee 100644 --- a/interface/external/oculus/readme.txt +++ b/interface/external/oculus/readme.txt @@ -10,4 +10,7 @@ You can download the Oculus SDK from https://developer.oculusvr.com/ (account cr You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects). If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'oculus' that contains the three folders mentioned above. -2. Clear your build directory, run cmake and build, and you should be all set. \ No newline at end of file + NOTE: For Windows users, you should copy libovr.lib and libovrd.lib from the \oculus\Lib\Win32\VS2010 directory to the \oculus\Lib\Win32\ directory. + +2. Clear your build directory, run cmake and build, and you should be all set. + diff --git a/interface/resources/shaders/perlin_modulate.frag b/interface/resources/shaders/perlin_modulate.frag index 8693b14e1b..23d31ff72e 100644 --- a/interface/resources/shaders/perlin_modulate.frag +++ b/interface/resources/shaders/perlin_modulate.frag @@ -11,18 +11,114 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// the texture containing our permutations and normals -uniform sampler2D permutationNormalTexture; +// implementation based on Ken Perlin's Improved Noise reference implementation (orig. in Java) at +// http://mrl.nyu.edu/~perlin/noise/ + +uniform sampler2D permutationTexture; // the noise frequency -const float frequency = 65536.0; // looks better with current TREE_SCALE, was 1024 when TREE_SCALE was either 512 or 128 +const float frequency = 256.0; +//const float frequency = 65536.0; // looks better with current TREE_SCALE, was 1024 when TREE_SCALE was either 512 or 128 // the noise amplitude -const float amplitude = 0.1; +const float amplitude = 0.5; // the position in model space varying vec3 position; +// gradient based on gradients from cube edge centers rather than random from texture lookup +float randomEdgeGrad(int hash, vec3 position){ + int h = int(mod(hash, 16)); + float u = h < 8 ? position.x : position.y; + float v = h < 4 ? position.y : h == 12 || h == 14 ? position.x : position.z; + bool even = mod(hash, 2) == 0; + bool four = mod(hash, 4) == 0; + return (even ? u : -u) + (four ? v : -v); +} + +// still have the option to lookup based on texture +float randomTextureGrad(int hash, vec3 position){ + float u = float(hash) / 256.0; + vec3 g = -1 + 2 * texture2D(permutationTexture, vec2(u, 0.75)).rgb; + return dot(position, g); +} + +float improvedGrad(int hash, vec3 position){ +// Untested whether texture lookup is faster than math, uncomment one line or the other to try out +// cube edge gradients versus random spherical gradients sent in texture. + +// return randomTextureGrad(hash, position); + return randomEdgeGrad(hash, position); +} + +// 5th order fade function to remove 2nd order discontinuties +vec3 fade3(vec3 t){ + return t * t * t * (t * (t * 6 - 15) + 10); +} + +int permutation(int index){ + float u = float(index) / 256.0; + float t = texture2D(permutationTexture, vec2(u, 0.25)).r; + return int(t * 256); +} + +float improvedNoise(vec3 position){ + int X = int(mod(floor(position.x), 256)); + int Y = int(mod(floor(position.y), 256)); + int Z = int(mod(floor(position.z), 256)); + + vec3 fracs = fract(position); + + vec3 fades = fade3(fracs); + + int A = permutation(X + 0) + Y; + int AA = permutation(A + 0) + Z; + int AB = permutation(A + 1) + Z; + int B = permutation(X + 1) + Y; + int BA = permutation(B + 0) + Z; + int BB = permutation(B + 1) + Z; + + float gradAA0 = improvedGrad(permutation(AA + 0), vec3(fracs.x , fracs.y , fracs.z )); + float gradBA0 = improvedGrad(permutation(BA + 0), vec3(fracs.x - 1, fracs.y , fracs.z )); + float gradAB0 = improvedGrad(permutation(AB + 0), vec3(fracs.x , fracs.y - 1, fracs.z )); + float gradBB0 = improvedGrad(permutation(BB + 0), vec3(fracs.x - 1, fracs.y - 1, fracs.z )); + float gradAA1 = improvedGrad(permutation(AA + 1), vec3(fracs.x , fracs.y , fracs.z - 1)); + float gradBA1 = improvedGrad(permutation(BA + 1), vec3(fracs.x - 1, fracs.y , fracs.z - 1)); + float gradAB1 = improvedGrad(permutation(AB + 1), vec3(fracs.x , fracs.y - 1, fracs.z - 1)); + float gradBB1 = improvedGrad(permutation(BB + 1), vec3(fracs.x - 1, fracs.y - 1, fracs.z - 1)); + + return mix(mix(mix(gradAA0, gradBA0, fades.x), mix(gradAB0, gradBB0, fades.x), fades.y), mix(mix(gradAA1, gradBA1, fades.x), mix(gradAB1, gradBB1, fades.x), fades.y), fades.z); +} + +float turbulence(vec3 position, float power){ + return (1.0f / power) * improvedNoise(power * position); +} + +float turb(vec3 position){ + return turbulence(position, 1) + + turbulence(position, 2), + + turbulence(position, 4) + + turbulence(position, 8) + + turbulence(position, 16) + + turbulence(position, 32) + + turbulence(position, 64) + + turbulence(position, 128) + ; +} + + +void main(void) { + + // get noise in range 0 .. 1 + float noise = clamp(0.5f + amplitude * turb(position * frequency), 0, 1); + + // apply vertex lighting + vec3 color = gl_Color.rgb * vec3(noise, noise, noise); + gl_FragColor = vec4(color, 1); +} + + +/* old implementation // returns the gradient at a single corner of our sampling cube vec3 grad(vec3 location) { float p1 = texture2D(permutationNormalTexture, vec2(location.x / 256.0, 0.25)).r; @@ -60,7 +156,4 @@ float perlin(vec3 location) { mix(mix(ffcv, cfcv, params.x), mix(fccv, cccv, params.x), params.y), params.z); } - -void main(void) { - gl_FragColor = vec4(gl_Color.rgb * (1.0 + amplitude*(perlin(position * frequency) - 1.0)), 1.0); -} +*/ \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 85320a47d7..ecc9c6e481 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -53,17 +52,18 @@ #include #include +#include #include #include +#include #include +#include #include #include #include #include #include #include -#include -#include #include "Application.h" #include "InterfaceVersion.h" @@ -315,12 +315,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - _networkAccessManager = new QNetworkAccessManager(this); - QNetworkDiskCache* cache = new QNetworkDiskCache(_networkAccessManager); + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkDiskCache* cache = new QNetworkDiskCache(&networkAccessManager); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); - _networkAccessManager->setCache(cache); + QMetaObject::invokeMethod(&networkAccessManager, "setCache", + Qt::BlockingQueuedConnection, + Q_ARG(QAbstractNetworkCache*, cache)); - ResourceCache::setNetworkAccessManager(_networkAccessManager); ResourceCache::setRequestLimit(3); _window->setCentralWidget(_glWidget); @@ -403,18 +404,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : } Application::~Application() { + qInstallMessageHandler(NULL); + + saveSettings(); + storeSizeAndPosition(); + saveScripts(); + int DELAY_TIME = 1000; UserActivityLogger::getInstance().close(DELAY_TIME); - qInstallMessageHandler(NULL); - // make sure we don't call the idle timer any more delete idleTimer; - + _sharedVoxelSystem.changeTree(new VoxelTree); - - saveSettings(); - delete _voxelImporter; // let the avatar mixer know we're out @@ -437,8 +439,6 @@ Application::~Application() { _particleEditSender.terminate(); _modelEditSender.terminate(); - storeSizeAndPosition(); - saveScripts(); VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown Menu::getInstance()->deleteLater(); @@ -446,8 +446,6 @@ Application::~Application() { _myAvatar = NULL; delete _glWidget; - - AccountManager::getInstance().destroy(); } void Application::saveSettings() { @@ -595,13 +593,17 @@ void Application::paintGL() { //Note, the camera distance is set in Camera::setMode() so we dont have to do it here. _myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing _myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition()); - _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation()); + if (OculusManager::isConnected()) { + _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation()); + } else { + _myCamera.setTargetRotation(_myAvatar->getHead()->getOrientation()); + } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setTightness(0.0f); _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); - _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition()); + _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0)); } // Update camera position @@ -639,6 +641,10 @@ void Application::paintGL() { //If we aren't using the glow shader, we have to clear the color and depth buffer if (!glowEnabled) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } else if (OculusManager::isConnected()) { + //Clear the color buffer to ensure that there isnt any residual color + //Left over from when OR was not connected. + glClear(GL_COLOR_BUFFER_BIT); } if (OculusManager::isConnected()) { @@ -650,13 +656,8 @@ void Application::paintGL() { } } else if (TV3DManager::isConnected()) { - if (glowEnabled) { - _glowEffect.prepare(); - } + TV3DManager::display(whichCamera); - if (glowEnabled) { - _glowEffect.render(); - } } else { if (glowEnabled) { @@ -1144,7 +1145,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { _lastMouseMove = usecTimestampNow(); - if (_mouseHidden && showMouse && !OculusManager::isConnected()) { + if (_mouseHidden && showMouse && !OculusManager::isConnected() && !TV3DManager::isConnected()) { getGLWidget()->setCursor(Qt::ArrowCursor); _mouseHidden = false; _seenMouseMove = true; @@ -3808,7 +3809,8 @@ void Application::initAvatarAndViewFrustum() { void Application::checkVersion() { QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL))); latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); - connect(Application::getInstance()->getNetworkAccessManager()->get(latestVersionRequest), SIGNAL(finished()), SLOT(parseVersionXml())); + QNetworkReply* reply = NetworkAccessManager::getInstance().get(latestVersionRequest); + connect(reply, SIGNAL(finished()), SLOT(parseVersionXml())); } void Application::parseVersionXml() { diff --git a/interface/src/Application.h b/interface/src/Application.h index 56b6f673ae..11f406abf0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -97,7 +97,6 @@ class QActionGroup; class QGLWidget; class QKeyEvent; class QMouseEvent; -class QNetworkAccessManager; class QSettings; class QWheelEvent; @@ -236,7 +235,6 @@ public: void lockOctreeSceneStats() { _octreeSceneStatsLock.lockForRead(); } void unlockOctreeSceneStats() { _octreeSceneStatsLock.unlock(); } - QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; } GeometryCache* getGeometryCache() { return &_geometryCache; } AnimationCache* getAnimationCache() { return &_animationCache; } TextureCache* getTextureCache() { return &_textureCache; } @@ -423,7 +421,6 @@ private: QThread* _nodeThread; DatagramProcessor _datagramProcessor; - QNetworkAccessManager* _networkAccessManager; QMutex _settingsMutex; QSettings* _settings; int _numChangedSettings; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index f5f148969c..df9df878bb 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -432,7 +432,7 @@ void Audio::handleAudioInput() { float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(_numInputCallbackBytes); - unsigned int inputSamplesRequired = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio; + int inputSamplesRequired = (int)((float)NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio); QByteArray inputByteArray = _inputDevice->readAll(); @@ -1379,8 +1379,10 @@ bool Audio::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) { // cleanup any previously initialized device if (_audioInput) { + // The call to stop() causes _inputDevice to be destructed. + // That in turn causes it to be disconnected (see for example + // http://stackoverflow.com/questions/9264750/qt-signals-and-slots-object-disconnect). _audioInput->stop(); - disconnect(_inputDevice); _inputDevice = NULL; delete _audioInput; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 402347c5d4..dba5feca9e 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -91,6 +91,7 @@ Menu::Menu() : _jsConsole(NULL), _octreeStatsDialog(NULL), _lodToolsDialog(NULL), + _userLocationsDialog(NULL), _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM), _voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE), _oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE), @@ -166,6 +167,11 @@ Menu::Menu() : Qt::CTRL | Qt::Key_N, this, SLOT(nameLocation())); + addActionToQMenuAndActionHash(fileMenu, + MenuOption::MyLocations, + Qt::CTRL | Qt::Key_K, + this, + SLOT(toggleLocationList())); addActionToQMenuAndActionHash(fileMenu, MenuOption::GoTo, Qt::Key_At, @@ -1184,6 +1190,17 @@ void Menu::namedLocationCreated(LocationManager::NamedLocationCreateResponse res msgBox.exec(); } +void Menu::toggleLocationList() { + if (!_userLocationsDialog) { + _userLocationsDialog = new UserLocationsDialog(Application::getInstance()->getWindow()); + } + if (_userLocationsDialog->isVisible()) { + _userLocationsDialog->hide(); + } else { + _userLocationsDialog->show(); + } +} + void Menu::nameLocation() { // check if user is logged in or show login dialog if not diff --git a/interface/src/Menu.h b/interface/src/Menu.h index a15d3712f1..06b5c5c9f4 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -29,6 +29,7 @@ #include "ui/JSConsole.h" #include "ui/LoginDialog.h" #include "ui/ScriptEditorWindow.h" +#include "ui/UserLocationsDialog.h" const float ADJUST_LOD_DOWN_FPS = 40.0; const float ADJUST_LOD_UP_FPS = 55.0; @@ -199,6 +200,7 @@ private slots: void goToDomainDialog(); void goToLocation(); void nameLocation(); + void toggleLocationList(); void bandwidthDetailsClosed(); void octreeStatsDetailsClosed(); void lodToolsClosed(); @@ -265,6 +267,7 @@ private: QDialog* _jsConsole; OctreeStatsDialog* _octreeStatsDialog; LodToolsDialog* _lodToolsDialog; + UserLocationsDialog* _userLocationsDialog; int _maxVoxels; float _voxelSizeScale; float _oculusUIAngularSize; @@ -395,6 +398,7 @@ namespace MenuOption { const QString MoveWithLean = "Move with Lean"; const QString MuteAudio = "Mute Microphone"; const QString MuteEnvironment = "Mute Environment"; + const QString MyLocations = "My Locations..."; const QString NameLocation = "Name this location"; const QString NewVoxelCullingMode = "New Voxel Culling Mode"; const QString OctreeStats = "Voxel and Particle Statistics"; diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index 81bca0fc5a..12a4b145cc 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -396,7 +396,7 @@ void ModelUploader::uploadFailed(QNetworkReply::NetworkError errorCode, const QS void ModelUploader::checkS3() { qDebug() << "Checking S3 for " << _url; QNetworkRequest request(_url); - QNetworkReply* reply = _networkAccessManager.head(request); + QNetworkReply* reply = NetworkAccessManager::getInstance().head(request); connect(reply, SIGNAL(finished()), SLOT(processCheck())); } diff --git a/interface/src/ModelUploader.h b/interface/src/ModelUploader.h index 634de05640..2596120751 100644 --- a/interface/src/ModelUploader.h +++ b/interface/src/ModelUploader.h @@ -58,7 +58,6 @@ private: bool _readyToSend; QHttpMultiPart* _dataMultiPart; - QNetworkAccessManager _networkAccessManager; int _numberOfChecks; QTimer _timer; diff --git a/interface/src/ScriptsModel.cpp b/interface/src/ScriptsModel.cpp index f9ed94f3fa..7b24587129 100644 --- a/interface/src/ScriptsModel.cpp +++ b/interface/src/ScriptsModel.cpp @@ -11,14 +11,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include #include #include #include -#include "ScriptsModel.h" +#include + #include "Menu.h" +#include "ScriptsModel.h" static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com"; static const QString PUBLIC_URL = "http://public.highfidelity.io"; @@ -30,8 +31,6 @@ static const QString IS_TRUNCATED_NAME = "IsTruncated"; static const QString CONTAINER_NAME = "Contents"; static const QString KEY_NAME = "Key"; -static const int SCRIPT_PATH = Qt::UserRole; - ScriptItem::ScriptItem(const QString& filename, const QString& fullPath) : _filename(filename), _fullPath(fullPath) { @@ -113,14 +112,15 @@ void ScriptsModel::requestRemoteFiles(QString marker) { } url.setQuery(query); - QNetworkAccessManager* accessManager = new QNetworkAccessManager(this); - connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*))); - + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request(url); - accessManager->get(request); + QNetworkReply* reply = networkAccessManager.get(request); + connect(reply, SIGNAL(finished()), SLOT(downloadFinished())); + } -void ScriptsModel::downloadFinished(QNetworkReply* reply) { +void ScriptsModel::downloadFinished() { + QNetworkReply* reply = static_cast(sender()); bool finished = true; if (reply->error() == QNetworkReply::NoError) { diff --git a/interface/src/ScriptsModel.h b/interface/src/ScriptsModel.h index 250c7eb9a8..ca72a8d8f4 100644 --- a/interface/src/ScriptsModel.h +++ b/interface/src/ScriptsModel.h @@ -43,7 +43,7 @@ public: protected slots: void updateScriptsLocation(const QString& newPath); - void downloadFinished(QNetworkReply* reply); + void downloadFinished(); void reloadLocalFiles(); void reloadRemoteFiles(); diff --git a/interface/src/UserLocationsModel.cpp b/interface/src/UserLocationsModel.cpp new file mode 100644 index 0000000000..e84cae8f95 --- /dev/null +++ b/interface/src/UserLocationsModel.cpp @@ -0,0 +1,246 @@ +// +// UserLocationsModel.cpp +// interface/src +// +// Created by Ryan Huffman on 06/24/14. +// Copyright 2014 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 +// + +#include +#include +#include +#include + +#include "AccountManager.h" +#include "Application.h" +#include "UserLocationsModel.h" + +static const QString PLACES_GET = "/api/v1/places"; +static const QString PLACES_UPDATE = "/api/v1/places/%1"; +static const QString PLACES_DELETE= "/api/v1/places/%1"; + +UserLocation::UserLocation(QString id, QString name, QString location) : + _id(id), + _name(name), + _location(location), + _previousName(name), + _updating(false) { +} + +void UserLocation::requestRename(const QString& newName) { + if (!_updating && newName.toLower() != _name) { + _updating = true; + + JSONCallbackParameters callbackParams(this, "handleRenameResponse", this, "handleRenameError"); + QJsonObject jsonNameObject; + jsonNameObject.insert("name", QJsonValue(newName)); + QJsonDocument jsonDocument(jsonNameObject); + AccountManager::getInstance().authenticatedRequest(PLACES_UPDATE.arg(_id), + QNetworkAccessManager::PutOperation, + callbackParams, + jsonDocument.toJson()); + _previousName = _name; + _name = newName; + + emit updated(_name); + } +} + +void UserLocation::handleRenameResponse(const QJsonObject& responseData) { + _updating = false; + + QJsonValue status = responseData["status"]; + if (!status.isUndefined() && status.toString() == "success") { + QString updatedName = responseData["data"].toObject()["name"].toString(); + _name = updatedName; + } else { + _name = _previousName; + + QString msg = "There was an error renaming location '" + _name + "'"; + + QJsonValue data = responseData["data"]; + if (!data.isUndefined()) { + QJsonValue nameError = data.toObject()["name"]; + if (!nameError.isUndefined()) { + msg += ": " + nameError.toString(); + } + } + qDebug() << msg; + QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg); + } + + emit updated(_name); +} + +void UserLocation::handleRenameError(QNetworkReply::NetworkError error, const QString& errorString) { + _updating = false; + + QString msg = "There was an error renaming location '" + _name + "': " + errorString; + qDebug() << msg; + QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg); + + emit updated(_name); +} + +void UserLocation::requestDelete() { + if (!_updating) { + _updating = true; + + JSONCallbackParameters callbackParams(this, "handleDeleteResponse", this, "handleDeleteError"); + AccountManager::getInstance().authenticatedRequest(PLACES_DELETE.arg(_id), + QNetworkAccessManager::DeleteOperation, + callbackParams); + } +} + +void UserLocation::handleDeleteResponse(const QJsonObject& responseData) { + _updating = false; + + QJsonValue status = responseData["status"]; + if (!status.isUndefined() && status.toString() == "success") { + emit deleted(_name); + } else { + QString msg = "There was an error deleting location '" + _name + "'"; + qDebug() << msg; + QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg); + } +} + +void UserLocation::handleDeleteError(QNetworkReply::NetworkError error, const QString& errorString) { + _updating = false; + + QString msg = "There was an error deleting location '" + _name + "': " + errorString; + qDebug() << msg; + QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg); +} + +UserLocationsModel::UserLocationsModel(QObject* parent) : + QAbstractListModel(parent), + _updating(false) { + + refresh(); +} + +UserLocationsModel::~UserLocationsModel() { + qDeleteAll(_locations); + _locations.clear(); +} + +void UserLocationsModel::update() { + beginResetModel(); + endResetModel(); +} + +void UserLocationsModel::deleteLocation(const QModelIndex& index) { + UserLocation* location = _locations[index.row()]; + location->requestDelete(); +} + +void UserLocationsModel::renameLocation(const QModelIndex& index, const QString& newName) { + UserLocation* location = _locations[index.row()]; + location->requestRename(newName); +} + +void UserLocationsModel::refresh() { + if (!_updating) { + beginResetModel(); + qDeleteAll(_locations); + _locations.clear(); + _updating = true; + endResetModel(); + + JSONCallbackParameters callbackParams(this, "handleLocationsResponse"); + AccountManager::getInstance().authenticatedRequest(PLACES_GET, + QNetworkAccessManager::GetOperation, + callbackParams); + } +} + +void UserLocationsModel::handleLocationsResponse(const QJsonObject& responseData) { + _updating = false; + + QJsonValue status = responseData["status"]; + if (!status.isUndefined() && status.toString() == "success") { + beginResetModel(); + QJsonArray locations = responseData["data"].toObject()["places"].toArray(); + for (QJsonArray::const_iterator it = locations.constBegin(); it != locations.constEnd(); it++) { + QJsonObject location = (*it).toObject(); + QJsonObject address = location["address"].toObject(); + UserLocation* userLocation = new UserLocation(location["id"].toString(), location["name"].toString(), + "hifi://" + address["domain"].toString() + + "/" + address["position"].toString() + + "/" + address["orientation"].toString()); + _locations.append(userLocation); + connect(userLocation, &UserLocation::deleted, this, &UserLocationsModel::removeLocation); + connect(userLocation, &UserLocation::updated, this, &UserLocationsModel::update); + } + endResetModel(); + } else { + qDebug() << "Error loading location data"; + } +} + +void UserLocationsModel::removeLocation(const QString& name) { + beginResetModel(); + for (QList::iterator it = _locations.begin(); it != _locations.end(); it++) { + if ((*it)->name() == name) { + _locations.erase(it); + break; + } + } + endResetModel(); +} + +int UserLocationsModel::rowCount(const QModelIndex& parent) const { + if (parent.isValid()) { + return 0; + } + + if (_updating) { + return 1; + } + + return _locations.length(); +} + +QVariant UserLocationsModel::data(const QModelIndex& index, int role) const { + if (role == Qt::DisplayRole) { + if (_updating) { + return QVariant("Updating..."); + } else if (index.row() > _locations.length()) { + return QVariant(); + } else if (index.column() == NameColumn) { + return _locations[index.row()]->name(); + } else if (index.column() == LocationColumn) { + return QVariant(_locations[index.row()]->location()); + } + } + + return QVariant(); + +} +QVariant UserLocationsModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case NameColumn: return "Name"; + case LocationColumn: return "Location"; + default: return QVariant(); + } + } + + return QVariant(); +} + +Qt::ItemFlags UserLocationsModel::flags(const QModelIndex& index) const { + if (index.row() < _locations.length()) { + UserLocation* ul = _locations[index.row()]; + if (ul->isUpdating()) { + return Qt::NoItemFlags; + } + } + + return QAbstractListModel::flags(index); +} diff --git a/interface/src/UserLocationsModel.h b/interface/src/UserLocationsModel.h new file mode 100644 index 0000000000..d3f86faa5a --- /dev/null +++ b/interface/src/UserLocationsModel.h @@ -0,0 +1,82 @@ +// +// UserLocationsModel.h +// interface/src +// +// Created by Ryan Huffman on 06/24/14. +// Copyright 2014 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 +// + +#ifndef hifi_UserLocationsModel_h +#define hifi_UserLocationsModel_h + +#include +#include +#include + + +class UserLocation : public QObject { + Q_OBJECT +public: + UserLocation(QString id, QString name, QString location); + bool isUpdating() { return _updating; } + void requestRename(const QString& newName); + void requestDelete(); + + QString id() { return _id; } + QString name() { return _name; } + QString location() { return _location; } + +public slots: + void handleRenameResponse(const QJsonObject& responseData); + void handleRenameError(QNetworkReply::NetworkError error, const QString& errorString); + void handleDeleteResponse(const QJsonObject& responseData); + void handleDeleteError(QNetworkReply::NetworkError error, const QString& errorString); + +signals: + void updated(const QString& name); + void deleted(const QString& name); + +private: + QString _id; + QString _name; + QString _location; + QString _previousName; + bool _updating; + +}; + +class UserLocationsModel : public QAbstractListModel { + Q_OBJECT +public: + UserLocationsModel(QObject* parent = NULL); + ~UserLocationsModel(); + + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const { return 2; }; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual Qt::ItemFlags flags(const QModelIndex& index) const; + + void deleteLocation(const QModelIndex& index); + void renameLocation(const QModelIndex& index, const QString& newName); + + enum Columns { + NameColumn = 0, + LocationColumn + }; + +public slots: + void refresh(); + void update(); + void handleLocationsResponse(const QJsonObject& responseData); + void removeLocation(const QString& name); + +private: + bool _updating; + QList _locations; +}; + +#endif // hifi_UserLocationsModel_h diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index c3f31ff6e7..59e5b08cc0 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -51,10 +51,10 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX glm::mat3 axes = glm::mat3_cast(glm::quat()); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) * joint.preTransform * glm::mat4_cast(joint.preRotation))); - state._rotationInParentFrame = glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2])) + state.setRotationInParentFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2])) * glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getFinalYaw(), glm::normalize(inverse * axes[1])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalPitch(), glm::normalize(inverse * axes[0])) - * joint.rotation; + * joint.rotation); } void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { @@ -68,8 +68,8 @@ void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJ _owningHead->getSaccade() - _translation, 1.0f)); glm::quat between = rotationBetween(front, lookAt); const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; - state._rotationInParentFrame = glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * - joint.rotation; + state.setRotationInParentFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * + joint.rotation); } void FaceModel::updateJointState(int index) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 3b09a1a2ba..2f35b96181 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -219,10 +219,9 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { JointState& parentState = _jointStates[parentJointIndex]; parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY); // lock hand to forearm by slamming its rotation (in parent-frame) to identity - _jointStates[jointIndex]._rotationInParentFrame = glm::quat(); + _jointStates[jointIndex].setRotationInParentFrame(glm::quat()); } else { - setJointPosition(jointIndex, palmPosition, palmRotation, - true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); + inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY); } } @@ -258,9 +257,9 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const glm::mat3 axes = glm::mat3_cast(glm::quat()); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) * joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation))); - state._rotationInParentFrame = glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), + state.setRotationInParentFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), - glm::normalize(inverse * axes[0])) * joint.rotation; + glm::normalize(inverse * axes[0])) * joint.rotation); } void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { @@ -537,6 +536,11 @@ void SkeletonModel::buildRagdollConstraints() { } } +void SkeletonModel::updateVisibleJointStates() { + Model::updateVisibleJointStates(); + // TODO: implement this to move visible joints to agree with joint shape positions +} + // virtual void SkeletonModel::stepRagdollForward(float deltaTime) { const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.03f; @@ -644,7 +648,6 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; continue; } - assert(parentIndex != -1); glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation; transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) @@ -700,7 +703,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { _boundingRadius = 0.5f * glm::length(diagonal); } -void SkeletonModel::resetShapePositions() { +void SkeletonModel::resetShapePositionsToDefaultPose() { // DEBUG method. // Moves shapes to the joint default locations for debug visibility into // how the bounding shape is computed. diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index b91c112b6a..76d0d45efa 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -93,6 +93,8 @@ public: /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; + + virtual void updateVisibleJointStates(); // virtual overrride from Ragdoll virtual void stepRagdollForward(float deltaTime); @@ -104,7 +106,7 @@ public: float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } const CapsuleShape& getBoundingShape() const { return _boundingShape; } - void resetShapePositions(); // DEBUG method + void resetShapePositionsToDefaultPose(); // DEBUG method void renderRagdoll(); protected: diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index dbe444bf9b..199c313119 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -266,7 +266,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); - // We only need to render the overlays to a texture once, then we just render the texture as a quad + // We only need to render the overlays to a texture once, then we just render the texture on the hemisphere // PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay() applicationOverlay.renderOverlay(true); const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::DisplayOculusOverlays); diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index b5cc28b07f..25d3ff892a 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -93,6 +93,18 @@ void TV3DManager::display(Camera& whichCamera) { int portalW = Application::getInstance()->getGLWidget()->width() / 2; int portalH = Application::getInstance()->getGLWidget()->height(); + const bool glowEnabled = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect); + + ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); + + // We only need to render the overlays to a texture once, then we just render the texture as a quad + // PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay() + applicationOverlay.renderOverlay(true); + + if (glowEnabled) { + Application::getInstance()->getGlowEffect()->prepare(); + } + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); @@ -102,13 +114,21 @@ void TV3DManager::display(Camera& whichCamera) { glPushMatrix(); { + glMatrixMode(GL_PROJECTION); glLoadIdentity(); // reset projection matrix glFrustum(_leftEye.left, _leftEye.right, _leftEye.bottom, _leftEye.top, nearZ, farZ); // set left view frustum + GLfloat p[4][4]; + glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0])); + GLfloat cotangent = p[1][1]; + GLfloat fov = atan(1.0f / cotangent); glTranslatef(_leftEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); Application::getInstance()->displaySide(whichCamera); + + applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); } glPopMatrix(); glDisable(GL_SCISSOR_TEST); @@ -124,14 +144,25 @@ void TV3DManager::display(Camera& whichCamera) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); // reset projection matrix glFrustum(_rightEye.left, _rightEye.right, _rightEye.bottom, _rightEye.top, nearZ, farZ); // set left view frustum + GLfloat p[4][4]; + glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0])); + GLfloat cotangent = p[1][1]; + GLfloat fov = atan(1.0f / cotangent); glTranslatef(_rightEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); Application::getInstance()->displaySide(whichCamera); + + applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); } glPopMatrix(); glDisable(GL_SCISSOR_TEST); // reset the viewport to how we started glViewport(0, 0, Application::getInstance()->getGLWidget()->width(), Application::getInstance()->getGLWidget()->height()); + + if (glowEnabled) { + Application::getInstance()->getGlowEffect()->render(); + } } diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index e66a2f44e9..8e4aecdf51 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -11,14 +11,26 @@ #include -//#include #include #include "JointState.h" JointState::JointState() : _animationPriority(0.0f), - _fbxJoint(NULL) { + _fbxJoint(NULL), + _isConstrained(false) { +} + +void JointState::copyState(const JointState& state) { + _animationPriority = state._animationPriority; + _transform = state._transform; + _rotation = extractRotation(_transform); + _rotationInParentFrame = state._rotationInParentFrame; + + _visibleTransform = state._visibleTransform; + _visibleRotation = extractRotation(_visibleTransform); + _visibleRotationInParentFrame = state._visibleRotationInParentFrame; + // DO NOT copy _fbxJoint } void JointState::setFBXJoint(const FBXJoint* joint) { @@ -26,14 +38,10 @@ void JointState::setFBXJoint(const FBXJoint* joint) { _rotationInParentFrame = joint->rotation; // NOTE: JointState does not own the FBXJoint to which it points. _fbxJoint = joint; -} - -void JointState::copyState(const JointState& state) { - _rotationInParentFrame = state._rotationInParentFrame; - _transform = state._transform; - _rotation = extractRotation(_transform); - _animationPriority = state._animationPriority; - // DO NOT copy _fbxJoint + // precompute whether there are any constraints or not + float distanceMin = glm::distance(_fbxJoint->rotationMin, glm::vec3(-PI)); + float distanceMax = glm::distance(_fbxJoint->rotationMax, glm::vec3(PI)); + _isConstrained = distanceMin > EPSILON || distanceMax > EPSILON; } void JointState::computeTransform(const glm::mat4& parentTransform) { @@ -43,6 +51,13 @@ void JointState::computeTransform(const glm::mat4& parentTransform) { _rotation = extractRotation(_transform); } +void JointState::computeVisibleTransform(const glm::mat4& parentTransform) { + glm::quat modifiedRotation = _fbxJoint->preRotation * _visibleRotationInParentFrame * _fbxJoint->postRotation; + glm::mat4 modifiedTransform = _fbxJoint->preTransform * glm::mat4_cast(modifiedRotation) * _fbxJoint->postTransform; + _visibleTransform = parentTransform * glm::translate(_fbxJoint->translation) * modifiedTransform; + _visibleRotation = extractRotation(_visibleTransform); +} + glm::quat JointState::getRotationFromBindToModelFrame() const { return _rotation * _fbxJoint->inverseBindRotation; } @@ -50,16 +65,16 @@ glm::quat JointState::getRotationFromBindToModelFrame() const { void JointState::restoreRotation(float fraction, float priority) { assert(_fbxJoint != NULL); if (priority == _animationPriority || _animationPriority == 0.0f) { - _rotationInParentFrame = safeMix(_rotationInParentFrame, _fbxJoint->rotation, fraction); + setRotationInParentFrame(safeMix(_rotationInParentFrame, _fbxJoint->rotation, fraction)); _animationPriority = 0.0f; } } void JointState::setRotationFromBindFrame(const glm::quat& rotation, float priority) { + // rotation is from bind- to model-frame assert(_fbxJoint != NULL); if (priority >= _animationPriority) { - // rotation is from bind- to model-frame - _rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation); + setRotationInParentFrame(_rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation)); _animationPriority = priority; } } @@ -68,6 +83,9 @@ void JointState::clearTransformTranslation() { _transform[3][0] = 0.0f; _transform[3][1] = 0.0f; _transform[3][2] = 0.0f; + _visibleTransform[3][0] = 0.0f; + _visibleTransform[3][1] = 0.0f; + _visibleTransform[3][2] = 0.0f; } void JointState::setRotation(const glm::quat& rotation, bool constrain, float priority) { @@ -75,27 +93,59 @@ void JointState::setRotation(const glm::quat& rotation, bool constrain, float pr } void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, float priority) { - // NOTE: delta is in jointParent-frame + // NOTE: delta is in model-frame assert(_fbxJoint != NULL); if (priority < _animationPriority) { return; } _animationPriority = priority; - if (!constrain || (_fbxJoint->rotationMin == glm::vec3(-PI, -PI, -PI) && - _fbxJoint->rotationMax == glm::vec3(PI, PI, PI))) { + if (!constrain || !_isConstrained) { // no constraints _rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation; _rotation = delta * _rotation; return; } - glm::quat targetRotation = delta * _rotation; - glm::vec3 eulers = safeEulerAngles(_rotationInParentFrame * glm::inverse(_rotation) * targetRotation); - glm::quat newRotation = glm::quat(glm::clamp(eulers, _fbxJoint->rotationMin, _fbxJoint->rotationMax)); - _rotation = _rotation * glm::inverse(_rotationInParentFrame) * newRotation; - _rotationInParentFrame = newRotation; + glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation; + setRotationInParentFrame(targetRotation); +} + +/// Applies delta rotation to joint but mixes a little bit of the default pose as well. +/// This helps keep an IK solution stable. +void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float priority) { + // NOTE: delta is in model-frame + assert(_fbxJoint != NULL); + if (priority < _animationPriority) { + return; + } + _animationPriority = priority; + glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation; + if (mixFactor > 0.0f && mixFactor <= 1.0f) { + targetRotation = safeMix(targetRotation, _fbxJoint->rotation, mixFactor); + } + setRotationInParentFrame(targetRotation); +} + + +glm::quat JointState::computeParentRotation() const { + // R = Rp * Rpre * r * Rpost + // Rp = R * (Rpre * r * Rpost)^ + return _rotation * glm::inverse(_fbxJoint->preRotation * _rotationInParentFrame * _fbxJoint->postRotation); +} + +void JointState::setRotationInParentFrame(const glm::quat& targetRotation) { + glm::quat parentRotation = computeParentRotation(); + _rotationInParentFrame = targetRotation; + // R' = Rp * Rpre * r' * Rpost + _rotation = parentRotation * _fbxJoint->preRotation * _rotationInParentFrame * _fbxJoint->postRotation; } const glm::vec3& JointState::getDefaultTranslationInParentFrame() const { assert(_fbxJoint != NULL); return _fbxJoint->translation; } + +void JointState::slaveVisibleTransform() { + _visibleTransform = _transform; + _visibleRotation = _rotation; + _visibleRotationInParentFrame = _rotationInParentFrame; +} diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index b1a584d4ec..8412cfd0cb 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -22,12 +22,19 @@ class JointState { public: JointState(); + void copyState(const JointState& state); + void setFBXJoint(const FBXJoint* joint); const FBXJoint& getFBXJoint() const { return *_fbxJoint; } - void copyState(const JointState& state); void computeTransform(const glm::mat4& parentTransform); + + void computeVisibleTransform(const glm::mat4& parentTransform); + const glm::mat4& getVisibleTransform() const { return _visibleTransform; } + glm::quat getVisibleRotation() const { return _visibleRotation; } + glm::vec3 getVisiblePosition() const { return extractTranslation(_visibleTransform); } + const glm::mat4& getTransform() const { return _transform; } glm::quat getRotation() const { return _rotation; } @@ -39,11 +46,19 @@ public: /// \param rotation rotation of joint in model-frame void setRotation(const glm::quat& rotation, bool constrain, float priority); - /// \param delta is in the jointParent-frame + /// \param delta is in the model-frame void applyRotationDelta(const glm::quat& delta, bool constrain = true, float priority = 1.0f); - const glm::vec3& getDefaultTranslationInParentFrame() const; + /// Applies delta rotation to joint but mixes a little bit of the default pose as well. + /// This helps keep an IK solution stable. + /// \param delta rotation change in model-frame + /// \param mixFactor fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all) + /// \param priority priority level of this animation blend + void mixRotationDelta(const glm::quat& delta, float mixFactor, float priority = 1.0f); + /// Blends a fraciton of default pose into joint rotation. + /// \param fraction fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all) + /// \param priority priority level of this animation blend void restoreRotation(float fraction, float priority); /// \param rotation is from bind- to model-frame @@ -51,16 +66,36 @@ public: /// NOTE: the JointState's model-frame transform/rotation are NOT updated! void setRotationFromBindFrame(const glm::quat& rotation, float priority); + void setRotationInParentFrame(const glm::quat& targetRotation); + const glm::quat& getRotationInParentFrame() const { return _rotationInParentFrame; } + + const glm::vec3& getDefaultTranslationInParentFrame() const; + + void clearTransformTranslation(); - glm::quat _rotationInParentFrame; // joint- to parentJoint-frame + void slaveVisibleTransform(); + float _animationPriority; // the priority of the animation affecting this joint private: + /// \return parent model-frame rotation + // (used to keep _rotation consistent when modifying _rotationInWorldFrame directly) + glm::quat computeParentRotation() const; + + /// debug helper function + void loadBindRotation(); + glm::mat4 _transform; // joint- to model-frame glm::quat _rotation; // joint- to model-frame + glm::quat _rotationInParentFrame; // joint- to parentJoint-frame + + glm::mat4 _visibleTransform; + glm::quat _visibleRotation; + glm::quat _visibleRotationInParentFrame; const FBXJoint* _fbxJoint; // JointState does NOT own its FBXJoint + bool _isConstrained; }; #endif // hifi_JointState_h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 3b5cda4fd2..ffca2c8ea6 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -39,8 +39,8 @@ Model::Model(QObject* parent) : _scaledToFit(false), _snapModelToCenter(false), _snappedToCenter(false), + _showTrueJointTransforms(false), _rootIndex(-1), - //_enableCollisionShapes(false), _lodDistance(0.0f), _pupilDilation(0.0f), _url("http://invalid.com") { @@ -460,7 +460,7 @@ void Model::reset() { } const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i]._rotationInParentFrame = geometry.joints.at(i).rotation; + _jointStates[i].setRotationInParentFrame(geometry.joints.at(i).rotation); } } @@ -571,6 +571,9 @@ void Model::setJointStates(QVector states) { radius = distance; } } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } _boundingRadius = radius; } @@ -686,7 +689,7 @@ bool Model::getJointState(int index, glm::quat& rotation) const { if (index == -1 || index >= _jointStates.size()) { return false; } - rotation = _jointStates.at(index)._rotationInParentFrame; + rotation = _jointStates.at(index).getRotationInParentFrame(); const glm::quat& defaultRotation = _geometry->getFBXGeometry().joints.at(index).rotation; return glm::abs(rotation.x - defaultRotation.x) >= EPSILON || glm::abs(rotation.y - defaultRotation.y) >= EPSILON || @@ -699,7 +702,7 @@ void Model::setJointState(int index, bool valid, const glm::quat& rotation, floa JointState& state = _jointStates[index]; if (priority >= state._animationPriority) { if (valid) { - state._rotationInParentFrame = rotation; + state.setRotationInParentFrame(rotation); state._animationPriority = priority; } else { state.restoreRotation(1.0f, priority); @@ -765,6 +768,23 @@ bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const return true; } +bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + // position is in world-frame + position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition(); + return true; +} + +bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + rotation = _rotation * _jointStates[jointIndex].getVisibleRotation(); + return true; +} + QStringList Model::getJointNames() const { if (QThread::currentThread() != thread()) { QStringList result; @@ -918,6 +938,8 @@ void Model::simulateInternal(float deltaTime) { for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i); } + updateVisibleJointStates(); + _shapesAreDirty = ! _shapes.isEmpty(); // update the attachment transforms and simulate them @@ -928,8 +950,13 @@ void Model::simulateInternal(float deltaTime) { glm::vec3 jointTranslation = _translation; glm::quat jointRotation = _rotation; - getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation); - getJointRotationInWorldFrame(attachment.jointIndex, jointRotation); + if (_showTrueJointTransforms) { + getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation); + getJointRotationInWorldFrame(attachment.jointIndex, jointRotation); + } else { + getVisibleJointPositionInWorldFrame(attachment.jointIndex, jointTranslation); + getVisibleJointRotationInWorldFrame(attachment.jointIndex, jointRotation); + } model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale); model->setRotation(jointRotation * attachment.rotation); @@ -944,9 +971,16 @@ void Model::simulateInternal(float deltaTime) { for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); - for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + if (_showTrueJointTransforms) { + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + } + } else { + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; + } } } @@ -972,6 +1006,14 @@ void Model::updateJointState(int index) { } } +void Model::updateVisibleJointStates() { + if (!_showTrueJointTransforms) { + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } + } +} + bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { if (jointIndex == -1 || _jointStates.isEmpty()) { @@ -1058,6 +1100,128 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl return true; } +void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { + // NOTE: targetRotation is from bind- to model-frame + + if (endIndex == -1 || _jointStates.isEmpty()) { + return; + } + + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; + if (freeLineage.isEmpty()) { + return; + } + int numFree = freeLineage.size(); + + // store and remember topmost parent transform + glm::mat4 topParentTransform; + { + int index = freeLineage.last(); + const JointState& state = _jointStates.at(index); + const FBXJoint& joint = state.getFBXJoint(); + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + topParentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + } else { + topParentTransform = _jointStates[parentIndex].getTransform(); + } + } + + // this is a cyclic coordinate descent algorithm: see + // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d + + // keep track of the position of the end-effector + JointState& endState = _jointStates[endIndex]; + glm::vec3 endPosition = endState.getPosition(); + float distanceToGo = glm::distance(targetPosition, endPosition); + + const int MAX_ITERATION_COUNT = 2; + const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm + int numIterations = 0; + do { + ++numIterations; + // moving up, rotate each free joint to get endPosition closer to target + for (int j = 1; j < numFree; j++) { + int nextIndex = freeLineage.at(j); + JointState& nextState = _jointStates[nextIndex]; + FBXJoint nextJoint = nextState.getFBXJoint(); + if (! nextJoint.isFree) { + continue; + } + + glm::vec3 pivot = nextState.getPosition(); + glm::vec3 leverArm = endPosition - pivot; + float leverLength = glm::length(leverArm); + if (leverLength < EPSILON) { + continue; + } + glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot); + + /* DON'T REMOVE! This code provides the gravitational effect on the IK solution. + * It is commented out for the moment because we're blending the IK solution with + * the default pose which provides similar stability, but we might want to use + * gravity again later. + + // We want to mix the shortest rotation with one that will pull the system down with gravity. + // So we compute a simplified center of mass, where each joint has a mass of 1.0 and we don't + // bother averaging it because we only need direction. + if (j > 1) { + + glm::vec3 centerOfMass(0.0f); + for (int k = 0; k < j; ++k) { + int massIndex = freeLineage.at(k); + centerOfMass += _jointStates[massIndex].getPosition() - pivot; + } + // the gravitational effect is a rotation that tends to align the two cross products + const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.f, 0.0f); + glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm), + glm::cross(worldAlignment, leverArm)); + + float gravityAngle = glm::angle(gravityDelta); + const float MIN_GRAVITY_ANGLE = 0.1f; + float mixFactor = 0.5f; + if (gravityAngle < MIN_GRAVITY_ANGLE) { + // the final rotation is a mix of the two + mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE; + } + deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor); + } + */ + + // Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose + // at in the process. This provides stability to the IK solution and removes the necessity + // for the gravity effect. + glm::quat oldNextRotation = nextState.getRotation(); + float mixFactor = 0.03f; + nextState.mixRotationDelta(deltaRotation, mixFactor, priority); + + // measure the result of the rotation which may have been modified by + // blending and constraints + glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation); + endPosition = pivot + actualDelta * leverArm; + } + + // recompute transforms from the top down + glm::mat4 parentTransform = topParentTransform; + for (int j = numFree - 1; j >= 0; --j) { + JointState& freeState = _jointStates[freeLineage.at(j)]; + freeState.computeTransform(parentTransform); + parentTransform = freeState.getTransform(); + } + + // measure our success + endPosition = endState.getPosition(); + distanceToGo = glm::distance(targetPosition, endPosition); + } while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR); + + // set final rotation of the end joint + endState.setRotationFromBindFrame(targetRotation, priority); + + _shapesAreDirty = !_shapes.isEmpty(); +} + bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; @@ -1605,7 +1769,7 @@ void AnimationHandle::applyFrame(float frameIndex) { if (mapping != -1) { JointState& state = _model->_jointStates[mapping]; if (_priority >= state._animationPriority) { - state._rotationInParentFrame = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); + state.setRotationInParentFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction)); state._animationPriority = _priority; } } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 2045a0c9b5..835207b7eb 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -120,6 +120,9 @@ public: bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const; + bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const; + bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; + /// \param jointIndex index of joint in model structure /// \param position[out] position of joint in model-frame /// \return true if joint exists @@ -152,6 +155,7 @@ protected: bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space bool _snappedToCenter; /// are we currently snapped to center + bool _showTrueJointTransforms; int _rootIndex; QVector _jointStates; @@ -176,6 +180,8 @@ protected: /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); + + virtual void updateVisibleJointStates(); /// \param jointIndex index of joint in model structure /// \param position position of joint in model-frame @@ -188,6 +194,8 @@ protected: bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation = glm::quat(), bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f); + + void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority); /// Restores the indexed joint to its default position. /// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 55a67ce854..01c3dc1cc1 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -85,6 +85,33 @@ void TextureCache::setFrameBufferSize(QSize frameBufferSize) { } } +// use fixed table of permutations. Could also make ordered list programmatically +// and then shuffle algorithm. For testing, this ensures consistent behavior in each run. +// this list taken from Ken Perlin's Improved Noise reference implementation (orig. in Java) at +// http://mrl.nyu.edu/~perlin/noise/ + +const int permutation[256] = +{ + 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, + 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, + 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, + 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, + 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, + 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, + 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, + 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, + 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, + 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, + 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, + 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, + 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, + 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, + 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, + 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 +}; + +#define USE_CHRIS_NOISE 1 + GLuint TextureCache::getPermutationNormalTextureID() { if (_permutationNormalTextureID == 0) { glGenTextures(1, &_permutationNormalTextureID); @@ -92,10 +119,17 @@ GLuint TextureCache::getPermutationNormalTextureID() { // the first line consists of random permutation offsets unsigned char data[256 * 2 * 3]; +#if (USE_CHRIS_NOISE==1) + for (int i = 0; i < 256; i++) { + data[3*i+0] = permutation[i]; + data[3*i+1] = permutation[i]; + data[3*i+2] = permutation[i]; +#else for (int i = 0; i < 256 * 3; i++) { data[i] = rand() % 256; +#endif } - // the next, random unit normals + for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) { glm::vec3 randvec = glm::sphericalRand(1.0f); data[i] = ((randvec.x + 1.0f) / 2.0f) * 255.0f; @@ -105,7 +139,6 @@ GLuint TextureCache::getPermutationNormalTextureID() { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glBindTexture(GL_TEXTURE_2D, 0); } return _permutationNormalTextureID; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 77e8986297..4c445958ec 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -41,8 +41,7 @@ ApplicationOverlay::ApplicationOverlay() : _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), _alpha(1.0f), _active(true), - _crosshairTexture(0) -{ + _crosshairTexture(0) { memset(_reticleActive, 0, sizeof(_reticleActive)); memset(_magActive, 0, sizeof(_reticleActive)); @@ -206,7 +205,7 @@ void ApplicationOverlay::getClickLocation(int &x, int &y) const { } } -// Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane. +// Draws the FBO texture for Oculus rift. void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { if (_alpha == 0.0f) { @@ -293,6 +292,107 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { } +// Draws the FBO texture for 3DTV. +void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov) { + + if (_alpha == 0.0f) { + return; + } + + Application* application = Application::getInstance(); + + MyAvatar* myAvatar = application->getAvatar(); + const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); + + glActiveTexture(GL_TEXTURE0); + + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); + glEnable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + + glMatrixMode(GL_MODELVIEW); + + glPushMatrix(); + glLoadIdentity(); + // Transform to world space + glm::quat rotation = whichCamera.getRotation(); + glm::vec3 axis2 = glm::axis(rotation); + glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z); + glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z); + + // Translate to the front of the camera + glm::vec3 pos = whichCamera.getPosition(); + glm::quat rot = myAvatar->getOrientation(); + glm::vec3 axis = glm::axis(rot); + + glTranslatef(pos.x, pos.y, pos.z); + glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z); + + glColor4f(1.0f, 1.0f, 1.0f, _alpha); + + //Render + // fov -= RADIANS_PER_DEGREE * 2.5f; //reduce by 5 degrees so it fits in the view + const GLfloat distance = 1.0f; + + const GLfloat halfQuadHeight = distance * tan(fov); + const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio; + const GLfloat quadWidth = halfQuadWidth * 2.0f; + const GLfloat quadHeight = halfQuadHeight * 2.0f; + + GLfloat x = -halfQuadWidth; + GLfloat y = -halfQuadHeight; + glDisable(GL_DEPTH_TEST); + + glBegin(GL_QUADS); + + glTexCoord2f(0.0f, 1.0f); glVertex3f(x, y + quadHeight, -distance); + glTexCoord2f(1.0f, 1.0f); glVertex3f(x + quadWidth, y + quadHeight, -distance); + glTexCoord2f(1.0f, 0.0f); glVertex3f(x + quadWidth, y, -distance); + glTexCoord2f(0.0f, 0.0f); glVertex3f(x, y, -distance); + + glEnd(); + + if (_crosshairTexture == 0) { + _crosshairTexture = Application::getInstance()->getGLWidget()->bindTexture(QImage(Application::resourcesPath() + "images/sixense-reticle.png")); + } + + //draw the mouse pointer + glBindTexture(GL_TEXTURE_2D, _crosshairTexture); + + const float reticleSize = 40.0f / application->getGLWidget()->width() * quadWidth; + x -= reticleSize / 2.0f; + y += reticleSize / 2.0f; + const float mouseX = (application->getMouseX() / (float)application->getGLWidget()->width()) * quadWidth; + const float mouseY = (1.0 - (application->getMouseY() / (float)application->getGLWidget()->height())) * quadHeight; + + glBegin(GL_QUADS); + + glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]); + + glTexCoord2d(0.0f, 0.0f); glVertex3f(x + mouseX, y + mouseY, -distance); + glTexCoord2d(1.0f, 0.0f); glVertex3f(x + mouseX + reticleSize, y + mouseY, -distance); + glTexCoord2d(1.0f, 1.0f); glVertex3f(x + mouseX + reticleSize, y + mouseY - reticleSize, -distance); + glTexCoord2d(0.0f, 1.0f); glVertex3f(x + mouseX, y + mouseY - reticleSize, -distance); + + glEnd(); + + glEnable(GL_DEPTH_TEST); + + glPopMatrix(); + + glDepthMask(GL_TRUE); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glEnable(GL_LIGHTING); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); +} + //Renders optional pointers void ApplicationOverlay::renderPointers() { Application* application = Application::getInstance(); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index b9f9596ccf..7c1f87d575 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -29,6 +29,7 @@ public: void renderOverlay(bool renderToTexture = false); void displayOverlayTexture(); void displayOverlayTextureOculus(Camera& whichCamera); + void displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov); void computeOculusPickRay(float x, float y, glm::vec3& direction) const; void getClickLocation(int &x, int &y) const; diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 203c54d97a..ec583a14f7 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -14,10 +14,11 @@ #include #include #include -#include #include #include +#include + #include "Application.h" #include "ModelsBrowser.h" @@ -210,10 +211,10 @@ void ModelHandler::update() { } for (int i = 0; i < _model.rowCount(); ++i) { QUrl url(_model.item(i,0)->data(Qt::UserRole).toString()); - QNetworkAccessManager* accessManager = new QNetworkAccessManager(this); + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request(url); - accessManager->head(request); - connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*))); + QNetworkReply* reply = networkAccessManager.head(request); + connect(reply, SIGNAL(finished()), SLOT(processCheck())); } _lock.unlock(); } @@ -233,7 +234,8 @@ void ModelHandler::exit() { _lock.unlock(); } -void ModelHandler::downloadFinished(QNetworkReply* reply) { +void ModelHandler::downloadFinished() { + QNetworkReply* reply = static_cast(sender()); QByteArray data = reply->readAll(); if (!data.isEmpty()) { @@ -261,10 +263,10 @@ void ModelHandler::queryNewFiles(QString marker) { // Download url.setQuery(query); - QNetworkAccessManager* accessManager = new QNetworkAccessManager(this); + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request(url); - accessManager->get(request); - connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*))); + QNetworkReply* reply = networkAccessManager.get(request); + connect(reply, SIGNAL(finished()), SLOT(processCheck())); } diff --git a/interface/src/ui/ModelsBrowser.h b/interface/src/ui/ModelsBrowser.h index 3e832c9dbe..8063fd8eeb 100644 --- a/interface/src/ui/ModelsBrowser.h +++ b/interface/src/ui/ModelsBrowser.h @@ -43,7 +43,7 @@ public slots: void exit(); private slots: - void downloadFinished(QNetworkReply* reply); + void downloadFinished(); private: bool _initiateExit; diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp index 513bbd899a..d25408e568 100644 --- a/interface/src/ui/ScriptEditorWidget.cpp +++ b/interface/src/ui/ScriptEditorWidget.cpp @@ -148,8 +148,8 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) { disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } } else { - QNetworkAccessManager* networkManager = new QNetworkAccessManager(this); - QNetworkReply* reply = networkManager->get(QNetworkRequest(url)); + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); qDebug() << "Downloading included script at" << scriptPath; QEventLoop loop; QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp index 617d5e7101..046aa4d12c 100644 --- a/interface/src/ui/SnapshotShareDialog.cpp +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -40,8 +40,7 @@ Q_DECLARE_METATYPE(QNetworkAccessManager::Operation) SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : QDialog(parent), - _fileName(fileName), - _networkAccessManager(NULL) + _fileName(fileName) { setAttribute(Qt::WA_DeleteOnClose); @@ -92,10 +91,6 @@ void SnapshotShareDialog::uploadSnapshot() { return; } - if (!_networkAccessManager) { - _networkAccessManager = new QNetworkAccessManager(this); - } - QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpPart apiKeyPart; @@ -118,9 +113,7 @@ void SnapshotShareDialog::uploadSnapshot() { QUrl url(FORUM_UPLOADS_URL); QNetworkRequest request(url); - QNetworkReply* reply = _networkAccessManager->post(request, multiPart); - - + QNetworkReply* reply = NetworkAccessManager::getInstance().post(request, multiPart); connect(reply, &QNetworkReply::finished, this, &SnapshotShareDialog::uploadRequestFinished); QEventLoop loop; @@ -129,11 +122,6 @@ void SnapshotShareDialog::uploadSnapshot() { } void SnapshotShareDialog::sendForumPost(QString snapshotPath) { - - if (!_networkAccessManager) { - _networkAccessManager = new QNetworkAccessManager(this); - } - // post to Discourse forum QNetworkRequest request; QUrl forumUrl(FORUM_POST_URL); @@ -148,7 +136,7 @@ void SnapshotShareDialog::sendForumPost(QString snapshotPath) { request.setUrl(forumUrl); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - QNetworkReply* requestReply = _networkAccessManager->post(request, postData); + QNetworkReply* requestReply = NetworkAccessManager::getInstance().post(request, postData); connect(requestReply, &QNetworkReply::finished, this, &SnapshotShareDialog::postRequestFinished); QEventLoop loop; diff --git a/interface/src/ui/SnapshotShareDialog.h b/interface/src/ui/SnapshotShareDialog.h index a8795d578a..ced9f2d951 100644 --- a/interface/src/ui/SnapshotShareDialog.h +++ b/interface/src/ui/SnapshotShareDialog.h @@ -14,7 +14,6 @@ #include "ui_shareSnapshot.h" -#include #include #include @@ -26,7 +25,6 @@ public: private: QString _fileName; - QNetworkAccessManager* _networkAccessManager; Ui_SnapshotShareDialog _ui; void uploadSnapshot(); diff --git a/interface/src/ui/UserLocationsDialog.cpp b/interface/src/ui/UserLocationsDialog.cpp new file mode 100644 index 0000000000..f72e66ce77 --- /dev/null +++ b/interface/src/ui/UserLocationsDialog.cpp @@ -0,0 +1,77 @@ +// +// UserLocationsDialog.cpp +// interface/src/ui +// +// Created by Ryan Huffman on 06/24/14. +// Copyright 2014 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 +// + +#include +#include +#include + +#include "Menu.h" +#include "UserLocationsDialog.h" + +UserLocationsDialog::UserLocationsDialog(QWidget* parent) : + QDialog(parent), + _ui(), + _proxyModel(this), + _userLocationsModel(this) { + + _ui.setupUi(this); + + _proxyModel.setSourceModel(&_userLocationsModel); + _proxyModel.setDynamicSortFilter(true); + + _ui.locationsTreeView->setModel(&_proxyModel); + _ui.locationsTreeView->setSortingEnabled(true); + _ui.locationsTreeView->sortByColumn(UserLocationsModel::NameColumn, Qt::AscendingOrder); + + connect(_ui.locationsTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &UserLocationsDialog::updateEnabled); + connect(&_userLocationsModel, &UserLocationsModel::modelReset, this, &UserLocationsDialog::updateEnabled); + connect(&_userLocationsModel, &UserLocationsModel::modelReset, &_proxyModel, &QSortFilterProxyModel::invalidate); + connect(_ui.locationsTreeView, &QTreeView::doubleClicked, this, &UserLocationsDialog::goToModelIndex); + + connect(_ui.deleteButton, &QPushButton::clicked, this, &UserLocationsDialog::deleteSelection); + connect(_ui.renameButton, &QPushButton::clicked, this, &UserLocationsDialog::renameSelection); + connect(_ui.refreshButton, &QPushButton::clicked, &_userLocationsModel, &UserLocationsModel::refresh); + + this->setWindowTitle("My Locations"); +} + +void UserLocationsDialog::updateEnabled() { + bool enabled = _ui.locationsTreeView->selectionModel()->hasSelection(); + _ui.renameButton->setEnabled(enabled); + _ui.deleteButton->setEnabled(enabled); +} + +void UserLocationsDialog::goToModelIndex(const QModelIndex& index) { + QVariant location = _proxyModel.data(index.sibling(index.row(), UserLocationsModel::LocationColumn)); + Menu::getInstance()->goToURL(location.toString()); +} + +void UserLocationsDialog::deleteSelection() { + QModelIndex selection = _ui.locationsTreeView->selectionModel()->currentIndex(); + selection = _proxyModel.mapToSource(selection); + if (selection.isValid()) { + _userLocationsModel.deleteLocation(selection); + } +} + +void UserLocationsDialog::renameSelection() { + QModelIndex selection = _ui.locationsTreeView->selectionModel()->currentIndex(); + selection = _proxyModel.mapToSource(selection); + if (selection.isValid()) { + bool ok; + QString name = _userLocationsModel.data(selection.sibling(selection.row(), UserLocationsModel::NameColumn)).toString(); + QString newName = QInputDialog::getText(this, "Rename '" + name + "'", "Set name to:", QLineEdit::Normal, name, &ok); + if (ok && !newName.isEmpty()) { + _userLocationsModel.renameLocation(selection, newName); + } + } +} diff --git a/interface/src/ui/UserLocationsDialog.h b/interface/src/ui/UserLocationsDialog.h new file mode 100644 index 0000000000..0e596ece87 --- /dev/null +++ b/interface/src/ui/UserLocationsDialog.h @@ -0,0 +1,35 @@ +// +// UserLocationsDialog.h +// interface/src/ui +// +// Created by Ryan Huffman on 06/24/14. +// Copyright 2014 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 +// + +#ifndef hifi_UserLocationsDialog_h +#define hifi_UserLocationsDialog_h + +#include "ui_userLocationsDialog.h" +#include "UserLocationsModel.h" + +class UserLocationsDialog : public QDialog { + Q_OBJECT +public: + UserLocationsDialog(QWidget* parent = NULL); + +protected slots: + void updateEnabled(); + void goToModelIndex(const QModelIndex& index); + void deleteSelection(); + void renameSelection(); + +private: + Ui::UserLocationsDialog _ui; + QSortFilterProxyModel _proxyModel; + UserLocationsModel _userLocationsModel; +}; + +#endif // hifi_UserLocationsDialog_h diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 40de565155..8742f19c3d 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -14,8 +14,7 @@ #include "BillboardOverlay.h" BillboardOverlay::BillboardOverlay() -: _manager(NULL), - _scale(1.0f), +: _scale(1.0f), _isFacingAvatar(true) { } @@ -119,18 +118,13 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { } } -// TODO: handle setting image multiple times, how do we manage releasing the bound texture? void BillboardOverlay::setBillboardURL(const QUrl url) { - // TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made? - _manager->deleteLater(); - _manager = new QNetworkAccessManager(); - connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); - _manager->get(QNetworkRequest(url)); + QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(url)); + connect(reply, &QNetworkReply::finished, this, &BillboardOverlay::replyFinished); } -void BillboardOverlay::replyFinished(QNetworkReply* reply) { +void BillboardOverlay::replyFinished() { // replace our byte array with the downloaded data + QNetworkReply* reply = static_cast(sender()); _billboard = reply->readAll(); - _manager->deleteLater(); - _manager = NULL; } diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 473e8a066f..0037d1a4f7 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -27,12 +27,11 @@ public: virtual void setProperties(const QScriptValue& properties); private slots: - void replyFinished(QNetworkReply* reply); + void replyFinished(); private: void setBillboardURL(const QUrl url); - QNetworkAccessManager* _manager; QUrl _url; QByteArray _billboard; QSize _size; diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 79b1b23de5..aeea781eb6 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -19,7 +19,6 @@ #include "ImageOverlay.h" ImageOverlay::ImageOverlay() : - _manager(NULL), _textureID(0), _renderImage(false), _textureBound(false), @@ -36,21 +35,18 @@ ImageOverlay::~ImageOverlay() { // TODO: handle setting image multiple times, how do we manage releasing the bound texture? void ImageOverlay::setImageURL(const QUrl& url) { - // TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made? - _manager->deleteLater(); - _manager = new QNetworkAccessManager(); - connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); - _manager->get(QNetworkRequest(url)); + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); + connect(reply, &QNetworkReply::finished, this, &ImageOverlay::replyFinished); } -void ImageOverlay::replyFinished(QNetworkReply* reply) { - +void ImageOverlay::replyFinished() { + QNetworkReply* reply = static_cast(sender()); + // replace our byte array with the downloaded data QByteArray rawData = reply->readAll(); _textureImage.loadFromData(rawData); _renderImage = true; - _manager->deleteLater(); - _manager = NULL; } void ImageOverlay::render() { diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h index 613cd95989..633567287d 100644 --- a/interface/src/ui/overlays/ImageOverlay.h +++ b/interface/src/ui/overlays/ImageOverlay.h @@ -16,13 +16,13 @@ #include #include -#include #include #include #include #include #include +#include #include #include "Overlay.h" @@ -46,13 +46,12 @@ public: virtual void setProperties(const QScriptValue& properties); private slots: - void replyFinished(QNetworkReply* reply); // we actually want to hide this... + void replyFinished(); // we actually want to hide this... private: QUrl _imageURL; QImage _textureImage; - QNetworkAccessManager* _manager; GLuint _textureID; QRect _fromImage; // where from in the image to sample diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index dd483da27a..581947c074 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -84,13 +84,13 @@ void Overlays::render3D() { return; } bool myAvatarComputed = false; - MyAvatar* avatar; + MyAvatar* avatar = NULL; glm::quat myAvatarRotation; - glm::vec3 myAvatarPosition; - float angle; - glm::vec3 axis; - float myAvatarScale; - + glm::vec3 myAvatarPosition(0.0f); + float angle = 0.0f; + glm::vec3 axis(0.0f, 1.0f, 0.0f); + float myAvatarScale = 1.0f; + foreach(Overlay* thisOverlay, _overlays3D) { glPushMatrix(); switch (thisOverlay->getAnchor()) { diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index 78a037762e..c2aafb24e8 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -16,7 +16,6 @@ #include #include -#include #include #include #include diff --git a/interface/ui/userLocationsDialog.ui b/interface/ui/userLocationsDialog.ui new file mode 100644 index 0000000000..609ce1c8ab --- /dev/null +++ b/interface/ui/userLocationsDialog.ui @@ -0,0 +1,130 @@ + + + UserLocationsDialog + + + + 0 + 0 + 929 + 633 + + + + Form + + + + -1 + + + 12 + + + 12 + + + 12 + + + 12 + + + + + + + + font-size: 16px + + + My Locations + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Refresh + + + + + + + + + + 0 + + + false + + + + + + + + -1 + + + 12 + + + 12 + + + 12 + + + 12 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Rename + + + + + + + Delete + + + + + + + + + + + diff --git a/libraries/audio/CMakeLists.txt b/libraries/audio/CMakeLists.txt index 60636ba051..fafdfc7e6c 100644 --- a/libraries/audio/CMakeLists.txt +++ b/libraries/audio/CMakeLists.txt @@ -12,6 +12,9 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm set(TARGET_NAME audio) +find_package(Qt5 COMPONENTS Script) +include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}") + # set up the external glm library include(${MACRO_DIR}/SetupHifiLibrary.cmake) setup_hifi_library(${TARGET_NAME}) @@ -26,4 +29,4 @@ link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") # add a definition for ssize_t so that windows doesn't bail if (WIN32) add_definitions(-Dssize_t=long) -endif () \ No newline at end of file +endif () diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 6ae3b19541..7937a925a8 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -53,7 +53,7 @@ void AudioRingBuffer::reset() { _isStarved = true; } -void AudioRingBuffer::resizeForFrameSize(qint64 numFrameSamples) { +void AudioRingBuffer::resizeForFrameSize(int numFrameSamples) { delete[] _buffer; _sampleCapacity = numFrameSamples * RING_BUFFER_LENGTH_FRAMES; _buffer = new int16_t[_sampleCapacity]; @@ -70,14 +70,14 @@ int AudioRingBuffer::parseData(const QByteArray& packet) { return writeData(packet.data() + numBytesBeforeAudioData, packet.size() - numBytesBeforeAudioData); } -qint64 AudioRingBuffer::readSamples(int16_t* destination, qint64 maxSamples) { +int AudioRingBuffer::readSamples(int16_t* destination, int maxSamples) { return readData((char*) destination, maxSamples * sizeof(int16_t)); } -qint64 AudioRingBuffer::readData(char *data, qint64 maxSize) { +int AudioRingBuffer::readData(char *data, int maxSize) { // only copy up to the number of samples we have available - int numReadSamples = std::min((unsigned) (maxSize / sizeof(int16_t)), samplesAvailable()); + int numReadSamples = std::min((int) (maxSize / sizeof(int16_t)), samplesAvailable()); // If we're in random access mode, then we consider our number of available read samples slightly // differently. Namely, if anything has been written, we say we have as many samples as they ask for @@ -118,14 +118,14 @@ qint64 AudioRingBuffer::readData(char *data, qint64 maxSize) { return numReadSamples * sizeof(int16_t); } -qint64 AudioRingBuffer::writeSamples(const int16_t* source, qint64 maxSamples) { +int AudioRingBuffer::writeSamples(const int16_t* source, int maxSamples) { return writeData((const char*) source, maxSamples * sizeof(int16_t)); } -qint64 AudioRingBuffer::writeData(const char* data, qint64 maxSize) { +int AudioRingBuffer::writeData(const char* data, int maxSize) { // make sure we have enough bytes left for this to be the right amount of audio // otherwise we should not copy that data, and leave the buffer pointers where they are - int samplesToCopy = std::min((quint64)(maxSize / sizeof(int16_t)), (quint64)_sampleCapacity); + int samplesToCopy = std::min((int)(maxSize / sizeof(int16_t)), _sampleCapacity); int samplesRoomFor = _sampleCapacity - samplesAvailable(); if (samplesToCopy > samplesRoomFor) { @@ -167,7 +167,7 @@ void AudioRingBuffer::shiftReadPosition(unsigned int numSamples) { } } -unsigned int AudioRingBuffer::samplesAvailable() const { +int AudioRingBuffer::samplesAvailable() const { if (!_endOfLastWrite) { return 0; } @@ -208,7 +208,7 @@ int AudioRingBuffer::addSilentFrame(int numSilentSamples) { return numSilentSamples * sizeof(int16_t); } -bool AudioRingBuffer::isNotStarvedOrHasMinimumSamples(unsigned int numRequiredSamples) const { +bool AudioRingBuffer::isNotStarvedOrHasMinimumSamples(int numRequiredSamples) const { if (!_isStarved) { return true; } else { diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 8d19f9c0bb..80a2abeba4 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -43,7 +43,7 @@ public: ~AudioRingBuffer(); void reset(); - void resizeForFrameSize(qint64 numFrameSamples); + void resizeForFrameSize(int numFrameSamples); int getSampleCapacity() const { return _sampleCapacity; } @@ -53,20 +53,20 @@ public: const int16_t* getNextOutput() const { return _nextOutput; } const int16_t* getBuffer() const { return _buffer; } - qint64 readSamples(int16_t* destination, qint64 maxSamples); - qint64 writeSamples(const int16_t* source, qint64 maxSamples); + int readSamples(int16_t* destination, int maxSamples); + int writeSamples(const int16_t* source, int maxSamples); - qint64 readData(char* data, qint64 maxSize); - qint64 writeData(const char* data, qint64 maxSize); + int readData(char* data, int maxSize); + int writeData(const char* data, int maxSize); int16_t& operator[](const int index); const int16_t& operator[] (const int index) const; void shiftReadPosition(unsigned int numSamples); - unsigned int samplesAvailable() const; + int samplesAvailable() const; - bool isNotStarvedOrHasMinimumSamples(unsigned int numRequiredSamples) const; + bool isNotStarvedOrHasMinimumSamples(int numRequiredSamples) const; bool isStarved() const { return _isStarved; } void setIsStarved(bool isStarved) { _isStarved = isStarved; } diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 546ed97fe2..e11d73358c 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -212,7 +212,7 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix() { } return false; - } else if (samplesAvailable() < (unsigned int)samplesPerFrame) { + } else if (samplesAvailable() < samplesPerFrame) { // if the buffer doesn't have a full frame of samples to take for mixing, it is starved _isStarved = true; diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 7ef3afdf29..03c9f6b8ee 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -15,12 +15,12 @@ #include #include -#include #include #include #include #include +#include #include #include "AudioRingBuffer.h" @@ -73,11 +73,11 @@ Sound::Sound(const QUrl& sampleURL, QObject* parent) : // assume we have a QApplication or QCoreApplication instance and use the // QNetworkAccess manager to grab the raw audio file at the given URL - QNetworkAccessManager *manager = new QNetworkAccessManager(this); + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); qDebug() << "Requesting audio file" << sampleURL.toDisplayString(); - QNetworkReply* soundDownload = manager->get(QNetworkRequest(sampleURL)); + QNetworkReply* soundDownload = networkAccessManager.get(QNetworkRequest(sampleURL)); connect(soundDownload, &QNetworkReply::finished, this, &Sound::replyFinished); connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError))); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 94066d9a1c..3f3e71c5e8 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -16,10 +16,10 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -33,8 +33,6 @@ quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND; using namespace std; -QNetworkAccessManager* AvatarData::networkAccessManager = NULL; - AvatarData::AvatarData() : _sessionUUID(), _handPosition(0,0,0), @@ -751,18 +749,15 @@ void AvatarData::setBillboard(const QByteArray& billboard) { void AvatarData::setBillboardFromURL(const QString &billboardURL) { _billboardURL = billboardURL; - if (AvatarData::networkAccessManager) { - qDebug() << "Changing billboard for avatar to PNG at" << qPrintable(billboardURL); - - QNetworkRequest billboardRequest; - billboardRequest.setUrl(QUrl(billboardURL)); - - QNetworkReply* networkReply = AvatarData::networkAccessManager->get(billboardRequest); - connect(networkReply, SIGNAL(finished()), this, SLOT(setBillboardFromNetworkReply())); - - } else { - qDebug() << "Billboard PNG download requested but no network access manager is available."; - } + + qDebug() << "Changing billboard for avatar to PNG at" << qPrintable(billboardURL); + + QNetworkRequest billboardRequest; + billboardRequest.setUrl(QUrl(billboardURL)); + + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkReply* networkReply = networkAccessManager.get(billboardRequest); + connect(networkReply, SIGNAL(finished()), this, SLOT(setBillboardFromNetworkReply())); } void AvatarData::setBillboardFromNetworkReply() { @@ -839,8 +834,9 @@ void AvatarData::updateJointMappings() { _jointIndices.clear(); _jointNames.clear(); - if (networkAccessManager && _skeletonModelURL.fileName().toLower().endsWith(".fst")) { - QNetworkReply* networkReply = networkAccessManager->get(QNetworkRequest(_skeletonModelURL)); + if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) { + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkReply* networkReply = networkAccessManager.get(QNetworkRequest(_skeletonModelURL)); connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply())); } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 4c7136fd0a..1b8f6e6007 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -102,7 +102,6 @@ enum KeyState { const glm::vec3 vec3Zero(0.0f); class QDataStream; -class QNetworkAccessManager; class AttachmentData; class JointData; @@ -269,8 +268,6 @@ public: QElapsedTimer& getLastUpdateTimer() { return _lastUpdateTimer; } virtual float getBoundingRadius() const { return 1.f; } - - static void setNetworkAccessManager(QNetworkAccessManager* sharedAccessManager) { networkAccessManager = sharedAccessManager; } public slots: void sendIdentityPacket(); @@ -323,8 +320,6 @@ protected: QHash _jointIndices; ///< 1-based, since zero is returned for missing keys QStringList _jointNames; ///< in order of depth-first traversal - - static QNetworkAccessManager* networkAccessManager; quint64 _errorLogExpiry; ///< time in future when to log an error diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 918261a953..563d735790 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -37,19 +37,20 @@ Q_DECLARE_METATYPE(JSONCallbackParameters) const QString ACCOUNTS_GROUP = "accounts"; -JSONCallbackParameters::JSONCallbackParameters() : - jsonCallbackReceiver(NULL), - jsonCallbackMethod(), - errorCallbackReceiver(NULL), - errorCallbackMethod(), - updateReciever(NULL), - updateSlot() +JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, const QString& jsonCallbackMethod, + QObject* errorCallbackReceiver, const QString& errorCallbackMethod, + QObject* updateReceiver, const QString& updateSlot) : + jsonCallbackReceiver(jsonCallbackReceiver), + jsonCallbackMethod(jsonCallbackMethod), + errorCallbackReceiver(errorCallbackReceiver), + errorCallbackMethod(errorCallbackMethod), + updateReciever(updateReceiver), + updateSlot(updateSlot) { } AccountManager::AccountManager() : _authURL(), - _networkAccessManager(NULL), _pendingCallbackMap(), _accountInfo() { @@ -153,9 +154,7 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager:: const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) { - if (!_networkAccessManager) { - _networkAccessManager = new QNetworkAccessManager(this); - } + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); if (hasValidAccessToken()) { QNetworkRequest authenticatedRequest; @@ -184,26 +183,29 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager:: switch (operation) { case QNetworkAccessManager::GetOperation: - networkReply = _networkAccessManager->get(authenticatedRequest); + networkReply = networkAccessManager.get(authenticatedRequest); break; case QNetworkAccessManager::PostOperation: case QNetworkAccessManager::PutOperation: if (dataMultiPart) { if (operation == QNetworkAccessManager::PostOperation) { - networkReply = _networkAccessManager->post(authenticatedRequest, dataMultiPart); + networkReply = networkAccessManager.post(authenticatedRequest, dataMultiPart); } else { - networkReply = _networkAccessManager->put(authenticatedRequest, dataMultiPart); + networkReply = networkAccessManager.put(authenticatedRequest, dataMultiPart); } dataMultiPart->setParent(networkReply); } else { authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); if (operation == QNetworkAccessManager::PostOperation) { - networkReply = _networkAccessManager->post(authenticatedRequest, dataByteArray); + networkReply = networkAccessManager.post(authenticatedRequest, dataByteArray); } else { - networkReply = _networkAccessManager->put(authenticatedRequest, dataByteArray); + networkReply = networkAccessManager.put(authenticatedRequest, dataByteArray); } } + break; + case QNetworkAccessManager::DeleteOperation: + networkReply = networkAccessManager.sendCustomRequest(authenticatedRequest, "DELETE"); break; default: // other methods not yet handled @@ -304,9 +306,7 @@ bool AccountManager::checkAndSignalForAccessToken() { void AccountManager::requestAccessToken(const QString& login, const QString& password) { - if (!_networkAccessManager) { - _networkAccessManager = new QNetworkAccessManager(this); - } + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request; @@ -324,7 +324,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas request.setUrl(grantURL); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - QNetworkReply* requestReply = _networkAccessManager->post(request, postData); + QNetworkReply* requestReply = networkAccessManager.post(request, postData); connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); } @@ -376,15 +376,13 @@ void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) } void AccountManager::requestProfile() { - if (!_networkAccessManager) { - _networkAccessManager = new QNetworkAccessManager(this); - } + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QUrl profileURL = _authURL; profileURL.setPath("/api/v1/users/profile"); profileURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); - QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL)); + QNetworkReply* profileReply = networkAccessManager.get(QNetworkRequest(profileURL)); connect(profileReply, &QNetworkReply::finished, this, &AccountManager::requestProfileFinished); connect(profileReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError))); } diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index c18836ca54..49a39c1a22 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -15,14 +15,17 @@ #include #include #include -#include #include +#include "NetworkAccessManager.h" + #include "DataServerAccountInfo.h" class JSONCallbackParameters { public: - JSONCallbackParameters(); + JSONCallbackParameters(QObject* jsonCallbackReceiver = NULL, const QString& jsonCallbackMethod = QString(), + QObject* errorCallbackReceiver = NULL, const QString& errorCallbackMethod = QString(), + QObject* updateReceiver = NULL, const QString& updateSlot = QString()); bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; } @@ -58,8 +61,6 @@ public: const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; } - void destroy() { delete _networkAccessManager; } - public slots: void requestAccessTokenFinished(); void requestProfileFinished(); @@ -93,7 +94,6 @@ private: QHttpMultiPart* dataMultiPart); QUrl _authURL; - QNetworkAccessManager* _networkAccessManager; QMap _pendingCallbackMap; DataServerAccountInfo _accountInfo; diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp new file mode 100644 index 0000000000..b9eda27947 --- /dev/null +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -0,0 +1,149 @@ +// +// NetworkAccessManager.cpp +// +// +// Created by Clement on 7/1/14. +// Copyright 2014 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 +// + +#include +#include + +#include "NetworkAccessManager.h" + +NetworkAccessManager& NetworkAccessManager::getInstance() { + static NetworkAccessManager sharedInstance; + return sharedInstance; +} + +NetworkAccessManager::NetworkAccessManager() { +} + +QNetworkReply* NetworkAccessManager::get(const QNetworkRequest& request) { + if (QThread::currentThread() != thread()) { + QNetworkReply* result; + QMetaObject::invokeMethod(this, + "get", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QNetworkReply*, result), + Q_ARG(const QNetworkRequest, request)); + return result; + } + return QNetworkAccessManager::get(request); +} + +QNetworkReply* NetworkAccessManager::head(const QNetworkRequest& request) { + if (QThread::currentThread() != thread()) { + QNetworkReply* result; + QMetaObject::invokeMethod(this, + "head", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QNetworkReply*, result), + Q_ARG(const QNetworkRequest, request)); + return result; + } + return QNetworkAccessManager::head(request); +} + +QNetworkReply* NetworkAccessManager::post(const QNetworkRequest& request, QIODevice* data) { + if (QThread::currentThread() != thread()) { + QNetworkReply* result; + QMetaObject::invokeMethod(this, + "post", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QNetworkReply*, result), + Q_ARG(const QNetworkRequest, request), + Q_ARG(QIODevice*, data)); + return result; + } + return QNetworkAccessManager::post(request, data); +} + +QNetworkReply* NetworkAccessManager::post(const QNetworkRequest& request, const QByteArray& data) { + if (QThread::currentThread() != thread()) { + QNetworkReply* result; + QMetaObject::invokeMethod(this, + "post", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QNetworkReply*, result), + Q_ARG(const QNetworkRequest, request), + Q_ARG(const QByteArray, data)); + return result; + } + return QNetworkAccessManager::post(request, data); +} + +QNetworkReply* NetworkAccessManager::post(const QNetworkRequest& request, QHttpMultiPart* multiPart) { + if (QThread::currentThread() != thread()) { + QNetworkReply* result; + QMetaObject::invokeMethod(this, + "post", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QNetworkReply*, result), + Q_ARG(const QNetworkRequest, request), + Q_ARG(QHttpMultiPart*, multiPart)); + return result; + } + return QNetworkAccessManager::post(request, multiPart); +} + +QNetworkReply* NetworkAccessManager::put(const QNetworkRequest& request, QIODevice* data) { + if (QThread::currentThread() != thread()) { + QNetworkReply* result; + QMetaObject::invokeMethod(this, + "put", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QNetworkReply*, result), + Q_ARG(const QNetworkRequest, request), + Q_ARG(QIODevice*, data)); + return result; + } + return QNetworkAccessManager::put(request, data); +} + +QNetworkReply* NetworkAccessManager::put(const QNetworkRequest& request, QHttpMultiPart* multiPart) { + if (QThread::currentThread() != thread()) { + QNetworkReply* result; + QMetaObject::invokeMethod(this, + "put", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QNetworkReply*, result), + Q_ARG(const QNetworkRequest, request), + Q_ARG(QHttpMultiPart*, multiPart)); + return result; + } + return QNetworkAccessManager::put(request, multiPart); +} + +QNetworkReply* NetworkAccessManager::put(const QNetworkRequest & request, const QByteArray & data) { + if (QThread::currentThread() != thread()) { + QNetworkReply* result; + QMetaObject::invokeMethod(this, + "put", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QNetworkReply*, result), + Q_ARG(const QNetworkRequest, request), + Q_ARG(const QByteArray, data)); + return result; + } + return QNetworkAccessManager::put(request, data); +} + + +QNetworkReply* NetworkAccessManager::sendCustomRequest(const QNetworkRequest& request, const QByteArray& verb, QIODevice* data) { + if (QThread::currentThread() != thread()) { + QNetworkReply* result; + QMetaObject::invokeMethod(this, + "sendCustomRequest", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QNetworkReply*, result), + Q_ARG(const QNetworkRequest, request), + Q_ARG(const QByteArray, verb), + Q_ARG(QIODevice*, data)); + return result; + } + return QNetworkAccessManager::sendCustomRequest(request, verb, data); +} \ No newline at end of file diff --git a/libraries/networking/src/NetworkAccessManager.h b/libraries/networking/src/NetworkAccessManager.h new file mode 100644 index 0000000000..ba97f12552 --- /dev/null +++ b/libraries/networking/src/NetworkAccessManager.h @@ -0,0 +1,41 @@ +// +// NetworkAccessManager.h +// +// +// Created by Clement on 7/1/14. +// Copyright 2014 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 +// + +#ifndef hifi_NetworkAccessManager_h +#define hifi_NetworkAccessManager_h + +#include +#include +#include + +/// Wrapper around QNetworkAccessManager wo that we only use one instance +/// For any other method you should need, make sure to be on the right thread +/// or call the method using QMetaObject::invokeMethod() +class NetworkAccessManager : public QNetworkAccessManager { + Q_OBJECT +public: + static NetworkAccessManager& getInstance(); + + Q_INVOKABLE QNetworkReply* get(const QNetworkRequest& request); + Q_INVOKABLE QNetworkReply* head(const QNetworkRequest& request); + Q_INVOKABLE QNetworkReply* post(const QNetworkRequest& request, QIODevice* data); + Q_INVOKABLE QNetworkReply* post(const QNetworkRequest& request, const QByteArray& data); + Q_INVOKABLE QNetworkReply* post(const QNetworkRequest& request, QHttpMultiPart* multiPart); + Q_INVOKABLE QNetworkReply* put(const QNetworkRequest& request, QIODevice* data); + Q_INVOKABLE QNetworkReply* put(const QNetworkRequest& request, QHttpMultiPart* multiPart); + Q_INVOKABLE QNetworkReply* put(const QNetworkRequest& request, const QByteArray& data); + Q_INVOKABLE QNetworkReply* sendCustomRequest(const QNetworkRequest& request, const QByteArray& verb, QIODevice* data = 0); + +private: + NetworkAccessManager(); +}; + +#endif // hifi_NetworkAccessManager_h \ No newline at end of file diff --git a/libraries/shared/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp similarity index 97% rename from libraries/shared/src/ResourceCache.cpp rename to libraries/networking/src/ResourceCache.cpp index 14998232d6..a183e2f9a1 100644 --- a/libraries/shared/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -16,6 +16,8 @@ #include #include +#include "NetworkAccessManager.h" + #include "ResourceCache.h" ResourceCache::ResourceCache(QObject* parent) : @@ -103,8 +105,6 @@ void ResourceCache::requestCompleted(Resource* resource) { } } -QNetworkAccessManager* ResourceCache::_networkAccessManager = NULL; - const int DEFAULT_REQUEST_LIMIT = 10; int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT; @@ -219,7 +219,7 @@ void Resource::init() { if (_url.isEmpty()) { _startedLoading = _loaded = true; - } else if (!(_url.isValid() && ResourceCache::getNetworkAccessManager())) { + } else if (!(_url.isValid())) { _startedLoading = _failedToLoad = true; } } @@ -272,7 +272,7 @@ void Resource::handleReplyTimeout() { } void Resource::makeRequest() { - _reply = ResourceCache::getNetworkAccessManager()->get(_request); + _reply = NetworkAccessManager::getInstance().get(_request); connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64))); connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError())); diff --git a/libraries/shared/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h similarity index 94% rename from libraries/shared/src/ResourceCache.h rename to libraries/networking/src/ResourceCache.h index 2404485c46..1593ad45fc 100644 --- a/libraries/shared/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -22,7 +22,6 @@ #include #include -class QNetworkAccessManager; class QNetworkReply; class QTimer; @@ -33,10 +32,6 @@ class ResourceCache : public QObject { Q_OBJECT public: - - static void setNetworkAccessManager(QNetworkAccessManager* manager) { _networkAccessManager = manager; } - static QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; } - static void setRequestLimit(int limit) { _requestLimit = limit; } static int getRequestLimit() { return _requestLimit; } @@ -76,7 +71,6 @@ private: QHash > _resources; int _lastLRUKey; - static QNetworkAccessManager* _networkAccessManager; static int _requestLimit; static QList > _pendingRequests; static QList _loadingRequests; diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index aa18cb43ee..5b20c82263 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -83,17 +83,10 @@ void UserActivityLogger::close(int delayTime) { // In order to get the end of the session, we need to give the account manager enough time to send the packet. QEventLoop loop; - // Here we connect the callbacks to stop the event loop - JSONCallbackParameters params; - params.jsonCallbackReceiver = &loop; - params.errorCallbackReceiver = &loop; - params.jsonCallbackMethod = "quit"; - params.errorCallbackMethod = "quit"; - // In case something goes wrong, we also setup a timer so that the delai is not greater than delayTime QTimer timer; connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); // Now we can log it - logAction(ACTION_NAME, QJsonObject(), params); + logAction(ACTION_NAME, QJsonObject()); timer.start(delayTime); loop.exec(); } diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index d8d5887d97..0291690c3d 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -183,7 +183,6 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA) // MIN_VALID_SPEED is obtained by computing speed gained at one gravity after the shortest expected frame const float MIN_EXPECTED_FRAME_PERIOD = 0.0167f; // 1/60th of a second -const float HALTING_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE); void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { // particles that are in hand, don't collide with avatars diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 04e970d68d..63ae58b924 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -23,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -142,8 +142,8 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, emit errorMessage("ERROR Loading file:" + fileName); } } else { - QNetworkAccessManager* networkManager = new QNetworkAccessManager(this); - QNetworkReply* reply = networkManager->get(QNetworkRequest(url)); + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); qDebug() << "Downloading included script at" << url; QEventLoop loop; QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); @@ -648,8 +648,8 @@ void ScriptEngine::include(const QString& includeFile) { QString includeContents; if (url.scheme() == "http" || url.scheme() == "ftp") { - QNetworkAccessManager* networkManager = new QNetworkAccessManager(this); - QNetworkReply* reply = networkManager->get(QNetworkRequest(url)); + NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); qDebug() << "Downloading included script at" << includeFile; QEventLoop loop; QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index a81f8950fa..d9b7312bf4 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -14,6 +14,8 @@ #include +#include + #include "XMLHttpRequestClass.h" XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) : @@ -22,7 +24,6 @@ XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) : _url(), _method(""), _responseType(""), - _manager(this), _request(), _reply(NULL), _sendData(NULL), @@ -161,7 +162,7 @@ void XMLHttpRequestClass::send(const QString& data) { } void XMLHttpRequestClass::doSend() { - _reply = _manager.sendCustomRequest(_request, _method.toLatin1(), _sendData); + _reply = NetworkAccessManager::getInstance().sendCustomRequest(_request, _method.toLatin1(), _sendData); connectToReply(_reply); diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index 49a952e638..48f1a596e1 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -13,7 +13,6 @@ #define hifi_XMLHttpRequestClass_h #include -#include #include #include #include @@ -104,7 +103,6 @@ private: QUrl _url; QString _method; QString _responseType; - QNetworkAccessManager _manager; QNetworkRequest _request; QNetworkReply* _reply; QBuffer* _sendData; diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 7f9a34492d..560546473c 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -10,7 +10,7 @@ set(MACRO_DIR "${ROOT_DIR}/cmake/macros") set(TARGET_NAME shared) project(${TARGET_NAME}) -find_package(Qt5 COMPONENTS Network Widgets Xml) +find_package(Qt5 COMPONENTS Network Widgets Xml Script) include(${MACRO_DIR}/SetupHifiLibrary.cmake) setup_hifi_library(${TARGET_NAME}) @@ -32,4 +32,8 @@ if (UNIX AND NOT APPLE) target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}") endif (UNIX AND NOT APPLE) -target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets) \ No newline at end of file +# There is something special (bug) about Qt5Scripts, that we have to explicitly add its include +# directory when Qt5 (5.2.1) is compiled from source and is not in a standard place. +include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}") + +target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets) diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 637a5e955c..e2f95a213c 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -24,9 +24,6 @@ int MAX_ENTITIES_PER_SIMULATION = 64; int MAX_COLLISIONS_PER_SIMULATION = 256; -const int NUM_SHAPE_BITS = 6; -const int SHAPE_INDEX_MASK = (1 << (NUM_SHAPE_BITS + 1)) - 1; - PhysicsSimulation::PhysicsSimulation() : _collisionList(MAX_COLLISIONS_PER_SIMULATION), _numIterations(0), _numCollisions(0), _constraintError(0.0f), _stepTime(0) { } diff --git a/tests/networking/src/SequenceNumberStatsTests.cpp b/tests/networking/src/SequenceNumberStatsTests.cpp index 89a14deb20..62651eaa6f 100644 --- a/tests/networking/src/SequenceNumberStatsTests.cpp +++ b/tests/networking/src/SequenceNumberStatsTests.cpp @@ -23,7 +23,7 @@ void SequenceNumberStatsTests::runAllTests() { pruneTest(); } -const int UINT16_RANGE = std::numeric_limits::max() + 1; +const quint32 UINT16_RANGE = std::numeric_limits::max() + 1; void SequenceNumberStatsTests::rolloverTest() { @@ -34,7 +34,7 @@ void SequenceNumberStatsTests::rolloverTest() { quint16 seq = 79; // start on some random number for (int R = 0; R < 2; R++) { - for (int i = 0; i < 3 * UINT16_RANGE; i++) { + for (quint32 i = 0; i < 3 * UINT16_RANGE; i++) { stats.sequenceNumberReceived(seq); seq = seq + (quint16)1; @@ -53,12 +53,12 @@ void SequenceNumberStatsTests::earlyLateTest() { SequenceNumberStats stats; quint16 seq = 65530; - int numSent = 0; + quint32 numSent = 0; - int numEarly = 0; - int numLate = 0; - int numLost = 0; - int numRecovered = 0; + quint32 numEarly = 0; + quint32 numLate = 0; + quint32 numLost = 0; + quint32 numRecovered = 0; for (int R = 0; R < 2; R++) { for (int T = 0; T < 10000; T++) { @@ -122,12 +122,12 @@ void SequenceNumberStatsTests::duplicateTest() { SequenceNumberStats stats; quint16 seq = 12345; - int numSent = 0; + quint32 numSent = 0; - int numDuplicate = 0; - int numEarly = 0; - int numLate = 0; - int numLost = 0; + quint32 numDuplicate = 0; + quint32 numEarly = 0; + quint32 numLate = 0; + quint32 numLost = 0; for (int R = 0; R < 2; R++) { for (int T = 0; T < 10000; T++) { @@ -210,10 +210,10 @@ void SequenceNumberStatsTests::pruneTest() { SequenceNumberStats stats; quint16 seq = 54321; - int numSent = 0; + quint32 numSent = 0; - int numEarly = 0; - int numLost = 0; + quint32 numEarly = 0; + quint32 numLost = 0; for (int R = 0; R < 2; R++) { for (int T = 0; T < 1000; T++) {