mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 07:12:40 +02:00
Merge branch 'master' into feature/secondary-pose-support
This commit is contained in:
commit
2383933252
45 changed files with 981 additions and 307 deletions
39
BUILD.md
39
BUILD.md
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
BIN
interface/resources/images/cursor-arrow.png
Normal file
BIN
interface/resources/images/cursor-arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
BIN
interface/resources/images/cursor-link.png
Normal file
BIN
interface/resources/images/cursor-link.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
BIN
interface/resources/images/cursor-none.png
Normal file
BIN
interface/resources/images/cursor-none.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 133 B |
BIN
interface/resources/images/cursor-reticle.png
Normal file
BIN
interface/resources/images/cursor-reticle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9 KiB |
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -578,6 +578,7 @@ signals:
|
|||
void wentAway();
|
||||
void wentActive();
|
||||
void skeletonChanged();
|
||||
void dominantHandChanged(const QString& hand);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -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" };
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
3
libraries/midi/CMakeLists.txt
Normal file
3
libraries/midi/CMakeLists.txt
Normal 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
275
libraries/midi/src/Midi.cpp
Normal 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
72
libraries/midi/src/Midi.h
Normal 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
|
|
@ -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());
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
50
scripts/tutorials/createMidiSphere.js
Normal file
50
scripts/tutorials/createMidiSphere.js
Normal 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();
|
52
scripts/tutorials/entity_scripts/midiSphere.js
Normal file
52
scripts/tutorials/entity_scripts/midiSphere.js
Normal 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();
|
||||
});
|
|
@ -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()
|
||||
|
||||
|
|
Loading…
Reference in a new issue