Merge branch 'master' of https://github.com/highfidelity/hifi into metavoxels

This commit is contained in:
Andrzej Kapolka 2014-07-03 11:33:22 -07:00
commit 38383effcc
66 changed files with 2313 additions and 262 deletions

View file

@ -10,6 +10,7 @@ add_definitions(-DGLM_FORCE_RADIANS)
if (WIN32) if (WIN32)
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ") set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic") #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas") #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas")

View file

@ -12,6 +12,9 @@ set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
# setup for find modules # setup for find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/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 # set up the external glm library
include("${MACRO_DIR}/IncludeGLM.cmake") include("${MACRO_DIR}/IncludeGLM.cmake")
include_glm(${TARGET_NAME} "${ROOT_DIR}") include_glm(${TARGET_NAME} "${ROOT_DIR}")

View file

@ -13,13 +13,13 @@
#include <QtCore/QEventLoop> #include <QtCore/QEventLoop>
#include <QtCore/QStandardPaths> #include <QtCore/QStandardPaths>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkDiskCache> #include <QtNetwork/QNetworkDiskCache>
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <AudioRingBuffer.h> #include <AudioRingBuffer.h>
#include <AvatarData.h> #include <AvatarData.h>
#include <NetworkAccessManager.h>
#include <NodeList.h> #include <NodeList.h>
#include <PacketHeaders.h> #include <PacketHeaders.h>
#include <ResourceCache.h> #include <ResourceCache.h>
@ -208,12 +208,14 @@ void Agent::run() {
scriptURL = QUrl(_payload); scriptURL = QUrl(_payload);
} }
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this); NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply *reply = networkManager->get(QNetworkRequest(scriptURL)); QNetworkReply *reply = networkAccessManager.get(QNetworkRequest(scriptURL));
QNetworkDiskCache* cache = new QNetworkDiskCache(networkManager); QNetworkDiskCache* cache = new QNetworkDiskCache(&networkAccessManager);
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache"); 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(); qDebug() << "Downloading script at" << scriptURL.toString();
@ -222,10 +224,6 @@ void Agent::run() {
loop.exec(); loop.exec();
// let the AvatarData and ResourceCache classes use our QNetworkAccessManager
AvatarData::setNetworkAccessManager(networkManager);
ResourceCache::setNetworkAccessManager(networkManager);
QString scriptContents(reply->readAll()); QString scriptContents(reply->readAll());
qDebug() << "Downloaded script:" << scriptContents; qDebug() << "Downloaded script:" << scriptContents;

View file

@ -38,11 +38,11 @@
#include <QtCore/QJsonObject> #include <QtCore/QJsonObject>
#include <QtCore/QJsonValue> #include <QtCore/QJsonValue>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <Logging.h> #include <Logging.h>
#include <NetworkAccessManager.h>
#include <NodeList.h> #include <NodeList.h>
#include <Node.h> #include <Node.h>
#include <PacketHeaders.h> #include <PacketHeaders.h>
@ -99,6 +99,7 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf
bool shouldAttenuate = (bufferToAdd != listeningNodeBuffer); bool shouldAttenuate = (bufferToAdd != listeningNodeBuffer);
if (shouldAttenuate) { if (shouldAttenuate) {
// if the two buffer pointers do not match then these are different buffers // if the two buffer pointers do not match then these are different buffers
glm::vec3 relativePosition = bufferToAdd->getPosition() - listeningNodeBuffer->getPosition(); glm::vec3 relativePosition = bufferToAdd->getPosition() - listeningNodeBuffer->getPosition();
@ -482,8 +483,8 @@ void AudioMixer::run() {
nodeList->linkedDataCreateCallback = attachNewBufferToNode; nodeList->linkedDataCreateCallback = attachNewBufferToNode;
// setup a QNetworkAccessManager to ask the domain-server for our settings // setup a NetworkAccessManager to ask the domain-server for our settings
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this); NetworkAccessManager& networkManager = NetworkAccessManager::getInstance();
QUrl settingsJSONURL; QUrl settingsJSONURL;
settingsJSONURL.setScheme("http"); settingsJSONURL.setScheme("http");
@ -500,7 +501,7 @@ void AudioMixer::run() {
qDebug() << "Requesting settings for assignment from domain-server at" << settingsJSONURL.toString(); qDebug() << "Requesting settings for assignment from domain-server at" << settingsJSONURL.toString();
while (!reply || reply->error() != QNetworkReply::NoError) { while (!reply || reply->error() != QNetworkReply::NoError) {
reply = networkManager->get(QNetworkRequest(settingsJSONURL)); reply = networkManager.get(QNetworkRequest(settingsJSONURL));
QEventLoop loop; QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));

View file

@ -10,7 +10,6 @@
// //
#include <QJsonObject> #include <QJsonObject>
#include <QNetworkAccessManager>
#include <QTimer> #include <QTimer>
#include <QUuid> #include <QUuid>

View file

@ -51,8 +51,6 @@ DomainServer::DomainServer(int argc, char* argv[]) :
_argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); _argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments());
_networkAccessManager = new QNetworkAccessManager(this);
if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) { if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) {
// we either read a certificate and private key or were not passed one // we either read a certificate and private key or were not passed one
// and completed login or did not need to // and completed login or did not need to
@ -1196,7 +1194,7 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
QNetworkRequest tokenRequest(tokenRequestUrl); QNetworkRequest tokenRequest(tokenRequestUrl);
tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); 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); 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.setPath("/api/v1/users/profile");
profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken)); 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); qDebug() << "Requesting access token for user with session UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID);

View file

@ -103,8 +103,6 @@ private:
bool _isUsingDTLS; bool _isUsingDTLS;
QNetworkAccessManager* _networkAccessManager;
QUrl _oauthProviderURL; QUrl _oauthProviderURL;
QString _oauthClientID; QString _oauthClientID;
QString _oauthClientSecret; QString _oauthClientSecret;

View file

@ -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). 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. 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.
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. 2. Clear your build directory, run cmake and build, and you should be all set.

View file

@ -11,18 +11,114 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
// the texture containing our permutations and normals // implementation based on Ken Perlin's Improved Noise reference implementation (orig. in Java) at
uniform sampler2D permutationNormalTexture; // http://mrl.nyu.edu/~perlin/noise/
uniform sampler2D permutationTexture;
// the noise frequency // 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 // the noise amplitude
const float amplitude = 0.1; const float amplitude = 0.5;
// the position in model space // the position in model space
varying vec3 position; 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 // returns the gradient at a single corner of our sampling cube
vec3 grad(vec3 location) { vec3 grad(vec3 location) {
float p1 = texture2D(permutationNormalTexture, vec2(location.x / 256.0, 0.25)).r; 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), mix(mix(ffcv, cfcv, params.x), mix(fccv, cccv, params.x), params.y),
params.z); params.z);
} }
*/
void main(void) {
gl_FragColor = vec4(gl_Color.rgb * (1.0 + amplitude*(perlin(position * frequency) - 1.0)), 1.0);
}

View file

