From 5f73b76be755175c766e709e6965fe4e4b651b6e Mon Sep 17 00:00:00 2001 From: stojce Date: Sun, 2 Mar 2014 14:36:01 +0100 Subject: [PATCH 001/108] Share dialog --- interface/src/ui/Snapshot.cpp | 3 + interface/src/ui/SnapshotShareDialog.cpp | 13 ++++ interface/src/ui/SnapshotShareDialog.h | 14 ++++ interface/ui/shareSnapshot.ui | 99 ++++++++++++++++++++++++ snapshotShareDialog.cpp | 9 +++ snapshotShareDialog.h | 14 ++++ 6 files changed, 152 insertions(+) create mode 100644 interface/src/ui/SnapshotShareDialog.cpp create mode 100644 interface/src/ui/SnapshotShareDialog.h create mode 100644 interface/ui/shareSnapshot.ui create mode 100644 snapshotShareDialog.cpp create mode 100644 snapshotShareDialog.h diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index f0fef33cee..89b0d56a0b 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -9,6 +9,7 @@ #include "Snapshot.h" #include +#include #include #include @@ -41,6 +42,8 @@ void Snapshot::saveSnapshot(QGLWidget* widget, QString username, glm::vec3 locat QString fileName = FileUtils::standardPath(SNAPSHOTS_DIRECTORY); fileName.append(QString(FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation))); shot.save(fileName, 0, 100); + + } diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp new file mode 100644 index 0000000000..76556e0222 --- /dev/null +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -0,0 +1,13 @@ +// +// snapshotShareDialog.cpp +// hifi +// +// Created by Stojce Slavkovski on 2/16/14. +// +// + +#include "snapshotShareDialog.h" + +#include "ui_shareSnapshot.h" + + diff --git a/interface/src/ui/SnapshotShareDialog.h b/interface/src/ui/SnapshotShareDialog.h new file mode 100644 index 0000000000..77e92216fd --- /dev/null +++ b/interface/src/ui/SnapshotShareDialog.h @@ -0,0 +1,14 @@ +// +// snapshotShareDialog.h +// hifi +// +// Created by Stojce Slavkovski on 2/16/14. +// +// + +#ifndef __hifi__snapshotShareDialog__ +#define __hifi__snapshotShareDialog__ + +#include + +#endif /* defined(__hifi__snapshotShareDialog__) */ diff --git a/interface/ui/shareSnapshot.ui b/interface/ui/shareSnapshot.ui new file mode 100644 index 0000000000..94c1b4220e --- /dev/null +++ b/interface/ui/shareSnapshot.ui @@ -0,0 +1,99 @@ + + + Dialog + + + Qt::NonModal + + + + 0 + 0 + 808 + 577 + + + + PointingHandCursor + + + Share with community + + + background-color: rgb(255, 255, 255); + + + + + -1 + -1 + 681 + 511 + + + + + + + + 0 + 0 + + + + background-color: #000; + + + + + + + + 0 + 0 + + + + Notes about this image + + + + + + + + + + 0 + 0 + + + + + + + + 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; + + + Share + + + + + + + + + + + diff --git a/snapshotShareDialog.cpp b/snapshotShareDialog.cpp new file mode 100644 index 0000000000..53a445292f --- /dev/null +++ b/snapshotShareDialog.cpp @@ -0,0 +1,9 @@ +// +// snapshotShareDialog.cpp +// hifi +// +// Created by Stojce Slavkovski on 2/16/14. +// +// + +#include "snapshotShareDialog.h" diff --git a/snapshotShareDialog.h b/snapshotShareDialog.h new file mode 100644 index 0000000000..77e92216fd --- /dev/null +++ b/snapshotShareDialog.h @@ -0,0 +1,14 @@ +// +// snapshotShareDialog.h +// hifi +// +// Created by Stojce Slavkovski on 2/16/14. +// +// + +#ifndef __hifi__snapshotShareDialog__ +#define __hifi__snapshotShareDialog__ + +#include + +#endif /* defined(__hifi__snapshotShareDialog__) */ From e2eb34b6c997bb428004a56f12930b80d7d6645a Mon Sep 17 00:00:00 2001 From: stojce Date: Sun, 23 Mar 2014 07:03:20 +0100 Subject: [PATCH 002/108] Layout setup image ratio fixed image centre position --- interface/interface_en.ts | 33 +++- interface/src/Application.cpp | 7 +- interface/src/Application.h | 2 + interface/src/ui/Snapshot.cpp | 5 +- interface/src/ui/Snapshot.h | 2 +- interface/src/ui/SnapshotShareDialog.cpp | 23 ++- interface/src/ui/SnapshotShareDialog.h | 15 +- interface/ui/shareSnapshot.ui | 187 +++++++++++++++++++++-- 8 files changed, 247 insertions(+), 27 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 34e3614716..f8de770fca 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file @@ -158,4 +158,25 @@ + + SnapshotShareDialog + + + + Share with community + + + + + + Notes about this image + + + + + + Share + + + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9563cbf3b4..e819422d46 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3671,5 +3671,10 @@ void Application::takeSnapshot() { player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath())); player->play(); - Snapshot::saveSnapshot(_glWidget, _myAvatar); + QString fileName = Snapshot::saveSnapshot(_glWidget, _myAvatar); + + if (!_snapshotShareDialog) { + _snapshotShareDialog = new SnapshotShareDialog(fileName, _glWidget); + } + _snapshotShareDialog->exec(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 28060113a9..bc401c7de5 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -66,6 +66,7 @@ #include "ui/BandwidthDialog.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" @@ -473,6 +474,7 @@ private: std::vector _voxelFades; ControllerScriptingInterface _controllerScriptingInterface; QPointer _logDialog; + QPointer _snapshotShareDialog; FileLogger* _logger; diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 998aef82e4..90a226c594 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -11,7 +11,6 @@ #include #include -#include #include "Snapshot.h" @@ -61,7 +60,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(); @@ -93,7 +92,7 @@ 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; } diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 1b78e8328e..83cfe5bc85 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -38,7 +38,7 @@ private: class Snapshot { public: - static void saveSnapshot(QGLWidget* widget, Avatar* avatar); + static QString saveSnapshot(QGLWidget* widget, Avatar* avatar); static SnapshotMetaData* parseSnapshotData(QString snapshotPath); }; diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp index 76556e0222..2af564ff0b 100644 --- a/interface/src/ui/SnapshotShareDialog.cpp +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -1,13 +1,30 @@ // -// snapshotShareDialog.cpp +// SnapshotShareDialog.cpp // hifi // // Created by Stojce Slavkovski on 2/16/14. // // -#include "snapshotShareDialog.h" +#include "SnapshotShareDialog.h" -#include "ui_shareSnapshot.h" +SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : QDialog(parent), _fileName(fileName) { + setAttribute(Qt::WA_DeleteOnClose); + ui.setupUi(this); + + QPixmap snaphsotPixmap(fileName); + float snapshotRatio = static_cast(snaphsotPixmap.size().width()) / snaphsotPixmap.size().height(); + float labelRatio = static_cast(ui.snapshotWidget->size().width()) / ui.snapshotWidget->size().height(); + + // set the same aspect ratio of label as of snapshot + if (snapshotRatio > labelRatio) { + ui.snapshotWidget->setFixedHeight(ui.snapshotWidget->size().width() / snapshotRatio); + } else { + ui.snapshotWidget->setFixedWidth(ui.snapshotWidget->size().height() * snapshotRatio); + } + + ui.snapshotWidget->setPixmap(snaphsotPixmap); + ui.snapshotWidget->adjustSize(); +} diff --git a/interface/src/ui/SnapshotShareDialog.h b/interface/src/ui/SnapshotShareDialog.h index 77e92216fd..077acd182b 100644 --- a/interface/src/ui/SnapshotShareDialog.h +++ b/interface/src/ui/SnapshotShareDialog.h @@ -1,5 +1,5 @@ // -// snapshotShareDialog.h +// SnapshotShareDialog.h // hifi // // Created by Stojce Slavkovski on 2/16/14. @@ -9,6 +9,17 @@ #ifndef __hifi__snapshotShareDialog__ #define __hifi__snapshotShareDialog__ -#include +#include "ui_shareSnapshot.h" + +class SnapshotShareDialog : public QDialog { + Q_OBJECT + +public: + SnapshotShareDialog(QString fileName, QWidget* parent = 0); + +private: + QString _fileName; + Ui_SnapshotShareDialog ui; +}; #endif /* defined(__hifi__snapshotShareDialog__) */ diff --git a/interface/ui/shareSnapshot.ui b/interface/ui/shareSnapshot.ui index 94c1b4220e..039e611115 100644 --- a/interface/ui/shareSnapshot.ui +++ b/interface/ui/shareSnapshot.ui @@ -1,7 +1,7 @@ - Dialog - + SnapshotShareDialog + Qt::NonModal @@ -9,10 +9,28 @@ 0 0 - 808 - 577 + 789 + 645 + + + 0 + 0 + + + + + 789 + 645 + + + + + 789 + 645 + + PointingHandCursor @@ -22,33 +40,149 @@ background-color: rgb(255, 255, 255); + + true + -1 -1 - 681 - 511 + 792 + 645 - + - + 0 0 - - background-color: #000; + + + 790 + 510 + + + background-color: #333; + + + QFrame::StyledPanel + + + QFrame::Raised + + + 0 + + + + + -1 + 1 + 791 + 512 + + + + + 0 + + + + + + 0 + 0 + + + + + 790 + 510 + + + + background-color: #ccc; + + + 0 + + + true + + + Qt::AlignCenter + + + 0 + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + - + 0 0 @@ -56,10 +190,25 @@ Notes about this image + + 0 + + + 20 + + + 0 + + + 20 + + + 20 + @@ -70,6 +219,22 @@ + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + From 0a0515b652d359bc833faacecea08ce3b42aa330 Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 24 Mar 2014 20:35:28 +0100 Subject: [PATCH 003/108] layout / font changes --- interface/interface_en.ts | 10 +++++----- interface/ui/shareSnapshot.ui | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index f8de770fca..ce777b8551 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -162,19 +162,19 @@ SnapshotShareDialog - + Share with community - - + + Notes about this image - - + + Share diff --git a/interface/ui/shareSnapshot.ui b/interface/ui/shareSnapshot.ui index 039e611115..3d729cf9df 100644 --- a/interface/ui/shareSnapshot.ui +++ b/interface/ui/shareSnapshot.ui @@ -53,6 +53,9 @@ + + 0 + @@ -68,13 +71,13 @@ - background-color: #333; + background-color: #000 - QFrame::StyledPanel + QFrame::NoFrame - QFrame::Raised + QFrame::Plain 0 @@ -84,7 +87,7 @@ -1 1 - 791 + 792 512 @@ -187,9 +190,28 @@ 0 + + + 0 + 30 + + + + + Helvetica + 75 + true + + + + color: #666; + Notes about this image + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + 0 @@ -217,6 +239,11 @@ 0 + + + Helvetica + + From 63a29cad0a591c1c6598dd6520f4b2788ffcfb0c Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 24 Mar 2014 20:39:13 +0100 Subject: [PATCH 004/108] layout fixes --- interface/interface_en.ts | 10 +++--- interface/ui/shareSnapshot.ui | 68 ++++++++--------------------------- 2 files changed, 19 insertions(+), 59 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index ce777b8551..2d2f4c1c07 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -162,19 +162,19 @@ SnapshotShareDialog - + Share with community - - + + Notes about this image - - + + Share diff --git a/interface/ui/shareSnapshot.ui b/interface/ui/shareSnapshot.ui index 3d729cf9df..85334cbc65 100644 --- a/interface/ui/shareSnapshot.ui +++ b/interface/ui/shareSnapshot.ui @@ -59,7 +59,7 @@ - + 0 0 @@ -70,6 +70,12 @@ 510 + + + 790 + 510 + + background-color: #000 @@ -95,7 +101,7 @@ 0 - + @@ -109,6 +115,12 @@ 510 + + + 790 + 510 + + background-color: #ccc; @@ -126,58 +138,6 @@ - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - From e5a66ddb90909210635a1a4596e10fafdcf1abd2 Mon Sep 17 00:00:00 2001 From: stojce Date: Tue, 25 Mar 2014 21:56:20 +0100 Subject: [PATCH 005/108] New layout behaviour --- interface/interface_en.ts | 21 ++ interface/src/ui/SnapshotShareDialog.cpp | 16 + interface/ui/shareSnapshot.ui | 374 +++++++++++------------ 3 files changed, 214 insertions(+), 197 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index beada5df43..0a5b7e4057 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -158,4 +158,25 @@ + + SnapshotShareDialog + + + + Share with community + + + + + + Notes about this image + + + + + + Share + + + diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp index 2af564ff0b..d57951a484 100644 --- a/interface/src/ui/SnapshotShareDialog.cpp +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -6,6 +6,9 @@ // // +const int NARROW_SNAPSHOT_DIALOG_SIZE = 500; +const int WIDE_SNAPSHOT_DIALOG_WIDTH = 650; + #include "SnapshotShareDialog.h" SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : QDialog(parent), _fileName(fileName) { @@ -16,11 +19,24 @@ SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : QD QPixmap snaphsotPixmap(fileName); float snapshotRatio = static_cast(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(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(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(ui.snapshotWidget->size().height() * snapshotRatio); } diff --git a/interface/ui/shareSnapshot.ui b/interface/ui/shareSnapshot.ui index 85334cbc65..cb8754a46d 100644 --- a/interface/ui/shareSnapshot.ui +++ b/interface/ui/shareSnapshot.ui @@ -9,26 +9,26 @@ 0 0 - 789 - 645 + 502 + 625 - + 0 0 - 789 - 645 + 500 + 625 - 789 - 645 + 502 + 625 @@ -43,189 +43,168 @@ true - - - - -1 - -1 - 792 - 645 - + + + 0 - - - 0 - - - - - - 0 - 0 - - - - - 790 - 510 - - - - - 790 - 510 - - - - background-color: #000 - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - - -1 - 1 - 792 - 512 - + + 0 + + + 0 + + + 0 + + + 0 + + + + + QLayout::SetMinAndMaxSize + + + + + + 0 + 0 + + + + + 500 + 500 + + + + + 30 + 2000 + + + + background-color: #ccc; + + + 0 + + + true + + + Qt::AlignCenter + + + 0 - - - 0 - - - - - - 0 - 0 - - - - - 790 - 510 - - - - - 790 - 510 - - - - background-color: #ccc; - - - 0 - - - true - - - Qt::AlignCenter - - - 0 - - - - - - - - - - - 0 - 0 - - - - - 0 - 30 - - - - - Helvetica - 75 - true - - - - color: #666; - - - Notes about this image - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - 0 - - - 20 - - - - - - - 0 - - - 20 - - - 20 - - - - - - 0 - 0 - - - - - Helvetica - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - background-color: #333333; + + + + + + 0 + 0 + + + + + 0 + 30 + + + + + Helvetica + 75 + true + + + + color: #666; +padding-left:20px; + + + 0 + + + Notes about this image + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + 0 + + + 0 + + + + + + + 0 + + + QLayout::SetFixedSize + + + 20 + + + 20 + + + + + + 0 + 0 + + + + + 16777215 + 60 + + + + + Helvetica + 14 + + + + QFrame::Box + + + QFrame::Plain + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + background-color: #333333; border-width: 0; border-radius: 9px; border-radius: 9px; @@ -235,16 +214,17 @@ color: #FFFFFF; width: 120px; height: 50px; - - - Share - - - - - - - + + + Share + + + + + + + + From b223d10e700d40a53455196fc1b6cca4d87ca23d Mon Sep 17 00:00:00 2001 From: stojce Date: Wed, 26 Mar 2014 21:24:18 +0100 Subject: [PATCH 006/108] Layout fixes --- interface/interface_en.ts | 39 ++++++++----- interface/ui/shareSnapshot.ui | 106 ++++++++++++++++++++++++++++++---- 2 files changed, 120 insertions(+), 25 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 39e9142400..6baf4e7054 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file @@ -162,19 +162,30 @@ SnapshotShareDialog - - Share with community + + Share with Alphas + Share with community - - + + Notes about this image - - + + + <!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> + + + + + Share diff --git a/interface/ui/shareSnapshot.ui b/interface/ui/shareSnapshot.ui index cb8754a46d..988acfcd56 100644 --- a/interface/ui/shareSnapshot.ui +++ b/interface/ui/shareSnapshot.ui @@ -10,7 +10,7 @@ 0 0 502 - 625 + 616 @@ -22,20 +22,20 @@ 500 - 625 + 616 502 - 625 + 616 PointingHandCursor - Share with community + Share with Alphas background-color: rgb(255, 255, 255); @@ -61,6 +61,9 @@ + + 0 + QLayout::SetMinAndMaxSize @@ -101,10 +104,26 @@ + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 9 + + + + - + 0 0 @@ -112,12 +131,13 @@ 0 - 30 + 19 Helvetica + 14 75 true @@ -126,6 +146,9 @@ color: #666; padding-left:20px; + + QFrame::NoFrame + 0 @@ -152,11 +175,27 @@ padding-left:20px; QLayout::SetFixedSize - 20 + 0 - 20 + 0 + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 19 + 20 + + + + @@ -177,12 +216,25 @@ padding-left:20px; 14 + + border-color: #c00 + - QFrame::Box + QFrame::StyledPanel QFrame::Plain + + 1 + + + <!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> + @@ -195,8 +247,8 @@ padding-left:20px; - 20 - 20 + 25 + 19 @@ -220,8 +272,40 @@ padding-left:20px; + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 25 + 20 + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 11 + + + + From 9f05d71796d47f0899ef2f81cbfbdea7fecc7ecd Mon Sep 17 00:00:00 2001 From: stojce Date: Wed, 26 Mar 2014 21:30:02 +0100 Subject: [PATCH 007/108] fixed QTextEdit border color --- interface/ui/shareSnapshot.ui | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/ui/shareSnapshot.ui b/interface/ui/shareSnapshot.ui index 988acfcd56..19857bfb28 100644 --- a/interface/ui/shareSnapshot.ui +++ b/interface/ui/shareSnapshot.ui @@ -217,16 +217,16 @@ padding-left:20px; - border-color: #c00 + border: 1px solid #c5c5c5; - QFrame::StyledPanel + QFrame::NoFrame QFrame::Plain - 1 + 0 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> From d90e15ad5801928bd788460f0ea5e4194ddb1407 Mon Sep 17 00:00:00 2001 From: stojce Date: Thu, 27 Mar 2014 17:56:42 +0100 Subject: [PATCH 008/108] Don't show share dialog for anonymous users --- interface/src/Application.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 42c8efb952..1e0976c021 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3715,6 +3715,11 @@ void Application::takeSnapshot() { QString fileName = Snapshot::saveSnapshot(_glWidget, _myAvatar); + AccountManager& accountManager = AccountManager::getInstance(); + if (!accountManager.isLoggedIn()) { + return; + } + if (!_snapshotShareDialog) { _snapshotShareDialog = new SnapshotShareDialog(fileName, _glWidget); } From d3fecd7a31b503c9bf820b1f3c03cb2ff5eb932b Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Fri, 2 May 2014 22:16:19 +0200 Subject: [PATCH 009/108] store discourse api key --- interface/src/ui/SnapshotShareDialog.cpp | 6 ++++++ interface/src/ui/SnapshotShareDialog.h | 3 +++ interface/ui/shareSnapshot.ui | 19 ++++++++++++++++++- .../networking/src/DataServerAccountInfo.cpp | 10 +++++++++- .../networking/src/DataServerAccountInfo.h | 6 +++++- 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp index d57951a484..081d3518b2 100644 --- a/interface/src/ui/SnapshotShareDialog.cpp +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -44,3 +44,9 @@ SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : QD ui.snapshotWidget->setPixmap(snaphsotPixmap); ui.snapshotWidget->adjustSize(); } + +void SnapshotShareDialog::accept() { + // post to Discourse forum + + close(); +} diff --git a/interface/src/ui/SnapshotShareDialog.h b/interface/src/ui/SnapshotShareDialog.h index 077acd182b..2823c8b78d 100644 --- a/interface/src/ui/SnapshotShareDialog.h +++ b/interface/src/ui/SnapshotShareDialog.h @@ -20,6 +20,9 @@ public: private: QString _fileName; Ui_SnapshotShareDialog ui; + +private slots: + void accept(); }; #endif /* defined(__hifi__snapshotShareDialog__) */ diff --git a/interface/ui/shareSnapshot.ui b/interface/ui/shareSnapshot.ui index 19857bfb28..0f59349442 100644 --- a/interface/ui/shareSnapshot.ui +++ b/interface/ui/shareSnapshot.ui @@ -311,5 +311,22 @@ p, li { white-space: pre-wrap; } - + + + shareButton + clicked() + SnapshotShareDialog + accept() + + + 420 + 565 + + + 250 + 307 + + + + diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 87d3b694a7..d7cbd91e78 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -16,7 +16,8 @@ DataServerAccountInfo::DataServerAccountInfo() : _accessToken(), _username(), - _xmppPassword() + _xmppPassword(), + _discourseApiKey() { } @@ -29,6 +30,7 @@ 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) { @@ -65,6 +67,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; diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 21380c0855..a7d1fa9cb0 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -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 From c400630bc440e13171ea06397b0bc9660bf9761a Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sat, 3 May 2014 21:12:20 +0200 Subject: [PATCH 010/108] make post --- interface/src/ui/SnapshotShareDialog.cpp | 60 +++++++++++++++++-- interface/src/ui/SnapshotShareDialog.h | 6 ++ .../networking/src/DataServerAccountInfo.cpp | 2 + 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp index 081d3518b2..921400ba1e 100644 --- a/interface/src/ui/SnapshotShareDialog.cpp +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -6,10 +6,17 @@ // // +#include "SnapshotShareDialog.h" +#include "AccountManager.h" + +#include +#include +#include +#include + const int NARROW_SNAPSHOT_DIALOG_SIZE = 500; const int WIDE_SNAPSHOT_DIALOG_WIDTH = 650; - -#include "SnapshotShareDialog.h" +const QString FORUM_POST_URL = "http://localhost:4000"; SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : QDialog(parent), _fileName(fileName) { @@ -46,7 +53,52 @@ SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : QD } void SnapshotShareDialog::accept() { - // post to Discourse forum - + close(); + + // post to Discourse forum +// AccountManager& accountManager = AccountManager::getInstance(); + QNetworkAccessManager* _networkAccessManager = NULL; + + if (!_networkAccessManager) { + _networkAccessManager = new QNetworkAccessManager(this); + } + + QNetworkRequest request; + + QUrl grantURL(FORUM_POST_URL); + grantURL.setPath("/posts"); + + + QByteArray postData; + // postData.append("api_key=" + accountManager.getAccountInfo().getDiscourseApiKey() + "&"); + postData.append("api_key=9168f53930b2fc69ec278414d6ff04fed723ef717867a25954143150d3e2dfe8&"); + postData.append("topic_id=64&"); + postData.append("raw=" + QUrl::toPercentEncoding(ui.textEdit->toPlainText())); + + request.setUrl(grantURL); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + + QNetworkReply* requestReply = _networkAccessManager->post(request, postData); + connect(_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(serviceRequestFinished(QNetworkReply*))); + + connect(requestReply, &QNetworkReply::finished, this, &SnapshotShareDialog::requestFinished); + connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); } + +void SnapshotShareDialog::serviceRequestFinished(QNetworkReply* reply) { + qDebug() << reply->errorString(); +} + +void SnapshotShareDialog::requestFinished() { + + QNetworkReply* requestReply = reinterpret_cast(sender()); + + qDebug() << requestReply->errorString(); + delete requestReply; +} + +void SnapshotShareDialog::requestError(QNetworkReply::NetworkError error) { + // TODO: error handling + qDebug() << "AccountManager requestError - " << error; +} \ No newline at end of file diff --git a/interface/src/ui/SnapshotShareDialog.h b/interface/src/ui/SnapshotShareDialog.h index 2823c8b78d..18a66616db 100644 --- a/interface/src/ui/SnapshotShareDialog.h +++ b/interface/src/ui/SnapshotShareDialog.h @@ -10,6 +10,7 @@ #define __hifi__snapshotShareDialog__ #include "ui_shareSnapshot.h" +#include class SnapshotShareDialog : public QDialog { Q_OBJECT @@ -21,6 +22,11 @@ private: QString _fileName; Ui_SnapshotShareDialog ui; +public slots: + void requestFinished(); + void requestError(QNetworkReply::NetworkError error); + void serviceRequestFinished(QNetworkReply* reply); + private slots: void accept(); }; diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index d7cbd91e78..a9522148a8 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -37,6 +37,7 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI _accessToken = otherInfo._accessToken; _username = otherInfo._username; _xmppPassword = otherInfo._xmppPassword; + _discourseApiKey = otherInfo._discourseApiKey; } DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) { @@ -51,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) { From d93480458f0941281db882ee63db2f0b7fa461bd Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sun, 4 May 2014 18:30:58 +0200 Subject: [PATCH 011/108] Posting Image to forum --- interface/src/Application.cpp | 2 +- interface/src/ui/SnapshotShareDialog.cpp | 125 ++++++++++++++++------- interface/src/ui/SnapshotShareDialog.h | 23 +++-- 3 files changed, 102 insertions(+), 48 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0d99f60f58..a6fd36a0d8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3663,7 +3663,7 @@ void Application::takeSnapshot() { if (!_snapshotShareDialog) { _snapshotShareDialog = new SnapshotShareDialog(fileName, _glWidget); } - _snapshotShareDialog->exec(); + _snapshotShareDialog->show(); } void Application::urlGoTo(int argc, const char * constArgv[]) { diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp index 921400ba1e..a5d598dc8b 100644 --- a/interface/src/ui/SnapshotShareDialog.cpp +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -1,28 +1,41 @@ // // SnapshotShareDialog.cpp -// hifi +// 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 +#include #include -#include #include const int NARROW_SNAPSHOT_DIALOG_SIZE = 500; const int WIDE_SNAPSHOT_DIALOG_WIDTH = 650; -const QString FORUM_POST_URL = "http://localhost:4000"; -SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : QDialog(parent), _fileName(fileName) { +const QString FORUM_URL = "http://localhost:4000"; +const QString FORUM_UPLOADS_URL = FORUM_URL + "/uploads"; +const QString FORUM_POST_URL = FORUM_URL + "/posts"; +const QString FORUM_REPLY_TO_TOPIC = "64"; +const QString FORUM_POST_TEMPLATE = "

%2

"; + +Q_DECLARE_METATYPE(QNetworkAccessManager::Operation) + +SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : + QDialog(parent), + _fileName(fileName), + _networkAccessManager(NULL) +{ setAttribute(Qt::WA_DeleteOnClose); - ui.setupUi(this); + _ui.setupUi(this); QPixmap snaphsotPixmap(fileName); float snapshotRatio = static_cast(snaphsotPixmap.size().width()) / snaphsotPixmap.size().height(); @@ -30,70 +43,104 @@ SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : QD // narrow snapshot if (snapshotRatio > 1) { setFixedWidth(WIDE_SNAPSHOT_DIALOG_WIDTH); - ui.snapshotWidget->setFixedWidth(WIDE_SNAPSHOT_DIALOG_WIDTH); + _ui.snapshotWidget->setFixedWidth(WIDE_SNAPSHOT_DIALOG_WIDTH); } - float labelRatio = static_cast(ui.snapshotWidget->size().width()) / ui.snapshotWidget->size().height(); + float labelRatio = static_cast(_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(ui.snapshotWidget->size().width() / snapshotRatio); + 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())); + if (_ui.snapshotWidget->size().height() < NARROW_SNAPSHOT_DIALOG_SIZE) { + setFixedHeight(size().height() - (oldHeight - _ui.snapshotWidget->size().height())); } } else { - ui.snapshotWidget->setFixedWidth(ui.snapshotWidget->size().height() * snapshotRatio); + _ui.snapshotWidget->setFixedWidth((int) (_ui.snapshotWidget->size().height() * snapshotRatio)); } - ui.snapshotWidget->setPixmap(snaphsotPixmap); - ui.snapshotWidget->adjustSize(); + _ui.snapshotWidget->setPixmap(snaphsotPixmap); + _ui.snapshotWidget->adjustSize(); } void SnapshotShareDialog::accept() { - + uploadSnapshot(); + sendForumPost("/uploads/default/25/b607c8faea6de9c3.jpg"); close(); - - // post to Discourse forum -// AccountManager& accountManager = AccountManager::getInstance(); - QNetworkAccessManager* _networkAccessManager = NULL; +} + +void SnapshotShareDialog::uploadSnapshot() { if (!_networkAccessManager) { _networkAccessManager = new QNetworkAccessManager(this); } - QNetworkRequest request; + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); - QUrl grantURL(FORUM_POST_URL); - grantURL.setPath("/posts"); + QHttpPart apiKeyPart; + apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"api_key\"")); + apiKeyPart.setBody("9168f53930b2fc69ec278414d6ff04fed723ef717867a25954143150d3e2dfe8"); +// apiKeyPart.setBody(AccountManager::getInstance().getAccountInfo().getDiscourseApiKey().toLatin1()); + QFile *file = new QFile(_fileName); + file->open(QIODevice::ReadOnly); - QByteArray postData; - // postData.append("api_key=" + accountManager.getAccountInfo().getDiscourseApiKey() + "&"); - postData.append("api_key=9168f53930b2fc69ec278414d6ff04fed723ef717867a25954143150d3e2dfe8&"); - postData.append("topic_id=64&"); - postData.append("raw=" + QUrl::toPercentEncoding(ui.textEdit->toPlainText())); + 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 - request.setUrl(grantURL); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + multiPart->append(apiKeyPart); + multiPart->append(imagePart); - QNetworkReply* requestReply = _networkAccessManager->post(request, postData); - connect(_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(serviceRequestFinished(QNetworkReply*))); + QUrl url(FORUM_UPLOADS_URL); + QNetworkRequest request(url); - connect(requestReply, &QNetworkReply::finished, this, &SnapshotShareDialog::requestFinished); - connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); + QNetworkReply *reply = _networkAccessManager->post(request, multiPart); + bool check; + Q_UNUSED(check); + + check = connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); + Q_ASSERT(check); } -void SnapshotShareDialog::serviceRequestFinished(QNetworkReply* reply) { - qDebug() << reply->errorString(); +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.getAccountInfo().getDiscourseApiKey(); + query.addQueryItem("api_key", "9168f53930b2fc69ec278414d6ff04fed723ef717867a25954143150d3e2dfe8"); + 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); + QNetworkReply* requestReply = _networkAccessManager->post(request, postData); + + bool check; + Q_UNUSED(check); + + check = connect(requestReply, &QNetworkReply::finished, this, &SnapshotShareDialog::requestFinished); + Q_ASSERT(check); + + check = connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); + Q_ASSERT(check); } void SnapshotShareDialog::requestFinished() { - QNetworkReply* requestReply = reinterpret_cast(sender()); - qDebug() << requestReply->errorString(); delete requestReply; } @@ -101,4 +148,4 @@ void SnapshotShareDialog::requestFinished() { void SnapshotShareDialog::requestError(QNetworkReply::NetworkError error) { // TODO: error handling qDebug() << "AccountManager requestError - " << error; -} \ No newline at end of file +} diff --git a/interface/src/ui/SnapshotShareDialog.h b/interface/src/ui/SnapshotShareDialog.h index 18a66616db..ea973022f3 100644 --- a/interface/src/ui/SnapshotShareDialog.h +++ b/interface/src/ui/SnapshotShareDialog.h @@ -1,16 +1,22 @@ // // SnapshotShareDialog.h -// hifi +// 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__ +#ifndef hifi_snapshotShareDialog +#define hifi_snapshotShareDialog #include "ui_shareSnapshot.h" + +#include #include +#include class SnapshotShareDialog : public QDialog { Q_OBJECT @@ -20,15 +26,16 @@ public: private: QString _fileName; - Ui_SnapshotShareDialog ui; + QNetworkAccessManager* _networkAccessManager; + Ui_SnapshotShareDialog _ui; + + void uploadSnapshot(); + void sendForumPost(QString snapshotPath); public slots: void requestFinished(); void requestError(QNetworkReply::NetworkError error); - void serviceRequestFinished(QNetworkReply* reply); - -private slots: void accept(); }; -#endif /* defined(__hifi__snapshotShareDialog__) */ +#endif // hifi_snapshotShareDialog From f668cc85812c33fb179be2dae88902c7eb2cfc8b Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Mon, 5 May 2014 00:55:24 +0200 Subject: [PATCH 012/108] Discourse upload image - success message set - fixed signal/slots in QDialog w/QEventLoop - layout fixes --- interface/src/ui/SnapshotShareDialog.cpp | 100 +++++++++++++++++------ interface/src/ui/SnapshotShareDialog.h | 6 +- interface/ui/shareSnapshot.ui | 53 +++++++++++- 3 files changed, 125 insertions(+), 34 deletions(-) diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp index a5d598dc8b..fc738c9761 100644 --- a/interface/src/ui/SnapshotShareDialog.cpp +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -13,17 +13,24 @@ #include "AccountManager.h" #include +#include +#include #include +#include #include + const int NARROW_SNAPSHOT_DIALOG_SIZE = 500; const int WIDE_SNAPSHOT_DIALOG_WIDTH = 650; +const int SUCCESS_LABEL_HEIGHT = 140; -const QString FORUM_URL = "http://localhost:4000"; +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 = "64"; +const QString FORUM_REPLY_TO_TOPIC = "244"; const QString FORUM_POST_TEMPLATE = "

%2

"; +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 ...
%1"; Q_DECLARE_METATYPE(QNetworkAccessManager::Operation) @@ -67,12 +74,15 @@ SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) : void SnapshotShareDialog::accept() { uploadSnapshot(); - sendForumPost("/uploads/default/25/b607c8faea6de9c3.jpg"); - close(); } void SnapshotShareDialog::uploadSnapshot() { + if (AccountManager::getInstance().getAccountInfo().getDiscourseApiKey().isEmpty()) { + QMessageBox::warning(this, "", "Your Discourse API key is missing, you cannot share snapshots."); + return; + } + if (!_networkAccessManager) { _networkAccessManager = new QNetworkAccessManager(this); } @@ -81,8 +91,7 @@ void SnapshotShareDialog::uploadSnapshot() { QHttpPart apiKeyPart; apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"api_key\"")); - apiKeyPart.setBody("9168f53930b2fc69ec278414d6ff04fed723ef717867a25954143150d3e2dfe8"); -// apiKeyPart.setBody(AccountManager::getInstance().getAccountInfo().getDiscourseApiKey().toLatin1()); + apiKeyPart.setBody(AccountManager::getInstance().getAccountInfo().getDiscourseApiKey().toLatin1()); QFile *file = new QFile(_fileName); file->open(QIODevice::ReadOnly); @@ -101,11 +110,13 @@ void SnapshotShareDialog::uploadSnapshot() { QNetworkRequest request(url); QNetworkReply *reply = _networkAccessManager->post(request, multiPart); - bool check; - Q_UNUSED(check); - check = connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); - Q_ASSERT(check); + + connect(reply, &QNetworkReply::finished, this, &SnapshotShareDialog::uploadRequestFinished); + + QEventLoop loop; + connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); } void SnapshotShareDialog::sendForumPost(QString snapshotPath) { @@ -119,33 +130,68 @@ void SnapshotShareDialog::sendForumPost(QString snapshotPath) { QUrl forumUrl(FORUM_POST_URL); QUrlQuery query; -// query.addQueryItem("api_key", accountManager.getAccountInfo().getDiscourseApiKey(); - query.addQueryItem("api_key", "9168f53930b2fc69ec278414d6ff04fed723ef717867a25954143150d3e2dfe8"); + 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); - bool check; - Q_UNUSED(check); - - check = connect(requestReply, &QNetworkReply::finished, this, &SnapshotShareDialog::requestFinished); - Q_ASSERT(check); - - check = connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); - Q_ASSERT(check); + QEventLoop loop; + connect(requestReply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); } -void SnapshotShareDialog::requestFinished() { +void SnapshotShareDialog::postRequestFinished() { + QNetworkReply* requestReply = reinterpret_cast(sender()); - qDebug() << requestReply->errorString(); + 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, "", SHARE_DEFAULT_ERROR); + } +} + +void SnapshotShareDialog::uploadRequestFinished() { + + QNetworkReply* requestReply = reinterpret_cast(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; } - -void SnapshotShareDialog::requestError(QNetworkReply::NetworkError error) { - // TODO: error handling - qDebug() << "AccountManager requestError - " << error; -} diff --git a/interface/src/ui/SnapshotShareDialog.h b/interface/src/ui/SnapshotShareDialog.h index ea973022f3..bc92e63e4c 100644 --- a/interface/src/ui/SnapshotShareDialog.h +++ b/interface/src/ui/SnapshotShareDialog.h @@ -32,9 +32,9 @@ private: void uploadSnapshot(); void sendForumPost(QString snapshotPath); -public slots: - void requestFinished(); - void requestError(QNetworkReply::NetworkError error); +private slots: + void uploadRequestFinished(); + void postRequestFinished(); void accept(); }; diff --git a/interface/ui/shareSnapshot.ui b/interface/ui/shareSnapshot.ui index 0f59349442..df7fc4939f 100644 --- a/interface/ui/shareSnapshot.ui +++ b/interface/ui/shareSnapshot.ui @@ -31,9 +31,6 @@ 616 - - PointingHandCursor - Share with Alphas @@ -121,7 +118,55 @@ - + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 0 + + + + + Arial + + + + color: #666 + + + + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + Qt::TextBrowserInteraction + + + + + 0 From 7d7e0129656959558a563f87ceabfd0d4c399c9b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 May 2014 11:28:09 -0700 Subject: [PATCH 013/108] Add XMLHttpRequest --- .../script-engine/src/XMLHttpRequestClass.cpp | 233 ++++++++++++++++++ .../script-engine/src/XMLHttpRequestClass.h | 128 ++++++++++ 2 files changed, 361 insertions(+) create mode 100644 libraries/script-engine/src/XMLHttpRequestClass.cpp create mode 100644 libraries/script-engine/src/XMLHttpRequestClass.h diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp new file mode 100644 index 0000000000..77b03db999 --- /dev/null +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -0,0 +1,233 @@ +// +// XMLHttpRequestClass.cpp +// libraries/script-engine/src/ +// +// Created by Ryan Huffman on 5/2/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// This class is an implementation of the XMLHttpRequest object for scripting use. It provides a near-complete implementation +// of the class described in the Mozilla docs: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "XMLHttpRequestClass.h" + +XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) : + _engine(engine), + _async(true), + _url(), + _method(""), + _responseType(""), + _manager(this), + _request(), + _reply(NULL), + _sendData(NULL), + _rawResponseData(), + _responseData(""), + _onTimeout(QScriptValue::NullValue), + _onReadyStateChange(QScriptValue::NullValue), + _readyState(XMLHttpRequestClass::UNSENT), + _errorCode(QNetworkReply::NoError), + _timeout(0), + _timer(this), + _numRedirects(0) { + + _timer.setSingleShot(true); +} + +XMLHttpRequestClass::~XMLHttpRequestClass() { + if (_reply) { delete _reply; } + if (_sendData) { delete _sendData; } +} + +QScriptValue XMLHttpRequestClass::constructor(QScriptContext* context, QScriptEngine* engine) { + return engine->newQObject(new XMLHttpRequestClass(engine)); +} + +QScriptValue XMLHttpRequestClass::getStatus() const { + if (_reply) { + return QScriptValue(_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()); + } + return QScriptValue(0); +} + +QString XMLHttpRequestClass::getStatusText() const { + if (_reply) { + return _reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); + } + return ""; +} + +void XMLHttpRequestClass::abort() { + _abortRequest(); +} + +void XMLHttpRequestClass::setRequestHeader(const QString& name, const QString& value) { + _request.setRawHeader(QByteArray(name.toLatin1()), QByteArray(value.toLatin1())); +} + +void XMLHttpRequestClass::requestMetaDataChanged() { + QVariant redirect = _reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + + // If this is a redirect, abort the current request and start a new one + if (redirect.isValid() && _numRedirects < MAXIMUM_REDIRECTS) { + _numRedirects++; + _abortRequest(); + + QUrl newUrl = _url.resolved(redirect.toUrl().toString()); + _request.setUrl(newUrl); + _doSend(); + } +} + +void XMLHttpRequestClass::requestDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { + if (_readyState == OPENED && bytesReceived > 0) { + _setReadyState(HEADERS_RECEIVED); + _setReadyState(LOADING); + } +} + +QScriptValue XMLHttpRequestClass::getAllResponseHeaders() const { + if (_reply) { + QList headerList = _reply->rawHeaderPairs(); + QByteArray headers; + for (int i = 0; i < headerList.size(); i++) { + headers.append(headerList[i].first); + headers.append(": "); + headers.append(headerList[i].second); + headers.append("\n"); + } + return QString(headers.data()); + } + return QScriptValue(""); +} + +QScriptValue XMLHttpRequestClass::getResponseHeader(const QString& name) const { + if (_reply && _reply->hasRawHeader(name.toLatin1())) { + return QScriptValue(QString(_reply->rawHeader(name.toLatin1()))); + } + return QScriptValue::NullValue; +} + +void XMLHttpRequestClass::_setReadyState(ReadyState readyState) { + if (readyState != _readyState) { + _readyState = readyState; + if (_onReadyStateChange.isFunction()) { + _onReadyStateChange.call(QScriptValue::NullValue); + } + } +} + +void XMLHttpRequestClass::open(const QString& method, const QString& url, bool async, const QString& username, + const QString& password) { + if (_readyState == UNSENT) { + _async = async; + _url.setUrl(url); + if (!username.isEmpty()) { + _url.setUserName(username); + } + if (!password.isEmpty()) { + _url.setPassword(password); + } + _request.setUrl(_url); + _method = method; + _setReadyState(OPENED); + } +} + +void XMLHttpRequestClass::send() { + send(QString::Null()); +} + +void XMLHttpRequestClass::send(const QString& data) { + if (_readyState == OPENED && !_reply) { + if (!data.isNull()) { + _sendData = new QBuffer(this); + _sendData->setData(data.toUtf8()); + } + + _doSend(); + + if (!_async) { + QEventLoop loop; + connect(this, SIGNAL(requestComplete()), &loop, SLOT(quit())); + loop.exec(); + } + } +} + +void XMLHttpRequestClass::_doSend() { + _reply = _manager.sendCustomRequest(_request, _method.toLatin1(), _sendData); + + _connectToReply(_reply); + + if (_timeout > 0) { + _timer.start(_timeout); + connect(&_timer, SIGNAL(timeout()), this, SLOT(requestTimeout())); + } +} + +void XMLHttpRequestClass::requestTimeout() { + if (_onTimeout.isFunction()) { + _onTimeout.call(QScriptValue::NullValue); + } + _abortRequest(); + _errorCode = QNetworkReply::TimeoutError; + _setReadyState(DONE); + emit requestComplete(); +} + +void XMLHttpRequestClass::requestError(QNetworkReply::NetworkError code) { +} + +void XMLHttpRequestClass::requestFinished() { + disconnect(&_timer, SIGNAL(timeout()), this, SLOT(requestTimeout())); + + _errorCode = _reply->error(); + if (_errorCode == QNetworkReply::NoError) { + _rawResponseData.append(_reply->readAll()); + + if (_responseType == "json") { + _responseData = _engine->evaluate("(" + QString(_rawResponseData.data()) + ")"); + if (_responseData.isError()) { + _engine->clearExceptions(); + _responseData = QScriptValue::NullValue; + } + } else if (_responseType == "arraybuffer") { + _responseData = QScriptValue(_rawResponseData.data()); + } else { + _responseData = QScriptValue(QString(_rawResponseData.data())); + } + } + _setReadyState(DONE); + emit requestComplete(); +} + +void XMLHttpRequestClass::_abortRequest() { + // Disconnect from signals we don't want to receive any longer. + disconnect(&_timer, SIGNAL(timeout()), this, SLOT(requestTimeout())); + if (_reply) { + _disconnectFromReply(_reply); + _reply->abort(); + delete _reply; + _reply = NULL; + } +} + +void XMLHttpRequestClass::_connectToReply(QNetworkReply* reply) { + connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); + connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(requestDownloadProgress(qint64, qint64))); + connect(reply, SIGNAL(metaDataChanged()), this, SLOT(requestMetaDataChanged())); +} + +void XMLHttpRequestClass::_disconnectFromReply(QNetworkReply* reply) { + disconnect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); + disconnect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); + disconnect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(requestDownloadProgress(qint64, qint64))); + disconnect(reply, SIGNAL(metaDataChanged()), this, SLOT(requestMetaDataChanged())); +} diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h new file mode 100644 index 0000000000..b1b33e4cc6 --- /dev/null +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -0,0 +1,128 @@ +// +// XMLHttpRequestClass.h +// libraries/script-engine/src/ +// +// Created by Ryan Huffman on 5/2/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// 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_XMLHttpRequestClass_h +#define hifi_XMLHttpRequestClass_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class XMLHttpRequestClass : public QObject { + Q_OBJECT + Q_PROPERTY(QScriptValue response READ getResponse) + Q_PROPERTY(QScriptValue responseText READ getResponseText) + Q_PROPERTY(QString responseType READ getResponseType WRITE setResponseType) + Q_PROPERTY(QScriptValue status READ getStatus) + Q_PROPERTY(QString statusText READ getStatusText) + Q_PROPERTY(QScriptValue readyState READ getReadyState) + Q_PROPERTY(QScriptValue errorCode READ getError) + Q_PROPERTY(int timeout READ getTimeout WRITE setTimeout) + + Q_PROPERTY(int UNSENT READ getUnsent) + Q_PROPERTY(int OPENED READ getOpened) + Q_PROPERTY(int HEADERS_RECEIVED READ getHeadersReceived) + Q_PROPERTY(int LOADING READ getLoading) + Q_PROPERTY(int DONE READ getDone) + + // Callbacks + Q_PROPERTY(QScriptValue ontimeout WRITE setOnTimeout) + Q_PROPERTY(QScriptValue onreadystatechange WRITE setOnReadyStateChange) +public: + XMLHttpRequestClass(QScriptEngine* engine); + ~XMLHttpRequestClass(); + + static const int MAXIMUM_REDIRECTS = 5; + enum ReadyState { + UNSENT = 0, + OPENED, + HEADERS_RECEIVED, + LOADING, + DONE + }; + + int getUnsent() const { return UNSENT; }; + int getOpened() const { return OPENED; }; + int getHeadersReceived() const { return HEADERS_RECEIVED; }; + int getLoading() const { return LOADING; }; + int getDone() const { return DONE; }; + + static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); + static QString escapeJavascriptString(const QString& jsString); + + int getTimeout() const { return _timeout; } + void setTimeout(int timeout) { _timeout = timeout; } + QScriptValue getResponse() const { return _responseData; } + QScriptValue getResponseText() const { return QScriptValue(QString(_rawResponseData.data())); } + QString getResponseType() const { return _responseType; } + void setResponseType(const QString& responseType) { _responseType = responseType; } + QScriptValue getReadyState() const { return QScriptValue(_readyState); } + QScriptValue getError() const { return QScriptValue(_errorCode); } + QScriptValue getStatus() const; + QString getStatusText() const; + + void setOnTimeout(QScriptValue function) { _onTimeout = function; } + void setOnReadyStateChange(QScriptValue function) { _onReadyStateChange = function; } + +public slots: + void abort(); + void setRequestHeader(const QString& name, const QString& value); + void open(const QString& method, const QString& url, bool async = true, const QString& username = "", + const QString& password = ""); + void send(); + void send(const QString& data); + QScriptValue getAllResponseHeaders() const; + QScriptValue getResponseHeader(const QString& name) const; + +signals: + void requestComplete(); + +private: + void _setReadyState(ReadyState readyState); + void _doSend(); + void _connectToReply(QNetworkReply* reply); + void _disconnectFromReply(QNetworkReply* reply); + void _abortRequest(); + + QScriptEngine* _engine; + bool _async; + QUrl _url; + QString _method; + QString _responseType; + QNetworkAccessManager _manager; + QNetworkRequest _request; + QNetworkReply* _reply; + QBuffer* _sendData; + QByteArray _rawResponseData; + QScriptValue _responseData; + QScriptValue _onTimeout; + QScriptValue _onReadyStateChange; + ReadyState _readyState; + QNetworkReply::NetworkError _errorCode; + int _timeout; + QTimer _timer; + int _numRedirects; + +private slots: + void requestFinished(); + void requestError(QNetworkReply::NetworkError code); + void requestMetaDataChanged(); + void requestDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void requestTimeout(); +}; + +#endif // hifi_XMLHttpRequestClass_h From 2dda87fe7f4c5d8d8ed78e0ea73c2794d6b26c97 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 May 2014 11:28:35 -0700 Subject: [PATCH 014/108] Add XMLHttpRequest constructor to ScriptEngine --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 402f1a2885..41ed07cc23 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -34,6 +34,7 @@ #include "MenuItemProperties.h" #include "LocalVoxels.h" #include "ScriptEngine.h" +#include "XMLHttpRequestClass.h" VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface; @@ -214,6 +215,9 @@ void ScriptEngine::init() { qScriptRegisterSequenceMetaType >(&_engine); qScriptRegisterSequenceMetaType >(&_engine); + QScriptValue xmlHttpRequestConstructorValue = _engine.newFunction(XMLHttpRequestClass::constructor); + _engine.globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); + QScriptValue printConstructorValue = _engine.newFunction(debugPrint); _engine.globalObject().setProperty("print", printConstructorValue); From 39e6e61a6d2026c3c57da3a425566551a6fca982 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 May 2014 11:31:33 -0700 Subject: [PATCH 015/108] Add XMLHttpRequest tests and examples --- examples/Test.js | 68 +++++++++++++++ examples/streetAreaExample.js | 51 ++++++++++++ examples/testXMLHttpRequest.js | 147 +++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 examples/Test.js create mode 100644 examples/streetAreaExample.js create mode 100644 examples/testXMLHttpRequest.js diff --git a/examples/Test.js b/examples/Test.js new file mode 100644 index 0000000000..c56c7ebdc1 --- /dev/null +++ b/examples/Test.js @@ -0,0 +1,68 @@ +// +// Test.js +// examples +// +// Created by Ryan Huffman on 5/4//14 +// Copyright 2014 High Fidelity, Inc. +// +// This provides very basic unit testing functionality. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +test = function(name, func) { + print("Running test: " + name); + + var unitTest = new UnitTest(name, func); + + try { + unitTest.run(); + print(" Success: " + unitTest.numAssertions + " assertions passed"); + } catch (error) { + print(" Failure: " + error.message); + } +}; + +AssertionException = function(expected, actual, message) { + print("Creating exception"); + this.message = message + "\n: " + actual + " != " + expected; + this.name = 'AssertionException'; +}; + +UnitTest = function(name, func) { + this.numAssertions = 0; + this.func = func; +}; + +UnitTest.prototype.run = function() { + this.func(); +}; + +UnitTest.prototype.assertNotEquals = function(expected, actual, message) { + this.numAssertions++; + if (expected == actual) { + throw new AssertionException(expected, actual, message); + } +}; + +UnitTest.prototype.assertEquals = function(expected, actual, message) { + this.numAssertions++; + if (expected != actual) { + throw new AssertionException(expected, actual, message); + } +}; + +UnitTest.prototype.assertHasProperty = function(property, actual, message) { + this.numAssertions++; + if (actual[property] === undefined) { + throw new AssertionException(property, actual, message); + } +}; + +UnitTest.prototype.assertNull = function(value, message) { + this.numAssertions++; + if (value !== null) { + throw new AssertionException(value, null, message); + } +}; diff --git a/examples/streetAreaExample.js b/examples/streetAreaExample.js new file mode 100644 index 0000000000..5e92753689 --- /dev/null +++ b/examples/streetAreaExample.js @@ -0,0 +1,51 @@ +// +// streetAreaExample.js +// examples +// +// Created by Ryan Huffman on 5/4//14 +// Copyright 2014 High Fidelity, Inc. +// +// This is an example script showing how to load JSON data using XMLHttpRequest. +// +// URL Macro created by Thijs Wenker. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var url = "https://script.google.com/macros/s/AKfycbwIo4lmF-qUwX1Z-9eA_P-g2gse9oFhNcjVyyksGukyDDEFXgU/exec?action=listOwners&domain=alpha.highfidelity.io"; +print("Loading street data from " + url); + +var req = new XMLHttpRequest(); + +// Set response type to "json". This will tell XMLHttpRequest to parse the response data as json, so req.response can be used +// as a regular javascript object +req.responseType = 'json'; + +req.open("GET", url, false); +req.send(); + +if (req.status == 200) { + for (var domain in req.response) { + print("DOMAIN: " + domain); + var locations = req.response[domain]; + var userAreas = []; + for (var i = 0; i < locations.length; i++) { + var loc = locations[i]; + var x1 = loc[1], + y1 = loc[2], + x2 = loc[3], + y2 = loc[4]; + userAreas.push({ + username: loc[0], + area: Math.abs(x2 - x1) * Math.abs(y2 - y1), + }); + } + userAreas.sort(function(a, b) { return a.area > b.area ? -1 : (a.area < b.area ? 1 : 0) }); + for (var i = 0; i < userAreas.length; i++) { + print(userAreas[i].username + ": " + userAreas[i].area + " sq units"); + } + } +} else { + print("Error loading data: " + req.status + " " + req.statusText + ", " + req.errorCode); +} diff --git a/examples/testXMLHttpRequest.js b/examples/testXMLHttpRequest.js new file mode 100644 index 0000000000..6471b2fdeb --- /dev/null +++ b/examples/testXMLHttpRequest.js @@ -0,0 +1,147 @@ +// +// testXMLHttpRequest.js +// examples +// +// Created by Ryan Huffman on 5/4//14 +// Copyright 2014 High Fidelity, Inc. +// +// XMLHttpRequest Tests +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("Test.js"); + +test("Test default request values", function(finished) { + var req = new XMLHttpRequest(); + + this.assertEquals(req.UNSENT, req.readyState, "readyState should be UNSENT"); + this.assertEquals(0, req.status, "status should be `0` by default"); + this.assertEquals("", req.statusText, "statusText should be empty string by default"); + this.assertEquals("", req.getAllResponseHeaders(), "getAllResponseHeaders() should return empty string by default"); + this.assertEquals("", req.response, "response should be empty string by default"); + this.assertEquals("", req.responseText, "responseText should be empty string by default"); + this.assertEquals("", req.responseType, "responseType should be empty string by default"); + this.assertEquals(0, req.timeout, "timeout should be `0` by default"); + this.assertEquals(0, req.errorCode, "there should be no error by default"); +}); + + +test("Test readyStates", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + var statesVisited = [true, false, false, false, false] + + req.onreadystatechange = function() { + statesVisited[req.readyState] = true; + }; + + req.open("GET", "https://gist.githubusercontent.com/huffman/33cc618fec183d1bccd0/raw/test.json", false); + req.send(); + for (var i = 0; i <= req.DONE; i++) { + this.assertEquals(true, statesVisited[i], i + " should be set"); + } + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); +}); + +test("Test TEXT request", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + req.open("GET", "https://gist.githubusercontent.com/huffman/33cc618fec183d1bccd0/raw/test.json", false); + req.send(); + + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertEquals(200, req.status, "status should be `200`"); + this.assertEquals(0, req.errorCode); + this.assertEquals("OK", req.statusText, "statusText should be `OK`"); + this.assertNotEquals("", req.getAllResponseHeaders(), "headers should no longer be empty string"); + this.assertNull(req.getResponseHeader('invalidheader'), "invalid header should return `null`"); + this.assertEquals("GitHub.com", req.getResponseHeader('Server'), "Server header should be GitHub.com"); + this.assertEquals('{"id": 1}', req.response); + this.assertEquals('{"id": 1}', req.responseText); +}); + +test("Test JSON request", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + req.responseType = "json"; + req.open("GET", "https://gist.githubusercontent.com/huffman/33cc618fec183d1bccd0/raw/test.json", false); + req.send(); + + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertEquals(200, req.status, "status should be `200`"); + this.assertEquals(0, req.errorCode); + this.assertEquals("OK", req.statusText, "statusText should be `OK`"); + this.assertNotEquals("", req.getAllResponseHeaders(), "headers should no longer be empty string"); + this.assertNull(req.getResponseHeader('invalidheader'), "invalid header should return `null`"); + this.assertEquals("GitHub.com", req.getResponseHeader('Server'), "Server header should be GitHub.com"); + this.assertHasProperty('id', req.response); + this.assertEquals(1, req.response.id); + this.assertEquals('{"id": 1}', req.responseText); +}); + +test("Test Bad URL", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + req.open("POST", "hifi://domain/path", false); + req.send(); + + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertNotEquals(0, req.errorCode); +}); + +test("Test Bad Method Error", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + req.open("POST", "https://www.google.com", false); + + req.send("randomdata"); + + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertEquals(405, req.status); + this.assertEquals(202, req.errorCode); + + req.open("POST", "https://www.google.com", false) + req.send(); + + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertEquals(405, req.status); + this.assertEquals(202, req.errorCode); +}); + +test("Test abort", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + req.open("POST", "https://www.google.com", true) + req.send(); + req.abort(); + + this.assertEquals(0, req.status); + this.assertEquals(0, req.errorCode); +}); + +test("Test timeout", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + var timedOut = false; + + req.ontimeout = function() { + timedOut = true; + }; + + req.open("POST", "https://gist.githubusercontent.com/huffman/33cc618fec183d1bccd0/raw/test.json", false) + req.timeout = 1; + req.send(); + + this.assertEquals(true, timedOut, "request should have timed out"); + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertEquals(0, req.status, "status should be `0`"); + this.assertEquals(4, req.errorCode, "4 is the timeout error code for QNetworkReply::NetworkError"); +}); From 5a7b133d2556bae5b9620e67b219c3e692a707a8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 May 2014 11:38:12 -0700 Subject: [PATCH 016/108] Fix dates in XMLHttpRequest js files --- examples/Test.js | 2 +- examples/streetAreaExample.js | 2 +- examples/testXMLHttpRequest.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/Test.js b/examples/Test.js index c56c7ebdc1..056ec3cbbf 100644 --- a/examples/Test.js +++ b/examples/Test.js @@ -2,7 +2,7 @@ // Test.js // examples // -// Created by Ryan Huffman on 5/4//14 +// Created by Ryan Huffman on 5/4/14 // Copyright 2014 High Fidelity, Inc. // // This provides very basic unit testing functionality. diff --git a/examples/streetAreaExample.js b/examples/streetAreaExample.js index 5e92753689..deb10dd65a 100644 --- a/examples/streetAreaExample.js +++ b/examples/streetAreaExample.js @@ -2,7 +2,7 @@ // streetAreaExample.js // examples // -// Created by Ryan Huffman on 5/4//14 +// Created by Ryan Huffman on 5/4/14 // Copyright 2014 High Fidelity, Inc. // // This is an example script showing how to load JSON data using XMLHttpRequest. diff --git a/examples/testXMLHttpRequest.js b/examples/testXMLHttpRequest.js index 6471b2fdeb..421eb458e4 100644 --- a/examples/testXMLHttpRequest.js +++ b/examples/testXMLHttpRequest.js @@ -2,7 +2,7 @@ // testXMLHttpRequest.js // examples // -// Created by Ryan Huffman on 5/4//14 +// Created by Ryan Huffman on 5/4/14 // Copyright 2014 High Fidelity, Inc. // // XMLHttpRequest Tests From f3305a51e28047feaad47b43bac1ea9735af6200 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 May 2014 13:09:49 -0700 Subject: [PATCH 017/108] Remove XMLHttpRequestClass::escapeJavascriptString --- libraries/script-engine/src/XMLHttpRequestClass.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index b1b33e4cc6..bff88d91a9 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -62,7 +62,6 @@ public: int getDone() const { return DONE; }; static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); - static QString escapeJavascriptString(const QString& jsString); int getTimeout() const { return _timeout; } void setTimeout(int timeout) { _timeout = timeout; } From 01e5b009eee25fb0d9cb891d8c279a1ee035990b Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 7 May 2014 20:43:23 -0700 Subject: [PATCH 018/108] scope menus better, pause hotkey, no stay paused, remove menu for shared face culling ops --- interface/src/Audio.cpp | 1 + interface/src/Menu.cpp | 22 ++++--------------- interface/src/Menu.h | 2 -- interface/src/voxels/VoxelSystem.cpp | 32 ---------------------------- interface/src/voxels/VoxelSystem.h | 4 +--- 5 files changed, 6 insertions(+), 55 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 68e38615bf..bef6f4c2da 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -1068,6 +1068,7 @@ void Audio::toggleScope() { memset(_scopeInput.data(), 0, width * sizeof(int16_t)); memset(_scopeOutputLeft.data(), 0, width * sizeof(int16_t)); memset(_scopeOutputRight.data(), 0, width * sizeof(int16_t)); + _scopeEnabledPause = false; } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0587e979f6..eecc18534c 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -272,9 +272,6 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true); addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails())); addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, this, SLOT(octreeStatsDetails())); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::AudioScope, 0, false, - appInstance->getAudio(), - SLOT(toggleScope())); QMenu* developerMenu = addMenu("Developer"); @@ -371,20 +368,6 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings); addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::SuppressShortTimings); - addCheckableActionToQMenuAndActionHash(renderDebugMenu, - MenuOption::CullSharedFaces, - Qt::CTRL | Qt::SHIFT | Qt::Key_C, - false, - appInstance->getVoxels(), - SLOT(cullSharedFaces())); - - addCheckableActionToQMenuAndActionHash(renderDebugMenu, - MenuOption::ShowCulledSharedFaces, - 0, - false, - appInstance->getVoxels(), - SLOT(showCulledSharedFaces())); - QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools"); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, @@ -408,8 +391,11 @@ Menu::Menu() : false, appInstance->getAudio(), SLOT(toggleToneInjection())); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_P, false, + appInstance->getAudio(), + SLOT(toggleScope())); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScopePause, - Qt::CTRL | Qt::Key_P, + Qt::CTRL | Qt::SHIFT | Qt::Key_P , false, appInstance->getAudio(), SLOT(toggleScopePause())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index d1097929f7..1e13d5ff2a 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -297,7 +297,6 @@ namespace MenuOption { const QString CollideWithParticles = "Collide With Particles"; const QString CollideWithVoxels = "Collide With Voxels"; const QString Collisions = "Collisions"; - const QString CullSharedFaces = "Cull Shared Voxel Faces"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DecreaseVoxelSize = "Decrease Voxel Size"; const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD"; @@ -363,7 +362,6 @@ namespace MenuOption { const QString SettingsExport = "Export Settings"; const QString SettingsImport = "Import Settings"; const QString Shadows = "Shadows"; - const QString ShowCulledSharedFaces = "Show Culled Shared Voxel Faces"; const QString ShowIKConstraints = "Show IK Constraints"; const QString Stars = "Stars"; const QString Stats = "Stats"; diff --git a/interface/src/voxels/VoxelSystem.cpp b/interface/src/voxels/VoxelSystem.cpp index 0fac5a338c..8937cef7dd 100644 --- a/interface/src/voxels/VoxelSystem.cpp +++ b/interface/src/voxels/VoxelSystem.cpp @@ -1711,38 +1711,6 @@ bool VoxelSystem::inspectForExteriorOcclusionsOperation(OctreeElement* element, return true; } - -void VoxelSystem::cullSharedFaces() { - - if (Menu::getInstance()->isOptionChecked(MenuOption::CullSharedFaces)) { - _useVoxelShader = false; - _usePrimitiveRenderer = true; - inspectForOcclusions(); - } else { - _usePrimitiveRenderer = false; - clearAllNodesBufferIndex(); - } - _writeRenderFullVBO = true; - _tree->setDirtyBit(); - setupNewVoxelsForDrawing(); -} - -void VoxelSystem::showCulledSharedFaces() { - - _tree->lockForRead(); - if (Menu::getInstance()->isOptionChecked(MenuOption::ShowCulledSharedFaces)) { - _showCulledSharedFaces = true; - } else { - _showCulledSharedFaces = false; - } - _tree->unlock(); - if (Menu::getInstance()->isOptionChecked(MenuOption::CullSharedFaces)) { - _writeRenderFullVBO = true; - _tree->setDirtyBit(); - setupNewVoxelsForDrawing(); - } -} - void VoxelSystem::inspectForOcclusions() { if (_inOcclusions) { diff --git a/interface/src/voxels/VoxelSystem.h b/interface/src/voxels/VoxelSystem.h index b134fe1539..15e2b20a75 100644 --- a/interface/src/voxels/VoxelSystem.h +++ b/interface/src/voxels/VoxelSystem.h @@ -95,9 +95,7 @@ public slots: // Methods that recurse tree void forceRedrawEntireTree(); void clearAllNodesBufferIndex(); - void cullSharedFaces(); - void showCulledSharedFaces(); - + void setDisableFastVoxelPipeline(bool disableFastVoxelPipeline); void setUseVoxelShader(bool useVoxelShader); void setVoxelsAsPoints(bool voxelsAsPoints); From 8f4fb04021c192b3fdb078ed0fd0ac3148f3dc90 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 7 May 2014 22:24:05 -0700 Subject: [PATCH 019/108] clearer visualization of controller hand target, remove hand paddles --- interface/src/avatar/Hand.cpp | 29 ++++++++++++++++++++++------- interface/src/avatar/Hand.h | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 320a8477c1..a6cd7ff10e 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -213,7 +213,7 @@ void Hand::render(bool isMine, Model::RenderMode renderMode) { } if (renderMode != Model::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) { - renderLeapHands(isMine); + renderHandTargets(isMine); } glEnable(GL_DEPTH_TEST); @@ -221,11 +221,11 @@ void Hand::render(bool isMine, Model::RenderMode renderMode) { } -void Hand::renderLeapHands(bool isMine) { +void Hand::renderHandTargets(bool isMine) { const float alpha = 1.0f; - const glm::vec3 handColor(1.0, 0.84, 0.66); // use the skin color + const glm::vec3 handColor(1.0, 0.0, 0.0); // Color the hand targets red to be different than skin glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); @@ -262,23 +262,37 @@ void Hand::renderLeapHands(bool isMine) { glPopMatrix(); } } - - // Draw the finger root cones + + const float PALM_BALL_RADIUS = 0.03f; + const float PALM_DISK_RADIUS = 0.06f; + const float PALM_DISK_THICKNESS = 0.01f; + const float PALM_FINGER_ROD_RADIUS = 0.003f; + + // Draw the palm ball and disk 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, 0.5); + glColor4f(handColor.r, handColor.g, handColor.b, alpha); glm::vec3 tip = finger.getTipPosition(); glm::vec3 root = finger.getRootPosition(); - Avatar::renderJointConnectingCone(root, tip, 0.001f, 0.003f); + 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(); + } } } } + /* // 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); @@ -309,6 +323,7 @@ void Hand::renderLeapHands(bool isMine) { Avatar::renderJointConnectingCone(root, tip, HAND_PADDLE_RADIUS, 0.f); } } + */ glDepthMask(GL_TRUE); glEnable(GL_DEPTH_TEST); diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 757a74db29..9c2bc2c2c0 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -76,7 +76,7 @@ private: std::vector _leapFingerTipBalls; std::vector _leapFingerRootBalls; - void renderLeapHands(bool isMine); + void renderHandTargets(bool isMine); void renderLeapFingerTrails(); void calculateGeometry(); From e439694d72b5c791346a255af10178fe31ec424f Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 7 May 2014 22:56:06 -0700 Subject: [PATCH 020/108] =?UTF-8?q?reset=20only=20pitch=20and=20roll=20on?= =?UTF-8?q?=20spacebar=20so=20that=20you=20don=E2=80=99t=20turn=20away=20f?= =?UTF-8?q?rom=20someone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- interface/src/avatar/MyAvatar.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1655a17f08..f320a2759d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -99,7 +99,11 @@ void MyAvatar::reset() { setVelocity(glm::vec3(0.0f)); setThrust(glm::vec3(0.0f)); - setOrientation(glm::quat(glm::vec3(0.0f))); + // Reset the pitch and roll components of the avatar's orientation, preserve yaw direction + glm::vec3 eulers = safeEulerAngles(getOrientation()); + eulers.x = 0.f; + eulers.z = 0.f; + setOrientation(glm::quat(eulers)); } void MyAvatar::update(float deltaTime) { From 1e276f6dae1d87c8eeb526b74022fbba342916da Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 7 May 2014 23:41:58 -0700 Subject: [PATCH 021/108] Improved hydra idle detection by tracking total distance moved, fixed NaN in starting velocities --- interface/src/devices/SixenseManager.cpp | 24 ++++++++++++++++++------ interface/src/devices/SixenseManager.h | 1 + 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index f7c00411c1..0435519124 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -30,6 +30,7 @@ const float NECK_Z = 300.f; // millimeters SixenseManager::SixenseManager() { #ifdef HAVE_SIXENSE _lastMovement = 0; + _amountMoved = glm::vec3(0.0f); _calibrationState = CALIBRATION_STATE_IDLE; // By default we assume the _neckBase (in orb frame) is as high above the orb @@ -122,14 +123,21 @@ void SixenseManager::update(float deltaTime) { palm->setRawRotation(rotation); // Compute current velocity from position change - glm::vec3 rawVelocity = (position - palm->getRawPosition()) / deltaTime / 1000.f; + glm::vec3 rawVelocity; + if (deltaTime > 0.f) { + rawVelocity = (position - palm->getRawPosition()) / deltaTime / 1000.f; + } else { + rawVelocity = glm::vec3(0.0f); + } palm->setRawVelocity(rawVelocity); // meters/sec palm->setRawPosition(position); // use the velocity to determine whether there's any movement (if the hand isn't new) - const float MOVEMENT_SPEED_THRESHOLD = 0.05f; - if (glm::length(rawVelocity) > MOVEMENT_SPEED_THRESHOLD && foundHand) { + const float MOVEMENT_DISTANCE_THRESHOLD = 0.003f; + _amountMoved += rawVelocity * deltaTime; + if (glm::length(_amountMoved) > MOVEMENT_DISTANCE_THRESHOLD && foundHand) { _lastMovement = usecTimestampNow(); + _amountMoved = glm::vec3(0.0f); } // initialize the "finger" based on the direction @@ -143,7 +151,11 @@ void SixenseManager::update(float deltaTime) { // Store the one fingertip in the palm structure so we can track velocity glm::vec3 oldTipPosition = palm->getTipRawPosition(); - palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime / 1000.f); + if (deltaTime > 0.f) { + palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime / 1000.f); + } else { + palm->setTipVelocity(glm::vec3(0.f)); + } palm->setTipPosition(newTipPosition); // three fingers indicates to the skeleton that we have enough data to determine direction @@ -158,8 +170,8 @@ void SixenseManager::update(float deltaTime) { } // if the controllers haven't been moved in a while, disable - const unsigned int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000; - if (usecTimestampNow() - _lastMovement > MOVEMENT_DISABLE_DURATION) { + const unsigned int MOVEMENT_DISABLE_SECONDS = 3; + if (usecTimestampNow() - _lastMovement > (MOVEMENT_DISABLE_SECONDS * 1000 * 1000)) { for (std::vector::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) { it->setActive(false); } diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 2fc6b3dcb3..a98d4c0e4e 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -64,6 +64,7 @@ private: #endif quint64 _lastMovement; + glm::vec3 _amountMoved; }; #endif // hifi_SixenseManager_h From 2797d0ab3b4d4498d4981800764874e25f70cf10 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 8 May 2014 00:09:07 -0700 Subject: [PATCH 022/108] removed hand trying to follow mouse and other cruft --- interface/src/Application.cpp | 38 +-------------------------- interface/src/Application.h | 6 ----- interface/src/avatar/MyAvatar.cpp | 43 ++++++------------------------- interface/src/avatar/MyAvatar.h | 1 - 4 files changed, 9 insertions(+), 79 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fb414311ef..61666ccdeb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1217,10 +1217,6 @@ void Application::touchBeginEvent(QTouchEvent* event) { return; } - // put any application specific touch behavior below here.. - _lastTouchAvgX = _touchAvgX; - _lastTouchAvgY = _touchAvgY; - } void Application::touchEndEvent(QTouchEvent* event) { @@ -1875,34 +1871,6 @@ void Application::updateMyAvatarLookAtPosition() { _myAvatar->getHead()->setLookAtPosition(lookAtSpot); } -void Application::updateHandAndTouch(float deltaTime) { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateHandAndTouch()"); - - // Update from Touch - if (_isTouchPressed) { - _lastTouchAvgX = _touchAvgX; - _lastTouchAvgY = _touchAvgY; - } -} - -void Application::updateLeap(float deltaTime) { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateLeap()"); -} - -void Application::updateSixense(float deltaTime) { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateSixense()"); - - _sixenseManager.update(deltaTime); -} - -void Application::updateSerialDevices(float deltaTime) { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateSerialDevices()"); -} - void Application::updateThreads(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateThreads()"); @@ -2016,11 +1984,7 @@ void Application::update(float deltaTime) { updateVisage(); _myAvatar->updateLookAtTargetAvatar(); updateMyAvatarLookAtPosition(); - - updateHandAndTouch(deltaTime); // Update state for touch sensors - updateLeap(deltaTime); // Leap finger-sensing device - updateSixense(deltaTime); // Razer Hydra controllers - updateSerialDevices(deltaTime); // Read serial port interface devices + _sixenseManager.update(deltaTime); updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... _avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them... diff --git a/interface/src/Application.h b/interface/src/Application.h index 54e6bcdcac..91a7ebd29b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -343,10 +343,6 @@ private: void updateFaceshift(); void updateVisage(); void updateMyAvatarLookAtPosition(); - void updateHandAndTouch(float deltaTime); - void updateLeap(float deltaTime); - void updateSixense(float deltaTime); - void updateSerialDevices(float deltaTime); void updateThreads(float deltaTime); void updateMetavoxels(float deltaTime); void updateCamera(float deltaTime); @@ -477,8 +473,6 @@ private: float _touchAvgX; float _touchAvgY; - float _lastTouchAvgX; - float _lastTouchAvgY; float _touchDragStartedAvgX; float _touchDragStartedAvgY; bool _isTouchPressed; // true if multitouch has been pressed (clear when finished) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f320a2759d..2f931b0a54 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -136,7 +136,14 @@ void MyAvatar::simulate(float deltaTime) { } // update the movement of the hand and process handshaking with other avatars... - updateHandMovementAndTouching(deltaTime); + bool pointing = false; + if (_mousePressed) { + _handState = HAND_STATE_GRASPING; + } else if (pointing) { + _handState = HAND_STATE_POINTING; + } else { + _handState = HAND_STATE_NULL; + } updateOrientation(deltaTime); @@ -915,40 +922,6 @@ void MyAvatar::updateThrust(float deltaTime) { } */ -void MyAvatar::updateHandMovementAndTouching(float deltaTime) { - glm::quat orientation = getOrientation(); - - // reset hand and arm positions according to hand movement - glm::vec3 up = orientation * IDENTITY_UP; - - bool pointing = false; - if (glm::length(_mouseRayDirection) > EPSILON && !Application::getInstance()->isMouseHidden()) { - // confine to the approximate shoulder plane - glm::vec3 pointDirection = _mouseRayDirection; - if (glm::dot(_mouseRayDirection, up) > 0.0f) { - glm::vec3 projectedVector = glm::cross(up, glm::cross(_mouseRayDirection, up)); - if (glm::length(projectedVector) > EPSILON) { - pointDirection = glm::normalize(projectedVector); - } - } - glm::vec3 shoulderPosition; - if (_skeletonModel.getRightShoulderPosition(shoulderPosition)) { - glm::vec3 farVector = _mouseRayOrigin + pointDirection * (float)TREE_SCALE - shoulderPosition; - const float ARM_RETRACTION = 0.75f; - float retractedLength = _skeletonModel.getRightArmLength() * ARM_RETRACTION; - setHandPosition(shoulderPosition + glm::normalize(farVector) * retractedLength); - pointing = true; - } - } - - if (_mousePressed) { - _handState = HAND_STATE_GRASPING; - } else if (pointing) { - _handState = HAND_STATE_POINTING; - } else { - _handState = HAND_STATE_NULL; - } -} void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) { glm::vec3 up = getBodyUpDirection(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1c80d3b969..f715a37db4 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -147,7 +147,6 @@ private: float computeMotorTimescale(); void applyMotor(float deltaTime); void applyThrust(float deltaTime); - void updateHandMovementAndTouching(float deltaTime); void updateCollisionWithAvatars(float deltaTime); void updateCollisionWithEnvironment(float deltaTime, float radius); void updateCollisionWithVoxels(float deltaTime, float radius); From 2c6c57eec2875823fadd14c0f5d4028ce26bc17d Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 8 May 2014 00:28:02 -0700 Subject: [PATCH 023/108] increase hand restoration rate, improve function name clarity --- interface/src/avatar/MyAvatar.cpp | 4 ++-- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/SkeletonModel.cpp | 9 ++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2f931b0a54..c3591862d1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -109,7 +109,7 @@ void MyAvatar::reset() { void MyAvatar::update(float deltaTime) { Head* head = getHead(); head->relaxLean(deltaTime); - updateFromGyros(deltaTime); + updateFromFaceTracker(deltaTime); if (Menu::getInstance()->isOptionChecked(MenuOption::MoveWithLean)) { // Faceshift drive is enabled, set the avatar drive based on the head position moveWithLean(); @@ -239,7 +239,7 @@ void MyAvatar::simulate(float deltaTime) { } // Update avatar head rotation with sensor data -void MyAvatar::updateFromGyros(float deltaTime) { +void MyAvatar::updateFromFaceTracker(float deltaTime) { glm::vec3 estimatedPosition, estimatedRotation; FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f715a37db4..c07c27033c 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -38,7 +38,7 @@ public: void reset(); void update(float deltaTime); void simulate(float deltaTime); - void updateFromGyros(float deltaTime); + void updateFromFaceTracker(float deltaTime); void moveWithLean(); void render(const glm::vec3& cameraPosition, RenderMode renderMode = NORMAL_RENDER_MODE); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index e4c796d3ce..8c21a3240f 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -38,24 +38,23 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { Hand* hand = _owningAvatar->getHand(); hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); - const float HAND_RESTORATION_PERIOD = 1.f; // seconds - float handRestorePercent = glm::clamp(deltaTime / HAND_RESTORATION_PERIOD, 0.f, 1.f); + const float HAND_RESTORATION_RATE = 0.25f; const FBXGeometry& geometry = _geometry->getFBXGeometry(); if (leftPalmIndex == -1) { // no Leap data; set hands from mouse if (_owningAvatar->getHandState() == HAND_STATE_NULL) { - restoreRightHandPosition(handRestorePercent); + restoreRightHandPosition(HAND_RESTORATION_RATE); } else { applyHandPosition(geometry.rightHandJointIndex, _owningAvatar->getHandPosition()); } - restoreLeftHandPosition(handRestorePercent); + restoreLeftHandPosition(HAND_RESTORATION_RATE); } else if (leftPalmIndex == rightPalmIndex) { // right hand only applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, hand->getPalms()[leftPalmIndex]); - restoreLeftHandPosition(handRestorePercent); + restoreLeftHandPosition(HAND_RESTORATION_RATE); } else { applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices, From 4185faa82ad72aa632baacee17e40ab652f9f1a1 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 8 May 2014 01:06:26 -0700 Subject: [PATCH 024/108] Tweaks to improve mouth and brow appearance when just audio driven --- interface/src/avatar/Head.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 9cb8ea3d9c..ce02bb8e55 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -106,15 +106,10 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { const float BROW_LIFT_THRESHOLD = 100.0f; if (_audioAttack > BROW_LIFT_THRESHOLD) { - _browAudioLift += sqrtf(_audioAttack) * 0.00005f; + _browAudioLift += sqrtf(_audioAttack) * 0.01f; } - - const float CLAMP = 0.01f; - if (_browAudioLift > CLAMP) { - _browAudioLift = CLAMP; - } - - _browAudioLift *= 0.7f; + _browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f); + qDebug() << _browAudioLift; const float BLINK_SPEED = 10.0f; const float FULLY_OPEN = 0.0f; @@ -147,12 +142,12 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { } // use data to update fake Faceshift blendshape coefficients - const float BROW_LIFT_SCALE = 500.0f; - const float JAW_OPEN_SCALE = 0.01f; - const float JAW_OPEN_DEAD_ZONE = 0.75f; - Application::getInstance()->getFaceshift()->updateFakeCoefficients(_leftEyeBlink, _rightEyeBlink, - min(1.0f, _browAudioLift * BROW_LIFT_SCALE), glm::clamp(sqrt(_averageLoudness * JAW_OPEN_SCALE) - - JAW_OPEN_DEAD_ZONE, 0.0f, 1.0f), _blendshapeCoefficients); + const float JAW_OPEN_SCALE = 10.f; + Application::getInstance()->getFaceshift()->updateFakeCoefficients(_leftEyeBlink, + _rightEyeBlink, + _browAudioLift, + glm::clamp(log(_averageLoudness) / JAW_OPEN_SCALE, 0.0f, 1.0f), + _blendshapeCoefficients); } if (!isMine) { From ef67a72f23b55383c8da20bcb0a286e68a998881 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 May 2014 07:22:55 -0700 Subject: [PATCH 025/108] Remove icon paths from runningScriptsWidget.ui These icon paths are already set programmatically. --- interface/ui/runningScriptsWidget.ui | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/interface/ui/runningScriptsWidget.ui b/interface/ui/runningScriptsWidget.ui index 6abd0f2d5a..cb0106cd48 100644 --- a/interface/ui/runningScriptsWidget.ui +++ b/interface/ui/runningScriptsWidget.ui @@ -88,10 +88,6 @@ padding-top: 3px; Reload all - - - ../resources/images/reload.svg../resources/images/reload.svg - @@ -115,10 +111,6 @@ padding-top: 3px; Stop all - - - ../resources/images/stop.svg../resources/images/stop.svg - @@ -251,10 +243,6 @@ padding-top: 3px; Load script - - - ../resources/images/plus.svg../resources/images/plus.svg - widgetTitle currentlyRunningLabel From fdaa42b21902813475fee733b5f8cce784cbfa42 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 May 2014 07:54:49 -0700 Subject: [PATCH 026/108] Add hifi:// clickable links to chat The handling also needed to be updated to handle domains correctly - just routing to goToURL instead of gTo fixes this. --- interface/src/Menu.cpp | 4 +--- interface/src/ui/ChatWindow.cpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ad2b220d55..a027a7939c 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -984,9 +984,7 @@ void Menu::goToUser(const QString& user) { /// Open a url, shortcutting any "hifi" scheme URLs to the local application. void Menu::openUrl(const QUrl& url) { if (url.scheme() == "hifi") { - QString path = url.toString(QUrl::RemoveScheme); - path = path.remove(QRegExp("^:?/*")); - goTo(path); + goToURL(url.toString()); } else { QDesktopServices::openUrl(url); } diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 72445fa69a..fce900f352 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -30,7 +30,7 @@ const int NUM_MESSAGES_TO_TIME_STAMP = 20; -const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)"); +const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?)|(?:hifi))://\\S+)"); const QRegularExpression regexHifiLinks("([#@]\\S+)"); const QString mentionSoundsPath("/sounds/mention/"); const QString mentionRegex("@(\\b%1\\b)"); From 71789b65e71bd5c2f836ab0a7e6a7221d6b1f603 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 8 May 2014 11:02:35 -0700 Subject: [PATCH 027/108] remove debug --- interface/src/avatar/Head.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index ce02bb8e55..2d0599b31f 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -109,8 +109,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { _browAudioLift += sqrtf(_audioAttack) * 0.01f; } _browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f); - qDebug() << _browAudioLift; - + const float BLINK_SPEED = 10.0f; const float FULLY_OPEN = 0.0f; const float FULLY_CLOSED = 1.0f; From a8a53e35dd4762e7567aa9d27d68706ca235fbbc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 8 May 2014 11:12:16 -0700 Subject: [PATCH 028/108] make sure shouldStop for injector defaults to false --- libraries/audio/src/AudioInjector.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 364d14cfbb..1fe9f1336f 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -22,14 +22,18 @@ #include "AudioInjector.h" AudioInjector::AudioInjector(QObject* parent) : - QObject(parent) + QObject(parent), + _sound(NULL), + _options(), + _shouldStop(false) { } AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) : _sound(sound), - _options(injectorOptions) + _options(injectorOptions), + _shouldStop(false) { } From 96a9a478712a2052414b6476970dd2f3a0db93e2 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Thu, 8 May 2014 20:54:28 +0200 Subject: [PATCH 029/108] minor code fixes --- interface/src/ui/SnapshotShareDialog.cpp | 10 +++++----- interface/src/ui/SnapshotShareDialog.h | 4 ++-- snapshotShareDialog.cpp | 9 --------- snapshotShareDialog.h | 14 -------------- 4 files changed, 7 insertions(+), 30 deletions(-) delete mode 100644 snapshotShareDialog.cpp delete mode 100644 snapshotShareDialog.h diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp index fc738c9761..5ba284618f 100644 --- a/interface/src/ui/SnapshotShareDialog.cpp +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -13,11 +13,11 @@ #include "AccountManager.h" #include -#include -#include -#include -#include #include +#include +#include +#include +#include const int NARROW_SNAPSHOT_DIALOG_SIZE = 500; @@ -177,7 +177,7 @@ void SnapshotShareDialog::postRequestFinished() { errorMessage = errorArray.first().toString(); } } - QMessageBox::warning(this, "", SHARE_DEFAULT_ERROR); + QMessageBox::warning(this, "", errorMessage); } } diff --git a/interface/src/ui/SnapshotShareDialog.h b/interface/src/ui/SnapshotShareDialog.h index bc92e63e4c..a8795d578a 100644 --- a/interface/src/ui/SnapshotShareDialog.h +++ b/interface/src/ui/SnapshotShareDialog.h @@ -14,9 +14,9 @@ #include "ui_shareSnapshot.h" -#include -#include #include +#include +#include class SnapshotShareDialog : public QDialog { Q_OBJECT diff --git a/snapshotShareDialog.cpp b/snapshotShareDialog.cpp deleted file mode 100644 index 53a445292f..0000000000 --- a/snapshotShareDialog.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// -// snapshotShareDialog.cpp -// hifi -// -// Created by Stojce Slavkovski on 2/16/14. -// -// - -#include "snapshotShareDialog.h" diff --git a/snapshotShareDialog.h b/snapshotShareDialog.h deleted file mode 100644 index 77e92216fd..0000000000 --- a/snapshotShareDialog.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// snapshotShareDialog.h -// hifi -// -// Created by Stojce Slavkovski on 2/16/14. -// -// - -#ifndef __hifi__snapshotShareDialog__ -#define __hifi__snapshotShareDialog__ - -#include - -#endif /* defined(__hifi__snapshotShareDialog__) */ From f866828db2a2f4f3dd43ae742a7fb443e103f222 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 8 May 2014 15:20:14 -0700 Subject: [PATCH 030/108] Avatar attachment scripting bits. --- libraries/avatars/src/AvatarData.cpp | 114 ++++++++++++++++++- libraries/avatars/src/AvatarData.h | 46 +++++++- libraries/script-engine/src/ScriptEngine.cpp | 1 + 3 files changed, 155 insertions(+), 6 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index ead5967c78..b226da8b5c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -661,16 +661,66 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { updateJointMappings(); } -void AvatarData::setAttachmentData(const QVector& attachmentData) { - _attachmentData = attachmentData; -} - void AvatarData::setDisplayName(const QString& displayName) { _displayName = displayName; qDebug() << "Changing display name for avatar to" << displayName; } +QVector AvatarData::getAttachmentData() const { + if (QThread::currentThread() != thread()) { + QVector result; + QMetaObject::invokeMethod(const_cast(this), "getAttachmentData", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QVector, result)); + return result; + } + return _attachmentData; +} + +void AvatarData::setAttachmentData(const QVector& attachmentData) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setAttachmentData", Q_ARG(const QVector&, attachmentData)); + return; + } + _attachmentData = attachmentData; +} + +void AvatarData::attach(const QString& modelURL, const QString& jointName, + const glm::vec3& translation, const glm::quat& rotation, float scale) { + QVector attachmentData = getAttachmentData(); + AttachmentData data; + data.modelURL = modelURL; + data.jointName = jointName; + data.translation = translation; + data.rotation = rotation; + data.scale = scale; + attachmentData.append(data); + setAttachmentData(attachmentData); +} + +void AvatarData::detachOne(const QString& modelURL, const QString& jointName) { + QVector attachmentData = getAttachmentData(); + for (QVector::iterator it = attachmentData.begin(); it != attachmentData.end(); it++) { + if (it->modelURL == modelURL && (jointName.isEmpty() || it->jointName == jointName)) { + attachmentData.erase(it); + return; + } + } + setAttachmentData(attachmentData); +} + +void AvatarData::detachAll(const QString& modelURL, const QString& jointName) { + QVector attachmentData = getAttachmentData(); + for (QVector::iterator it = attachmentData.begin(); it != attachmentData.end(); ) { + if (it->modelURL == modelURL && (jointName.isEmpty() || it->jointName == jointName)) { + it = attachmentData.erase(it); + } else { + it++; + } + } + setAttachmentData(attachmentData); +} + void AvatarData::setBillboard(const QByteArray& billboard) { _billboard = billboard; @@ -793,3 +843,59 @@ QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) { attachment.translation >> attachment.rotation >> attachment.scale; } +void AttachmentDataObject::setModelURL(const QString& modelURL) const { + AttachmentData data = qscriptvalue_cast(thisObject()); + data.modelURL = modelURL; + thisObject() = engine()->toScriptValue(data); +} + +QString AttachmentDataObject::getModelURL() const { + return qscriptvalue_cast(thisObject()).modelURL.toString(); +} + +void AttachmentDataObject::setJointName(const QString& jointName) const { + AttachmentData data = qscriptvalue_cast(thisObject()); + data.jointName = jointName; + thisObject() = engine()->toScriptValue(data); +} + +QString AttachmentDataObject::getJointName() const { + return qscriptvalue_cast(thisObject()).jointName; +} + +void AttachmentDataObject::setTranslation(const glm::vec3& translation) const { + AttachmentData data = qscriptvalue_cast(thisObject()); + data.translation = translation; + thisObject() = engine()->toScriptValue(data); +} + +glm::vec3 AttachmentDataObject::getTranslation() const { + return qscriptvalue_cast(thisObject()).translation; +} + +void AttachmentDataObject::setRotation(const glm::quat& rotation) const { + AttachmentData data = qscriptvalue_cast(thisObject()); + data.rotation = rotation; + thisObject() = engine()->toScriptValue(data); +} + +glm::quat AttachmentDataObject::getRotation() const { + return qscriptvalue_cast(thisObject()).rotation; +} + +void AttachmentDataObject::setScale(float scale) const { + AttachmentData data = qscriptvalue_cast(thisObject()); + data.scale = scale; + thisObject() = engine()->toScriptValue(data); +} + +float AttachmentDataObject::getScale() const { + return qscriptvalue_cast(thisObject()).scale; +} + +void registerAvatarTypes(QScriptEngine* engine) { + qScriptRegisterSequenceMetaType >(engine); + engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( + new AttachmentDataObject(), QScriptEngine::ScriptOwnership)); +} + diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 50c1608be8..1fd8f6c425 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -41,6 +41,7 @@ typedef unsigned long long quint64; #include #include #include +#include #include #include @@ -123,6 +124,7 @@ class AvatarData : public QObject { Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName) Q_PROPERTY(QString faceModelURL READ getFaceModelURLFromScript WRITE setFaceModelURLFromScript) Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript) + Q_PROPERTY(QVector attachmentData READ getAttachmentData WRITE setAttachmentData) Q_PROPERTY(QString billboardURL READ getBillboardURL WRITE setBillboardFromURL) Q_PROPERTY(QStringList jointNames READ getJointNames) @@ -229,13 +231,21 @@ public: const QUrl& getFaceModelURL() const { return _faceModelURL; } QString getFaceModelURLString() const { return _faceModelURL.toString(); } const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; } - const QVector& getAttachmentData() const { return _attachmentData; } const QString& getDisplayName() const { return _displayName; } virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); - virtual void setAttachmentData(const QVector& attachmentData); + virtual void setDisplayName(const QString& displayName); + Q_INVOKABLE QVector getAttachmentData() const; + Q_INVOKABLE virtual void setAttachmentData(const QVector& attachmentData); + + Q_INVOKABLE virtual void attach(const QString& modelURL, const QString& jointName = QString(), + const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f); + + Q_INVOKABLE void detachOne(const QString& modelURL, const QString& jointName = QString()); + Q_INVOKABLE void detachAll(const QString& modelURL, const QString& jointName = QString()); + virtual void setBillboard(const QByteArray& billboard); const QByteArray& getBillboard() const { return _billboard; } @@ -347,4 +357,36 @@ public: QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment); QDataStream& operator>>(QDataStream& in, AttachmentData& attachment); +Q_DECLARE_METATYPE(AttachmentData) +Q_DECLARE_METATYPE(QVector) + +/// Scriptable wrapper for attachments. +class AttachmentDataObject : public QObject, protected QScriptable { + Q_OBJECT + Q_PROPERTY(QString modelURL READ getModelURL WRITE setModelURL) + Q_PROPERTY(QString jointName READ getJointName WRITE setJointName) + Q_PROPERTY(glm::vec3 translation READ getTranslation WRITE setTranslation) + Q_PROPERTY(glm::quat rotation READ getRotation WRITE setRotation) + Q_PROPERTY(float scale READ getScale WRITE setScale) + +public: + + Q_INVOKABLE void setModelURL(const QString& modelURL) const; + Q_INVOKABLE QString getModelURL() const; + + Q_INVOKABLE void setJointName(const QString& jointName) const; + Q_INVOKABLE QString getJointName() const; + + Q_INVOKABLE void setTranslation(const glm::vec3& translation) const; + Q_INVOKABLE glm::vec3 getTranslation() const; + + Q_INVOKABLE void setRotation(const glm::quat& rotation) const; + Q_INVOKABLE glm::quat getRotation() const; + + Q_INVOKABLE void setScale(float scale) const; + Q_INVOKABLE float getScale() const; +}; + +void registerAvatarTypes(QScriptEngine* engine); + #endif // hifi_AvatarData_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 9be2cb5252..be97b37b46 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -210,6 +210,7 @@ void ScriptEngine::init() { registerEventTypes(&_engine); registerMenuItemProperties(&_engine); registerAnimationTypes(&_engine); + registerAvatarTypes(&_engine); qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue); qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue); From c02b708ee5605db06caebd85548166b5ebc99989 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 8 May 2014 15:54:30 -0700 Subject: [PATCH 031/108] Provide the option to avoid making duplicate attachments. --- examples/gun.js | 7 ++++++- interface/src/avatar/Avatar.cpp | 4 +++- libraries/avatars/src/AvatarData.cpp | 27 ++++++++++++++++++++++++--- libraries/avatars/src/AvatarData.h | 3 ++- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/examples/gun.js b/examples/gun.js index 4bfdb140b8..6cdf634239 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -39,6 +39,8 @@ var impactSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-pub var targetHitSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Space%20Invaders/hit.raw"); var targetLaunchSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Space%20Invaders/shoot.raw"); +var gunModel = "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/attachments/Raygun2.fst"; + var audioOptions = new AudioInjectionOptions(); audioOptions.volume = 0.9; @@ -190,6 +192,8 @@ function keyPressEvent(event) { } } +MyAvatar.attach(gunModel, "RightHand", {x: -0.10, y: 0.0, z: 0.0}, Quat.fromPitchYawRollDegrees(-90, 180, 0), 0.14); + function update(deltaTime) { // Check for mouseLook movement, update rotation // rotate body yaw for yaw received from mouse @@ -303,7 +307,8 @@ function mouseMoveEvent(event) { function scriptEnding() { Overlays.deleteOverlay(reticle); - Overlays.deleteOverlay(text); + Overlays.deleteOverlay(text); + MyAvatar.detachOne(gunModel); } Particles.particleCollisionWithVoxel.connect(particleCollisionWithVoxel); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index bc2bbb6712..bac339813f 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -709,7 +709,9 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { void Avatar::setAttachmentData(const QVector& attachmentData) { AvatarData::setAttachmentData(attachmentData); - + if (QThread::currentThread() != thread()) { + return; + } // make sure we have as many models as attachments while (_attachmentModels.size() < attachmentData.size()) { Model* model = new Model(this); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index b226da8b5c..26613e51d0 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -685,9 +685,22 @@ void AvatarData::setAttachmentData(const QVector& attachmentData _attachmentData = attachmentData; } -void AvatarData::attach(const QString& modelURL, const QString& jointName, - const glm::vec3& translation, const glm::quat& rotation, float scale) { +void AvatarData::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation, + const glm::quat& rotation, float scale, bool allowDuplicates) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "attach", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName), + Q_ARG(const glm::vec3&, translation), Q_ARG(const glm::quat&, rotation), + Q_ARG(float, scale), Q_ARG(bool, allowDuplicates)); + return; + } QVector attachmentData = getAttachmentData(); + if (!allowDuplicates) { + foreach (const AttachmentData& data, attachmentData) { + if (data.modelURL == modelURL && (jointName.isEmpty() || data.jointName == jointName)) { + return; + } + } + } AttachmentData data; data.modelURL = modelURL; data.jointName = jointName; @@ -699,17 +712,25 @@ void AvatarData::attach(const QString& modelURL, const QString& jointName, } void AvatarData::detachOne(const QString& modelURL, const QString& jointName) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "detachOne", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName)); + return; + } QVector attachmentData = getAttachmentData(); for (QVector::iterator it = attachmentData.begin(); it != attachmentData.end(); it++) { if (it->modelURL == modelURL && (jointName.isEmpty() || it->jointName == jointName)) { attachmentData.erase(it); + setAttachmentData(attachmentData); return; } } - setAttachmentData(attachmentData); } void AvatarData::detachAll(const QString& modelURL, const QString& jointName) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "detachAll", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName)); + return; + } QVector attachmentData = getAttachmentData(); for (QVector::iterator it = attachmentData.begin(); it != attachmentData.end(); ) { if (it->modelURL == modelURL && (jointName.isEmpty() || it->jointName == jointName)) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 1fd8f6c425..cc5ab16e35 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -241,7 +241,8 @@ public: Q_INVOKABLE virtual void setAttachmentData(const QVector& attachmentData); Q_INVOKABLE virtual void attach(const QString& modelURL, const QString& jointName = QString(), - const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f); + const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, + bool allowDuplicates = false); Q_INVOKABLE void detachOne(const QString& modelURL, const QString& jointName = QString()); Q_INVOKABLE void detachAll(const QString& modelURL, const QString& jointName = QString()); From acab09b89a79ea04e1aa221040dd1c935a7753cd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 8 May 2014 16:23:15 -0700 Subject: [PATCH 032/108] Update billboard when attachments change. --- interface/src/avatar/MyAvatar.cpp | 13 +++++++++++++ interface/src/avatar/MyAvatar.h | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c3591862d1..7bc35ebd6d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -584,6 +584,14 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _billboardValid = false; } +void MyAvatar::setAttachmentData(const QVector& attachmentData) { + Avatar::setAttachmentData(attachmentData); + if (QThread::currentThread() != thread()) { + return; + } + _billboardValid = false; +} + void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { return; // wait until both models are loaded @@ -1247,6 +1255,11 @@ void MyAvatar::maybeUpdateBillboard() { if (_billboardValid || !(_skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures())) { return; } + foreach (Model* model, _attachmentModels) { + if (!model->isLoadedWithTextures()) { + return; + } + } QImage image = Application::getInstance()->renderAvatarBillboard(); _billboard.clear(); QBuffer buffer(&_billboard); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c07c27033c..885d56b61f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -86,7 +86,8 @@ public: virtual void clearJointData(int index); virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); - + virtual void setAttachmentData(const QVector& attachmentData); + virtual void setCollisionGroups(quint32 collisionGroups); void setMotionBehaviorsByScript(quint32 flags); From cd78be7d2500a2a4a00bd19f1c147302969aab50 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 8 May 2014 16:47:18 -0700 Subject: [PATCH 033/108] Some work on mouse tools for editModels.js --- examples/editModels.js | 34 +++++++++++++++++++++++++++++++++- examples/testScript.js | 0 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 examples/testScript.js diff --git a/examples/editModels.js b/examples/editModels.js index ecf398edfa..737b6a85a5 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -33,6 +33,7 @@ var modelURLs = [ "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/slimer.fbx", ]; +var tools = []; var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; var numberOfTools = 1; var toolHeight = 50; @@ -44,13 +45,44 @@ var toolsY = (windowDimensions.y - toolsHeight) / 2; var firstModel = Overlays.addOverlay("image", { - x: 0, y: 0, width: toolWidth, height: toolHeight, subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, imageURL: toolIconUrl + "voxel-tool.svg", x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight, visible: true, alpha: 0.9 }); +function Tool(iconURL) { + this.overlay = Overlays.addOverlay("image", { + subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, + imageURL: iconURL, + x: toolsX, + y: toolsY + ((toolHeight + toolVerticalSpacing) * tools.length), + width: toolWidth, + height: toolHeight, + visible: true, + alpha: 0.9 + }); + + + this.cleanup = function() { + Ovelays.deleteOverlay(this.overlay); + } + tools[tools.length] = this; + return tools.length - 1; +} +Tool.ICON_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; +Tool.HEIGHT = 50; +Tool.WIDTH = 50; + +function ToolBar(direction, x, y) { + this.tools = []; + + this.numberOfTools = function() { + return this.tools.length; + } +} +ToolBar.SPACING = 4; + function controller(wichSide) { this.side = wichSide; diff --git a/examples/testScript.js b/examples/testScript.js new file mode 100644 index 0000000000..e69de29bb2 From f019e2366e1065c06042920a7adf6115207ff1a5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 8 May 2014 17:10:02 -0700 Subject: [PATCH 034/108] Add lift for walking up stairs Also some fixes for freeing avatars trapped in voxel collisions --- interface/src/avatar/MyAvatar.cpp | 97 ++++++++++++++++++++++++------- interface/src/avatar/MyAvatar.h | 2 +- 2 files changed, 77 insertions(+), 22 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index dd68201b46..8e44138679 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -46,6 +46,9 @@ const float PITCH_SPEED = 100.0f; // degrees/sec const float COLLISION_RADIUS_SCALAR = 1.2f; // pertains to avatar-to-avatar collisions const float COLLISION_RADIUS_SCALE = 0.125f; +const float MIN_KEYBOARD_CONTROL_SPEED = 2.0f; +const float MAX_WALKING_SPEED = 3.0f * MIN_KEYBOARD_CONTROL_SPEED; + const float DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5.0f * 1000.0f; // TODO: normalize avatar speed for standard avatar size, then scale all motion logic @@ -64,7 +67,7 @@ MyAvatar::MyAvatar() : _distanceToNearestAvatar(std::numeric_limits::max()), _wasPushing(false), _isPushing(false), - _wasStuck(false), + _trapDuration(0.0f), _thrust(0.0f), _motorVelocity(0.0f), _motorTimescale(DEFAULT_MOTOR_TIMESCALE), @@ -215,7 +218,7 @@ void MyAvatar::simulate(float deltaTime) { if (_collisionGroups & COLLISION_GROUP_VOXELS) { updateCollisionWithVoxels(deltaTime, radius); } else { - _wasStuck = false; + _trapDuration = 0.0f; } if (_collisionGroups & COLLISION_GROUP_AVATARS) { updateCollisionWithAvatars(deltaTime); @@ -697,8 +700,6 @@ void MyAvatar::updateMotorFromKeyboard(float deltaTime, bool walking) { if (directionLength > EPSILON) { direction /= directionLength; // the finalMotorSpeed depends on whether we are walking or not - const float MIN_KEYBOARD_CONTROL_SPEED = 2.0f; - const float MAX_WALKING_SPEED = 3.0f * MIN_KEYBOARD_CONTROL_SPEED; float finalMaxMotorSpeed = walking ? MAX_WALKING_SPEED : _maxMotorSpeed; float motorLength = glm::length(_motorVelocity); @@ -962,48 +963,102 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) { static CollisionList myCollisions(64); void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { - const float MIN_STUCK_SPEED = 100.0f; + const float MAX_VOXEL_COLLISION_SPEED = 100.0f; float speed = glm::length(_velocity); - if (speed > MIN_STUCK_SPEED) { + if (speed > MAX_VOXEL_COLLISION_SPEED) { // don't even bother to try to collide against voxles when moving very fast + _trapDuration = 0.0f; return; } + bool isTrapped = false; myCollisions.clear(); const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape(); - if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions)) { + if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions, Octree::TryLock)) { const float VOXEL_ELASTICITY = 0.0f; const float VOXEL_DAMPING = 0.0f; float capsuleRadius = boundingShape.getRadius(); - + float capsuleHalfHeight = boundingShape.getHalfHeight(); + const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight; + const float MIN_STEP_HEIGHT = 0.0f; + glm::vec3 footBase = boundingShape.getPosition() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection; + float highestStep = 0.0f; + glm::vec3 stepPenetration(0.0f); glm::vec3 totalPenetration(0.0f); - bool isStuck = false; + for (int i = 0; i < myCollisions.size(); ++i) { CollisionInfo* collision = myCollisions[i]; - float depth = glm::length(collision->_penetration); - if (depth > capsuleRadius) { - isStuck = true; - if (_wasStuck) { - glm::vec3 cubeCenter = collision->_vecData; - float cubeSide = collision->_floatData; + glm::vec3 cubeCenter = collision->_vecData; + float cubeSide = collision->_floatData; + float verticalDepth = glm::dot(collision->_penetration, _worldUpDirection); + float horizontalDepth = glm::length(collision->_penetration - verticalDepth * _worldUpDirection); + const float MAX_TRAP_PERIOD = 0.125f; + if (horizontalDepth > capsuleRadius || fabsf(verticalDepth) > MAX_STEP_HEIGHT) { + isTrapped = true; + if (_trapDuration > MAX_TRAP_PERIOD) { float distance = glm::dot(boundingShape.getPosition() - cubeCenter, _worldUpDirection); if (distance < 0.0f) { distance = fabsf(distance) + 0.5f * cubeSide; } - distance += capsuleRadius + boundingShape.getHalfHeight(); + distance += capsuleRadius + capsuleHalfHeight; totalPenetration = addPenetrations(totalPenetration, - distance * _worldUpDirection); continue; } + } else if (_trapDuration > MAX_TRAP_PERIOD) { + // we're trapped, ignore this collision + continue; } totalPenetration = addPenetrations(totalPenetration, collision->_penetration); + if (glm::dot(collision->_penetration, _velocity) >= 0.0f) { + glm::vec3 cubeTop = cubeCenter + (0.5f * cubeSide) * _worldUpDirection; + float stepHeight = glm::dot(_worldUpDirection, cubeTop - footBase); + if (stepHeight > highestStep) { + highestStep = stepHeight; + stepPenetration = collision->_penetration; + } + } + } + + float penetrationLength = glm::length(totalPenetration); + if (penetrationLength < EPSILON) { + _trapDuration = 0.0f; + return; + } + float verticalPenetration = glm::dot(totalPenetration, _worldUpDirection); + if (highestStep > MIN_STEP_HEIGHT && highestStep < MAX_STEP_HEIGHT && verticalPenetration <= 0.0f) { + // we're colliding against an edge + glm::vec3 targetVelocity = _motorVelocity; + if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) { + // rotate _motorVelocity into world frame + glm::quat rotation = getHead()->getCameraOrientation(); + targetVelocity = rotation * _motorVelocity; + } + if (_wasPushing && glm::dot(targetVelocity, totalPenetration) > EPSILON) { + // we're puhing into the edge, so we want to lift + + // remove unhelpful horizontal component of the step's penetration + totalPenetration -= stepPenetration - (glm::dot(stepPenetration, _worldUpDirection) * _worldUpDirection); + + // further adjust penetration to help lift + float liftSpeed = glm::max(MAX_WALKING_SPEED, speed); + float thisStep = glm::min(liftSpeed * deltaTime, highestStep); + float extraStep = glm::dot(totalPenetration, _worldUpDirection) + thisStep; + if (extraStep > 0.0f) { + totalPenetration -= extraStep * _worldUpDirection; + } + + _position -= totalPenetration; + } else { + // we're not pushing into the edge, so let the avatar fall + applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING); + } + } else { + applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING); } - applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING); - _wasStuck = isStuck; const float VOXEL_COLLISION_FREQUENCY = 0.5f; updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY); - } else { - _wasStuck = false; - } + } + _trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f; } void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity, float damping) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1c80d3b969..ec20f69d6a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -125,7 +125,7 @@ private: bool _wasPushing; bool _isPushing; - bool _wasStuck; + float _trapDuration; // seconds that avatar has been trapped by collisions glm::vec3 _thrust; // final acceleration from outside sources for the current frame glm::vec3 _motorVelocity; // intended velocity of avatar motion From c424f6ce69b41f9b65bc2a320cbf700562d798fd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 8 May 2014 18:00:41 -0700 Subject: [PATCH 035/108] Save attachment data for attachment model/skeleton model combinations, let scripts use those saved parameters so that you can tweak the location of (for example) the gun and have it saved appropriately. --- interface/src/avatar/MyAvatar.cpp | 66 ++++++++++++++++++++++++++ interface/src/avatar/MyAvatar.h | 7 +++ interface/src/ui/AttachmentsDialog.cpp | 56 ++++++++++++++++------ interface/src/ui/AttachmentsDialog.h | 3 ++ libraries/avatars/src/AvatarData.cpp | 4 +- libraries/avatars/src/AvatarData.h | 2 +- 6 files changed, 120 insertions(+), 18 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7bc35ebd6d..c46135f069 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -513,6 +513,55 @@ void MyAvatar::loadData(QSettings* settings) { settings->endGroup(); } +void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { + QSettings* settings = Application::getInstance()->lockSettings(); + settings->beginGroup("savedAttachmentData"); + settings->beginGroup(_skeletonModel.getURL().toString()); + settings->beginGroup(attachment.modelURL.toString()); + + settings->setValue("jointName", attachment.jointName); + settings->setValue("translation_x", attachment.translation.x); + settings->setValue("translation_y", attachment.translation.y); + settings->setValue("translation_z", attachment.translation.z); + glm::vec3 eulers = safeEulerAngles(attachment.rotation); + settings->setValue("rotation_x", eulers.x); + settings->setValue("rotation_y", eulers.y); + settings->setValue("rotation_z", eulers.z); + settings->setValue("scale", attachment.scale); + + settings->endGroup(); + settings->endGroup(); + settings->endGroup(); + Application::getInstance()->unlockSettings(); +} + +AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL) const { + QSettings* settings = Application::getInstance()->lockSettings(); + settings->beginGroup("savedAttachmentData"); + settings->beginGroup(_skeletonModel.getURL().toString()); + settings->beginGroup(modelURL.toString()); + + 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); + + settings->endGroup(); + settings->endGroup(); + settings->endGroup(); + Application::getInstance()->unlockSettings(); + + return attachment; +} + int MyAvatar::parseDataAtOffset(const QByteArray& packet, int offset) { qDebug() << "Error: ignoring update packet for MyAvatar" << " packetLength = " << packet.size() @@ -592,6 +641,23 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) _billboardValid = false; } +void MyAvatar::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation, + const glm::quat& rotation, float scale, bool allowDuplicates, bool useSaved) { + if (QThread::currentThread() != thread()) { + Avatar::attach(modelURL, jointName, translation, rotation, scale, allowDuplicates, useSaved); + return; + } + if (useSaved) { + AttachmentData attachment = loadAttachmentData(modelURL); + if (!attachment.jointName.isEmpty()) { + Avatar::attach(modelURL, attachment.jointName, attachment.translation, + attachment.rotation, attachment.scale, allowDuplicates, useSaved); + return; + } + } + Avatar::attach(modelURL, jointName, translation, rotation, scale, allowDuplicates, useSaved); +} + void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { return; // wait until both models are loaded diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 885d56b61f..b41c2b6d99 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -66,6 +66,9 @@ public: void saveData(QSettings* settings); void loadData(QSettings* settings); + void saveAttachmentData(const AttachmentData& attachment) const; + AttachmentData loadAttachmentData(const QUrl& modelURL) const; + // Set what driving keys are being pressed to control thrust levels void setDriveKeys(int key, float val) { _driveKeys[key] = val; }; bool getDriveKeys(int key) { return _driveKeys[key] != 0.f; }; @@ -88,6 +91,10 @@ public: virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setAttachmentData(const QVector& attachmentData); + virtual void attach(const QString& modelURL, const QString& jointName = QString(), + const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, + bool allowDuplicates = false, bool useSaved = true); + virtual void setCollisionGroups(quint32 collisionGroups); void setMotionBehaviorsByScript(quint32 flags); diff --git a/interface/src/ui/AttachmentsDialog.cpp b/interface/src/ui/AttachmentsDialog.cpp index 016098699b..f9f49738e9 100644 --- a/interface/src/ui/AttachmentsDialog.cpp +++ b/interface/src/ui/AttachmentsDialog.cpp @@ -76,27 +76,28 @@ void AttachmentsDialog::addAttachment(const AttachmentData& data) { _attachments->insertWidget(_attachments->count() - 1, new AttachmentPanel(this, data)); } -static QDoubleSpinBox* createTranslationBox(AttachmentsDialog* dialog, float value) { +static QDoubleSpinBox* createTranslationBox(AttachmentPanel* panel, float value) { QDoubleSpinBox* box = new QDoubleSpinBox(); box->setSingleStep(0.01); box->setMinimum(-FLT_MAX); box->setMaximum(FLT_MAX); box->setValue(value); - dialog->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); + panel->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); return box; } -static QDoubleSpinBox* createRotationBox(AttachmentsDialog* dialog, float value) { +static QDoubleSpinBox* createRotationBox(AttachmentPanel* panel, float value) { QDoubleSpinBox* box = new QDoubleSpinBox(); box->setMinimum(-180.0); box->setMaximum(180.0); box->setWrapping(true); box->setValue(value); - dialog->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); + panel->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); return box; } -AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data) { +AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data) : + _dialog(dialog) { setFrameStyle(QFrame::StyledPanel); QFormLayout* layout = new QFormLayout(); @@ -107,7 +108,7 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData layout->addRow("Model URL:", urlBox); urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1); _modelURL->setText(data.modelURL.toString()); - dialog->connect(_modelURL, SIGNAL(returnPressed()), SLOT(updateAttachmentData())); + connect(_modelURL, SIGNAL(returnPressed()), SLOT(modelURLChanged())); QPushButton* chooseURL = new QPushButton("Choose"); urlBox->addWidget(chooseURL); connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL())); @@ -120,26 +121,26 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData } } _jointName->setCurrentText(data.jointName); - dialog->connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(updateAttachmentData())); + connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(updateAttachmentData())); QHBoxLayout* translationBox = new QHBoxLayout(); - translationBox->addWidget(_translationX = createTranslationBox(dialog, data.translation.x)); - translationBox->addWidget(_translationY = createTranslationBox(dialog, data.translation.y)); - translationBox->addWidget(_translationZ = createTranslationBox(dialog, data.translation.z)); + translationBox->addWidget(_translationX = createTranslationBox(this, data.translation.x)); + translationBox->addWidget(_translationY = createTranslationBox(this, data.translation.y)); + translationBox->addWidget(_translationZ = createTranslationBox(this, data.translation.z)); layout->addRow("Translation:", translationBox); QHBoxLayout* rotationBox = new QHBoxLayout(); glm::vec3 eulers = glm::degrees(safeEulerAngles(data.rotation)); - rotationBox->addWidget(_rotationX = createRotationBox(dialog, eulers.x)); - rotationBox->addWidget(_rotationY = createRotationBox(dialog, eulers.y)); - rotationBox->addWidget(_rotationZ = createRotationBox(dialog, eulers.z)); + rotationBox->addWidget(_rotationX = createRotationBox(this, eulers.x)); + rotationBox->addWidget(_rotationY = createRotationBox(this, eulers.y)); + rotationBox->addWidget(_rotationZ = createRotationBox(this, eulers.z)); layout->addRow("Rotation:", rotationBox); layout->addRow("Scale:", _scale = new QDoubleSpinBox()); _scale->setSingleStep(0.01); _scale->setMaximum(FLT_MAX); _scale->setValue(data.scale); - dialog->connect(_scale, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); + connect(_scale, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); QPushButton* remove = new QPushButton("Delete"); layout->addRow(remove); @@ -165,5 +166,30 @@ void AttachmentPanel::chooseModelURL() { void AttachmentPanel::setModelURL(const QString& url) { _modelURL->setText(url); - emit _modelURL->returnPressed(); + modelURLChanged(); +} + +void AttachmentPanel::modelURLChanged() { + // check for saved attachment data + AttachmentData attachment = Application::getInstance()->getAvatar()->loadAttachmentData(_modelURL->text()); + if (!attachment.jointName.isEmpty()) { + _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); + } + _dialog->updateAttachmentData(); +} + +void AttachmentPanel::updateAttachmentData() { + // save the attachment data under the model URL (if any) + if (!_modelURL->text().isEmpty()) { + Application::getInstance()->getAvatar()->saveAttachmentData(getAttachmentData()); + } + _dialog->updateAttachmentData(); } diff --git a/interface/src/ui/AttachmentsDialog.h b/interface/src/ui/AttachmentsDialog.h index 4e67ae8882..7e9319fba8 100644 --- a/interface/src/ui/AttachmentsDialog.h +++ b/interface/src/ui/AttachmentsDialog.h @@ -60,9 +60,12 @@ private slots: void chooseModelURL(); void setModelURL(const QString& url); + void modelURLChanged(); + void updateAttachmentData(); private: + AttachmentsDialog* _dialog; QLineEdit* _modelURL; QComboBox* _jointName; QDoubleSpinBox* _translationX; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 26613e51d0..94066d9a1c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -686,11 +686,11 @@ void AvatarData::setAttachmentData(const QVector& attachmentData } void AvatarData::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation, - const glm::quat& rotation, float scale, bool allowDuplicates) { + const glm::quat& rotation, float scale, bool allowDuplicates, bool useSaved) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "attach", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName), Q_ARG(const glm::vec3&, translation), Q_ARG(const glm::quat&, rotation), - Q_ARG(float, scale), Q_ARG(bool, allowDuplicates)); + Q_ARG(float, scale), Q_ARG(bool, allowDuplicates), Q_ARG(bool, useSaved)); return; } QVector attachmentData = getAttachmentData(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index cc5ab16e35..dd604d06f5 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -242,7 +242,7 @@ public: Q_INVOKABLE virtual void attach(const QString& modelURL, const QString& jointName = QString(), const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, - bool allowDuplicates = false); + bool allowDuplicates = false, bool useSaved = true); Q_INVOKABLE void detachOne(const QString& modelURL, const QString& jointName = QString()); Q_INVOKABLE void detachAll(const QString& modelURL, const QString& jointName = QString()); From 95f6bcbbd65d2a2d5f758bbddae36d4c80fb841f Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 8 May 2014 22:54:35 -0700 Subject: [PATCH 036/108] finish air guitar, gun initial position on hand tweak --- examples/airGuitar.js | 85 ++++++++++++++++++++++++++++--------------- examples/gun.js | 2 +- 2 files changed, 56 insertions(+), 31 deletions(-) diff --git a/examples/airGuitar.js b/examples/airGuitar.js index 08898579a7..d7028b8ad7 100644 --- a/examples/airGuitar.js +++ b/examples/airGuitar.js @@ -25,22 +25,21 @@ function vMinus(a, b) { return rval; } -// First, load two percussion sounds to be used on the sticks +// Load sounds that will be played -var guitarType = 2; +var chords = new Array(); +// Nylon string guitar +chords[1] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+A.raw"); +chords[2] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+B.raw"); +chords[3] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+E.raw"); +// Electric guitar +chords[4] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+A+short.raw"); +chords[5] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+B+short.raw"); +chords[6] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+E+short.raw"); -if (guitarType == 1) { - var chord1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+A.raw"); - var chord2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+B.raw"); - var chord3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+E.raw"); -} else { - var chord1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+A+short.raw"); - var chord2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+B+short.raw"); - var chord3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+E+short.raw"); -} +var guitarSelector = 3; - -var whichChord = chord1; +var whichChord = chords[guitarSelector + 1]; var leftHanded = false; if (leftHanded) { @@ -55,40 +54,66 @@ var lastPosition = { x: 0.0, y: 0.0, z: 0.0 }; +var soundPlaying = false; +var selectorPressed = false; function checkHands(deltaTime) { for (var palm = 0; palm < 2; palm++) { var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1); - var speed = length(palmVelocity); + var speed = length(palmVelocity) / 4.0; var position = Controller.getSpatialControlPosition(palm * 2 + 1); var myPelvis = MyAvatar.position; + var trigger = Controller.getTriggerValue(strumHand); + var chord = Controller.getTriggerValue(chordHand); + + if ((chord > 0.1) && Audio.isInjectorPlaying(soundPlaying)) { + // If chord finger trigger pulled, stop current chord + Audio.stopInjector(soundPlaying); + } + + var BUTTON_COUNT = 6; + + // Change guitars if button FWD (5) pressed + if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 5)) { + if (!selectorPressed) { + if (guitarSelector == 0) { + guitarSelector = 3; + } else { + guitarSelector = 0; + } + selectorPressed = true; + } + } else { + selectorPressed = false; + } + + if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 1)) { + whichChord = chords[guitarSelector + 1]; + } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 2)) { + whichChord = chords[guitarSelector + 2]; + } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 3)) { + whichChord = chords[guitarSelector + 3]; + } if (palm == strumHand) { - var STRUM_HEIGHT_ABOVE_PELVIS = -0.30; + var STRUM_HEIGHT_ABOVE_PELVIS = 0.00; var strumTriggerHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS; //printVector(position); - if ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) { - // If hand passes downward through guitar strings, play a chord! + if ( ( ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) || + ((position.y > strumTriggerHeight) && (lastPosition.y <= strumTriggerHeight)) ) && (trigger > 0.1) ){ + // If hand passes downward or upward through 'strings', and finger trigger pulled, play var options = new AudioInjectionOptions(); options.position = position; if (speed > 1.0) { speed = 1.0; } options.volume = speed; - Audio.playSound(whichChord, options); + if (Audio.isInjectorPlaying(soundPlaying)) { + Audio.stopInjector(soundPlaying); + } + soundPlaying = Audio.playSound(whichChord, options); } lastPosition = Controller.getSpatialControlPosition(palm * 2 + 1); - } else { - // This is the chord controller - var distanceFromPelvis = Vec3.length(Vec3.subtract(position, myPelvis)); - //print(distanceFromPelvis); - if (distanceFromPelvis > 0.63) { - whichChord = chord3; - } else if (distanceFromPelvis > 0.55) { - whichChord = chord2; - } else { - whichChord = chord1; - } - } + } } } diff --git a/examples/gun.js b/examples/gun.js index 6cdf634239..07994bec48 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -192,7 +192,7 @@ function keyPressEvent(event) { } } -MyAvatar.attach(gunModel, "RightHand", {x: -0.10, y: 0.0, z: 0.0}, Quat.fromPitchYawRollDegrees(-90, 180, 0), 0.14); +MyAvatar.attach(gunModel, "RightHand", {x: -0.02, y: -.14, z: 0.07}, Quat.fromPitchYawRollDegrees(-70, -151, 72), 0.20); function update(deltaTime) { // Check for mouseLook movement, update rotation From 4a1672957e994e771e13a60acd95a53f39cd1ea0 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 8 May 2014 23:23:44 -0700 Subject: [PATCH 037/108] guitar is now auto-attached --- examples/airGuitar.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/airGuitar.js b/examples/airGuitar.js index d7028b8ad7..b7c4b72948 100644 --- a/examples/airGuitar.js +++ b/examples/airGuitar.js @@ -25,6 +25,9 @@ function vMinus(a, b) { return rval; } +// The model file to be used for the guitar +var guitarModel = "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/attachments/guitar.fst"; + // Load sounds that will be played var chords = new Array(); @@ -57,6 +60,8 @@ var lastPosition = { x: 0.0, var soundPlaying = false; var selectorPressed = false; +MyAvatar.attach(guitarModel, "Hips", {x: -0.0, y: -0.0, z: 0.0}, Quat.fromPitchYawRollDegrees(0, 0, 0), 1.0); + function checkHands(deltaTime) { for (var palm = 0; palm < 2; palm++) { var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1); @@ -117,5 +122,9 @@ function checkHands(deltaTime) { } } +function scriptEnding() { + MyAvatar.detachOne(guitarModel); +} // Connect a call back that happens every frame -Script.update.connect(checkHands); \ No newline at end of file +Script.update.connect(checkHands); +Script.scriptEnding.connect(scriptEnding); From a7f44bf2077737d72a88c9bd9e5c448ab307c8bd Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 9 May 2014 00:16:11 -0700 Subject: [PATCH 038/108] double fisted gun action --- examples/gun.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/examples/gun.js b/examples/gun.js index 07994bec48..e404ae1d4d 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -28,7 +28,7 @@ var BULLET_VELOCITY = 5.0; var MIN_THROWER_DELAY = 1000; var MAX_THROWER_DELAY = 1000; var LEFT_BUTTON_3 = 3; -var RELOAD_INTERVAL = 9; +var RELOAD_INTERVAL = 5; var showScore = false; @@ -92,12 +92,12 @@ function printVector(string, vector) { } function shootBullet(position, velocity) { - var BULLET_SIZE = 0.02; + var BULLET_SIZE = 0.01; var BULLET_GRAVITY = -0.02; Particles.addParticle( { position: position, radius: BULLET_SIZE, - color: { red: 200, green: 0, blue: 0 }, + color: { red: 10, green: 10, blue: 10 }, velocity: velocity, gravity: { x: 0, y: BULLET_GRAVITY, z: 0 }, damping: 0 }); @@ -187,12 +187,23 @@ function keyPressEvent(event) { if (event.text == "t") { var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; Script.setTimeout(shootTarget, time); - } if (event.text == ".") { + } else if (event.text == ".") { shootFromMouse(); + } else if (event.text == "r") { + playLoadSound(); } } +function playLoadSound() { + audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); + Audio.playSound(loadSound, audioOptions); +} + MyAvatar.attach(gunModel, "RightHand", {x: -0.02, y: -.14, z: 0.07}, Quat.fromPitchYawRollDegrees(-70, -151, 72), 0.20); +MyAvatar.attach(gunModel, "LeftHand", {x: -0.02, y: -.14, z: 0.07}, Quat.fromPitchYawRollDegrees(-70, -151, 72), 0.20); + +// Give a bit of time to load before playing sound +Script.setTimeout(playLoadSound, 2000); function update(deltaTime) { // Check for mouseLook movement, update rotation @@ -309,6 +320,7 @@ function scriptEnding() { Overlays.deleteOverlay(reticle); Overlays.deleteOverlay(text); MyAvatar.detachOne(gunModel); + MyAvatar.detachOne(gunModel); } Particles.particleCollisionWithVoxel.connect(particleCollisionWithVoxel); From 217080fe1db781821ffe044934a12d7d209d4e50 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 9 May 2014 10:27:01 -0700 Subject: [PATCH 039/108] cleanup - remove unused leap fingers --- interface/src/avatar/Avatar.cpp | 1 - interface/src/avatar/Hand.cpp | 79 +------------------------------ interface/src/avatar/Hand.h | 12 ----- interface/src/avatar/MyAvatar.cpp | 1 - 4 files changed, 2 insertions(+), 91 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index bac339813f..4de608fc6b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -73,7 +73,6 @@ const float BILLBOARD_LOD_DISTANCE = 40.0f; void Avatar::init() { getHead()->init(); - getHand()->init(); _skeletonModel.init(); _initialized = true; _shouldRenderBillboard = (getLODDistance() >= BILLBOARD_LOD_DISTANCE); diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index a6cd7ff10e..576790714e 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -29,22 +29,11 @@ const float PALM_COLLISION_RADIUS = 0.03f; Hand::Hand(Avatar* owningAvatar) : HandData((AvatarData*)owningAvatar), - - _owningAvatar(owningAvatar), - _renderAlpha(1.0) + _owningAvatar(owningAvatar) { } -void Hand::init() { -} - -void Hand::reset() { -} - void Hand::simulate(float deltaTime, bool isMine) { - - calculateGeometry(); - if (isMine) { // Iterate hand controllers, take actions as needed for (size_t i = 0; i < getNumPalms(); ++i) { @@ -146,55 +135,7 @@ void Hand::resolvePenetrations() { } } -void Hand::calculateGeometry() { - // generate finger tip balls.... - _leapFingerTipBalls.clear(); - 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()) { - const float standardBallRadius = FINGERTIP_COLLISION_RADIUS; - HandBall ball; - ball.rotation = getBaseOrientation(); - ball.position = finger.getTipPosition(); - ball.radius = standardBallRadius; - ball.touchForce = 0.0; - ball.isCollidable = true; - ball.isColliding = false; - _leapFingerTipBalls.push_back(ball); - } - } - } - } - - // generate finger root balls.... - _leapFingerRootBalls.clear(); - 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()) { - const float standardBallRadius = 0.005f; - HandBall ball; - ball.rotation = getBaseOrientation(); - ball.position = finger.getRootPosition(); - ball.radius = standardBallRadius; - ball.touchForce = 0.0; - ball.isCollidable = true; - ball.isColliding = false; - _leapFingerRootBalls.push_back(ball); - } - } - } - } -} - void Hand::render(bool isMine, Model::RenderMode renderMode) { - _renderAlpha = 1.0; - if (renderMode != Model::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) { // draw a green sphere at hand joint location, which is actually near the wrist) @@ -218,7 +159,6 @@ void Hand::render(bool isMine, Model::RenderMode renderMode) { glEnable(GL_DEPTH_TEST); glEnable(GL_RESCALE_NORMAL); - } void Hand::renderHandTargets(bool isMine) { @@ -239,6 +179,7 @@ void Hand::renderHandTargets(bool isMine) { glm::vec3 targetPosition; palm.getBallHoldPosition(targetPosition); glPushMatrix(); + glTranslatef(targetPosition.x, targetPosition.y, targetPosition.z); const float collisionRadius = 0.05f; glColor4f(0.5f,0.5f,0.5f, alpha); @@ -247,22 +188,6 @@ void Hand::renderHandTargets(bool isMine) { } } - glPushMatrix(); - // Draw the leap balls - for (size_t i = 0; i < _leapFingerTipBalls.size(); i++) { - if (alpha > 0.0f) { - if (_leapFingerTipBalls[i].isColliding) { - glColor4f(handColor.r, 0, 0, alpha); - } else { - glColor4f(handColor.r, handColor.g, handColor.b, alpha); - } - glPushMatrix(); - glTranslatef(_leapFingerTipBalls[i].position.x, _leapFingerTipBalls[i].position.y, _leapFingerTipBalls[i].position.z); - glutSolidSphere(_leapFingerTipBalls[i].radius, 20.0f, 20.0f); - glPopMatrix(); - } - } - const float PALM_BALL_RADIUS = 0.03f; const float PALM_DISK_RADIUS = 0.06f; const float PALM_DISK_THICKNESS = 0.01f; diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 9c2bc2c2c0..5d171f2809 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -50,15 +50,9 @@ public: float touchForce; // a scalar determining the amount that the cursor (or hand) is penetrating the ball }; - void init(); - void reset(); void simulate(float deltaTime, bool isMine); void render(bool isMine, Model::RenderMode renderMode = Model::DEFAULT_RENDER_MODE); - // getters - const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;} - const glm::vec3& getLeapFingerRootBallPosition(int ball) const { return _leapFingerRootBalls[ball].position;} - void collideAgainstAvatar(Avatar* avatar, bool isMyHand); void collideAgainstOurself(); @@ -72,14 +66,8 @@ private: int _controllerButtons; /// Button states read from hand-held controllers Avatar* _owningAvatar; - float _renderAlpha; - std::vector _leapFingerTipBalls; - std::vector _leapFingerRootBalls; void renderHandTargets(bool isMine); - void renderLeapFingerTrails(); - - void calculateGeometry(); }; #endif // hifi_Hand_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6d7962bff1..6ce362071b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -97,7 +97,6 @@ MyAvatar::~MyAvatar() { void MyAvatar::reset() { _skeletonModel.reset(); getHead()->reset(); - getHand()->reset(); _oculusYawOffset = 0.0f; setVelocity(glm::vec3(0.0f)); From b9fecb8b92daa7cee2be643fac465a7dba4abec5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 9 May 2014 14:34:13 -0700 Subject: [PATCH 040/108] Wait for scripts to finish before restarting them. --- interface/src/Application.cpp | 126 ++++++++++++++++------------------ interface/src/Application.h | 4 +- 2 files changed, 63 insertions(+), 67 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 61666ccdeb..1f7ddb616a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3374,71 +3374,6 @@ void Application::saveScripts() { _settings->endArray(); } -void Application::stopAllScripts() { - // stops all current running scripts - for (int i = 0; i < _scriptEnginesHash.size(); ++i) { - _scriptEnginesHash.values().at(i)->stop(); - qDebug() << "stopping script..." << getRunningScripts().at(i); - } - _scriptEnginesHash.clear(); - _runningScriptsWidget->setRunningScripts(getRunningScripts()); - bumpSettings(); -} - -void Application::stopScript(const QString &scriptName) { - if (_scriptEnginesHash.contains(scriptName)) { - _scriptEnginesHash.value(scriptName)->stop(); - qDebug() << "stopping script..." << scriptName; - _scriptEnginesHash.remove(scriptName); - _runningScriptsWidget->setRunningScripts(getRunningScripts()); - bumpSettings(); - } -} - -void Application::reloadAllScripts() { - // remember all the current scripts so we can reload them - QStringList reloadList = getRunningScripts(); - // reloads all current running scripts - stopAllScripts(); - - foreach (QString scriptName, reloadList){ - qDebug() << "reloading script..." << scriptName; - loadScript(scriptName); - } -} - -void Application::manageRunningScriptsWidgetVisibility(bool shown) { - if (_runningScriptsWidgetWasVisible && shown) { - _runningScriptsWidget->show(); - } else if (_runningScriptsWidgetWasVisible && !shown) { - _runningScriptsWidget->hide(); - } -} - -void Application::toggleRunningScriptsWidget() { - if (_runningScriptsWidgetWasVisible) { - _runningScriptsWidget->hide(); - _runningScriptsWidgetWasVisible = false; - } else { - _runningScriptsWidget->setBoundary(QRect(_window->geometry().topLeft(), - _window->size())); - _runningScriptsWidget->show(); - _runningScriptsWidgetWasVisible = true; - } -} - -void Application::uploadHead() { - uploadModel(HEAD_MODEL); -} - -void Application::uploadSkeleton() { - uploadModel(SKELETON_MODEL); -} - -void Application::uploadAttachment() { - uploadModel(ATTACHMENT_MODEL); -} - ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) { if(loadScriptFromEditor && _scriptEnginesHash.contains(scriptName) && !_scriptEnginesHash[scriptName]->isFinished()){ return _scriptEnginesHash[scriptName]; @@ -3518,6 +3453,67 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript return scriptEngine; } +void Application::stopAllScripts(bool restart) { + // stops all current running scripts + for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); + it != _scriptEnginesHash.constEnd(); it++) { + if (restart) { + connect(it.value(), SIGNAL(finished(const QString&)), SLOT(loadScript(const QString&))); + } + it.value()->stop(); + qDebug() << "stopping script..." << it.key(); + } + _scriptEnginesHash.clear(); + _runningScriptsWidget->setRunningScripts(getRunningScripts()); + bumpSettings(); +} + +void Application::stopScript(const QString &scriptName) { + if (_scriptEnginesHash.contains(scriptName)) { + _scriptEnginesHash.value(scriptName)->stop(); + qDebug() << "stopping script..." << scriptName; + _scriptEnginesHash.remove(scriptName); + _runningScriptsWidget->setRunningScripts(getRunningScripts()); + bumpSettings(); + } +} + +void Application::reloadAllScripts() { + stopAllScripts(true); +} + +void Application::manageRunningScriptsWidgetVisibility(bool shown) { + if (_runningScriptsWidgetWasVisible && shown) { + _runningScriptsWidget->show(); + } else if (_runningScriptsWidgetWasVisible && !shown) { + _runningScriptsWidget->hide(); + } +} + +void Application::toggleRunningScriptsWidget() { + if (_runningScriptsWidgetWasVisible) { + _runningScriptsWidget->hide(); + _runningScriptsWidgetWasVisible = false; + } else { + _runningScriptsWidget->setBoundary(QRect(_window->geometry().topLeft(), + _window->size())); + _runningScriptsWidget->show(); + _runningScriptsWidgetWasVisible = true; + } +} + +void Application::uploadHead() { + uploadModel(HEAD_MODEL); +} + +void Application::uploadSkeleton() { + uploadModel(SKELETON_MODEL); +} + +void Application::uploadAttachment() { + uploadModel(ATTACHMENT_MODEL); +} + QString Application::getPreviousScriptLocation() { QString suggestedName; if (_previousScriptLocation.isEmpty()) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 91a7ebd29b..ea0de764b9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -126,7 +126,6 @@ public: ~Application(); void restoreSizeAndPosition(); - ScriptEngine* loadScript(const QString& fileNameString, bool loadScriptFromEditor = false); void loadScripts(); QString getPreviousScriptLocation(); void setPreviousScriptLocation(const QString& previousScriptLocation); @@ -291,7 +290,8 @@ public slots: void loadScriptURLDialog(); void toggleLogDialog(); void initAvatarAndViewFrustum(); - void stopAllScripts(); + ScriptEngine* loadScript(const QString& fileNameString, bool loadScriptFromEditor = false); + void stopAllScripts(bool restart = false); void stopScript(const QString& scriptName); void reloadAllScripts(); void toggleRunningScriptsWidget(); From e1717b1c53f0fed9af37246d51854e510344954a Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 9 May 2014 15:02:47 -0700 Subject: [PATCH 041/108] Fix bug with mirror settings not initialized --- interface/src/Application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 61666ccdeb..c9b14d50a1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -152,6 +152,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _cameraPushback(0.0f), _scaleMirror(1.0f), + _rotateMirror(0.0f), + _raiseMirror(0.0f), _mouseX(0), _mouseY(0), _lastMouseMove(usecTimestampNow()), From 0474375a39568fbe719cc3ffdd957cd093e727c4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 9 May 2014 15:13:58 -0700 Subject: [PATCH 042/108] Don't render head attachments in first person mode. --- interface/src/avatar/Avatar.cpp | 8 +++++--- interface/src/avatar/Avatar.h | 2 +- interface/src/avatar/MyAvatar.cpp | 19 ++++++++++++++++++- interface/src/avatar/MyAvatar.h | 3 +++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4de608fc6b..32df49ca0f 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -353,7 +353,7 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) { return; } _skeletonModel.render(1.0f, modelRenderMode); - renderAttachments(modelRenderMode); + renderAttachments(renderMode); getHand()->render(false, modelRenderMode); } getHead()->render(1.0f, modelRenderMode); @@ -380,9 +380,11 @@ void Avatar::simulateAttachments(float deltaTime) { } } -void Avatar::renderAttachments(Model::RenderMode renderMode) { +void Avatar::renderAttachments(RenderMode renderMode) { + Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? + Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; foreach (Model* model, _attachmentModels) { - model->render(1.0f, renderMode); + model->render(1.0f, modelRenderMode); } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index edd53e4b8f..289a0500d0 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -192,7 +192,7 @@ protected: virtual bool shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const; void simulateAttachments(float deltaTime); - void renderAttachments(Model::RenderMode renderMode); + virtual void renderAttachments(RenderMode renderMode); virtual void updateJointMappings(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6ce362071b..52eff6e2ce 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -669,7 +669,7 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; _skeletonModel.render(1.0f, modelRenderMode); - renderAttachments(modelRenderMode); + renderAttachments(renderMode); // Render head so long as the camera isn't inside it if (shouldRenderHead(Application::getInstance()->getCamera()->getPosition(), renderMode)) { @@ -1489,6 +1489,23 @@ void MyAvatar::updateMotionBehaviorsFromMenu() { } } +void MyAvatar::renderAttachments(RenderMode renderMode) { + if (!Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON || renderMode == MIRROR_RENDER_MODE) { + Avatar::renderAttachments(renderMode); + return; + } + const FBXGeometry& geometry = _skeletonModel.getGeometry()->getFBXGeometry(); + QString headJointName = (geometry.headJointIndex == -1) ? QString() : geometry.joints.at(geometry.headJointIndex).name; + Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? + Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; + for (int i = 0; i < _attachmentData.size(); i++) { + const QString& jointName = _attachmentData.at(i).jointName; + if (jointName != headJointName && jointName != "Head") { + _attachmentModels.at(i)->render(1.0f, modelRenderMode); + } + } +} + void MyAvatar::setCollisionGroups(quint32 collisionGroups) { Avatar::setCollisionGroups(collisionGroups & VALID_COLLISION_GROUPS); Menu* menu = Menu::getInstance(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 96249aec33..2e47d9c973 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -121,6 +121,9 @@ public slots: signals: void transformChanged(); +protected: + virtual void renderAttachments(RenderMode renderMode); + private: bool _mousePressed; float _bodyPitchDelta; // degrees From 3329cb7638d114ac4aa91cec49ae325da1c7e797 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 9 May 2014 15:25:48 -0700 Subject: [PATCH 043/108] Set the LOD distance on attachments (controls the load priority, too). --- interface/src/avatar/Avatar.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 32df49ca0f..b74adee55a 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -370,6 +370,9 @@ void Avatar::simulateAttachments(float deltaTime) { int jointIndex = getJointIndex(attachment.jointName); glm::vec3 jointPosition; glm::quat jointRotation; + if (!isMyAvatar()) { + model->setLODDistance(getLODDistance()); + } if (_skeletonModel.getJointPosition(jointIndex, jointPosition) && _skeletonModel.getJointRotation(jointIndex, jointRotation)) { model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale); From 106c8bffd842fc28a4c3c32cba8351c15f61f9f3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 9 May 2014 16:08:06 -0700 Subject: [PATCH 044/108] first cut at animations in models --- assignment-client/CMakeLists.txt | 1 + examples/placeModelsWithHands.js | 22 +++ interface/CMakeLists.txt | 1 + interface/src/models/ModelTreeRenderer.cpp | 62 +++++-- interface/src/models/ModelTreeRenderer.h | 6 +- interface/src/renderer/Model.cpp | 21 ++- interface/src/renderer/Model.h | 2 + .../src/AnimationCache.cpp | 4 +- .../src/AnimationCache.h | 3 + .../src/AnimationObject.cpp | 0 .../src/AnimationObject.h | 0 libraries/fbx/src/FBXReader.cpp | 1 + libraries/fbx/src/FBXReader.h | 2 + libraries/models/CMakeLists.txt | 1 + libraries/models/src/ModelItem.cpp | 156 +++++++++++++++++- libraries/models/src/ModelItem.h | 34 +++- libraries/networking/src/PacketHeaders.cpp | 2 + libraries/particles/CMakeLists.txt | 1 + libraries/script-engine/CMakeLists.txt | 1 + libraries/script-engine/src/ScriptEngine.h | 5 +- 20 files changed, 297 insertions(+), 28 deletions(-) rename libraries/{script-engine => animation}/src/AnimationCache.cpp (98%) rename libraries/{script-engine => animation}/src/AnimationCache.h (96%) rename libraries/{script-engine => animation}/src/AnimationObject.cpp (100%) rename libraries/{script-engine => animation}/src/AnimationObject.h (100%) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index e783001228..6436ffbd75 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -33,6 +33,7 @@ link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(embedded-webserver ${TARGET_NAME} "${ROOT_DIR}") diff --git a/examples/placeModelsWithHands.js b/examples/placeModelsWithHands.js index e1ac151fe4..f16945472d 100644 --- a/examples/placeModelsWithHands.js +++ b/examples/placeModelsWithHands.js @@ -37,6 +37,7 @@ var radiusMinimum = 0.05; var radiusMaximum = 0.5; var modelURLs = [ + "http://www.fungibleinsight.com/faces/beta.fst", "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/attachments/topHat.fst", "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX", "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx", @@ -48,6 +49,19 @@ var modelURLs = [ "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/slimer.fbx", ]; +var animationURLs = [ + "http://www.fungibleinsight.com/faces/gangnam_style_2.fbx", + "", + "", + "", + "", + "", + "", + "", + "", + "", +]; + var currentModelURL = 1; var numModels = modelURLs.length; @@ -214,10 +228,18 @@ function checkControllerSide(whichSide) { modelRotation: palmRotation, modelURL: modelURLs[currentModelURL] }; + + if (animationURLs[currentModelURL] !== "") { + properties.animationURL = animationURLs[currentModelURL]; + } debugPrint("modelRadius=" +modelRadius); newModel = Models.addModel(properties); + + print("just added model... newModel=" + newModel.creatorTokenID); + print("properties.animationURL=" + properties.animationURL); + if (whichSide == LEFT_PALM) { leftModelAlreadyInHand = true; leftHandModel = newModel; diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0a56109260..4d02415df2 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -127,6 +127,7 @@ link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(avatars ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(audio ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}") # find any optional libraries diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index c762182290..e5c55ef066 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -20,11 +20,16 @@ ModelTreeRenderer::ModelTreeRenderer() : } ModelTreeRenderer::~ModelTreeRenderer() { - // delete the models in _modelsItemModels - foreach(Model* model, _modelsItemModels) { + // delete the models in _knownModelsItemModels + foreach(Model* model, _knownModelsItemModels) { delete model; } - _modelsItemModels.clear(); + _knownModelsItemModels.clear(); + + foreach(Model* model, _unknownModelsItemModels) { + delete model; + } + _unknownModelsItemModels.clear(); } void ModelTreeRenderer::init() { @@ -43,17 +48,27 @@ void ModelTreeRenderer::render(RenderMode renderMode) { OctreeRenderer::render(renderMode); } -Model* ModelTreeRenderer::getModel(const QString& url) { +Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { Model* model = NULL; - // if we don't already have this model then create it and initialize it - if (_modelsItemModels.find(url) == _modelsItemModels.end()) { - model = new Model(); - model->init(); - model->setURL(QUrl(url)); - _modelsItemModels[url] = model; + if (modelItem.isKnownID()) { + if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) { + model = _knownModelsItemModels[modelItem.getID()]; + } else { + model = new Model(); + model->init(); + model->setURL(QUrl(modelItem.getModelURL())); + _knownModelsItemModels[modelItem.getID()] = model; + } } else { - model = _modelsItemModels[url]; + if (_unknownModelsItemModels.find(modelItem.getCreatorTokenID()) != _unknownModelsItemModels.end()) { + model = _unknownModelsItemModels[modelItem.getCreatorTokenID()]; + } else { + model = new Model(); + model->init(); + model->setURL(QUrl(modelItem.getModelURL())); + _unknownModelsItemModels[modelItem.getCreatorTokenID()] = model; + } } return model; } @@ -63,7 +78,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // we need to iterate the actual modelItems of the element ModelTreeElement* modelTreeElement = (ModelTreeElement*)element; - const QList& modelItems = modelTreeElement->getModels(); + QList& modelItems = modelTreeElement->getModels(); uint16_t numberOfModels = modelItems.size(); @@ -139,7 +154,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) } for (uint16_t i = 0; i < numberOfModels; i++) { - const ModelItem& modelItem = modelItems[i]; + ModelItem& modelItem = modelItems[i]; // render modelItem aspoints AABox modelBox = modelItem.getAABox(); modelBox.scale(TREE_SCALE); @@ -156,7 +171,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) glPushMatrix(); const float alpha = 1.0f; - Model* model = getModel(modelItem.getModelURL()); + Model* model = getModel(modelItem); model->setScaleToFit(true, radius * 2.0f); model->setSnapModelToCenter(true); @@ -167,6 +182,25 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // set the position model->setTranslation(position); + + qDebug() << "modelItem.getModelURL()=" << modelItem.getModelURL(); + qDebug() << "modelItem.getAnimationURL()=" << modelItem.getAnimationURL(); + qDebug() << "modelItem.hasAnimation()=" << modelItem.hasAnimation(); + + // handle animations.. + if (modelItem.hasAnimation()) { + if (!modelItem.jointsMapped()) { + QStringList modelJointNames = model->getJointNames(); + modelItem.mapJoints(modelJointNames); + } + + QVector frameData = modelItem.getAnimationFrame(); + for (int i = 0; i < frameData.size(); i++) { + model->setJointState(i, true, frameData[i]); + } + } + + // make sure to simulate so everything gets set up correctly for rendering model->simulate(0.0f); // TODO: should we allow modelItems to have alpha on their models? diff --git a/interface/src/models/ModelTreeRenderer.h b/interface/src/models/ModelTreeRenderer.h index 7af5bbf317..e0b8d7d0a2 100644 --- a/interface/src/models/ModelTreeRenderer.h +++ b/interface/src/models/ModelTreeRenderer.h @@ -49,9 +49,9 @@ public: virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE); protected: - Model* getModel(const QString& url); - - QMap _modelsItemModels; + Model* getModel(const ModelItem& modelItem); + QMap _knownModelsItemModels; + QMap _unknownModelsItemModels; }; #endif // hifi_ModelTreeRenderer_h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index f46fd48beb..b2c1665c2f 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -430,7 +430,8 @@ Extents Model::getMeshExtents() const { return Extents(); } const Extents& extents = _geometry->getFBXGeometry().meshExtents; - Extents scaledExtents = { extents.minimum * _scale, extents.maximum * _scale }; + glm::vec3 scale = _scale * _geometry->getFBXGeometry().fstScaled; + Extents scaledExtents = { extents.minimum * scale, extents.maximum * scale }; return scaledExtents; } @@ -438,8 +439,13 @@ Extents Model::getUnscaledMeshExtents() const { if (!isActive()) { return Extents(); } + const Extents& extents = _geometry->getFBXGeometry().meshExtents; - return extents; + + // even though our caller asked for "unscaled" we need to include any fst scaling + float scale = _geometry->getFBXGeometry().fstScaled; + Extents scaledExtents = { extents.minimum * scale, extents.maximum * scale }; + return scaledExtents; } bool Model::getJointState(int index, glm::quat& rotation) const { @@ -573,6 +579,17 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind) return true; } +QStringList Model::getJointNames() const { + if (QThread::currentThread() != thread()) { + QStringList result; + QMetaObject::invokeMethod(const_cast(this), "getJointNames", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QStringList, result)); + return result; + } + return isActive() ? _geometry->getFBXGeometry().getJointNames() : QStringList(); +} + + void Model::clearShapes() { for (int i = 0; i < _jointShapes.size(); ++i) { delete _jointShapes[i]; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 1a469c8122..b7a42930dc 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -182,6 +182,8 @@ public: bool getJointPosition(int jointIndex, glm::vec3& position) const; bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const; + + QStringList getJointNames() const; void clearShapes(); void rebuildShapes(); diff --git a/libraries/script-engine/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp similarity index 98% rename from libraries/script-engine/src/AnimationCache.cpp rename to libraries/animation/src/AnimationCache.cpp index 8e1493f075..ce7e4cdf36 100644 --- a/libraries/script-engine/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -36,7 +36,8 @@ QSharedPointer AnimationCache::createResource(const QUrl& url, const Q } Animation::Animation(const QUrl& url) : - Resource(url) { + Resource(url), + _isValid(false) { } class AnimationReader : public QRunnable { @@ -93,6 +94,7 @@ QVector Animation::getFrames() const { void Animation::setGeometry(const FBXGeometry& geometry) { _geometry = geometry; finishedLoading(true); + _isValid = true; } void Animation::downloadFinished(QNetworkReply* reply) { diff --git a/libraries/script-engine/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h similarity index 96% rename from libraries/script-engine/src/AnimationCache.h rename to libraries/animation/src/AnimationCache.h index 23183adf10..392443e7b5 100644 --- a/libraries/script-engine/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -53,6 +53,8 @@ public: Q_INVOKABLE QStringList getJointNames() const; Q_INVOKABLE QVector getFrames() const; + + bool isValid() const { return _isValid; } protected: @@ -63,6 +65,7 @@ protected: private: FBXGeometry _geometry; + bool _isValid; }; #endif // hifi_AnimationCache_h diff --git a/libraries/script-engine/src/AnimationObject.cpp b/libraries/animation/src/AnimationObject.cpp similarity index 100% rename from libraries/script-engine/src/AnimationObject.cpp rename to libraries/animation/src/AnimationObject.cpp diff --git a/libraries/script-engine/src/AnimationObject.h b/libraries/animation/src/AnimationObject.h similarity index 100% rename from libraries/script-engine/src/AnimationObject.h rename to libraries/animation/src/AnimationObject.h diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 1fc03ceb66..f743a383d1 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1385,6 +1385,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // get offset transform from mapping float offsetScale = mapping.value("scale", 1.0f).toFloat(); + geometry.fstScaled = offsetScale; glm::quat offsetRotation = glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(), mapping.value("ry").toFloat(), mapping.value("rz").toFloat()))); geometry.offset = glm::translate(glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 51e7380181..567a32c1cb 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -212,6 +212,8 @@ public: Extents bindExtents; Extents meshExtents; + float fstScaled; + QVector animationFrames; QVector attachments; diff --git a/libraries/models/CMakeLists.txt b/libraries/models/CMakeLists.txt index 062352e50c..1e70942872 100644 --- a/libraries/models/CMakeLists.txt +++ b/libraries/models/CMakeLists.txt @@ -28,6 +28,7 @@ link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") # for streamable link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}") diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index af8500bf25..785183da56 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -85,6 +85,12 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties& _shouldDie = false; _modelURL = MODEL_DEFAULT_MODEL_URL; _modelRotation = MODEL_DEFAULT_MODEL_ROTATION; + + // animation related + _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _frameIndex = 0.0f; + _jointMappingCompleted = false; + _lastAnimated = now; setProperties(properties); } @@ -110,6 +116,12 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t _shouldDie = false; _modelURL = MODEL_DEFAULT_MODEL_URL; _modelRotation = MODEL_DEFAULT_MODEL_ROTATION; + + // animation related + _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _frameIndex = 0.0f; + _jointMappingCompleted = false; + _lastAnimated = now; } bool ModelItem::appendModelData(OctreePacketData* packetData) const { @@ -150,6 +162,16 @@ bool ModelItem::appendModelData(OctreePacketData* packetData) const { if (success) { success = packetData->appendValue(getModelRotation()); } + + // animationURL + if (success) { + uint16_t animationURLLength = _animationURL.size() + 1; // include NULL + success = packetData->appendValue(animationURLLength); + if (success) { + success = packetData->appendRawData((const unsigned char*)qPrintable(_animationURL), animationURLLength); + } + } + return success; } @@ -215,7 +237,7 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += sizeof(modelURLLength); bytesRead += sizeof(modelURLLength); QString modelURLString((const char*)dataAt); - _modelURL = modelURLString; + setModelURL(modelURLString); dataAt += modelURLLength; bytesRead += modelURLLength; @@ -224,6 +246,19 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += bytes; bytesRead += bytes; + // animationURL + uint16_t animationURLLength; + memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); + dataAt += sizeof(animationURLLength); + bytesRead += sizeof(animationURLLength); + QString animationURLString((const char*)dataAt); + setAnimationURL(animationURLString); + dataAt += animationURLLength; + bytesRead += animationURLLength; + +qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + + //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); } return bytesRead; @@ -346,6 +381,21 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& processedBytes += bytes; } + // animationURL + if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_URL) == MODEL_PACKET_CONTAINS_ANIMATION_URL)) { + uint16_t animationURLLength; + memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); + dataAt += sizeof(animationURLLength); + processedBytes += sizeof(animationURLLength); + QString tempString((const char*)dataAt); + newModelItem._animationURL = tempString; + dataAt += animationURLLength; + processedBytes += animationURLLength; + +qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); + + } + const bool wantDebugging = false; if (wantDebugging) { qDebug("ModelItem::fromEditPacket()..."); @@ -476,6 +526,21 @@ bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id sizeOut += bytes; } + // animationURL + if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_URL) == MODEL_PACKET_CONTAINS_ANIMATION_URL)) { + uint16_t urlLength = properties.getAnimationURL().size() + 1; + memcpy(copyAt, &urlLength, sizeof(urlLength)); + copyAt += sizeof(urlLength); + sizeOut += sizeof(urlLength); + memcpy(copyAt, qPrintable(properties.getAnimationURL()), urlLength); + copyAt += urlLength; + sizeOut += urlLength; + +qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable(properties.getAnimationURL()); + + } + + bool wantDebugging = false; if (wantDebugging) { qDebug("encodeModelItemEditMessageDetails()...."); @@ -521,6 +586,66 @@ void ModelItem::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssi } } + +QMap ModelItem::_loadedAnimations; // TODO: cleanup?? +AnimationCache ModelItem::_animationCache; + +Animation* ModelItem::getAnimation(const QString& url) { + AnimationPointer animation; + + // if we don't already have this model then create it and initialize it + if (_loadedAnimations.find(url) == _loadedAnimations.end()) { + animation = _animationCache.getAnimation(url); + _loadedAnimations[url] = animation; + } else { + animation = _loadedAnimations[url]; + } + return animation.data(); +} + +void ModelItem::mapJoints(const QStringList& modelJointNames) { + // if we don't have animation, or we're already joint mapped then bail early + if (!hasAnimation() || _jointMappingCompleted) { + return; + } + + Animation* myAnimation = getAnimation(_animationURL); + + if (!_jointMappingCompleted) { + QStringList animationJointNames = myAnimation->getJointNames(); + if (modelJointNames.size() > 0 && animationJointNames.size() > 0) { + _jointMapping.resize(modelJointNames.size()); + for (int i = 0; i < modelJointNames.size(); i++) { + _jointMapping[i] = animationJointNames.indexOf(modelJointNames[i]); + } + _jointMappingCompleted = true; + } + } +} + +QVector ModelItem::getAnimationFrame() { + QVector frameData; + if (hasAnimation() && _jointMappingCompleted) { + quint64 now = usecTimestampNow(); + float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; + _lastAnimated = now; + const float FRAME_RATE = 10.0f; + _frameIndex += deltaTime * FRAME_RATE; + Animation* myAnimation = getAnimation(_animationURL); + QVector frames = myAnimation->getFrames(); + int frameIndex = (int)std::floor(_frameIndex) % frames.size(); + QVector rotations = frames[frameIndex].rotations; + frameData.resize(_jointMapping.size()); + for (int j = 0; j < _jointMapping.size(); j++) { + int rotationIndex = _jointMapping[j]; + if (rotationIndex != -1 && rotationIndex < rotations.size()) { + frameData[j] = rotations[rotationIndex]; + } + } + } + return frameData; +} + void ModelItem::update(const quint64& now) { _lastUpdated = now; setShouldDie(getShouldDie()); @@ -547,6 +672,7 @@ ModelItemProperties::ModelItemProperties() : _shouldDie(false), _modelURL(""), _modelRotation(MODEL_DEFAULT_MODEL_ROTATION), + _animationURL(""), _id(UNKNOWN_MODEL_ID), _idSet(false), @@ -558,6 +684,7 @@ ModelItemProperties::ModelItemProperties() : _shouldDieChanged(false), _modelURLChanged(false), _modelRotationChanged(false), + _animationURLChanged(false), _defaultSettings(true) { } @@ -589,6 +716,11 @@ uint16_t ModelItemProperties::getChangedBits() const { changedBits += MODEL_PACKET_CONTAINS_MODEL_ROTATION; } + if (_animationURLChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_URL; + } + + return changedBits; } @@ -611,6 +743,7 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const QScriptValue modelRotation = quatToScriptValue(engine, _modelRotation); properties.setProperty("modelRotation", modelRotation); + properties.setProperty("animationURL", _animationURL); if (_idSet) { properties.setProperty("id", _id); @@ -707,6 +840,16 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { } } + QScriptValue animationURL = object.property("animationURL"); + if (animationURL.isValid()) { + QString newAnimationURL; + newAnimationURL = animationURL.toVariant().toString(); + if (_defaultSettings || newAnimationURL != _animationURL) { + _animationURL = newAnimationURL; + _animationURLChanged = true; + } + } + _lastEdited = usecTimestampNow(); } @@ -741,7 +884,14 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { modelItem.setModelRotation(_modelRotation); somethingChanged = true; } - + + if (_animationURLChanged) { + modelItem.setAnimationURL(_animationURL); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; + } + if (somethingChanged) { bool wantDebug = false; if (wantDebug) { @@ -761,6 +911,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _shouldDie = modelItem.getShouldDie(); _modelURL = modelItem.getModelURL(); _modelRotation = modelItem.getModelRotation(); + _animationURL = modelItem.getAnimationURL(); _id = modelItem.getID(); _idSet = true; @@ -772,6 +923,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _shouldDieChanged = false; _modelURLChanged = false; _modelRotationChanged = false; + _animationURLChanged = false; _defaultSettings = false; } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 9edcf482c0..72a12c9b2c 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -39,14 +40,16 @@ const uint32_t UNKNOWN_MODEL_ID = 0xFFFFFFFF; const uint16_t MODEL_PACKET_CONTAINS_RADIUS = 1; const uint16_t MODEL_PACKET_CONTAINS_POSITION = 2; const uint16_t MODEL_PACKET_CONTAINS_COLOR = 4; -const uint16_t MODEL_PACKET_CONTAINS_SHOULDDIE = 512; -const uint16_t MODEL_PACKET_CONTAINS_MODEL_URL = 1024; -const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 2048; +const uint16_t MODEL_PACKET_CONTAINS_SHOULDDIE = 8; +const uint16_t MODEL_PACKET_CONTAINS_MODEL_URL = 16; +const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 32; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_URL = 64; const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container const QString MODEL_DEFAULT_MODEL_URL(""); const glm::quat MODEL_DEFAULT_MODEL_ROTATION; +const QString MODEL_DEFAULT_ANIMATION_URL(""); /// A collection of properties of a model item used in the scripting API. Translates between the actual properties of a model /// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of @@ -69,6 +72,7 @@ public: const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } + const QString& getAnimationURL() const { return _animationURL; } quint64 getLastEdited() const { return _lastEdited; } uint16_t getChangedBits() const; @@ -82,6 +86,7 @@ public: // model related properties void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; } + void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; } /// used by ModelScriptingInterface to return ModelItemProperties for unknown models void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; } @@ -97,6 +102,7 @@ private: QString _modelURL; glm::quat _modelRotation; + QString _animationURL; uint32_t _id; bool _idSet; @@ -109,6 +115,7 @@ private: bool _modelURLChanged; bool _modelRotationChanged; + bool _animationURLChanged; bool _defaultSettings; }; Q_DECLARE_METATYPE(ModelItemProperties); @@ -178,6 +185,8 @@ public: bool hasModel() const { return !_modelURL.isEmpty(); } const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } + bool hasAnimation() const { return !_animationURL.isEmpty(); } + const QString& getAnimationURL() const { return _animationURL; } ModelItemID getModelItemID() const { return ModelItemID(getID(), getCreatorTokenID(), getID() != UNKNOWN_MODEL_ID); } ModelItemProperties getProperties() const; @@ -196,6 +205,7 @@ public: bool getShouldDie() const { return _shouldDie; } uint32_t getCreatorTokenID() const { return _creatorTokenID; } bool isNewlyCreated() const { return _newlyCreated; } + bool isKnownID() const { return getID() != UNKNOWN_MODEL_ID; } /// set position in domain scale units (0.0 - 1.0) void setPosition(const glm::vec3& value) { _position = value; } @@ -215,6 +225,7 @@ public: // model related properties void setModelURL(const QString& url) { _modelURL = url; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; } + void setAnimationURL(const QString& url) { _animationURL = url; } void setProperties(const ModelItemProperties& properties); @@ -239,6 +250,10 @@ public: static uint32_t getNextCreatorTokenID(); static void handleAddModelResponse(const QByteArray& packet); + void mapJoints(const QStringList& modelJointNames); + QVector getAnimationFrame(); + bool jointsMapped() const { return _jointMappingCompleted; }; + protected: glm::vec3 _position; rgbColor _color; @@ -256,10 +271,23 @@ protected: quint64 _lastUpdated; quint64 _lastEdited; + quint64 _lastAnimated; + + QString _animationURL; + float _frameIndex; // we keep this as a float and round to int only when we need the exact index + bool _jointMappingCompleted; + QVector _jointMapping; + // used by the static interfaces for creator token ids static uint32_t _nextCreatorTokenID; static std::map _tokenIDsToIDs; + + + static Animation* getAnimation(const QString& url); + static QMap _loadedAnimations; + static AnimationCache _animationCache; + }; #endif // hifi_ModelItem_h diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 0785b81581..b9eee6e0c9 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -66,6 +66,8 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeOctreeStats: return 1; + case PacketTypeModelData: + return 1; default: return 0; } diff --git a/libraries/particles/CMakeLists.txt b/libraries/particles/CMakeLists.txt index 1cb60756a2..8cd2f30012 100644 --- a/libraries/particles/CMakeLists.txt +++ b/libraries/particles/CMakeLists.txt @@ -25,6 +25,7 @@ link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") # link ZLIB and GnuTLS find_package(ZLIB) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index ee918ff864..0374ad570c 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -27,6 +27,7 @@ link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") # link ZLIB find_package(ZLIB) diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 09d41e3e2e..96cc874453 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -18,13 +18,12 @@ #include #include +#include #include -#include - #include #include +#include -#include "AnimationCache.h" #include "AbstractControllerScriptingInterface.h" #include "Quat.h" #include "ScriptUUID.h" From d8488b34efafaf54caa5e3cb1516333cf598b05c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 9 May 2014 16:08:16 -0700 Subject: [PATCH 045/108] first cut at animations in models --- libraries/animation/CMakeLists.txt | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 libraries/animation/CMakeLists.txt diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt new file mode 100644 index 0000000000..36088ba4bd --- /dev/null +++ b/libraries/animation/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 2.8) + +if (WIN32) + cmake_policy (SET CMP0020 NEW) +endif (WIN32) + +set(ROOT_DIR ../..) +set(MACRO_DIR "${ROOT_DIR}/cmake/macros") + +# setup for find modules +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") + +set(TARGET_NAME animation) + +find_package(Qt5Widgets REQUIRED) + +include(${MACRO_DIR}/SetupHifiLibrary.cmake) +setup_hifi_library(${TARGET_NAME}) + +include(${MACRO_DIR}/IncludeGLM.cmake) +include_glm(${TARGET_NAME} "${ROOT_DIR}") + +include(${MACRO_DIR}/LinkHifiLibrary.cmake) +link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") + +# link ZLIB +find_package(ZLIB) +find_package(GnuTLS REQUIRED) + +# add a definition for ssize_t so that windows doesn't bail on gnutls.h +if (WIN32) + add_definitions(-Dssize_t=long) +endif () + +include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}" "${GNUTLS_INCLUDE_DIR}") +target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" "${GNUTLS_LIBRARY}" Qt5::Widgets) From ea1bcd13a4d579a6f9c76962d8f5d7c372931dab Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 9 May 2014 17:39:03 -0700 Subject: [PATCH 046/108] Updated guitar with more chords, key triggers --- examples/airGuitar.js | 85 +++++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/examples/airGuitar.js b/examples/airGuitar.js index b7c4b72948..78c16be7bc 100644 --- a/examples/airGuitar.js +++ b/examples/airGuitar.js @@ -35,16 +35,26 @@ var chords = new Array(); chords[1] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+A.raw"); chords[2] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+B.raw"); chords[3] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+E.raw"); +chords[4] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+G.raw"); + // Electric guitar -chords[4] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+A+short.raw"); -chords[5] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+B+short.raw"); -chords[6] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+E+short.raw"); +chords[5] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+A+short.raw"); +chords[6] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+B+short.raw"); +chords[7] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+E+short.raw"); +chords[8] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+G+short.raw"); -var guitarSelector = 3; +// Steel Guitar +chords[9] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Steel+A.raw"); +chords[10] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Steel+B.raw"); +chords[11] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Steel+E.raw"); +chords[12] = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Steel+G.raw"); -var whichChord = chords[guitarSelector + 1]; +var NUM_CHORDS = 4; +var NUM_GUITARS = 3; +var guitarSelector = NUM_CHORDS; +var whichChord = 1; -var leftHanded = false; +var leftHanded = true; if (leftHanded) { var strumHand = 0; var chordHand = 1; @@ -59,20 +69,23 @@ var lastPosition = { x: 0.0, var soundPlaying = false; var selectorPressed = false; +var position; MyAvatar.attach(guitarModel, "Hips", {x: -0.0, y: -0.0, z: 0.0}, Quat.fromPitchYawRollDegrees(0, 0, 0), 1.0); function checkHands(deltaTime) { for (var palm = 0; palm < 2; palm++) { var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1); - var speed = length(palmVelocity) / 4.0; + var volume = length(palmVelocity) / 5.0; var position = Controller.getSpatialControlPosition(palm * 2 + 1); var myPelvis = MyAvatar.position; var trigger = Controller.getTriggerValue(strumHand); var chord = Controller.getTriggerValue(chordHand); + if (volume > 1.0) volume = 1.0; if ((chord > 0.1) && Audio.isInjectorPlaying(soundPlaying)) { // If chord finger trigger pulled, stop current chord + print("stopped sound"); Audio.stopInjector(soundPlaying); } @@ -81,11 +94,10 @@ function checkHands(deltaTime) { // Change guitars if button FWD (5) pressed if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 5)) { if (!selectorPressed) { - if (guitarSelector == 0) { - guitarSelector = 3; - } else { + guitarSelector += NUM_CHORDS; + if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) { guitarSelector = 0; - } + } selectorPressed = true; } } else { @@ -93,38 +105,65 @@ function checkHands(deltaTime) { } if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 1)) { - whichChord = chords[guitarSelector + 1]; + whichChord = 1; } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 2)) { - whichChord = chords[guitarSelector + 2]; + whichChord = 2; } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 3)) { - whichChord = chords[guitarSelector + 3]; + whichChord = 3; + } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 4)) { + whichChord = 4; } if (palm == strumHand) { - var STRUM_HEIGHT_ABOVE_PELVIS = 0.00; + var STRUM_HEIGHT_ABOVE_PELVIS = 0.10; var strumTriggerHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS; //printVector(position); if ( ( ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) || ((position.y > strumTriggerHeight) && (lastPosition.y <= strumTriggerHeight)) ) && (trigger > 0.1) ){ // If hand passes downward or upward through 'strings', and finger trigger pulled, play - var options = new AudioInjectionOptions(); - options.position = position; - if (speed > 1.0) { speed = 1.0; } - options.volume = speed; - if (Audio.isInjectorPlaying(soundPlaying)) { - Audio.stopInjector(soundPlaying); - } - soundPlaying = Audio.playSound(whichChord, options); + playChord(position, volume); } lastPosition = Controller.getSpatialControlPosition(palm * 2 + 1); } } } +function playChord(position, volume) { + var options = new AudioInjectionOptions(); + options.position = position; + options.volume = volume; + if (Audio.isInjectorPlaying(soundPlaying)) { + print("stopped sound"); + Audio.stopInjector(soundPlaying); + } + print("Played sound: " + whichChord + " at volume " + options.volume); + soundPlaying = Audio.playSound(chords[guitarSelector + whichChord], options); +} + +function keyPressEvent(event) { + // check for keypresses and use those to trigger sounds if not hydra + keyVolume = 0.4; + if (event.text == "1") { + whichChord = 1; + playChord(MyAvatar.position, keyVolume); + } else if (event.text == "2") { + whichChord = 2; + playChord(MyAvatar.position, keyVolume); + } else if (event.text == "3") { + whichChord = 3; + playChord(MyAvatar.position, keyVolume); + } else if (event.text == "4") { + whichChord = 4; + playChord(MyAvatar.position, keyVolume); + } +} + function scriptEnding() { MyAvatar.detachOne(guitarModel); } // Connect a call back that happens every frame Script.update.connect(checkHands); Script.scriptEnding.connect(scriptEnding); +Controller.keyPressEvent.connect(keyPressEvent); + From 363f545f7cdc0d166af2649c30437b68478b5259 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 9 May 2014 17:50:08 -0700 Subject: [PATCH 047/108] After uploading, refresh the uploaded files so that they are immediately redownloaded. --- interface/src/ModelUploader.cpp | 25 +++++++---- interface/src/ModelUploader.h | 2 + interface/src/renderer/GeometryCache.cpp | 9 ++++ interface/src/renderer/GeometryCache.h | 1 + interface/src/renderer/Model.cpp | 3 ++ libraries/shared/src/ResourceCache.cpp | 55 ++++++++++++++++++------ libraries/shared/src/ResourceCache.h | 7 +++ 7 files changed, 79 insertions(+), 23 deletions(-) diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index ce8691998d..ec7fcee6f2 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -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 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); } } } diff --git a/interface/src/ModelUploader.h b/interface/src/ModelUploader.h index 766bd55318..634de05640 100644 --- a/interface/src/ModelUploader.h +++ b/interface/src/ModelUploader.h @@ -49,6 +49,8 @@ private slots: private: QString _url; + QString _textureBase; + QSet _textureFilenames; int _lodCount; int _texturesCount; int _totalSize; diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 8d31cdce1d..ec68c87a76 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -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")) { diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index a9b274fedc..deecfd56c5 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -96,6 +96,7 @@ public: protected: + virtual void init(); virtual void downloadFinished(QNetworkReply* reply); virtual void reinsert(); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index f46fd48beb..e1bfd21bba 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -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(); diff --git a/libraries/shared/src/ResourceCache.cpp b/libraries/shared/src/ResourceCache.cpp index 2f26e344fd..14998232d6 100644 --- a/libraries/shared/src/ResourceCache.cpp +++ b/libraries/shared/src/ResourceCache.cpp @@ -30,6 +30,13 @@ ResourceCache::~ResourceCache() { } } +void ResourceCache::refresh(const QUrl& url) { + QSharedPointer resource = _resources.value(url); + if (!resource.isNull()) { + resource->refresh(); + } +} + QSharedPointer 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 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); diff --git a/libraries/shared/src/ResourceCache.h b/libraries/shared/src/ResourceCache.h index 0cfabd26fc..2404485c46 100644 --- a/libraries/shared/src/ResourceCache.h +++ b/libraries/shared/src/ResourceCache.h @@ -47,6 +47,8 @@ public: ResourceCache(QObject* parent = NULL); virtual ~ResourceCache(); + void refresh(const QUrl& url); + protected: QMap > _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& 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; From 3b7e0814fe2d2572102e03e9447d29539840ae77 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sun, 11 May 2014 09:57:43 +0200 Subject: [PATCH 048/108] CR fixes --- interface/src/XmppClient.cpp | 2 +- interface/src/ui/SnapshotShareDialog.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/XmppClient.cpp b/interface/src/XmppClient.cpp index 666906681c..2d421b1afa 100644 --- a/interface/src/XmppClient.cpp +++ b/interface/src/XmppClient.cpp @@ -16,7 +16,7 @@ #include "XmppClient.h" const QString DEFAULT_XMPP_SERVER = "chat.highfidelity.io"; -const QString DEFAULT_CHAT_ROOM = "public@public-chat.highfidelity.io"; +const QString DEFAULT_CHAT_ROOM = "test@public-chat.highfidelity.io"; XmppClient::XmppClient() : _xmppClient(), diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp index 5ba284618f..2ac1c81f52 100644 --- a/interface/src/ui/SnapshotShareDialog.cpp +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -87,13 +87,13 @@ void SnapshotShareDialog::uploadSnapshot() { _networkAccessManager = new QNetworkAccessManager(this); } - QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + 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); + QFile* file = new QFile(_fileName); file->open(QIODevice::ReadOnly); QHttpPart imagePart; @@ -109,7 +109,7 @@ void SnapshotShareDialog::uploadSnapshot() { QUrl url(FORUM_UPLOADS_URL); QNetworkRequest request(url); - QNetworkReply *reply = _networkAccessManager->post(request, multiPart); + QNetworkReply* reply = _networkAccessManager->post(request, multiPart); connect(reply, &QNetworkReply::finished, this, &SnapshotShareDialog::uploadRequestFinished); From 3e1d0536c174310bb025f0516ae475282773d785 Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Sun, 11 May 2014 16:18:43 +0200 Subject: [PATCH 049/108] Fix for: #19626 - Fix the Windows 7 crash The image data for glTexImage2D is size=width x height without any borders. So there must be a border of 0 given when calling glTexImage2D, otherwise there is not enough image data causing crashes when textures get loaded. The effect occurs reproducible on Win7 with ATI cards. Maybe their OpenGL-Implementation is less robust ... --- interface/src/avatar/Avatar.cpp | 2 +- interface/src/renderer/TextureCache.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b74adee55a..41dc50b1fa 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -406,7 +406,7 @@ void Avatar::renderBillboard() { } _billboardTexture.reset(new Texture()); glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID()); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index f31e4f9060..0588ca70d2 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -366,10 +366,10 @@ void NetworkTexture::setImage(const QImage& image, bool translucent) { imageLoaded(image); glBindTexture(GL_TEXTURE_2D, getID()); if (image.hasAlphaChannel()) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits()); } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width(), image.height(), 1, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width(), image.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image.constBits()); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -404,10 +404,10 @@ QSharedPointer DilatableNetworkTexture::getDilatedTexture(float dilatio glBindTexture(GL_TEXTURE_2D, texture->getID()); if (dilatedImage.hasAlphaChannel()) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dilatedImage.width(), dilatedImage.height(), 1, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dilatedImage.width(), dilatedImage.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, dilatedImage.constBits()); } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dilatedImage.width(), dilatedImage.height(), 1, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dilatedImage.width(), dilatedImage.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, dilatedImage.constBits()); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); From 3ffd4cd65dd8675e5561b77d35b423b189c9da73 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 09:13:53 -0700 Subject: [PATCH 050/108] removed some debug --- interface/src/models/ModelTreeRenderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index e5c55ef066..36a22621b2 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -183,9 +183,11 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // set the position model->setTranslation(position); + /* qDebug() << "modelItem.getModelURL()=" << modelItem.getModelURL(); qDebug() << "modelItem.getAnimationURL()=" << modelItem.getAnimationURL(); qDebug() << "modelItem.hasAnimation()=" << modelItem.hasAnimation(); + */ // handle animations.. if (modelItem.hasAnimation()) { From 070d9960961766c201b4b34c9c1b5082e8eab2ef Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Mon, 12 May 2014 19:47:41 +0200 Subject: [PATCH 051/108] change `key missing` message --- interface/src/ui/SnapshotShareDialog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp index 2ac1c81f52..b5694b3e48 100644 --- a/interface/src/ui/SnapshotShareDialog.cpp +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -79,7 +79,8 @@ void SnapshotShareDialog::accept() { void SnapshotShareDialog::uploadSnapshot() { if (AccountManager::getInstance().getAccountInfo().getDiscourseApiKey().isEmpty()) { - QMessageBox::warning(this, "", "Your Discourse API key is missing, you cannot share snapshots."); + QMessageBox::warning(this, "", + "Your Discourse API key is missing, you cannot share snapshots. Please try to relog."); return; } From 02ca7c75eb1feb084609f47445915f668499fc94 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 11:02:10 -0700 Subject: [PATCH 052/108] first cut at adding version parsing to model items, not really working --- libraries/models/src/ModelItem.cpp | 23 ++++++++++++----------- libraries/models/src/ModelItem.h | 2 ++ libraries/octree/src/Octree.cpp | 6 ++++-- libraries/octree/src/Octree.h | 7 +++++-- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 785183da56..1c735c2075 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -246,18 +246,19 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += bytes; bytesRead += bytes; - // animationURL - uint16_t animationURLLength; - memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); - dataAt += sizeof(animationURLLength); - bytesRead += sizeof(animationURLLength); - QString animationURLString((const char*)dataAt); - setAnimationURL(animationURLString); - dataAt += animationURLLength; - bytesRead += animationURLLength; - -qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + if (args.bitstreamVersion >= VERSION_MODELS_HAVE_ANIMATION) { + // animationURL + uint16_t animationURLLength; + memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); + dataAt += sizeof(animationURLLength); + bytesRead += sizeof(animationURLLength); + QString animationURLString((const char*)dataAt); + setAnimationURL(animationURLString); + dataAt += animationURLLength; + bytesRead += animationURLLength; + qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + } //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 72a12c9b2c..cafd150e6d 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -51,6 +51,8 @@ const QString MODEL_DEFAULT_MODEL_URL(""); const glm::quat MODEL_DEFAULT_MODEL_ROTATION; const QString MODEL_DEFAULT_ANIMATION_URL(""); +const PacketVersion VERSION_MODELS_HAVE_ANIMATION = 1; + /// A collection of properties of a model item used in the scripting API. Translates between the actual properties of a model /// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of /// model item properties via JavaScript hashes/QScriptValues diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 5b766ecdd7..9f08adb1d9 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1556,6 +1556,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, bool Octree::readFromSVOFile(const char* fileName) { bool fileOk = false; + PacketVersion gotVersion = 0; std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate); if(file.is_open()) { emit importSize(1.0f, 1.0f, 1.0f); @@ -1587,7 +1588,7 @@ bool Octree::readFromSVOFile(const char* fileName) { dataAt += sizeof(expectedType); dataLength -= sizeof(expectedType); PacketVersion expectedVersion = versionForPacketType(expectedType); - PacketVersion gotVersion = *dataAt; + gotVersion = *dataAt; if (gotVersion == expectedVersion) { dataAt += sizeof(expectedVersion); dataLength -= sizeof(expectedVersion); @@ -1602,7 +1603,8 @@ bool Octree::readFromSVOFile(const char* fileName) { fileOk = true; // assume the file is ok } if (fileOk) { - ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, SharedNodePointer(), wantImportProgress); + ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, + SharedNodePointer(), wantImportProgress, gotVersion); readBitstreamToTree(dataAt, dataLength, args); } delete[] entireFile; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 4a17cb3c1d..0b9cc31a2a 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -170,6 +170,7 @@ public: QUuid sourceUUID; SharedNodePointer sourceNode; bool wantImportProgress; + PacketVersion bitstreamVersion; ReadBitstreamToTreeParams( bool includeColor = WANT_COLOR, @@ -177,13 +178,15 @@ public: OctreeElement* destinationElement = NULL, QUuid sourceUUID = QUuid(), SharedNodePointer sourceNode = SharedNodePointer(), - bool wantImportProgress = false) : + bool wantImportProgress = false, + PacketVersion bitstreamVersion = 0) : includeColor(includeColor), includeExistsBits(includeExistsBits), destinationElement(destinationElement), sourceUUID(sourceUUID), sourceNode(sourceNode), - wantImportProgress(wantImportProgress) + wantImportProgress(wantImportProgress), + bitstreamVersion(bitstreamVersion) {} }; From 47268fef32e264cd1d09af83c832d109b1b55276 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 12 May 2014 11:06:14 -0700 Subject: [PATCH 053/108] Fixed kooky conditional. --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 52eff6e2ce..6778460ec0 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1490,7 +1490,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; } From 49dedb72c97b3e44d5af3e9d80a929d136ea65e0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 12 May 2014 11:09:12 -0700 Subject: [PATCH 054/108] remove FingerData class and related cleanup --- interface/src/BuckyBalls.cpp | 8 +- interface/src/avatar/Avatar.cpp | 16 +-- interface/src/avatar/Hand.cpp | 64 ++--------- interface/src/avatar/SkeletonModel.cpp | 26 +---- interface/src/devices/SixenseManager.cpp | 29 ++--- .../ControllerScriptingInterface.cpp | 4 +- libraries/avatars/src/HandData.cpp | 107 +++--------------- libraries/avatars/src/HandData.h | 82 +++----------- libraries/shared/src/SharedUtil.h | 1 + 9 files changed, 63 insertions(+), 274 deletions(-) diff --git a/interface/src/BuckyBalls.cpp b/interface/src/BuckyBalls.cpp index e1ec41dca1..68d1167071 100644 --- a/interface/src/BuckyBalls.cpp +++ b/interface/src/BuckyBalls.cpp @@ -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(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4de608fc6b..00b7b8c682 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -604,18 +604,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) { @@ -626,8 +614,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 diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 576790714e..78eab424ab 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -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); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 8c21a3240f..7a6fadeb6b 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -159,29 +159,13 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& 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 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)) { diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 0435519124..461b414b2b 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -106,7 +106,7 @@ void SixenseManager::update(float deltaTime) { palm->setControllerButtons(data->buttons); palm->setTrigger(data->trigger); palm->setJoystick(data->joystick_x, data->joystick_y); - + glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); // Transform the measured position into body frame. glm::vec3 neck = _neckBase; @@ -117,15 +117,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()) * (METERS_PER_MILLIMETER / deltaTime); } else { rawVelocity = glm::vec3(0.0f); } @@ -140,29 +137,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 = 300.0f; // 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) * (METERS_PER_MILLIMETER / 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 +156,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::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) { it->setActive(false); } @@ -236,7 +221,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) 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; diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index aa14f769de..5e58ac66ea 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -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 diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index c2e3b51cb3..109eab5c5d 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -26,7 +26,7 @@ HandData::HandData(AvatarData* owningAvatar) : addNewPalm(); } -glm::vec3 HandData::worldVectorToLeapVector(const glm::vec3& worldVector) const { +glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const { return glm::inverse(getBaseOrientation()) * worldVector / LEAP_UNIT_SCALE; } @@ -66,7 +66,6 @@ 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), @@ -78,57 +77,11 @@ _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 +110,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.03f); + 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); } diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index a37e3a5814..968d7bbe38 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -21,7 +21,6 @@ #include "SharedUtil.h" class AvatarData; -class FingerData; class PalmData; const int NUM_HANDS = 2; @@ -41,17 +40,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 * LEAP_UNIT_SCALE; } - 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& getPalms() { return _palms; } const std::vector& getPalms() const { return _palms; } @@ -63,9 +61,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,63 +84,18 @@ 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 _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& getFingers() { return _fingers; } - const std::vector& 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; } @@ -153,7 +103,6 @@ public: 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 +111,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 +147,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 _fingers; glm::quat _rawRotation; glm::vec3 _rawPosition; - glm::vec3 _rawNormal; glm::vec3 _rawVelocity; glm::vec3 _rotationalVelocity; glm::quat _lastRotation; diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 4a3fe2a129..d111439b7e 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -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; From efb3edf4006de207c23a9ad65de4355eccb024c1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 12 May 2014 11:39:21 -0700 Subject: [PATCH 055/108] Store sixense data in meters not millimeters --- interface/src/devices/SixenseManager.cpp | 22 ++++++++++++++-------- libraries/avatars/src/HandData.cpp | 2 +- libraries/avatars/src/HandData.h | 4 +--- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 461b414b2b..1698cebcf9 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -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() { @@ -107,7 +107,10 @@ void SixenseManager::update(float deltaTime) { 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. @@ -122,7 +125,7 @@ void SixenseManager::update(float deltaTime) { // Compute current velocity from position change glm::vec3 rawVelocity; if (deltaTime > 0.f) { - rawVelocity = (position - palm->getRawPosition()) * (METERS_PER_MILLIMETER / deltaTime); + rawVelocity = (position - palm->getRawPosition()) / deltaTime; } else { rawVelocity = glm::vec3(0.0f); } @@ -138,12 +141,12 @@ void SixenseManager::update(float deltaTime) { } // Store the one fingertip in the palm structure so we can track velocity - const float FINGER_LENGTH = 300.0f; // meters + 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; glm::vec3 oldTipPosition = palm->getTipRawPosition(); if (deltaTime > 0.f) { - palm->setTipVelocity((newTipPosition - oldTipPosition) * (METERS_PER_MILLIMETER / deltaTime)); + palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); } else { palm->setTipVelocity(glm::vec3(0.f)); } @@ -173,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) { @@ -214,10 +217,13 @@ 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); diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 109eab5c5d..4dad06fa84 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -27,7 +27,7 @@ HandData::HandData(AvatarData* owningAvatar) : } glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const { - return glm::inverse(getBaseOrientation()) * worldVector / LEAP_UNIT_SCALE; + return glm::inverse(getBaseOrientation()) * worldVector; } PalmData& HandData::addNewPalm() { diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 968d7bbe38..d1e11aed8e 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -30,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; @@ -42,7 +40,7 @@ public: // position conversion glm::vec3 localToWorldPosition(const glm::vec3& localPosition) { - return getBasePosition() + getBaseOrientation() * localPosition * LEAP_UNIT_SCALE; + return getBasePosition() + getBaseOrientation() * localPosition; } glm::vec3 localToWorldDirection(const glm::vec3& localVector) { From 8fe74d006a96429f606365f14cc82e4bc46a1698 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 11:40:15 -0700 Subject: [PATCH 056/108] correctly handle reading models.svo files with older version --- libraries/models/src/ModelItem.cpp | 3 +++ libraries/models/src/ModelTree.h | 1 + libraries/octree/src/Octree.cpp | 12 +++++++----- libraries/octree/src/Octree.h | 4 ++++ libraries/octree/src/OctreeRenderer.cpp | 3 ++- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 1c735c2075..a818105521 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -188,6 +188,7 @@ int ModelItem::expectedBytes() { } int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { + int bytesRead = 0; if (bytesLeftToRead >= expectedBytes()) { int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0; @@ -258,6 +259,8 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT bytesRead += animationURLLength; qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + } else { + qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); diff --git a/libraries/models/src/ModelTree.h b/libraries/models/src/ModelTree.h index ac25cdc003..10ef62c0a0 100644 --- a/libraries/models/src/ModelTree.h +++ b/libraries/models/src/ModelTree.h @@ -36,6 +36,7 @@ public: // own definition. Implement these to allow your octree based server to support editing virtual bool getWantSVOfileVersions() const { return true; } virtual PacketType expectedDataPacketType() const { return PacketTypeModelData; } + virtual bool canProcessVersion(PacketVersion thisVersion) const { return true; } // we support all versions virtual bool handlesEditPacketType(PacketType packetType) const; virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 9f08adb1d9..12ccf98c70 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1587,14 +1587,16 @@ bool Octree::readFromSVOFile(const char* fileName) { if (gotType == expectedType) { dataAt += sizeof(expectedType); dataLength -= sizeof(expectedType); - PacketVersion expectedVersion = versionForPacketType(expectedType); gotVersion = *dataAt; - if (gotVersion == expectedVersion) { - dataAt += sizeof(expectedVersion); - dataLength -= sizeof(expectedVersion); + if (canProcessVersion(gotVersion)) { + dataAt += sizeof(gotVersion); + dataLength -= sizeof(gotVersion); fileOk = true; + qDebug("SVO file version match. Expected: %d Got: %d", + versionForPacketType(expectedDataPacketType()), gotVersion); } else { - qDebug("SVO file version mismatch. Expected: %d Got: %d", expectedVersion, gotVersion); + qDebug("SVO file version mismatch. Expected: %d Got: %d", + versionForPacketType(expectedDataPacketType()), gotVersion); } } else { qDebug("SVO file type mismatch. Expected: %c Got: %c", expectedType, gotType); diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 0b9cc31a2a..84212586f8 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -203,6 +203,9 @@ public: // own definition. Implement these to allow your octree based server to support editing virtual bool getWantSVOfileVersions() const { return false; } virtual PacketType expectedDataPacketType() const { return PacketTypeUnknown; } + virtual bool canProcessVersion(PacketVersion thisVersion) const { + return thisVersion == versionForPacketType(expectedDataPacketType()); } + virtual PacketVersion expectedVersion() const { return versionForPacketType(expectedDataPacketType()); } virtual bool handlesEditPacketType(PacketType packetType) const { return false; } virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength, const unsigned char* editData, int maxLength, const SharedNodePointer& sourceNode) { return 0; } @@ -306,6 +309,7 @@ public: bool getIsViewing() const { return _isViewing; } void setIsViewing(bool isViewing) { _isViewing = isViewing; } + signals: void importSize(float x, float y, float z); diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index c1ce3cb218..1a85518181 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -64,6 +64,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar unsigned int numBytesPacketHeader = numBytesForPacketHeader(dataByteArray); QUuid sourceUUID = uuidFromPacketHeader(dataByteArray); PacketType expectedType = getExpectedPacketType(); + PacketVersion expectedVersion = _tree->expectedVersion(); // TODO: would be better to read this from the packet! if(command == expectedType) { PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PacketType", showTimingDetails); @@ -115,7 +116,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar if (sectionLength) { // ask the VoxelTree to read the bitstream into the tree ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, - sourceUUID, sourceNode); + sourceUUID, sourceNode, false, expectedVersion); _tree->lockForWrite(); OctreePacketData packetData(packetIsCompressed); packetData.loadFinalizedContent(dataAt, sectionLength); From cea472b7304b9498055d5b6ada716c8113644f0c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 12 May 2014 11:48:56 -0700 Subject: [PATCH 057/108] remove the last of Leap specific cruft --- interface/src/avatar/SkeletonModel.cpp | 4 ++-- libraries/avatars/src/HandData.cpp | 1 - libraries/avatars/src/HandData.h | 3 --- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 7a6fadeb6b..a2e637f4e7 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -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 { diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 4dad06fa84..446cf2bc2a 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -71,7 +71,6 @@ _rotationalVelocity(0.f), _totalPenetration(0.f), _controllerButtons(0), _isActive(false), -_leapID(LEAPID_INVALID), _sixenseID(SIXENSEID_INVALID), _numFramesWithoutData(0), _owningHandData(owningHandData), diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index d1e11aed8e..1f2d134c43 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -91,11 +91,9 @@ public: const glm::vec3& getRawPosition() const { return _rawPosition; } bool isActive() const { return _isActive; } - int getLeapID() const { return _leapID; } int getSixenseID() const { return _sixenseID; } 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; }; @@ -166,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; From e983be401354958de53788c311027566f8af64bc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 12 May 2014 11:52:16 -0700 Subject: [PATCH 058/108] fix typo (finger length = 0.3m, not 0.03m) --- libraries/avatars/src/HandData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 446cf2bc2a..bd366f020a 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -110,7 +110,7 @@ glm::vec3 HandData::getBasePosition() const { } glm::vec3 PalmData::getFingerTipPosition() const { - glm::vec3 fingerOffset(0.0f, 0.0f, 0.03f); + 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)); } From 7ffb700564f028944224ebc1c5f19e77121e73b9 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Mon, 12 May 2014 20:55:24 +0200 Subject: [PATCH 059/108] fixed mention sounds dir for deployment --- .../mention => mention-sounds}/Mentioned A.wav | Bin .../mention => mention-sounds}/Mentioned B.wav | Bin .../mention => mention-sounds}/Mentioned C.wav | Bin interface/src/XmppClient.cpp | 2 +- interface/src/ui/ChatWindow.cpp | 2 +- 5 files changed, 2 insertions(+), 2 deletions(-) rename interface/resources/{sounds/mention => mention-sounds}/Mentioned A.wav (100%) rename interface/resources/{sounds/mention => mention-sounds}/Mentioned B.wav (100%) rename interface/resources/{sounds/mention => mention-sounds}/Mentioned C.wav (100%) diff --git a/interface/resources/sounds/mention/Mentioned A.wav b/interface/resources/mention-sounds/Mentioned A.wav similarity index 100% rename from interface/resources/sounds/mention/Mentioned A.wav rename to interface/resources/mention-sounds/Mentioned A.wav diff --git a/interface/resources/sounds/mention/Mentioned B.wav b/interface/resources/mention-sounds/Mentioned B.wav similarity index 100% rename from interface/resources/sounds/mention/Mentioned B.wav rename to interface/resources/mention-sounds/Mentioned B.wav diff --git a/interface/resources/sounds/mention/Mentioned C.wav b/interface/resources/mention-sounds/Mentioned C.wav similarity index 100% rename from interface/resources/sounds/mention/Mentioned C.wav rename to interface/resources/mention-sounds/Mentioned C.wav diff --git a/interface/src/XmppClient.cpp b/interface/src/XmppClient.cpp index 2d421b1afa..666906681c 100644 --- a/interface/src/XmppClient.cpp +++ b/interface/src/XmppClient.cpp @@ -16,7 +16,7 @@ #include "XmppClient.h" const QString DEFAULT_XMPP_SERVER = "chat.highfidelity.io"; -const QString DEFAULT_CHAT_ROOM = "test@public-chat.highfidelity.io"; +const QString DEFAULT_CHAT_ROOM = "public@public-chat.highfidelity.io"; XmppClient::XmppClient() : _xmppClient(), diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index fce900f352..611f955031 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -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) : From 8541df9c32093360cfd4b8aa5bbdf9cb2853864c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 12 May 2014 12:16:30 -0700 Subject: [PATCH 060/108] Remember attachment parameters per joint (as well as the last joint used) so that the gun script correctly allows both guns' positions to be tweaked. --- interface/src/avatar/MyAvatar.cpp | 40 ++++++++++++------- interface/src/avatar/MyAvatar.h | 2 +- interface/src/ui/AttachmentsDialog.cpp | 55 ++++++++++++++++++++------ interface/src/ui/AttachmentsDialog.h | 4 ++ libraries/avatars/src/AvatarData.h | 2 + 5 files changed, 77 insertions(+), 26 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6778460ec0..36c51dc9fd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -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; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 2e47d9c973..d446c2e895 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -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; }; diff --git a/interface/src/ui/AttachmentsDialog.cpp b/interface/src/ui/AttachmentsDialog.cpp index f9f49738e9..44dd2452e6 100644 --- a/interface/src/ui/AttachmentsDialog.cpp +++ b/interface/src/ui/AttachmentsDialog.cpp @@ -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(); +} diff --git a/interface/src/ui/AttachmentsDialog.h b/interface/src/ui/AttachmentsDialog.h index 7e9319fba8..59696c96f1 100644 --- a/interface/src/ui/AttachmentsDialog.h +++ b/interface/src/ui/AttachmentsDialog.h @@ -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 diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index dd604d06f5..072070e98c 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -352,6 +352,8 @@ public: AttachmentData(); + bool isValid() const { return modelURL.isValid(); } + bool operator==(const AttachmentData& other) const; }; From e4d2f075867f7fec8903008ede141682117dcea3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 12:51:45 -0700 Subject: [PATCH 061/108] fixed bug in writing small SVO files --- libraries/models/src/ModelItem.cpp | 3 ++- libraries/models/src/ModelTreeElement.cpp | 2 +- libraries/octree/src/Octree.cpp | 7 +------ 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index a818105521..aedd1fef47 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -263,7 +263,7 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } - //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); + //qDebug() << "ModelItem::readModelDataFromBuffer()... "; debugDump(); } return bytesRead; } @@ -417,6 +417,7 @@ void ModelItem::debugDump() const { qDebug(" position:%f,%f,%f", _position.x, _position.y, _position.z); qDebug(" radius:%f", getRadius()); qDebug(" color:%d,%d,%d", _color[0], _color[1], _color[2]); + qDebug() << " modelURL:" << qPrintable(getModelURL()); } bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id, const ModelItemProperties& properties, diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index 5c5d5100cf..4edcdd3e92 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -324,7 +324,7 @@ int ModelTreeElement::readElementDataFromBuffer(const unsigned char* data, int b dataAt += sizeof(numberOfModels); bytesLeftToRead -= (int)sizeof(numberOfModels); bytesRead += sizeof(numberOfModels); - + if (bytesLeftToRead >= (int)(numberOfModels * expectedBytesPerModel)) { for (uint16_t i = 0; i < numberOfModels; i++) { ModelItem tempModel; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 12ccf98c70..128677ff2b 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -335,10 +335,8 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt); int theseBytesRead = 0; theseBytesRead += octalCodeBytes; - theseBytesRead += readElementData(bitstreamRootElement, bitstreamAt + octalCodeBytes, bufferSizeBytes - (bytesRead + octalCodeBytes), args); - // skip bitstream to new startPoint bitstreamAt += theseBytesRead; bytesRead += theseBytesRead; @@ -1619,7 +1617,6 @@ bool Octree::readFromSVOFile(const char* fileName) { } void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { - std::ofstream file(fileName, std::ios::out|std::ios::binary); if(file.is_open()) { @@ -1642,13 +1639,12 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { nodeBag.insert(_rootElement); } - static OctreePacketData packetData; + OctreePacketData packetData; int bytesWritten = 0; bool lastPacketWritten = false; while (!nodeBag.isEmpty()) { OctreeElement* subTree = nodeBag.extract(); - lockForRead(); // do tree locking down here so that we have shorter slices and less thread contention EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS); bytesWritten = encodeTreeBitstream(subTree, &packetData, nodeBag, params); @@ -1670,7 +1666,6 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { if (!lastPacketWritten) { file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize()); } - } file.close(); } From 5309c5ad8bd0e7228c4b23a98e76d68d1b4d9ba0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 13:44:44 -0700 Subject: [PATCH 062/108] add more properties to animated models --- examples/placeModelsWithHands.js | 1 + libraries/models/src/ModelItem.cpp | 132 +++++++++++++++++- libraries/models/src/ModelItem.h | 19 ++- .../models/src/ModelsScriptingInterface.h | 14 ++ 4 files changed, 159 insertions(+), 7 deletions(-) diff --git a/examples/placeModelsWithHands.js b/examples/placeModelsWithHands.js index f16945472d..bd0915da54 100644 --- a/examples/placeModelsWithHands.js +++ b/examples/placeModelsWithHands.js @@ -231,6 +231,7 @@ function checkControllerSide(whichSide) { if (animationURLs[currentModelURL] !== "") { properties.animationURL = animationURLs[currentModelURL]; + properties.isAnimationPlaying = true; } debugPrint("modelRadius=" +modelRadius); diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index aedd1fef47..9dd1173259 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -88,7 +88,9 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties& // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _isAnimationPlaying = false; _frameIndex = 0.0f; + _jointMappingCompleted = false; _lastAnimated = now; @@ -119,6 +121,7 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _isAnimationPlaying = false; _frameIndex = 0.0f; _jointMappingCompleted = false; _lastAnimated = now; @@ -172,6 +175,16 @@ bool ModelItem::appendModelData(OctreePacketData* packetData) const { } } + // isAnimationPlaying + if (success) { + success = packetData->appendValue(getIsAnimationPlaying()); + } + + // frameIndex + if (success) { + success = packetData->appendValue(getFrameIndex()); + } + return success; } @@ -259,6 +272,17 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT bytesRead += animationURLLength; qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + + // isAnimationPlaying + memcpy(&_isAnimationPlaying, dataAt, sizeof(_isAnimationPlaying)); + dataAt += sizeof(_isAnimationPlaying); + bytesRead += sizeof(_isAnimationPlaying); + + // frameIndex + memcpy(&_frameIndex, dataAt, sizeof(_frameIndex)); + dataAt += sizeof(_frameIndex); + bytesRead += sizeof(_frameIndex); + } else { qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } @@ -395,9 +419,25 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& newModelItem._animationURL = tempString; dataAt += animationURLLength; processedBytes += animationURLLength; - qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); + } + // isAnimationPlaying + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { + + memcpy(&newModelItem._isAnimationPlaying, dataAt, sizeof(newModelItem._isAnimationPlaying)); + dataAt += sizeof(newModelItem._isAnimationPlaying); + processedBytes += sizeof(newModelItem._isAnimationPlaying); + } + + // frameIndex + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { + + memcpy(&newModelItem._frameIndex, dataAt, sizeof(newModelItem._frameIndex)); + dataAt += sizeof(newModelItem._frameIndex); + processedBytes += sizeof(newModelItem._frameIndex); } const bool wantDebugging = false; @@ -545,6 +585,30 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable } + // isAnimationPlaying + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { + + bool isAnimationPlaying = properties.getIsAnimationPlaying(); + memcpy(copyAt, &isAnimationPlaying, sizeof(isAnimationPlaying)); + copyAt += sizeof(isAnimationPlaying); + sizeOut += sizeof(isAnimationPlaying); + + +qDebug() << "encodeModelItemEditMessageDetails()... isAnimationPlaying=" << isAnimationPlaying; + } + + // frameIndex + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { + + float frameIndex = properties.getFrameIndex(); + memcpy(copyAt, &frameIndex, sizeof(frameIndex)); + copyAt += sizeof(frameIndex); + sizeOut += sizeof(frameIndex); + +qDebug() << "encodeModelItemEditMessageDetails()... frameIndex=" << frameIndex; + } bool wantDebugging = false; if (wantDebugging) { @@ -631,11 +695,16 @@ void ModelItem::mapJoints(const QStringList& modelJointNames) { QVector ModelItem::getAnimationFrame() { QVector frameData; if (hasAnimation() && _jointMappingCompleted) { - quint64 now = usecTimestampNow(); - float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; - _lastAnimated = now; - const float FRAME_RATE = 10.0f; - _frameIndex += deltaTime * FRAME_RATE; + + // only advance the frame index if we're playing + if (getIsAnimationPlaying()) { + quint64 now = usecTimestampNow(); + float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; + _lastAnimated = now; + const float FRAME_RATE = 10.0f; + _frameIndex += deltaTime * FRAME_RATE; + } + Animation* myAnimation = getAnimation(_animationURL); QVector frames = myAnimation->getFrames(); int frameIndex = (int)std::floor(_frameIndex) % frames.size(); @@ -678,6 +747,8 @@ ModelItemProperties::ModelItemProperties() : _modelURL(""), _modelRotation(MODEL_DEFAULT_MODEL_ROTATION), _animationURL(""), + _isAnimationPlaying(false), + _frameIndex(0.0), _id(UNKNOWN_MODEL_ID), _idSet(false), @@ -690,6 +761,8 @@ ModelItemProperties::ModelItemProperties() : _modelURLChanged(false), _modelRotationChanged(false), _animationURLChanged(false), + _isAnimationPlayingChanged(false), + _frameIndexChanged(false), _defaultSettings(true) { } @@ -725,6 +798,13 @@ uint16_t ModelItemProperties::getChangedBits() const { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_URL; } + if (_isAnimationPlayingChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_PLAYING; + } + + if (_frameIndexChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_FRAME; + } return changedBits; } @@ -749,6 +829,8 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const properties.setProperty("modelRotation", modelRotation); properties.setProperty("animationURL", _animationURL); + properties.setProperty("isAnimationPlaying", _isAnimationPlaying); + properties.setProperty("frameIndex", _frameIndex); if (_idSet) { properties.setProperty("id", _id); @@ -855,6 +937,26 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { } } + QScriptValue isAnimationPlaying = object.property("isAnimationPlaying"); + if (isAnimationPlaying.isValid()) { + bool newIsAnimationPlaying; + newIsAnimationPlaying = isAnimationPlaying.toVariant().toBool(); + if (_defaultSettings || newIsAnimationPlaying != _isAnimationPlaying) { + _isAnimationPlaying = newIsAnimationPlaying; + _isAnimationPlayingChanged = true; + } + } + + QScriptValue frameIndex = object.property("frameIndex"); + if (frameIndex.isValid()) { + float newFrameIndex; + newFrameIndex = frameIndex.toVariant().toFloat(); + if (_defaultSettings || newFrameIndex != _frameIndex) { + _frameIndex = newFrameIndex; + _frameIndexChanged = true; + } + } + _lastEdited = usecTimestampNow(); } @@ -897,6 +999,20 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; } + if (_isAnimationPlayingChanged) { + modelItem.setIsAnimationPlaying(_isAnimationPlaying); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... _isAnimationPlaying=" << _isAnimationPlaying; + } + + if (_frameIndexChanged) { + modelItem.setFrameIndex(_frameIndex); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... _frameIndex=" << _frameIndex; + } + if (somethingChanged) { bool wantDebug = false; if (wantDebug) { @@ -917,6 +1033,8 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURL = modelItem.getModelURL(); _modelRotation = modelItem.getModelRotation(); _animationURL = modelItem.getAnimationURL(); + _isAnimationPlaying = modelItem.getIsAnimationPlaying(); + _frameIndex = modelItem.getFrameIndex(); _id = modelItem.getID(); _idSet = true; @@ -929,6 +1047,8 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURLChanged = false; _modelRotationChanged = false; _animationURLChanged = false; + _isAnimationPlayingChanged = false; + _frameIndexChanged = false; _defaultSettings = false; } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index cafd150e6d..a6c317d122 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -44,6 +44,8 @@ const uint16_t MODEL_PACKET_CONTAINS_SHOULDDIE = 8; const uint16_t MODEL_PACKET_CONTAINS_MODEL_URL = 16; const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 32; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_URL = 64; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_PLAYING = 128; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_FRAME = 256; const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container @@ -75,6 +77,8 @@ public: const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } const QString& getAnimationURL() const { return _animationURL; } + float getFrameIndex() const { return _frameIndex; } + bool getIsAnimationPlaying() const { return _isAnimationPlaying; } quint64 getLastEdited() const { return _lastEdited; } uint16_t getChangedBits() const; @@ -89,6 +93,8 @@ public: void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; } void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; } + void setFrameIndex(float value) { _frameIndex = value; _frameIndexChanged = true; } + void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; _isAnimationPlayingChanged = true; } /// used by ModelScriptingInterface to return ModelItemProperties for unknown models void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; } @@ -105,6 +111,8 @@ private: QString _modelURL; glm::quat _modelRotation; QString _animationURL; + bool _isAnimationPlaying; + float _frameIndex; uint32_t _id; bool _idSet; @@ -118,6 +126,8 @@ private: bool _modelURLChanged; bool _modelRotationChanged; bool _animationURLChanged; + bool _isAnimationPlayingChanged; + bool _frameIndexChanged; bool _defaultSettings; }; Q_DECLARE_METATYPE(ModelItemProperties); @@ -228,6 +238,8 @@ public: void setModelURL(const QString& url) { _modelURL = url; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; } void setAnimationURL(const QString& url) { _animationURL = url; } + void setFrameIndex(float value) { _frameIndex = value; } + void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; } void setProperties(const ModelItemProperties& properties); @@ -254,7 +266,10 @@ public: void mapJoints(const QStringList& modelJointNames); QVector getAnimationFrame(); - bool jointsMapped() const { return _jointMappingCompleted; }; + bool jointsMapped() const { return _jointMappingCompleted; } + + bool getIsAnimationPlaying() const { return _isAnimationPlaying; } + float getFrameIndex() const { return _frameIndex; } protected: glm::vec3 _position; @@ -277,6 +292,8 @@ protected: QString _animationURL; float _frameIndex; // we keep this as a float and round to int only when we need the exact index + bool _isAnimationPlaying; + bool _jointMappingCompleted; QVector _jointMapping; diff --git a/libraries/models/src/ModelsScriptingInterface.h b/libraries/models/src/ModelsScriptingInterface.h index bf8e193f25..f08ec715a9 100644 --- a/libraries/models/src/ModelsScriptingInterface.h +++ b/libraries/models/src/ModelsScriptingInterface.h @@ -59,6 +59,20 @@ public slots: /// this function will not find any models in script engine contexts which don't have access to models QVector findModels(const glm::vec3& center, float radius) const; + /* + /// pauses the model animation. + ModelItemID pauseModelAnimation(ModelItemID modelID); + + /// plays the model animation. + ModelItemID playModelAnimation(ModelItemID modelID); + + /// gets the current frame of the model animation. + float getModelAnimationFrame(ModelItemID modelID); + + /// gets the current frame of the model animation. + void setModelAnimationFrame(ModelItemID modelID, float frame); + */ + signals: void modelCollisionWithVoxel(const ModelItemID& modelID, const VoxelDetail& voxel, const CollisionInfo& collision); void modelCollisionWithModel(const ModelItemID& idA, const ModelItemID& idB, const CollisionInfo& collision); From a1a5372b7411d37b871c81afca42abae15359be9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 12 May 2014 14:07:33 -0700 Subject: [PATCH 063/108] add missing glPushMatrix() --- interface/src/avatar/Hand.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 576790714e..4774e23cb2 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -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); From 0fc33f575ffece93f0a928ba50523598c884e871 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 14:07:54 -0700 Subject: [PATCH 064/108] moving frame advancement to update() --- libraries/models/src/ModelItem.cpp | 35 +++++++++++++++-------- libraries/models/src/ModelTreeElement.cpp | 5 ++++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 9dd1173259..f9b1092c84 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -283,6 +283,8 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += sizeof(_frameIndex); bytesRead += sizeof(_frameIndex); + qDebug() << "readModelDataFromBuffer()... _frameIndex=" << _frameIndex; + } else { qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } @@ -695,16 +697,6 @@ void ModelItem::mapJoints(const QStringList& modelJointNames) { QVector ModelItem::getAnimationFrame() { QVector frameData; if (hasAnimation() && _jointMappingCompleted) { - - // only advance the frame index if we're playing - if (getIsAnimationPlaying()) { - quint64 now = usecTimestampNow(); - float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; - _lastAnimated = now; - const float FRAME_RATE = 10.0f; - _frameIndex += deltaTime * FRAME_RATE; - } - Animation* myAnimation = getAnimation(_animationURL); QVector frames = myAnimation->getFrames(); int frameIndex = (int)std::floor(_frameIndex) % frames.size(); @@ -720,9 +712,28 @@ QVector ModelItem::getAnimationFrame() { return frameData; } -void ModelItem::update(const quint64& now) { - _lastUpdated = now; +void ModelItem::update(const quint64& updateTime) { + _lastUpdated = updateTime; setShouldDie(getShouldDie()); + +//qDebug() << "ModelItem::update() now=" << now; + + // only advance the frame index if we're playing + if (getIsAnimationPlaying()) { + + quint64 now = usecTimestampNow(); + float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; +qDebug() << "ModelItem::update() now=" << now; +qDebug() << " updateTime=" << updateTime; +qDebug() << " _lastAnimated=" << _lastAnimated; +qDebug() << " deltaTime=" << deltaTime; + + _lastAnimated = now; + const float FRAME_RATE = 10.0f; + _frameIndex += deltaTime * FRAME_RATE; +qDebug() << " _frameIndex=" << _frameIndex; + + } } void ModelItem::copyChangedProperties(const ModelItem& other) { diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index 4edcdd3e92..e8b37c478b 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -102,10 +102,15 @@ bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const { } void ModelTreeElement::update(ModelTreeUpdateArgs& args) { +//qDebug() << "ModelTreeElement::update()..."; // update our contained models QList::iterator modelItr = _modelItems->begin(); while(modelItr != _modelItems->end()) { ModelItem& model = (*modelItr); + + // TODO: this _lastChanged isn't actually changing because we're not marking this element as changed. + // how do we want to handle this??? We really only want to consider an element changed when it is + // edited... not just animated... model.update(_lastChanged); // If the model wants to die, or if it's left our bounding box, then move it From e0486b26549062d0311b706cc0d58465d43883e7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 12 May 2014 15:25:25 -0700 Subject: [PATCH 065/108] Starting on PrioVR integration. --- cmake/modules/FindPrioVR.cmake | 42 +++++++++++++++++++ interface/CMakeLists.txt | 9 +++++ interface/external/priovr/readme.txt | 11 +++++ interface/src/Application.cpp | 1 + interface/src/Application.h | 2 + interface/src/devices/PrioVR.cpp | 60 ++++++++++++++++++++++++++++ interface/src/devices/PrioVR.h | 41 +++++++++++++++++++ 7 files changed, 166 insertions(+) create mode 100644 cmake/modules/FindPrioVR.cmake create mode 100644 interface/external/priovr/readme.txt create mode 100644 interface/src/devices/PrioVR.cpp create mode 100644 interface/src/devices/PrioVR.h diff --git a/cmake/modules/FindPrioVR.cmake b/cmake/modules/FindPrioVR.cmake new file mode 100644 index 0000000000..96ee1a7bdc --- /dev/null +++ b/cmake/modules/FindPrioVR.cmake @@ -0,0 +1,42 @@ +# Try to find the PrioVT library +# +# You must provide a PRIOVR_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# PRIOVR_FOUND - system found PrioVR +# PRIOVR_INCLUDE_DIRS - the PrioVR include directory +# PRIOVR_LIBRARIES - Link this to use PrioVR +# +# Created on 5/12/2014 by Andrzej Kapolka +# Copyright (c) 2014 High Fidelity +# + +if (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) + # in cache already + set(PRIOVR_FOUND TRUE) +else (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) + find_path(PRIOVR_INCLUDE_DIRS yei_threespace_api.h ${PRIOVR_ROOT_DIR}/include) + + if (WIN32) + find_library(PRIOVR_LIBRARIES ThreeSpace_API.lib ${PRIOVR_ROOT_DIR}) + endif (WIN32) + + if (PRIOVR_INCLUDE_DIRS AND PRIOVR_LIBRARIES) + set(PRIOVR_FOUND TRUE) + endif (PRIOVR_INCLUDE_DIRS AND PRIOVR_LIBRARIES) + + if (PRIOVR_FOUND) + if (NOT PRIOVR_FIND_QUIETLY) + message(STATUS "Found PrioVR... ${PRIOVR_LIBRARIES}") + endif (NOT PRIOVR_FIND_QUIETLY) + else () + if (PRIOVR_FIND_REQUIRED) + message(FATAL_ERROR "Could not find PrioVR") + endif (PRIOVR_FIND_REQUIRED) + endif () + + # show the PRIOVR_INCLUDE_DIRS and PRIOVR_LIBRARIES variables only in the advanced view + mark_as_advanced(PRIOVR_INCLUDE_DIRS PRIOVR_LIBRARIES) + +endif (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0a56109260..d075fbe9d0 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -15,6 +15,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake set(FACEPLUS_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceplus") set(FACESHIFT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceshift") set(LIBOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/oculus") +set(PRIOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/priovr") set(SIXENSE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense") set(VISAGE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/visage") @@ -133,6 +134,7 @@ link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}") find_package(Faceplus) find_package(Faceshift) find_package(LibOVR) +find_package(PrioVR) find_package(Sixense) find_package(Visage) find_package(ZLIB) @@ -183,6 +185,13 @@ if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) target_link_libraries(${TARGET_NAME} "${LIBOVR_LIBRARIES}") endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) +# and with PrioVR library +if (PRIOVR_FOUND AND NOT DISABLE_PRIOVR) + add_definitions(-DHAVE_PRIOVR) + include_directories(SYSTEM "${PRIOVR_INCLUDE_DIRS}") + target_link_libraries(${TARGET_NAME} "${PRIOVR_LIBRARIES}") +endif (PRIOVR_FOUND AND NOT DISABLE_PRIOVR) + # and with qxmpp for chat if (QXMPP_FOUND AND NOT DISABLE_QXMPP) add_definitions(-DHAVE_QXMPP -DQXMPP_STATIC) diff --git a/interface/external/priovr/readme.txt b/interface/external/priovr/readme.txt new file mode 100644 index 0000000000..92a2aff711 --- /dev/null +++ b/interface/external/priovr/readme.txt @@ -0,0 +1,11 @@ + +Instructions for adding the PrioVR driver to Interface +Andrzej Kapolka, May 12, 2014 + +1. Copy the PrioVR sdk folders (include, *.lib) into the interface/external/priovr folder. + This readme.txt should be there as well. + +2. Copy the PrioVR DLLs from the API into your path. + +3. Delete your build directory, run cmake and build, and you should be all set. + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0b59fc3aa2..cc1ea14d2d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1987,6 +1987,7 @@ void Application::update(float deltaTime) { _myAvatar->updateLookAtTargetAvatar(); updateMyAvatarLookAtPosition(); _sixenseManager.update(deltaTime); + _prioVR.update(); updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... _avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them... diff --git a/interface/src/Application.h b/interface/src/Application.h index 67cf7dad44..77f8024b6c 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -58,6 +58,7 @@ #include "avatar/MyAvatar.h" #include "devices/Faceplus.h" #include "devices/Faceshift.h" +#include "devices/PrioVR.h" #include "devices/SixenseManager.h" #include "devices/Visage.h" #include "models/ModelTreeRenderer.h" @@ -441,6 +442,7 @@ private: Visage _visage; SixenseManager _sixenseManager; + PrioVR _prioVR; Camera _myCamera; // My view onto the world Camera _viewFrustumOffsetCamera; // The camera we use to sometimes show the view frustum from an offset mode diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp new file mode 100644 index 0000000000..cd28c6ab68 --- /dev/null +++ b/interface/src/devices/PrioVR.cpp @@ -0,0 +1,60 @@ +// +// PrioVR.cpp +// interface/src/devices +// +// Created by Andrzej Kapolka on 5/12/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 "PrioVR.h" + +PrioVR::PrioVR() { +#ifdef HAVE_PRIOVR + TSS_ComPort comPort; + if (!tss_getComPorts(&comPort, 1, 0, PVR_FIND_BS)) { + _baseStation = TSS_NO_DEVICE_ID; + return; + } + _baseStation = tss_createTSDeviceStr(comPort.com_port, TSS_TIMESTAMP_SYSTEM); + if (_baseStation == TSS_NO_DEVICE_ID) { + return; + } + for (int i = 0; i < MAX_SENSOR_NODES; i++) { + tss_getSensorFromDongle(_baseStation, i, &_sensorNodes[i]); + if (_sensorNodes[i] == TSS_NO_DEVICE_ID) { + continue; + } + int present; + tss_isPresent(_sensorNodes[i], &present); + if (!present) { + _sensorNodes[i] = TSS_NO_DEVICE_ID; + } + } + tss_startStreaming(_baseStation, NULL); +#endif +} + +PrioVR::~PrioVR() { +#ifdef HAVE_PRIOVR + if (_baseStation != TSS_NO_DEVICE_ID) { + tss_stopStreaming(_baseStation, NULL); + } +#endif +} + +void PrioVR::update() { +#ifdef HAVE_PRIOVR + for (int i = 0; i < MAX_SENSOR_NODES; i++) { + if (_sensorNodes[i] == TSS_NO_DEVICE_ID) { + continue; + } + glm::quat rotation; + if (!tss_getLastStreamData(_sensorNodes[i], (char*)&rotation, sizeof(glm::quat), NULL)) { + qDebug() << i << rotation.x << rotation.y << rotation.z << rotation.w; + } + } +#endif +} diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h new file mode 100644 index 0000000000..97fec2e1ee --- /dev/null +++ b/interface/src/devices/PrioVR.h @@ -0,0 +1,41 @@ +// +// PrioVR.h +// interface/src/devices +// +// Created by Andrzej Kapolka on 5/12/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_PrioVR_h +#define hifi_PrioVR_h + +#include + +#ifdef HAVE_PRIOVR +#include +#endif + +/// Handles interaction with the PrioVR skeleton tracking suit. +class PrioVR : public QObject { + Q_OBJECT + +public: + + PrioVR(); + virtual ~PrioVR(); + + void update(); + +private: +#ifdef HAVE_PRIOVR + TSS_Device_Id _baseStation; + + const int MAX_SENSOR_NODES = 20; + TSS_Device_Id _sensorNodes[MAX_SENSOR_NODES]; +#endif +}; + +#endif // hifi_PrioVR_h From 90d577a48c4050e3a85b34ea054b7aa7cb78c727 Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Tue, 13 May 2014 00:37:36 +0200 Subject: [PATCH 066/108] Stats area is now always right of mirror display, regardless if mirror display is enabled or not as there is always a remaining audio meter. Display, resize and click cases have been adapted by removing the condition for checking display status of mirror. --- interface/src/Application.cpp | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0b59fc3aa2..8858b6d324 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -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); From 8b15873488f7003dcb0c7e5ca36e2538474a895b Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 12 May 2014 16:37:52 -0600 Subject: [PATCH 067/108] Change to Oculus find path in Windows --- cmake/modules/FindLibOVR.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index c07b4c14c1..415eeeb1c5 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -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) From 415bc64ce9cd9090c4eae9d8e3d8c58bddc7e085 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 12 May 2014 16:44:15 -0600 Subject: [PATCH 068/108] Moving stuff around --- cmake/modules/FindLibOVR.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 415eeeb1c5..a85e1bec55 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -24,7 +24,7 @@ if (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) else (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) set(LIBOVR_SEARCH_DIRS "${LIBOVR_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/oculus") - find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_DIRS}) + find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include VS2010 HINTS ${LIBOVR_SEARCH_DIRS}) if (APPLE) find_library(LIBOVR_LIBRARIES "Lib/MacOS/Release/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) @@ -49,7 +49,7 @@ else (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) set(WINDOWS_LIBOVR_NAME "libovr.lib") endif() - find_library(LIBOVR_LIBRARIES "Lib/Win32/VS2010/${LIBOVR_NAME}" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARIES "Lib/Win32/${LIBOVR_NAME}" HINTS ${LIBOVR_SEARCH_DIRS}) endif () if (LIBOVR_INCLUDE_DIRS AND LIBOVR_LIBRARIES) From 1869248351b8c73e5c5a271a7d28f2978f34798a Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 12 May 2014 16:46:24 -0600 Subject: [PATCH 069/108] Moving stuff around --- cmake/modules/FindLibOVR.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index a85e1bec55..058dc0a228 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -24,7 +24,7 @@ if (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) else (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) set(LIBOVR_SEARCH_DIRS "${LIBOVR_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/oculus") - find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include VS2010 HINTS ${LIBOVR_SEARCH_DIRS}) + find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_DIRS}) if (APPLE) find_library(LIBOVR_LIBRARIES "Lib/MacOS/Release/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) @@ -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/${LIBOVR_NAME}" PATH_SUFFIXES VS2010 HINTS ${LIBOVR_SEARCH_DIRS}) endif () if (LIBOVR_INCLUDE_DIRS AND LIBOVR_LIBRARIES) From 37700c6ad3a331aac0bd3b8b4c2896512ba0b3e7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 May 2014 17:10:19 -0700 Subject: [PATCH 070/108] Fixed prompt size --- examples/editModels.js | 167 ++++++++------- examples/testScript.js | 0 examples/toolBars.js | 200 ++++++++++++++++++ .../scripting/WindowScriptingInterface.cpp | 1 + 4 files changed, 291 insertions(+), 77 deletions(-) delete mode 100644 examples/testScript.js create mode 100644 examples/toolBars.js diff --git a/examples/editModels.js b/examples/editModels.js index 9acbff4aa6..90ce87d259 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -9,7 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +Script.include("toolBars.js"); + var windowDimensions = Controller.getViewportDimensions(); +var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; +var toolHeight = 50; +var toolWidth = 50; var LASER_WIDTH = 4; var LASER_COLOR = { red: 255, green: 0, blue: 0 }; @@ -33,56 +38,7 @@ var modelURLs = [ "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/slimer.fbx", ]; -var tools = []; -var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; -var numberOfTools = 1; -var toolHeight = 50; -var toolWidth = 50; -var toolVerticalSpacing = 4; -var toolsHeight = toolHeight * numberOfTools + toolVerticalSpacing * (numberOfTools - 1); -var toolsX = windowDimensions.x - 8 - toolWidth; -var toolsY = (windowDimensions.y - toolsHeight) / 2; - - -var firstModel = Overlays.addOverlay("image", { - subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, - imageURL: toolIconUrl + "voxel-tool.svg", - x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }); -function Tool(iconURL) { - this.overlay = Overlays.addOverlay("image", { - subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, - imageURL: iconURL, - x: toolsX, - y: toolsY + ((toolHeight + toolVerticalSpacing) * tools.length), - width: toolWidth, - height: toolHeight, - visible: true, - alpha: 0.9 - }); - - - this.cleanup = function() { - Ovelays.deleteOverlay(this.overlay); - } - tools[tools.length] = this; - return tools.length - 1; -} -Tool.ICON_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; -Tool.HEIGHT = 50; -Tool.WIDTH = 50; - -function ToolBar(direction, x, y) { - this.tools = []; - - this.numberOfTools = function() { - return this.tools.length; - } -} -ToolBar.SPACING = 4; - +var toolBar; function controller(wichSide) { this.side = wichSide; @@ -238,6 +194,13 @@ function controller(wichSide) { }); } + this.hideLaser = function() { + Overlays.editOverlay(this.laser, { visible: false }); + Overlays.editOverlay(this.ball, { visible: false }); + Overlays.editOverlay(this.leftRight, { visible: false }); + Overlays.editOverlay(this.topDown, { visible: false }); + } + this.moveModel = function () { if (this.grabbing) { var newPosition = Vec3.sum(this.palmPosition, @@ -377,67 +340,117 @@ function moveModels() { rightController.moveModel(); } +var hydraConnected = false; function checkController(deltaTime) { var numberOfButtons = Controller.getNumberOfButtons(); var numberOfTriggers = Controller.getNumberOfTriggers(); var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; - - moveOverlays(); // this is expected for hydras - if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) { - //print("no hydra connected?"); - return; // bail if no hydra + if (numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2) { + if (!hydraConnected) { + hydraConnected = true; + } + + leftController.update(); + rightController.update(); + moveModels(); + } else { + if (hydraConnected) { + hydraConnected = false; + + leftController.hideLaser(); + rightController.hideLaser(); + } } - leftController.update(); - rightController.update(); - moveModels(); + moveOverlays(); } function moveOverlays() { - windowDimensions = Controller.getViewportDimensions(); - - toolsX = windowDimensions.x - 8 - toolWidth; - toolsY = (windowDimensions.y - toolsHeight) / 2; - - Overlays.editOverlay(firstModel, { - x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight, + if (typeof(toolBar) === 'undefined') { + toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); + // New Model + toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 }); + // Move YZ + toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Move XZ + toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Move XY + toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + } else if (windowDimensions.x == Controller.getViewportDimensions().x && + windowDimensions.y == Controller.getViewportDimensions().y) { + return; + } + + + windowDimensions = Controller.getViewportDimensions(); + var toolsX = windowDimensions.x - 8 - toolBar.width; + var toolsY = (windowDimensions.y - toolBar.height) / 2; + + toolBar.move(toolsX, toolsY); } function mousePressEvent(event) { var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); var url; - - if (clickedOverlay == firstModel) { + var index = toolBar.clicked(clickedOverlay); + if (index == 0) { url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]); if (url == null) { - return; } - } else { + return; + } + + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); + Models.addModel({ position: position, + radius: radiusDefault, + modelURL: url + }); + } else if (index == -1) { print("Didn't click on anything"); - return; } +} + +function mouseMoveEvent(event) { - var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); - Models.addModel({ position: position, - radius: radiusDefault, - modelURL: url - }); } function scriptEnding() { leftController.cleanup(); rightController.cleanup(); - - Overlays.deleteOverlay(firstModel); + toolBar.cleanup(); } Script.scriptEnding.connect(scriptEnding); // register the call back so it fires before each data send Script.update.connect(checkController); Controller.mousePressEvent.connect(mousePressEvent); +Controller.mousePressEvent.connect(mouseMoveEvent); diff --git a/examples/testScript.js b/examples/testScript.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/toolBars.js b/examples/toolBars.js new file mode 100644 index 0000000000..62a01a9a15 --- /dev/null +++ b/examples/toolBars.js @@ -0,0 +1,200 @@ +// +// testScript.js +// examples +// +// Created by ClĂ©ment Brisset on 5/7/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 +// + +Overlay2D = function(properties, overlay) { // overlay is an optionnal variable + if (!(typeof(properties) === 'undefined')) { + if(typeof(overlay) === 'undefined') { + overlay = Overlays.addOverlay("image", properties); + print("New overlay: " + overlay); + } else { + Overlays.editOverlay(overlay, properties); + } + } + + this.overlay = function() { + return overlay; + } + this.x = function() { + return properties.x; + } + this.y = function() { + return properties.y; + } + this.width = function() { + return properties.width; + } + this.height = function() { + return properties.height; + } + this.alpha = function() { + return properties.alpha; + } + this.visible = function() { + return properties.visible; + } + + + this.move = function(x, y) { + properties.x = x; + properties.y = y; + Overlays.editOverlay(overlay, { x: x, y: y }); + } + this.resize = function(width, height) { + properties.width = width; + properties.height = height; + Overlays.editOverlay(overlay, { width: width, height: height }); + } + this.setAlpha = function(alpha) { + properties.alpha = alpha; + Overlays.editOverlay(overlay, { alpha: alpha }); + } + this.show = function(doShow) { + properties.visible = doShow; + Overlays.editOverlay(overlay, { visible: doShow }); + } + + this.clicked = function(clickedOverlay) { + return (overlay == clickedOverlay ? true : false); + } + + this.cleanup = function() { + print("Cleanup"); + Overlays.deleteOverlay(overlay); + } +} + + +Tool = function(properties, selectable, selected) { // selectable and selected are optional variables. + Overlay2D.call(this, properties); + + if(typeof(selectable)==='undefined') { + selectable = false; + if(typeof(selected)==='undefined') { + selected = false; + + } + } + + this.selectable = function() { + return selectable; + } + + if (this.selectable()) { + this.selected = function() { + return selected; + } + this.select = function(doSelect) { + selected = doSelect; + properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; + Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); + } + this.toggle = function() { + selected = !selected; + properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; + Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); + + return selected; + } + + this.select(selected); + } + + this.baseClicked = this.clicked; + this.clicked = function(clickedOverlay) { + if (this.baseClicked(clickedOverlay)) { + if (selectable) { + this.toggle(); + } + return true; + } + return false; + } +} +Tool.prototype = new Overlay2D; +Tool.IMAGE_HEIGHT = 50; +Tool.IMAGE_WIDTH = 50; + +ToolBar = function(x, y, direction) { + this.tools = []; + this.x = x; + this.y = y; + this.width = 0; + this.height = 0; + + + this.addTool = function(properties, selectable, selected) { + if (direction == ToolBar.HORIZONTAL) { + properties.x = this.x + this.width; + properties.y = this.y; + this.width += properties.width + ToolBar.SPACING; + this.height += Math.max(properties.height, this.height); + } else { + properties.x = this.x; + properties.y = this.y + this.height; + this.width = Math.max(properties.width, this.width); + this.height += properties.height + ToolBar.SPACING; + } + + this.tools[this.tools.length] = new Tool(properties, selectable, selected); + return this.tools.length - 1; + } + + this.move = function(x, y) { + var dx = x - this.x; + var dy = y - this.y; + this.x = x; + this.y = y; + for(var tool in this.tools) { + this.tools[tool].move(this.tools[tool].x() + dx, this.tools[tool].y() + dy); + } + } + + this.setAlpha = function(alpha) { + for(var tool in this.tools) { + this.tools[tool].setAlpha(alpha); + } + } + + this.show = function(doShow) { + for(var tool in this.tools) { + this.tools[tool].show(doShow); + } + } + + this.clicked = function(clickedOverlay) { + for(var tool in this.tools) { + if (this.tools[tool].visible() && this.tools[tool].clicked(clickedOverlay)) { + return tool; + } + } + return -1; + } + + this.numberOfTools = function() { + return this.tools.length; + } + + this.cleanup = function() { + for(var tool in this.tools) { + this.tools[tool].cleanup(); + delete this.tools[tool]; + } + + this.tools = []; + this.x = x; + this.y = y; + this.width = 0; + this.height = 0; + } +} +ToolBar.SPACING = 4; +ToolBar.VERTICAL = 0; +ToolBar.HORIZONTAL = 1; \ No newline at end of file diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 144415d6ee..e10197d488 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -78,6 +78,7 @@ QScriptValue WindowScriptingInterface::showPrompt(const QString& message, const promptDialog.setWindowTitle(""); promptDialog.setLabelText(message); promptDialog.setTextValue(defaultText); + promptDialog.setFixedSize(600, 200); if (promptDialog.exec() == QDialog::Accepted) { return QScriptValue(promptDialog.textValue()); From 52a3c70bf589fffa319a9aec039c15a708e5f990 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 12 May 2014 17:11:02 -0700 Subject: [PATCH 071/108] Updates to PrioVR interface. --- cmake/modules/FindPrioVR.cmake | 4 +-- interface/src/devices/PrioVR.cpp | 62 +++++++++++++++----------------- interface/src/devices/PrioVR.h | 15 +++++--- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/cmake/modules/FindPrioVR.cmake b/cmake/modules/FindPrioVR.cmake index 96ee1a7bdc..6e78045321 100644 --- a/cmake/modules/FindPrioVR.cmake +++ b/cmake/modules/FindPrioVR.cmake @@ -16,10 +16,10 @@ if (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) # in cache already set(PRIOVR_FOUND TRUE) else (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) - find_path(PRIOVR_INCLUDE_DIRS yei_threespace_api.h ${PRIOVR_ROOT_DIR}/include) + find_path(PRIOVR_INCLUDE_DIRS yei_skeletal_api.h ${PRIOVR_ROOT_DIR}/include) if (WIN32) - find_library(PRIOVR_LIBRARIES ThreeSpace_API.lib ${PRIOVR_ROOT_DIR}) + find_library(PRIOVR_LIBRARIES Skeletal_API.lib ${PRIOVR_ROOT_DIR}/lib) endif (WIN32) if (PRIOVR_INCLUDE_DIRS AND PRIOVR_LIBRARIES) diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index cd28c6ab68..f9f807bdb7 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -9,52 +9,48 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "PrioVR.h" +const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000009, 0x0000000A, 0x0000000C, 0x0000000D, + 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; +const unsigned char AXIS_LIST[] = { 9, 43, 37, 37, 37, 13, 13, 13, 52, 52, 28, 28 }; +const int LIST_LENGTH = sizeof(SERIAL_LIST) / sizeof(SERIAL_LIST[0]); + PrioVR::PrioVR() { #ifdef HAVE_PRIOVR - TSS_ComPort comPort; - if (!tss_getComPorts(&comPort, 1, 0, PVR_FIND_BS)) { - _baseStation = TSS_NO_DEVICE_ID; - return; - } - _baseStation = tss_createTSDeviceStr(comPort.com_port, TSS_TIMESTAMP_SYSTEM); - if (_baseStation == TSS_NO_DEVICE_ID) { - return; - } - for (int i = 0; i < MAX_SENSOR_NODES; i++) { - tss_getSensorFromDongle(_baseStation, i, &_sensorNodes[i]); - if (_sensorNodes[i] == TSS_NO_DEVICE_ID) { - continue; - } - int present; - tss_isPresent(_sensorNodes[i], &present); - if (!present) { - _sensorNodes[i] = TSS_NO_DEVICE_ID; - } - } - tss_startStreaming(_baseStation, NULL); + _jointsDiscovered.resize(LIST_LENGTH); + _skeletalDevice = yei_setUpPrioVRSensors(0x00000000, const_cast(SERIAL_LIST), + const_cast(AXIS_LIST), _jointsDiscovered.data(), LIST_LENGTH, YEI_TIMESTAMP_SYSTEM); + if (!_skeletalDevice) { + return; + } + _jointRotations.resize(LIST_LENGTH); + yei_tareSensors(_skeletalDevice); #endif } PrioVR::~PrioVR() { #ifdef HAVE_PRIOVR - if (_baseStation != TSS_NO_DEVICE_ID) { - tss_stopStreaming(_baseStation, NULL); - } + if (_skeletalDevice) { + yei_stopStreaming(_skeletalDevice); + } #endif } void PrioVR::update() { #ifdef HAVE_PRIOVR - for (int i = 0; i < MAX_SENSOR_NODES; i++) { - if (_sensorNodes[i] == TSS_NO_DEVICE_ID) { - continue; - } - glm::quat rotation; - if (!tss_getLastStreamData(_sensorNodes[i], (char*)&rotation, sizeof(glm::quat), NULL)) { - qDebug() << i << rotation.x << rotation.y << rotation.z << rotation.w; - } - } + if (!_skeletalDevice) { + return; + } + unsigned int timestamp; + yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), + _jointRotations.size() * sizeof(glm::quat), ×tamp); + for (int i = 0; i < _jointsDiscovered.size(); i++) { + if (_jointsDiscovered.at(i)) { + qDebug() << i << _jointRotations.at(i).x << _jointRotations.at(i).y << _jointRotations.at(i).z << _jointRotations.at(i).w; + } + } #endif } diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h index 97fec2e1ee..d5bac44a9a 100644 --- a/interface/src/devices/PrioVR.h +++ b/interface/src/devices/PrioVR.h @@ -13,9 +13,14 @@ #define hifi_PrioVR_h #include +#include + +#include #ifdef HAVE_PRIOVR -#include +extern "C" { +#include +} #endif /// Handles interaction with the PrioVR skeleton tracking suit. @@ -31,11 +36,11 @@ public: private: #ifdef HAVE_PRIOVR - TSS_Device_Id _baseStation; - - const int MAX_SENSOR_NODES = 20; - TSS_Device_Id _sensorNodes[MAX_SENSOR_NODES]; + YEI_Device_Id _skeletalDevice; #endif + + QVector _jointsDiscovered; + QVector _jointRotations; }; #endif // hifi_PrioVR_h From 6c58bec550a04d71d1269dfd10540a0fd3bac19e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 12 May 2014 17:32:06 -0700 Subject: [PATCH 072/108] Untabbed. --- interface/src/devices/PrioVR.cpp | 36 ++++++++++++++++---------------- interface/src/devices/PrioVR.h | 4 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index f9f807bdb7..9b5427be07 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -14,20 +14,20 @@ #include "PrioVR.h" const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000009, 0x0000000A, 0x0000000C, 0x0000000D, - 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; + 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; const unsigned char AXIS_LIST[] = { 9, 43, 37, 37, 37, 13, 13, 13, 52, 52, 28, 28 }; const int LIST_LENGTH = sizeof(SERIAL_LIST) / sizeof(SERIAL_LIST[0]); PrioVR::PrioVR() { #ifdef HAVE_PRIOVR - _jointsDiscovered.resize(LIST_LENGTH); + _jointsDiscovered.resize(LIST_LENGTH); _skeletalDevice = yei_setUpPrioVRSensors(0x00000000, const_cast(SERIAL_LIST), - const_cast(AXIS_LIST), _jointsDiscovered.data(), LIST_LENGTH, YEI_TIMESTAMP_SYSTEM); - if (!_skeletalDevice) { - return; - } - _jointRotations.resize(LIST_LENGTH); - yei_tareSensors(_skeletalDevice); + const_cast(AXIS_LIST), _jointsDiscovered.data(), LIST_LENGTH, YEI_TIMESTAMP_SYSTEM); + if (!_skeletalDevice) { + return; + } + _jointRotations.resize(LIST_LENGTH); + yei_tareSensors(_skeletalDevice); #endif } @@ -42,15 +42,15 @@ PrioVR::~PrioVR() { void PrioVR::update() { #ifdef HAVE_PRIOVR if (!_skeletalDevice) { - return; - } - unsigned int timestamp; - yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), - _jointRotations.size() * sizeof(glm::quat), ×tamp); - for (int i = 0; i < _jointsDiscovered.size(); i++) { - if (_jointsDiscovered.at(i)) { - qDebug() << i << _jointRotations.at(i).x << _jointRotations.at(i).y << _jointRotations.at(i).z << _jointRotations.at(i).w; - } - } + return; + } + unsigned int timestamp; + yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), + _jointRotations.size() * sizeof(glm::quat), ×tamp); + for (int i = 0; i < _jointsDiscovered.size(); i++) { + if (_jointsDiscovered.at(i)) { + qDebug() << i << _jointRotations.at(i).x << _jointRotations.at(i).y << _jointRotations.at(i).z << _jointRotations.at(i).w; + } + } #endif } diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h index d5bac44a9a..1c26009b33 100644 --- a/interface/src/devices/PrioVR.h +++ b/interface/src/devices/PrioVR.h @@ -39,8 +39,8 @@ private: YEI_Device_Id _skeletalDevice; #endif - QVector _jointsDiscovered; - QVector _jointRotations; + QVector _jointsDiscovered; + QVector _jointRotations; }; #endif // hifi_PrioVR_h From 4a0b4de1787fddccc1ded9545a33bc1bf051af24 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 12 May 2014 17:55:07 -0700 Subject: [PATCH 073/108] Better instructions. --- interface/external/priovr/readme.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/interface/external/priovr/readme.txt b/interface/external/priovr/readme.txt index 92a2aff711..202a90cf12 100644 --- a/interface/external/priovr/readme.txt +++ b/interface/external/priovr/readme.txt @@ -2,10 +2,15 @@ Instructions for adding the PrioVR driver to Interface Andrzej Kapolka, May 12, 2014 -1. Copy the PrioVR sdk folders (include, *.lib) into the interface/external/priovr folder. - This readme.txt should be there as well. +1. Download and install the YEI drivers from https://www.yeitechnology.com/yei-3-space-sensor-software-suite. If using + Window 8+, follow the workaround instructions at http://forum.yeitechnology.com/viewtopic.php?f=3&t=24. + +2. Get the PrioVR skeleton API, open ts_c_api2_priovr2/visual_studio/ThreeSpace_API_2/ThreeSpace_API_2.sln + in Visual Studio, and build it. -2. Copy the PrioVR DLLs from the API into your path. - -3. Delete your build directory, run cmake and build, and you should be all set. +3. Copy ts_c_api2_priovr2/visual_studio/ThreeSpace_API_2/Skeletal_API/yei_skeletal_api.h to interface/external/priovr/include, + ts_c_api2_priovr2/visual_studio/ThreeSpace_API_2/Debug/Skeletal_API.lib to interface/external/priovr/lib, and + ts_c_api2_priovr2/visual_studio/ThreeSpace_API_2/Debug/*.dll to your path. + +4. Delete your build directory, run cmake and build, and you should be all set. From c7e103e98992e24d6faf3a7ed3e3bcac10b9ecc9 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 12 May 2014 19:43:56 -0600 Subject: [PATCH 074/108] Not working with path_suffixes, testing something else --- cmake/modules/FindLibOVR.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 058dc0a228..415eeeb1c5 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -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}" PATH_SUFFIXES VS2010 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) From 55d96147578860f1898832be0549109f9fa3c374 Mon Sep 17 00:00:00 2001 From: Bennett Goble Date: Tue, 13 May 2014 00:58:49 -0400 Subject: [PATCH 075/108] Menu.cpp Typo: goToOrientation calling goToDestination. --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e1d0c6a574..2daf5b0240 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -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) { From a56567a3693aa5018d5f27c306a3a285389abe7b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 13 May 2014 09:27:31 -0700 Subject: [PATCH 076/108] add support for adjustable FPS --- examples/placeModelsWithHands.js | 2 +- libraries/models/src/ModelItem.cpp | 228 ++++++++++++------ libraries/models/src/ModelItem.h | 37 +-- .../models/src/ModelsScriptingInterface.h | 5 +- 4 files changed, 176 insertions(+), 96 deletions(-) diff --git a/examples/placeModelsWithHands.js b/examples/placeModelsWithHands.js index bd0915da54..47b4615924 100644 --- a/examples/placeModelsWithHands.js +++ b/examples/placeModelsWithHands.js @@ -231,7 +231,7 @@ function checkControllerSide(whichSide) { if (animationURLs[currentModelURL] !== "") { properties.animationURL = animationURLs[currentModelURL]; - properties.isAnimationPlaying = true; + properties.animationIsPlaying = true; } debugPrint("modelRadius=" +modelRadius); diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index f9b1092c84..474a6a38a4 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -88,8 +88,9 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties& // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; - _isAnimationPlaying = false; - _frameIndex = 0.0f; + _animationIsPlaying = false; + _animationFrameIndex = 0.0f; + _animationFPS = MODEL_DEFAULT_ANIMATION_FPS; _jointMappingCompleted = false; _lastAnimated = now; @@ -121,8 +122,9 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; - _isAnimationPlaying = false; - _frameIndex = 0.0f; + _animationIsPlaying = false; + _animationFrameIndex = 0.0f; + _animationFPS = MODEL_DEFAULT_ANIMATION_FPS; _jointMappingCompleted = false; _lastAnimated = now; } @@ -175,14 +177,19 @@ bool ModelItem::appendModelData(OctreePacketData* packetData) const { } } - // isAnimationPlaying + // animationIsPlaying if (success) { - success = packetData->appendValue(getIsAnimationPlaying()); + success = packetData->appendValue(getAnimationIsPlaying()); } - // frameIndex + // animationFrameIndex if (success) { - success = packetData->appendValue(getFrameIndex()); + success = packetData->appendValue(getAnimationFrameIndex()); + } + + // animationFPS + if (success) { + success = packetData->appendValue(getAnimationFPS()); } return success; @@ -273,17 +280,24 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); - // isAnimationPlaying - memcpy(&_isAnimationPlaying, dataAt, sizeof(_isAnimationPlaying)); - dataAt += sizeof(_isAnimationPlaying); - bytesRead += sizeof(_isAnimationPlaying); + // animationIsPlaying + memcpy(&_animationIsPlaying, dataAt, sizeof(_animationIsPlaying)); + dataAt += sizeof(_animationIsPlaying); + bytesRead += sizeof(_animationIsPlaying); - // frameIndex - memcpy(&_frameIndex, dataAt, sizeof(_frameIndex)); - dataAt += sizeof(_frameIndex); - bytesRead += sizeof(_frameIndex); + // animationFrameIndex + memcpy(&_animationFrameIndex, dataAt, sizeof(_animationFrameIndex)); + dataAt += sizeof(_animationFrameIndex); + bytesRead += sizeof(_animationFrameIndex); - qDebug() << "readModelDataFromBuffer()... _frameIndex=" << _frameIndex; + qDebug() << "readModelDataFromBuffer()... _animationFrameIndex=" << _animationFrameIndex; + + // animationFPS + memcpy(&_animationFPS, dataAt, sizeof(_animationFPS)); + dataAt += sizeof(_animationFPS); + bytesRead += sizeof(_animationFPS); + + qDebug() << "readModelDataFromBuffer()... _animationFPS=" << _animationFPS; } else { qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; @@ -424,22 +438,31 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); } - // isAnimationPlaying + // animationIsPlaying if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { - memcpy(&newModelItem._isAnimationPlaying, dataAt, sizeof(newModelItem._isAnimationPlaying)); - dataAt += sizeof(newModelItem._isAnimationPlaying); - processedBytes += sizeof(newModelItem._isAnimationPlaying); + memcpy(&newModelItem._animationIsPlaying, dataAt, sizeof(newModelItem._animationIsPlaying)); + dataAt += sizeof(newModelItem._animationIsPlaying); + processedBytes += sizeof(newModelItem._animationIsPlaying); } - // frameIndex + // animationFrameIndex if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { - memcpy(&newModelItem._frameIndex, dataAt, sizeof(newModelItem._frameIndex)); - dataAt += sizeof(newModelItem._frameIndex); - processedBytes += sizeof(newModelItem._frameIndex); + memcpy(&newModelItem._animationFrameIndex, dataAt, sizeof(newModelItem._animationFrameIndex)); + dataAt += sizeof(newModelItem._animationFrameIndex); + processedBytes += sizeof(newModelItem._animationFrameIndex); + } + + // animationFPS + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FPS) == MODEL_PACKET_CONTAINS_ANIMATION_FPS)) { + + memcpy(&newModelItem._animationFPS, dataAt, sizeof(newModelItem._animationFPS)); + dataAt += sizeof(newModelItem._animationFPS); + processedBytes += sizeof(newModelItem._animationFPS); } const bool wantDebugging = false; @@ -587,29 +610,41 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable } - // isAnimationPlaying + // animationIsPlaying if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { - bool isAnimationPlaying = properties.getIsAnimationPlaying(); - memcpy(copyAt, &isAnimationPlaying, sizeof(isAnimationPlaying)); - copyAt += sizeof(isAnimationPlaying); - sizeOut += sizeof(isAnimationPlaying); + bool animationIsPlaying = properties.getAnimationIsPlaying(); + memcpy(copyAt, &animationIsPlaying, sizeof(animationIsPlaying)); + copyAt += sizeof(animationIsPlaying); + sizeOut += sizeof(animationIsPlaying); -qDebug() << "encodeModelItemEditMessageDetails()... isAnimationPlaying=" << isAnimationPlaying; +qDebug() << "encodeModelItemEditMessageDetails()... animationIsPlaying=" << animationIsPlaying; } - // frameIndex + // animationFrameIndex if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { - float frameIndex = properties.getFrameIndex(); - memcpy(copyAt, &frameIndex, sizeof(frameIndex)); - copyAt += sizeof(frameIndex); - sizeOut += sizeof(frameIndex); + float animationFrameIndex = properties.getAnimationFrameIndex(); + memcpy(copyAt, &animationFrameIndex, sizeof(animationFrameIndex)); + copyAt += sizeof(animationFrameIndex); + sizeOut += sizeof(animationFrameIndex); -qDebug() << "encodeModelItemEditMessageDetails()... frameIndex=" << frameIndex; +qDebug() << "encodeModelItemEditMessageDetails()... animationFrameIndex=" << animationFrameIndex; + } + + // animationFPS + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FPS) == MODEL_PACKET_CONTAINS_ANIMATION_FPS)) { + + float animationFPS = properties.getAnimationFPS(); + memcpy(copyAt, &animationFPS, sizeof(animationFPS)); + copyAt += sizeof(animationFPS); + sizeOut += sizeof(animationFPS); + +qDebug() << "encodeModelItemEditMessageDetails()... animationFPS=" << animationFPS; } bool wantDebugging = false; @@ -699,8 +734,8 @@ QVector ModelItem::getAnimationFrame() { if (hasAnimation() && _jointMappingCompleted) { Animation* myAnimation = getAnimation(_animationURL); QVector frames = myAnimation->getFrames(); - int frameIndex = (int)std::floor(_frameIndex) % frames.size(); - QVector rotations = frames[frameIndex].rotations; + int animationFrameIndex = (int)std::floor(_animationFrameIndex) % frames.size(); + QVector rotations = frames[animationFrameIndex].rotations; frameData.resize(_jointMapping.size()); for (int j = 0; j < _jointMapping.size(); j++) { int rotationIndex = _jointMapping[j]; @@ -716,23 +751,29 @@ void ModelItem::update(const quint64& updateTime) { _lastUpdated = updateTime; setShouldDie(getShouldDie()); -//qDebug() << "ModelItem::update() now=" << now; + quint64 now = usecTimestampNow(); // only advance the frame index if we're playing - if (getIsAnimationPlaying()) { + if (getAnimationIsPlaying()) { - quint64 now = usecTimestampNow(); float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; -qDebug() << "ModelItem::update() now=" << now; -qDebug() << " updateTime=" << updateTime; -qDebug() << " _lastAnimated=" << _lastAnimated; -qDebug() << " deltaTime=" << deltaTime; - + + const bool wantDebugging = false; + if (wantDebugging) { + qDebug() << "ModelItem::update() now=" << now; + qDebug() << " updateTime=" << updateTime; + qDebug() << " _lastAnimated=" << _lastAnimated; + qDebug() << " deltaTime=" << deltaTime; + } _lastAnimated = now; - const float FRAME_RATE = 10.0f; - _frameIndex += deltaTime * FRAME_RATE; -qDebug() << " _frameIndex=" << _frameIndex; + _animationFrameIndex += deltaTime * _animationFPS; + if (wantDebugging) { + qDebug() << " _animationFrameIndex=" << _animationFrameIndex; + } + + } else { + _lastAnimated = now; } } @@ -758,8 +799,9 @@ ModelItemProperties::ModelItemProperties() : _modelURL(""), _modelRotation(MODEL_DEFAULT_MODEL_ROTATION), _animationURL(""), - _isAnimationPlaying(false), - _frameIndex(0.0), + _animationIsPlaying(false), + _animationFrameIndex(0.0), + _animationFPS(MODEL_DEFAULT_ANIMATION_FPS), _id(UNKNOWN_MODEL_ID), _idSet(false), @@ -772,8 +814,9 @@ ModelItemProperties::ModelItemProperties() : _modelURLChanged(false), _modelRotationChanged(false), _animationURLChanged(false), - _isAnimationPlayingChanged(false), - _frameIndexChanged(false), + _animationIsPlayingChanged(false), + _animationFrameIndexChanged(false), + _animationFPSChanged(false), _defaultSettings(true) { } @@ -809,14 +852,18 @@ uint16_t ModelItemProperties::getChangedBits() const { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_URL; } - if (_isAnimationPlayingChanged) { + if (_animationIsPlayingChanged) { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_PLAYING; } - if (_frameIndexChanged) { + if (_animationFrameIndexChanged) { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_FRAME; } + if (_animationFPSChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_FPS; + } + return changedBits; } @@ -840,8 +887,9 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const properties.setProperty("modelRotation", modelRotation); properties.setProperty("animationURL", _animationURL); - properties.setProperty("isAnimationPlaying", _isAnimationPlaying); - properties.setProperty("frameIndex", _frameIndex); + properties.setProperty("animationIsPlaying", _animationIsPlaying); + properties.setProperty("animationFrameIndex", _animationFrameIndex); + properties.setProperty("animationFPS", _animationFPS); if (_idSet) { properties.setProperty("id", _id); @@ -948,23 +996,34 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { } } - QScriptValue isAnimationPlaying = object.property("isAnimationPlaying"); - if (isAnimationPlaying.isValid()) { + QScriptValue animationIsPlaying = object.property("animationIsPlaying"); + if (animationIsPlaying.isValid()) { bool newIsAnimationPlaying; - newIsAnimationPlaying = isAnimationPlaying.toVariant().toBool(); - if (_defaultSettings || newIsAnimationPlaying != _isAnimationPlaying) { - _isAnimationPlaying = newIsAnimationPlaying; - _isAnimationPlayingChanged = true; + newIsAnimationPlaying = animationIsPlaying.toVariant().toBool(); + if (_defaultSettings || newIsAnimationPlaying != _animationIsPlaying) { + _animationIsPlaying = newIsAnimationPlaying; + _animationIsPlayingChanged = true; } } - QScriptValue frameIndex = object.property("frameIndex"); - if (frameIndex.isValid()) { + QScriptValue animationFrameIndex = object.property("animationFrameIndex"); + if (animationFrameIndex.isValid()) { float newFrameIndex; - newFrameIndex = frameIndex.toVariant().toFloat(); - if (_defaultSettings || newFrameIndex != _frameIndex) { - _frameIndex = newFrameIndex; - _frameIndexChanged = true; + newFrameIndex = animationFrameIndex.toVariant().toFloat(); + if (_defaultSettings || newFrameIndex != _animationFrameIndex) { + _animationFrameIndex = newFrameIndex; + _animationFrameIndexChanged = true; + } + } + + QScriptValue animationFPS = object.property("animationFPS"); + if (animationFPS.isValid()) { + float newFPS; + newFPS = animationFPS.toVariant().toFloat(); + if (_defaultSettings || newFPS != _animationFPS) { + _animationFPS = newFPS; + _animationFPSChanged = true; +qDebug() << "ModelItemProperties::copyFromScriptValue()... _animationFPS=" << _animationFPS; } } @@ -1010,18 +1069,25 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; } - if (_isAnimationPlayingChanged) { - modelItem.setIsAnimationPlaying(_isAnimationPlaying); + if (_animationIsPlayingChanged) { + modelItem.setAnimationIsPlaying(_animationIsPlaying); somethingChanged = true; -qDebug() << "ModelItemProperties::copyToModelItem()... _isAnimationPlaying=" << _isAnimationPlaying; +qDebug() << "ModelItemProperties::copyToModelItem()... _animationIsPlaying=" << _animationIsPlaying; } - if (_frameIndexChanged) { - modelItem.setFrameIndex(_frameIndex); + if (_animationFrameIndexChanged) { + modelItem.setAnimationFrameIndex(_animationFrameIndex); somethingChanged = true; -qDebug() << "ModelItemProperties::copyToModelItem()... _frameIndex=" << _frameIndex; +qDebug() << "ModelItemProperties::copyToModelItem()... _animationFrameIndex=" << _animationFrameIndex; + } + + if (_animationFPSChanged) { + modelItem.setAnimationFPS(_animationFPS); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... _animationFPS=" << _animationFPS; } if (somethingChanged) { @@ -1044,8 +1110,9 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURL = modelItem.getModelURL(); _modelRotation = modelItem.getModelRotation(); _animationURL = modelItem.getAnimationURL(); - _isAnimationPlaying = modelItem.getIsAnimationPlaying(); - _frameIndex = modelItem.getFrameIndex(); + _animationIsPlaying = modelItem.getAnimationIsPlaying(); + _animationFrameIndex = modelItem.getAnimationFrameIndex(); + _animationFPS = modelItem.getAnimationFPS(); _id = modelItem.getID(); _idSet = true; @@ -1058,8 +1125,9 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURLChanged = false; _modelRotationChanged = false; _animationURLChanged = false; - _isAnimationPlayingChanged = false; - _frameIndexChanged = false; + _animationIsPlayingChanged = false; + _animationFrameIndexChanged = false; + _animationFPSChanged = false; _defaultSettings = false; } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index a6c317d122..18074fbe13 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -46,12 +46,14 @@ const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 32; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_URL = 64; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_PLAYING = 128; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_FRAME = 256; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_FPS = 512; const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container const QString MODEL_DEFAULT_MODEL_URL(""); const glm::quat MODEL_DEFAULT_MODEL_ROTATION; const QString MODEL_DEFAULT_ANIMATION_URL(""); +const float MODEL_DEFAULT_ANIMATION_FPS = 30.0f; const PacketVersion VERSION_MODELS_HAVE_ANIMATION = 1; @@ -77,8 +79,9 @@ public: const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } const QString& getAnimationURL() const { return _animationURL; } - float getFrameIndex() const { return _frameIndex; } - bool getIsAnimationPlaying() const { return _isAnimationPlaying; } + float getAnimationFrameIndex() const { return _animationFrameIndex; } + bool getAnimationIsPlaying() const { return _animationIsPlaying; } + float getAnimationFPS() const { return _animationFPS; } quint64 getLastEdited() const { return _lastEdited; } uint16_t getChangedBits() const; @@ -93,8 +96,9 @@ public: void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; } void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; } - void setFrameIndex(float value) { _frameIndex = value; _frameIndexChanged = true; } - void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; _isAnimationPlayingChanged = true; } + void setAnimationFrameIndex(float value) { _animationFrameIndex = value; _animationFrameIndexChanged = true; } + void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; } + void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; } /// used by ModelScriptingInterface to return ModelItemProperties for unknown models void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; } @@ -111,8 +115,9 @@ private: QString _modelURL; glm::quat _modelRotation; QString _animationURL; - bool _isAnimationPlaying; - float _frameIndex; + bool _animationIsPlaying; + float _animationFrameIndex; + float _animationFPS; uint32_t _id; bool _idSet; @@ -126,8 +131,9 @@ private: bool _modelURLChanged; bool _modelRotationChanged; bool _animationURLChanged; - bool _isAnimationPlayingChanged; - bool _frameIndexChanged; + bool _animationIsPlayingChanged; + bool _animationFrameIndexChanged; + bool _animationFPSChanged; bool _defaultSettings; }; Q_DECLARE_METATYPE(ModelItemProperties); @@ -238,8 +244,9 @@ public: void setModelURL(const QString& url) { _modelURL = url; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; } void setAnimationURL(const QString& url) { _animationURL = url; } - void setFrameIndex(float value) { _frameIndex = value; } - void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; } + void setAnimationFrameIndex(float value) { _animationFrameIndex = value; } + void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; } + void setAnimationFPS(float value) { _animationFPS = value; } void setProperties(const ModelItemProperties& properties); @@ -268,8 +275,9 @@ public: QVector getAnimationFrame(); bool jointsMapped() const { return _jointMappingCompleted; } - bool getIsAnimationPlaying() const { return _isAnimationPlaying; } - float getFrameIndex() const { return _frameIndex; } + bool getAnimationIsPlaying() const { return _animationIsPlaying; } + float getAnimationFrameIndex() const { return _animationFrameIndex; } + float getAnimationFPS() const { return _animationFPS; } protected: glm::vec3 _position; @@ -291,8 +299,9 @@ protected: quint64 _lastAnimated; QString _animationURL; - float _frameIndex; // we keep this as a float and round to int only when we need the exact index - bool _isAnimationPlaying; + float _animationFrameIndex; // we keep this as a float and round to int only when we need the exact index + bool _animationIsPlaying; + float _animationFPS; bool _jointMappingCompleted; QVector _jointMapping; diff --git a/libraries/models/src/ModelsScriptingInterface.h b/libraries/models/src/ModelsScriptingInterface.h index f08ec715a9..8447e5018d 100644 --- a/libraries/models/src/ModelsScriptingInterface.h +++ b/libraries/models/src/ModelsScriptingInterface.h @@ -65,7 +65,10 @@ public slots: /// plays the model animation. ModelItemID playModelAnimation(ModelItemID modelID); - + */ + + + /* /// gets the current frame of the model animation. float getModelAnimationFrame(ModelItemID modelID); From debd76b2cbb41b1ffef4000d7c13945314baf0d5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 May 2014 10:38:43 -0700 Subject: [PATCH 077/108] use sheet window modality so OAuth window doesn't take over full screen --- interface/src/ui/OAuthWebViewHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index b3b914bd64..83d900cd5c 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -59,7 +59,7 @@ void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authoriz _activeWebView = new QWebView; // keep the window on top and delete it when it closes - _activeWebView->setWindowFlags(Qt::WindowStaysOnTopHint); + _activeWebView->setWindowFlags(Qt::Sheet | Qt::WindowStaysOnTopHint); _activeWebView->setAttribute(Qt::WA_DeleteOnClose); qDebug() << "Displaying QWebView for OAuth authorization at" << authorizationURL.toString(); From 607094075ce050d3f504fcdfa296254e9e4fea3c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 13 May 2014 10:38:56 -0700 Subject: [PATCH 078/108] Revert "Merge pull request #2834 from andrew/thermonuclear" This reverts commit e088c3d1150aaee0401adc5e1fbe0bb4d9c9d6cc, reversing changes made to d124c0319d9aaefd9eb223db8db41f492a60ad0b. --- interface/src/BuckyBalls.cpp | 8 +- interface/src/avatar/Avatar.cpp | 16 ++- interface/src/avatar/Hand.cpp | 64 ++++++++-- interface/src/avatar/SkeletonModel.cpp | 30 +++-- interface/src/devices/SixenseManager.cpp | 45 ++++--- .../ControllerScriptingInterface.cpp | 4 +- libraries/avatars/src/HandData.cpp | 110 +++++++++++++++--- libraries/avatars/src/HandData.h | 87 +++++++++++--- libraries/shared/src/SharedUtil.h | 1 - 9 files changed, 288 insertions(+), 77 deletions(-) diff --git a/interface/src/BuckyBalls.cpp b/interface/src/BuckyBalls.cpp index 68d1167071..e1ec41dca1 100644 --- a/interface/src/BuckyBalls.cpp +++ b/interface/src/BuckyBalls.cpp @@ -60,13 +60,15 @@ BuckyBalls::BuckyBalls() { void BuckyBalls::grab(PalmData& palm, float deltaTime) { float penetration; - glm::vec3 fingerTipPosition = palm.getFingerTipPosition(); + glm::vec3 diff; + FingerData& finger = palm.getFingers()[0]; // Sixense has only one finger + glm::vec3 fingerTipPosition = finger.getTipPosition(); if (palm.getControllerButtons() & BUTTON_FWD) { if (!_bballIsGrabbed[palm.getSixenseID()]) { // Look for a ball to grab for (int i = 0; i < NUM_BBALLS; i++) { - glm::vec3 diff = _bballPosition[i] - fingerTipPosition; + diff = _bballPosition[i] - fingerTipPosition; penetration = glm::length(diff) - (_bballRadius[i] + COLLISION_RADIUS); if (penetration < 0.f) { _bballIsGrabbed[palm.getSixenseID()] = i; @@ -75,7 +77,7 @@ void BuckyBalls::grab(PalmData& palm, float deltaTime) { } if (_bballIsGrabbed[palm.getSixenseID()]) { // If ball being grabbed, move with finger - glm::vec3 diff = _bballPosition[_bballIsGrabbed[palm.getSixenseID()]] - fingerTipPosition; + 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(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 2bd0bbbc6d..41dc50b1fa 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -609,6 +609,18 @@ 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) { @@ -619,10 +631,8 @@ 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->getPalmDirection(); + glm::vec3 diskNormal = palm->getNormal(); const float DISK_THICKNESS = 0.08f; // collide against the disk diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 35132af929..4774e23cb2 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -176,7 +176,8 @@ void Hand::renderHandTargets(bool isMine) { if (!palm.isActive()) { continue; } - glm::vec3 targetPosition = palm.getFingerTipPosition(); + glm::vec3 targetPosition; + palm.getBallHoldPosition(targetPosition); glPushMatrix(); glTranslatef(targetPosition.x, targetPosition.y, targetPosition.z); @@ -196,20 +197,59 @@ void Hand::renderHandTargets(bool isMine) { for (size_t i = 0; i < getNumPalms(); ++i) { PalmData& palm = getPalms()[i]; if (palm.isActive()) { - 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(); + 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(); + + } + } } } + /* + // 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); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index a2e637f4e7..8c21a3240f 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -33,7 +33,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { return; // only simulate for own avatar } - // find the left and rightmost active palms + // find the left and rightmost active Leap 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) { - // palms are not yet set, use mouse + // no Leap data; set hands from mouse if (_owningAvatar->getHandState() == HAND_STATE_NULL) { restoreRightHandPosition(HAND_RESTORATION_RATE); } else { @@ -159,13 +159,29 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin } else { getJointRotation(jointIndex, palmRotation, true); } - palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palm.getPalmDirection()) * palmRotation; + palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()) * palmRotation; + // sort the finger indices by raw x, get the average direction + QVector 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 - // 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; + 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; + } // set hand position, rotation if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) { diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 1698cebcf9..0435519124 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -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 = 0.25f; // meters -const float NECK_Y = 0.3f; // meters -const float NECK_Z = 0.3f; // meters +const float NECK_X = 250.f; // millimeters +const float NECK_Y = 300.f; // millimeters +const float NECK_Z = 300.f; // millimeters #endif SixenseManager::SixenseManager() { @@ -106,11 +106,8 @@ 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; + glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); // 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. @@ -120,12 +117,15 @@ 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; + rawVelocity = (position - palm->getRawPosition()) / deltaTime / 1000.f; } else { rawVelocity = glm::vec3(0.0f); } @@ -140,17 +140,29 @@ void SixenseManager::update(float deltaTime) { _amountMoved = glm::vec3(0.0f); } - // Store the one fingertip in the palm structure so we can track velocity - const float FINGER_LENGTH = 0.3f; // meters + // initialize the "finger" based on the direction + FingerData finger(palm, hand); + finger.setActive(true); + finger.setRawRootPosition(position); + const float FINGER_LENGTH = 300.0f; // Millimeters 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); + palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime / 1000.f); } 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) { @@ -159,7 +171,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 * USECS_PER_SECOND)) { + if (usecTimestampNow() - _lastMovement > (MOVEMENT_DISABLE_SECONDS * 1000 * 1000)) { for (std::vector::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) { it->setActive(false); } @@ -176,8 +188,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 = 0.3f; // meters -const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters +const float MINIMUM_ARM_REACH = 300.f; // millimeters +const float MAXIMUM_NOISE_LEVEL = 50.f; // millimeters const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired void SixenseManager::updateCalibration(const sixenseControllerData* controllers) { @@ -217,17 +229,14 @@ 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.0f * MINIMUM_ARM_REACH) { + if (reach > 2.f * MINIMUM_ARM_REACH) { qDebug("started: sixense calibration"); _averageLeft = positionLeft; _averageRight = positionRight; diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 5e58ac66ea..aa14f769de 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -198,9 +198,9 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex if (palmData) { switch (controlOfPalm) { case PALM_SPATIALCONTROL: - return palmData->getPalmDirection(); + return palmData->getNormal(); case TIP_SPATIALCONTROL: - return palmData->getFingerDirection(); + return palmData->getNormal(); // currently the tip doesn't have a unique normal, use the palm normal } } return glm::vec3(0); // bad index diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index bd366f020a..c2e3b51cb3 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -26,8 +26,8 @@ HandData::HandData(AvatarData* owningAvatar) : addNewPalm(); } -glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const { - return glm::inverse(getBaseOrientation()) * worldVector; +glm::vec3 HandData::worldVectorToLeapVector(const glm::vec3& worldVector) const { + return glm::inverse(getBaseOrientation()) * worldVector / LEAP_UNIT_SCALE; } PalmData& HandData::addNewPalm() { @@ -66,21 +66,69 @@ 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) { +_collisionlessPaddleExpiry(0) +{ + for (int i = 0; i < NUM_FINGERS_PER_HAND; ++i) { + _fingers.push_back(FingerData(this, owningHandData)); + } } void PalmData::addToPosition(const glm::vec3& delta) { - _rawPosition += _owningHandData->worldToLocalVector(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(); + } + } } bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, @@ -109,20 +157,54 @@ glm::vec3 HandData::getBasePosition() const { return _owningAvatarData->getPosition(); } -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::setTrailLength(unsigned int length) { + _tipTrailPositions.resize(length); + _tipTrailCurrentStartIndex = 0; + _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); +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::getPalmDirection() const { - const glm::vec3 LOCAL_PALM_DIRECTION(0.0f, -1.0f, 0.0f); - return _owningHandData->localToWorldDirection(_rawRotation * LOCAL_PALM_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(); + } } diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 1f2d134c43..a37e3a5814 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -21,6 +21,7 @@ #include "SharedUtil.h" class AvatarData; +class FingerData; class PalmData; const int NUM_HANDS = 2; @@ -30,6 +31,8 @@ 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; @@ -38,16 +41,17 @@ 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 localToWorldPosition(const glm::vec3& localPosition) { - return getBasePosition() + getBaseOrientation() * localPosition; + glm::vec3 leapPositionToWorldPosition(const glm::vec3& leapPosition) { + return getBasePosition() + getBaseOrientation() * (leapPosition * LEAP_UNIT_SCALE); } - - glm::vec3 localToWorldDirection(const glm::vec3& localVector) { - return getBaseOrientation() * localVector; - } - - glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const; + glm::vec3 leapDirectionToWorldDirection(const glm::vec3& leapDirection) { + return getBaseOrientation() * leapDirection; + } + glm::vec3 worldVectorToLeapVector(const glm::vec3& worldVector) const; std::vector& getPalms() { return _palms; } const std::vector& getPalms() const { return _palms; } @@ -59,6 +63,9 @@ 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 @@ -82,23 +89,71 @@ 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 _tipTrailPositions; + int _tipTrailCurrentStartIndex; + int _tipTrailCurrentValidLength; + PalmData* _owningPalmData; + HandData* _owningHandData; +}; class PalmData { public: PalmData(HandData* owningHandData); - glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); } - glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); } + glm::vec3 getPosition() const { return _owningHandData->leapPositionToWorldPosition(_rawPosition); } + glm::vec3 getNormal() const { return _owningHandData->leapDirectionToWorldDirection(_rawNormal); } + glm::vec3 getVelocity() const { return _owningHandData->leapDirectionToWorldDirection(_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& getFingers() { return _fingers; } + const std::vector& 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); @@ -107,11 +162,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->localToWorldPosition(_tipPosition); } + const glm::vec3 getTipPosition() const { return _owningHandData->leapPositionToWorldPosition(_tipPosition); } const glm::vec3& getTipRawPosition() const { return _tipPosition; } void setTipVelocity(const glm::vec3& velocity) { _tipVelocity = velocity; } - const glm::vec3 getTipVelocity() const { return _owningHandData->localToWorldDirection(_tipVelocity); } + const glm::vec3 getTipVelocity() const { return _owningHandData->leapDirectionToWorldDirection(_tipVelocity); } const glm::vec3& getTipRawVelocity() const { return _tipVelocity; } void incrementFramesWithoutData() { _numFramesWithoutData++; } @@ -143,14 +198,11 @@ 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 _fingers; glm::quat _rawRotation; glm::vec3 _rawPosition; + glm::vec3 _rawNormal; glm::vec3 _rawVelocity; glm::vec3 _rotationalVelocity; glm::quat _lastRotation; @@ -164,6 +216,7 @@ 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; diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index d111439b7e..4a3fe2a129 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -54,7 +54,6 @@ 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; From 0321821b559b94125d9e2d217a478af931a79079 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 13 May 2014 11:38:24 -0700 Subject: [PATCH 079/108] More work on PrioVR integration. --- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/SkeletonModel.cpp | 12 ++--- interface/src/avatar/SkeletonModel.h | 3 +- interface/src/devices/PrioVR.cpp | 35 ++++++++++---- interface/src/devices/PrioVR.h | 5 +- libraries/fbx/src/FBXReader.cpp | 65 +++++++++++++++----------- libraries/fbx/src/FBXReader.h | 9 ++-- 8 files changed, 81 insertions(+), 54 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 36c51dc9fd..e6d6634653 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -111,7 +111,7 @@ void MyAvatar::reset() { void MyAvatar::update(float deltaTime) { Head* head = getHead(); head->relaxLean(deltaTime); - updateFromFaceTracker(deltaTime); + updateFromTrackers(deltaTime); if (Menu::getInstance()->isOptionChecked(MenuOption::MoveWithLean)) { // Faceshift drive is enabled, set the avatar drive based on the head position moveWithLean(); @@ -241,7 +241,7 @@ void MyAvatar::simulate(float deltaTime) { } // Update avatar head rotation with sensor data -void MyAvatar::updateFromFaceTracker(float deltaTime) { +void MyAvatar::updateFromTrackers(float deltaTime) { glm::vec3 estimatedPosition, estimatedRotation; FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d446c2e895..2df74f23c2 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -38,7 +38,7 @@ public: void reset(); void update(float deltaTime); void simulate(float deltaTime); - void updateFromFaceTracker(float deltaTime); + void updateFromTrackers(float deltaTime); void moveWithLean(); void render(const glm::vec3& cameraPosition, RenderMode renderMode = NORMAL_RENDER_MODE); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 8c21a3240f..96510dad25 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -52,15 +52,12 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } else if (leftPalmIndex == rightPalmIndex) { // right hand only - applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, - hand->getPalms()[leftPalmIndex]); + applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]); restoreLeftHandPosition(HAND_RESTORATION_RATE); } else { - applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices, - hand->getPalms()[leftPalmIndex]); - applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, - hand->getPalms()[rightPalmIndex]); + applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]); + applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]); } } @@ -140,8 +137,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector)); } -void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJointIndices, - const QVector& fingertipJointIndices, PalmData& palm) { +void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { if (jointIndex == -1) { return; } diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index ee6b3b9de3..20384829ea 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -39,8 +39,7 @@ protected: void applyHandPosition(int jointIndex, const glm::vec3& position); - void applyPalmData(int jointIndex, const QVector& fingerJointIndices, - const QVector& fingertipJointIndices, PalmData& palm); + void applyPalmData(int jointIndex, PalmData& palm); /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index 9b5427be07..5fe6b56b11 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -11,22 +11,44 @@ #include +#include + #include "PrioVR.h" -const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000009, 0x0000000A, 0x0000000C, 0x0000000D, - 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; +const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000009, 0x0000000A, + 0x0000000C, 0x0000000D, 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; const unsigned char AXIS_LIST[] = { 9, 43, 37, 37, 37, 13, 13, 13, 52, 52, 28, 28 }; const int LIST_LENGTH = sizeof(SERIAL_LIST) / sizeof(SERIAL_LIST[0]); +const char* JOINT_NAMES[] = { "Head", "Spine", "LeftArm", "LeftForeArm", "LeftHand", "RightArm", + "RightForeArm", "RightHand", "LeftUpLeg", "LeftLeg", "RightUpLeft", "RightLeg" }; + +#ifdef HAVE_PRIOVR +static int indexOfHumanIKJoint(const char* jointName) { + for (int i = 0;; i++) { + QByteArray humanIKJoint = HUMANIK_JOINTS[i]; + if (humanIKJoint.isEmpty()) { + return -1; + } + if (humanIKJoint == jointName) { + return i; + } + } +} +#endif + PrioVR::PrioVR() { #ifdef HAVE_PRIOVR - _jointsDiscovered.resize(LIST_LENGTH); + char jointsDiscovered[LIST_LENGTH]; _skeletalDevice = yei_setUpPrioVRSensors(0x00000000, const_cast(SERIAL_LIST), - const_cast(AXIS_LIST), _jointsDiscovered.data(), LIST_LENGTH, YEI_TIMESTAMP_SYSTEM); + const_cast(AXIS_LIST), jointsDiscovered, LIST_LENGTH, YEI_TIMESTAMP_SYSTEM); if (!_skeletalDevice) { return; } _jointRotations.resize(LIST_LENGTH); + for (int i = 0; i < LIST_LENGTH; i++) { + _humanIKJointIndices.append(jointsDiscovered[i] ? indexOfHumanIKJoint(JOINT_NAMES[i]) : -1); + } yei_tareSensors(_skeletalDevice); #endif } @@ -47,10 +69,5 @@ void PrioVR::update() { unsigned int timestamp; yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), _jointRotations.size() * sizeof(glm::quat), ×tamp); - for (int i = 0; i < _jointsDiscovered.size(); i++) { - if (_jointsDiscovered.at(i)) { - qDebug() << i << _jointRotations.at(i).x << _jointRotations.at(i).y << _jointRotations.at(i).z << _jointRotations.at(i).w; - } - } #endif } diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h index 1c26009b33..8bb21f4357 100644 --- a/interface/src/devices/PrioVR.h +++ b/interface/src/devices/PrioVR.h @@ -31,6 +31,9 @@ public: PrioVR(); virtual ~PrioVR(); + + const QVector& getHumanIKJointIndices() const { return _humanIKJointIndices; } + const QVector& getJointRotations() const { return _jointRotations; } void update(); @@ -39,7 +42,7 @@ private: YEI_Device_Id _skeletalDevice; #endif - QVector _jointsDiscovered; + QVector _humanIKJointIndices; QVector _jointRotations; }; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 1fc03ceb66..8d2ba4001a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -577,6 +577,25 @@ const char* FACESHIFT_BLENDSHAPES[] = { "" }; +const char* HUMANIK_JOINTS[] = { + "RightHand", + "RightForeArm", + "RightArm", + "Head", + "LeftArm", + "LeftForeArm", + "LeftHand", + "Spine", + "Hips", + "RightUpLeg", + "LeftUpLeg", + "RightLeg", + "LeftLeg", + "RightFoot", + "LeftFoot", + "" +}; + class FBXModel { public: QString name; @@ -1012,10 +1031,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QString jointHeadName = processID(getString(joints.value("jointHead", "jointHead"))); QString jointLeftHandName = processID(getString(joints.value("jointLeftHand", "jointLeftHand"))); QString jointRightHandName = processID(getString(joints.value("jointRightHand", "jointRightHand"))); - QVariantList jointLeftFingerNames = joints.values("jointLeftFinger"); - QVariantList jointRightFingerNames = joints.values("jointRightFinger"); - QVariantList jointLeftFingertipNames = joints.values("jointLeftFingertip"); - QVariantList jointRightFingertipNames = joints.values("jointRightFingertip"); QString jointEyeLeftID; QString jointEyeRightID; QString jointNeckID; @@ -1024,10 +1039,16 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QString jointHeadID; QString jointLeftHandID; QString jointRightHandID; - QVector jointLeftFingerIDs(jointLeftFingerNames.size()); - QVector jointRightFingerIDs(jointRightFingerNames.size()); - QVector jointLeftFingertipIDs(jointLeftFingertipNames.size()); - QVector jointRightFingertipIDs(jointRightFingertipNames.size()); + + QVector humanIKJointNames; + for (int i = 0;; i++) { + QByteArray jointName = HUMANIK_JOINTS[i]; + if (jointName.isEmpty()) { + break; + } + humanIKJointNames.append(processID(getString(joints.value(jointName, jointName)))); + } + QVector humanIKJointIDs(humanIKJointNames.size()); QVariantHash blendshapeMappings = mapping.value("bs").toHash(); QMultiHash blendshapeIndices; @@ -1091,7 +1112,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else { name = getID(object.properties); } - int index; if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") { jointEyeLeftID = getID(object.properties); @@ -1115,19 +1135,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else if (name == jointRightHandName) { jointRightHandID = getID(object.properties); - - } else if ((index = jointLeftFingerNames.indexOf(name)) != -1) { - jointLeftFingerIDs[index] = getID(object.properties); - - } else if ((index = jointRightFingerNames.indexOf(name)) != -1) { - jointRightFingerIDs[index] = getID(object.properties); - - } else if ((index = jointLeftFingertipNames.indexOf(name)) != -1) { - jointLeftFingertipIDs[index] = getID(object.properties); - - } else if ((index = jointRightFingertipNames.indexOf(name)) != -1) { - jointRightFingertipIDs[index] = getID(object.properties); } + int humanIKJointIndex = humanIKJointNames.indexOf(name); + if (humanIKJointIndex != -1) { + humanIKJointIDs[humanIKJointIndex] = getID(object.properties); + } + glm::vec3 translation; // NOTE: the euler angles as supplied by the FBX file are in degrees glm::vec3 rotationOffset; @@ -1513,11 +1526,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) geometry.headJointIndex = modelIDs.indexOf(jointHeadID); geometry.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID); geometry.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); - geometry.leftFingerJointIndices = getIndices(jointLeftFingerIDs, modelIDs); - geometry.rightFingerJointIndices = getIndices(jointRightFingerIDs, modelIDs); - geometry.leftFingertipJointIndices = getIndices(jointLeftFingertipIDs, modelIDs); - geometry.rightFingertipJointIndices = getIndices(jointRightFingertipIDs, modelIDs); - + + foreach (const QString& id, humanIKJointIDs) { + geometry.humanIKJointIndices.append(modelIDs.indexOf(id)); + } + // extract the translation component of the neck transform if (geometry.neckJointIndex != -1) { const glm::mat4& transform = geometry.joints.at(geometry.neckJointIndex).transform; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 51e7380181..38251e4065 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -30,6 +30,9 @@ typedef QList FBXNodeList; /// The names of the blendshapes expected by Faceshift, terminated with an empty string. extern const char* FACESHIFT_BLENDSHAPES[]; +/// The names of the joints in the Maya HumanIK rig, terminated with an empty string. +extern const char* HUMANIK_JOINTS[]; + class Extents { public: /// set minimum and maximum to FLT_MAX and -FLT_MAX respectively @@ -199,11 +202,7 @@ public: int leftHandJointIndex; int rightHandJointIndex; - QVector leftFingerJointIndices; - QVector rightFingerJointIndices; - - QVector leftFingertipJointIndices; - QVector rightFingertipJointIndices; + QVector humanIKJointIndices; glm::vec3 palmDirection; From c59358ac96446c6a5e8947f4688db93afe51c64b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 May 2014 14:10:01 -0700 Subject: [PATCH 080/108] First cut at editModels with the mouse --- examples/editModels.js | 273 ++++++++++++++++++++++++++++++++++------- examples/toolBars.js | 39 +++--- 2 files changed, 244 insertions(+), 68 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 90ce87d259..46e8248681 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -368,41 +368,100 @@ function checkController(deltaTime) { moveOverlays(); } +var clickEvent = false; +var newModel; +var modifierType = -1; +var moveYZ; +var moveXZ; +var moveXY; +var yaw; +var pitch; +var roll; +var scale; + +var modelSelected = false; +var selectedModelID; +var selectedModelProperties; + + +function initToolBar() { + toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); + // New Model + newModel = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }); + print("New Model: " + newModel) + + // Move YZ + moveYZ = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Move XZ + moveXZ = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Move XY + moveXY = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + + + // Yaw + yaw = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Pitch + pitch = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Roll + roll = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + + + // Scale + scale = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); +} + function moveOverlays() { if (typeof(toolBar) === 'undefined') { - toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); - // New Model - toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }); - // Move YZ - toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Move XZ - toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Move XY - toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); + initToolBar(); + } else if (windowDimensions.x == Controller.getViewportDimensions().x && windowDimensions.y == Controller.getViewportDimensions().y) { return; @@ -417,27 +476,147 @@ function moveOverlays() { } function mousePressEvent(event) { + modelSelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - var url; - var index = toolBar.clicked(clickedOverlay); - if (index == 0) { - url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]); - if (url == null) { - return; - } + + if (clickedOverlay != 0) { + var index = toolBar.clicked(clickedOverlay); - var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); - Models.addModel({ position: position, - radius: radiusDefault, - modelURL: url - }); - } else if (index == -1) { - print("Didn't click on anything"); + switch(index) { + case newModel: + var url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]); + if (url == null) { + return; + } + + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); + Models.addModel({ position: position, + radius: radiusDefault, + modelURL: url + }); + break; + case moveYZ: + print("Selected moveYZ"); + + break; + case moveXZ: + print("Selected moveXZ"); + + break; + case moveXY: + print("Selected moveXY"); + + break; + case yaw: + print("Selected yaw"); + + break; + case pitch: + print("Selected pitch"); + + break; + case roll: + print("Selected roll"); + + break; + case scale: + print("Selected scale"); + + break; + default: + clickEvent = false; + return; + } + clickEvent = true; + + if (modifierType != -1) { + toolBar.tools[modifierType].select(false); + } + modifierType = index; + } else { + var pickRay = Camera.computePickRay(event.x, event.y); + Vec3.print("Looking at: ", pickRay.origin); + var foundModels = Models.findModels(pickray.origin, LASER_LENGTH_FACTOR); + for (var i = 0; i < foundModels.length; i++) { + if (!foundModels[i].isKnownID) { + var identify = Models.identifyModel(foundModels[i]); + if (!identify.isKnownID) { + print("Unknown ID " + identify.id + "(update loop)"); + return; + } + foundModels[i] = identify; + } + + var properties = Models.getModelProperties(foundModels[i]); + print("Checking properties: " + properties.id + " " + properties.isKnownID); + + // P P - Model + // /| A - Palm + // / | d B - unit vector toward tip + // / | X - base of the perpendicular line + // A---X----->B d - distance fom axis + // x x - distance from A + // + // |X-A| = (P-A).B + // X == A + ((P-A).B)B + // d = |P-X| + + var A = pickRay.origin; + var B = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, LASER_LENGTH_FACTOR)); + var P = properties.position; + + var x = Vec3.dot(Vec3.subtract(P, A), B); + var X = Vec3.sum(A, Vec3.multiply(B, x)); + var d = Vec3.length(Vec3.subtract(P, X)); + + if (d < properties.radius && 0 < x && x < LASER_LENGTH_FACTOR) { + modelSelected = true; + selectedModelID = foundModels[i]; + selectedModelProperties = properties; + return; + } + } } } function mouseMoveEvent(event) { + if (clickEvent && !modelSelected) { + return; + } + print("Dragging"); + + switch(modifierType) { + case moveYZ: + print("Move " + moveYZ); + break; + case moveXZ: + print("Move " + moveXZ); + + break; + case moveXY: + print("Move " + moveXY); + + break; + case yaw: + print("Move " + yaw); + + break; + case pitch: + print("Move " + pitch); + + break; + case roll: + print("Move " + roll); + + break; + case scale: + print("Move " + scale); + + break; + } + + Model.editModel(selectedModelID, selectedModelProperties); } function scriptEnding() { diff --git a/examples/toolBars.js b/examples/toolBars.js index 62a01a9a15..7f8f09575b 100644 --- a/examples/toolBars.js +++ b/examples/toolBars.js @@ -13,7 +13,6 @@ Overlay2D = function(properties, overlay) { // overlay is an optionnal variable if (!(typeof(properties) === 'undefined')) { if(typeof(overlay) === 'undefined') { overlay = Overlays.addOverlay("image", properties); - print("New overlay: " + overlay); } else { Overlays.editOverlay(overlay, properties); } @@ -87,25 +86,23 @@ Tool = function(properties, selectable, selected) { // selectable and selected a return selectable; } - if (this.selectable()) { - this.selected = function() { - return selected; - } - this.select = function(doSelect) { - selected = doSelect; - properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; - Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); - } - this.toggle = function() { - selected = !selected; - properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; - Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); - - return selected; - } - - this.select(selected); + this.selected = function() { + return selected; } + this.select = function(doSelect) { + selected = doSelect; + properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; + Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); + } + this.toggle = function() { + selected = !selected; + properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; + Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); + + return selected; + } + + this.select(selected); this.baseClicked = this.clicked; this.clicked = function(clickedOverlay) { @@ -144,7 +141,7 @@ ToolBar = function(x, y, direction) { } this.tools[this.tools.length] = new Tool(properties, selectable, selected); - return this.tools.length - 1; + return ((this.tools.length) - 1); } this.move = function(x, y) { @@ -172,7 +169,7 @@ ToolBar = function(x, y, direction) { this.clicked = function(clickedOverlay) { for(var tool in this.tools) { if (this.tools[tool].visible() && this.tools[tool].clicked(clickedOverlay)) { - return tool; + return parseInt(tool); } } return -1; From 701bc63bd19b37907ad7272898f6b044ec42948e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 May 2014 14:34:08 -0700 Subject: [PATCH 081/108] Removed old toolbar --- examples/editModels.js | 185 +++++------------------------------------ 1 file changed, 22 insertions(+), 163 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 46e8248681..07b31e77d2 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -18,7 +18,7 @@ var toolWidth = 50; var LASER_WIDTH = 4; var LASER_COLOR = { red: 255, green: 0, blue: 0 }; -var LASER_LENGTH_FACTOR = 1.5; +var LASER_LENGTH_FACTOR = 5; var LEFT = 0; var RIGHT = 1; @@ -368,20 +368,6 @@ function checkController(deltaTime) { moveOverlays(); } -var clickEvent = false; -var newModel; -var modifierType = -1; -var moveYZ; -var moveXZ; -var moveXY; -var yaw; -var pitch; -var roll; -var scale; - -var modelSelected = false; -var selectedModelID; -var selectedModelProperties; function initToolBar() { @@ -394,68 +380,6 @@ function initToolBar() { visible: true, alpha: 0.9 }); - print("New Model: " + newModel) - - // Move YZ - moveYZ = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Move XZ - moveXZ = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Move XY - moveXY = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - - - // Yaw - yaw = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Pitch - pitch = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Roll - roll = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - - - // Scale - scale = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); } function moveOverlays() { @@ -475,74 +399,39 @@ function moveOverlays() { toolBar.move(toolsX, toolsY); } + + +var modelSelected = false; +var selectedModelID; +var selectedModelProperties; + function mousePressEvent(event) { - modelSelected = false; + var modelSelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - if (clickedOverlay != 0) { - var index = toolBar.clicked(clickedOverlay); - - switch(index) { - case newModel: - var url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]); - if (url == null) { - return; - } - - var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); - Models.addModel({ position: position, - radius: radiusDefault, - modelURL: url - }); - break; - case moveYZ: - print("Selected moveYZ"); - - break; - case moveXZ: - print("Selected moveXZ"); - - break; - case moveXY: - print("Selected moveXY"); - - break; - case yaw: - print("Selected yaw"); - - break; - case pitch: - print("Selected pitch"); - - break; - case roll: - print("Selected roll"); - - break; - case scale: - print("Selected scale"); - - break; - default: - clickEvent = false; - return; + if (newModel == toolBar.clicked(clickedOverlay)) { + var url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]); + if (url == null) { + return; } - clickEvent = true; - if (modifierType != -1) { - toolBar.tools[modifierType].select(false); - } - modifierType = index; + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); + Models.addModel({ position: position, + radius: radiusDefault, + modelURL: url + }); + } else { var pickRay = Camera.computePickRay(event.x, event.y); - Vec3.print("Looking at: ", pickRay.origin); + Vec3.print("[Mouse] Looking at: ", pickRay.origin); var foundModels = Models.findModels(pickray.origin, LASER_LENGTH_FACTOR); + print("Num: " + foundModels.length.toString()); for (var i = 0; i < foundModels.length; i++) { if (!foundModels[i].isKnownID) { var identify = Models.identifyModel(foundModels[i]); if (!identify.isKnownID) { print("Unknown ID " + identify.id + "(update loop)"); - return; + continue; } foundModels[i] = identify; } @@ -580,41 +469,11 @@ function mousePressEvent(event) { } function mouseMoveEvent(event) { - if (clickEvent && !modelSelected) { + if (!modelSelected) { return; } - print("Dragging"); - switch(modifierType) { - case moveYZ: - print("Move " + moveYZ); - break; - case moveXZ: - print("Move " + moveXZ); - - break; - case moveXY: - print("Move " + moveXY); - - break; - case yaw: - print("Move " + yaw); - - break; - case pitch: - print("Move " + pitch); - - break; - case roll: - print("Move " + roll); - - break; - case scale: - print("Move " + scale); - - break; - } Model.editModel(selectedModelID, selectedModelProperties); } From eceb600de721500952878e55abf62f0d2c393637 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 13 May 2014 14:44:24 -0700 Subject: [PATCH 082/108] More work on PrioVR integration. --- interface/src/Application.cpp | 5 +++ interface/src/Application.h | 4 +++ interface/src/avatar/MyAvatar.cpp | 47 ++++++++++++++++---------- interface/src/avatar/SkeletonModel.cpp | 20 +++++++++-- interface/src/devices/PrioVR.cpp | 40 +++++++++++++++++++++- interface/src/devices/PrioVR.h | 13 +++++++ 6 files changed, 108 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index deefd10ab0..f9eaf89ea4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2752,6 +2752,9 @@ void Application::displayOverlay() { drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 0.0f, 0, frameTimer, WHITE_TEXT); } + // give external parties a change to hook in + emit renderingOverlay(); + _overlays.render2D(); glPopMatrix(); @@ -3060,6 +3063,8 @@ void Application::resetSensors() { OculusManager::reset(); } + _prioVR.reset(); + QCursor::setPos(_mouseX, _mouseY); _myAvatar->reset(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 77f8024b6c..985859f4d0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -195,6 +195,7 @@ public: Visage* getVisage() { return &_visage; } FaceTracker* getActiveFaceTracker(); SixenseManager* getSixenseManager() { return &_sixenseManager; } + PrioVR* getPrioVR() { return &_prioVR; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } QUndoStack* getUndoStack() { return &_undoStack; } @@ -268,6 +269,9 @@ signals: /// Fired when we're rendering in-world interface elements; allows external parties to hook in. void renderingInWorldInterface(); + /// Fired when we're rendering the overlay. + void renderingOverlay(); + /// Fired when the import window is closed void importDone(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e6d6634653..8358fecd9d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -244,23 +244,28 @@ void MyAvatar::simulate(float deltaTime) { void MyAvatar::updateFromTrackers(float deltaTime) { glm::vec3 estimatedPosition, estimatedRotation; - FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); - if (tracker) { - estimatedPosition = tracker->getHeadTranslation(); - estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation())); - - // Rotate the body if the head is turned beyond the screen - if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) { - const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f; - const float TRACKER_MIN_YAW_TURN = 15.0f; - const float TRACKER_MAX_YAW_TURN = 50.0f; - if ( (fabs(estimatedRotation.y) > TRACKER_MIN_YAW_TURN) && - (fabs(estimatedRotation.y) < TRACKER_MAX_YAW_TURN) ) { - if (estimatedRotation.y > 0.0f) { - _bodyYawDelta += (estimatedRotation.y - TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY; - } else { - _bodyYawDelta += (estimatedRotation.y + TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY; - } + if (Application::getInstance()->getPrioVR()->isActive()) { + estimatedRotation = glm::degrees(safeEulerAngles(Application::getInstance()->getPrioVR()->getHeadRotation())); + + } else { + FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); + if (tracker) { + estimatedPosition = tracker->getHeadTranslation(); + estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation())); + } + } + + // Rotate the body if the head is turned beyond the screen + if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) { + const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f; + const float TRACKER_MIN_YAW_TURN = 15.0f; + const float TRACKER_MAX_YAW_TURN = 50.0f; + if ( (fabs(estimatedRotation.y) > TRACKER_MIN_YAW_TURN) && + (fabs(estimatedRotation.y) < TRACKER_MAX_YAW_TURN) ) { + if (estimatedRotation.y > 0.0f) { + _bodyYawDelta += (estimatedRotation.y - TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY; + } else { + _bodyYawDelta += (estimatedRotation.y + TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY; } } } @@ -278,6 +283,14 @@ void MyAvatar::updateFromTrackers(float deltaTime) { head->setDeltaYaw(estimatedRotation.y * magnifyFieldOfView); head->setDeltaRoll(estimatedRotation.z); + // the priovr can give us exact lean + if (Application::getInstance()->getPrioVR()->isActive()) { + glm::vec3 eulers = glm::degrees(safeEulerAngles(Application::getInstance()->getPrioVR()->getTorsoRotation())); + head->setLeanSideways(eulers.z); + head->setLeanForward(eulers.x); + return; + } + // Update torso lean distance based on accelerometer data const float TORSO_LENGTH = 0.5f; glm::vec3 relativePosition = estimatedPosition - glm::vec3(0.0f, -TORSO_LENGTH, 0.0f); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 96510dad25..7a515d63dc 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -33,14 +33,28 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { return; // only simulate for own avatar } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + PrioVR* prioVR = Application::getInstance()->getPrioVR(); + if (prioVR->isActive()) { + for (int i = 0; i < prioVR->getJointRotations().size(); i++) { + int humanIKJointIndex = prioVR->getHumanIKJointIndices().at(i); + if (humanIKJointIndex == -1) { + continue; + } + int jointIndex = geometry.humanIKJointIndices.at(humanIKJointIndex); + if (jointIndex != -1) { + setJointRotation(jointIndex, _rotation * prioVR->getJointRotations().at(i), true); + } + } + return; + } + // find the left and rightmost active Leap palms int leftPalmIndex, rightPalmIndex; Hand* hand = _owningAvatar->getHand(); hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); - const float HAND_RESTORATION_RATE = 0.25f; - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const float HAND_RESTORATION_RATE = 0.25f; if (leftPalmIndex == -1) { // no Leap data; set hands from mouse if (_owningAvatar->getHandState() == HAND_STATE_NULL) { diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index 5fe6b56b11..adda3b5cb2 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -13,7 +13,9 @@ #include +#include "Application.h" #include "PrioVR.h" +#include "ui/TextRenderer.h" const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000009, 0x0000000A, 0x0000000C, 0x0000000D, 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; @@ -49,7 +51,6 @@ PrioVR::PrioVR() { for (int i = 0; i < LIST_LENGTH; i++) { _humanIKJointIndices.append(jointsDiscovered[i] ? indexOfHumanIKJoint(JOINT_NAMES[i]) : -1); } - yei_tareSensors(_skeletalDevice); #endif } @@ -61,6 +62,16 @@ PrioVR::~PrioVR() { #endif } +glm::quat PrioVR::getHeadRotation() const { + const int HEAD_ROTATION_INDEX = 0; + return _jointRotations.size() > HEAD_ROTATION_INDEX ? _jointRotations.at(HEAD_ROTATION_INDEX) : glm::quat(); +} + +glm::quat PrioVR::getTorsoRotation() const { + const int TORSO_ROTATION_INDEX = 1; + return _jointRotations.size() > TORSO_ROTATION_INDEX ? _jointRotations.at(TORSO_ROTATION_INDEX) : glm::quat(); +} + void PrioVR::update() { #ifdef HAVE_PRIOVR if (!_skeletalDevice) { @@ -71,3 +82,30 @@ void PrioVR::update() { _jointRotations.size() * sizeof(glm::quat), ×tamp); #endif } + +void PrioVR::reset() { +#ifdef HAVE_PRIOVR + if (!_skeletalDevice) { + return; + } + connect(Application::getInstance(), SIGNAL(renderingOverlay()), SLOT(renderCalibrationCountdown())); + _calibrationCountdownStarted = QDateTime::currentDateTime(); +#endif +} + +void PrioVR::renderCalibrationCountdown() { +#ifdef HAVE_PRIOVR + const int COUNTDOWN_SECONDS = 3; + int secondsRemaining = COUNTDOWN_SECONDS - _calibrationCountdownStarted.secsTo(QDateTime::currentDateTime()); + if (secondsRemaining == 0) { + yei_tareSensors(_skeletalDevice); + Application::getInstance()->disconnect(this); + return; + } + static TextRenderer textRenderer(MONO_FONT_FAMILY, 18, QFont::Bold, false, TextRenderer::OUTLINE_EFFECT, 2); + QByteArray text = "Assume T-Pose in " + QByteArray::number(secondsRemaining) + "..."; + textRenderer.draw((Application::getInstance()->getGLWidget()->width() - textRenderer.computeWidth(text.constData())) / 2, + Application::getInstance()->getGLWidget()->height() / 2, + text); +#endif +} diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h index 8bb21f4357..9cd7bda5d4 100644 --- a/interface/src/devices/PrioVR.h +++ b/interface/src/devices/PrioVR.h @@ -12,6 +12,7 @@ #ifndef hifi_PrioVR_h #define hifi_PrioVR_h +#include #include #include @@ -32,10 +33,20 @@ public: PrioVR(); virtual ~PrioVR(); + bool isActive() const { return !_jointRotations.isEmpty(); } + + glm::quat getHeadRotation() const; + glm::quat getTorsoRotation() const; + const QVector& getHumanIKJointIndices() const { return _humanIKJointIndices; } const QVector& getJointRotations() const { return _jointRotations; } void update(); + void reset(); + +private slots: + + void renderCalibrationCountdown(); private: #ifdef HAVE_PRIOVR @@ -44,6 +55,8 @@ private: QVector _humanIKJointIndices; QVector _jointRotations; + + QDateTime _calibrationCountdownStarted; }; #endif // hifi_PrioVR_h From 624ae4732f8a11af7aabc9c1abab9282fb751acb Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 May 2014 14:58:34 -0700 Subject: [PATCH 083/108] Fixes to PickRay check --- examples/editModels.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 07b31e77d2..017e4ba740 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -406,7 +406,7 @@ var selectedModelID; var selectedModelProperties; function mousePressEvent(event) { - var modelSelected = false; + modelSelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); if (newModel == toolBar.clicked(clickedOverlay)) { @@ -424,8 +424,7 @@ function mousePressEvent(event) { } else { var pickRay = Camera.computePickRay(event.x, event.y); Vec3.print("[Mouse] Looking at: ", pickRay.origin); - var foundModels = Models.findModels(pickray.origin, LASER_LENGTH_FACTOR); - print("Num: " + foundModels.length.toString()); + var foundModels = Models.findModels(pickRay.origin, LASER_LENGTH_FACTOR); for (var i = 0; i < foundModels.length; i++) { if (!foundModels[i].isKnownID) { var identify = Models.identifyModel(foundModels[i]); @@ -451,7 +450,7 @@ function mousePressEvent(event) { // d = |P-X| var A = pickRay.origin; - var B = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, LASER_LENGTH_FACTOR)); + var B = Vec3.normalize(pickRay.direction); var P = properties.position; var x = Vec3.dot(Vec3.subtract(P, A), B); @@ -462,6 +461,7 @@ function mousePressEvent(event) { modelSelected = true; selectedModelID = foundModels[i]; selectedModelProperties = properties; + print("Clicked on " + selectedModelID.id + " " + modelSelected); return; } } @@ -475,7 +475,7 @@ function mouseMoveEvent(event) { print("Dragging"); - Model.editModel(selectedModelID, selectedModelProperties); + //Model.editModel(selectedModelID, selectedModelProperties); } function scriptEnding() { @@ -488,7 +488,7 @@ Script.scriptEnding.connect(scriptEnding); // register the call back so it fires before each data send Script.update.connect(checkController); Controller.mousePressEvent.connect(mousePressEvent); -Controller.mousePressEvent.connect(mouseMoveEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); From bca5636f9b424f3024e7186218656a9e70dc7dc9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 13 May 2014 15:05:00 -0700 Subject: [PATCH 084/108] Looks like "reflection" can be used as a synonym for "specular." --- libraries/fbx/src/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 8d2ba4001a..ce71e90114 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1365,7 +1365,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else if (type.contains("bump") || type.contains("normal")) { bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); - } else if (type.contains("specular")) { + } else if (type.contains("specular") || type.contains("reflection")) { specularTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } else if (type == "lcl rotation") { From da7081300291c6179d7325c5ad58606d861ad996 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 13 May 2014 15:45:24 -0700 Subject: [PATCH 085/108] On Windows, Visage takes the folder containing the license file, not the license file itself. --- interface/src/devices/Visage.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index a467d2d4a8..8173519478 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -41,7 +41,11 @@ Visage::Visage() : _headOrigin(DEFAULT_HEAD_ORIGIN) { #ifdef HAVE_VISAGE +#ifdef WIN32 + QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage"; +#else QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage/license.vlc"; +#endif initializeLicenseManager(licensePath.data()); _tracker = new VisageTracker2(Application::resourcesPath().toLatin1() + "visage/tracker.cfg"); _data = new FaceData(); From 19404fe0e5238c6315c5e0d16097d29385381caf Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Wed, 14 May 2014 07:10:31 +0200 Subject: [PATCH 086/108] Changed start domain to sandbox. Changed start location to xyz: 6270, 211, 6000. goHome behaviour is unchanged and will still just go to the start location keeping the currently select domain. --- interface/src/avatar/Avatar.h | 7 +++---- libraries/networking/src/DomainHandler.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 289a0500d0..c2a8af55b2 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -54,10 +54,9 @@ enum ScreenTintLayer { NUM_SCREEN_TINT_LAYERS }; -// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found) -// this is basically in the center of the ground plane. Slightly adjusted. This was asked for by -// Grayson as he's building a street around here for demo dinner 2 -const glm::vec3 START_LOCATION(0.485f * TREE_SCALE, 0.0f, 0.5f * TREE_SCALE); +// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found). +// This is the start location in the Sandbox (xyz: 6270, 211, 6000). +const glm::vec3 START_LOCATION(0.38269043f * TREE_SCALE, 0.01287842f * TREE_SCALE, 0.36621094f * TREE_SCALE); class Texture; diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index b78b8875c4..599f6d4a0f 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -21,7 +21,7 @@ #include "DTLSClientSession.h" #include "HifiSockAddr.h" -const QString DEFAULT_DOMAIN_HOSTNAME = "alpha.highfidelity.io"; +const QString DEFAULT_DOMAIN_HOSTNAME = "sandbox.highfidelity.io"; const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102; const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103; From d59bedfa96a1efaab3011ad18697ae71788f5a0f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 13 May 2014 22:25:08 -0700 Subject: [PATCH 087/108] Fix running scripts not properly updated when done programmatically Add a slot to Application to receive ScriptEngine::finished signals. Notify the Running scripts widget of stopped scripts so it can properly move them to the recent scripts list. --- interface/src/Application.cpp | 16 ++++++++++------ interface/src/Application.h | 1 + interface/src/ui/RunningScriptsWidget.cpp | 9 ++++----- interface/src/ui/RunningScriptsWidget.h | 1 + 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8858b6d324..ab8d69c319 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3405,6 +3405,8 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable); connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater())); + connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(scriptFinished(const QString&))); + scriptEngine->registerGlobalObject("Overlays", &_overlays); QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", WindowScriptingInterface::getInstance()); @@ -3447,6 +3449,14 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript return scriptEngine; } +void Application::scriptFinished(const QString &scriptName) { + if (_scriptEnginesHash.remove(scriptName)) { + _runningScriptsWidget->scriptStopped(scriptName); + _runningScriptsWidget->setRunningScripts(getRunningScripts()); + bumpSettings(); + } +} + void Application::stopAllScripts(bool restart) { // stops all current running scripts for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); @@ -3457,18 +3467,12 @@ void Application::stopAllScripts(bool restart) { it.value()->stop(); qDebug() << "stopping script..." << it.key(); } - _scriptEnginesHash.clear(); - _runningScriptsWidget->setRunningScripts(getRunningScripts()); - bumpSettings(); } void Application::stopScript(const QString &scriptName) { if (_scriptEnginesHash.contains(scriptName)) { _scriptEnginesHash.value(scriptName)->stop(); qDebug() << "stopping script..." << scriptName; - _scriptEnginesHash.remove(scriptName); - _runningScriptsWidget->setRunningScripts(getRunningScripts()); - bumpSettings(); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 67cf7dad44..174ac61c06 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -292,6 +292,7 @@ public slots: void toggleLogDialog(); void initAvatarAndViewFrustum(); ScriptEngine* loadScript(const QString& fileNameString, bool loadScriptFromEditor = false); + void scriptFinished(const QString& scriptName); void stopAllScripts(bool restart = false); void stopScript(const QString& scriptName); void reloadAllScripts(); diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 61241f71fb..328974dc9d 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -157,6 +157,10 @@ void RunningScriptsWidget::paintEvent(QPaintEvent* event) { painter.end(); } +void RunningScriptsWidget::scriptStopped(const QString& scriptName) { + _recentlyLoadedScripts.prepend(scriptName); +} + void RunningScriptsWidget::stopScript(int row, int column) { if (column == 1) { // make sure the user has clicked on the close icon _lastStoppedScript = _runningScriptsTable->item(row, 0)->toolTip(); @@ -169,11 +173,6 @@ void RunningScriptsWidget::loadScript(int row, int column) { } void RunningScriptsWidget::allScriptsStopped() { - QStringList list = Application::getInstance()->getRunningScripts(); - for (int i = 0; i < list.size(); ++i) { - _recentlyLoadedScripts.prepend(list.at(i)); - } - Application::getInstance()->stopAllScripts(); } diff --git a/interface/src/ui/RunningScriptsWidget.h b/interface/src/ui/RunningScriptsWidget.h index ad310c4ed4..14a1f4a58e 100644 --- a/interface/src/ui/RunningScriptsWidget.h +++ b/interface/src/ui/RunningScriptsWidget.h @@ -36,6 +36,7 @@ protected: virtual void paintEvent(QPaintEvent* event); public slots: + void scriptStopped(const QString& scriptName); void setBoundary(const QRect& rect); private slots: From b01b14485491fcb3962d75839657c9cfc24c4b15 Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Wed, 14 May 2014 07:36:27 +0200 Subject: [PATCH 088/108] No more hand movement when clicking with the mouse. --- interface/src/avatar/MyAvatar.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 36c51dc9fd..20e4bcc44e 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -137,15 +137,8 @@ void MyAvatar::simulate(float deltaTime) { Application::getInstance()->getCamera()->setScale(scale); } - // update the movement of the hand and process handshaking with other avatars... - bool pointing = false; - if (_mousePressed) { - _handState = HAND_STATE_GRASPING; - } else if (pointing) { - _handState = HAND_STATE_POINTING; - } else { - _handState = HAND_STATE_NULL; - } + // no extra movement of the hand here any more ... + _handState = HAND_STATE_NULL; updateOrientation(deltaTime); From 8247e5a55277e4361af4fd3663dfb944f637033f Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Wed, 14 May 2014 17:25:40 +0200 Subject: [PATCH 089/108] added discourse key in shift operators --- libraries/networking/src/DataServerAccountInfo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index a9522148a8..0fdb5ff4b1 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -76,11 +76,11 @@ void DataServerAccountInfo::setDiscourseApiKey(const QString& discourseApiKey) { } QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { - out << info._accessToken << info._username << info._xmppPassword; + out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey; return out; } QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) { - in >> info._accessToken >> info._username >> info._xmppPassword; + in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey; return in; } From d2d9ca878884c3e097aab36f34ea3e356c74b368 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 14 May 2014 09:04:59 -0700 Subject: [PATCH 090/108] optimization: only compute baseTransform once --- interface/src/renderer/Model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index f46fd48beb..0c688865f3 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -128,6 +128,7 @@ QVector Model::createJointStates(const FBXGeometry& geometry) jointIsSet.fill(false, numJoints); int numJointsSet = 0; int lastNumJointsSet = -1; + glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset); while (numJointsSet < numJoints && numJointsSet != lastNumJointsSet) { lastNumJointsSet = numJointsSet; for (int i = 0; i < numJoints; ++i) { @@ -138,7 +139,6 @@ QVector Model::createJointStates(const FBXGeometry& geometry) const FBXJoint& joint = geometry.joints[i]; int parentIndex = joint.parentIndex; if (parentIndex == -1) { - glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset); glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation; state.transform = baseTransform * geometry.offset * glm::translate(state.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; From 366e9c7d3452cbed0cc78edbd59463c8a615b64f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 14 May 2014 09:05:54 -0700 Subject: [PATCH 091/108] PalmDataA::getPalmDirection --> getNormal() --- interface/src/avatar/Avatar.cpp | 2 +- interface/src/avatar/Hand.cpp | 3 +-- interface/src/scripting/ControllerScriptingInterface.cpp | 2 +- libraries/avatars/src/HandData.cpp | 2 +- libraries/avatars/src/HandData.h | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 2bd0bbbc6d..20f6275441 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -622,7 +622,7 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti glm::vec3 fingerAxis = palm->getFingerDirection(); glm::vec3 diskCenter = handPosition + HAND_PADDLE_OFFSET * fingerAxis; - glm::vec3 diskNormal = palm->getPalmDirection(); + glm::vec3 diskNormal = palm->getNormal(); const float DISK_THICKNESS = 0.08f; // collide against the disk diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 78eab424ab..ee7a633a54 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -23,7 +23,6 @@ using namespace std; -const float FINGERTIP_COLLISION_RADIUS = 0.01f; const float PALM_COLLISION_RADIUS = 0.03f; @@ -201,7 +200,7 @@ void Hand::renderHandTargets(bool isMine) { 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; + glm::vec3 offsetFromPalm = root + palm.getNormal() * PALM_DISK_THICKNESS; Avatar::renderJointConnectingCone(root, offsetFromPalm, PALM_DISK_RADIUS, 0.0f); glPushMatrix(); glTranslatef(root.x, root.y, root.z); diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 5e58ac66ea..58a08066d6 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -198,7 +198,7 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex if (palmData) { switch (controlOfPalm) { case PALM_SPATIALCONTROL: - return palmData->getPalmDirection(); + return palmData->getNormal(); case TIP_SPATIALCONTROL: return palmData->getFingerDirection(); } diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index bd366f020a..0105145466 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -120,7 +120,7 @@ glm::vec3 PalmData::getFingerDirection() const { return _owningHandData->localToWorldDirection(_rawRotation * LOCAL_FINGER_DIRECTION); } -glm::vec3 PalmData::getPalmDirection() const { +glm::vec3 PalmData::getNormal() const { const glm::vec3 LOCAL_PALM_DIRECTION(0.0f, -1.0f, 0.0f); return _owningHandData->localToWorldDirection(_rawRotation * LOCAL_PALM_DIRECTION); } diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 1f2d134c43..505b1b600a 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -146,7 +146,7 @@ public: // return world-frame: glm::vec3 getFingerTipPosition() const; glm::vec3 getFingerDirection() const; - glm::vec3 getPalmDirection() const; + glm::vec3 getNormal() const; private: glm::quat _rawRotation; From 498f2843b06655028db150ea7870fde8beea6b9c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 14 May 2014 09:06:46 -0700 Subject: [PATCH 092/108] Fix for bad hand rotations --- interface/src/avatar/SkeletonModel.cpp | 22 ++++++++-------------- interface/src/avatar/SkeletonModel.h | 3 +-- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index a2e637f4e7..544f573eda 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -52,15 +52,12 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } else if (leftPalmIndex == rightPalmIndex) { // right hand only - applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, - hand->getPalms()[leftPalmIndex]); + applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]); restoreLeftHandPosition(HAND_RESTORATION_RATE); } else { - applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices, - hand->getPalms()[leftPalmIndex]); - applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, - hand->getPalms()[rightPalmIndex]); + applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]); + applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]); } } @@ -140,8 +137,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector)); } -void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJointIndices, - const QVector& fingertipJointIndices, PalmData& palm) { +void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { if (jointIndex == -1) { return; } @@ -152,19 +148,17 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin return; } - // rotate palm to align with palm direction + // rotate palm to align with its normal (normal points out of hand's palm) glm::quat palmRotation; if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) { getJointRotation(parentJointIndex, palmRotation, true); } else { getJointRotation(jointIndex, palmRotation, true); } - palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palm.getPalmDirection()) * palmRotation; + palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()) * palmRotation; - // rotate forearm according to average finger direction - // 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); + // rotate palm to align with finger direction + glm::vec3 direction = palm.getFingerDirection(); palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction) * palmRotation; // set hand position, rotation diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index ee6b3b9de3..20384829ea 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -39,8 +39,7 @@ protected: void applyHandPosition(int jointIndex, const glm::vec3& position); - void applyPalmData(int jointIndex, const QVector& fingerJointIndices, - const QVector& fingertipJointIndices, PalmData& palm); + void applyPalmData(int jointIndex, PalmData& palm); /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); From 146b9958cb0380a0cdefb7e928eb1ca7175cdf10 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 May 2014 09:41:13 -0700 Subject: [PATCH 093/108] Fix style issue --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ab8d69c319..c23804166c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3449,7 +3449,7 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript return scriptEngine; } -void Application::scriptFinished(const QString &scriptName) { +void Application::scriptFinished(const QString& scriptName) { if (_scriptEnginesHash.remove(scriptName)) { _runningScriptsWidget->scriptStopped(scriptName); _runningScriptsWidget->setRunningScripts(getRunningScripts()); From 310f184978b4b396d698e72916b21f3187465ea4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 May 2014 09:53:44 -0700 Subject: [PATCH 094/108] Add getters for onTimeout and onReadyStateChange --- libraries/script-engine/src/XMLHttpRequestClass.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index bff88d91a9..e94c67562a 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -40,8 +40,8 @@ class XMLHttpRequestClass : public QObject { Q_PROPERTY(int DONE READ getDone) // Callbacks - Q_PROPERTY(QScriptValue ontimeout WRITE setOnTimeout) - Q_PROPERTY(QScriptValue onreadystatechange WRITE setOnReadyStateChange) + Q_PROPERTY(QScriptValue ontimeout READ getOnTimeout WRITE setOnTimeout) + Q_PROPERTY(QScriptValue onreadystatechange READ getOnReadyStateChange WRITE setOnReadyStateChange) public: XMLHttpRequestClass(QScriptEngine* engine); ~XMLHttpRequestClass(); @@ -74,7 +74,9 @@ public: QScriptValue getStatus() const; QString getStatusText() const; + QScriptValue getOnTimeout() const { return _onTimeout; } void setOnTimeout(QScriptValue function) { _onTimeout = function; } + QScriptValue getOnReadyStateChange() const { return _onReadyStateChange; } void setOnReadyStateChange(QScriptValue function) { _onReadyStateChange = function; } public slots: From 02676848c605643e46789767a489701e5b8286dd Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 May 2014 09:58:04 -0700 Subject: [PATCH 095/108] Fix order of components in streetAreaExample.js --- examples/streetAreaExample.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/streetAreaExample.js b/examples/streetAreaExample.js index deb10dd65a..b4efd99b70 100644 --- a/examples/streetAreaExample.js +++ b/examples/streetAreaExample.js @@ -33,8 +33,8 @@ if (req.status == 200) { for (var i = 0; i < locations.length; i++) { var loc = locations[i]; var x1 = loc[1], - y1 = loc[2], - x2 = loc[3], + x2 = loc[2], + y1 = loc[3], y2 = loc[4]; userAreas.push({ username: loc[0], From bd56ab911dad4ad92cc1162b602c5e81353d8cba Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 May 2014 10:05:04 -0700 Subject: [PATCH 096/108] Fix bug with js print() breaking on certain characters The message text needs to be escaped before beign evaluated. --- libraries/script-engine/src/ScriptEngine.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index be97b37b46..7b09916a1a 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -49,7 +49,11 @@ static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* eng static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ qDebug() << "script:print()<<" << context->argument(0).toString(); - engine->evaluate("Script.print('" + context->argument(0).toString() + "')"); + QString message = context->argument(0).toString() + .replace("\\", "\\\\") + .replace("\n", "\\n") + .replace("'", "\\'"); + engine->evaluate("Script.print('" + message + "')"); return QScriptValue(); } From c16654628a5530f4051703f739eb95741deaf9a7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 May 2014 10:08:54 -0700 Subject: [PATCH 097/108] Add carriage return to escaped string in print() --- libraries/script-engine/src/ScriptEngine.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 7b09916a1a..3427d0b19d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -52,6 +52,7 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ QString message = context->argument(0).toString() .replace("\\", "\\\\") .replace("\n", "\\n") + .replace("\r", "\\r") .replace("'", "\\'"); engine->evaluate("Script.print('" + message + "')"); return QScriptValue(); From a5c10220e6d62ddcac541893f0209c9be446eb50 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 May 2014 10:16:04 -0700 Subject: [PATCH 098/108] Remove _ prefix from private methods in XMLHttpRequest --- .../script-engine/src/XMLHttpRequestClass.cpp | 34 +++++++++---------- .../script-engine/src/XMLHttpRequestClass.h | 10 +++--- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index 77b03db999..a81f8950fa 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -63,7 +63,7 @@ QString XMLHttpRequestClass::getStatusText() const { } void XMLHttpRequestClass::abort() { - _abortRequest(); + abortRequest(); } void XMLHttpRequestClass::setRequestHeader(const QString& name, const QString& value) { @@ -76,18 +76,18 @@ void XMLHttpRequestClass::requestMetaDataChanged() { // If this is a redirect, abort the current request and start a new one if (redirect.isValid() && _numRedirects < MAXIMUM_REDIRECTS) { _numRedirects++; - _abortRequest(); + abortRequest(); QUrl newUrl = _url.resolved(redirect.toUrl().toString()); _request.setUrl(newUrl); - _doSend(); + doSend(); } } void XMLHttpRequestClass::requestDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { if (_readyState == OPENED && bytesReceived > 0) { - _setReadyState(HEADERS_RECEIVED); - _setReadyState(LOADING); + setReadyState(HEADERS_RECEIVED); + setReadyState(LOADING); } } @@ -113,7 +113,7 @@ QScriptValue XMLHttpRequestClass::getResponseHeader(const QString& name) const { return QScriptValue::NullValue; } -void XMLHttpRequestClass::_setReadyState(ReadyState readyState) { +void XMLHttpRequestClass::setReadyState(ReadyState readyState) { if (readyState != _readyState) { _readyState = readyState; if (_onReadyStateChange.isFunction()) { @@ -135,7 +135,7 @@ void XMLHttpRequestClass::open(const QString& method, const QString& url, bool a } _request.setUrl(_url); _method = method; - _setReadyState(OPENED); + setReadyState(OPENED); } } @@ -150,7 +150,7 @@ void XMLHttpRequestClass::send(const QString& data) { _sendData->setData(data.toUtf8()); } - _doSend(); + doSend(); if (!_async) { QEventLoop loop; @@ -160,10 +160,10 @@ void XMLHttpRequestClass::send(const QString& data) { } } -void XMLHttpRequestClass::_doSend() { +void XMLHttpRequestClass::doSend() { _reply = _manager.sendCustomRequest(_request, _method.toLatin1(), _sendData); - _connectToReply(_reply); + connectToReply(_reply); if (_timeout > 0) { _timer.start(_timeout); @@ -175,9 +175,9 @@ void XMLHttpRequestClass::requestTimeout() { if (_onTimeout.isFunction()) { _onTimeout.call(QScriptValue::NullValue); } - _abortRequest(); + abortRequest(); _errorCode = QNetworkReply::TimeoutError; - _setReadyState(DONE); + setReadyState(DONE); emit requestComplete(); } @@ -203,29 +203,29 @@ void XMLHttpRequestClass::requestFinished() { _responseData = QScriptValue(QString(_rawResponseData.data())); } } - _setReadyState(DONE); + setReadyState(DONE); emit requestComplete(); } -void XMLHttpRequestClass::_abortRequest() { +void XMLHttpRequestClass::abortRequest() { // Disconnect from signals we don't want to receive any longer. disconnect(&_timer, SIGNAL(timeout()), this, SLOT(requestTimeout())); if (_reply) { - _disconnectFromReply(_reply); + disconnectFromReply(_reply); _reply->abort(); delete _reply; _reply = NULL; } } -void XMLHttpRequestClass::_connectToReply(QNetworkReply* reply) { +void XMLHttpRequestClass::connectToReply(QNetworkReply* reply) { connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(requestDownloadProgress(qint64, qint64))); connect(reply, SIGNAL(metaDataChanged()), this, SLOT(requestMetaDataChanged())); } -void XMLHttpRequestClass::_disconnectFromReply(QNetworkReply* reply) { +void XMLHttpRequestClass::disconnectFromReply(QNetworkReply* reply) { disconnect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); disconnect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); disconnect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(requestDownloadProgress(qint64, qint64))); diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index e94c67562a..49a952e638 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -93,11 +93,11 @@ signals: void requestComplete(); private: - void _setReadyState(ReadyState readyState); - void _doSend(); - void _connectToReply(QNetworkReply* reply); - void _disconnectFromReply(QNetworkReply* reply); - void _abortRequest(); + void setReadyState(ReadyState readyState); + void doSend(); + void connectToReply(QNetworkReply* reply); + void disconnectFromReply(QNetworkReply* reply); + void abortRequest(); QScriptEngine* _engine; bool _async; From 5ef71d6a3557715773d2dde964aedb08bb33a158 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 14 May 2014 10:40:50 -0700 Subject: [PATCH 099/108] Full mouse handling --- examples/editModels.js | 121 +++++++++++++++++- .../models/src/ModelsScriptingInterface.cpp | 1 - 2 files changed, 119 insertions(+), 3 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 017e4ba740..70a2e178ae 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -404,8 +404,24 @@ function moveOverlays() { var modelSelected = false; var selectedModelID; var selectedModelProperties; +var mouseLastPosition; +var orientation; +var intersection; + + +var SCALE_FACTOR = 200.0; +var TRANSLATION_FACTOR = 100.0; +var ROTATION_FACTOR = 100.0; + +function rayPlaneIntersection(pickRay, point, normal) { + var d = -Vec3.dot(point, normal); + var t = -(Vec3.dot(pickRay.origin, normal) + d) / Vec3.dot(pickRay.direction, normal); + + return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); +} function mousePressEvent(event) { + mouseLastPosition = { x: event.x, y: event.y }; modelSelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); @@ -461,21 +477,122 @@ function mousePressEvent(event) { modelSelected = true; selectedModelID = foundModels[i]; selectedModelProperties = properties; + + selectedModelProperties.oldRadius = selectedModelProperties.radius; + selectedModelProperties.oldPosition = { + x: selectedModelProperties.position.x, + y: selectedModelProperties.position.y, + z: selectedModelProperties.position.z, + }; + selectedModelProperties.oldRotation = { + x: selectedModelProperties.modelRotation.x, + y: selectedModelProperties.modelRotation.y, + z: selectedModelProperties.modelRotation.z, + w: selectedModelProperties.modelRotation.w, + }; + + + orientation = MyAvatar.orientation; + intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); print("Clicked on " + selectedModelID.id + " " + modelSelected); + return; } } } } +var oldModifier = 0; +var modifier = 0; +var wasShifted = false; function mouseMoveEvent(event) { if (!modelSelected) { return; } - print("Dragging"); + + if (event.isLeftButton) { + if (event.isRightButton) { + modifier = 1; // Scale + } else { + modifier = 2; // Translate + } + } else if (event.isRightButton) { + modifier = 3; // rotate + } else { + modifier = 0; + } + + var pickRay = Camera.computePickRay(event.x, event.y); + if (wasShifted != event.isShifted || modifier != oldModifier) { + selectedModelProperties.oldRadius = selectedModelProperties.radius; + + selectedModelProperties.oldPosition = { + x: selectedModelProperties.position.x, + y: selectedModelProperties.position.y, + z: selectedModelProperties.position.z, + }; + selectedModelProperties.oldRotation = { + x: selectedModelProperties.modelRotation.x, + y: selectedModelProperties.modelRotation.y, + z: selectedModelProperties.modelRotation.z, + w: selectedModelProperties.modelRotation.w, + }; + orientation = MyAvatar.orientation; + intersection = rayPlaneIntersection(pickRay, + selectedModelProperties.oldPosition, + Quat.getFront(orientation)); + + mouseLastPosition = { x: event.x, y: event.y }; + wasShifted = event.isShifted; + oldModifier = modifier; + return; + } - //Model.editModel(selectedModelID, selectedModelProperties); + switch (modifier) { + case 0: + return; + case 1: + // Let's Scale + selectedModelProperties.radius = (selectedModelProperties.oldRadius * + (1.0 + (mouseLastPosition.y - event.y) / SCALE_FACTOR)); + + if (selectedModelProperties.radius < 0.01) { + print("Scale too small ... bailling."); + return; + } + break; + + case 2: + // Let's translate + var newIntersection = rayPlaneIntersection(pickRay, + selectedModelProperties.oldPosition, + Quat.getFront(orientation)); + var vector = Vec3.subtract(newIntersection, intersection) + if (event.isShifted) { + var i = Vec3.dot(vector, Quat.getRight(orientation)); + var j = Vec3.dot(vector, Quat.getUp(orientation)); + vector = Vec3.sum(Vec3.multiply(Quat.getRight(orientation), i), + Vec3.multiply(Quat.getFront(orientation), j)); + } + + selectedModelProperties.position = Vec3.sum(selectedModelProperties.oldPosition, vector); + break; + case 3: + // Let's rotate + var rotation = Quat.fromVec3Degrees({ x: event.y - mouseLastPosition.y, y: event.x - mouseLastPosition.x, z: 0 }); + if (event.isShifted) { + rotation = Quat.fromVec3Degrees({ x: event.y - mouseLastPosition.y, y: 0, z: mouseLastPosition.x - event.x }); + } + + var newRotation = Quat.multiply(orientation, rotation); + newRotation = Quat.multiply(newRotation, Quat.inverse(orientation)); + + selectedModelProperties.modelRotation = Quat.multiply(newRotation, selectedModelProperties.oldRotation); + break; + } + + Models.editModel(selectedModelID, selectedModelProperties); } function scriptEnding() { diff --git a/libraries/models/src/ModelsScriptingInterface.cpp b/libraries/models/src/ModelsScriptingInterface.cpp index 446b0280a4..7625eef998 100644 --- a/libraries/models/src/ModelsScriptingInterface.cpp +++ b/libraries/models/src/ModelsScriptingInterface.cpp @@ -105,7 +105,6 @@ ModelItemID ModelsScriptingInterface::editModel(ModelItemID modelID, const Model _modelTree->updateModel(modelID, properties); _modelTree->unlock(); } - return modelID; } From 53d7aeab4f1b6a38d4820d1d055725522d0b9ea7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 14 May 2014 12:02:38 -0700 Subject: [PATCH 100/108] Fixed typo --- examples/toolBars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/toolBars.js b/examples/toolBars.js index 7f8f09575b..88b07276f0 100644 --- a/examples/toolBars.js +++ b/examples/toolBars.js @@ -1,5 +1,5 @@ // -// testScript.js +// toolBars.js // examples // // Created by ClĂ©ment Brisset on 5/7/14. From 0c34d9e065299e35374a38ad58c2e7a88e627337 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 12:59:59 -0700 Subject: [PATCH 101/108] cleanup remove some debug --- interface/src/models/ModelTreeRenderer.cpp | 6 ---- libraries/models/src/ModelItem.cpp | 35 ++----------------- libraries/models/src/ModelTreeElement.cpp | 1 - .../models/src/ModelsScriptingInterface.h | 17 --------- 4 files changed, 2 insertions(+), 57 deletions(-) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index 36a22621b2..ae60683745 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -183,12 +183,6 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // set the position model->setTranslation(position); - /* - qDebug() << "modelItem.getModelURL()=" << modelItem.getModelURL(); - qDebug() << "modelItem.getAnimationURL()=" << modelItem.getAnimationURL(); - qDebug() << "modelItem.hasAnimation()=" << modelItem.hasAnimation(); - */ - // handle animations.. if (modelItem.hasAnimation()) { if (!modelItem.jointsMapped()) { diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 474a6a38a4..7a4f9e3edc 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -278,8 +278,6 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += animationURLLength; bytesRead += animationURLLength; - qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); - // animationIsPlaying memcpy(&_animationIsPlaying, dataAt, sizeof(_animationIsPlaying)); dataAt += sizeof(_animationIsPlaying); @@ -290,26 +288,16 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += sizeof(_animationFrameIndex); bytesRead += sizeof(_animationFrameIndex); - qDebug() << "readModelDataFromBuffer()... _animationFrameIndex=" << _animationFrameIndex; - // animationFPS memcpy(&_animationFPS, dataAt, sizeof(_animationFPS)); dataAt += sizeof(_animationFPS); bytesRead += sizeof(_animationFPS); - - qDebug() << "readModelDataFromBuffer()... _animationFPS=" << _animationFPS; - - } else { - qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } - - //qDebug() << "ModelItem::readModelDataFromBuffer()... "; debugDump(); } return bytesRead; } ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ModelTree* tree, bool& valid) { - ModelItem newModelItem; // id and _lastUpdated will get set here... const unsigned char* dataAt = data; processedBytes = 0; @@ -419,7 +407,8 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& } // modelRotation - if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_MODEL_ROTATION) == MODEL_PACKET_CONTAINS_MODEL_ROTATION)) { + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_MODEL_ROTATION) == MODEL_PACKET_CONTAINS_MODEL_ROTATION)) { int bytes = unpackOrientationQuatFromBytes(dataAt, newModelItem._modelRotation); dataAt += bytes; processedBytes += bytes; @@ -435,7 +424,6 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& newModelItem._animationURL = tempString; dataAt += animationURLLength; processedBytes += animationURLLength; -qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); } // animationIsPlaying @@ -605,9 +593,6 @@ bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id memcpy(copyAt, qPrintable(properties.getAnimationURL()), urlLength); copyAt += urlLength; sizeOut += urlLength; - -qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable(properties.getAnimationURL()); - } // animationIsPlaying @@ -618,9 +603,6 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable memcpy(copyAt, &animationIsPlaying, sizeof(animationIsPlaying)); copyAt += sizeof(animationIsPlaying); sizeOut += sizeof(animationIsPlaying); - - -qDebug() << "encodeModelItemEditMessageDetails()... animationIsPlaying=" << animationIsPlaying; } // animationFrameIndex @@ -631,8 +613,6 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationIsPlaying=" << anim memcpy(copyAt, &animationFrameIndex, sizeof(animationFrameIndex)); copyAt += sizeof(animationFrameIndex); sizeOut += sizeof(animationFrameIndex); - -qDebug() << "encodeModelItemEditMessageDetails()... animationFrameIndex=" << animationFrameIndex; } // animationFPS @@ -643,8 +623,6 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationFrameIndex=" << ani memcpy(copyAt, &animationFPS, sizeof(animationFPS)); copyAt += sizeof(animationFPS); sizeOut += sizeof(animationFPS); - -qDebug() << "encodeModelItemEditMessageDetails()... animationFPS=" << animationFPS; } bool wantDebugging = false; @@ -1023,7 +1001,6 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { if (_defaultSettings || newFPS != _animationFPS) { _animationFPS = newFPS; _animationFPSChanged = true; -qDebug() << "ModelItemProperties::copyFromScriptValue()... _animationFPS=" << _animationFPS; } } @@ -1065,29 +1042,21 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { if (_animationURLChanged) { modelItem.setAnimationURL(_animationURL); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; } if (_animationIsPlayingChanged) { modelItem.setAnimationIsPlaying(_animationIsPlaying); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... _animationIsPlaying=" << _animationIsPlaying; } if (_animationFrameIndexChanged) { modelItem.setAnimationFrameIndex(_animationFrameIndex); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... _animationFrameIndex=" << _animationFrameIndex; } if (_animationFPSChanged) { modelItem.setAnimationFPS(_animationFPS); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... _animationFPS=" << _animationFPS; } if (somethingChanged) { diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index e8b37c478b..c5dce04fe2 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -102,7 +102,6 @@ bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const { } void ModelTreeElement::update(ModelTreeUpdateArgs& args) { -//qDebug() << "ModelTreeElement::update()..."; // update our contained models QList::iterator modelItr = _modelItems->begin(); while(modelItr != _modelItems->end()) { diff --git a/libraries/models/src/ModelsScriptingInterface.h b/libraries/models/src/ModelsScriptingInterface.h index 8447e5018d..bf8e193f25 100644 --- a/libraries/models/src/ModelsScriptingInterface.h +++ b/libraries/models/src/ModelsScriptingInterface.h @@ -59,23 +59,6 @@ public slots: /// this function will not find any models in script engine contexts which don't have access to models QVector findModels(const glm::vec3& center, float radius) const; - /* - /// pauses the model animation. - ModelItemID pauseModelAnimation(ModelItemID modelID); - - /// plays the model animation. - ModelItemID playModelAnimation(ModelItemID modelID); - */ - - - /* - /// gets the current frame of the model animation. - float getModelAnimationFrame(ModelItemID modelID); - - /// gets the current frame of the model animation. - void setModelAnimationFrame(ModelItemID modelID, float frame); - */ - signals: void modelCollisionWithVoxel(const ModelItemID& modelID, const VoxelDetail& voxel, const CollisionInfo& collision); void modelCollisionWithModel(const ModelItemID& idA, const ModelItemID& idB, const CollisionInfo& collision); From 7c44162209ffc816108c5180e428587e7b4fc105 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 13:00:35 -0700 Subject: [PATCH 102/108] animated model example --- examples/animatedModelExample.js | 131 +++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 examples/animatedModelExample.js diff --git a/examples/animatedModelExample.js b/examples/animatedModelExample.js new file mode 100644 index 0000000000..150ca36bcd --- /dev/null +++ b/examples/animatedModelExample.js @@ -0,0 +1,131 @@ +// +// animatedModelExample.js +// examples +// +// Created by Brad Hefta-Gaub on 12/31/13. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example script that demonstrates creating and editing a model +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var count = 0; +var moveUntil = 6000; +var stopAfter = moveUntil + 100; + +var pitch = 0.0; +var yaw = 0.0; +var roll = 0.0; +var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll) + +var originalProperties = { + position: { x: 10, + y: 0, + z: 0 }, + + radius : 1, + + color: { red: 0, + green: 255, + blue: 0 }, + + modelURL: "http://www.fungibleinsight.com/faces/beta.fst", + modelRotation: rotation, + animationURL: "http://www.fungibleinsight.com/faces/gangnam_style_2.fbx", + animationIsPlaying: true, +}; + +var positionDelta = { x: 0, y: 0, z: 0 }; + + +var modelID = Models.addModel(originalProperties); +print("Models.addModel()... modelID.creatorTokenID = " + modelID.creatorTokenID); + +var isPlaying = true; +var playPauseEveryWhile = 360; +var animationFPS = 30; +var adjustFPSEveryWhile = 120; + +function moveModel(deltaTime) { + +//print("count =" + count); + +print("(count % playPauseEveryWhile)=" + (count % playPauseEveryWhile)); + + if (count % playPauseEveryWhile == 0) { + isPlaying = !isPlaying; + print("isPlaying=" + isPlaying); + } + +print("(count % adjustFPSEveryWhile)=" + (count % adjustFPSEveryWhile)); + + if (count % adjustFPSEveryWhile == 0) { + +print("considering adjusting animationFPS=" + animationFPS); + + if (animationFPS == 30) { + animationFPS = 10; + } else if (animationFPS == 10) { + animationFPS = 60; + } else if (animationFPS == 60) { + animationFPS = 30; + } + print("animationFPS=" + animationFPS); + isPlaying = true; + print("always start playing if we change the FPS -- isPlaying=" + isPlaying); + } + + if (count >= moveUntil) { + + // delete it... + if (count == moveUntil) { + print("calling Models.deleteModel()"); + Models.deleteModel(modelID); + } + + // stop it... + if (count >= stopAfter) { + print("calling Script.stop()"); + Script.stop(); + } + + count++; + return; // break early + } + + count++; + + //print("modelID.creatorTokenID = " + modelID.creatorTokenID); + + if (true) { + var newProperties = { + //position: { + // x: originalProperties.position.x + (count * positionDelta.x), + // y: originalProperties.position.y + (count * positionDelta.y), + // z: originalProperties.position.z + (count * positionDelta.z) + //}, + animationIsPlaying: isPlaying, + animationFPS: animationFPS, + }; + + + //print("modelID = " + modelID); + //print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z); + + Models.editModel(modelID, newProperties); + } +} + + +// register the call back so it fires before each data send +Script.update.connect(moveModel); + + +Script.scriptEnding.connect(function () { + print("cleaning up..."); + print("modelID="+ modelID.creatorTokenID + ", id:" + modelID.id); + Models.deleteModel(modelID); +}); + From 0a8dc78c963d3796041c3d50ee819ec7f2b1dc3f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 13:08:32 -0700 Subject: [PATCH 103/108] tweaks to animation script example --- examples/animatedModelExample.js | 39 +++++++++++++------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/examples/animatedModelExample.js b/examples/animatedModelExample.js index 150ca36bcd..5199eb419f 100644 --- a/examples/animatedModelExample.js +++ b/examples/animatedModelExample.js @@ -37,9 +37,6 @@ var originalProperties = { animationIsPlaying: true, }; -var positionDelta = { x: 0, y: 0, z: 0 }; - - var modelID = Models.addModel(originalProperties); print("Models.addModel()... modelID.creatorTokenID = " + modelID.creatorTokenID); @@ -47,24 +44,17 @@ var isPlaying = true; var playPauseEveryWhile = 360; var animationFPS = 30; var adjustFPSEveryWhile = 120; +var resetFrameEveryWhile = 600; function moveModel(deltaTime) { - -//print("count =" + count); - -print("(count % playPauseEveryWhile)=" + (count % playPauseEveryWhile)); - + var somethingChanged = false; if (count % playPauseEveryWhile == 0) { isPlaying = !isPlaying; print("isPlaying=" + isPlaying); + somethingChanged = true; } -print("(count % adjustFPSEveryWhile)=" + (count % adjustFPSEveryWhile)); - if (count % adjustFPSEveryWhile == 0) { - -print("considering adjusting animationFPS=" + animationFPS); - if (animationFPS == 30) { animationFPS = 10; } else if (animationFPS == 10) { @@ -75,6 +65,12 @@ print("considering adjusting animationFPS=" + animationFPS); print("animationFPS=" + animationFPS); isPlaying = true; print("always start playing if we change the FPS -- isPlaying=" + isPlaying); + somethingChanged = true; + } + + if (count % resetFrameEveryWhile == 0) { + resetFrame = true; + somethingChanged = true; } if (count >= moveUntil) { @@ -99,20 +95,17 @@ print("considering adjusting animationFPS=" + animationFPS); //print("modelID.creatorTokenID = " + modelID.creatorTokenID); - if (true) { + if (somethingChanged) { var newProperties = { - //position: { - // x: originalProperties.position.x + (count * positionDelta.x), - // y: originalProperties.position.y + (count * positionDelta.y), - // z: originalProperties.position.z + (count * positionDelta.z) - //}, animationIsPlaying: isPlaying, animationFPS: animationFPS, }; - - - //print("modelID = " + modelID); - //print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z); + + if (resetFrame) { + print("resetting the frame!"); + newProperties.animationFrameIndex = 0; + resetFrame = false; + } Models.editModel(modelID, newProperties); } From fa20e8ff950e6bd8453b96abe138c558555cadee Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 13:17:10 -0700 Subject: [PATCH 104/108] fix the agent support for scripting models --- assignment-client/src/Agent.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 9b2134ba44..e6c14d06da 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -250,6 +250,12 @@ void Agent::run() { _particleViewer.init(); _scriptEngine.getParticlesScriptingInterface()->setParticleTree(_particleViewer.getTree()); + _scriptEngine.registerGlobalObject("ModelViewer", &_modelViewer); + JurisdictionListener* modelJL = _scriptEngine.getModelsScriptingInterface()->getJurisdictionListener(); + _modelViewer.setJurisdictionListener(modelJL); + _modelViewer.init(); + _scriptEngine.getModelsScriptingInterface()->setModelTree(_modelViewer.getTree()); + _scriptEngine.setScriptContents(scriptContents); _scriptEngine.run(); setFinished(true); From cf230d1cc33ecc92153df953ac18ce789f9608ed Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 14 May 2014 13:36:26 -0700 Subject: [PATCH 105/108] PrioVR tweaks. --- interface/src/avatar/MyAvatar.cpp | 4 +++- interface/src/avatar/SkeletonModel.cpp | 2 +- interface/src/devices/PrioVR.cpp | 10 ++++++++-- interface/src/devices/Visage.cpp | 3 ++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 014d53520e..5ef4b6ecc0 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -239,7 +239,9 @@ void MyAvatar::updateFromTrackers(float deltaTime) { if (Application::getInstance()->getPrioVR()->isActive()) { estimatedRotation = glm::degrees(safeEulerAngles(Application::getInstance()->getPrioVR()->getHeadRotation())); - + estimatedRotation.x *= -1.0f; + estimatedRotation.z *= -1.0f; + } else { FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); if (tracker) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 5c017cd094..e48ebfa63c 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -200,7 +200,7 @@ void SkeletonModel::updateJointState(int index) { } void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { - if (!_owningAvatar->isMyAvatar()) { + if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) { return; } // get the rotation axes in joint space and use them to adjust the rotation diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index adda3b5cb2..605ace5c9b 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -22,8 +22,8 @@ const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000 const unsigned char AXIS_LIST[] = { 9, 43, 37, 37, 37, 13, 13, 13, 52, 52, 28, 28 }; const int LIST_LENGTH = sizeof(SERIAL_LIST) / sizeof(SERIAL_LIST[0]); -const char* JOINT_NAMES[] = { "Head", "Spine", "LeftArm", "LeftForeArm", "LeftHand", "RightArm", - "RightForeArm", "RightHand", "LeftUpLeg", "LeftLeg", "RightUpLeft", "RightLeg" }; +const char* JOINT_NAMES[] = { "Neck", "Spine", "LeftArm", "LeftForeArm", "LeftHand", "RightArm", + "RightForeArm", "RightHand", "LeftUpLeg", "LeftLeg", "RightUpLeg", "RightLeg" }; #ifdef HAVE_PRIOVR static int indexOfHumanIKJoint(const char* jointName) { @@ -80,6 +80,12 @@ void PrioVR::update() { unsigned int timestamp; yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), _jointRotations.size() * sizeof(glm::quat), ×tamp); + + // convert to our expected coordinate system + for (int i = 0; i < _jointRotations.size(); i++) { + _jointRotations[i].y *= -1.0f; + _jointRotations[i].z *= -1.0f; + } #endif } diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index 8173519478..7c8c3ae89e 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -55,7 +55,8 @@ Visage::Visage() : Visage::~Visage() { #ifdef HAVE_VISAGE _tracker->stop(); - delete _tracker; + // deleting the tracker crashes windows; disable for now + //delete _tracker; delete _data; #endif } From 4cbe8a41dba751095b0f58e6bd643fdf29bb8950 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 14 May 2014 13:40:03 -0700 Subject: [PATCH 106/108] Add PrioVR to .gitignore. --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index c296a918af..8d537b993f 100644 --- a/.gitignore +++ b/.gitignore @@ -46,5 +46,9 @@ interface/resources/visage/* interface/external/faceplus/* !interface/external/faceplus/readme.txt +# Ignore PrioVR +interface/external/priovr/* +!interface/external/priovr/readme.txt + # Ignore interfaceCache for Linux users interface/interfaceCache/ From 95c05a720dd6fd23799fe95285fdad31bb346a41 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 14 May 2014 13:48:03 -0700 Subject: [PATCH 107/108] Tabs -> spaces. --- interface/src/avatar/MyAvatar.cpp | 4 ++-- interface/src/devices/PrioVR.cpp | 12 ++++++------ interface/src/devices/Visage.cpp | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5ef4b6ecc0..1eac264ae4 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -239,8 +239,8 @@ void MyAvatar::updateFromTrackers(float deltaTime) { if (Application::getInstance()->getPrioVR()->isActive()) { estimatedRotation = glm::degrees(safeEulerAngles(Application::getInstance()->getPrioVR()->getHeadRotation())); - estimatedRotation.x *= -1.0f; - estimatedRotation.z *= -1.0f; + estimatedRotation.x *= -1.0f; + estimatedRotation.z *= -1.0f; } else { FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index 605ace5c9b..064e2be4b5 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -80,12 +80,12 @@ void PrioVR::update() { unsigned int timestamp; yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), _jointRotations.size() * sizeof(glm::quat), ×tamp); - - // convert to our expected coordinate system - for (int i = 0; i < _jointRotations.size(); i++) { - _jointRotations[i].y *= -1.0f; - _jointRotations[i].z *= -1.0f; - } + + // convert to our expected coordinate system + for (int i = 0; i < _jointRotations.size(); i++) { + _jointRotations[i].y *= -1.0f; + _jointRotations[i].z *= -1.0f; + } #endif } diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index 7c8c3ae89e..119d89654a 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -56,7 +56,7 @@ Visage::~Visage() { #ifdef HAVE_VISAGE _tracker->stop(); // deleting the tracker crashes windows; disable for now - //delete _tracker; + //delete _tracker; delete _data; #endif } From f6c1d3e635b2e7d26175c25153a06ac148876e2e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 14:00:31 -0700 Subject: [PATCH 108/108] clean up animations --- libraries/models/src/ModelItem.cpp | 19 ++++++++++++++++++- libraries/models/src/ModelItem.h | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 7a4f9e3edc..c04f9a76ae 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -671,9 +671,26 @@ void ModelItem::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssi } -QMap ModelItem::_loadedAnimations; // TODO: cleanup?? +QMap ModelItem::_loadedAnimations; // TODO: improve cleanup by leveraging the AnimationPointer(s) AnimationCache ModelItem::_animationCache; +// This class/instance will cleanup the animations once unloaded. +class ModelAnimationsBookkeeper { +public: + ~ModelAnimationsBookkeeper() { + ModelItem::cleanupLoadedAnimations(); + } +}; + +ModelAnimationsBookkeeper modelAnimationsBookkeeperInstance; + +void ModelItem::cleanupLoadedAnimations() { + foreach(AnimationPointer animation, _loadedAnimations) { + animation.clear(); + } + _loadedAnimations.clear(); +} + Animation* ModelItem::getAnimation(const QString& url) { AnimationPointer animation; diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 18074fbe13..847e58e7c2 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -278,6 +278,8 @@ public: bool getAnimationIsPlaying() const { return _animationIsPlaying; } float getAnimationFrameIndex() const { return _animationFrameIndex; } float getAnimationFPS() const { return _animationFPS; } + + static void cleanupLoadedAnimations(); protected: glm::vec3 _position;