mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into modelserver
This commit is contained in:
commit
02bb816d5a
37 changed files with 893 additions and 373 deletions
|
@ -49,7 +49,7 @@ else (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS)
|
|||
set(WINDOWS_LIBOVR_NAME "libovr.lib")
|
||||
endif()
|
||||
|
||||
find_library(LIBOVR_LIBRARIES "Lib/Win32/${LIBOVR_NAME}" HINTS ${LIBOVR_SEARCH_DIRS})
|
||||
find_library(LIBOVR_LIBRARIES "Lib/Win32/VS2010/${LIBOVR_NAME}" HINTS ${LIBOVR_SEARCH_DIRS})
|
||||
endif ()
|
||||
|
||||
if (LIBOVR_INCLUDE_DIRS AND LIBOVR_LIBRARIES)
|
||||
|
|
|
@ -686,11 +686,8 @@ void Application::resizeGL(int width, int height) {
|
|||
glLoadIdentity();
|
||||
|
||||
// update Stats width
|
||||
int horizontalOffset = 0;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
// mirror is enabled, let's set horizontal offset to give stats some margin
|
||||
horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
|
||||
}
|
||||
// let's set horizontal offset to give stats some margin to mirror
|
||||
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
|
||||
Stats::getInstance()->resetWidth(width, horizontalOffset);
|
||||
}
|
||||
|
||||
|
@ -1164,10 +1161,8 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
|
|||
_mousePressed = false;
|
||||
checkBandwidthMeterClick();
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||
int horizontalOffset = 0;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
horizontalOffset = MIRROR_VIEW_WIDTH;
|
||||
}
|
||||
// let's set horizontal offset to give stats some margin to mirror
|
||||
int horizontalOffset = MIRROR_VIEW_WIDTH;
|
||||
Stats::getInstance()->checkClick(_mouseX, _mouseY, _mouseDragStartedX, _mouseDragStartedY, horizontalOffset);
|
||||
}
|
||||
}
|
||||
|
@ -2732,11 +2727,8 @@ void Application::displayOverlay() {
|
|||
glPointSize(1.0f);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||
int horizontalOffset = 0;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
// mirror is enabled, let's set horizontal offset to give stats some margin
|
||||
horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
|
||||
}
|
||||
// let's set horizontal offset to give stats some margin to mirror
|
||||
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
|
||||
int voxelPacketsToProcess = _voxelProcessor.packetsToProcessCount();
|
||||
// Onscreen text about position, servers, etc
|
||||
Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, _fps, _packetsPerSecond, _bytesPerSecond, voxelPacketsToProcess);
|
||||
|
@ -3655,7 +3647,17 @@ void Application::takeSnapshot() {
|
|||
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||
player->play();
|
||||
|
||||
Snapshot::saveSnapshot(_glWidget, _myAvatar);
|
||||
QString fileName = Snapshot::saveSnapshot(_glWidget, _myAvatar);
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
if (!accountManager.isLoggedIn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_snapshotShareDialog) {
|
||||
_snapshotShareDialog = new SnapshotShareDialog(fileName, _glWidget);
|
||||
}
|
||||
_snapshotShareDialog->show();
|
||||
}
|
||||
|
||||
void Application::urlGoTo(int argc, const char * constArgv[]) {
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
#include "ui/ModelsBrowser.h"
|
||||
#include "ui/OctreeStatsDialog.h"
|
||||
#include "ui/RearMirrorTools.h"
|
||||
#include "ui/SnapshotShareDialog.h"
|
||||
#include "ui/LodToolsDialog.h"
|
||||
#include "ui/LogDialog.h"
|
||||
#include "ui/UpdateDialog.h"
|
||||
|
@ -517,6 +518,7 @@ private:
|
|||
std::vector<VoxelFade> _voxelFades;
|
||||
ControllerScriptingInterface _controllerScriptingInterface;
|
||||
QPointer<LogDialog> _logDialog;
|
||||
QPointer<SnapshotShareDialog> _snapshotShareDialog;
|
||||
|
||||
QString _previousScriptLocation;
|
||||
|
||||
|
|
|
@ -60,15 +60,13 @@ BuckyBalls::BuckyBalls() {
|
|||
|
||||
void BuckyBalls::grab(PalmData& palm, float deltaTime) {
|
||||
float penetration;
|
||||
glm::vec3 diff;
|
||||
FingerData& finger = palm.getFingers()[0]; // Sixense has only one finger
|
||||
glm::vec3 fingerTipPosition = finger.getTipPosition();
|
||||
glm::vec3 fingerTipPosition = palm.getFingerTipPosition();
|
||||
|
||||
if (palm.getControllerButtons() & BUTTON_FWD) {
|
||||
if (!_bballIsGrabbed[palm.getSixenseID()]) {
|
||||
// Look for a ball to grab
|
||||
for (int i = 0; i < NUM_BBALLS; i++) {
|
||||
diff = _bballPosition[i] - fingerTipPosition;
|
||||
glm::vec3 diff = _bballPosition[i] - fingerTipPosition;
|
||||
penetration = glm::length(diff) - (_bballRadius[i] + COLLISION_RADIUS);
|
||||
if (penetration < 0.f) {
|
||||
_bballIsGrabbed[palm.getSixenseID()] = i;
|
||||
|
@ -77,7 +75,7 @@ void BuckyBalls::grab(PalmData& palm, float deltaTime) {
|
|||
}
|
||||
if (_bballIsGrabbed[palm.getSixenseID()]) {
|
||||
// If ball being grabbed, move with finger
|
||||
diff = _bballPosition[_bballIsGrabbed[palm.getSixenseID()]] - fingerTipPosition;
|
||||
glm::vec3 diff = _bballPosition[_bballIsGrabbed[palm.getSixenseID()]] - fingerTipPosition;
|
||||
penetration = glm::length(diff) - (_bballRadius[_bballIsGrabbed[palm.getSixenseID()]] + COLLISION_RADIUS);
|
||||
_bballPosition[_bballIsGrabbed[palm.getSixenseID()]] -= glm::normalize(diff) * penetration;
|
||||
glm::vec3 fingerTipVelocity = palm.getTipVelocity();
|
||||
|
|
|
@ -889,7 +889,7 @@ void Menu::goToDomainDialog() {
|
|||
}
|
||||
|
||||
void Menu::goToOrientation(QString orientation) {
|
||||
LocationManager::getInstance().goToDestination(orientation);
|
||||
LocationManager::getInstance().goToOrientation(orientation);
|
||||
}
|
||||
|
||||
bool Menu::goToDestination(QString destination) {
|
||||
|
|
|
@ -49,7 +49,7 @@ static const QString TRANSLATION_Z_FIELD = "tz";
|
|||
static const QString JOINT_FIELD = "joint";
|
||||
static const QString FREE_JOINT_FIELD = "freeJoint";
|
||||
|
||||
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
|
||||
static const QString S3_URL = "http://public.highfidelity.io";
|
||||
static const QString DATA_SERVER_URL = "https://data-web.highfidelity.io";
|
||||
static const QString MODEL_URL = "/api/v1/models";
|
||||
|
||||
|
@ -201,12 +201,15 @@ bool ModelUploader::zip() {
|
|||
mapping = properties.getMapping();
|
||||
|
||||
QByteArray nameField = mapping.value(NAME_FIELD).toByteArray();
|
||||
QString urlBase;
|
||||
if (!nameField.isEmpty()) {
|
||||
QHttpPart textPart;
|
||||
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"model_name\"");
|
||||
textPart.setBody(nameField);
|
||||
_dataMultiPart->append(textPart);
|
||||
_url = S3_URL + "/models/" + MODEL_TYPE_NAMES[_modelType] + "/" + nameField + ".fst";
|
||||
urlBase = S3_URL + "/models/" + MODEL_TYPE_NAMES[_modelType] + "/" + nameField;
|
||||
_url = urlBase + ".fst";
|
||||
|
||||
} else {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
|
@ -218,6 +221,7 @@ bool ModelUploader::zip() {
|
|||
|
||||
QByteArray texdirField = mapping.value(TEXDIR_FIELD).toByteArray();
|
||||
QString texDir;
|
||||
_textureBase = urlBase + "/textures/";
|
||||
if (!texdirField.isEmpty()) {
|
||||
texDir = basePath + "/" + texdirField;
|
||||
QFileInfo texInfo(texDir);
|
||||
|
@ -407,6 +411,10 @@ void ModelUploader::processCheck() {
|
|||
QString("ModelUploader::processCheck()"),
|
||||
QString("Your model is now available in the browser."),
|
||||
QMessageBox::Ok);
|
||||
Application::getInstance()->getGeometryCache()->refresh(_url);
|
||||
foreach (const QByteArray& filename, _textureFilenames) {
|
||||
Application::getInstance()->getTextureCache()->refresh(_textureBase + filename);
|
||||
}
|
||||
deleteLater();
|
||||
break;
|
||||
case QNetworkReply::ContentNotFoundError:
|
||||
|
@ -428,32 +436,31 @@ void ModelUploader::processCheck() {
|
|||
}
|
||||
|
||||
bool ModelUploader::addTextures(const QString& texdir, const FBXGeometry& geometry) {
|
||||
QSet<QByteArray> added;
|
||||
foreach (FBXMesh mesh, geometry.meshes) {
|
||||
foreach (FBXMeshPart part, mesh.parts) {
|
||||
if (!part.diffuseTexture.filename.isEmpty() && part.diffuseTexture.content.isEmpty() &&
|
||||
!added.contains(part.diffuseTexture.filename)) {
|
||||
!_textureFilenames.contains(part.diffuseTexture.filename)) {
|
||||
if (!addPart(texdir + "/" + part.diffuseTexture.filename,
|
||||
QString("texture%1").arg(++_texturesCount), true)) {
|
||||
return false;
|
||||
}
|
||||
added.insert(part.diffuseTexture.filename);
|
||||
_textureFilenames.insert(part.diffuseTexture.filename);
|
||||
}
|
||||
if (!part.normalTexture.filename.isEmpty() && part.normalTexture.content.isEmpty() &&
|
||||
!added.contains(part.normalTexture.filename)) {
|
||||
!_textureFilenames.contains(part.normalTexture.filename)) {
|
||||
if (!addPart(texdir + "/" + part.normalTexture.filename,
|
||||
QString("texture%1").arg(++_texturesCount), true)) {
|
||||
return false;
|
||||
}
|
||||
added.insert(part.normalTexture.filename);
|
||||
_textureFilenames.insert(part.normalTexture.filename);
|
||||
}
|
||||
if (!part.specularTexture.filename.isEmpty() && part.specularTexture.content.isEmpty() &&
|
||||
!added.contains(part.specularTexture.filename)) {
|
||||
!_textureFilenames.contains(part.specularTexture.filename)) {
|
||||
if (!addPart(texdir + "/" + part.specularTexture.filename,
|
||||
QString("texture%1").arg(++_texturesCount), true)) {
|
||||
return false;
|
||||
}
|
||||
added.insert(part.specularTexture.filename);
|
||||
_textureFilenames.insert(part.specularTexture.filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ private slots:
|
|||
|
||||
private:
|
||||
QString _url;
|
||||
QString _textureBase;
|
||||
QSet<QByteArray> _textureFilenames;
|
||||
int _lodCount;
|
||||
int _texturesCount;
|
||||
int _totalSize;
|
||||
|
|
|
@ -609,18 +609,6 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti
|
|||
const PalmData* palm = handData->getPalm(i);
|
||||
if (palm && palm->hasPaddle()) {
|
||||
// create a disk collision proxy where the hand is
|
||||
glm::vec3 fingerAxis(0.0f);
|
||||
for (size_t f = 0; f < palm->getNumFingers(); ++f) {
|
||||
const FingerData& finger = (palm->getFingers())[f];
|
||||
if (finger.isActive()) {
|
||||
// compute finger axis
|
||||
glm::vec3 fingerTip = finger.getTipPosition();
|
||||
glm::vec3 fingerRoot = finger.getRootPosition();
|
||||
fingerAxis = glm::normalize(fingerTip - fingerRoot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int jointIndex = -1;
|
||||
glm::vec3 handPosition;
|
||||
if (i == 0) {
|
||||
|
@ -631,8 +619,10 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti
|
|||
_skeletonModel.getRightHandPosition(handPosition);
|
||||
jointIndex = _skeletonModel.getRightHandJointIndex();
|
||||
}
|
||||
|
||||
glm::vec3 fingerAxis = palm->getFingerDirection();
|
||||
glm::vec3 diskCenter = handPosition + HAND_PADDLE_OFFSET * fingerAxis;
|
||||
glm::vec3 diskNormal = palm->getNormal();
|
||||
glm::vec3 diskNormal = palm->getPalmDirection();
|
||||
const float DISK_THICKNESS = 0.08f;
|
||||
|
||||
// collide against the disk
|
||||
|
|
|
@ -162,9 +162,9 @@ void Hand::render(bool isMine, Model::RenderMode renderMode) {
|
|||
}
|
||||
|
||||
void Hand::renderHandTargets(bool isMine) {
|
||||
glPushMatrix();
|
||||
|
||||
const float alpha = 1.0f;
|
||||
|
||||
const glm::vec3 handColor(1.0, 0.0, 0.0); // Color the hand targets red to be different than skin
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
@ -176,8 +176,7 @@ void Hand::renderHandTargets(bool isMine) {
|
|||
if (!palm.isActive()) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 targetPosition;
|
||||
palm.getBallHoldPosition(targetPosition);
|
||||
glm::vec3 targetPosition = palm.getFingerTipPosition();
|
||||
glPushMatrix();
|
||||
glTranslatef(targetPosition.x, targetPosition.y, targetPosition.z);
|
||||
|
||||
|
@ -197,59 +196,20 @@ void Hand::renderHandTargets(bool isMine) {
|
|||
for (size_t i = 0; i < getNumPalms(); ++i) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
if (palm.isActive()) {
|
||||
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
|
||||
FingerData& finger = palm.getFingers()[f];
|
||||
if (finger.isActive()) {
|
||||
glColor4f(handColor.r, handColor.g, handColor.b, alpha);
|
||||
glm::vec3 tip = finger.getTipPosition();
|
||||
glm::vec3 root = finger.getRootPosition();
|
||||
Avatar::renderJointConnectingCone(root, tip, PALM_FINGER_ROD_RADIUS, PALM_FINGER_ROD_RADIUS);
|
||||
// Render sphere at palm/finger root
|
||||
glm::vec3 palmNormal = root + palm.getNormal() * PALM_DISK_THICKNESS;
|
||||
Avatar::renderJointConnectingCone(root, palmNormal, PALM_DISK_RADIUS, 0.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(root.x, root.y, root.z);
|
||||
glutSolidSphere(PALM_BALL_RADIUS, 20.0f, 20.0f);
|
||||
glPopMatrix();
|
||||
|
||||
}
|
||||
}
|
||||
glColor4f(handColor.r, handColor.g, handColor.b, alpha);
|
||||
glm::vec3 tip = palm.getFingerTipPosition();
|
||||
glm::vec3 root = palm.getPosition();
|
||||
Avatar::renderJointConnectingCone(root, tip, PALM_FINGER_ROD_RADIUS, PALM_FINGER_ROD_RADIUS);
|
||||
// Render sphere at palm/finger root
|
||||
glm::vec3 offsetFromPalm = root + palm.getPalmDirection() * PALM_DISK_THICKNESS;
|
||||
Avatar::renderJointConnectingCone(root, offsetFromPalm, PALM_DISK_RADIUS, 0.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(root.x, root.y, root.z);
|
||||
glutSolidSphere(PALM_BALL_RADIUS, 20.0f, 20.0f);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Draw the hand paddles
|
||||
int MAX_NUM_PADDLES = 2; // one for left and one for right
|
||||
glColor4f(handColor.r, handColor.g, handColor.b, 0.3f);
|
||||
for (int i = 0; i < MAX_NUM_PADDLES; i++) {
|
||||
const PalmData* palm = getPalm(i);
|
||||
if (palm) {
|
||||
// compute finger axis
|
||||
glm::vec3 fingerAxis(0.f);
|
||||
for (size_t f = 0; f < palm->getNumFingers(); ++f) {
|
||||
const FingerData& finger = (palm->getFingers())[f];
|
||||
if (finger.isActive()) {
|
||||
glm::vec3 fingerTip = finger.getTipPosition();
|
||||
glm::vec3 fingerRoot = finger.getRootPosition();
|
||||
fingerAxis = glm::normalize(fingerTip - fingerRoot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// compute paddle position
|
||||
glm::vec3 handPosition;
|
||||
if (i == SIXENSE_CONTROLLER_ID_LEFT_HAND) {
|
||||
_owningAvatar->getSkeletonModel().getLeftHandPosition(handPosition);
|
||||
} else if (i == SIXENSE_CONTROLLER_ID_RIGHT_HAND) {
|
||||
_owningAvatar->getSkeletonModel().getRightHandPosition(handPosition);
|
||||
}
|
||||
glm::vec3 tip = handPosition + HAND_PADDLE_OFFSET * fingerAxis;
|
||||
glm::vec3 root = tip + palm->getNormal() * HAND_PADDLE_THICKNESS;
|
||||
// render a very shallow cone as the paddle
|
||||
Avatar::renderJointConnectingCone(root, tip, HAND_PADDLE_RADIUS, 0.f);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
|
|
|
@ -520,8 +520,9 @@ void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const {
|
|||
settings->beginGroup("savedAttachmentData");
|
||||
settings->beginGroup(_skeletonModel.getURL().toString());
|
||||
settings->beginGroup(attachment.modelURL.toString());
|
||||
|
||||
settings->setValue("jointName", attachment.jointName);
|
||||
|
||||
settings->beginGroup(attachment.jointName);
|
||||
settings->setValue("translation_x", attachment.translation.x);
|
||||
settings->setValue("translation_y", attachment.translation.y);
|
||||
settings->setValue("translation_z", attachment.translation.z);
|
||||
|
@ -534,10 +535,11 @@ void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const {
|
|||
settings->endGroup();
|
||||
settings->endGroup();
|
||||
settings->endGroup();
|
||||
settings->endGroup();
|
||||
Application::getInstance()->unlockSettings();
|
||||
}
|
||||
|
||||
AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL) const {
|
||||
AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString& jointName) const {
|
||||
QSettings* settings = Application::getInstance()->lockSettings();
|
||||
settings->beginGroup("savedAttachmentData");
|
||||
settings->beginGroup(_skeletonModel.getURL().toString());
|
||||
|
@ -545,20 +547,30 @@ AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL) const {
|
|||
|
||||
AttachmentData attachment;
|
||||
attachment.modelURL = modelURL;
|
||||
attachment.jointName = settings->value("jointName").toString();
|
||||
attachment.translation.x = loadSetting(settings, "translation_x", 0.0f);
|
||||
attachment.translation.y = loadSetting(settings, "translation_y", 0.0f);
|
||||
attachment.translation.z = loadSetting(settings, "translation_z", 0.0f);
|
||||
glm::vec3 eulers;
|
||||
eulers.x = loadSetting(settings, "rotation_x", 0.0f);
|
||||
eulers.y = loadSetting(settings, "rotation_y", 0.0f);
|
||||
eulers.z = loadSetting(settings, "rotation_z", 0.0f);
|
||||
attachment.rotation = glm::quat(eulers);
|
||||
attachment.scale = loadSetting(settings, "scale", 1.0f);
|
||||
if (jointName.isEmpty()) {
|
||||
attachment.jointName = settings->value("jointName").toString();
|
||||
} else {
|
||||
attachment.jointName = jointName;
|
||||
}
|
||||
settings->beginGroup(attachment.jointName);
|
||||
if (settings->contains("translation_x")) {
|
||||
attachment.translation.x = loadSetting(settings, "translation_x", 0.0f);
|
||||
attachment.translation.y = loadSetting(settings, "translation_y", 0.0f);
|
||||
attachment.translation.z = loadSetting(settings, "translation_z", 0.0f);
|
||||
glm::vec3 eulers;
|
||||
eulers.x = loadSetting(settings, "rotation_x", 0.0f);
|
||||
eulers.y = loadSetting(settings, "rotation_y", 0.0f);
|
||||
eulers.z = loadSetting(settings, "rotation_z", 0.0f);
|
||||
attachment.rotation = glm::quat(eulers);
|
||||
attachment.scale = loadSetting(settings, "scale", 1.0f);
|
||||
} else {
|
||||
attachment = AttachmentData();
|
||||
}
|
||||
|
||||
settings->endGroup();
|
||||
settings->endGroup();
|
||||
settings->endGroup();
|
||||
settings->endGroup();
|
||||
Application::getInstance()->unlockSettings();
|
||||
|
||||
return attachment;
|
||||
|
@ -650,8 +662,8 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, const g
|
|||
return;
|
||||
}
|
||||
if (useSaved) {
|
||||
AttachmentData attachment = loadAttachmentData(modelURL);
|
||||
if (!attachment.jointName.isEmpty()) {
|
||||
AttachmentData attachment = loadAttachmentData(modelURL, jointName);
|
||||
if (attachment.isValid()) {
|
||||
Avatar::attach(modelURL, attachment.jointName, attachment.translation,
|
||||
attachment.rotation, attachment.scale, allowDuplicates, useSaved);
|
||||
return;
|
||||
|
@ -1490,7 +1502,7 @@ void MyAvatar::updateMotionBehaviorsFromMenu() {
|
|||
}
|
||||
|
||||
void MyAvatar::renderAttachments(RenderMode renderMode) {
|
||||
if (!Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON || renderMode == MIRROR_RENDER_MODE) {
|
||||
if (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON || renderMode == MIRROR_RENDER_MODE) {
|
||||
Avatar::renderAttachments(renderMode);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ public:
|
|||
void loadData(QSettings* settings);
|
||||
|
||||
void saveAttachmentData(const AttachmentData& attachment) const;
|
||||
AttachmentData loadAttachmentData(const QUrl& modelURL) const;
|
||||
AttachmentData loadAttachmentData(const QUrl& modelURL, const QString& jointName = QString()) const;
|
||||
|
||||
// Set what driving keys are being pressed to control thrust levels
|
||||
void setDriveKeys(int key, float val) { _driveKeys[key] = val; };
|
||||
|
|
|
@ -33,7 +33,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
return; // only simulate for own avatar
|
||||
}
|
||||
|
||||
// find the left and rightmost active Leap palms
|
||||
// find the left and rightmost active palms
|
||||
int leftPalmIndex, rightPalmIndex;
|
||||
Hand* hand = _owningAvatar->getHand();
|
||||
hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
|
||||
|
@ -42,7 +42,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (leftPalmIndex == -1) {
|
||||
// no Leap data; set hands from mouse
|
||||
// palms are not yet set, use mouse
|
||||
if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
|
||||
restoreRightHandPosition(HAND_RESTORATION_RATE);
|
||||
} else {
|
||||
|
@ -159,29 +159,13 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
|
|||
} else {
|
||||
getJointRotation(jointIndex, palmRotation, true);
|
||||
}
|
||||
palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()) * palmRotation;
|
||||
palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palm.getPalmDirection()) * palmRotation;
|
||||
|
||||
// sort the finger indices by raw x, get the average direction
|
||||
QVector<IndexValue> fingerIndices;
|
||||
glm::vec3 direction;
|
||||
for (size_t i = 0; i < palm.getNumFingers(); i++) {
|
||||
glm::vec3 fingerVector = palm.getFingers()[i].getTipPosition() - palm.getPosition();
|
||||
float length = glm::length(fingerVector);
|
||||
if (length > EPSILON) {
|
||||
direction += fingerVector / length;
|
||||
}
|
||||
fingerVector = glm::inverse(palmRotation) * fingerVector * -sign;
|
||||
IndexValue indexValue = { (int)i, atan2f(fingerVector.z, fingerVector.x) };
|
||||
fingerIndices.append(indexValue);
|
||||
}
|
||||
qSort(fingerIndices.begin(), fingerIndices.end());
|
||||
|
||||
// rotate forearm according to average finger direction
|
||||
float directionLength = glm::length(direction);
|
||||
const unsigned int MIN_ROTATION_FINGERS = 3;
|
||||
if (directionLength > EPSILON && palm.getNumFingers() >= MIN_ROTATION_FINGERS) {
|
||||
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction) * palmRotation;
|
||||
}
|
||||
// NOTE: we're doing this in the avatar local frame, so we DON'T want to use Palm::getHandDirection()
|
||||
// which returns the world-frame.
|
||||
glm::vec3 direction = palm.getRawRotation() * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction) * palmRotation;
|
||||
|
||||
// set hand position, rotation
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
||||
|
|
|
@ -22,9 +22,9 @@ const int CALIBRATION_STATE_Z = 3;
|
|||
const int CALIBRATION_STATE_COMPLETE = 4;
|
||||
|
||||
// default (expected) location of neck in sixense space
|
||||
const float NECK_X = 250.f; // millimeters
|
||||
const float NECK_Y = 300.f; // millimeters
|
||||
const float NECK_Z = 300.f; // millimeters
|
||||
const float NECK_X = 0.25f; // meters
|
||||
const float NECK_Y = 0.3f; // meters
|
||||
const float NECK_Z = 0.3f; // meters
|
||||
#endif
|
||||
|
||||
SixenseManager::SixenseManager() {
|
||||
|
@ -106,8 +106,11 @@ void SixenseManager::update(float deltaTime) {
|
|||
palm->setControllerButtons(data->buttons);
|
||||
palm->setTrigger(data->trigger);
|
||||
palm->setJoystick(data->joystick_x, data->joystick_y);
|
||||
|
||||
|
||||
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
|
||||
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
|
||||
position *= METERS_PER_MILLIMETER;
|
||||
|
||||
// Transform the measured position into body frame.
|
||||
glm::vec3 neck = _neckBase;
|
||||
// Zeroing y component of the "neck" effectively raises the measured position a little bit.
|
||||
|
@ -117,15 +120,12 @@ void SixenseManager::update(float deltaTime) {
|
|||
// Rotation of Palm
|
||||
glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]);
|
||||
rotation = glm::angleAxis(PI, glm::vec3(0.f, 1.f, 0.f)) * _orbRotation * rotation;
|
||||
const glm::vec3 PALM_VECTOR(0.0f, -1.0f, 0.0f);
|
||||
glm::vec3 newNormal = rotation * PALM_VECTOR;
|
||||
palm->setRawNormal(newNormal);
|
||||
palm->setRawRotation(rotation);
|
||||
|
||||
// Compute current velocity from position change
|
||||
glm::vec3 rawVelocity;
|
||||
if (deltaTime > 0.f) {
|
||||
rawVelocity = (position - palm->getRawPosition()) / deltaTime / 1000.f;
|
||||
rawVelocity = (position - palm->getRawPosition()) / deltaTime;
|
||||
} else {
|
||||
rawVelocity = glm::vec3(0.0f);
|
||||
}
|
||||
|
@ -140,29 +140,17 @@ void SixenseManager::update(float deltaTime) {
|
|||
_amountMoved = glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
// initialize the "finger" based on the direction
|
||||
FingerData finger(palm, hand);
|
||||
finger.setActive(true);
|
||||
finger.setRawRootPosition(position);
|
||||
const float FINGER_LENGTH = 300.0f; // Millimeters
|
||||
// Store the one fingertip in the palm structure so we can track velocity
|
||||
const float FINGER_LENGTH = 0.3f; // meters
|
||||
const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH);
|
||||
const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
|
||||
finger.setRawTipPosition(position + rotation * FINGER_VECTOR);
|
||||
|
||||
// Store the one fingertip in the palm structure so we can track velocity
|
||||
glm::vec3 oldTipPosition = palm->getTipRawPosition();
|
||||
if (deltaTime > 0.f) {
|
||||
palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime / 1000.f);
|
||||
palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);
|
||||
} else {
|
||||
palm->setTipVelocity(glm::vec3(0.f));
|
||||
}
|
||||
palm->setTipPosition(newTipPosition);
|
||||
|
||||
// three fingers indicates to the skeleton that we have enough data to determine direction
|
||||
palm->getFingers().clear();
|
||||
palm->getFingers().push_back(finger);
|
||||
palm->getFingers().push_back(finger);
|
||||
palm->getFingers().push_back(finger);
|
||||
}
|
||||
|
||||
if (numActiveControllers == 2) {
|
||||
|
@ -171,7 +159,7 @@ void SixenseManager::update(float deltaTime) {
|
|||
|
||||
// if the controllers haven't been moved in a while, disable
|
||||
const unsigned int MOVEMENT_DISABLE_SECONDS = 3;
|
||||
if (usecTimestampNow() - _lastMovement > (MOVEMENT_DISABLE_SECONDS * 1000 * 1000)) {
|
||||
if (usecTimestampNow() - _lastMovement > (MOVEMENT_DISABLE_SECONDS * USECS_PER_SECOND)) {
|
||||
for (std::vector<PalmData>::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) {
|
||||
it->setActive(false);
|
||||
}
|
||||
|
@ -188,8 +176,8 @@ void SixenseManager::update(float deltaTime) {
|
|||
// (4) move arms a bit forward (Z)
|
||||
// (5) release BUTTON_FWD on both hands
|
||||
|
||||
const float MINIMUM_ARM_REACH = 300.f; // millimeters
|
||||
const float MAXIMUM_NOISE_LEVEL = 50.f; // millimeters
|
||||
const float MINIMUM_ARM_REACH = 0.3f; // meters
|
||||
const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters
|
||||
const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired
|
||||
|
||||
void SixenseManager::updateCalibration(const sixenseControllerData* controllers) {
|
||||
|
@ -229,14 +217,17 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
|
|||
return;
|
||||
}
|
||||
|
||||
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
|
||||
const float* pos = dataLeft->pos;
|
||||
glm::vec3 positionLeft(pos[0], pos[1], pos[2]);
|
||||
positionLeft *= METERS_PER_MILLIMETER;
|
||||
pos = dataRight->pos;
|
||||
glm::vec3 positionRight(pos[0], pos[1], pos[2]);
|
||||
positionRight *= METERS_PER_MILLIMETER;
|
||||
|
||||
if (_calibrationState == CALIBRATION_STATE_IDLE) {
|
||||
float reach = glm::distance(positionLeft, positionRight);
|
||||
if (reach > 2.f * MINIMUM_ARM_REACH) {
|
||||
if (reach > 2.0f * MINIMUM_ARM_REACH) {
|
||||
qDebug("started: sixense calibration");
|
||||
_averageLeft = positionLeft;
|
||||
_averageRight = positionRight;
|
||||
|
|
|
@ -506,6 +506,15 @@ void GeometryReader::run() {
|
|||
_reply->deleteLater();
|
||||
}
|
||||
|
||||
void NetworkGeometry::init() {
|
||||
_mapping = QVariantHash();
|
||||
_geometry = FBXGeometry();
|
||||
_meshes.clear();
|
||||
_lods.clear();
|
||||
_request.setUrl(_url);
|
||||
Resource::init();
|
||||
}
|
||||
|
||||
void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
|
||||
QUrl url = reply->url();
|
||||
if (url.path().toLower().endsWith(".fst")) {
|
||||
|
|
|
@ -96,6 +96,7 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
virtual void init();
|
||||
virtual void downloadFinished(QNetworkReply* reply);
|
||||
virtual void reinsert();
|
||||
|
||||
|
|
|
@ -319,6 +319,9 @@ bool Model::updateGeometry() {
|
|||
_jointStates = createJointStates(fbxGeometry);
|
||||
needToRebuild = true;
|
||||
}
|
||||
} else if (!geometry->isLoaded()) {
|
||||
deleteGeometry();
|
||||
_dilatedTextures.clear();
|
||||
}
|
||||
_geometry->setLoadPriority(this, -_lodDistance);
|
||||
_geometry->ensureLoading();
|
||||
|
|
|
@ -198,9 +198,9 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex
|
|||
if (palmData) {
|
||||
switch (controlOfPalm) {
|
||||
case PALM_SPATIALCONTROL:
|
||||
return palmData->getNormal();
|
||||
return palmData->getPalmDirection();
|
||||
case TIP_SPATIALCONTROL:
|
||||
return palmData->getNormal(); // currently the tip doesn't have a unique normal, use the palm normal
|
||||
return palmData->getFingerDirection();
|
||||
}
|
||||
}
|
||||
return glm::vec3(0); // bad index
|
||||
|
|
|
@ -97,7 +97,8 @@ static QDoubleSpinBox* createRotationBox(AttachmentPanel* panel, float value) {
|
|||
}
|
||||
|
||||
AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data) :
|
||||
_dialog(dialog) {
|
||||
_dialog(dialog),
|
||||
_applying(false) {
|
||||
setFrameStyle(QFrame::StyledPanel);
|
||||
|
||||
QFormLayout* layout = new QFormLayout();
|
||||
|
@ -121,7 +122,7 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData
|
|||
}
|
||||
}
|
||||
_jointName->setCurrentText(data.jointName);
|
||||
connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(updateAttachmentData()));
|
||||
connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(jointNameChanged()));
|
||||
|
||||
QHBoxLayout* translationBox = new QHBoxLayout();
|
||||
translationBox->addWidget(_translationX = createTranslationBox(this, data.translation.x));
|
||||
|
@ -171,25 +172,57 @@ void AttachmentPanel::setModelURL(const QString& url) {
|
|||
|
||||
void AttachmentPanel::modelURLChanged() {
|
||||
// check for saved attachment data
|
||||
if (_modelURL->text().isEmpty()) {
|
||||
_dialog->updateAttachmentData();
|
||||
return;
|
||||
}
|
||||
AttachmentData attachment = Application::getInstance()->getAvatar()->loadAttachmentData(_modelURL->text());
|
||||
if (!attachment.jointName.isEmpty()) {
|
||||
if (attachment.isValid()) {
|
||||
_applying = true;
|
||||
_jointName->setCurrentText(attachment.jointName);
|
||||
_translationX->setValue(attachment.translation.x);
|
||||
_translationY->setValue(attachment.translation.y);
|
||||
_translationZ->setValue(attachment.translation.z);
|
||||
glm::vec3 eulers = glm::degrees(safeEulerAngles(attachment.rotation));
|
||||
_rotationX->setValue(eulers.x);
|
||||
_rotationY->setValue(eulers.y);
|
||||
_rotationZ->setValue(eulers.z);
|
||||
_scale->setValue(attachment.scale);
|
||||
applyAttachmentData(attachment);
|
||||
}
|
||||
_dialog->updateAttachmentData();
|
||||
}
|
||||
|
||||
void AttachmentPanel::jointNameChanged() {
|
||||
if (_applying) {
|
||||
return;
|
||||
}
|
||||
// check for saved attachment data specific to this joint
|
||||
if (_modelURL->text().isEmpty()) {
|
||||
_dialog->updateAttachmentData();
|
||||
return;
|
||||
}
|
||||
AttachmentData attachment = Application::getInstance()->getAvatar()->loadAttachmentData(
|
||||
_modelURL->text(), _jointName->currentText());
|
||||
if (attachment.isValid()) {
|
||||
applyAttachmentData(attachment);
|
||||
}
|
||||
updateAttachmentData();
|
||||
}
|
||||
|
||||
void AttachmentPanel::updateAttachmentData() {
|
||||
if (_applying) {
|
||||
return;
|
||||
}
|
||||
// save the attachment data under the model URL (if any)
|
||||
if (!_modelURL->text().isEmpty()) {
|
||||
Application::getInstance()->getAvatar()->saveAttachmentData(getAttachmentData());
|
||||
}
|
||||
_dialog->updateAttachmentData();
|
||||
}
|
||||
|
||||
void AttachmentPanel::applyAttachmentData(const AttachmentData& attachment) {
|
||||
_applying = true;
|
||||
_translationX->setValue(attachment.translation.x);
|
||||
_translationY->setValue(attachment.translation.y);
|
||||
_translationZ->setValue(attachment.translation.z);
|
||||
glm::vec3 eulers = glm::degrees(safeEulerAngles(attachment.rotation));
|
||||
_rotationX->setValue(eulers.x);
|
||||
_rotationY->setValue(eulers.y);
|
||||
_rotationZ->setValue(eulers.z);
|
||||
_scale->setValue(attachment.scale);
|
||||
_applying = false;
|
||||
_dialog->updateAttachmentData();
|
||||
}
|
||||
|
|
|
@ -61,10 +61,13 @@ private slots:
|
|||
void chooseModelURL();
|
||||
void setModelURL(const QString& url);
|
||||
void modelURLChanged();
|
||||
void jointNameChanged();
|
||||
void updateAttachmentData();
|
||||
|
||||
private:
|
||||
|
||||
void applyAttachmentData(const AttachmentData& attachment);
|
||||
|
||||
AttachmentsDialog* _dialog;
|
||||
QLineEdit* _modelURL;
|
||||
QComboBox* _jointName;
|
||||
|
@ -75,6 +78,7 @@ private:
|
|||
QDoubleSpinBox* _rotationY;
|
||||
QDoubleSpinBox* _rotationZ;
|
||||
QDoubleSpinBox* _scale;
|
||||
bool _applying;
|
||||
};
|
||||
|
||||
#endif // hifi_AttachmentsDialog_h
|
||||
|
|
|
@ -32,7 +32,7 @@ const int NUM_MESSAGES_TO_TIME_STAMP = 20;
|
|||
|
||||
const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?)|(?:hifi))://\\S+)");
|
||||
const QRegularExpression regexHifiLinks("([#@]\\S+)");
|
||||
const QString mentionSoundsPath("/sounds/mention/");
|
||||
const QString mentionSoundsPath("/mention-sounds/");
|
||||
const QString mentionRegex("@(\\b%1\\b)");
|
||||
|
||||
ChatWindow::ChatWindow(QWidget* parent) :
|
||||
|
|
|
@ -64,7 +64,7 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
|
|||
return data;
|
||||
}
|
||||
|
||||
void Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
|
||||
QString Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
|
||||
QImage shot = widget->grabFrameBuffer();
|
||||
|
||||
glm::vec3 location = avatar->getPosition();
|
||||
|
@ -99,6 +99,8 @@ void Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
|
|||
|
||||
fileName.append(QString(FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation)));
|
||||
shot.save(fileName, 0, 100);
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ private:
|
|||
class Snapshot {
|
||||
|
||||
public:
|
||||
static void saveSnapshot(QGLWidget* widget, Avatar* avatar);
|
||||
static QString saveSnapshot(QGLWidget* widget, Avatar* avatar);
|
||||
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
|
||||
};
|
||||
|
||||
|
|
198
interface/src/ui/SnapshotShareDialog.cpp
Normal file
198
interface/src/ui/SnapshotShareDialog.cpp
Normal file
|
@ -0,0 +1,198 @@
|
|||
//
|
||||
// SnapshotShareDialog.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stojce Slavkovski on 2/16/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 "SnapshotShareDialog.h"
|
||||
#include "AccountManager.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QHttpMultiPart>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QMessageBox>
|
||||
#include <QUrlQuery>
|
||||
|
||||
|
||||
const int NARROW_SNAPSHOT_DIALOG_SIZE = 500;
|
||||
const int WIDE_SNAPSHOT_DIALOG_WIDTH = 650;
|
||||
const int SUCCESS_LABEL_HEIGHT = 140;
|
||||
|
||||
const QString FORUM_URL = "https://alphas.highfidelity.io";
|
||||
const QString FORUM_UPLOADS_URL = FORUM_URL + "/uploads";
|
||||
const QString FORUM_POST_URL = FORUM_URL + "/posts";
|
||||
const QString FORUM_REPLY_TO_TOPIC = "244";
|
||||
const QString FORUM_POST_TEMPLATE = "<img src='%1'/><p>%2</p>";
|
||||
const QString SHARE_DEFAULT_ERROR = "The server isn't responding. Please try again in a few minutes.";
|
||||
const QString SUCCESS_LABEL_TEMPLATE = "Success!!! Go check out your image ...<br/><a style='color:#333;text-decoration:none' href='%1'>%1</a>";
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
|
||||
|
||||
SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) :
|
||||
QDialog(parent),
|
||||
_fileName(fileName),
|
||||
_networkAccessManager(NULL)
|
||||
{
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
_ui.setupUi(this);
|
||||
|
||||
QPixmap snaphsotPixmap(fileName);
|
||||
float snapshotRatio = static_cast<float>(snaphsotPixmap.size().width()) / snaphsotPixmap.size().height();
|
||||
|
||||
// narrow snapshot
|
||||
if (snapshotRatio > 1) {
|
||||
setFixedWidth(WIDE_SNAPSHOT_DIALOG_WIDTH);
|
||||
_ui.snapshotWidget->setFixedWidth(WIDE_SNAPSHOT_DIALOG_WIDTH);
|
||||
}
|
||||
|
||||
float labelRatio = static_cast<float>(_ui.snapshotWidget->size().width()) / _ui.snapshotWidget->size().height();
|
||||
|
||||
// set the same aspect ratio of label as of snapshot
|
||||
if (snapshotRatio > labelRatio) {
|
||||
int oldHeight = _ui.snapshotWidget->size().height();
|
||||
_ui.snapshotWidget->setFixedHeight((int) (_ui.snapshotWidget->size().width() / snapshotRatio));
|
||||
|
||||
// if height is less then original, resize the window as well
|
||||
if (_ui.snapshotWidget->size().height() < NARROW_SNAPSHOT_DIALOG_SIZE) {
|
||||
setFixedHeight(size().height() - (oldHeight - _ui.snapshotWidget->size().height()));
|
||||
}
|
||||
} else {
|
||||
_ui.snapshotWidget->setFixedWidth((int) (_ui.snapshotWidget->size().height() * snapshotRatio));
|
||||
}
|
||||
|
||||
_ui.snapshotWidget->setPixmap(snaphsotPixmap);
|
||||
_ui.snapshotWidget->adjustSize();
|
||||
}
|
||||
|
||||
void SnapshotShareDialog::accept() {
|
||||
uploadSnapshot();
|
||||
}
|
||||
|
||||
void SnapshotShareDialog::uploadSnapshot() {
|
||||
|
||||
if (AccountManager::getInstance().getAccountInfo().getDiscourseApiKey().isEmpty()) {
|
||||
QMessageBox::warning(this, "",
|
||||
"Your Discourse API key is missing, you cannot share snapshots. Please try to relog.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_networkAccessManager) {
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
}
|
||||
|
||||
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
|
||||
QHttpPart apiKeyPart;
|
||||
apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"api_key\""));
|
||||
apiKeyPart.setBody(AccountManager::getInstance().getAccountInfo().getDiscourseApiKey().toLatin1());
|
||||
|
||||
QFile* file = new QFile(_fileName);
|
||||
file->open(QIODevice::ReadOnly);
|
||||
|
||||
QHttpPart imagePart;
|
||||
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
|
||||
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QVariant("form-data; name=\"file\"; filename=\"" + file->fileName() +"\""));
|
||||
imagePart.setBodyDevice(file);
|
||||
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
|
||||
|
||||
multiPart->append(apiKeyPart);
|
||||
multiPart->append(imagePart);
|
||||
|
||||
QUrl url(FORUM_UPLOADS_URL);
|
||||
QNetworkRequest request(url);
|
||||
|
||||
QNetworkReply* reply = _networkAccessManager->post(request, multiPart);
|
||||
|
||||
|
||||
connect(reply, &QNetworkReply::finished, this, &SnapshotShareDialog::uploadRequestFinished);
|
||||
|
||||
QEventLoop loop;
|
||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
void SnapshotShareDialog::sendForumPost(QString snapshotPath) {
|
||||
|
||||
if (!_networkAccessManager) {
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
}
|
||||
|
||||
// post to Discourse forum
|
||||
QNetworkRequest request;
|
||||
QUrl forumUrl(FORUM_POST_URL);
|
||||
|
||||
QUrlQuery query;
|
||||
query.addQueryItem("api_key", AccountManager::getInstance().getAccountInfo().getDiscourseApiKey());
|
||||
query.addQueryItem("topic_id", FORUM_REPLY_TO_TOPIC);
|
||||
query.addQueryItem("raw", FORUM_POST_TEMPLATE.arg(snapshotPath, _ui.textEdit->toPlainText()));
|
||||
forumUrl.setQuery(query);
|
||||
|
||||
QByteArray postData = forumUrl.toEncoded(QUrl::RemoveFragment);
|
||||
request.setUrl(forumUrl);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
QNetworkReply* requestReply = _networkAccessManager->post(request, postData);
|
||||
connect(requestReply, &QNetworkReply::finished, this, &SnapshotShareDialog::postRequestFinished);
|
||||
|
||||
QEventLoop loop;
|
||||
connect(requestReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
void SnapshotShareDialog::postRequestFinished() {
|
||||
|
||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||
const QJsonObject& responseObject = jsonResponse.object();
|
||||
|
||||
if (responseObject.contains("id")) {
|
||||
_ui.textEdit->setHtml("");
|
||||
const QString urlTemplate = "%1/t/%2/%3/%4";
|
||||
QString link = urlTemplate.arg(FORUM_URL,
|
||||
responseObject["topic_slug"].toString(),
|
||||
QString::number(responseObject["topic_id"].toDouble()),
|
||||
QString::number(responseObject["post_number"].toDouble()));
|
||||
|
||||
_ui.successLabel->setText(SUCCESS_LABEL_TEMPLATE.arg(link));
|
||||
_ui.successLabel->setFixedHeight(SUCCESS_LABEL_HEIGHT);
|
||||
|
||||
// hide input widgets
|
||||
_ui.shareButton->hide();
|
||||
_ui.textEdit->hide();
|
||||
_ui.labelNotes->hide();
|
||||
|
||||
} else {
|
||||
QString errorMessage(SHARE_DEFAULT_ERROR);
|
||||
if (responseObject.contains("errors")) {
|
||||
QJsonArray errorArray = responseObject["errors"].toArray();
|
||||
if (!errorArray.first().toString().isEmpty()) {
|
||||
errorMessage = errorArray.first().toString();
|
||||
}
|
||||
}
|
||||
QMessageBox::warning(this, "", errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapshotShareDialog::uploadRequestFinished() {
|
||||
|
||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||
const QJsonObject& responseObject = jsonResponse.object();
|
||||
|
||||
if (responseObject.contains("url")) {
|
||||
sendForumPost(responseObject["url"].toString());
|
||||
} else {
|
||||
QMessageBox::warning(this, "", SHARE_DEFAULT_ERROR);
|
||||
}
|
||||
|
||||
delete requestReply;
|
||||
}
|
41
interface/src/ui/SnapshotShareDialog.h
Normal file
41
interface/src/ui/SnapshotShareDialog.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// SnapshotShareDialog.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stojce Slavkovski on 2/16/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_snapshotShareDialog
|
||||
#define hifi_snapshotShareDialog
|
||||
|
||||
#include "ui_shareSnapshot.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
|
||||
class SnapshotShareDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SnapshotShareDialog(QString fileName, QWidget* parent = 0);
|
||||
|
||||
private:
|
||||
QString _fileName;
|
||||
QNetworkAccessManager* _networkAccessManager;
|
||||
Ui_SnapshotShareDialog _ui;
|
||||
|
||||
void uploadSnapshot();
|
||||
void sendForumPost(QString snapshotPath);
|
||||
|
||||
private slots:
|
||||
void uploadRequestFinished();
|
||||
void postRequestFinished();
|
||||
void accept();
|
||||
};
|
||||
|
||||
#endif // hifi_snapshotShareDialog
|
|
@ -88,10 +88,6 @@ padding-top: 3px;</string>
|
|||
<property name="text">
|
||||
<string>Reload all</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../resources/images/reload.svg</normaloff>../resources/images/reload.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="stopAllButton">
|
||||
<property name="geometry">
|
||||
|
@ -115,10 +111,6 @@ padding-top: 3px;</string>
|
|||
<property name="text">
|
||||
<string>Stop all</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../resources/images/stop.svg</normaloff>../resources/images/stop.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="recentlyLoadedLabel">
|
||||
<property name="geometry">
|
||||
|
@ -251,10 +243,6 @@ padding-top: 3px;</string>
|
|||
<property name="text">
|
||||
<string>Load script</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../resources/images/plus.svg</normaloff>../resources/images/plus.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
<zorder>widgetTitle</zorder>
|
||||
<zorder>currentlyRunningLabel</zorder>
|
||||
|
|
377
interface/ui/shareSnapshot.ui
Normal file
377
interface/ui/shareSnapshot.ui
Normal file
|
@ -0,0 +1,377 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SnapshotShareDialog</class>
|
||||
<widget class="QDialog" name="SnapshotShareDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>502</width>
|
||||
<height>616</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>616</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>502</width>
|
||||
<height>616</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Share with Alphas</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgb(255, 255, 255);</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinAndMaxSize</enum>
|
||||
</property>
|
||||
<item alignment="Qt::AlignHCenter|Qt::AlignTop">
|
||||
<widget class="QLabel" name="snapshotWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>2000</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: #ccc;</string>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>9</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="successLabel">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: #666</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextBrowserInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelNotes">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>19</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica</family>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: #666;
|
||||
padding-left:20px;</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Notes about this image</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetFixedSize</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>19</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica</family>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border: 1px solid #c5c5c5;</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="html">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Helvetica'; font-size:14pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>25</width>
|
||||
<height>19</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="shareButton">
|
||||
<property name="styleSheet">
|
||||
<string notr="true"> background-color: #333333;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
border-radius: 9px;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
color: #FFFFFF;
|
||||
width: 120px;
|
||||
height: 50px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Share</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>25</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>11</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>shareButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>SnapshotShareDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>420</x>
|
||||
<y>565</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>250</x>
|
||||
<y>307</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -352,6 +352,8 @@ public:
|
|||
|
||||
AttachmentData();
|
||||
|
||||
bool isValid() const { return modelURL.isValid(); }
|
||||
|
||||
bool operator==(const AttachmentData& other) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ HandData::HandData(AvatarData* owningAvatar) :
|
|||
addNewPalm();
|
||||
}
|
||||
|
||||
glm::vec3 HandData::worldVectorToLeapVector(const glm::vec3& worldVector) const {
|
||||
return glm::inverse(getBaseOrientation()) * worldVector / LEAP_UNIT_SCALE;
|
||||
glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const {
|
||||
return glm::inverse(getBaseOrientation()) * worldVector;
|
||||
}
|
||||
|
||||
PalmData& HandData::addNewPalm() {
|
||||
|
@ -66,69 +66,21 @@ void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex)
|
|||
PalmData::PalmData(HandData* owningHandData) :
|
||||
_rawRotation(0.f, 0.f, 0.f, 1.f),
|
||||
_rawPosition(0.f),
|
||||
_rawNormal(0.f, 1.f, 0.f),
|
||||
_rawVelocity(0.f),
|
||||
_rotationalVelocity(0.f),
|
||||
_totalPenetration(0.f),
|
||||
_controllerButtons(0),
|
||||
_isActive(false),
|
||||
_leapID(LEAPID_INVALID),
|
||||
_sixenseID(SIXENSEID_INVALID),
|
||||
_numFramesWithoutData(0),
|
||||
_owningHandData(owningHandData),
|
||||
_isCollidingWithVoxel(false),
|
||||
_isCollidingWithPalm(false),
|
||||
_collisionlessPaddleExpiry(0)
|
||||
{
|
||||
for (int i = 0; i < NUM_FINGERS_PER_HAND; ++i) {
|
||||
_fingers.push_back(FingerData(this, owningHandData));
|
||||
}
|
||||
_collisionlessPaddleExpiry(0) {
|
||||
}
|
||||
|
||||
void PalmData::addToPosition(const glm::vec3& delta) {
|
||||
// convert to Leap coordinates, then add to palm and finger positions
|
||||
glm::vec3 leapDelta = _owningHandData->worldVectorToLeapVector(delta);
|
||||
_rawPosition += leapDelta;
|
||||
for (size_t i = 0; i < getNumFingers(); i++) {
|
||||
FingerData& finger = _fingers[i];
|
||||
if (finger.isActive()) {
|
||||
finger.setRawTipPosition(finger.getTipRawPosition() + leapDelta);
|
||||
finger.setRawRootPosition(finger.getRootRawPosition() + leapDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FingerData::FingerData(PalmData* owningPalmData, HandData* owningHandData) :
|
||||
_tipRawPosition(0, 0, 0),
|
||||
_rootRawPosition(0, 0, 0),
|
||||
_isActive(false),
|
||||
_leapID(LEAPID_INVALID),
|
||||
_numFramesWithoutData(0),
|
||||
_owningPalmData(owningPalmData),
|
||||
_owningHandData(owningHandData)
|
||||
{
|
||||
const int standardTrailLength = 10;
|
||||
setTrailLength(standardTrailLength);
|
||||
}
|
||||
|
||||
void HandData::setFingerTrailLength(unsigned int length) {
|
||||
for (size_t i = 0; i < getNumPalms(); ++i) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
|
||||
FingerData& finger = palm.getFingers()[f];
|
||||
finger.setTrailLength(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandData::updateFingerTrails() {
|
||||
for (size_t i = 0; i < getNumPalms(); ++i) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
|
||||
FingerData& finger = palm.getFingers()[f];
|
||||
finger.updateTrail();
|
||||
}
|
||||
}
|
||||
_rawPosition += _owningHandData->worldToLocalVector(delta);
|
||||
}
|
||||
|
||||
bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
|
||||
|
@ -157,54 +109,20 @@ glm::vec3 HandData::getBasePosition() const {
|
|||
return _owningAvatarData->getPosition();
|
||||
}
|
||||
|
||||
void FingerData::setTrailLength(unsigned int length) {
|
||||
_tipTrailPositions.resize(length);
|
||||
_tipTrailCurrentStartIndex = 0;
|
||||
_tipTrailCurrentValidLength = 0;
|
||||
glm::vec3 PalmData::getFingerTipPosition() const {
|
||||
glm::vec3 fingerOffset(0.0f, 0.0f, 0.3f);
|
||||
glm::vec3 palmOffset(0.0f, -0.08f, 0.0f);
|
||||
return getPosition() + _owningHandData->localToWorldDirection(_rawRotation * (fingerOffset + palmOffset));
|
||||
}
|
||||
|
||||
void FingerData::updateTrail() {
|
||||
if (_tipTrailPositions.size() == 0)
|
||||
return;
|
||||
|
||||
if (_isActive) {
|
||||
// Add the next point in the trail.
|
||||
_tipTrailCurrentStartIndex--;
|
||||
if (_tipTrailCurrentStartIndex < 0)
|
||||
_tipTrailCurrentStartIndex = _tipTrailPositions.size() - 1;
|
||||
|
||||
_tipTrailPositions[_tipTrailCurrentStartIndex] = getTipPosition();
|
||||
|
||||
if (_tipTrailCurrentValidLength < (int)_tipTrailPositions.size())
|
||||
_tipTrailCurrentValidLength++;
|
||||
}
|
||||
else {
|
||||
// It's not active, so just kill the trail.
|
||||
_tipTrailCurrentValidLength = 0;
|
||||
}
|
||||
glm::vec3 PalmData::getFingerDirection() const {
|
||||
const glm::vec3 LOCAL_FINGER_DIRECTION(0.0f, 0.0f, 1.0f);
|
||||
return _owningHandData->localToWorldDirection(_rawRotation * LOCAL_FINGER_DIRECTION);
|
||||
}
|
||||
|
||||
int FingerData::getTrailNumPositions() {
|
||||
return _tipTrailCurrentValidLength;
|
||||
}
|
||||
|
||||
const glm::vec3& FingerData::getTrailPosition(int index) {
|
||||
if (index >= _tipTrailCurrentValidLength) {
|
||||
static glm::vec3 zero(0,0,0);
|
||||
return zero;
|
||||
}
|
||||
int posIndex = (index + _tipTrailCurrentStartIndex) % _tipTrailCurrentValidLength;
|
||||
return _tipTrailPositions[posIndex];
|
||||
}
|
||||
|
||||
void PalmData::getBallHoldPosition(glm::vec3& position) const {
|
||||
const float BALL_FORWARD_OFFSET = 0.08f; // put the ball a bit forward of fingers
|
||||
position = BALL_FORWARD_OFFSET * getNormal();
|
||||
if (_fingers.size() > 0) {
|
||||
position += _fingers[0].getTipPosition();
|
||||
} else {
|
||||
position += getPosition();
|
||||
}
|
||||
glm::vec3 PalmData::getPalmDirection() const {
|
||||
const glm::vec3 LOCAL_PALM_DIRECTION(0.0f, -1.0f, 0.0f);
|
||||
return _owningHandData->localToWorldDirection(_rawRotation * LOCAL_PALM_DIRECTION);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "SharedUtil.h"
|
||||
|
||||
class AvatarData;
|
||||
class FingerData;
|
||||
class PalmData;
|
||||
|
||||
const int NUM_HANDS = 2;
|
||||
|
@ -31,8 +30,6 @@ const int NUM_FINGERS = NUM_HANDS * NUM_FINGERS_PER_HAND;
|
|||
const int LEAPID_INVALID = -1;
|
||||
const int SIXENSEID_INVALID = -1;
|
||||
|
||||
const float LEAP_UNIT_SCALE = 0.001f; ///< convert mm to meters
|
||||
|
||||
const int SIXENSE_CONTROLLER_ID_LEFT_HAND = 0;
|
||||
const int SIXENSE_CONTROLLER_ID_RIGHT_HAND = 1;
|
||||
|
||||
|
@ -41,17 +38,16 @@ public:
|
|||
HandData(AvatarData* owningAvatar);
|
||||
virtual ~HandData() {}
|
||||
|
||||
// These methods return the positions in Leap-relative space.
|
||||
// To convert to world coordinates, use Hand::leapPositionToWorldPosition.
|
||||
|
||||
// position conversion
|
||||
glm::vec3 leapPositionToWorldPosition(const glm::vec3& leapPosition) {
|
||||
return getBasePosition() + getBaseOrientation() * (leapPosition * LEAP_UNIT_SCALE);
|
||||
glm::vec3 localToWorldPosition(const glm::vec3& localPosition) {
|
||||
return getBasePosition() + getBaseOrientation() * localPosition;
|
||||
}
|
||||
glm::vec3 leapDirectionToWorldDirection(const glm::vec3& leapDirection) {
|
||||
return getBaseOrientation() * leapDirection;
|
||||
}
|
||||
glm::vec3 worldVectorToLeapVector(const glm::vec3& worldVector) const;
|
||||
|
||||
glm::vec3 localToWorldDirection(const glm::vec3& localVector) {
|
||||
return getBaseOrientation() * localVector;
|
||||
}
|
||||
|
||||
glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const;
|
||||
|
||||
std::vector<PalmData>& getPalms() { return _palms; }
|
||||
const std::vector<PalmData>& getPalms() const { return _palms; }
|
||||
|
@ -63,9 +59,6 @@ public:
|
|||
/// both is not found.
|
||||
void getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const;
|
||||
|
||||
void setFingerTrailLength(unsigned int length);
|
||||
void updateFingerTrails();
|
||||
|
||||
/// Checks for penetration between the described sphere and the hand.
|
||||
/// \param penetratorCenter the center of the penetration test sphere
|
||||
/// \param penetratorRadius the radius of the penetration test sphere
|
||||
|
@ -89,71 +82,23 @@ private:
|
|||
HandData& operator= (const HandData&);
|
||||
};
|
||||
|
||||
class FingerData {
|
||||
public:
|
||||
FingerData(PalmData* owningPalmData, HandData* owningHandData);
|
||||
|
||||
glm::vec3 getTipPosition() const { return _owningHandData->leapPositionToWorldPosition(_tipRawPosition); }
|
||||
glm::vec3 getRootPosition() const { return _owningHandData->leapPositionToWorldPosition(_rootRawPosition); }
|
||||
const glm::vec3& getTipRawPosition() const { return _tipRawPosition; }
|
||||
const glm::vec3& getRootRawPosition() const { return _rootRawPosition; }
|
||||
bool isActive() const { return _isActive; }
|
||||
int getLeapID() const { return _leapID; }
|
||||
|
||||
void setActive(bool active) { _isActive = active; }
|
||||
void setLeapID(int id) { _leapID = id; }
|
||||
void setRawTipPosition(const glm::vec3& pos) { _tipRawPosition = pos; }
|
||||
void setRawRootPosition(const glm::vec3& pos) { _rootRawPosition = pos; }
|
||||
|
||||
void setTrailLength(unsigned int length);
|
||||
void updateTrail();
|
||||
|
||||
int getTrailNumPositions();
|
||||
const glm::vec3& getTrailPosition(int index);
|
||||
|
||||
void incrementFramesWithoutData() { _numFramesWithoutData++; }
|
||||
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
|
||||
int getFramesWithoutData() const { return _numFramesWithoutData; }
|
||||
|
||||
private:
|
||||
glm::vec3 _tipRawPosition;
|
||||
glm::vec3 _rootRawPosition;
|
||||
bool _isActive; // This has current valid data
|
||||
int _leapID; // the Leap's serial id for this tracked object
|
||||
int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost.
|
||||
std::vector<glm::vec3> _tipTrailPositions;
|
||||
int _tipTrailCurrentStartIndex;
|
||||
int _tipTrailCurrentValidLength;
|
||||
PalmData* _owningPalmData;
|
||||
HandData* _owningHandData;
|
||||
};
|
||||
|
||||
class PalmData {
|
||||
public:
|
||||
PalmData(HandData* owningHandData);
|
||||
glm::vec3 getPosition() const { return _owningHandData->leapPositionToWorldPosition(_rawPosition); }
|
||||
glm::vec3 getNormal() const { return _owningHandData->leapDirectionToWorldDirection(_rawNormal); }
|
||||
glm::vec3 getVelocity() const { return _owningHandData->leapDirectionToWorldDirection(_rawVelocity); }
|
||||
glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); }
|
||||
glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); }
|
||||
|
||||
const glm::vec3& getRawPosition() const { return _rawPosition; }
|
||||
const glm::vec3& getRawNormal() const { return _rawNormal; }
|
||||
bool isActive() const { return _isActive; }
|
||||
int getLeapID() const { return _leapID; }
|
||||
int getSixenseID() const { return _sixenseID; }
|
||||
|
||||
|
||||
std::vector<FingerData>& getFingers() { return _fingers; }
|
||||
const std::vector<FingerData>& getFingers() const { return _fingers; }
|
||||
size_t getNumFingers() const { return _fingers.size(); }
|
||||
|
||||
void setActive(bool active) { _isActive = active; }
|
||||
void setLeapID(int id) { _leapID = id; }
|
||||
void setSixenseID(int id) { _sixenseID = id; }
|
||||
|
||||
void setRawRotation(const glm::quat rawRotation) { _rawRotation = rawRotation; };
|
||||
glm::quat getRawRotation() const { return _rawRotation; }
|
||||
void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
|
||||
void setRawNormal(const glm::vec3& normal) { _rawNormal = normal; }
|
||||
void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }
|
||||
const glm::vec3& getRawVelocity() const { return _rawVelocity; }
|
||||
void addToPosition(const glm::vec3& delta);
|
||||
|
@ -162,11 +107,11 @@ public:
|
|||
void resolvePenetrations() { addToPosition(-_totalPenetration); _totalPenetration = glm::vec3(0.f); }
|
||||
|
||||
void setTipPosition(const glm::vec3& position) { _tipPosition = position; }
|
||||
const glm::vec3 getTipPosition() const { return _owningHandData->leapPositionToWorldPosition(_tipPosition); }
|
||||
const glm::vec3 getTipPosition() const { return _owningHandData->localToWorldPosition(_tipPosition); }
|
||||
const glm::vec3& getTipRawPosition() const { return _tipPosition; }
|
||||
|
||||
void setTipVelocity(const glm::vec3& velocity) { _tipVelocity = velocity; }
|
||||
const glm::vec3 getTipVelocity() const { return _owningHandData->leapDirectionToWorldDirection(_tipVelocity); }
|
||||
const glm::vec3 getTipVelocity() const { return _owningHandData->localToWorldDirection(_tipVelocity); }
|
||||
const glm::vec3& getTipRawVelocity() const { return _tipVelocity; }
|
||||
|
||||
void incrementFramesWithoutData() { _numFramesWithoutData++; }
|
||||
|
@ -198,11 +143,14 @@ public:
|
|||
/// Store position where the palm holds the ball.
|
||||
void getBallHoldPosition(glm::vec3& position) const;
|
||||
|
||||
// return world-frame:
|
||||
glm::vec3 getFingerTipPosition() const;
|
||||
glm::vec3 getFingerDirection() const;
|
||||
glm::vec3 getPalmDirection() const;
|
||||
|
||||
private:
|
||||
std::vector<FingerData> _fingers;
|
||||
glm::quat _rawRotation;
|
||||
glm::vec3 _rawPosition;
|
||||
glm::vec3 _rawNormal;
|
||||
glm::vec3 _rawVelocity;
|
||||
glm::vec3 _rotationalVelocity;
|
||||
glm::quat _lastRotation;
|
||||
|
@ -216,7 +164,6 @@ private:
|
|||
float _joystickX, _joystickY;
|
||||
|
||||
bool _isActive; // This has current valid data
|
||||
int _leapID; // the Leap's serial id for this tracked object
|
||||
int _sixenseID; // Sixense controller ID for this palm
|
||||
int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost.
|
||||
HandData* _owningHandData;
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
DataServerAccountInfo::DataServerAccountInfo() :
|
||||
_accessToken(),
|
||||
_username(),
|
||||
_xmppPassword()
|
||||
_xmppPassword(),
|
||||
_discourseApiKey()
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -29,12 +30,14 @@ DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) :
|
|||
QJsonObject userJSONObject = jsonObject["user"].toObject();
|
||||
setUsername(userJSONObject["username"].toString());
|
||||
setXMPPPassword(userJSONObject["xmpp_password"].toString());
|
||||
setDiscourseApiKey(userJSONObject["discourse_api_key"].toString());
|
||||
}
|
||||
|
||||
DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) {
|
||||
_accessToken = otherInfo._accessToken;
|
||||
_username = otherInfo._username;
|
||||
_xmppPassword = otherInfo._xmppPassword;
|
||||
_discourseApiKey = otherInfo._discourseApiKey;
|
||||
}
|
||||
|
||||
DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) {
|
||||
|
@ -49,6 +52,7 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
|
|||
swap(_accessToken, otherInfo._accessToken);
|
||||
swap(_username, otherInfo._username);
|
||||
swap(_xmppPassword, otherInfo._xmppPassword);
|
||||
swap(_discourseApiKey, otherInfo._discourseApiKey);
|
||||
}
|
||||
|
||||
void DataServerAccountInfo::setUsername(const QString& username) {
|
||||
|
@ -65,6 +69,12 @@ void DataServerAccountInfo::setXMPPPassword(const QString& xmppPassword) {
|
|||
}
|
||||
}
|
||||
|
||||
void DataServerAccountInfo::setDiscourseApiKey(const QString& discourseApiKey) {
|
||||
if (_discourseApiKey != discourseApiKey) {
|
||||
_discourseApiKey = discourseApiKey;
|
||||
}
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {
|
||||
out << info._accessToken << info._username << info._xmppPassword;
|
||||
return out;
|
||||
|
|
|
@ -31,7 +31,10 @@ public:
|
|||
|
||||
const QString& getXMPPPassword() const { return _xmppPassword; }
|
||||
void setXMPPPassword(const QString& xmppPassword);
|
||||
|
||||
|
||||
const QString& getDiscourseApiKey() const { return _discourseApiKey; }
|
||||
void setDiscourseApiKey(const QString& discourseApiKey);
|
||||
|
||||
friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info);
|
||||
friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info);
|
||||
private:
|
||||
|
@ -40,6 +43,7 @@ private:
|
|||
OAuthAccessToken _accessToken;
|
||||
QString _username;
|
||||
QString _xmppPassword;
|
||||
QString _discourseApiKey;
|
||||
};
|
||||
|
||||
#endif // hifi_DataServerAccountInfo_h
|
||||
|
|
|
@ -30,6 +30,13 @@ ResourceCache::~ResourceCache() {
|
|||
}
|
||||
}
|
||||
|
||||
void ResourceCache::refresh(const QUrl& url) {
|
||||
QSharedPointer<Resource> resource = _resources.value(url);
|
||||
if (!resource.isNull()) {
|
||||
resource->refresh();
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback, bool delayLoad, void* extra) {
|
||||
if (!url.isValid() && !url.isEmpty() && fallback.isValid()) {
|
||||
return getResource(fallback, QUrl(), delayLoad);
|
||||
|
@ -107,25 +114,15 @@ QList<Resource*> ResourceCache::_loadingRequests;
|
|||
Resource::Resource(const QUrl& url, bool delayLoad) :
|
||||
_url(url),
|
||||
_request(url),
|
||||
_startedLoading(false),
|
||||
_failedToLoad(false),
|
||||
_loaded(false),
|
||||
_lruKey(0),
|
||||
_reply(NULL),
|
||||
_attempts(0) {
|
||||
_reply(NULL) {
|
||||
|
||||
init();
|
||||
|
||||
if (url.isEmpty()) {
|
||||
_startedLoading = _loaded = true;
|
||||
return;
|
||||
|
||||
} else if (!(url.isValid() && ResourceCache::getNetworkAccessManager())) {
|
||||
_startedLoading = _failedToLoad = true;
|
||||
return;
|
||||
}
|
||||
_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
|
||||
// start loading immediately unless instructed otherwise
|
||||
if (!delayLoad) {
|
||||
if (!(_startedLoading || delayLoad)) {
|
||||
attemptRequest();
|
||||
}
|
||||
}
|
||||
|
@ -178,6 +175,22 @@ float Resource::getLoadPriority() {
|
|||
return highestPriority;
|
||||
}
|
||||
|
||||
void Resource::refresh() {
|
||||
if (_reply == NULL && !(_loaded || _failedToLoad)) {
|
||||
return;
|
||||
}
|
||||
if (_reply) {
|
||||
ResourceCache::requestCompleted(this);
|
||||
delete _reply;
|
||||
_reply = NULL;
|
||||
}
|
||||
init();
|
||||
_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
|
||||
if (!_startedLoading) {
|
||||
attemptRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void Resource::allReferencesCleared() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "allReferencesCleared");
|
||||
|
@ -197,6 +210,20 @@ void Resource::allReferencesCleared() {
|
|||
}
|
||||
}
|
||||
|
||||
void Resource::init() {
|
||||
_startedLoading = false;
|
||||
_failedToLoad = false;
|
||||
_loaded = false;
|
||||
_attempts = 0;
|
||||
|
||||
if (_url.isEmpty()) {
|
||||
_startedLoading = _loaded = true;
|
||||
|
||||
} else if (!(_url.isValid() && ResourceCache::getNetworkAccessManager())) {
|
||||
_startedLoading = _failedToLoad = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Resource::attemptRequest() {
|
||||
_startedLoading = true;
|
||||
ResourceCache::attemptRequest(this);
|
||||
|
|
|
@ -47,6 +47,8 @@ public:
|
|||
ResourceCache(QObject* parent = NULL);
|
||||
virtual ~ResourceCache();
|
||||
|
||||
void refresh(const QUrl& url);
|
||||
|
||||
protected:
|
||||
|
||||
QMap<int, QSharedPointer<Resource> > _unusedResources;
|
||||
|
@ -119,6 +121,9 @@ public:
|
|||
/// For loading resources, returns the load progress.
|
||||
float getProgress() const { return (_bytesTotal == 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; }
|
||||
|
||||
/// Refreshes the resource.
|
||||
void refresh();
|
||||
|
||||
void setSelf(const QWeakPointer<Resource>& self) { _self = self; }
|
||||
|
||||
void setCache(ResourceCache* cache) { _cache = cache; }
|
||||
|
@ -131,6 +136,8 @@ protected slots:
|
|||
|
||||
protected:
|
||||
|
||||
virtual void init();
|
||||
|
||||
/// Called when the download has finished. The recipient should delete the reply when done with it.
|
||||
virtual void downloadFinished(QNetworkReply* reply) = 0;
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ static const float SQUARE_ROOT_OF_3 = (float)sqrt(3.f);
|
|||
static const float METERS_PER_DECIMETER = 0.1f;
|
||||
static const float METERS_PER_CENTIMETER = 0.01f;
|
||||
static const float METERS_PER_MILLIMETER = 0.001f;
|
||||
static const float MILLIMETERS_PER_METER = 1000.0f;
|
||||
static const quint64 USECS_PER_MSEC = 1000;
|
||||
static const quint64 MSECS_PER_SECOND = 1000;
|
||||
static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
|
||||
|
|
Loading…
Reference in a new issue