@ -32,7 +32,6 @@
#include <QKeyEvent> #include <QKeyEvent>
#include <QMenuBar> #include <QMenuBar>
#include <QMouseEvent> #include <QMouseEvent>
#include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkDiskCache> #include <QNetworkDiskCache>
#include <QOpenGLFramebufferObject> #include <QOpenGLFramebufferObject>
@ -53,17 +52,18 @@
#include <AccountManager.h> #include <AccountManager.h>
#include <AudioInjector.h> #include <AudioInjector.h>
#include <LocalVoxelsList.h>
#include <Logging.h> #include <Logging.h>
#include <ModelsScriptingInterface.h> #include <ModelsScriptingInterface.h>
#include <NetworkAccessManager.h>
#include <OctalCode.h> #include <OctalCode.h>
#include <OctreeSceneStats.h>
#include <PacketHeaders.h> #include <PacketHeaders.h>
#include <ParticlesScriptingInterface.h> #include <ParticlesScriptingInterface.h>
#include <PerfStat.h> #include <PerfStat.h>
#include <ResourceCache.h> #include <ResourceCache.h>
#include <UserActivityLogger.h> #include <UserActivityLogger.h>
#include <UUID.h> #include <UUID.h>
#include <OctreeSceneStats.h>
#include <LocalVoxelsList.h>
#include "Application.h" #include "Application.h"
#include "InterfaceVersion.h" #include "InterfaceVersion.h"
@ -314,12 +314,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
_networkAccessManager = new QNetworkAccessManager(this); NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkDiskCache* cache = new QNetworkDiskCache(_networkAccessManager); QNetworkDiskCache* cache = new QNetworkDiskCache(&networkAccessManager);
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); 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); ResourceCache::setRequestLimit(3);
_window->setCentralWidget(_glWidget); _window->setCentralWidget(_glWidget);
@ -398,18 +399,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
} }
Application::~Application() { Application::~Application() {
qInstallMessageHandler(NULL);
saveSettings();
storeSizeAndPosition();
saveScripts();
int DELAY_TIME = 1000; int DELAY_TIME = 1000;
UserActivityLogger::getInstance().close(DELAY_TIME); UserActivityLogger::getInstance().close(DELAY_TIME);
qInstallMessageHandler(NULL);
// make sure we don't call the idle timer any more // make sure we don't call the idle timer any more
delete idleTimer; delete idleTimer;
_sharedVoxelSystem.changeTree(new VoxelTree); _sharedVoxelSystem.changeTree(new VoxelTree);
saveSettings();
delete _voxelImporter; delete _voxelImporter;
// let the avatar mixer know we're out // let the avatar mixer know we're out
@ -432,8 +434,6 @@ Application::~Application() {
_particleEditSender.terminate(); _particleEditSender.terminate();
_modelEditSender.terminate(); _modelEditSender.terminate();
storeSizeAndPosition();
saveScripts();
VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
Menu::getInstance()->deleteLater(); Menu::getInstance()->deleteLater();
@ -441,8 +441,6 @@ Application::~Application() {
_myAvatar = NULL; _myAvatar = NULL;
delete _glWidget; delete _glWidget;
AccountManager::getInstance().destroy();
} }
void Application::saveSettings() { void Application::saveSettings() {
@ -590,13 +588,17 @@ void Application::paintGL() {
//Note, the camera distance is set in Camera::setMode() so we dont have to do it here. //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.setTightness(0.0f); // Camera is directly connected to head without smoothing
_myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition()); _myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());
if (OculusManager::isConnected()) {
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation()); _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation());
} else {
_myCamera.setTargetRotation(_myAvatar->getHead()->getOrientation());
}
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_myCamera.setTightness(0.0f); _myCamera.setTightness(0.0f);
_myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _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 // Update camera position
@ -634,6 +636,10 @@ void Application::paintGL() {
//If we aren't using the glow shader, we have to clear the color and depth buffer //If we aren't using the glow shader, we have to clear the color and depth buffer
if (!glowEnabled) { if (!glowEnabled) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 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()) { if (OculusManager::isConnected()) {
@ -645,13 +651,8 @@ void Application::paintGL() {
} }
} else if (TV3DManager::isConnected()) { } else if (TV3DManager::isConnected()) {
if (glowEnabled) {
_glowEffect.prepare();
}
TV3DManager::display(whichCamera); TV3DManager::display(whichCamera);
if (glowEnabled) {
_glowEffect.render();
}
} else { } else {
if (glowEnabled) { if (glowEnabled) {
@ -1139,7 +1140,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
_lastMouseMove = usecTimestampNow(); _lastMouseMove = usecTimestampNow();
if (_mouseHidden && showMouse && !OculusManager::isConnected()) { if (_mouseHidden && showMouse && !OculusManager::isConnected() && !TV3DManager::isConnected()) {
getGLWidget()->setCursor(Qt::ArrowCursor); getGLWidget()->setCursor(Qt::ArrowCursor);
_mouseHidden = false; _mouseHidden = false;
_seenMouseMove = true; _seenMouseMove = true;
@ -3802,7 +3803,8 @@ void Application::initAvatarAndViewFrustum() {
void Application::checkVersion() { void Application::checkVersion() {
QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL))); QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL)));
latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); 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() { void Application::parseVersionXml() {

View file

@ -97,7 +97,6 @@ class QActionGroup;
class QGLWidget; class QGLWidget;
class QKeyEvent; class QKeyEvent;
class QMouseEvent; class QMouseEvent;
class QNetworkAccessManager;
class QSettings; class QSettings;
class QWheelEvent; class QWheelEvent;
@ -236,7 +235,6 @@ public:
void lockOctreeSceneStats() { _octreeSceneStatsLock.lockForRead(); } void lockOctreeSceneStats() { _octreeSceneStatsLock.lockForRead(); }
void unlockOctreeSceneStats() { _octreeSceneStatsLock.unlock(); } void unlockOctreeSceneStats() { _octreeSceneStatsLock.unlock(); }
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
GeometryCache* getGeometryCache() { return &_geometryCache; } GeometryCache* getGeometryCache() { return &_geometryCache; }
AnimationCache* getAnimationCache() { return &_animationCache; } AnimationCache* getAnimationCache() { return &_animationCache; }
TextureCache* getTextureCache() { return &_textureCache; } TextureCache* getTextureCache() { return &_textureCache; }
@ -423,7 +421,6 @@ private:
QThread* _nodeThread; QThread* _nodeThread;
DatagramProcessor _datagramProcessor; DatagramProcessor _datagramProcessor;
QNetworkAccessManager* _networkAccessManager;
QMutex _settingsMutex; QMutex _settingsMutex;
QSettings* _settings; QSettings* _settings;
int _numChangedSettings; int _numChangedSettings;

View file

@ -1379,8 +1379,10 @@ bool Audio::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) {
// cleanup any previously initialized device // cleanup any previously initialized device
if (_audioInput) { 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(); _audioInput->stop();
disconnect(_inputDevice);
_inputDevice = NULL; _inputDevice = NULL;
delete _audioInput; delete _audioInput;

View file

@ -91,6 +91,7 @@ Menu::Menu() :
_jsConsole(NULL), _jsConsole(NULL),
_octreeStatsDialog(NULL), _octreeStatsDialog(NULL),
_lodToolsDialog(NULL), _lodToolsDialog(NULL),
_userLocationsDialog(NULL),
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM), _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE), _voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
_oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE), _oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE),
@ -166,6 +167,11 @@ Menu::Menu() :
Qt::CTRL | Qt::Key_N, Qt::CTRL | Qt::Key_N,
this, this,
SLOT(nameLocation())); SLOT(nameLocation()));
addActionToQMenuAndActionHash(fileMenu,
MenuOption::MyLocations,
Qt::CTRL | Qt::Key_K,
this,
SLOT(toggleLocationList()));
addActionToQMenuAndActionHash(fileMenu, addActionToQMenuAndActionHash(fileMenu,
MenuOption::GoTo, MenuOption::GoTo,
Qt::Key_At, Qt::Key_At,
@ -1184,6 +1190,17 @@ void Menu::namedLocationCreated(LocationManager::NamedLocationCreateResponse res
msgBox.exec(); msgBox.exec();
} }
void Menu::toggleLocationList() {
if (!_userLocationsDialog) {
_userLocationsDialog = new UserLocationsDialog(Application::getInstance()->getWindow());
}
if (_userLocationsDialog->isVisible()) {
_userLocationsDialog->hide();
} else {
_userLocationsDialog->show();
}
}
void Menu::nameLocation() { void Menu::nameLocation() {
// check if user is logged in or show login dialog if not // check if user is logged in or show login dialog if not

View file

@ -29,6 +29,7 @@
#include "ui/JSConsole.h" #include "ui/JSConsole.h"
#include "ui/LoginDialog.h" #include "ui/LoginDialog.h"
#include "ui/ScriptEditorWindow.h" #include "ui/ScriptEditorWindow.h"
#include "ui/UserLocationsDialog.h"
const float ADJUST_LOD_DOWN_FPS = 40.0; const float ADJUST_LOD_DOWN_FPS = 40.0;
const float ADJUST_LOD_UP_FPS = 55.0; const float ADJUST_LOD_UP_FPS = 55.0;
@ -199,6 +200,7 @@ private slots:
void goToDomainDialog(); void goToDomainDialog();
void goToLocation(); void goToLocation();
void nameLocation(); void nameLocation();
void toggleLocationList();
void bandwidthDetailsClosed(); void bandwidthDetailsClosed();
void octreeStatsDetailsClosed(); void octreeStatsDetailsClosed();
void lodToolsClosed(); void lodToolsClosed();
@ -265,6 +267,7 @@ private:
QDialog* _jsConsole; QDialog* _jsConsole;
OctreeStatsDialog* _octreeStatsDialog; OctreeStatsDialog* _octreeStatsDialog;
LodToolsDialog* _lodToolsDialog; LodToolsDialog* _lodToolsDialog;
UserLocationsDialog* _userLocationsDialog;
int _maxVoxels; int _maxVoxels;
float _voxelSizeScale; float _voxelSizeScale;
float _oculusUIAngularSize; float _oculusUIAngularSize;
@ -395,6 +398,7 @@ namespace MenuOption {
const QString MoveWithLean = "Move with Lean"; const QString MoveWithLean = "Move with Lean";
const QString MuteAudio = "Mute Microphone"; const QString MuteAudio = "Mute Microphone";
const QString MuteEnvironment = "Mute Environment"; const QString MuteEnvironment = "Mute Environment";
const QString MyLocations = "My Locations...";
const QString NameLocation = "Name this location"; const QString NameLocation = "Name this location";
const QString NewVoxelCullingMode = "New Voxel Culling Mode"; const QString NewVoxelCullingMode = "New Voxel Culling Mode";
const QString OctreeStats = "Voxel and Particle Statistics"; const QString OctreeStats = "Voxel and Particle Statistics";

View file

@ -396,7 +396,7 @@ void ModelUploader::uploadFailed(QNetworkReply::NetworkError errorCode, const QS
void ModelUploader::checkS3() { void ModelUploader::checkS3() {
qDebug() << "Checking S3 for " << _url; qDebug() << "Checking S3 for " << _url;
QNetworkRequest request(_url); QNetworkRequest request(_url);
QNetworkReply* reply = _networkAccessManager.head(request); QNetworkReply* reply = NetworkAccessManager::getInstance().head(request);
connect(reply, SIGNAL(finished()), SLOT(processCheck())); connect(reply, SIGNAL(finished()), SLOT(processCheck()));
} }

View file

@ -58,7 +58,6 @@ private:
bool _readyToSend; bool _readyToSend;
QHttpMultiPart* _dataMultiPart; QHttpMultiPart* _dataMultiPart;
QNetworkAccessManager _networkAccessManager;
int _numberOfChecks; int _numberOfChecks;
QTimer _timer; QTimer _timer;

View file

@ -11,14 +11,15 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <QNetworkAccessManager>
#include <QUrl> #include <QUrl>
#include <QUrlQuery> #include <QUrlQuery>
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include "ScriptsModel.h" #include <NetworkAccessManager.h>
#include "Menu.h" #include "Menu.h"
#include "ScriptsModel.h"
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com"; static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
static const QString PUBLIC_URL = "http://public.highfidelity.io"; 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 CONTAINER_NAME = "Contents";
static const QString KEY_NAME = "Key"; static const QString KEY_NAME = "Key";
static const int SCRIPT_PATH = Qt::UserRole;
ScriptItem::ScriptItem(const QString& filename, const QString& fullPath) : ScriptItem::ScriptItem(const QString& filename, const QString& fullPath) :
_filename(filename), _filename(filename),
_fullPath(fullPath) { _fullPath(fullPath) {
@ -113,14 +112,15 @@ void ScriptsModel::requestRemoteFiles(QString marker) {
} }
url.setQuery(query); url.setQuery(query);
QNetworkAccessManager* accessManager = new QNetworkAccessManager(this); NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*)));
QNetworkRequest request(url); 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<QNetworkReply*>(sender());
bool finished = true; bool finished = true;
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {

View file

@ -43,7 +43,7 @@ public:
protected slots: protected slots:
void updateScriptsLocation(const QString& newPath); void updateScriptsLocation(const QString& newPath);
void downloadFinished(QNetworkReply* reply); void downloadFinished();
void reloadLocalFiles(); void reloadLocalFiles();
void reloadRemoteFiles(); void reloadRemoteFiles();

View file

@ -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 <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QMessageBox>
#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<UserLocation*>::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);
}

View file

@ -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 <QAbstractListModel>
#include <QModelIndex>
#include <QVariant>
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<UserLocation*> _locations;
};
#endif // hifi_UserLocationsModel_h

View file

@ -51,10 +51,10 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
glm::mat3 axes = glm::mat3_cast(glm::quat()); glm::mat3 axes = glm::mat3_cast(glm::quat());
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) * glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) *
joint.preTransform * glm::mat4_cast(joint.preRotation))); 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->getFinalYaw(), glm::normalize(inverse * axes[1]))
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalPitch(), glm::normalize(inverse * axes[0])) * 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) { 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)); _owningHead->getSaccade() - _translation, 1.0f));
glm::quat between = rotationBetween(front, lookAt); glm::quat between = rotationBetween(front, lookAt);
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; 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)) * state.setRotationInParentFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
joint.rotation; joint.rotation);
} }
void FaceModel::updateJointState(int index) { void FaceModel::updateJointState(int index) {

View file

@ -219,10 +219,9 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
JointState& parentState = _jointStates[parentJointIndex]; JointState& parentState = _jointStates[parentJointIndex];
parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY); parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY);
// lock hand to forearm by slamming its rotation (in parent-frame) to identity // lock hand to forearm by slamming its rotation (in parent-frame) to identity
_jointStates[jointIndex]._rotationInParentFrame = glm::quat(); _jointStates[jointIndex].setRotationInParentFrame(glm::quat());
} else { } else {
setJointPosition(jointIndex, palmPosition, palmRotation, inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
} }
} }
@ -258,9 +257,9 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
glm::mat3 axes = glm::mat3_cast(glm::quat()); glm::mat3 axes = glm::mat3_cast(glm::quat());
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) * glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation))); 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[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) { 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 // virtual
void SkeletonModel::stepRagdollForward(float deltaTime) { void SkeletonModel::stepRagdollForward(float deltaTime) {
const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.03f; const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.03f;
@ -644,7 +648,6 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
_ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position;
continue; continue;
} }
assert(parentIndex != -1);
glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation; glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
@ -700,7 +703,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
_boundingRadius = 0.5f * glm::length(diagonal); _boundingRadius = 0.5f * glm::length(diagonal);
} }
void SkeletonModel::resetShapePositions() { void SkeletonModel::resetShapePositionsToDefaultPose() {
// DEBUG method. // DEBUG method.
// Moves shapes to the joint default locations for debug visibility into // Moves shapes to the joint default locations for debug visibility into
// how the bounding shape is computed. // how the bounding shape is computed.

View file

@ -94,6 +94,8 @@ public:
/// \return whether or not both eye meshes were found /// \return whether or not both eye meshes were found
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
virtual void updateVisibleJointStates();
// virtual overrride from Ragdoll // virtual overrride from Ragdoll
virtual void stepRagdollForward(float deltaTime); virtual void stepRagdollForward(float deltaTime);
@ -104,7 +106,7 @@ public:
float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } float getBoundingShapeRadius() const { return _boundingShape.getRadius(); }
const CapsuleShape& getBoundingShape() const { return _boundingShape; } const CapsuleShape& getBoundingShape() const { return _boundingShape; }
void resetShapePositions(); // DEBUG method void resetShapePositionsToDefaultPose(); // DEBUG method
void renderRagdoll(); void renderRagdoll();
protected: protected:

View file

@ -266,7 +266,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); 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() // PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
applicationOverlay.renderOverlay(true); applicationOverlay.renderOverlay(true);
const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::DisplayOculusOverlays); const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::DisplayOculusOverlays);

