mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 18:23:54 +02:00
Merge remote-tracking branch 'upstream/context-overlays' into contextOverlays_renderMOde
This commit is contained in:
commit
6ae516c46a
58 changed files with 1302 additions and 341 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.
|
||||
|
||||
|
|
35
BUILD_WIN.md
35
BUILD_WIN.md
|
@ -1,42 +1,45 @@
|
|||
This is a stand-alone guide for creating your first High Fidelity build for Windows 64-bit.
|
||||
|
||||
## Building High Fidelity
|
||||
Note: We are now using Visual Studio 2017 and Qt 5.9.1. If you are upgrading from Visual Studio 2013 and Qt 5.6.2, do a clean uninstall of those versions before going through this guide.
|
||||
|
||||
### Step 1. Installing Visual Studio 2013
|
||||
Note: The prerequisites will require about 10 GB of space on your drive.
|
||||
|
||||
If you don't already have the Community or Professional edition of Visual Studio 2013, download and install [Visual Studio Community 2013](https://www.visualstudio.com/en-us/news/releasenotes/vs2013-community-vs). You do not need to install any of the optional components when going through the installer.
|
||||
### Step 1. Visual Studio 2017
|
||||
|
||||
Note: Newer versions of Visual Studio are not yet compatible.
|
||||
If you don’t have Community or Professional edition of Visual Studio 2017, download [Visual Studio Community 2017](https://www.visualstudio.com/downloads/).
|
||||
|
||||
When selecting components, check "Desktop development with C++." Also check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)" on the Summary toolbar on the right.
|
||||
|
||||
### Step 2. Installing CMake
|
||||
|
||||
Download and install the [CMake 3.8.0 win64-x64 Installer](https://cmake.org/files/v3.8/cmake-3.8.0-win64-x64.msi). Make sure "Add CMake to system PATH for all users" is checked when going through the installer.
|
||||
Download and install the latest version of CMake 3.9. Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). Make sure to check "Add CMake to system PATH for all users" when prompted during installation.
|
||||
|
||||
### Step 3. Installing Qt
|
||||
|
||||
Download and install the [Qt 5.6.2 for Windows 64-bit (VS 2013)](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-windows-x86-msvc2013_64-5.6.2.exe).
|
||||
Download and install the [Qt Online Installer](https://www.qt.io/download-open-source/?hsCtaTracking=f977210e-de67-475f-a32b-65cec207fd03%7Cd62710cd-e1db-46aa-8d4d-2f1c1ffdacea). While installing, you only need to have the following components checked under Qt 5.9.1: "msvc2017 64-bit", "Qt WebEngine", and "Qt Script (Deprecated)".
|
||||
|
||||
Keep the default components checked when going through the installer.
|
||||
Note: Installing the Sources is optional but recommended if you have room for them (~2GB).
|
||||
|
||||
### Step 4. Setting Qt Environment Variable
|
||||
|
||||
Go to `Control Panel > System > Advanced System Settings > Environment Variables > New...` (or search “Environment Variables” in Start Search).
|
||||
* Set "Variable name": `QT_CMAKE_PREFIX_PATH`
|
||||
* Set "Variable value": `%QT_DIR%\5.6\msvc2013_64\lib\cmake`
|
||||
* Set "Variable value": `C:\Qt\5.9.1\msvc2017_64\lib\cmake`
|
||||
|
||||
### Step 5. Installing OpenSSL
|
||||
|
||||
Download and install the [Win64 OpenSSL v1.0.2L Installer](https://slproweb.com/download/Win64OpenSSL-1_0_2L.exe).
|
||||
Download and install the Win64 OpenSSL v1.0.2 Installer[https://slproweb.com/products/Win32OpenSSL.html].
|
||||
|
||||
### Step 6. Running CMake to Generate Build Files
|
||||
|
||||
Run Command Prompt from Start and run the following commands:
|
||||
````
|
||||
```
|
||||
cd "%HIFI_DIR%"
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Visual Studio 12 Win64"
|
||||
````
|
||||
cmake .. -G "Visual Studio 15 Win64"
|
||||
```
|
||||
|
||||
Where `%HIFI_DIR%` is the directory for the highfidelity repository.
|
||||
|
||||
|
@ -72,14 +75,6 @@ For any problems after Step #6, first try this:
|
|||
|
||||
Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory.
|
||||
|
||||
#### nmake cannot be found
|
||||
|
||||
Make sure nmake.exe is located at the following path:
|
||||
|
||||
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin
|
||||
|
||||
If not, add the directory where nmake is located to the PATH environment variable.
|
||||
|
||||
#### Qt is throwing an error
|
||||
|
||||
Make sure you have the correct version (5.6.2) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly.
|
||||
Make sure you have the correct version (5.9.1) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly.
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
{
|
||||
"name": "Vive to Standard",
|
||||
"channels": [
|
||||
{ "from": "Vive.LY", "to": "Standard.LeftIndexPoint",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "hysteresis", "min": 0.7, "max": 0.75 } ]
|
||||
},
|
||||
{ "from": "Vive.RY", "to": "Standard.RightIndexPoint",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "hysteresis", "min": 0.7, "max": 0.75 } ]
|
||||
},
|
||||
|
||||
{ "from": "Vive.LY", "when": "Vive.LSY", "filters": ["invert"], "to": "Standard.LY" },
|
||||
{ "from": "Vive.LX", "when": "Vive.LSX", "to": "Standard.LX" },
|
||||
{
|
||||
|
@ -13,6 +22,10 @@
|
|||
|
||||
{ "from": "Vive.LeftGrip", "to": "Standard.LeftGrip" },
|
||||
{ "from": "Vive.LS", "to": "Standard.LS" },
|
||||
{ "from": "Vive.LSTouch", "to": "Standard.LeftThumbUp",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "logicalNot" } ]
|
||||
},
|
||||
{ "from": "Vive.LSTouch", "to": "Standard.LSTouch" },
|
||||
|
||||
{ "from": "Vive.RY", "when": "Vive.RSY", "filters": ["invert"], "to": "Standard.RY" },
|
||||
|
@ -27,6 +40,10 @@
|
|||
|
||||
{ "from": "Vive.RightGrip", "to": "Standard.RightGrip" },
|
||||
{ "from": "Vive.RS", "to": "Standard.RS" },
|
||||
{ "from": "Vive.RSTouch", "to": "Standard.RightThumbUp",
|
||||
"peek": true,
|
||||
"filters": [ { "type": "logicalNot" } ]
|
||||
},
|
||||
{ "from": "Vive.RSTouch", "to": "Standard.RSTouch" },
|
||||
|
||||
{ "from": "Vive.LSCenter", "to": "Standard.LeftPrimaryThumb" },
|
||||
|
@ -59,7 +76,7 @@
|
|||
|
||||
{ "from": "Vive.Head", "to" : "Standard.Head"},
|
||||
|
||||
{ "from": "Vive.RightArm", "to" : "Standard.RightArm"},
|
||||
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm"}
|
||||
{ "from": "Vive.RightArm", "to" : "Standard.RightArm" },
|
||||
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }
|
||||
]
|
||||
}
|
||||
|
|
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();
|
||||
|
@ -151,13 +165,17 @@ ScrollingWindow {
|
|||
var SHAPE_TYPE_SIMPLE_HULL = 1;
|
||||
var SHAPE_TYPE_SIMPLE_COMPOUND = 2;
|
||||
var SHAPE_TYPE_STATIC_MESH = 3;
|
||||
|
||||
var SHAPE_TYPE_BOX = 4;
|
||||
var SHAPE_TYPE_SPHERE = 5;
|
||||
|
||||
var SHAPE_TYPES = [];
|
||||
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
|
||||
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
||||
|
||||
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
|
||||
|
||||
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
|
||||
var DYNAMIC_DEFAULT = false;
|
||||
var prompt = desktop.customInputDialog({
|
||||
|
@ -196,6 +214,12 @@ ScrollingWindow {
|
|||
case SHAPE_TYPE_STATIC_MESH:
|
||||
shapeType = "static-mesh";
|
||||
break;
|
||||
case SHAPE_TYPE_BOX:
|
||||
shapeType = "box";
|
||||
break;
|
||||
case SHAPE_TYPE_SPHERE:
|
||||
shapeType = "sphere";
|
||||
break;
|
||||
default:
|
||||
shapeType = "none";
|
||||
}
|
||||
|
@ -289,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) {
|
||||
|
@ -445,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 {
|
||||
|
@ -514,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();
|
||||
|
@ -152,13 +165,17 @@ Rectangle {
|
|||
var SHAPE_TYPE_SIMPLE_HULL = 1;
|
||||
var SHAPE_TYPE_SIMPLE_COMPOUND = 2;
|
||||
var SHAPE_TYPE_STATIC_MESH = 3;
|
||||
|
||||
var SHAPE_TYPE_BOX = 4;
|
||||
var SHAPE_TYPE_SPHERE = 5;
|
||||
|
||||
var SHAPE_TYPES = [];
|
||||
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
|
||||
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
||||
|
||||
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
|
||||
|
||||
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
|
||||
var DYNAMIC_DEFAULT = false;
|
||||
var prompt = tabletRoot.customInputDialog({
|
||||
|
@ -197,6 +214,12 @@ Rectangle {
|
|||
case SHAPE_TYPE_STATIC_MESH:
|
||||
shapeType = "static-mesh";
|
||||
break;
|
||||
case SHAPE_TYPE_BOX:
|
||||
shapeType = "box";
|
||||
break;
|
||||
case SHAPE_TYPE_SPHERE:
|
||||
shapeType = "sphere";
|
||||
break;
|
||||
default:
|
||||
shapeType = "none";
|
||||
}
|
||||
|
@ -290,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) {
|
||||
|
@ -459,7 +496,7 @@ Rectangle {
|
|||
width: 80
|
||||
|
||||
onClicked: root.renameFile()
|
||||
enabled: treeView.selection.hasSelection
|
||||
enabled: canRename()
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
|
@ -515,6 +552,7 @@ Rectangle {
|
|||
treeModel: assetProxyModel
|
||||
canEdit: true
|
||||
colorScheme: root.colorScheme
|
||||
selectionMode: SelectionMode.ExtendedSelection
|
||||
|
||||
modifyEl: renameEl
|
||||
|
||||
|
|
|
@ -145,7 +145,9 @@ Rectangle {
|
|||
model: ["No Collision",
|
||||
"Basic - Whole model",
|
||||
"Good - Sub-meshes",
|
||||
"Exact - All polygons"]
|
||||
"Exact - All polygons",
|
||||
"Box",
|
||||
"Sphere"]
|
||||
}
|
||||
|
||||
Row {
|
||||
|
|
|
@ -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);
|
||||
|
@ -1756,9 +1757,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;
|
||||
}
|
||||
|
||||
|
@ -2133,7 +2141,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());
|
||||
|
@ -2179,9 +2186,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);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2523,6 +2532,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);
|
||||
|
@ -3039,7 +3054,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
break;
|
||||
|
||||
case Qt::Key_F: {
|
||||
_physicsEngine->dumpNextStats();
|
||||
if (isOption) {
|
||||
_physicsEngine->dumpNextStats();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3083,9 +3100,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);
|
||||
|
@ -4479,27 +4500,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);
|
||||
|
|
|
@ -615,6 +615,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" };
|
||||
|
|
|
@ -116,7 +116,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
|
|||
|
||||
QMutexLocker locker(&_mutex);
|
||||
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) {
|
||||
|
||||
|
||||
// Reset all batch pipeline settings between overlay
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
batch.setResourceTexture(0, textureCache->getWhiteTexture()); // FIXME - do we really need to do this??
|
||||
|
@ -136,7 +136,7 @@ void Overlays::enable() {
|
|||
_enabled = true;
|
||||
}
|
||||
|
||||
// Note, can't be invoked by scripts, but can be called by the InterfaceParentFinder
|
||||
// Note, can't be invoked by scripts, but can be called by the InterfaceParentFinder
|
||||
// class on packet processing threads
|
||||
Overlay::Pointer Overlays::getOverlay(OverlayID id) const {
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
@ -244,8 +244,8 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
|
|||
|
||||
bool Overlays::editOverlay(OverlayID id, const QVariant& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
// NOTE editOverlay can be called very frequently in scripts and can't afford to
|
||||
// block waiting on the main thread. Additionally, no script actually
|
||||
// NOTE editOverlay can be called very frequently in scripts and can't afford to
|
||||
// block waiting on the main thread. Additionally, no script actually
|
||||
// examines the return value and does something useful with it, so use a non-blocking
|
||||
// invoke and just always return true
|
||||
QMetaObject::invokeMethod(this, "editOverlay", Q_ARG(OverlayID, id), Q_ARG(QVariant, properties));
|
||||
|
@ -706,27 +706,27 @@ bool Overlays::isAddedOverlay(OverlayID id) {
|
|||
}
|
||||
|
||||
void Overlays::sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
emit mousePressOnOverlay(overlayID, event);
|
||||
QMetaObject::invokeMethod(this, "mousePressOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event));
|
||||
}
|
||||
|
||||
void Overlays::sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
emit mouseReleaseOnOverlay(overlayID, event);
|
||||
QMetaObject::invokeMethod(this, "mouseReleaseOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event));
|
||||
}
|
||||
|
||||
void Overlays::sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
emit mouseMoveOnOverlay(overlayID, event);
|
||||
QMetaObject::invokeMethod(this, "mouseMoveOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event));
|
||||
}
|
||||
|
||||
void Overlays::sendHoverEnterOverlay(const OverlayID& id, const PointerEvent& event) {
|
||||
emit hoverEnterOverlay(id, event);
|
||||
QMetaObject::invokeMethod(this, "hoverEnterOverlay", Q_ARG(OverlayID, id), Q_ARG(PointerEvent, event));
|
||||
}
|
||||
|
||||
void Overlays::sendHoverOverOverlay(const OverlayID& id, const PointerEvent& event) {
|
||||
emit hoverOverOverlay(id, event);
|
||||
void Overlays::sendHoverOverOverlay(const OverlayID& id, const PointerEvent& event) {
|
||||
QMetaObject::invokeMethod(this, "hoverOverOverlay", Q_ARG(OverlayID, id), Q_ARG(PointerEvent, event));
|
||||
}
|
||||
|
||||
void Overlays::sendHoverLeaveOverlay(const OverlayID& id, const PointerEvent& event) {
|
||||
emit hoverLeaveOverlay(id, event);
|
||||
void Overlays::sendHoverLeaveOverlay(const OverlayID& id, const PointerEvent& event) {
|
||||
QMetaObject::invokeMethod(this, "hoverLeaveOverlay", Q_ARG(OverlayID, id), Q_ARG(PointerEvent, event));
|
||||
}
|
||||
|
||||
OverlayID Overlays::getKeyboardFocusOverlay() {
|
||||
|
@ -775,7 +775,7 @@ float Overlays::height() {
|
|||
|
||||
static const uint32_t MOUSE_POINTER_ID = 0;
|
||||
|
||||
static glm::vec2 projectOntoOverlayXYPlane(glm::vec3 position, glm::quat rotation, glm::vec2 dimensions, const PickRay& pickRay,
|
||||
static glm::vec2 projectOntoOverlayXYPlane(glm::vec3 position, glm::quat rotation, glm::vec2 dimensions, const PickRay& pickRay,
|
||||
const RayToOverlayIntersectionResult& rayPickResult) {
|
||||
|
||||
// Project the intersection point onto the local xy plane of the overlay.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "filters/DeadZoneFilter.h"
|
||||
#include "filters/HysteresisFilter.h"
|
||||
#include "filters/InvertFilter.h"
|
||||
#include "filters/NotFilter.h"
|
||||
#include "filters/PulseFilter.h"
|
||||
#include "filters/ScaleFilter.h"
|
||||
#include "filters/TranslateFilter.h"
|
||||
|
@ -40,6 +41,7 @@ REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPos
|
|||
REGISTER_FILTER_CLASS_INSTANCE(DeadZoneFilter, "deadZone")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(NotFilter, "logicalNot")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate")
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "filters/DeadZoneFilter.h"
|
||||
#include "filters/HysteresisFilter.h"
|
||||
#include "filters/InvertFilter.h"
|
||||
#include "filters/NotFilter.h"
|
||||
#include "filters/PulseFilter.h"
|
||||
#include "filters/ScaleFilter.h"
|
||||
#include "filters/TranslateFilter.h"
|
||||
|
@ -148,6 +149,11 @@ QObject* RouteBuilderProxy::pulse(float interval) {
|
|||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::logicalNot() {
|
||||
addFilter(std::make_shared<NotFilter>());
|
||||
return this;
|
||||
}
|
||||
|
||||
void RouteBuilderProxy::addFilter(Filter::Pointer filter) {
|
||||
_route->filters.push_back(filter);
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* postTransform(glm::mat4 transform);
|
||||
Q_INVOKABLE QObject* rotate(glm::quat rotation);
|
||||
Q_INVOKABLE QObject* lowVelocity(float rotationConstant, float translationConstant);
|
||||
Q_INVOKABLE QObject* logicalNot();
|
||||
|
||||
private:
|
||||
void to(const Endpoint::Pointer& destination);
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#include "NotFilter.h"
|
||||
|
||||
using namespace controller;
|
||||
|
||||
NotFilter::NotFilter() {
|
||||
}
|
||||
|
||||
float NotFilter::apply(float value) const {
|
||||
return (value == 0.0f) ? 1.0f : 0.0f;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
#ifndef hifi_Controllers_Filters_Not_h
|
||||
#define hifi_Controllers_Filters_Not_h
|
||||
|
||||
#include "../Filter.h"
|
||||
|
||||
namespace controller {
|
||||
|
||||
class NotFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(NotFilter);
|
||||
public:
|
||||
NotFilter();
|
||||
|
||||
virtual float apply(float value) const override;
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1361,8 +1361,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;
|
||||
}
|
||||
|
||||
|
@ -1430,26 +1429,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 {
|
||||
|
@ -1641,6 +1634,8 @@ void EntityItem::updateDimensions(const glm::vec3& value) {
|
|||
if (getDimensions() != value) {
|
||||
setDimensions(value);
|
||||
markDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
_queryAACubeSet = false;
|
||||
dimensionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2163,12 +2163,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)
|
||||
|
||||
|
|
|
@ -337,6 +337,8 @@ var toolBar = (function () {
|
|||
var SHAPE_TYPE_SIMPLE_HULL = 1;
|
||||
var SHAPE_TYPE_SIMPLE_COMPOUND = 2;
|
||||
var SHAPE_TYPE_STATIC_MESH = 3;
|
||||
var SHAPE_TYPE_BOX = 4;
|
||||
var SHAPE_TYPE_SPHERE = 5;
|
||||
var DYNAMIC_DEFAULT = false;
|
||||
|
||||
function handleNewModelDialogResult(result) {
|
||||
|
@ -353,6 +355,12 @@ var toolBar = (function () {
|
|||
case SHAPE_TYPE_STATIC_MESH:
|
||||
shapeType = "static-mesh";
|
||||
break;
|
||||
case SHAPE_TYPE_BOX:
|
||||
shapeType = "box";
|
||||
break;
|
||||
case SHAPE_TYPE_SPHERE:
|
||||
shapeType = "sphere";
|
||||
break;
|
||||
default:
|
||||
shapeType = "none";
|
||||
}
|
||||
|
@ -449,6 +457,8 @@ var toolBar = (function () {
|
|||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
|
||||
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
||||
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
|
||||
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
|
||||
|
||||
// tablet version of new-model dialog
|
||||
|
@ -654,6 +664,7 @@ var toolBar = (function () {
|
|||
selectionDisplay.triggerMapping.enable();
|
||||
print("starting tablet in landscape mode");
|
||||
tablet.landscape = true;
|
||||
entityIconOverlayManager.setIconsSelectable(null,false);
|
||||
// Not sure what the following was meant to accomplish, but it currently causes
|
||||
// everybody else to think that Interface has lost focus overall. fogbugzid:558
|
||||
// Window.setFocus();
|
||||
|
@ -981,6 +992,7 @@ function mouseClickEvent(event) {
|
|||
} else {
|
||||
selectionManager.addEntity(foundEntity, true);
|
||||
}
|
||||
|
||||
if (wantDebug) {
|
||||
print("Model selected: " + foundEntity);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* globals EntityIconOverlayManager:true */
|
||||
|
||||
EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) {
|
||||
|
||||
var visible = false;
|
||||
|
||||
// List of all created overlays
|
||||
|
@ -69,6 +70,29 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
this.setIconsSelectable = function(arrayOfSelectedEntityIDs, isIconsSelectable) {
|
||||
if (arrayOfSelectedEntityIDs === null) {
|
||||
for (var id in entityOverlays) {
|
||||
Overlays.editOverlay(entityOverlays[id], {
|
||||
ignoreRayIntersection: isIconsSelectable
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (var id in entityOverlays) {
|
||||
if (arrayOfSelectedEntityIDs.indexOf(id) !== -1) { // in the entityOverlays array and selectable
|
||||
Overlays.editOverlay(entityOverlays[id], {
|
||||
ignoreRayIntersection: isIconsSelectable
|
||||
});
|
||||
} else {
|
||||
Overlays.editOverlay(entityOverlays[id], {
|
||||
ignoreRayIntersection: !isIconsSelectable
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Allocate or get an unused overlay
|
||||
function getOverlay() {
|
||||
var overlay;
|
||||
|
@ -114,6 +138,9 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) {
|
|||
overlayProperties[key] = customProperties[key];
|
||||
}
|
||||
}
|
||||
if(properties.type === 'ParticleEffect' || properties.type === 'Light'){
|
||||
overlayProperties.ignoreRayIntersection = true;
|
||||
}
|
||||
Overlays.editOverlay(overlay, overlayProperties);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4060,6 +4060,8 @@ SelectionDisplay = (function() {
|
|||
return false;
|
||||
}
|
||||
|
||||
entityIconOverlayManager.setIconsSelectable(selectionManager.selections,true);
|
||||
|
||||
// ignore ray intersection for our selection box and yaw/pitch/roll
|
||||
result = Overlays.findRayIntersection(pickRay, true, null, [ yawHandle, pitchHandle, rollHandle, selectionBox ] );
|
||||
if (result.intersects) {
|
||||
|
|
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()
|
||||
|
||||
|
|
91
unpublishedScripts/marketplace/dart/createDart.js
Normal file
91
unpublishedScripts/marketplace/dart/createDart.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
"use strict";
|
||||
//
|
||||
// createDart.js
|
||||
//
|
||||
// Created by MrRoboman on 17/05/04
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Creates five throwing darts.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
var MODEL_URL = "https://hifi-content.s3.amazonaws.com/wadewatts/dart.fbx";
|
||||
var SCRIPT_URL = Script.resolvePath("./dart.js?v=" + Date.now());
|
||||
var START_POSITION = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), 0.75));
|
||||
var START_ROTATION = MyAvatar.orientation
|
||||
var SCALE_FACTOR = 1;
|
||||
|
||||
var dart = {
|
||||
type: "Model",
|
||||
shapeType: "box",
|
||||
name: "Dart",
|
||||
description: "Throw it at something!",
|
||||
script: SCRIPT_URL,
|
||||
modelURL: MODEL_URL,
|
||||
position: START_POSITION,
|
||||
rotation: START_ROTATION,
|
||||
lifetime: 300,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9.8,
|
||||
z: 0
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
z: 0.3
|
||||
},
|
||||
dynamic: true,
|
||||
owningAvatarID: MyAvatar.sessionUUID,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: true,
|
||||
invertSolidWhileHeld: true,
|
||||
ignoreIK: false
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
var avatarUp = Quat.getUp(MyAvatar.orientation);
|
||||
var platformPosition = Vec3.sum(START_POSITION, Vec3.multiply(avatarUp, -0.05));
|
||||
var platform = {
|
||||
type: "Box",
|
||||
name: "Dart Platform",
|
||||
description: "Holds darts",
|
||||
position: platformPosition,
|
||||
rotation: START_ROTATION,
|
||||
lifetime: 60,
|
||||
dimensions: {
|
||||
x: 0.15 * 5,
|
||||
y: 0.01,
|
||||
z: 0.3
|
||||
},
|
||||
color: {
|
||||
red: 129,
|
||||
green: 92,
|
||||
blue: 11
|
||||
},
|
||||
owningAvatarID: MyAvatar.sessionUUID,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: true,
|
||||
invertSolidWhileHeld: true,
|
||||
ignoreIK: false
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
Entities.addEntity(platform);
|
||||
|
||||
var dartCount = 5;
|
||||
var avatarRight = Quat.getRight(MyAvatar.orientation);
|
||||
for (var i = 0; i < dartCount; i++) {
|
||||
var j = i - Math.floor(dartCount / 2);
|
||||
var position = Vec3.sum(START_POSITION, Vec3.multiply(avatarRight, 0.15 * j));
|
||||
dart.position = position;
|
||||
Entities.addEntity(dart);
|
||||
}
|
||||
|
||||
Script.stop();
|
81
unpublishedScripts/marketplace/dart/dart.js
Normal file
81
unpublishedScripts/marketplace/dart/dart.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
"use strict";
|
||||
//
|
||||
// dart.js
|
||||
//
|
||||
// Created by MrRoboman on 17/05/13
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Simple throwing dart. Sticks to static objects.
|
||||
//
|
||||
// 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 THROW_FACTOR = 3;
|
||||
var DART_SOUND_URL = Script.resolvePath('https://hifi-content.s3.amazonaws.com/wadewatts/dart.wav?v=' + Date.now());
|
||||
|
||||
var Dart = function() {};
|
||||
|
||||
Dart.prototype = {
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
this.actionID = null;
|
||||
this.soundHasPlayed = true;
|
||||
this.dartSound = SoundCache.getSound(DART_SOUND_URL);
|
||||
},
|
||||
|
||||
playDartSound: function(sound) {
|
||||
if (this.soundHasPlayed) {
|
||||
return;
|
||||
}
|
||||
this.soundHasPlayed = true;
|
||||
var position = Entities.getEntityProperties(this.entityID, 'position').position;
|
||||
var audioProperties = {
|
||||
volume: 0.15,
|
||||
position: position
|
||||
};
|
||||
Audio.playSound(this.dartSound, audioProperties);
|
||||
},
|
||||
|
||||
releaseGrab: function() {
|
||||
this.soundHasPlayed = false;
|
||||
var velocity = Entities.getEntityProperties(this.entityID, 'velocity').velocity;
|
||||
|
||||
var newVelocity = {};
|
||||
Object.keys(velocity).forEach(function(key) {
|
||||
newVelocity[key] = velocity[key] * THROW_FACTOR;
|
||||
});
|
||||
|
||||
Entities.editEntity(this.entityID, {
|
||||
velocity: newVelocity
|
||||
});
|
||||
|
||||
if (this.actionID) {
|
||||
Entities.deleteAction(this.entityID, this.actionID);
|
||||
}
|
||||
this.actionID = Entities.addAction("travel-oriented", this.entityID, {
|
||||
forward: { x: 0, y: 0, z: 1 },
|
||||
angularTimeScale: 0.1,
|
||||
tag: "throwing dart",
|
||||
ttl: 3600
|
||||
});
|
||||
},
|
||||
|
||||
collisionWithEntity: function(myID, otherID, collisionInfo) {
|
||||
this.playDartSound();
|
||||
|
||||
Entities.editEntity(myID, {
|
||||
velocity: {x: 0, y: 0, z: 0},
|
||||
angularVelocity: {x: 0, y: 0, z: 0}
|
||||
});
|
||||
|
||||
if (this.actionID) {
|
||||
Entities.deleteAction(myID, this.actionID);
|
||||
this.actionID = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new Dart();
|
||||
});
|
Loading…
Reference in a new issue