Merge branch 'master' into feature/secondary-pose-support

This commit is contained in:
Anthony J. Thibault 2017-07-31 10:55:40 -07:00
commit 2383933252
45 changed files with 981 additions and 307 deletions

View file

@ -1,28 +1,25 @@
### Dependencies
* [cmake](https://cmake.org/download/) ~> 3.3.2
* [Qt](https://www.qt.io/download-open-source) ~> 5.6.2
* [OpenSSL](https://www.openssl.org/community/binaries.html)
* IMPORTANT: Use the latest available version of OpenSSL to avoid security vulnerabilities.
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
- [cmake](https://cmake.org/download/): 3.9
- [Qt](https://www.qt.io/download-open-source): 5.9.1
- [OpenSSL](https://www.openssl.org/): Use the latest available version of OpenSSL to avoid security vulnerabilities.
- [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
#### CMake External Project Dependencies
### CMake External Project Dependencies
* [boostconfig](https://github.com/boostorg/config) ~> 1.58
* [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases) ~> 2.83
* [GLEW](http://glew.sourceforge.net/)
* [glm](https://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.4
* [gverb](https://github.com/highfidelity/gverb)
* [Oculus SDK](https://developer.oculus.com/downloads/) ~> 0.6 (Win32) / 0.5 (Mac / Linux)
* [oglplus](http://oglplus.org/) ~> 0.63
* [OpenVR](https://github.com/ValveSoftware/openvr) ~> 0.91 (Win32 only)
* [Polyvox](http://www.volumesoffun.com/) ~> 0.2.1
* [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/) ~> 0.7.1
* [SDL2](https://www.libsdl.org/download-2.0.php) ~> 2.0.3
* [soxr](https://sourceforge.net/p/soxr/wiki/Home/) ~> 0.1.1
* [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3
* [Sixense](http://sixense.com/) ~> 071615
* [zlib](http://www.zlib.net/) ~> 1.28 (Win32 only)
These dependencies need not be installed manually. They are automatically downloaded on the platforms where they are required.
- [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases): 2.83
- [GLEW](http://glew.sourceforge.net/): 1.13
- [glm](https://glm.g-truc.net/0.9.8/index.html): 0.9.8
- [Oculus SDK](https://developer.oculus.com/downloads/): 1.11 (Win32) / 0.5 (Mac)
- [OpenVR](https://github.com/ValveSoftware/openvr): 1.0.6 (Win32 only)
- [Polyvox](http://www.volumesoffun.com/): 0.2.1
- [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/): 0.7.3
- [SDL2](https://www.libsdl.org/download-2.0.php): 2.0.3
- [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/): 4.3
- [Sixense](http://sixense.com/): 071615
- [zlib](http://www.zlib.net/): 1.28 (Win32 only)
- nVidia Texture Tools: 2.1
The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build/ext` folder in each of the subfolders for each external project.

View file

@ -13,7 +13,7 @@ setup_memory_debugger()
link_hifi_libraries(
audio avatars octree gpu model fbx entities
networking animation recording shared script-engine embedded-webserver
physics plugins
controllers physics plugins midi
)
if (WIN32)

View file

@ -49,7 +49,7 @@
Var STR_CONTAINS_VAR_3
Var STR_CONTAINS_VAR_4
Var STR_RETURN_VAR
Function StrContains
Exch $STR_NEEDLE
Exch 1
@ -343,22 +343,29 @@ SectionEnd
;--------------------------------
;Pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@"
Page custom InstallTypesPage ReadInstallTypes
!define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction
!insertmacro MUI_PAGE_DIRECTORY
;Start Menu Folder Page Configuration
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
!define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
!define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction
@CPACK_NSIS_PAGE_COMPONENTS@
Page custom PostInstallOptionsPage ReadPostInstallOptions
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
@ -442,6 +449,10 @@ Var CleanInstallCheckbox
Var CurrentOffset
Var OffsetUnits
Var CopyFromProductionCheckbox
Var ExpressInstallRadioButton
Var CustomInstallRadioButton
Var InstallTypeDialog
Var Express
!macro SetPostInstallOption Checkbox OptionName Default
; reads the value for the given post install option to the registry
@ -459,6 +470,60 @@ Var CopyFromProductionCheckbox
${EndIf}
!macroend
Function InstallTypesPage
!insertmacro MUI_HEADER_TEXT "Choose Installation Type" "Express or Custom Install"
nsDialogs::Create 1018
Pop $InstallTypeDialog
${If} $InstallTypeDialog == error
Abort
${EndIf}
StrCpy $CurrentOffset 0
StrCpy $OffsetUnits u
StrCpy $Express "0"
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls High Fidelity Interface and High Fidelity Sandbox"
pop $ExpressInstallRadioButton
${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel
IntOp $CurrentOffset $CurrentOffset + 15
${NSD_CreateRadiobutton} 30% $CurrentOffset$OffsetUnits 100% 10u "Custom Install (Advanced)"
pop $CustomInstallRadioButton
${NSD_OnClick} $CustomInstallRadioButton ChangeCustomLabel
${EndIf}
; Express Install selected by default
${NSD_Check} $ExpressInstallRadioButton
Call ChangeExpressLabel
nsDialogs::Show
FunctionEnd
Function ChangeExpressLabel
Push $R1
GetDlgItem $R1 $HWNDPARENT 1
SendMessage $R1 ${WM_SETTEXT} 0 "STR:Install"
Pop $R1
FunctionEnd
Function ChangeCustomLabel
Push $R1
GetDlgItem $R1 $HWNDPARENT 1
SendMessage $R1 ${WM_SETTEXT} 0 "STR:Next >"
Pop $R1
FunctionEnd
Function AbortFunction
; Check if Express is set, if so, abort the post install options page
Call HandleInstallTypes ; Sets Express if ExpressInstallRadioButton is checked and installs with defaults
StrCmp $Express "1" 0 end
Abort
end:
FunctionEnd
Function PostInstallOptionsPage
!insertmacro MUI_HEADER_TEXT "Setup Options" ""
@ -549,9 +614,15 @@ Function PostInstallOptionsPage
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Copy settings and content from production install"
Pop $CopyFromProductionCheckbox
${NSD_SetState} $CopyFromProductionCheckbox ${BST_CHECKED}
${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED}
${EndIf}
; Check if Express is set, if so, abort the post install options page
Call HandleInstallTypes ; Sets Express if ExpressInstallRadioButton is checked and installs with defaults
StrCmp $Express "1" 0 end
Abort
end:
nsDialogs::Show
FunctionEnd
@ -567,6 +638,16 @@ Var LaunchServerNowState
Var LaunchClientNowState
Var CopyFromProductionState
Var CleanInstallState
Var ExpressInstallState
Var CustomInstallState
Function ReadInstallTypes
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
; check if the user asked for express/custom install
${NSD_GetState} $ExpressInstallRadioButton $ExpressInstallState
${NSD_GetState} $CustomInstallRadioButton $CustomInstallState
${EndIf}
FunctionEnd
Function ReadPostInstallOptions
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
@ -603,6 +684,28 @@ Function ReadPostInstallOptions
${EndIf}
FunctionEnd
Function HandleInstallTypes
${If} $ExpressInstallState == ${BST_CHECKED}
StrCpy $Express "1"
; over ride custom checkboxes and select defaults
${NSD_SetState} $DesktopClientCheckbox ${BST_CHECKED}
${NSD_SetState} $ServerStartupCheckbox ${BST_CHECKED}
${NSD_SetState} $LaunchServerNowCheckbox ${BST_CHECKED}
${NSD_SetState} $LaunchClientNowCheckbox ${BST_CHECKED}
${If} @PR_BUILD@ == 1
${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED}
${EndIf}
; call ReadPostInstallOptions and HandlePostInstallOptions with defaults selected
Call ReadPostInstallOptions
Call HandlePostInstallOptions
${EndIf}
FunctionEnd
Function HandlePostInstallOptions
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
; check if the user asked for a desktop shortcut to High Fidelity
@ -624,6 +727,7 @@ Function HandlePostInstallOptions
!insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO
${EndIf}
; check if the user asked to have Sandbox launched every startup
${If} $ServerStartupState == ${BST_CHECKED}
; in case we added a shortcut in the global context, pull that now

View file

@ -195,7 +195,7 @@ link_hifi_libraries(
shared octree ktx gpu gl gpu-gl procedural model render
recording fbx networking model-networking entities avatars trackers
audio audio-client animation script-engine physics
render-utils entities-renderer avatars-renderer ui auto-updater
render-utils entities-renderer avatars-renderer ui auto-updater midi
controllers plugins image trackers
ui-plugins display-plugins input-plugins
${NON_ANDROID_LIBRARIES}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

View file

@ -28,6 +28,7 @@ ScrollingWindow {
minSize: Qt.vector2d(200, 300)
property int colorScheme: hifi.colorSchemes.dark
property int selectionMode: SelectionMode.ExtendedSelection
HifiConstants { id: hifi }
@ -35,7 +36,8 @@ ScrollingWindow {
property var assetProxyModel: Assets.proxyModel;
property var assetMappingsModel: Assets.mappingModel;
property var currentDirectory;
property var selectedItems: treeView.selection.selectedIndexes.length;
Settings {
category: "Overlay.AssetServer"
property alias x: root.x
@ -48,7 +50,7 @@ ScrollingWindow {
assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError);
reload();
}
function doDeleteFile(path) {
console.log("Deleting " + path);
@ -118,11 +120,23 @@ ScrollingWindow {
function canAddToWorld(path) {
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i];
if (selectedItems > 1) {
return false;
}
return supportedExtensions.reduce(function(total, current) {
return total | new RegExp(current).test(path);
}, false);
}
function canRename() {
if (treeView.selection.hasSelection && selectedItems == 1) {
return true;
} else {
return false;
}
}
function clear() {
Assets.mappingModel.clear();
@ -299,23 +313,37 @@ ScrollingWindow {
});
}
function deleteFile(index) {
var path = [];
if (!index) {
index = treeView.selection.currentIndex;
for (var i = 0; i < selectedItems; i++) {
treeView.selection.setCurrentIndex(treeView.selection.selectedIndexes[i], 0x100);
index = treeView.selection.currentIndex;
path[i] = assetProxyModel.data(index, 0x100);
}
}
var path = assetProxyModel.data(index, 0x100);
if (!path) {
return;
}
var modalMessage = "";
var items = selectedItems.toString();
var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101);
var typeString = isFolder ? 'folder' : 'file';
if (selectedItems > 1) {
modalMessage = "You are about to delete " + items + " items \nDo you want to continue?";
} else {
modalMessage = "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?";
}
var object = desktop.messageBox({
icon: hifi.icons.question,
buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No,
defaultButton: OriginalDialogs.StandardButton.Yes,
title: "Delete",
text: "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?"
text: modalMessage
});
object.selected.connect(function(button) {
if (button === OriginalDialogs.StandardButton.Yes) {
@ -455,20 +483,20 @@ ScrollingWindow {
color: hifi.buttons.black
colorScheme: root.colorScheme
width: 120
enabled: canAddToWorld(assetProxyModel.data(treeView.selection.currentIndex, 0x100))
onClicked: root.addToWorld()
}
HifiControls.Button {
text: "Rename"
color: hifi.buttons.black
colorScheme: root.colorScheme
width: 80
onClicked: root.renameFile()
enabled: treeView.selection.hasSelection
enabled: canRename()
}
HifiControls.Button {
@ -524,6 +552,7 @@ ScrollingWindow {
treeModel: assetProxyModel
canEdit: true
colorScheme: root.colorScheme
selectionMode: SelectionMode.ExtendedSelection
modifyEl: renameEl

View file

@ -37,6 +37,7 @@ Rectangle {
property var assetProxyModel: Assets.proxyModel;
property var assetMappingsModel: Assets.mappingModel;
property var currentDirectory;
property var selectedItems: treeView.selection.selectedIndexes.length;
Settings {
category: "Overlay.AssetServer"
@ -119,11 +120,23 @@ Rectangle {
function canAddToWorld(path) {
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i];
if (selectedItems > 1) {
return false;
}
return supportedExtensions.reduce(function(total, current) {
return total | new RegExp(current).test(path);
}, false);
}
function canRename() {
if (treeView.selection.hasSelection && selectedItems == 1) {
return true;
} else {
return false;
}
}
function clear() {
Assets.mappingModel.clear();
@ -300,23 +313,37 @@ Rectangle {
});
}
function deleteFile(index) {
var path = [];
if (!index) {
index = treeView.selection.currentIndex;
for (var i = 0; i < selectedItems; i++) {
treeView.selection.setCurrentIndex(treeView.selection.selectedIndexes[i], 0x100);
index = treeView.selection.currentIndex;
path[i] = assetProxyModel.data(index, 0x100);
}
}
var path = assetProxyModel.data(index, 0x100);
if (!path) {
return;
}
var modalMessage = "";
var items = selectedItems.toString();
var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101);
var typeString = isFolder ? 'folder' : 'file';
if (selectedItems > 1) {
modalMessage = "You are about to delete " + items + " items \nDo you want to continue?";
} else {
modalMessage = "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?";
}
var object = tabletRoot.messageBox({
icon: hifi.icons.question,
buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No,
defaultButton: OriginalDialogs.StandardButton.Yes,
title: "Delete",
text: "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?"
text: modalMessage
});
object.selected.connect(function(button) {
if (button === OriginalDialogs.StandardButton.Yes) {
@ -469,7 +496,7 @@ Rectangle {
width: 80
onClicked: root.renameFile()
enabled: treeView.selection.hasSelection
enabled: canRename()
}
HifiControls.Button {
@ -525,6 +552,7 @@ Rectangle {
treeModel: assetProxyModel
canEdit: true
colorScheme: root.colorScheme
selectionMode: SelectionMode.ExtendedSelection
modifyEl: renameEl

View file

@ -61,6 +61,7 @@
#include <AssetClient.h>
#include <AssetUpload.h>
#include <AutoUpdater.h>
#include <Midi.h>
#include <AudioInjectorManager.h>
#include <AvatarBookmarks.h>
#include <CursorManager.h>
@ -398,6 +399,10 @@ public:
return true;
}
}
if (message->message == WM_DEVICECHANGE) {
Midi::USBchanged(); // re-scan the MIDI bus
}
}
return false;
}
@ -567,6 +572,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<SceneScriptingInterface>();
DependencyManager::set<OffscreenUi>();
DependencyManager::set<AutoUpdater>();
DependencyManager::set<Midi>();
DependencyManager::set<PathUtils>();
DependencyManager::set<InterfaceDynamicFactory>();
DependencyManager::set<AudioInjectorManager>();
@ -618,6 +624,7 @@ const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f;
const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;
const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false;
const QString DEFAULT_CURSOR_NAME = "DEFAULT";
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runningMarkerExisted) :
QApplication(argc, argv),
@ -637,6 +644,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR),
_preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS),
_constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true),
_preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME),
_scaleMirror(1.0f),
_rotateMirror(0.0f),
_raiseMirror(0.0f),
@ -932,14 +940,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_glWidget->setFocusPolicy(Qt::StrongFocus);
_glWidget->setFocus();
#ifdef Q_OS_MAC
auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget
#else
// On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
// window menu, which is a pain, so only hide it for the GL surface
auto cursorTarget = _glWidget;
#endif
cursorTarget->setCursor(Qt::BlankCursor);
showCursor(Cursor::Manager::lookupIcon(_preferredCursor.get()));
// enable mouse tracking; otherwise, we only get drag events
_glWidget->setMouseTracking(true);
@ -1737,9 +1738,16 @@ void Application::checkChangeCursor() {
}
}
void Application::showCursor(const QCursor& cursor) {
void Application::showCursor(const Cursor::Icon& cursor) {
QMutexLocker locker(&_changeCursorLock);
_desiredCursor = cursor;
auto managedCursor = Cursor::Manager::instance().getCursor();
auto curIcon = managedCursor->getIcon();
if (curIcon != cursor) {
managedCursor->setIcon(cursor);
curIcon = cursor;
}
_desiredCursor = cursor == Cursor::Icon::SYSTEM ? Qt::ArrowCursor : Qt::BlankCursor;
_cursorNeedsChanging = true;
}
@ -2114,7 +2122,6 @@ void Application::initializeUi() {
surfaceContext->setContextProperty("AvatarManager", DependencyManager::get<AvatarManager>().data());
surfaceContext->setContextProperty("UndoStack", &_undoStackScriptingInterface);
surfaceContext->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
surfaceContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());
surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
surfaceContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
surfaceContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
@ -2160,9 +2167,11 @@ void Application::initializeUi() {
_window->setMenuBar(new Menu());
auto compositorHelper = DependencyManager::get<CompositorHelper>();
connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, [=] {
connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, this, [=] {
if (isHMDMode()) {
showCursor(compositorHelper->getAllowMouseCapture() ? Qt::BlankCursor : Qt::ArrowCursor);
showCursor(compositorHelper->getAllowMouseCapture() ?
Cursor::Manager::lookupIcon(_preferredCursor.get()) :
Cursor::Icon::SYSTEM);
}
});
@ -2504,6 +2513,12 @@ void Application::setPreferAvatarFingerOverStylus(bool value) {
_preferAvatarFingerOverStylusSetting.set(value);
}
void Application::setPreferredCursor(const QString& cursorName) {
qCDebug(interfaceapp) << "setPreferredCursor" << cursorName;
_preferredCursor.set(cursorName.isEmpty() ? DEFAULT_CURSOR_NAME : cursorName);
showCursor(Cursor::Manager::lookupIcon(_preferredCursor.get()));
}
void Application::setSettingConstrainToolbarPosition(bool setting) {
_constrainToolbarPosition.set(setting);
DependencyManager::get<OffscreenUi>()->setConstrainToolbarToCenterX(setting);
@ -3020,7 +3035,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_F: {
_physicsEngine->dumpNextStats();
if (isOption) {
_physicsEngine->dumpNextStats();
}
break;
}
@ -3064,9 +3081,13 @@ void Application::keyPressEvent(QKeyEvent* event) {
auto cursor = Cursor::Manager::instance().getCursor();
auto curIcon = cursor->getIcon();
if (curIcon == Cursor::Icon::DEFAULT) {
cursor->setIcon(Cursor::Icon::LINK);
showCursor(Cursor::Icon::RETICLE);
} else if (curIcon == Cursor::Icon::RETICLE) {
showCursor(Cursor::Icon::SYSTEM);
} else if (curIcon == Cursor::Icon::SYSTEM) {
showCursor(Cursor::Icon::LINK);
} else {
cursor->setIcon(Cursor::Icon::DEFAULT);
showCursor(Cursor::Icon::DEFAULT);
}
} else {
resetSensors(true);
@ -4459,27 +4480,31 @@ void Application::cameraModeChanged() {
void Application::cameraMenuChanged() {
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
auto menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
if (isHMDMode()) {
menu->setIsOptionChecked(MenuOption::FullscreenMirror, false);
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
} else if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
_myCamera.setMode(CAMERA_MODE_MIRROR);
}
} else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
} else if (menu->isOptionChecked(MenuOption::FirstPerson)) {
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN);
}
} else if (Menu::getInstance()->isOptionChecked(MenuOption::ThirdPerson)) {
} else if (menu->isOptionChecked(MenuOption::ThirdPerson)) {
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
if (getMyAvatar()->getBoomLength() == MyAvatar::ZOOM_MIN) {
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT);
}
}
} else if (Menu::getInstance()->isOptionChecked(MenuOption::IndependentMode)) {
} else if (menu->isOptionChecked(MenuOption::IndependentMode)) {
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
_myCamera.setMode(CAMERA_MODE_INDEPENDENT);
}
} else if (Menu::getInstance()->isOptionChecked(MenuOption::CameraEntityMode)) {
} else if (menu->isOptionChecked(MenuOption::CameraEntityMode)) {
if (_myCamera.getMode() != CAMERA_MODE_ENTITY) {
_myCamera.setMode(CAMERA_MODE_ENTITY);
}

View file

@ -55,6 +55,7 @@
#include "BandwidthRecorder.h"
#include "FancyCamera.h"
#include "ConnectionMonitor.h"
#include "CursorManager.h"
#include "gpu/Context.h"
#include "Menu.h"
#include "octree/OctreePacketProcessor.h"
@ -165,7 +166,7 @@ public:
QSize getDeviceSize() const;
bool hasFocus() const;
void showCursor(const QCursor& cursor);
void showCursor(const Cursor::Icon& cursor);
bool isThrottleRendering() const;
@ -400,6 +401,9 @@ public slots:
void loadDomainConnectionDialog();
void showScriptLogs();
const QString getPreferredCursor() const { return _preferredCursor.get(); }
void setPreferredCursor(const QString& cursor);
private slots:
void showDesktop();
void clearDomainOctreeDetails();
@ -564,6 +568,7 @@ private:
Setting::Handle<bool> _hmdTabletBecomesToolbarSetting;
Setting::Handle<bool> _preferAvatarFingerOverStylusSetting;
Setting::Handle<bool> _constrainToolbarPosition;
Setting::Handle<QString> _preferredCursor;
float _scaleMirror;
float _rotateMirror;
@ -637,7 +642,7 @@ private:
void checkChangeCursor();
mutable QMutex _changeCursorLock { QMutex::Recursive };
QCursor _desiredCursor{ Qt::BlankCursor };
Qt::CursorShape _desiredCursor{ Qt::BlankCursor };
bool _cursorNeedsChanging { false };
QThread* _deadlockWatchdogThread;

View file

@ -222,19 +222,19 @@ Menu::Menu() {
cameraModeGroup->setExclusive(true);
// View > First Person
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::FirstPerson, 0,
true, qApp, SLOT(cameraMenuChanged())));
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
viewMenu, MenuOption::FirstPerson, Qt::CTRL | Qt::Key_F,
true, qApp, SLOT(cameraMenuChanged())));
// View > Third Person
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::ThirdPerson, 0,
false, qApp, SLOT(cameraMenuChanged())));
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
viewMenu, MenuOption::ThirdPerson, Qt::CTRL | Qt::Key_G,
false, qApp, SLOT(cameraMenuChanged())));
// View > Mirror
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::FullscreenMirror, 0,
false, qApp, SLOT(cameraMenuChanged())));
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
viewMenu, MenuOption::FullscreenMirror, Qt::CTRL | Qt::Key_H,
false, qApp, SLOT(cameraMenuChanged())));
// View > Independent [advanced]
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,

View file

@ -258,6 +258,7 @@ MyAvatar::~MyAvatar() {
void MyAvatar::setDominantHand(const QString& hand) {
if (hand == DOMINANT_LEFT_HAND || hand == DOMINANT_RIGHT_HAND) {
_dominantHand = hand;
emit dominantHandChanged(_dominantHand);
}
}
@ -613,7 +614,7 @@ void MyAvatar::simulate(float deltaTime) {
MovingEntitiesOperator moveOperator(entityTree);
forEachDescendant([&](SpatiallyNestablePointer object) {
// if the queryBox has changed, tell the entity-server
if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) {
if (object->getNestableType() == NestableType::Entity && object->checkAndMaybeUpdateQueryAACube()) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
bool success;
AACube newCube = entity->getQueryAACube(success);

View file

@ -578,6 +578,7 @@ signals:
void wentAway();
void wentActive();
void skeletonChanged();
void dominantHandChanged(const QString& hand);
private:

View file

@ -106,6 +106,12 @@ void setupPreferences() {
auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); };
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter));
}
{
static const QString RETICLE_ICON_NAME = { Cursor::Manager::getIconName(Cursor::Icon::RETICLE) };
auto getter = []()->bool { return qApp->getPreferredCursor() == RETICLE_ICON_NAME; };
auto setter = [](bool value) { qApp->setPreferredCursor(value ? RETICLE_ICON_NAME : QString()); };
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Use reticle cursor instead of arrow", getter, setter));
}
// Snapshots
static const QString SNAPSHOTS { "Snapshots" };

View file

@ -706,27 +706,27 @@ bool Overlays::isAddedOverlay(OverlayID id) {
}
void Overlays::sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event) {
emit mousePressOnOverlay(overlayID, event);
QMetaObject::invokeMethod(this, "mousePressOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event));
}
void Overlays::sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event) {
emit mouseReleaseOnOverlay(overlayID, event);
QMetaObject::invokeMethod(this, "mouseReleaseOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event));
}
void Overlays::sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event) {
emit mouseMoveOnOverlay(overlayID, event);
QMetaObject::invokeMethod(this, "mouseMoveOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event));
}
void Overlays::sendHoverEnterOverlay(OverlayID id, PointerEvent event) {
emit hoverEnterOverlay(id, event);
QMetaObject::invokeMethod(this, "hoverEnterOverlay", Q_ARG(OverlayID, id), Q_ARG(PointerEvent, event));
}
void Overlays::sendHoverOverOverlay(OverlayID id, PointerEvent event) {
emit hoverOverOverlay(id, event);
QMetaObject::invokeMethod(this, "hoverOverOverlay", Q_ARG(OverlayID, id), Q_ARG(PointerEvent, event));
}
void Overlays::sendHoverLeaveOverlay(OverlayID id, PointerEvent event) {
emit hoverLeaveOverlay(id, event);
QMetaObject::invokeMethod(this, "hoverLeaveOverlay", Q_ARG(OverlayID, id), Q_ARG(PointerEvent, event));
}
OverlayID Overlays::getKeyboardFocusOverlay() {

View file

@ -323,7 +323,7 @@ signals:
private:
void cleanupOverlaysToDelete();
mutable QMutex _mutex;
mutable QMutex _mutex { QMutex::Recursive };
QMap<OverlayID, Overlay::Pointer> _overlaysHUD;
QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
#if OVERLAY_PANELS

View file

@ -100,30 +100,14 @@ Web3DOverlay::~Web3DOverlay() {
}
_webSurface->pause();
_webSurface->disconnect(_connection);
QObject::disconnect(_mousePressConnection);
_mousePressConnection = QMetaObject::Connection();
QObject::disconnect(_mouseReleaseConnection);
_mouseReleaseConnection = QMetaObject::Connection();
QObject::disconnect(_mouseMoveConnection);
_mouseMoveConnection = QMetaObject::Connection();
QObject::disconnect(_hoverLeaveConnection);
_hoverLeaveConnection = QMetaObject::Connection();
QObject::disconnect(_emitScriptEventConnection);
_emitScriptEventConnection = QMetaObject::Connection();
QObject::disconnect(_webEventReceivedConnection);
_webEventReceivedConnection = QMetaObject::Connection();
// The lifetime of the QML surface MUST be managed by the main thread
// Additionally, we MUST use local variables copied by value, rather than
// member variables, since they would implicitly refer to a this that
// is no longer valid
auto webSurface = _webSurface;
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
DependencyManager::get<OffscreenQmlSurfaceCache>()->release(QML, webSurface);
});
auto overlays = &(qApp->getOverlays());
QObject::disconnect(overlays, &Overlays::mousePressOnOverlay, this, nullptr);
QObject::disconnect(overlays, &Overlays::mouseReleaseOnOverlay, this, nullptr);
QObject::disconnect(overlays, &Overlays::mouseMoveOnOverlay, this, nullptr);
QObject::disconnect(overlays, &Overlays::hoverLeaveOverlay, this, nullptr);
QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
DependencyManager::get<OffscreenQmlSurfaceCache>()->release(QML, _webSurface);
_webSurface.reset();
}
auto geometryCache = DependencyManager::get<GeometryCache>();
@ -153,6 +137,9 @@ QString Web3DOverlay::pickURL() {
void Web3DOverlay::loadSourceURL() {
if (!_webSurface) {
return;
}
QUrl sourceUrl(_url);
if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" ||
@ -252,23 +239,24 @@ void Web3DOverlay::render(RenderArgs* args) {
}
};
_mousePressConnection = connect(&(qApp->getOverlays()), &Overlays::mousePressOnOverlay, this, forwardPointerEvent, Qt::DirectConnection);
_mouseReleaseConnection = connect(&(qApp->getOverlays()), &Overlays::mouseReleaseOnOverlay, this, forwardPointerEvent, Qt::DirectConnection);
_mouseMoveConnection = connect(&(qApp->getOverlays()), &Overlays::mouseMoveOnOverlay, this, forwardPointerEvent, Qt::DirectConnection);
_hoverLeaveConnection = connect(&(qApp->getOverlays()), &Overlays::hoverLeaveOverlay, this, [=](OverlayID overlayID, const PointerEvent& event) {
auto overlays = &(qApp->getOverlays());
QObject::connect(overlays, &Overlays::mousePressOnOverlay, this, forwardPointerEvent);
QObject::connect(overlays, &Overlays::mouseReleaseOnOverlay, this, forwardPointerEvent);
QObject::connect(overlays, &Overlays::mouseMoveOnOverlay, this, forwardPointerEvent);
QObject::connect(overlays, &Overlays::hoverLeaveOverlay, this, [=](OverlayID overlayID, const PointerEvent& event) {
auto self = weakSelf.lock();
if (!self) {
return;
}
if (self->_pressed && overlayID == selfOverlayID) {
if (overlayID == selfOverlayID && (self->_pressed || (!self->_activeTouchPoints.empty() && self->_touchBeginAccepted))) {
PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(),
event.getButton(), event.getButtons(), event.getKeyboardModifiers());
forwardPointerEvent(overlayID, event);
}
}, Qt::DirectConnection);
});
_emitScriptEventConnection = connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
_webEventReceivedConnection = connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
QObject::connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
} else {
if (_currentMaxFPS != _desiredMaxFPS) {
setMaxFPS(_desiredMaxFPS);
@ -361,19 +349,68 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) {
return;
}
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
QPointF windowPoint(windowPos.x, windowPos.y);
if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) {
this->_pressed = true;
} else if (event.getType() == PointerEvent::Release && event.getButton() == PointerEvent::PrimaryButton) {
this->_pressed = false;
//do not send secondary button events to tablet
if (event.getButton() == PointerEvent::SecondaryButton ||
//do not block composed events
event.getButtons() == PointerEvent::SecondaryButton) {
return;
}
QEvent::Type touchType;
Qt::TouchPointState touchPointState;
QEvent::Type mouseType;
QPointF windowPoint;
{
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
windowPoint = QPointF(windowPos.x, windowPos.y);
}
Qt::TouchPointState state = Qt::TouchPointStationary;
if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) {
state = Qt::TouchPointPressed;
} else if (event.getType() == PointerEvent::Release) {
state = Qt::TouchPointReleased;
} else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].pos()) {
state = Qt::TouchPointMoved;
}
QEvent::Type touchType = QEvent::TouchUpdate;
if (_activeTouchPoints.empty()) {
// If the first active touch point is being created, send a begin
touchType = QEvent::TouchBegin;
} if (state == Qt::TouchPointReleased && _activeTouchPoints.size() == 1 && _activeTouchPoints.count(event.getID())) {
// If the last active touch point is being released, send an end
touchType = QEvent::TouchEnd;
}
{
QTouchEvent::TouchPoint point;
point.setId(event.getID());
point.setState(state);
point.setPos(windowPoint);
point.setScreenPos(windowPoint);
_activeTouchPoints[event.getID()] = point;
}
QTouchEvent touchEvent(touchType, &_touchDevice, event.getKeyboardModifiers());
{
QList<QTouchEvent::TouchPoint> touchPoints;
Qt::TouchPointStates touchPointStates;
for (const auto& entry : _activeTouchPoints) {
touchPointStates |= entry.second.state();
touchPoints.push_back(entry.second);
}
touchEvent.setWindow(_webSurface->getWindow());
touchEvent.setTarget(_webSurface->getRootItem());
touchEvent.setTouchPoints(touchPoints);
touchEvent.setTouchPointStates(touchPointStates);
}
// Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover.
// FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times).
// This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery".
//
// In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will
// receive mouse events
Qt::MouseButton button = Qt::NoButton;
Qt::MouseButtons buttons = Qt::NoButton;
if (event.getButton() == PointerEvent::PrimaryButton) {
@ -383,85 +420,28 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) {
buttons |= Qt::LeftButton;
}
switch (event.getType()) {
case PointerEvent::Press:
touchType = QEvent::TouchBegin;
touchPointState = Qt::TouchPointPressed;
mouseType = QEvent::MouseButtonPress;
break;
case PointerEvent::Release:
touchType = QEvent::TouchEnd;
touchPointState = Qt::TouchPointReleased;
mouseType = QEvent::MouseButtonRelease;
break;
case PointerEvent::Move:
touchType = QEvent::TouchUpdate;
touchPointState = Qt::TouchPointMoved;
mouseType = QEvent::MouseMove;
if (((event.getButtons() & PointerEvent::PrimaryButton) > 0) != this->_pressed) {
// Mouse was pressed/released while off the overlay; convert touch and mouse events to press/release to reflect
// current mouse/touch status.
this->_pressed = !this->_pressed;
if (this->_pressed) {
touchType = QEvent::TouchBegin;
touchPointState = Qt::TouchPointPressed;
mouseType = QEvent::MouseButtonPress;
} else {
touchType = QEvent::TouchEnd;
touchPointState = Qt::TouchPointReleased;
mouseType = QEvent::MouseButtonRelease;
}
button = Qt::LeftButton;
buttons |= Qt::LeftButton;
}
break;
default:
return;
}
//do not send secondary button events to tablet
if (event.getButton() == PointerEvent::SecondaryButton ||
//do not block composed events
event.getButtons() == PointerEvent::SecondaryButton) {
return;
}
QTouchEvent::TouchPoint point;
point.setId(event.getID());
point.setState(touchPointState);
point.setPos(windowPoint);
point.setScreenPos(windowPoint);
QList<QTouchEvent::TouchPoint> touchPoints;
touchPoints.push_back(point);
QTouchEvent* touchEvent = new QTouchEvent(touchType, &_touchDevice, event.getKeyboardModifiers());
touchEvent->setWindow(_webSurface->getWindow());
touchEvent->setTarget(_webSurface->getRootItem());
touchEvent->setTouchPoints(touchPoints);
touchEvent->setTouchPointStates(touchPointState);
// Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover.
// FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times).
// This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery".
//
// In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will
// receive mouse events
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
if (event.getType() == PointerEvent::Move) {
QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier);
QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent);
QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier);
QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent);
}
#endif
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
if (touchType == QEvent::TouchBegin) {
_touchBeginAccepted = QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent);
} else if (_touchBeginAccepted) {
QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent);
}
// If this was a release event, remove the point from the active touch points
if (state == Qt::TouchPointReleased) {
_activeTouchPoints.erase(event.getID());
}
#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0)
if (event.getType() == PointerEvent::Move) {
QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier);
QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent);
QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier);
QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent);
}
#endif
}
@ -505,8 +485,8 @@ void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) {
return;
}
QMouseEvent* mouseEvent = new QMouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier);
QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent);
QMouseEvent mouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier);
QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent);
}
void Web3DOverlay::setProperties(const QVariantMap& properties) {
@ -608,6 +588,9 @@ void Web3DOverlay::setScriptURL(const QString& scriptURL) {
_scriptURL = scriptURL;
if (_webSurface) {
AbstractViewStateInterface::instance()->postLambdaEvent([this, scriptURL] {
if (!_webSurface) {
return;
}
_webSurface->getRootItem()->setProperty("scriptURL", scriptURL);
});
}
@ -631,9 +614,5 @@ Web3DOverlay* Web3DOverlay::createClone() const {
}
void Web3DOverlay::emitScriptEvent(const QVariant& message) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, message));
} else {
emit scriptEventReceived(message);
}
QMetaObject::invokeMethod(this, "scriptEventReceived", Q_ARG(QVariant, message));
}

View file

@ -72,7 +72,6 @@ signals:
private:
InputMode _inputMode { Touch };
QSharedPointer<OffscreenQmlSurface> _webSurface;
QMetaObject::Connection _connection;
gpu::TexturePointer _texture;
QString _url;
QString _scriptURL;
@ -82,20 +81,14 @@ private:
bool _showKeyboardFocusHighlight{ true };
bool _pressed{ false };
bool _touchBeginAccepted { false };
std::map<uint32_t, QTouchEvent::TouchPoint> _activeTouchPoints;
QTouchDevice _touchDevice;
uint8_t _desiredMaxFPS { 10 };
uint8_t _currentMaxFPS { 0 };
bool _mayNeedResize { false };
QMetaObject::Connection _mousePressConnection;
QMetaObject::Connection _mouseReleaseConnection;
QMetaObject::Connection _mouseMoveConnection;
QMetaObject::Connection _hoverLeaveConnection;
QMetaObject::Connection _emitScriptEventConnection;
QMetaObject::Connection _webEventReceivedConnection;
};
#endif // hifi_Web3DOverlay_h

View file

@ -1355,8 +1355,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy);
AACube saveQueryAACube = _queryAACube;
checkAndAdjustQueryAACube();
if (saveQueryAACube != _queryAACube) {
if (checkAndMaybeUpdateQueryAACube() && saveQueryAACube != _queryAACube) {
somethingChanged = true;
}
@ -1424,26 +1423,20 @@ void EntityItem::setDimensions(const glm::vec3& value) {
///
AACube EntityItem::getMaximumAACube(bool& success) const {
if (_recalcMaxAACube) {
// * we know that the position is the center of rotation
glm::vec3 centerOfRotation = getPosition(success); // also where _registration point is
if (success) {
_recalcMaxAACube = false;
// * we know that the registration point is the center of rotation
// * we can calculate the length of the furthest extent from the registration point
// as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint)
glm::vec3 dimensions = getDimensions();
glm::vec3 registrationPoint = (dimensions * _registrationPoint);
glm::vec3 registrationRemainder = (dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint));
glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder);
// we want to compute the furthestExtent that an entity can extend out from its "position"
// to do this we compute the max of these two vec3s: registration and 1-registration
// and then scale by dimensions
glm::vec3 maxExtents = getDimensions() * glm::max(_registrationPoint, glm::vec3(1.0f) - _registrationPoint);
// * we know that if you rotate in any direction you would create a sphere
// that has a radius of the length of furthest extent from registration point
float radius = glm::length(furthestExtentFromRegistration);
// there exists a sphere that contains maxExtents for all rotations
float radius = glm::length(maxExtents);
// * we know that the minimum bounding cube of this maximum possible sphere is
// (center - radius) to (center + radius)
// put a cube around the sphere
// TODO? replace _maxAACube with _boundingSphereRadius
glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius);
_maxAACube = AACube(minimumCorner, radius * 2.0f);
}
} else {
@ -1635,6 +1628,8 @@ void EntityItem::updateDimensions(const glm::vec3& value) {
if (getDimensions() != value) {
setDimensions(value);
markDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
_queryAACubeSet = false;
dimensionsChanged();
}
}

View file

@ -2157,12 +2157,17 @@ QList<QString> EntityItemProperties::listChangedProperties() {
return out;
}
bool EntityItemProperties::parentDependentPropertyChanged() const {
return localPositionChanged() || positionChanged() ||
localRotationChanged() || rotationChanged() ||
localVelocityChanged() || localAngularVelocityChanged();
bool EntityItemProperties::transformChanged() const {
return positionChanged() || rotationChanged() ||
localPositionChanged() || localRotationChanged();
}
bool EntityItemProperties::parentRelatedPropertyChanged() const {
return parentDependentPropertyChanged() || parentIDChanged() || parentJointIndexChanged();
return positionChanged() || rotationChanged() ||
localPositionChanged() || localRotationChanged() ||
parentIDChanged() || parentJointIndexChanged();
}
bool EntityItemProperties::queryAACubeRelatedPropertyChanged() const {
return parentRelatedPropertyChanged() || dimensionsChanged();
}

View file

@ -86,8 +86,9 @@ public:
EntityPropertyFlags getChangedProperties() const;
bool parentDependentPropertyChanged() const; // was there a changed in a property that requires parent info to interpret?
bool parentRelatedPropertyChanged() const; // parentDependentPropertyChanged or parentID or parentJointIndex
bool transformChanged() const;
bool parentRelatedPropertyChanged() const;
bool queryAACubeRelatedPropertyChanged() const;
AABox getAABox() const;

View file

@ -221,7 +221,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
_entityTree->withWriteLock([&] {
EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);
if (entity) {
if (propertiesWithSimID.parentRelatedPropertyChanged()) {
if (propertiesWithSimID.queryAACubeRelatedPropertyChanged()) {
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
bool success;
AACube queryAACube = entity->getQueryAACube(success);
@ -435,7 +435,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
entity->rememberHasSimulationOwnershipBid();
}
}
if (properties.parentRelatedPropertyChanged() && entity->computePuffedQueryAACube()) {
if (properties.queryAACubeRelatedPropertyChanged()) {
properties.setQueryAACube(entity->getQueryAACube());
}
entity->setLastBroadcast(usecTimestampNow());
@ -445,7 +445,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
// if they've changed.
entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
if (descendant->getNestableType() == NestableType::Entity) {
if (descendant->computePuffedQueryAACube()) {
if (descendant->checkAndMaybeUpdateQueryAACube()) {
EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(descendant);
EntityItemProperties newQueryCubeProperties;
newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube());

View file

@ -1675,6 +1675,7 @@ QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSen
addToNeedsParentFixupList(entity);
}
entity->forceQueryAACubeUpdate();
entity->checkAndMaybeUpdateQueryAACube();
moveOperator.addEntityToMoveList(entity, entity->getQueryAACube());
i++;
} else {
@ -1693,7 +1694,7 @@ QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSen
EntityItemPointer entity = localTree->findEntityByEntityItemID(newID);
if (entity) {
// queue the packet to send to the server
entity->computePuffedQueryAACube();
entity->updateQueryAACube();
EntityItemProperties properties = entity->getProperties();
properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity
packetSender->queueEditEntityMessage(PacketType::EntityAdd, localTree, newID, properties);

View file

@ -1001,7 +1001,10 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
if (!bestFitBefore && bestFitAfter) {
// This is the case where the entity existed, and is in some element in our tree...
if (currentContainingElement.get() != this) {
currentContainingElement->removeEntityItem(entityItem);
// if the currentContainingElement is non-null, remove the entity from it
if (currentContainingElement) {
currentContainingElement->removeEntityItem(entityItem);
}
addEntityItem(entityItem);
}
}

View file

@ -136,7 +136,7 @@ void SimpleEntitySimulation::sortEntitiesThatMoved() {
SetOfEntities::iterator itemItr = _entitiesToSort.begin();
while (itemItr != _entitiesToSort.end()) {
EntityItemPointer entity = *itemItr;
entity->computePuffedQueryAACube();
entity->checkAndMaybeUpdateQueryAACube();
++itemItr;
}
EntitySimulation::sortEntitiesThatMoved();

View file

@ -0,0 +1,3 @@
set(TARGET_NAME midi)
setup_hifi_library(Network)
link_hifi_libraries(shared networking)

275
libraries/midi/src/Midi.cpp Normal file
View file

@ -0,0 +1,275 @@
//
// Midi.cpp
// libraries/midi/src
//
// Created by Burt Sloane
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Midi.h"
#include <QtCore/QLoggingCategory>
#if defined Q_OS_WIN32
#include "Windows.h"
#endif
#if defined Q_OS_WIN32
const int MIDI_BYTE_MASK = 0x0FF;
const int MIDI_SHIFT_NOTE = 8;
const int MIDI_SHIFT_VELOCITY = 16;
#endif
const int MIDI_STATUS_MASK = 0x0F0;
const int MIDI_NOTE_OFF = 0x080;
const int MIDI_NOTE_ON = 0x090;
const int MIDI_CONTROL_CHANGE = 0x0b0;
const int MIDI_CHANNEL_MODE_ALL_NOTES_OFF = 0x07b;
static Midi* instance = NULL; // communicate this to non-class callbacks
static bool thruModeEnabled = false;
std::vector<QString> Midi::midiinexclude;
std::vector<QString> Midi::midioutexclude;
#if defined Q_OS_WIN32
#pragma comment(lib, "Winmm.lib")
//
std::vector<HMIDIIN> midihin;
std::vector<HMIDIOUT> midihout;
void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
switch (wMsg) {
case MIM_OPEN:
// message not used
break;
case MIM_CLOSE:
for (int i = 0; i < midihin.size(); i++) {
if (midihin[i] == hMidiIn) {
midihin[i] = NULL;
instance->allNotesOff();
}
}
break;
case MIM_DATA: {
int status = MIDI_BYTE_MASK & dwParam1;
int note = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_NOTE);
int vel = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_VELOCITY);
if (thruModeEnabled) {
instance->sendNote(status, note, vel); // relay the note on to all other midi devices
}
instance->noteReceived(status, note, vel); // notify the javascript
break;
}
}
}
void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
switch (wMsg) {
case MOM_OPEN:
// message not used
break;
case MOM_CLOSE:
for (int i = 0; i < midihout.size(); i++) {
if (midihout[i] == hmo) {
midihout[i] = NULL;
instance->allNotesOff();
}
}
break;
}
}
void Midi::sendNote(int status, int note, int vel) {
for (int i = 0; i < midihout.size(); i++) {
if (midihout[i] != NULL) {
midiOutShortMsg(midihout[i], status + (note << MIDI_SHIFT_NOTE) + (vel << MIDI_SHIFT_VELOCITY));
}
}
}
void Midi::MidiSetup() {
midihin.clear();
midihout.clear();
MIDIINCAPS incaps;
for (unsigned int i = 0; i < midiInGetNumDevs(); i++) {
midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS));
bool found = false;
for (int j = 0; j < midiinexclude.size(); j++) {
if (midiinexclude[j].toStdString().compare(incaps.szPname) == 0) {
found = true;
break;
}
}
if (!found) { // EXCLUDE AN INPUT BY NAME
HMIDIIN tmphin;
midiInOpen(&tmphin, i, (DWORD_PTR)MidiInProc, NULL, CALLBACK_FUNCTION);
midiInStart(tmphin);
midihin.push_back(tmphin);
}
}
MIDIOUTCAPS outcaps;
for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) {
midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS));
bool found = false;
for (int j = 0; j < midioutexclude.size(); j++) {
if (midioutexclude[j].toStdString().compare(outcaps.szPname) == 0) {
found = true;
break;
}
}
if (!found) { // EXCLUDE AN OUTPUT BY NAME
HMIDIOUT tmphout;
midiOutOpen(&tmphout, i, (DWORD_PTR)MidiOutProc, NULL, CALLBACK_FUNCTION);
midihout.push_back(tmphout);
}
}
allNotesOff();
}
void Midi::MidiCleanup() {
allNotesOff();
for (int i = 0; i < midihin.size(); i++) {
if (midihin[i] != NULL) {
midiInStop(midihin[i]);
midiInClose(midihin[i]);
}
}
for (int i = 0; i < midihout.size(); i++) {
if (midihout[i] != NULL) {
midiOutClose(midihout[i]);
}
}
midihin.clear();
midihout.clear();
}
#else
void Midi::sendNote(int status, int note, int vel) {
}
void Midi::MidiSetup() {
allNotesOff();
}
void Midi::MidiCleanup() {
allNotesOff();
}
#endif
void Midi::noteReceived(int status, int note, int velocity) {
if (((status & MIDI_STATUS_MASK) != MIDI_NOTE_OFF) &&
((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON)) {
return; // NOTE: only sending note-on and note-off to Javascript
}
QVariantMap eventData;
eventData["status"] = status;
eventData["note"] = note;
eventData["velocity"] = velocity;
emit midiNote(eventData);
}
//
Midi::Midi() {
instance = this;
#if defined Q_OS_WIN32
midioutexclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing
#endif
MidiSetup();
}
Midi::~Midi() {
}
void Midi::playMidiNote(int status, int note, int velocity) {
sendNote(status, note, velocity);
}
void Midi::allNotesOff() {
sendNote(MIDI_CONTROL_CHANGE, MIDI_CHANNEL_MODE_ALL_NOTES_OFF, 0); // all notes off
}
void Midi::resetDevices() {
MidiCleanup();
MidiSetup();
}
void Midi::USBchanged() {
instance->MidiCleanup();
instance->MidiSetup();
}
//
QStringList Midi::listMidiDevices(bool output) {
QStringList rv;
#if defined Q_OS_WIN32
if (output) {
MIDIOUTCAPS outcaps;
for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) {
midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS));
rv.append(outcaps.szPname);
}
} else {
MIDIINCAPS incaps;
for (unsigned int i = 0; i < midiInGetNumDevs(); i++) {
midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS));
rv.append(incaps.szPname);
}
}
#endif
return rv;
}
void Midi::unblockMidiDevice(QString name, bool output) {
if (output) {
for (unsigned long i = 0; i < midioutexclude.size(); i++) {
if (midioutexclude[i].toStdString().compare(name.toStdString()) == 0) {
midioutexclude.erase(midioutexclude.begin() + i);
break;
}
}
} else {
for (unsigned long i = 0; i < midiinexclude.size(); i++) {
if (midiinexclude[i].toStdString().compare(name.toStdString()) == 0) {
midiinexclude.erase(midiinexclude.begin() + i);
break;
}
}
}
}
void Midi::blockMidiDevice(QString name, bool output) {
unblockMidiDevice(name, output); // make sure it's only in there once
if (output) {
midioutexclude.push_back(name);
} else {
midiinexclude.push_back(name);
}
}
void Midi::thruModeEnable(bool enable) {
thruModeEnabled = enable;
}

72
libraries/midi/src/Midi.h Normal file
View file

@ -0,0 +1,72 @@
//
// Midi.h
// libraries/midi/src
//
// Created by Burt Sloane
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Midi_h
#define hifi_Midi_h
#include <QtCore/QObject>
#include <QAbstractNativeEventFilter>
#include <DependencyManager.h>
#include <vector>
#include <string>
class Midi : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
void noteReceived(int status, int note, int velocity); // relay a note to Javascript
void sendNote(int status, int note, int vel); // relay a note to MIDI outputs
static void USBchanged();
private:
static std::vector<QString> midiinexclude;
static std::vector<QString> midioutexclude;
private:
void MidiSetup();
void MidiCleanup();
signals:
void midiNote(QVariantMap eventData);
public slots:
/// play a note on all connected devices
/// @param {int} status: 0x80 is noteoff, 0x90 is noteon (if velocity=0, noteoff), etc
/// @param {int} note: midi note number
/// @param {int} velocity: note velocity (0 means noteoff)
Q_INVOKABLE void playMidiNote(int status, int note, int velocity);
/// turn off all notes on all connected devices
Q_INVOKABLE void allNotesOff();
/// clean up and re-discover attached devices
Q_INVOKABLE void resetDevices();
/// ask for a list of inputs/outputs
Q_INVOKABLE QStringList listMidiDevices(bool output);
/// block an input/output by name
Q_INVOKABLE void blockMidiDevice(QString name, bool output);
/// unblock an input/output by name
Q_INVOKABLE void unblockMidiDevice(QString name, bool output);
/// repeat all incoming notes to all outputs (default disabled)
Q_INVOKABLE void thruModeEnable(bool enable);
public:
Midi();
virtual ~Midi();
};
#endif // hifi_Midi_h

View file

@ -484,11 +484,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
return false;
}
if (_entity->dynamicDataNeedsTransmit()) {
return true;
}
if (_entity->queryAABoxNeedsUpdate()) {
if (_entity->dynamicDataNeedsTransmit() || _entity->queryAACubeNeedsUpdate()) {
return true;
}
@ -577,9 +573,11 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
properties.setActionData(_serverActionData);
}
if (properties.parentRelatedPropertyChanged() && _entity->computePuffedQueryAACube()) {
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
properties.setQueryAACube(_entity->getQueryAACube());
if (properties.transformChanged()) {
if (_entity->checkAndMaybeUpdateQueryAACube()) {
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
properties.setQueryAACube(_entity->getQueryAACube());
}
}
// set the LastEdited of the properties but NOT the entity itself
@ -643,7 +641,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
_entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
if (descendant->getNestableType() == NestableType::Entity) {
EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(descendant);
if (descendant->computePuffedQueryAACube()) {
if (descendant->checkAndMaybeUpdateQueryAACube()) {
EntityItemProperties newQueryCubeProperties;
newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube());
newQueryCubeProperties.setLastEdited(properties.getLastEdited());

View file

@ -16,6 +16,6 @@ if (NOT ANDROID)
endif ()
link_hifi_libraries(shared networking octree gpu procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image)
link_hifi_libraries(shared networking octree gpu procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image midi)
# ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit
include_hifi_library_headers(gl)

View file

@ -77,6 +77,7 @@
#include <Profile.h>
#include "../../midi/src/Midi.h" // FIXME why won't a simpler include work?
#include "MIDIEvent.h"
const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_EXCEPTIONS {
@ -667,6 +668,8 @@ void ScriptEngine::init() {
registerGlobalObject("Audio", DependencyManager::get<AudioScriptingInterface>().data());
registerGlobalObject("Midi", DependencyManager::get<Midi>().data());
registerGlobalObject("Entities", entityScriptingInterface.data());
registerGlobalObject("Quat", &_quatLibrary);
registerGlobalObject("Vec3", &_vec3Library);

View file

@ -25,8 +25,8 @@
class PathUtils : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
Q_PROPERTY(QString resources READ resourcesPath)
Q_PROPERTY(QUrl defaultScripts READ defaultScriptsLocation)
Q_PROPERTY(QString resources READ resourcesPath CONSTANT)
Q_PROPERTY(QUrl defaultScripts READ defaultScriptsLocation CONSTANT)
public:
static const QString& resourcesPath();

View file

@ -946,11 +946,35 @@ AACube SpatiallyNestable::getMaximumAACube(bool& success) const {
return AACube(getPosition(success) - glm::vec3(defaultAACubeSize / 2.0f), defaultAACubeSize);
}
bool SpatiallyNestable::checkAndAdjustQueryAACube() {
bool success;
const float PARENTED_EXPANSION_FACTOR = 3.0f;
bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube() {
bool success = false;
AACube maxAACube = getMaximumAACube(success);
if (success && (!_queryAACubeSet || !_queryAACube.contains(maxAACube))) {
setQueryAACube(maxAACube);
if (success) {
// maybe update _queryAACube
if (!_queryAACubeSet || (_parentID.isNull() && _children.size() == 0) || !_queryAACube.contains(maxAACube)) {
if (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) {
// make an expanded AACube centered on the object
float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale();
_queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale);
} else {
_queryAACube = maxAACube;
}
getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) {
bool childSuccess;
AACube descendantAACube = descendant->getQueryAACube(childSuccess);
if (childSuccess) {
if (_queryAACube.contains(descendantAACube)) {
return;
}
_queryAACube += descendantAACube.getMinimumPoint();
_queryAACube += descendantAACube.getMaximumPoint();
}
});
_queryAACubeSet = true;
}
}
return success;
}
@ -961,46 +985,34 @@ void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) {
return;
}
_queryAACube = queryAACube;
if (queryAACube.getScale() > 0.0f) {
_queryAACubeSet = true;
}
_queryAACubeSet = true;
}
bool SpatiallyNestable::queryAABoxNeedsUpdate() const {
bool success;
AACube currentAACube = getMaximumAACube(success);
if (!success) {
qCDebug(shared) << "can't getMaximumAACube for" << getID();
return false;
bool SpatiallyNestable::queryAACubeNeedsUpdate() const {
if (!_queryAACubeSet) {
return true;
}
// make sure children are still in their boxes, also.
bool childNeedsUpdate = false;
getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) {
if (!childNeedsUpdate && descendant->queryAABoxNeedsUpdate()) {
if (!childNeedsUpdate && descendant->queryAACubeNeedsUpdate()) {
childNeedsUpdate = true;
}
});
if (childNeedsUpdate) {
return true;
}
if (_queryAACubeSet && _queryAACube.contains(currentAACube)) {
return false;
}
return true;
return childNeedsUpdate;
}
bool SpatiallyNestable::computePuffedQueryAACube() {
if (!queryAABoxNeedsUpdate()) {
return false;
}
void SpatiallyNestable::updateQueryAACube() {
bool success;
AACube currentAACube = getMaximumAACube(success);
// make an AACube with edges thrice as long and centered on the object
_queryAACube = AACube(currentAACube.getCorner() - glm::vec3(currentAACube.getScale()), currentAACube.getScale() * 3.0f);
_queryAACubeSet = true;
AACube maxAACube = getMaximumAACube(success);
if (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) {
// make an expanded AACube centered on the object
float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale();
_queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale);
} else {
_queryAACube = maxAACube;
}
getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) {
bool success;
@ -1013,8 +1025,7 @@ bool SpatiallyNestable::computePuffedQueryAACube() {
_queryAACube += descendantAACube.getMaximumPoint();
}
});
return true;
_queryAACubeSet = true;
}
AACube SpatiallyNestable::getQueryAACube(bool& success) const {

View file

@ -102,11 +102,11 @@ public:
virtual glm::vec3 getParentAngularVelocity(bool& success) const;
virtual AACube getMaximumAACube(bool& success) const;
virtual bool checkAndAdjustQueryAACube();
virtual bool computePuffedQueryAACube();
bool checkAndMaybeUpdateQueryAACube();
void updateQueryAACube();
virtual void setQueryAACube(const AACube& queryAACube);
virtual bool queryAABoxNeedsUpdate() const;
virtual bool queryAACubeNeedsUpdate() const;
void forceQueryAACubeUpdate() { _queryAACubeSet = false; }
virtual AACube getQueryAACube(bool& success) const;
virtual AACube getQueryAACube() const;
@ -197,7 +197,7 @@ protected:
mutable QHash<QUuid, SpatiallyNestableWeakPointer> _children;
virtual void locationChanged(bool tellPhysics = true); // called when a this object's location has changed
virtual void dimensionsChanged() { } // called when a this object's dimensions have changed
virtual void dimensionsChanged() { _queryAACubeSet = false; } // called when a this object's dimensions have changed
virtual void parentDeleted() { } // called on children of a deleted parent
// _queryAACube is used to decide where something lives in the octree

View file

@ -31,12 +31,34 @@ namespace Cursor {
}
};
static QMap<uint16_t, QString> ICONS;
QMap<uint16_t, QString> Manager::ICON_NAMES {
{ Icon::SYSTEM, "SYSTEM", },
{ Icon::DEFAULT, "DEFAULT", },
{ Icon::LINK, "LINK", },
{ Icon::ARROW, "ARROW", },
{ Icon::RETICLE, "RETICLE", },
};
QMap<uint16_t, QString> Manager::ICONS;
static uint16_t _customIconId = Icon::USER_BASE;
Manager::Manager() {
ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/arrow.png";
ICONS[Icon::LINK] = PathUtils::resourcesPath() + "images/link.png";
ICONS[Icon::SYSTEM] = PathUtils::resourcesPath() + "images/cursor-none.png";
ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/cursor-arrow.png";
ICONS[Icon::LINK] = PathUtils::resourcesPath() + "images/cursor-link.png";
ICONS[Icon::ARROW] = PathUtils::resourcesPath() + "images/cursor-arrow.png";
ICONS[Icon::RETICLE] = PathUtils::resourcesPath() + "images/cursor-reticle.png";
}
Icon Manager::lookupIcon(const QString& name) {
for (const auto& kv : ICON_NAMES.toStdMap()) {
if (kv.second == name) {
return static_cast<Icon>(kv.first);
}
}
return Icon::DEFAULT;
}
const QString& Manager::getIconName(const Icon& icon) {
return ICON_NAMES.count(icon) ? ICON_NAMES[icon] : ICON_NAMES[Icon::DEFAULT];
}
Manager& Manager::instance() {

View file

@ -18,16 +18,18 @@ namespace Cursor {
};
enum Icon {
SYSTEM,
DEFAULT,
LINK,
GRAB,
ARROW,
RETICLE,
// Add new system cursors here
// User cursors will have ids over this value
USER_BASE = 0xFF,
};
class Instance {
public:
virtual Source getType() const = 0;
@ -49,6 +51,11 @@ namespace Cursor {
uint16_t registerIcon(const QString& path);
QList<uint16_t> registeredIcons() const;
const QString& getIconImage(uint16_t icon);
static QMap<uint16_t, QString> ICONS;
static QMap<uint16_t, QString> ICON_NAMES;
static Icon lookupIcon(const QString& name);
static const QString& getIconName(const Icon& icon);
private:
float _scale{ 1.0f };
};

View file

@ -329,6 +329,7 @@ void initializeQmlEngine(QQmlEngine* engine, QQuickWindow* window) {
rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext));
rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext));
rootContext->setContextProperty("HFTabletWebEngineProfile", new HFTabletWebEngineProfile(rootContext));
rootContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());
}
QQmlEngine* acquireEngine(QQuickWindow* window) {

View file

@ -825,7 +825,13 @@ TabletButtonProxy::~TabletButtonProxy() {
void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) {
Q_ASSERT(QThread::currentThread() == qApp->thread());
if (_qmlButton) {
QObject::disconnect(_qmlButton, &QQuickItem::destroyed, this, nullptr);
}
_qmlButton = qmlButton;
if (_qmlButton) {
QObject::connect(_qmlButton, &QQuickItem::destroyed, this, [this] { _qmlButton = nullptr; });
}
}
void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) {

View file

@ -126,15 +126,17 @@ void OculusBaseDisplayPlugin::internalDeactivate() {
}
bool OculusBaseDisplayPlugin::activateStandBySession() {
_session = acquireOculusSession();
if (!_session) {
return false;
_session = acquireOculusSession();
}
return true;
return _session;
}
void OculusBaseDisplayPlugin::deactivateSession() {
releaseOculusSession();
_session = nullptr;
// FIXME
// Switching to Qt 5.9 exposed a race condition or similar issue that caused a crash when putting on an Rift
// while already in VR mode. Commenting these out is a workaround.
//releaseOculusSession();
//_session = nullptr;
}
void OculusBaseDisplayPlugin::updatePresentPose() {
//mat4 sensorResetMat;

View file

@ -13,7 +13,7 @@ if (APPLE)
set(TARGET_NAME oculusLegacy)
setup_hifi_plugin()
link_hifi_libraries(shared gl gpu gpu-gl plugins ui ui-plugins display-plugins input-plugins)
link_hifi_libraries(shared gl gpu gpu-gl plugins ui ui-plugins display-plugins input-plugins midi)
include_hifi_library_headers(octree)

View file

@ -0,0 +1,50 @@
//
// Created by James B. Pollack @imgntn on April 18, 2016.
// Adapted by Burt
// Copyright 2016 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
//
//var SCRIPT_URL = "file:///e:/hifi/scripts/tutorials/entity_scripts/midiSphere.js";
var SCRIPT_URL = "http://hifi-files.s3-website-us-west-2.amazonaws.com/midiSphere.js";
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(0.5, Quat.getForward(Camera.getOrientation())));
var BALL_GRAVITY = {
x: 0,
y: 0,
z: 0
};
var BALL_DIMENSIONS = {
x: 0.4,
y: 0.4,
z: 0.4
};
var BALL_COLOR = {
red: 255,
green: 0,
blue: 0
};
var midiSphereProperties = {
name: 'MIDI Sphere',
shapeType: 'sphere',
type: 'Sphere',
script: SCRIPT_URL,
color: BALL_COLOR,
dimensions: BALL_DIMENSIONS,
gravity: BALL_GRAVITY,
dynamic: false,
position: center,
collisionless: false,
ignoreForCollisions: true
};
var midiSphere = Entities.addEntity(midiSphereProperties);
Script.stop();

View file

@ -0,0 +1,52 @@
// midiSphere.js
//
// Script Type: Entity
// Created by James B. Pollack @imgntn on 9/21/2015
// Adapted by Burt
// Copyright 2015 High Fidelity, Inc.
//
// This script listens to MIDI and makes the ball change color.
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
var _this;
function MidiSphere() {
_this = this;
this.clicked = false;
return;
}
MidiSphere.prototype = {
preload: function(entityID) {
this.entityID = entityID;
Midi.midiNote.connect(function(eventData) {
print("MidiSphere.noteReceived: "+JSON.stringify(eventData));
Entities.editEntity(entityID, { color: { red: 2*eventData.note, green: 2*eventData.note, blue: 2*eventData.note} });
});
print("MidiSphere.preload");
},
unload: function(entityID) {
print("MidiSphere.unload");
},
clickDownOnEntity: function(entityID, mouseEvent) {
print("MidiSphere.clickDownOnEntity");
if (this.clicked) {
Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} });
this.clicked = false;
Midi.playMidiNote(144, 64, 0);
} else {
Entities.editEntity(entityID, { color: { red: 255, green: 255, blue: 0} });
this.clicked = true;
Midi.playMidiNote(144, 64, 100);
}
}
};
// entity scripts should return a newly constructed object of our type
return new MidiSphere();
});

View file

@ -12,8 +12,7 @@ setup_hifi_project(Quick Gui OpenGL)
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
# link in the shared libraries
link_hifi_libraries(shared networking model fbx ktx image octree gl gpu gpu-gl render model-networking networking render-utils entities entities-renderer animation audio avatars script-engine physics procedural)
link_hifi_libraries(shared networking model fbx ktx image octree gl gpu gpu-gl render model-networking networking render-utils entities entities-renderer animation audio avatars script-engine physics procedural midi)
package_libraries_for_deployment()