View file

@ -93,6 +93,18 @@ void TV3DManager::display(Camera& whichCamera) {
int portalW = Application::getInstance()->getGLWidget()->width() / 2; int portalW = Application::getInstance()->getGLWidget()->width() / 2;
int portalH = Application::getInstance()->getGLWidget()->height(); 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); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
@ -102,13 +114,21 @@ void TV3DManager::display(Camera& whichCamera) {
glPushMatrix(); glPushMatrix();
{ {
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); // reset projection matrix glLoadIdentity(); // reset projection matrix
glFrustum(_leftEye.left, _leftEye.right, _leftEye.bottom, _leftEye.top, nearZ, farZ); // set left view frustum 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 glTranslatef(_leftEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glLoadIdentity();
Application::getInstance()->displaySide(whichCamera); Application::getInstance()->displaySide(whichCamera);
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
} }
glPopMatrix(); glPopMatrix();
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
@ -124,14 +144,25 @@ void TV3DManager::display(Camera& whichCamera) {
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); // reset projection matrix glLoadIdentity(); // reset projection matrix
glFrustum(_rightEye.left, _rightEye.right, _rightEye.bottom, _rightEye.top, nearZ, farZ); // set left view frustum 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 glTranslatef(_rightEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glLoadIdentity();
Application::getInstance()->displaySide(whichCamera); Application::getInstance()->displaySide(whichCamera);
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
} }
glPopMatrix(); glPopMatrix();
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
// reset the viewport to how we started // reset the viewport to how we started
glViewport(0, 0, Application::getInstance()->getGLWidget()->width(), Application::getInstance()->getGLWidget()->height()); glViewport(0, 0, Application::getInstance()->getGLWidget()->width(), Application::getInstance()->getGLWidget()->height());
if (glowEnabled) {
Application::getInstance()->getGlowEffect()->render();
}
} }

View file

@ -11,6 +11,7 @@
#include <glm/gtx/norm.hpp> #include <glm/gtx/norm.hpp>
#include <AngularConstraint.h>
//#include <GeometryUtil.h> //#include <GeometryUtil.h>
#include <SharedUtil.h> #include <SharedUtil.h>
@ -18,7 +19,26 @@
JointState::JointState() : JointState::JointState() :
_animationPriority(0.0f), _animationPriority(0.0f),
_fbxJoint(NULL) { _fbxJoint(NULL),
_constraint(NULL) {
}
JointState::JointState(const JointState& other) : _constraint(NULL) {
_transform = other._transform;
_rotation = other._rotation;
_rotationInParentFrame = other._rotationInParentFrame;
_animationPriority = other._animationPriority;
_fbxJoint = other._fbxJoint;
// DO NOT copy _constraint
}
JointState::~JointState() {
delete _constraint;
_constraint = NULL;
if (_constraint) {
delete _constraint;
_constraint = NULL;
}
} }
void JointState::setFBXJoint(const FBXJoint* joint) { void JointState::setFBXJoint(const FBXJoint* joint) {
@ -26,14 +46,34 @@ void JointState::setFBXJoint(const FBXJoint* joint) {
_rotationInParentFrame = joint->rotation; _rotationInParentFrame = joint->rotation;
// NOTE: JointState does not own the FBXJoint to which it points. // NOTE: JointState does not own the FBXJoint to which it points.
_fbxJoint = joint; _fbxJoint = joint;
if (_constraint) {
delete _constraint;
_constraint = NULL;
}
}
void JointState::updateConstraint() {
if (_constraint) {
delete _constraint;
_constraint = NULL;
}
if (glm::distance2(glm::vec3(-PI), _fbxJoint->rotationMin) > EPSILON ||
glm::distance2(glm::vec3(PI), _fbxJoint->rotationMax) > EPSILON ) {
// this joint has rotation constraints
_constraint = AngularConstraint::newAngularConstraint(_fbxJoint->rotationMin, _fbxJoint->rotationMax);
}
} }
void JointState::copyState(const JointState& state) { void JointState::copyState(const JointState& state) {
_rotationInParentFrame = state._rotationInParentFrame; _animationPriority = state._animationPriority;
_transform = state._transform; _transform = state._transform;
_rotation = extractRotation(_transform); _rotation = extractRotation(_transform);
_animationPriority = state._animationPriority; _rotationInParentFrame = state._rotationInParentFrame;
// DO NOT copy _fbxJoint
_visibleTransform = state._visibleTransform;
_visibleRotation = extractRotation(_visibleTransform);
_visibleRotationInParentFrame = state._visibleRotationInParentFrame;
// DO NOT copy _fbxJoint or _constraint
} }
void JointState::computeTransform(const glm::mat4& parentTransform) { void JointState::computeTransform(const glm::mat4& parentTransform) {
@ -43,6 +83,13 @@ void JointState::computeTransform(const glm::mat4& parentTransform) {
_rotation = extractRotation(_transform); _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 { glm::quat JointState::getRotationFromBindToModelFrame() const {
return _rotation * _fbxJoint->inverseBindRotation; return _rotation * _fbxJoint->inverseBindRotation;
} }
@ -50,16 +97,20 @@ glm::quat JointState::getRotationFromBindToModelFrame() const {
void JointState::restoreRotation(float fraction, float priority) { void JointState::restoreRotation(float fraction, float priority) {
assert(_fbxJoint != NULL); assert(_fbxJoint != NULL);
if (priority == _animationPriority || _animationPriority == 0.0f) { if (priority == _animationPriority || _animationPriority == 0.0f) {
_rotationInParentFrame = safeMix(_rotationInParentFrame, _fbxJoint->rotation, fraction); setRotationInParentFrame(safeMix(_rotationInParentFrame, _fbxJoint->rotation, fraction));
_animationPriority = 0.0f; _animationPriority = 0.0f;
} }
} }
void JointState::setRotationFromBindFrame(const glm::quat& rotation, float priority) { void JointState::setRotationFromBindFrame(const glm::quat& rotation, float priority, bool constrain) {
// rotation is from bind- to model-frame
assert(_fbxJoint != NULL); assert(_fbxJoint != NULL);
if (priority >= _animationPriority) { if (priority >= _animationPriority) {
// rotation is from bind- to model-frame glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation);
_rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation); if (constrain && _constraint) {
_constraint->softClamp(targetRotation, _rotationInParentFrame, 0.5f);
}
setRotationInParentFrame(targetRotation);
_animationPriority = priority; _animationPriority = priority;
} }
} }
@ -68,6 +119,9 @@ void JointState::clearTransformTranslation() {
_transform[3][0] = 0.0f; _transform[3][0] = 0.0f;
_transform[3][1] = 0.0f; _transform[3][1] = 0.0f;
_transform[3][2] = 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) { void JointState::setRotation(const glm::quat& rotation, bool constrain, float priority) {
@ -75,27 +129,61 @@ void JointState::setRotation(const glm::quat& rotation, bool constrain, float pr
} }
void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, float priority) { 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); assert(_fbxJoint != NULL);
if (priority < _animationPriority) { if (priority < _animationPriority) {
return; return;
} }
_animationPriority = priority; _animationPriority = priority;
if (!constrain || (_fbxJoint->rotationMin == glm::vec3(-PI, -PI, -PI) && if (!constrain || _constraint == NULL) {
_fbxJoint->rotationMax == glm::vec3(PI, PI, PI))) {
// no constraints // no constraints
_rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation; _rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation;
_rotation = delta * _rotation; _rotation = delta * _rotation;
return; return;
} }
glm::quat targetRotation = delta * _rotation; glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation;
glm::vec3 eulers = safeEulerAngles(_rotationInParentFrame * glm::inverse(_rotation) * targetRotation); setRotationInParentFrame(targetRotation);
glm::quat newRotation = glm::quat(glm::clamp(eulers, _fbxJoint->rotationMin, _fbxJoint->rotationMax)); }
_rotation = _rotation * glm::inverse(_rotationInParentFrame) * newRotation;
_rotationInParentFrame = newRotation; /// 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);
}
if (_constraint) {
_constraint->softClamp(targetRotation, _rotationInParentFrame, 0.5f);
}
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 { const glm::vec3& JointState::getDefaultTranslationInParentFrame() const {
assert(_fbxJoint != NULL); assert(_fbxJoint != NULL);
return _fbxJoint->translation; return _fbxJoint->translation;
} }
void JointState::slaveVisibleTransform() {
_visibleTransform = _transform;
_visibleRotation = _rotation;
_visibleRotationInParentFrame = _rotationInParentFrame;
}

View file

@ -18,16 +18,27 @@
#include <FBXReader.h> #include <FBXReader.h>
class AngularConstraint;
class JointState { class JointState {
public: public:
JointState(); JointState();
JointState(const JointState& other);
~JointState();
void setFBXJoint(const FBXJoint* joint); void setFBXJoint(const FBXJoint* joint);
const FBXJoint& getFBXJoint() const { return *_fbxJoint; } const FBXJoint& getFBXJoint() const { return *_fbxJoint; }
void updateConstraint();
void copyState(const JointState& state); void copyState(const JointState& state);
void computeTransform(const glm::mat4& parentTransform); 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; } const glm::mat4& getTransform() const { return _transform; }
glm::quat getRotation() const { return _rotation; } glm::quat getRotation() const { return _rotation; }
@ -39,28 +50,56 @@ public:
/// \param rotation rotation of joint in model-frame /// \param rotation rotation of joint in model-frame
void setRotation(const glm::quat& rotation, bool constrain, float priority); 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); 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); void restoreRotation(float fraction, float priority);
/// \param rotation is from bind- to model-frame /// \param rotation is from bind- to model-frame
/// computes and sets new _rotationInParentFrame /// computes and sets new _rotationInParentFrame
/// NOTE: the JointState's model-frame transform/rotation are NOT updated! /// NOTE: the JointState's model-frame transform/rotation are NOT updated!
void setRotationFromBindFrame(const glm::quat& rotation, float priority); void setRotationFromBindFrame(const glm::quat& rotation, float priority, bool constrain = false);
void setRotationInParentFrame(const glm::quat& targetRotation);
const glm::quat& getRotationInParentFrame() const { return _rotationInParentFrame; }
const glm::vec3& getDefaultTranslationInParentFrame() const;
void clearTransformTranslation(); void clearTransformTranslation();
glm::quat _rotationInParentFrame; // joint- to parentJoint-frame void slaveVisibleTransform();
float _animationPriority; // the priority of the animation affecting this joint float _animationPriority; // the priority of the animation affecting this joint
private: 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::mat4 _transform; // joint- to model-frame
glm::quat _rotation; // 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 const FBXJoint* _fbxJoint; // JointState does NOT own its FBXJoint
AngularConstraint* _constraint; // JointState owns its AngularConstraint
}; };
#endif // hifi_JointState_h #endif // hifi_JointState_h

View file

@ -39,8 +39,8 @@ Model::Model(QObject* parent) :
_scaledToFit(false), _scaledToFit(false),
_snapModelToCenter(false), _snapModelToCenter(false),
_snappedToCenter(false), _snappedToCenter(false),
_showTrueJointTransforms(false),
_rootIndex(-1), _rootIndex(-1),
//_enableCollisionShapes(false),
_lodDistance(0.0f), _lodDistance(0.0f),
_pupilDilation(0.0f), _pupilDilation(0.0f),
_url("http://invalid.com") { _url("http://invalid.com") {
@ -460,7 +460,7 @@ void Model::reset() {
} }
const FBXGeometry& geometry = _geometry->getFBXGeometry(); const FBXGeometry& geometry = _geometry->getFBXGeometry();
for (int i = 0; i < _jointStates.size(); i++) { for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i]._rotationInParentFrame = geometry.joints.at(i).rotation; _jointStates[i].setRotationInParentFrame(geometry.joints.at(i).rotation);
} }
} }
@ -561,8 +561,6 @@ bool Model::updateGeometry() {
void Model::setJointStates(QVector<JointState> states) { void Model::setJointStates(QVector<JointState> states) {
_jointStates = states; _jointStates = states;
// compute an approximate bounding radius for broadphase collision queries
// against PhysicsSimulation boundaries
int numJoints = _jointStates.size(); int numJoints = _jointStates.size();
float radius = 0.0f; float radius = 0.0f;
for (int i = 0; i < numJoints; ++i) { for (int i = 0; i < numJoints; ++i) {
@ -570,6 +568,10 @@ void Model::setJointStates(QVector<JointState> states) {
if (distance > radius) { if (distance > radius) {
radius = distance; radius = distance;
} }
_jointStates[i].updateConstraint();
}
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].slaveVisibleTransform();
} }
_boundingRadius = radius; _boundingRadius = radius;
} }
@ -686,7 +688,7 @@ bool Model::getJointState(int index, glm::quat& rotation) const {
if (index == -1 || index >= _jointStates.size()) { if (index == -1 || index >= _jointStates.size()) {
return false; return false;
} }
rotation = _jointStates.at(index)._rotationInParentFrame; rotation = _jointStates.at(index).getRotationInParentFrame();
const glm::quat& defaultRotation = _geometry->getFBXGeometry().joints.at(index).rotation; const glm::quat& defaultRotation = _geometry->getFBXGeometry().joints.at(index).rotation;
return glm::abs(rotation.x - defaultRotation.x) >= EPSILON || return glm::abs(rotation.x - defaultRotation.x) >= EPSILON ||
glm::abs(rotation.y - defaultRotation.y) >= EPSILON || glm::abs(rotation.y - defaultRotation.y) >= EPSILON ||
@ -699,7 +701,7 @@ void Model::setJointState(int index, bool valid, const glm::quat& rotation, floa
JointState& state = _jointStates[index]; JointState& state = _jointStates[index];
if (priority >= state._animationPriority) { if (priority >= state._animationPriority) {
if (valid) { if (valid) {
state._rotationInParentFrame = rotation; state.setRotationInParentFrame(rotation);
state._animationPriority = priority; state._animationPriority = priority;
} else { } else {
state.restoreRotation(1.0f, priority); state.restoreRotation(1.0f, priority);
@ -765,6 +767,23 @@ bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const
return true; 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 { QStringList Model::getJointNames() const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QStringList result; QStringList result;
@ -918,6 +937,8 @@ void Model::simulateInternal(float deltaTime) {
for (int i = 0; i < _jointStates.size(); i++) { for (int i = 0; i < _jointStates.size(); i++) {
updateJointState(i); updateJointState(i);
} }
updateVisibleJointStates();
_shapesAreDirty = ! _shapes.isEmpty(); _shapesAreDirty = ! _shapes.isEmpty();
// update the attachment transforms and simulate them // update the attachment transforms and simulate them
@ -928,8 +949,13 @@ void Model::simulateInternal(float deltaTime) {
glm::vec3 jointTranslation = _translation; glm::vec3 jointTranslation = _translation;
glm::quat jointRotation = _rotation; glm::quat jointRotation = _rotation;
if (_showTrueJointTransforms) {
getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation); getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
getJointRotationInWorldFrame(attachment.jointIndex, jointRotation); getJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
} else {
getVisibleJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
getVisibleJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
}
model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale); model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale);
model->setRotation(jointRotation * attachment.rotation); model->setRotation(jointRotation * attachment.rotation);
@ -944,10 +970,17 @@ void Model::simulateInternal(float deltaTime) {
for (int i = 0; i < _meshStates.size(); i++) { for (int i = 0; i < _meshStates.size(); i++) {
MeshState& state = _meshStates[i]; MeshState& state = _meshStates[i];
const FBXMesh& mesh = geometry.meshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i);
if (_showTrueJointTransforms) {
for (int j = 0; j < mesh.clusters.size(); j++) { for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j); const FBXCluster& cluster = mesh.clusters.at(j);
state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; 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;
}
}
} }
// post the blender // post the blender
@ -972,6 +1005,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, 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) { int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) { if (jointIndex == -1 || _jointStates.isEmpty()) {
@ -1058,6 +1099,121 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl
return true; 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<int>& 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);
// We want to mix the shortest rotation with one that will pull the system down with gravity
// so that limbs don't float unrealistically. To do this we compute a simplified center of mass
// where each joint has unit mass 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 for most models.
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, true);
_shapesAreDirty = !_shapes.isEmpty();
}
bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) { if (jointIndex == -1 || _jointStates.isEmpty()) {
return false; return false;
@ -1605,7 +1761,7 @@ void AnimationHandle::applyFrame(float frameIndex) {
if (mapping != -1) { if (mapping != -1) {
JointState& state = _model->_jointStates[mapping]; JointState& state = _model->_jointStates[mapping];
if (_priority >= state._animationPriority) { 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; state._animationPriority = _priority;
} }
} }

View file

@ -120,6 +120,9 @@ public:
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
bool getJointCombinedRotation(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 jointIndex index of joint in model structure
/// \param position[out] position of joint in model-frame /// \param position[out] position of joint in model-frame
/// \return true if joint exists /// \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 _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 _snappedToCenter; /// are we currently snapped to center
bool _showTrueJointTransforms;
int _rootIndex; int _rootIndex;
QVector<JointState> _jointStates; QVector<JointState> _jointStates;
@ -177,6 +181,8 @@ protected:
/// Updates the state of the joint at the specified index. /// Updates the state of the joint at the specified index.
virtual void updateJointState(int index); virtual void updateJointState(int index);
virtual void updateVisibleJointStates();
/// \param jointIndex index of joint in model structure /// \param jointIndex index of joint in model structure
/// \param position position of joint in model-frame /// \param position position of joint in model-frame
/// \param rotation rotation of joint in model-frame /// \param rotation rotation of joint in model-frame
@ -189,6 +195,8 @@ protected:
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, 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); 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. /// 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 /// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to
/// the original position /// the original position

View file

@ -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() { GLuint TextureCache::getPermutationNormalTextureID() {
if (_permutationNormalTextureID == 0) { if (_permutationNormalTextureID == 0) {
glGenTextures(1, &_permutationNormalTextureID); glGenTextures(1, &_permutationNormalTextureID);
@ -92,10 +119,17 @@ GLuint TextureCache::getPermutationNormalTextureID() {
// the first line consists of random permutation offsets // the first line consists of random permutation offsets
unsigned char data[256 * 2 * 3]; 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++) { for (int i = 0; i < 256 * 3; i++) {
data[i] = rand() % 256; data[i] = rand() % 256;
#endif
} }
// the next, random unit normals
for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) { for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) {
glm::vec3 randvec = glm::sphericalRand(1.0f); glm::vec3 randvec = glm::sphericalRand(1.0f);
data[i] = ((randvec.x + 1.0f) / 2.0f) * 255.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); 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_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }
return _permutationNormalTextureID; return _permutationNormalTextureID;

View file

@ -205,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) { void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
if (_alpha == 0.0f) { if (_alpha == 0.0f) {
@ -292,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 //Renders optional pointers
void ApplicationOverlay::renderPointers() { void ApplicationOverlay::renderPointers() {
Application* application = Application::getInstance(); Application* application = Application::getInstance();

View file

@ -29,6 +29,7 @@ public:
void renderOverlay(bool renderToTexture = false); void renderOverlay(bool renderToTexture = false);
void displayOverlayTexture(); void displayOverlayTexture();
void displayOverlayTextureOculus(Camera& whichCamera); void displayOverlayTextureOculus(Camera& whichCamera);
void displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov);
void computeOculusPickRay(float x, float y, glm::vec3& direction) const; void computeOculusPickRay(float x, float y, glm::vec3& direction) const;
void getClickLocation(int &x, int &y) const; void getClickLocation(int &x, int &y) const;

View file

@ -14,10 +14,11 @@
#include <QGridLayout> #include <QGridLayout>
#include <QHeaderView> #include <QHeaderView>
#include <QMessageBox> #include <QMessageBox>
#include <QNetworkAccessManager>
#include <QUrl> #include <QUrl>
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <NetworkAccessManager.h>
#include "Application.h" #include "Application.h"
#include "ModelsBrowser.h" #include "ModelsBrowser.h"
@ -210,10 +211,10 @@ void ModelHandler::update() {
} }
for (int i = 0; i < _model.rowCount(); ++i) { for (int i = 0; i < _model.rowCount(); ++i) {
QUrl url(_model.item(i,0)->data(Qt::UserRole).toString()); QUrl url(_model.item(i,0)->data(Qt::UserRole).toString());
QNetworkAccessManager* accessManager = new QNetworkAccessManager(this); NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request(url); QNetworkRequest request(url);
accessManager->head(request); QNetworkReply* reply = networkAccessManager.head(request);
connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*))); connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
} }
_lock.unlock(); _lock.unlock();
} }
@ -233,7 +234,8 @@ void ModelHandler::exit() {
_lock.unlock(); _lock.unlock();
} }
void ModelHandler::downloadFinished(QNetworkReply* reply) { void ModelHandler::downloadFinished() {
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
if (!data.isEmpty()) { if (!data.isEmpty()) {
@ -261,10 +263,10 @@ void ModelHandler::queryNewFiles(QString marker) {
// Download // Download
url.setQuery(query); url.setQuery(query);
QNetworkAccessManager* accessManager = new QNetworkAccessManager(this); NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request(url); QNetworkRequest request(url);
accessManager->get(request); QNetworkReply* reply = networkAccessManager.get(request);
connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*))); connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
} }

View file

@ -43,7 +43,7 @@ public slots:
void exit(); void exit();
private slots: private slots:
void downloadFinished(QNetworkReply* reply); void downloadFinished();
private: private:
bool _initiateExit; bool _initiateExit;

View file

@ -148,8 +148,8 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) {
disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
} }
} else { } else {
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this); NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkManager->get(QNetworkRequest(url)); QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
qDebug() << "Downloading included script at" << scriptPath; qDebug() << "Downloading included script at" << scriptPath;
QEventLoop loop; QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);

View file

@ -40,8 +40,7 @@ Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) :
QDialog(parent), QDialog(parent),
_fileName(fileName), _fileName(fileName)
_networkAccessManager(NULL)
{ {
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
@ -92,10 +91,6 @@ void SnapshotShareDialog::uploadSnapshot() {
return; return;
} }
if (!_networkAccessManager) {
_networkAccessManager = new QNetworkAccessManager(this);
}
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart apiKeyPart; QHttpPart apiKeyPart;
@ -118,9 +113,7 @@ void SnapshotShareDialog::uploadSnapshot() {
QUrl url(FORUM_UPLOADS_URL); QUrl url(FORUM_UPLOADS_URL);
QNetworkRequest request(url); QNetworkRequest request(url);
QNetworkReply* reply = _networkAccessManager->post(request, multiPart); QNetworkReply* reply = NetworkAccessManager::getInstance().post(request, multiPart);
connect(reply, &QNetworkReply::finished, this, &SnapshotShareDialog::uploadRequestFinished); connect(reply, &QNetworkReply::finished, this, &SnapshotShareDialog::uploadRequestFinished);
QEventLoop loop; QEventLoop loop;
@ -129,11 +122,6 @@ void SnapshotShareDialog::uploadSnapshot() {
} }
void SnapshotShareDialog::sendForumPost(QString snapshotPath) { void SnapshotShareDialog::sendForumPost(QString snapshotPath) {
if (!_networkAccessManager) {
_networkAccessManager = new QNetworkAccessManager(this);
}
// post to Discourse forum // post to Discourse forum
QNetworkRequest request; QNetworkRequest request;
QUrl forumUrl(FORUM_POST_URL); QUrl forumUrl(FORUM_POST_URL);
@ -148,7 +136,7 @@ void SnapshotShareDialog::sendForumPost(QString snapshotPath) {
request.setUrl(forumUrl); request.setUrl(forumUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); 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); connect(requestReply, &QNetworkReply::finished, this, &SnapshotShareDialog::postRequestFinished);
QEventLoop loop; QEventLoop loop;

View file

@ -14,7 +14,6 @@
#include "ui_shareSnapshot.h" #include "ui_shareSnapshot.h"
#include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <QUrl> #include <QUrl>
@ -26,7 +25,6 @@ public:
private: private:
QString _fileName; QString _fileName;
QNetworkAccessManager* _networkAccessManager;
Ui_SnapshotShareDialog _ui; Ui_SnapshotShareDialog _ui;
void uploadSnapshot(); void uploadSnapshot();

View file

@ -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 <QDebug>
#include <QInputDialog>
#include <QPushButton>
#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);
}
}
}

View file

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

View file

@ -14,8 +14,7 @@
#include "BillboardOverlay.h" #include "BillboardOverlay.h"
BillboardOverlay::BillboardOverlay() BillboardOverlay::BillboardOverlay()
: _manager(NULL), : _scale(1.0f),
_scale(1.0f),
_isFacingAvatar(true) { _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) { void BillboardOverlay::setBillboardURL(const QUrl url) {
// TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made? QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(url));
_manager->deleteLater(); connect(reply, &QNetworkReply::finished, this, &BillboardOverlay::replyFinished);
_manager = new QNetworkAccessManager();
connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
_manager->get(QNetworkRequest(url));
} }
void BillboardOverlay::replyFinished(QNetworkReply* reply) { void BillboardOverlay::replyFinished() {
// replace our byte array with the downloaded data // replace our byte array with the downloaded data
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
_billboard = reply->readAll(); _billboard = reply->readAll();
_manager->deleteLater();
_manager = NULL;
} }

View file

@ -27,12 +27,11 @@ public:
virtual void setProperties(const QScriptValue& properties); virtual void setProperties(const QScriptValue& properties);
private slots: private slots:
void replyFinished(QNetworkReply* reply); void replyFinished();
private: private:
void setBillboardURL(const QUrl url); void setBillboardURL(const QUrl url);
QNetworkAccessManager* _manager;
QUrl _url; QUrl _url;
QByteArray _billboard; QByteArray _billboard;
QSize _size; QSize _size;

View file

@ -19,7 +19,6 @@
#include "ImageOverlay.h" #include "ImageOverlay.h"
ImageOverlay::ImageOverlay() : ImageOverlay::ImageOverlay() :
_manager(NULL),
_textureID(0), _textureID(0),
_renderImage(false), _renderImage(false),
_textureBound(false), _textureBound(false),
@ -36,21 +35,18 @@ ImageOverlay::~ImageOverlay() {
// TODO: handle setting image multiple times, how do we manage releasing the bound texture? // TODO: handle setting image multiple times, how do we manage releasing the bound texture?
void ImageOverlay::setImageURL(const QUrl& url) { void ImageOverlay::setImageURL(const QUrl& url) {
// TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made? NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
_manager->deleteLater(); QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
_manager = new QNetworkAccessManager(); connect(reply, &QNetworkReply::finished, this, &ImageOverlay::replyFinished);
connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
_manager->get(QNetworkRequest(url));
} }
void ImageOverlay::replyFinished(QNetworkReply* reply) { void ImageOverlay::replyFinished() {
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
// replace our byte array with the downloaded data // replace our byte array with the downloaded data
QByteArray rawData = reply->readAll(); QByteArray rawData = reply->readAll();
_textureImage.loadFromData(rawData); _textureImage.loadFromData(rawData);
_renderImage = true; _renderImage = true;
_manager->deleteLater();
_manager = NULL;
} }
void ImageOverlay::render() { void ImageOverlay::render() {

View file

@ -16,13 +16,13 @@
#include <QGLWidget> #include <QGLWidget>
#include <QImage> #include <QImage>
#include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <QRect> #include <QRect>
#include <QScriptValue> #include <QScriptValue>
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include <NetworkAccessManager.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include "Overlay.h" #include "Overlay.h"
@ -46,13 +46,12 @@ public:
virtual void setProperties(const QScriptValue& properties); virtual void setProperties(const QScriptValue& properties);
private slots: private slots:
void replyFinished(QNetworkReply* reply); // we actually want to hide this... void replyFinished(); // we actually want to hide this...
private: private:
QUrl _imageURL; QUrl _imageURL;
QImage _textureImage; QImage _textureImage;
QNetworkAccessManager* _manager;
GLuint _textureID; GLuint _textureID;
QRect _fromImage; // where from in the image to sample QRect _fromImage; // where from in the image to sample

View file

@ -16,7 +16,6 @@
#include <QGLWidget> #include <QGLWidget>
#include <QImage> #include <QImage>
#include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <QRect> #include <QRect>
#include <QScriptValue> #include <QScriptValue>

View file

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UserLocationsDialog</class>
<widget class="QWidget" name="UserLocationsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>929</width>
<height>633</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>-1</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
<property name="topMargin">
<number>12</number>
</property>
<property name="rightMargin">
<number>12</number>
</property>
<property name="bottomMargin">
<number>12</number>
</property>
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="styleSheet">
<string notr="true">font-size: 16px</string>
</property>
<property name="text">
<string>My Locations</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="refreshButton">
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTreeView" name="locationsTreeView">
<property name="indentation">
<number>0</number>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>-1</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
<property name="topMargin">
<number>12</number>
</property>
<property name="rightMargin">
<number>12</number>
</property>
<property name="bottomMargin">
<number>12</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="renameButton">
<property name="text">
<string>Rename</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteButton">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -12,6 +12,9 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm
set(TARGET_NAME audio) set(TARGET_NAME audio)
find_package(Qt5 COMPONENTS Script)
include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}")
# set up the external glm library # set up the external glm library
include(${MACRO_DIR}/SetupHifiLibrary.cmake) include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME}) setup_hifi_library(${TARGET_NAME})

View file

@ -15,12 +15,12 @@
#include <QDataStream> #include <QDataStream>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <qendian.h> #include <qendian.h>
#include <LimitedNodeList.h> #include <LimitedNodeList.h>
#include <NetworkAccessManager.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include "AudioRingBuffer.h" #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 // assume we have a QApplication or QCoreApplication instance and use the
// QNetworkAccess manager to grab the raw audio file at the given URL // 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(); 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, &QNetworkReply::finished, this, &Sound::replyFinished);
connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError))); connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError)));
} }

View file

@ -16,10 +16,10 @@
#include <QtCore/QDataStream> #include <QtCore/QDataStream>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtCore/QUuid> #include <QtCore/QUuid>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <NetworkAccessManager.h>
#include <NodeList.h> #include <NodeList.h>
#include <PacketHeaders.h> #include <PacketHeaders.h>
#include <SharedUtil.h> #include <SharedUtil.h>
@ -33,8 +33,6 @@ quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND;
using namespace std; using namespace std;
QNetworkAccessManager* AvatarData::networkAccessManager = NULL;
AvatarData::AvatarData() : AvatarData::AvatarData() :
_sessionUUID(), _sessionUUID(),
_handPosition(0,0,0), _handPosition(0,0,0),
@ -751,18 +749,15 @@ void AvatarData::setBillboard(const QByteArray& billboard) {
void AvatarData::setBillboardFromURL(const QString &billboardURL) { void AvatarData::setBillboardFromURL(const QString &billboardURL) {
_billboardURL = billboardURL; _billboardURL = billboardURL;
if (AvatarData::networkAccessManager) {
qDebug() << "Changing billboard for avatar to PNG at" << qPrintable(billboardURL); qDebug() << "Changing billboard for avatar to PNG at" << qPrintable(billboardURL);
QNetworkRequest billboardRequest; QNetworkRequest billboardRequest;
billboardRequest.setUrl(QUrl(billboardURL)); billboardRequest.setUrl(QUrl(billboardURL));
QNetworkReply* networkReply = AvatarData::networkAccessManager->get(billboardRequest); NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* networkReply = networkAccessManager.get(billboardRequest);
connect(networkReply, SIGNAL(finished()), this, SLOT(setBillboardFromNetworkReply())); connect(networkReply, SIGNAL(finished()), this, SLOT(setBillboardFromNetworkReply()));
} else {
qDebug() << "Billboard PNG download requested but no network access manager is available.";
}
} }
void AvatarData::setBillboardFromNetworkReply() { void AvatarData::setBillboardFromNetworkReply() {
@ -839,8 +834,9 @@ void AvatarData::updateJointMappings() {
_jointIndices.clear(); _jointIndices.clear();
_jointNames.clear(); _jointNames.clear();
if (networkAccessManager && _skeletonModelURL.fileName().toLower().endsWith(".fst")) { if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
QNetworkReply* networkReply = networkAccessManager->get(QNetworkRequest(_skeletonModelURL)); NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* networkReply = networkAccessManager.get(QNetworkRequest(_skeletonModelURL));
connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply())); connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply()));
} }
} }

View file

@ -102,7 +102,6 @@ enum KeyState {
const glm::vec3 vec3Zero(0.0f); const glm::vec3 vec3Zero(0.0f);
class QDataStream; class QDataStream;
class QNetworkAccessManager;
class AttachmentData; class AttachmentData;
class JointData; class JointData;
@ -270,8 +269,6 @@ public:
virtual float getBoundingRadius() const { return 1.f; } virtual float getBoundingRadius() const { return 1.f; }
static void setNetworkAccessManager(QNetworkAccessManager* sharedAccessManager) { networkAccessManager = sharedAccessManager; }
public slots: public slots:
void sendIdentityPacket(); void sendIdentityPacket();
void sendBillboardPacket(); void sendBillboardPacket();
@ -324,8 +321,6 @@ protected:
QHash<QString, int> _jointIndices; ///< 1-based, since zero is returned for missing keys QHash<QString, int> _jointIndices; ///< 1-based, since zero is returned for missing keys
QStringList _jointNames; ///< in order of depth-first traversal QStringList _jointNames; ///< in order of depth-first traversal
static QNetworkAccessManager* networkAccessManager;
quint64 _errorLogExpiry; ///< time in future when to log an error quint64 _errorLogExpiry; ///< time in future when to log an error
QWeakPointer<Node> _owningAvatarMixer; QWeakPointer<Node> _owningAvatarMixer;

View file

@ -37,19 +37,20 @@ Q_DECLARE_METATYPE(JSONCallbackParameters)
const QString ACCOUNTS_GROUP = "accounts"; const QString ACCOUNTS_GROUP = "accounts";
JSONCallbackParameters::JSONCallbackParameters() : JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, const QString& jsonCallbackMethod,
jsonCallbackReceiver(NULL), QObject* errorCallbackReceiver, const QString& errorCallbackMethod,
jsonCallbackMethod(), QObject* updateReceiver, const QString& updateSlot) :
errorCallbackReceiver(NULL), jsonCallbackReceiver(jsonCallbackReceiver),
errorCallbackMethod(), jsonCallbackMethod(jsonCallbackMethod),
updateReciever(NULL), errorCallbackReceiver(errorCallbackReceiver),
updateSlot() errorCallbackMethod(errorCallbackMethod),
updateReciever(updateReceiver),
updateSlot(updateSlot)
{ {
} }
AccountManager::AccountManager() : AccountManager::AccountManager() :
_authURL(), _authURL(),
_networkAccessManager(NULL),
_pendingCallbackMap(), _pendingCallbackMap(),
_accountInfo() _accountInfo()
{ {
@ -153,9 +154,7 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
const JSONCallbackParameters& callbackParams, const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) { const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) {
if (!_networkAccessManager) { NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
_networkAccessManager = new QNetworkAccessManager(this);
}
if (hasValidAccessToken()) { if (hasValidAccessToken()) {
QNetworkRequest authenticatedRequest; QNetworkRequest authenticatedRequest;
@ -184,26 +183,29 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
switch (operation) { switch (operation) {
case QNetworkAccessManager::GetOperation: case QNetworkAccessManager::GetOperation:
networkReply = _networkAccessManager->get(authenticatedRequest); networkReply = networkAccessManager.get(authenticatedRequest);
break; break;
case QNetworkAccessManager::PostOperation: case QNetworkAccessManager::PostOperation:
case QNetworkAccessManager::PutOperation: case QNetworkAccessManager::PutOperation:
if (dataMultiPart) { if (dataMultiPart) {
if (operation == QNetworkAccessManager::PostOperation) { if (operation == QNetworkAccessManager::PostOperation) {
networkReply = _networkAccessManager->post(authenticatedRequest, dataMultiPart); networkReply = networkAccessManager.post(authenticatedRequest, dataMultiPart);
} else { } else {
networkReply = _networkAccessManager->put(authenticatedRequest, dataMultiPart); networkReply = networkAccessManager.put(authenticatedRequest, dataMultiPart);
} }
dataMultiPart->setParent(networkReply); dataMultiPart->setParent(networkReply);
} else { } else {
authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
if (operation == QNetworkAccessManager::PostOperation) { if (operation == QNetworkAccessManager::PostOperation) {
networkReply = _networkAccessManager->post(authenticatedRequest, dataByteArray); networkReply = networkAccessManager.post(authenticatedRequest, dataByteArray);
} else { } else {
networkReply = _networkAccessManager->put(authenticatedRequest, dataByteArray); networkReply = networkAccessManager.put(authenticatedRequest, dataByteArray);
} }
} }
break;
case QNetworkAccessManager::DeleteOperation:
networkReply = networkAccessManager.sendCustomRequest(authenticatedRequest, "DELETE");
break; break;
default: default:
// other methods not yet handled // other methods not yet handled
@ -304,9 +306,7 @@ bool AccountManager::checkAndSignalForAccessToken() {
void AccountManager::requestAccessToken(const QString& login, const QString& password) { void AccountManager::requestAccessToken(const QString& login, const QString& password) {
if (!_networkAccessManager) { NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
_networkAccessManager = new QNetworkAccessManager(this);
}
QNetworkRequest request; QNetworkRequest request;
@ -324,7 +324,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
request.setUrl(grantURL); request.setUrl(grantURL);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); 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, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished);
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
} }
@ -376,15 +376,13 @@ void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error)
} }
void AccountManager::requestProfile() { void AccountManager::requestProfile() {
if (!_networkAccessManager) { NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
_networkAccessManager = new QNetworkAccessManager(this);
}
QUrl profileURL = _authURL; QUrl profileURL = _authURL;
profileURL.setPath("/api/v1/users/profile"); profileURL.setPath("/api/v1/users/profile");
profileURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); 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, &QNetworkReply::finished, this, &AccountManager::requestProfileFinished);
connect(profileReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError))); connect(profileReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError)));
} }

View file

@ -15,14 +15,17 @@
#include <QtCore/QByteArray> #include <QtCore/QByteArray>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QUrl> #include <QtCore/QUrl>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include "NetworkAccessManager.h"
#include "DataServerAccountInfo.h" #include "DataServerAccountInfo.h"
class JSONCallbackParameters { class JSONCallbackParameters {
public: 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; } bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; }
@ -58,8 +61,6 @@ public:
const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; } const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; }
void destroy() { delete _networkAccessManager; }
public slots: public slots:
void requestAccessTokenFinished(); void requestAccessTokenFinished();
void requestProfileFinished(); void requestProfileFinished();
@ -93,7 +94,6 @@ private:
QHttpMultiPart* dataMultiPart); QHttpMultiPart* dataMultiPart);
QUrl _authURL; QUrl _authURL;
QNetworkAccessManager* _networkAccessManager;
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap; QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
DataServerAccountInfo _accountInfo; DataServerAccountInfo _accountInfo;

View file

@ -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 <QMetaObject>
#include <QThread>
#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);
}

View file

@ -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 <QNetworkAccessManager>
#include <QNetworkConfiguration>
#include <QNetworkProxy>
/// 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

View file

@ -16,6 +16,8 @@
#include <QTimer> #include <QTimer>
#include <QtDebug> #include <QtDebug>
#include "NetworkAccessManager.h"
#include "ResourceCache.h" #include "ResourceCache.h"
ResourceCache::ResourceCache(QObject* parent) : ResourceCache::ResourceCache(QObject* parent) :
@ -103,8 +105,6 @@ void ResourceCache::requestCompleted(Resource* resource) {
} }
} }
QNetworkAccessManager* ResourceCache::_networkAccessManager = NULL;
const int DEFAULT_REQUEST_LIMIT = 10; const int DEFAULT_REQUEST_LIMIT = 10;
int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT; int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT;
@ -219,7 +219,7 @@ void Resource::init() {
if (_url.isEmpty()) { if (_url.isEmpty()) {
_startedLoading = _loaded = true; _startedLoading = _loaded = true;
} else if (!(_url.isValid() && ResourceCache::getNetworkAccessManager())) { } else if (!(_url.isValid())) {
_startedLoading = _failedToLoad = true; _startedLoading = _failedToLoad = true;
} }
} }
@ -272,7 +272,7 @@ void Resource::handleReplyTimeout() {
} }
void Resource::makeRequest() { 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(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError())); connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));

View file

@ -22,7 +22,6 @@
#include <QUrl> #include <QUrl>
#include <QWeakPointer> #include <QWeakPointer>
class QNetworkAccessManager;
class QNetworkReply; class QNetworkReply;
class QTimer; class QTimer;
@ -33,10 +32,6 @@ class ResourceCache : public QObject {
Q_OBJECT Q_OBJECT
public: public:
static void setNetworkAccessManager(QNetworkAccessManager* manager) { _networkAccessManager = manager; }
static QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
static void setRequestLimit(int limit) { _requestLimit = limit; } static void setRequestLimit(int limit) { _requestLimit = limit; }
static int getRequestLimit() { return _requestLimit; } static int getRequestLimit() { return _requestLimit; }
@ -76,7 +71,6 @@ private:
QHash<QUrl, QWeakPointer<Resource> > _resources; QHash<QUrl, QWeakPointer<Resource> > _resources;
int _lastLRUKey; int _lastLRUKey;
static QNetworkAccessManager* _networkAccessManager;
static int _requestLimit; static int _requestLimit;
static QList<QPointer<Resource> > _pendingRequests; static QList<QPointer<Resource> > _pendingRequests;
static QList<Resource*> _loadingRequests; static QList<Resource*> _loadingRequests;

View file

@ -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. // In order to get the end of the session, we need to give the account manager enough time to send the packet.
QEventLoop loop; 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; QTimer timer;
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
// Now we can log it // Now we can log it
logAction(ACTION_NAME, QJsonObject(), params); logAction(ACTION_NAME, QJsonObject());
timer.start(delayTime); timer.start(delayTime);
loop.exec(); loop.exec();
} }

View file

@ -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 // 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 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) { void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
// particles that are in hand, don't collide with avatars // particles that are in hand, don't collide with avatars

View file

@ -13,7 +13,6 @@
#include <QtCore/QEventLoop> #include <QtCore/QEventLoop>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <QScriptEngine> #include <QScriptEngine>
@ -23,6 +22,7 @@
#include <AvatarData.h> #include <AvatarData.h>
#include <CollisionInfo.h> #include <CollisionInfo.h>
#include <ModelsScriptingInterface.h> #include <ModelsScriptingInterface.h>
#include <NetworkAccessManager.h>
#include <NodeList.h> #include <NodeList.h>
#include <PacketHeaders.h> #include <PacketHeaders.h>
#include <ParticlesScriptingInterface.h> #include <ParticlesScriptingInterface.h>
@ -141,8 +141,8 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL,
emit errorMessage("ERROR Loading file:" + fileName); emit errorMessage("ERROR Loading file:" + fileName);
} }
} else { } else {
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this); NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkManager->get(QNetworkRequest(url)); QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
qDebug() << "Downloading included script at" << url; qDebug() << "Downloading included script at" << url;
QEventLoop loop; QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
@ -469,7 +469,10 @@ void ScriptEngine::run() {
// pack a placeholder value for sequence number for now, will be packed when destination node is known // pack a placeholder value for sequence number for now, will be packed when destination node is known
int numPreSequenceNumberBytes = audioPacket.size(); int numPreSequenceNumberBytes = audioPacket.size();
packetStream << (quint16)0; packetStream << (quint16) 0;
// assume scripted avatar audio is mono and set channel flag to zero
packetStream << (quint8) 0;
// use the orientation and position of this avatar for the source of this audio // use the orientation and position of this avatar for the source of this audio
packetStream.writeRawData(reinterpret_cast<const char*>(&_avatarData->getPosition()), sizeof(glm::vec3)); packetStream.writeRawData(reinterpret_cast<const char*>(&_avatarData->getPosition()), sizeof(glm::vec3));
@ -646,8 +649,8 @@ void ScriptEngine::include(const QString& includeFile) {
QString includeContents; QString includeContents;
if (url.scheme() == "http" || url.scheme() == "ftp") { if (url.scheme() == "http" || url.scheme() == "ftp") {
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this); NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkManager->get(QNetworkRequest(url)); QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
qDebug() << "Downloading included script at" << includeFile; qDebug() << "Downloading included script at" << includeFile;
QEventLoop loop; QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));

View file

@ -14,6 +14,8 @@
#include <QEventLoop> #include <QEventLoop>
#include <NetworkAccessManager.h>
#include "XMLHttpRequestClass.h" #include "XMLHttpRequestClass.h"
XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) : XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) :
@ -22,7 +24,6 @@ XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) :
_url(), _url(),
_method(""), _method(""),
_responseType(""), _responseType(""),
_manager(this),
_request(), _request(),
_reply(NULL), _reply(NULL),
_sendData(NULL), _sendData(NULL),
@ -161,7 +162,7 @@ void XMLHttpRequestClass::send(const QString& data) {
} }
void XMLHttpRequestClass::doSend() { void XMLHttpRequestClass::doSend() {
_reply = _manager.sendCustomRequest(_request, _method.toLatin1(), _sendData); _reply = NetworkAccessManager::getInstance().sendCustomRequest(_request, _method.toLatin1(), _sendData);
connectToReply(_reply); connectToReply(_reply);

View file

@ -13,7 +13,6 @@
#define hifi_XMLHttpRequestClass_h #define hifi_XMLHttpRequestClass_h
#include <QBuffer> #include <QBuffer>
#include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QObject> #include <QObject>
@ -104,7 +103,6 @@ private:
QUrl _url; QUrl _url;
QString _method; QString _method;
QString _responseType; QString _responseType;
QNetworkAccessManager _manager;
QNetworkRequest _request; QNetworkRequest _request;
QNetworkReply* _reply; QNetworkReply* _reply;
QBuffer* _sendData; QBuffer* _sendData;

View file

@ -10,7 +10,7 @@ set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
set(TARGET_NAME shared) set(TARGET_NAME shared)
project(${TARGET_NAME}) project(${TARGET_NAME})
find_package(Qt5 COMPONENTS Network Widgets Xml) find_package(Qt5 COMPONENTS Network Widgets Xml Script)
include(${MACRO_DIR}/SetupHifiLibrary.cmake) include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME}) setup_hifi_library(${TARGET_NAME})
@ -32,4 +32,8 @@ if (UNIX AND NOT APPLE)
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}") target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
endif (UNIX AND NOT APPLE) endif (UNIX AND NOT APPLE)
# 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) target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets)

View file

@ -0,0 +1,201 @@
//
// AngularConstraint.cpp
// interface/src/renderer
//
// Created by Andrew Meadows on 2014.05.30
// 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 <glm/gtx/norm.hpp>
#include "AngularConstraint.h"
#include "SharedUtil.h"
// helper function
/// \param angle radian angle to be clamped within angleMin and angleMax
/// \param angleMin minimum value
/// \param angleMax maximum value
/// \return value between minAngle and maxAngle closest to angle
float clampAngle(float angle, float angleMin, float angleMax) {
float minDistance = angle - angleMin;
float maxDistance = angle - angleMax;
if (maxDistance > 0.0f) {
minDistance = glm::min(minDistance, angleMin + TWO_PI - angle);
angle = (minDistance < maxDistance) ? angleMin : angleMax;
} else if (minDistance < 0.0f) {
maxDistance = glm::max(maxDistance, angleMax - TWO_PI - angle);
angle = (minDistance > maxDistance) ? angleMin : angleMax;
}
return angle;
}
// static
AngularConstraint* AngularConstraint::newAngularConstraint(const glm::vec3& minAngles, const glm::vec3& maxAngles) {
float minDistance2 = glm::distance2(minAngles, glm::vec3(-PI, -PI, -PI));
float maxDistance2 = glm::distance2(maxAngles, glm::vec3(PI, PI, PI));
if (minDistance2 < EPSILON && maxDistance2 < EPSILON) {
// no constraint
return NULL;
}
// count the zero length elements
glm::vec3 rangeAngles = maxAngles - minAngles;
int pivotIndex = -1;
int numZeroes = 0;
for (int i = 0; i < 3; ++i) {
if (rangeAngles[i] < EPSILON) {
++numZeroes;
} else {
pivotIndex = i;
}
}
if (numZeroes == 2) {
// this is a hinge
int forwardIndex = (pivotIndex + 1) % 3;
glm::vec3 forwardAxis(0.0f);
forwardAxis[forwardIndex] = 1.0f;
glm::vec3 rotationAxis(0.0f);
rotationAxis[pivotIndex] = 1.0f;
return new HingeConstraint(forwardAxis, rotationAxis, minAngles[pivotIndex], maxAngles[pivotIndex]);
} else if (numZeroes == 0) {
// approximate the angular limits with a cone roller
// we assume the roll is about z
glm::vec3 middleAngles = 0.5f * (maxAngles + minAngles);
glm::quat yaw = glm::angleAxis(middleAngles[1], glm::vec3(0.0f, 1.0f, 0.0f));
glm::quat pitch = glm::angleAxis(middleAngles[0], glm::vec3(1.0f, 0.0f, 0.0f));
glm::vec3 coneAxis = pitch * yaw * glm::vec3(0.0f, 0.0f, 1.0f);
// the coneAngle is half the average range of the two non-roll rotations
glm::vec3 range = maxAngles - minAngles;
float coneAngle = 0.25f * (range[0] + range[1]);
return new ConeRollerConstraint(coneAngle, coneAxis, minAngles.z, maxAngles.z);
}
return NULL;
}
bool AngularConstraint::softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction) {
glm::quat clampedTarget = targetRotation;
bool clamped = clamp(clampedTarget);
if (clamped) {
// check if oldRotation is also clamped
glm::quat clampedOld = oldRotation;
bool clamped2 = clamp(clampedOld);
if (clamped2) {
// oldRotation is already beyond the constraint
// we clamp again midway between targetRotation and clamped oldPosition
clampedTarget = glm::shortMix(clampedOld, targetRotation, mixFraction);
// and then clamp that
clamp(clampedTarget);
}
// finally we mix targetRotation with the clampedTarget
targetRotation = glm::shortMix(clampedTarget, targetRotation, mixFraction);
}
return clamped;
}
HingeConstraint::HingeConstraint(const glm::vec3& forwardAxis, const glm::vec3& rotationAxis, float minAngle, float maxAngle)
: _minAngle(minAngle), _maxAngle(maxAngle) {
assert(_minAngle < _maxAngle);
// we accept the rotationAxis direction
assert(glm::length(rotationAxis) > EPSILON);
_rotationAxis = glm::normalize(rotationAxis);
// but we compute the final _forwardAxis
glm::vec3 otherAxis = glm::cross(_rotationAxis, forwardAxis);
assert(glm::length(otherAxis) > EPSILON);
_forwardAxis = glm::normalize(glm::cross(otherAxis, _rotationAxis));
}
// virtual
bool HingeConstraint::clamp(glm::quat& rotation) const {
glm::vec3 forward = rotation * _forwardAxis;
forward -= glm::dot(forward, _rotationAxis) * _rotationAxis;
float length = glm::length(forward);
if (length < EPSILON) {
// infinite number of solutions ==> choose the middle of the contrained range
rotation = glm::angleAxis(0.5f * (_minAngle + _maxAngle), _rotationAxis);
return true;
}
forward /= length;
float sign = (glm::dot(glm::cross(_forwardAxis, forward), _rotationAxis) > 0.0f ? 1.0f : -1.0f);
//float angle = sign * acos(glm::dot(forward, _forwardAxis) / length);
float angle = sign * acos(glm::dot(forward, _forwardAxis));
glm::quat newRotation = glm::angleAxis(clampAngle(angle, _minAngle, _maxAngle), _rotationAxis);
if (fabsf(1.0f - glm::dot(newRotation, rotation)) > EPSILON * EPSILON) {
rotation = newRotation;
return true;
}
return false;
}
bool HingeConstraint::softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction) {
// the hinge works best without a soft clamp
return clamp(targetRotation);
}
ConeRollerConstraint::ConeRollerConstraint(float coneAngle, const glm::vec3& coneAxis, float minRoll, float maxRoll)
: _coneAngle(coneAngle), _minRoll(minRoll), _maxRoll(maxRoll) {
assert(_maxRoll >= _minRoll);
float axisLength = glm::length(coneAxis);
assert(axisLength > EPSILON);
_coneAxis = coneAxis / axisLength;
}
// virtual
bool ConeRollerConstraint::clamp(glm::quat& rotation) const {
bool applied = false;
glm::vec3 rotatedAxis = rotation * _coneAxis;
glm::vec3 perpAxis = glm::cross(rotatedAxis, _coneAxis);
float perpAxisLength = glm::length(perpAxis);
if (perpAxisLength > EPSILON) {
perpAxis /= perpAxisLength;
// enforce the cone
float angle = acosf(glm::dot(rotatedAxis, _coneAxis));
if (angle > _coneAngle) {
rotation = glm::angleAxis(angle - _coneAngle, perpAxis) * rotation;
rotatedAxis = rotation * _coneAxis;
applied = true;
}
} else {
// the rotation is 100% roll
// there is no obvious perp axis so we must pick one
perpAxis = rotatedAxis;
// find the first non-zero element:
float iValue = 0.0f;
int i = 0;
for (i = 0; i < 3; ++i) {
if (fabsf(perpAxis[i]) > EPSILON) {
iValue = perpAxis[i];
break;
}
}
assert(i != 3);
// swap or negate the next element
int j = (i + 1) % 3;
float jValue = perpAxis[j];
if (fabsf(jValue - iValue) > EPSILON) {
perpAxis[i] = jValue;
perpAxis[j] = iValue;
} else {
perpAxis[i] = -iValue;
}
perpAxis = glm::cross(perpAxis, rotatedAxis);
perpAxisLength = glm::length(perpAxis);
assert(perpAxisLength > EPSILON);
perpAxis /= perpAxisLength;
}
// measure the roll
// NOTE: perpAxis is perpendicular to both _coneAxis and rotatedConeAxis, so we can
// rotate it again and we'll end up with an something that has only been rolled.
glm::vec3 rolledPerpAxis = rotation * perpAxis;
float sign = glm::dot(rotatedAxis, glm::cross(perpAxis, rolledPerpAxis)) > 0.0f ? 1.0f : -1.0f;
float roll = sign * angleBetween(rolledPerpAxis, perpAxis);
if (roll < _minRoll || roll > _maxRoll) {
float clampedRoll = clampAngle(roll, _minRoll, _maxRoll);
rotation = glm::normalize(glm::angleAxis(clampedRoll - roll, rotatedAxis) * rotation);
applied = true;
}
return applied;
}

View file

@ -0,0 +1,55 @@
//
// AngularConstraint.h
// interface/src/renderer
//
// Created by Andrew Meadows on 2014.05.30
// Copyright 2013 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_AngularConstraint_h
#define hifi_AngularConstraint_h
#include <glm/glm.hpp>
class AngularConstraint {
public:
/// \param minAngles minumum euler angles for the constraint
/// \param maxAngles minumum euler angles for the constraint
/// \return pointer to new AngularConstraint of the right type or NULL if none could be made
static AngularConstraint* newAngularConstraint(const glm::vec3& minAngles, const glm::vec3& maxAngles);
AngularConstraint() {}
virtual ~AngularConstraint() {}
virtual bool clamp(glm::quat& rotation) const = 0;
virtual bool softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction);
protected:
};
class HingeConstraint : public AngularConstraint {
public:
HingeConstraint(const glm::vec3& forwardAxis, const glm::vec3& rotationAxis, float minAngle, float maxAngle);
virtual bool clamp(glm::quat& rotation) const;
virtual bool softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction);
protected:
glm::vec3 _forwardAxis;
glm::vec3 _rotationAxis;
float _minAngle;
float _maxAngle;
};
class ConeRollerConstraint : public AngularConstraint {
public:
ConeRollerConstraint(float coneAngle, const glm::vec3& coneAxis, float minRoll, float maxRoll);
virtual bool clamp(glm::quat& rotation) const;
private:
float _coneAngle;
glm::vec3 _coneAxis;
float _minRoll;
float _maxRoll;
};
#endif // hifi_AngularConstraint_h

View file

@ -0,0 +1,476 @@
//
// AngularConstraintTests.cpp
// tests/physics/src
//
// Created by Andrew Meadows on 2014.05.30
// 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 <iostream>
#include <AngularConstraint.h>
#include <SharedUtil.h>
#include <StreamUtils.h>
#include "AngularConstraintTests.h"
void AngularConstraintTests::testHingeConstraint() {
float minAngle = -PI;
float maxAngle = 0.0f;
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
glm::vec3 minAngles(0.0f, -PI, 0.0f);
glm::vec3 maxAngles(0.0f, 0.0f, 0.0f);
AngularConstraint* c = AngularConstraint::newAngularConstraint(minAngles, maxAngles);
if (!c) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: newAngularConstraint() should make a constraint" << std::endl;
}
{ // test in middle of constraint
float angle = 0.5f * (minAngle + maxAngle);
glm::quat rotation = glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should not clamp()" << std::endl;
}
if (rotation != newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should not change rotation" << std::endl;
}
}
{ // test just inside min edge of constraint
float angle = minAngle + 10.f * EPSILON;
glm::quat rotation = glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should not clamp()" << std::endl;
}
if (rotation != newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should not change rotation" << std::endl;
}
}
{ // test just inside max edge of constraint
float angle = maxAngle - 10.f * EPSILON;
glm::quat rotation = glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should not clamp()" << std::endl;
}
if (rotation != newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should not change rotation" << std::endl;
}
}
{ // test just outside min edge of constraint
float angle = minAngle - 0.001f;
glm::quat rotation = glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should change rotation" << std::endl;
}
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
float qDot = glm::dot(expectedRotation, newRotation);
if (fabsf(qDot - 1.0f) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
{ // test just outside max edge of constraint
float angle = maxAngle + 0.001f;
glm::quat rotation = glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should change rotation" << std::endl;
}
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
float qDot = glm::dot(expectedRotation, newRotation);
if (fabsf(qDot - 1.0f) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
{ // test far outside min edge of constraint (wraps around to max)
float angle = minAngle - 0.75f * (TWO_PI - (maxAngle - minAngle));
glm::quat rotation = glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should change rotation" << std::endl;
}
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
float qDot = glm::dot(expectedRotation, newRotation);
if (fabsf(qDot - 1.0f) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
{ // test far outside max edge of constraint (wraps around to min)
float angle = maxAngle + 0.75f * (TWO_PI - (maxAngle - minAngle));
glm::quat rotation = glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should change rotation" << std::endl;
}
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
float qDot = glm::dot(expectedRotation, newRotation);
if (fabsf(qDot - 1.0f) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
float ACCEPTABLE_ERROR = 1.0e-4f;
{ // test nearby but off-axis rotation
float offAngle = 0.1f;
glm::quat offRotation(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
float angle = 0.5f * (maxAngle + minAngle);
glm::quat rotation = offRotation * glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should change rotation" << std::endl;
}
glm::quat expectedRotation = glm::angleAxis(angle, yAxis);
float qDot = glm::dot(expectedRotation, newRotation);
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
{ // test way off rotation > maxAngle
float offAngle = 0.5f;
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
float angle = maxAngle + 0.2f * (TWO_PI - (maxAngle - minAngle));
glm::quat rotation = glm::angleAxis(angle, yAxis);
rotation = offRotation * glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should change rotation" << std::endl;
}
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
float qDot = glm::dot(expectedRotation, newRotation);
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
{ // test way off rotation < minAngle
float offAngle = 0.5f;
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
float angle = minAngle - 0.2f * (TWO_PI - (maxAngle - minAngle));
glm::quat rotation = glm::angleAxis(angle, yAxis);
rotation = offRotation * glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should change rotation" << std::endl;
}
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
float qDot = glm::dot(expectedRotation, newRotation);
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
{ // test way off rotation > maxAngle with wrap over to minAngle
float offAngle = -0.5f;
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
float angle = maxAngle + 0.6f * (TWO_PI - (maxAngle - minAngle));
glm::quat rotation = glm::angleAxis(angle, yAxis);
rotation = offRotation * glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should change rotation" << std::endl;
}
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
float qDot = glm::dot(expectedRotation, newRotation);
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
{ // test way off rotation < minAngle with wrap over to maxAngle
float offAngle = -0.6f;
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
float angle = minAngle - 0.7f * (TWO_PI - (maxAngle - minAngle));
glm::quat rotation = glm::angleAxis(angle, yAxis);
rotation = offRotation * glm::angleAxis(angle, yAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint should change rotation" << std::endl;
}
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
float qDot = glm::dot(expectedRotation, newRotation);
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
delete c;
}
void AngularConstraintTests::testConeRollerConstraint() {
float minAngleX = -PI / 5.0f;
float minAngleY = -PI / 5.0f;
float minAngleZ = -PI / 8.0f;
float maxAngleX = PI / 4.0f;
float maxAngleY = PI / 3.0f;
float maxAngleZ = PI / 4.0f;
glm::vec3 minAngles(minAngleX, minAngleY, minAngleZ);
glm::vec3 maxAngles(maxAngleX, maxAngleY, maxAngleZ);
AngularConstraint* c = AngularConstraint::newAngularConstraint(minAngles, maxAngles);
float expectedConeAngle = 0.25 * (maxAngleX - minAngleX + maxAngleY - minAngleY);
glm::vec3 middleAngles = 0.5f * (maxAngles + minAngles);
glm::quat yaw = glm::angleAxis(middleAngles[1], glm::vec3(0.0f, 1.0f, 0.0f));
glm::quat pitch = glm::angleAxis(middleAngles[0], glm::vec3(1.0f, 0.0f, 0.0f));
glm::vec3 expectedConeAxis = pitch * yaw * glm::vec3(0.0f, 0.0f, 1.0f);
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
glm::vec3 perpAxis = glm::normalize(xAxis - glm::dot(xAxis, expectedConeAxis) * expectedConeAxis);
if (!c) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: newAngularConstraint() should make a constraint" << std::endl;
}
{ // test in middle of constraint
glm::vec3 angles(PI/20.0f, 0.0f, PI/10.0f);
glm::quat rotation(angles);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
}
if (rotation != newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
}
}
float deltaAngle = 0.001f;
{ // test just inside edge of cone
glm::quat rotation = glm::angleAxis(expectedConeAngle - deltaAngle, perpAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
}
if (rotation != newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
}
}
{ // test just outside edge of cone
glm::quat rotation = glm::angleAxis(expectedConeAngle + deltaAngle, perpAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
}
}
{ // test just inside min edge of roll
glm::quat rotation = glm::angleAxis(minAngleZ + deltaAngle, expectedConeAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
}
if (rotation != newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
}
}
{ // test just inside max edge of roll
glm::quat rotation = glm::angleAxis(maxAngleZ - deltaAngle, expectedConeAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
}
if (rotation != newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
}
}
{ // test just outside min edge of roll
glm::quat rotation = glm::angleAxis(minAngleZ - deltaAngle, expectedConeAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
}
glm::quat expectedRotation = glm::angleAxis(minAngleZ, expectedConeAxis);
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
{ // test just outside max edge of roll
glm::quat rotation = glm::angleAxis(maxAngleZ + deltaAngle, expectedConeAxis);
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
}
glm::quat expectedRotation = glm::angleAxis(maxAngleZ, expectedConeAxis);
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
deltaAngle = 0.25f * expectedConeAngle;
{ // test far outside cone and min roll
glm::quat roll = glm::angleAxis(minAngleZ - deltaAngle, expectedConeAxis);
glm::quat pitchYaw = glm::angleAxis(expectedConeAngle + deltaAngle, perpAxis);
glm::quat rotation = pitchYaw * roll;
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
}
glm::quat expectedRoll = glm::angleAxis(minAngleZ, expectedConeAxis);
glm::quat expectedPitchYaw = glm::angleAxis(expectedConeAngle, perpAxis);
glm::quat expectedRotation = expectedPitchYaw * expectedRoll;
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
{ // test far outside cone and max roll
glm::quat roll = glm::angleAxis(maxAngleZ + deltaAngle, expectedConeAxis);
glm::quat pitchYaw = glm::angleAxis(- expectedConeAngle - deltaAngle, perpAxis);
glm::quat rotation = pitchYaw * roll;
glm::quat newRotation = rotation;
bool constrained = c->clamp(newRotation);
if (!constrained) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
}
if (rotation == newRotation) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
}
glm::quat expectedRoll = glm::angleAxis(maxAngleZ, expectedConeAxis);
glm::quat expectedPitchYaw = glm::angleAxis(- expectedConeAngle, perpAxis);
glm::quat expectedRotation = expectedPitchYaw * expectedRoll;
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
}
}
delete c;
}
void AngularConstraintTests::runAllTests() {
testHingeConstraint();
testConeRollerConstraint();
}

View file

@ -0,0 +1,21 @@
//
// AngularConstraintTests.h
// tests/physics/src
//
// Created by Andrew Meadows on 2014.05.30
// 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_AngularConstraintTests_h
#define hifi_AngularConstraintTests_h
namespace AngularConstraintTests {
void testHingeConstraint();
void testConeRollerConstraint();
void runAllTests();
}
#endif // hifi_AngularConstraintTests_h

View file

@ -8,9 +8,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "AngularConstraintTests.h"
#include "MovingPercentileTests.h" #include "MovingPercentileTests.h"
int main(int argc, char** argv) { int main(int argc, char** argv) {
MovingPercentileTests::runAllTests(); MovingPercentileTests::runAllTests();
AngularConstraintTests::runAllTests();
return 0; return 0;
} }