merge from master

This commit is contained in:
SamGondelman 2017-08-04 12:17:54 -07:00
commit bd942ec406
135 changed files with 4572 additions and 1314 deletions

View file

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

BIN
Test Plan 2.docx Normal file

Binary file not shown.

View file

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

View file

@ -109,7 +109,7 @@ private:
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
AudioGate _audioGate;
bool _audioGateOpen { false };
bool _audioGateOpen { true };
bool _isNoiseGateEnabled { false };
CodecPluginPointer _codec;

View file

@ -6,8 +6,8 @@ if (WIN32)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi8.zip
URL_MD5 b01510437ea15527156bc25cdf733bd9
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi9.zip
URL_MD5 94f4765bdbcd53cd099f349ae031e769
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -22,23 +22,17 @@ macro(GENERATE_INSTALLERS)
set(CPACK_PACKAGE_FILE_NAME "HighFidelity-Beta-${BUILD_VERSION}")
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME})
if (PR_BUILD)
set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2")
endif ()
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME})
if (WIN32)
# include CMake module that will install compiler system libraries
# so that we have msvcr120 and msvcp120 installed with targets
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR})
# as long as we're including sixense plugin with installer
# we need re-distributables for VS 2011 as well
# this should be removed if/when sixense support is pulled
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS
"${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcr100.dll"
"${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcp100.dll"
)
# Do not install the Visual Studio C runtime libraries. The installer will do this automatically
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
include(InstallRequiredSystemLibraries)
set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico")
# install and reference the Add/Remove icon
@ -90,3 +84,4 @@ macro(GENERATE_INSTALLERS)
include(CPack)
endmacro()

View file

@ -22,9 +22,12 @@ macro(install_beside_console)
else ()
# setup install of executable and things copied by fixup/windeployqt
install(
FILES "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
DIRECTORY "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
DESTINATION ${COMPONENT_INSTALL_DIR}
COMPONENT ${SERVER_COMPONENT}
PATTERN "*.pdb" EXCLUDE
PATTERN "*.lib" EXCLUDE
PATTERN "*.exp" EXCLUDE
)
# on windows for PR and production builds, sign the executable

View file

@ -11,34 +11,28 @@
include(BundleUtilities)
# replace copy_resolved_item_into_bundle
#
# The official version of copy_resolved_item_into_bundle will print out a "warning:" when
# the resolved item matches the resolved embedded item. This not not really an issue that
# should rise to the level of a "warning" so we replace this message with a "status:"
#
function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
if (WIN32)
# ignore case on Windows
string(TOLOWER "${resolved_item}" resolved_item_compare)
string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
else()
set(resolved_item_compare "${resolved_item}")
set(resolved_embedded_item_compare "${resolved_embedded_item}")
function(gp_resolved_file_type_override resolved_file type_var)
if( file MATCHES ".*VCRUNTIME140.*" )
set(type "system" PARENT_SCOPE)
endif()
if ("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
# this is our only change from the original version
message(STATUS "status: resolved_item == resolved_embedded_item - not copying...")
else()
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
if(UNIX AND NOT APPLE)
file(RPATH_REMOVE FILE "${resolved_embedded_item}")
endif()
if( file MATCHES ".*concrt140.*" )
set(type "system" PARENT_SCOPE)
endif()
if( file MATCHES ".*msvcp140.*" )
set(type "system" PARENT_SCOPE)
endif()
if( file MATCHES ".*vcruntime140.*" )
set(type "system" PARENT_SCOPE)
endif()
if( file MATCHES ".*api-ms-win-crt-conio.*" )
set(type "system" PARENT_SCOPE)
endif()
if( file MATCHES ".*api-ms-win-core-winrt.*" )
set(type "system" PARENT_SCOPE)
endif()
endfunction()
message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@")
message(STATUS "Scanning for plugins from ${BUNDLE_PLUGIN_DIR}")
@ -52,3 +46,4 @@ endif()
file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}")
fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@")

View file

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

View file

@ -195,7 +195,7 @@ link_hifi_libraries(
shared octree ktx gpu gl gpu-gl procedural model render
recording fbx networking model-networking entities avatars trackers
audio audio-client animation script-engine physics
render-utils entities-renderer avatars-renderer ui auto-updater
render-utils entities-renderer avatars-renderer ui auto-updater midi
controllers plugins image trackers
ui-plugins display-plugins input-plugins
${NON_ANDROID_LIBRARIES}
@ -309,9 +309,12 @@ else (APPLE)
# setup install of executable and things copied by fixup/windeployqt
install(
FILES "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
DIRECTORY "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
DESTINATION ${INTERFACE_INSTALL_DIR}
COMPONENT ${CLIENT_COMPONENT}
PATTERN "*.pdb" EXCLUDE
PATTERN "*.lib" EXCLUDE
PATTERN "*.exp" EXCLUDE
)
set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_DIR}")

View file

@ -15,7 +15,7 @@
{ "comment" : "Mouse turn need to be small continuous increments",
"from": { "makeAxis" : [
[ "Keyboard.MouseMoveLeft" ],
[ "Keyboard.MouseMoveRight" ]
[ "Keyboard.MouseMoveRight" ]
]
},
"when": [ "Application.InHMD", "Application.SnapTurn", "Keyboard.RightMouseButton" ],
@ -31,8 +31,8 @@
{ "comment" : "Touchpad turn need to be small continuous increments, but without the RMB constraint",
"from": { "makeAxis" : [
[ "Keyboard.TouchpadLeft" ],
[ "Keyboard.TouchpadRight" ]
]
[ "Keyboard.TouchpadRight" ]
]
},
"when": [ "Application.InHMD", "Application.SnapTurn" ],
"to": "Actions.StepYaw",

View file

@ -109,6 +109,23 @@
{ "from": "Standard.Head", "to": "Actions.Head" },
{ "from": "Standard.LeftArm", "to": "Actions.LeftArm" },
{ "from": "Standard.RightArm", "to": "Actions.RightArm" }
{ "from": "Standard.RightArm", "to": "Actions.RightArm" },
{ "from": "Standard.TrackedObject00", "to" : "Actions.TrackedObject00" },
{ "from": "Standard.TrackedObject01", "to" : "Actions.TrackedObject01" },
{ "from": "Standard.TrackedObject02", "to" : "Actions.TrackedObject02" },
{ "from": "Standard.TrackedObject03", "to" : "Actions.TrackedObject03" },
{ "from": "Standard.TrackedObject04", "to" : "Actions.TrackedObject04" },
{ "from": "Standard.TrackedObject05", "to" : "Actions.TrackedObject05" },
{ "from": "Standard.TrackedObject06", "to" : "Actions.TrackedObject06" },
{ "from": "Standard.TrackedObject07", "to" : "Actions.TrackedObject07" },
{ "from": "Standard.TrackedObject08", "to" : "Actions.TrackedObject08" },
{ "from": "Standard.TrackedObject09", "to" : "Actions.TrackedObject09" },
{ "from": "Standard.TrackedObject10", "to" : "Actions.TrackedObject10" },
{ "from": "Standard.TrackedObject11", "to" : "Actions.TrackedObject11" },
{ "from": "Standard.TrackedObject12", "to" : "Actions.TrackedObject12" },
{ "from": "Standard.TrackedObject13", "to" : "Actions.TrackedObject13" },
{ "from": "Standard.TrackedObject14", "to" : "Actions.TrackedObject14" },
{ "from": "Standard.TrackedObject15", "to" : "Actions.TrackedObject15" }
]
}

View file

@ -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,24 @@
{ "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" },
{ "from": "Vive.TrackedObject00", "to" : "Standard.TrackedObject00" },
{ "from": "Vive.TrackedObject01", "to" : "Standard.TrackedObject01" },
{ "from": "Vive.TrackedObject02", "to" : "Standard.TrackedObject02" },
{ "from": "Vive.TrackedObject03", "to" : "Standard.TrackedObject03" },
{ "from": "Vive.TrackedObject04", "to" : "Standard.TrackedObject04" },
{ "from": "Vive.TrackedObject05", "to" : "Standard.TrackedObject05" },
{ "from": "Vive.TrackedObject06", "to" : "Standard.TrackedObject06" },
{ "from": "Vive.TrackedObject07", "to" : "Standard.TrackedObject07" },
{ "from": "Vive.TrackedObject08", "to" : "Standard.TrackedObject08" },
{ "from": "Vive.TrackedObject09", "to" : "Standard.TrackedObject09" },
{ "from": "Vive.TrackedObject10", "to" : "Standard.TrackedObject10" },
{ "from": "Vive.TrackedObject11", "to" : "Standard.TrackedObject11" },
{ "from": "Vive.TrackedObject12", "to" : "Standard.TrackedObject12" },
{ "from": "Vive.TrackedObject13", "to" : "Standard.TrackedObject13" },
{ "from": "Vive.TrackedObject14", "to" : "Standard.TrackedObject14" },
{ "from": "Vive.TrackedObject15", "to" : "Standard.TrackedObject15" }
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View file

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

View file

@ -33,7 +33,7 @@ Rectangle {
// only show the title if loaded through a "loader"
function showTitle() {
return root.parent.objectName == "loader";
return (root.parent !== null) && root.parent.objectName == "loader";
}
Column {

View file

@ -27,7 +27,7 @@ Rectangle {
color: "#00000000";
border {
width: (standalone || Audio.muted || mouseArea.containsMouse) ? 2 : 0;
width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0;
color: colors.border;
}
@ -61,7 +61,7 @@ Rectangle {
drag.target: dragTarget;
}
Item {
QtObject {
id: colors;
readonly property string unmuted: "#FFF";
@ -72,7 +72,7 @@ Rectangle {
readonly property string red: colors.muted;
readonly property string fill: "#55000000";
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
readonly property string icon: (Audio.muted && !mouseArea.containsMouse) ? muted : unmuted;
readonly property string icon: Audio.muted ? muted : unmuted;
}
Item {
@ -92,10 +92,8 @@ Rectangle {
readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg";
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
function exclusiveOr(a, b) { return (a || b) && !(a && b); }
id: image;
source: exclusiveOr(Audio.muted, mouseArea.containsMouse) ? mutedIcon : unmutedIcon;
source: Audio.muted ? mutedIcon : unmutedIcon;
width: 30;
height: 30;
@ -118,9 +116,9 @@ Rectangle {
Item {
id: status;
readonly property string color: (Audio.muted && !mouseArea.containsMouse) ? colors.muted : colors.unmuted;
readonly property string color: Audio.muted ? colors.muted : colors.unmuted;
visible: Audio.muted || mouseArea.containsMouse;
visible: Audio.muted;
anchors {
left: parent.left;
@ -133,14 +131,14 @@ Rectangle {
Text {
anchors {
horizontalCenter: parent.horizontalCenter;
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
color: parent.color;
text: Audio.muted ? (mouseArea.containsMouse ? "UNMUTE" : "MUTED") : "MUTE";
font.pointSize: 12;
text: Audio.muted ? "MUTED" : "MUTE";
font.pointSize: 12;
}
Rectangle {
@ -150,7 +148,7 @@ Rectangle {
}
width: 50;
height: 4;
height: 4;
color: parent.color;
}
@ -161,7 +159,7 @@ Rectangle {
}
width: 50;
height: 4;
height: 4;
color: parent.color;
}
}

View file

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

View file

@ -202,5 +202,11 @@ Item {
width: 480
height: 706
function setShown(value) {}
function setShown(value) {
if (value === true) {
HMD.openTablet()
} else {
HMD.closeTablet()
}
}
}

View file

@ -61,6 +61,7 @@
#include <AssetClient.h>
#include <AssetUpload.h>
#include <AutoUpdater.h>
#include <Midi.h>
#include <AudioInjectorManager.h>
#include <AvatarBookmarks.h>
#include <CursorManager.h>
@ -69,7 +70,7 @@
#include <EntityScriptClient.h>
#include <EntityScriptServerLogClient.h>
#include <EntityScriptingInterface.h>
#include <HoverOverlayInterface.h>
#include "ui/overlays/ContextOverlayInterface.h"
#include <ErrorDialog.h>
#include <FileScriptingInterface.h>
#include <Finally.h>
@ -400,6 +401,10 @@ public:
return true;
}
}
if (message->message == WM_DEVICECHANGE) {
Midi::USBchanged(); // re-scan the MIDI bus
}
}
return false;
}
@ -569,6 +574,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>();
@ -591,7 +597,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<Snapshot>();
DependencyManager::set<CloseEventSender>();
DependencyManager::set<ResourceManager>();
DependencyManager::set<HoverOverlayInterface>();
DependencyManager::set<ContextOverlayInterface>();
DependencyManager::set<LaserPointerScriptingInterface>();
DependencyManager::set<LaserPointerManager>();
@ -624,6 +630,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),
@ -643,6 +650,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),
@ -938,14 +946,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);
@ -1329,12 +1330,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Keyboard focus handling for Web overlays.
auto overlays = &(qApp->getOverlays());
connect(overlays, &Overlays::mousePressOnOverlay, [=](OverlayID overlayID, const PointerEvent& event) {
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
setKeyboardFocusOverlay(overlayID);
connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) {
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(overlays->getOverlay(overlayID));
// Only Web overlays can have keyboard focus.
if (thisOverlay) {
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
setKeyboardFocusOverlay(overlayID);
}
});
connect(overlays, &Overlays::overlayDeleted, [=](OverlayID overlayID) {
connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) {
if (overlayID == _keyboardFocusedOverlay.get()) {
setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
}
@ -1349,6 +1354,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
});
connect(overlays,
SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)),
DependencyManager::get<ContextOverlayInterface>().data(),
SLOT(contextOverlays_mousePressOnOverlay(const OverlayID&, const PointerEvent&)));
connect(overlays,
SIGNAL(hoverEnterOverlay(const OverlayID&, const PointerEvent&)),
DependencyManager::get<ContextOverlayInterface>().data(),
SLOT(contextOverlays_hoverEnterOverlay(const OverlayID&, const PointerEvent&)));
connect(overlays,
SIGNAL(hoverLeaveOverlay(const OverlayID&, const PointerEvent&)),
DependencyManager::get<ContextOverlayInterface>().data(),
SLOT(contextOverlays_hoverLeaveOverlay(const OverlayID&, const PointerEvent&)));
// Add periodic checks to send user activity data
static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000;
static int NEARBY_AVATAR_RADIUS_METERS = 10;
@ -1475,7 +1495,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
properties["atp_mapping_requests"] = atpMappingRequests;
properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false;
QJsonObject bytesDownloaded;
bytesDownloaded["atp"] = statTracker->getStat(STAT_ATP_RESOURCE_TOTAL_BYTES).toInt();
bytesDownloaded["http"] = statTracker->getStat(STAT_HTTP_RESOURCE_TOTAL_BYTES).toInt();
@ -1743,9 +1763,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;
}
@ -2120,7 +2147,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());
@ -2130,7 +2156,7 @@ void Application::initializeUi() {
surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
surfaceContext->setContextProperty("HoverOverlay", DependencyManager::get<HoverOverlayInterface>().data());
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
@ -2166,9 +2192,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);
}
});
@ -2320,7 +2348,7 @@ void Application::paintGL() {
}
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
if (isHMDMode()) {
auto mirrorBodyOrientation = myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f));
auto mirrorBodyOrientation = myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f));
glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix());
// Mirror HMD yaw and roll
@ -2342,7 +2370,7 @@ void Application::paintGL() {
+ mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror
+ mirrorBodyOrientation * hmdOffset);
} else {
_myCamera.setOrientation(myAvatar->getWorldAlignedOrientation()
_myCamera.setOrientation(myAvatar->getOrientation()
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
+ glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0)
@ -2524,6 +2552,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);
@ -3040,7 +3074,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_F: {
_physicsEngine->dumpNextStats();
if (isOption) {
_physicsEngine->dumpNextStats();
}
break;
}
@ -3084,9 +3120,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);
@ -4480,27 +4520,29 @@ void Application::cameraModeChanged() {
void Application::cameraMenuChanged() {
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
auto menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
_myCamera.setMode(CAMERA_MODE_MIRROR);
getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers
}
} 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);
}
@ -5404,6 +5446,17 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
}
renderArgs->_debugFlags = renderDebugFlags;
//ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction);
RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE;
auto contextOverlayInterface = DependencyManager::get<ContextOverlayInterface>();
if (contextOverlayInterface->getEnabled()) {
if (DependencyManager::get<ContextOverlayInterface>()->getIsInMarketplaceInspectionMode()) {
renderOutlineFlags = RenderArgs::RENDER_OUTLINE_MARKETPLACE_MODE;
} else {
renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES;
}
}
renderArgs->_outlineFlags = renderOutlineFlags;
}
}
@ -5868,7 +5921,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
auto entityScriptServerLog = DependencyManager::get<EntityScriptServerLogClient>();
scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data());
scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance());
scriptEngine->registerGlobalObject("HoverOverlay", DependencyManager::get<HoverOverlayInterface>().data());
scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
scriptEngine->registerGlobalObject("RayPick", DependencyManager::get<RayPickManager>().data());

View file

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

View file

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

View file

@ -258,6 +258,7 @@ MyAvatar::~MyAvatar() {
void MyAvatar::setDominantHand(const QString& hand) {
if (hand == DOMINANT_LEFT_HAND || hand == DOMINANT_RIGHT_HAND) {
_dominantHand = hand;
emit dominantHandChanged(_dominantHand);
}
}
@ -613,7 +614,7 @@ void MyAvatar::simulate(float deltaTime) {
MovingEntitiesOperator moveOperator(entityTree);
forEachDescendant([&](SpatiallyNestablePointer object) {
// if the queryBox has changed, tell the entity-server
if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) {
if (object->getNestableType() == NestableType::Entity && object->checkAndMaybeUpdateQueryAACube()) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
bool success;
AACube newCube = entity->getQueryAACube(success);
@ -1298,7 +1299,7 @@ eyeContactTarget MyAvatar::getEyeContactTarget() {
}
glm::vec3 MyAvatar::getDefaultEyePosition() const {
return getPosition() + getWorldAlignedOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition();
return getPosition() + getOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition();
}
const float SCRIPT_PRIORITY = 1.0f + 1.0f;
@ -1593,9 +1594,14 @@ void MyAvatar::updateMotors() {
motorRotation = getMyHead()->getHeadOrientation();
} else {
// non-hovering = walking: follow camera twist about vertical but not lift
// so we decompose camera's rotation and store the twist part in motorRotation
// we decompose camera's rotation and store the twist part in motorRotation
// however, we need to perform the decomposition in the avatar-frame
// using the local UP axis and then transform back into world-frame
glm::quat orientation = getOrientation();
glm::quat headOrientation = glm::inverse(orientation) * getMyHead()->getHeadOrientation(); // avatar-frame
glm::quat liftRotation;
swingTwistDecomposition(getMyHead()->getHeadOrientation(), _worldUpDirection, liftRotation, motorRotation);
swingTwistDecomposition(headOrientation, Vectors::UNIT_Y, liftRotation, motorRotation);
motorRotation = orientation * motorRotation;
}
const float DEFAULT_MOTOR_TIMESCALE = 0.2f;
const float INVALID_MOTOR_TIMESCALE = 1.0e6f;
@ -1649,11 +1655,31 @@ void MyAvatar::prepareForPhysicsSimulation() {
_prePhysicsRoomPose = AnimPose(_sensorToWorldMatrix);
}
// There are a number of possible strategies for this set of tools through endRender, below.
void MyAvatar::nextAttitude(glm::vec3 position, glm::quat orientation) {
bool success;
Transform trans = getTransform(success);
if (!success) {
qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed";
return;
}
trans.setTranslation(position);
trans.setRotation(orientation);
SpatiallyNestable::setTransform(trans, success);
if (!success) {
qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed";
}
updateAttitude(orientation);
}
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
glm::vec3 position = getPosition();
glm::quat orientation = getOrientation();
glm::vec3 position;
glm::quat orientation;
if (_characterController.isEnabledAndReady()) {
_characterController.getPositionAndOrientation(position, orientation);
} else {
position = getPosition();
orientation = getOrientation();
}
nextAttitude(position, orientation);
_bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix);
@ -2848,7 +2874,8 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix,
const glm::mat4& currentBodyMatrix, bool hasDriveInput) {
if (myAvatar.getHMDLeanRecenterEnabled()) {
if (myAvatar.getHMDLeanRecenterEnabled() &&
qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) {
if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Rotation);
}

View file

@ -438,6 +438,7 @@ public:
void updateMotors();
void prepareForPhysicsSimulation();
void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time.
void harvestResultsFromPhysicsSimulation(float deltaTime);
const QString& getCollisionSoundURL() { return _collisionSoundURL; }
@ -557,7 +558,6 @@ public:
Q_INVOKABLE bool isUp(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) > 0.0f; }; // true iff direction points up wrt avatar's definition of up.
Q_INVOKABLE bool isDown(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) < 0.0f; };
public slots:
void increaseSize();
void decreaseSize();
@ -615,6 +615,7 @@ signals:
void wentAway();
void wentActive();
void skeletonChanged();
void dominantHandChanged(const QString& hand);
private:

View file

@ -29,7 +29,7 @@ MyHead::MyHead(MyAvatar* owningAvatar) : Head(owningAvatar) {
glm::quat MyHead::getHeadOrientation() const {
// NOTE: Head::getHeadOrientation() is not used for orienting the camera "view" while in Oculus mode, so
// you may wonder why this code is here. This method will be called while in Oculus mode to determine how
// to change the driving direction while in Oculus mode. It is used to support driving toward where you're
// to change the driving direction while in Oculus mode. It is used to support driving toward where your
// head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not
// always the same.
@ -39,7 +39,7 @@ glm::quat MyHead::getHeadOrientation() const {
return headPose.rotation * Quaternions::Y_180;
}
return myAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f)));
return myAvatar->getOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f)));
}
void MyHead::simulate(float deltaTime) {

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "JSConsole.h"
#include <QFuture>
#include <QKeyEvent>
#include <QLabel>
@ -20,7 +22,6 @@
#include <PathUtils.h>
#include "Application.h"
#include "JSConsole.h"
#include "ScriptHighlighting.h"
const int NO_CURRENT_HISTORY_COMMAND = -1;
@ -60,14 +61,12 @@ void _writeLines(const QString& filename, const QList<QString>& lines) {
QTextStream(&file) << json;
}
JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) :
JSConsole::JSConsole(QWidget* parent, const QSharedPointer<ScriptEngine>& scriptEngine) :
QWidget(parent),
_ui(new Ui::Console),
_currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND),
_savedHistoryFilename(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + HISTORY_FILENAME),
_commandHistory(_readLines(_savedHistoryFilename)),
_ownScriptEngine(scriptEngine == NULL),
_scriptEngine(NULL) {
_commandHistory(_readLines(_savedHistoryFilename)) {
_ui->setupUi(this);
_ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap);
_ui->promptTextEdit->setWordWrapMode(QTextOption::NoWrap);
@ -90,36 +89,37 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) :
}
JSConsole::~JSConsole() {
disconnect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
disconnect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&)));
if (_ownScriptEngine) {
_scriptEngine->deleteLater();
if (_scriptEngine) {
disconnect(_scriptEngine.data(), SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
disconnect(_scriptEngine.data(), SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&)));
_scriptEngine.reset();
}
delete _ui;
}
void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) {
void JSConsole::setScriptEngine(const QSharedPointer<ScriptEngine>& scriptEngine) {
if (_scriptEngine == scriptEngine && scriptEngine != NULL) {
return;
}
if (_scriptEngine != NULL) {
disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint);
disconnect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo);
disconnect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning);
disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError);
if (_ownScriptEngine) {
_scriptEngine->deleteLater();
}
disconnect(_scriptEngine.data(), &ScriptEngine::printedMessage, this, &JSConsole::handlePrint);
disconnect(_scriptEngine.data(), &ScriptEngine::infoMessage, this, &JSConsole::handleInfo);
disconnect(_scriptEngine.data(), &ScriptEngine::warningMessage, this, &JSConsole::handleWarning);
disconnect(_scriptEngine.data(), &ScriptEngine::errorMessage, this, &JSConsole::handleError);
_scriptEngine.reset();
}
// if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine
_ownScriptEngine = (scriptEngine == NULL);
_scriptEngine = _ownScriptEngine ? DependencyManager::get<ScriptEngines>()->loadScript(_consoleFileName, false) : scriptEngine;
if (scriptEngine.isNull()) {
_scriptEngine = QSharedPointer<ScriptEngine>(DependencyManager::get<ScriptEngines>()->loadScript(_consoleFileName, false), &QObject::deleteLater);
} else {
_scriptEngine = scriptEngine;
}
connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint);
connect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo);
connect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning);
connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError);
connect(_scriptEngine.data(), &ScriptEngine::printedMessage, this, &JSConsole::handlePrint);
connect(_scriptEngine.data(), &ScriptEngine::infoMessage, this, &JSConsole::handleInfo);
connect(_scriptEngine.data(), &ScriptEngine::warningMessage, this, &JSConsole::handleWarning);
connect(_scriptEngine.data(), &ScriptEngine::errorMessage, this, &JSConsole::handleError);
}
void JSConsole::executeCommand(const QString& command) {
@ -135,19 +135,22 @@ void JSConsole::executeCommand(const QString& command) {
appendMessage(">", "<span style='" + COMMAND_STYLE + "'>" + command.toHtmlEscaped() + "</span>");
QFuture<QScriptValue> future = QtConcurrent::run(this, &JSConsole::executeCommandInWatcher, command);
QWeakPointer<ScriptEngine> weakScriptEngine = _scriptEngine;
auto consoleFileName = _consoleFileName;
QFuture<QScriptValue> future = QtConcurrent::run([weakScriptEngine, consoleFileName, command]()->QScriptValue{
QScriptValue result;
auto scriptEngine = weakScriptEngine.lock();
if (scriptEngine) {
BLOCKING_INVOKE_METHOD(scriptEngine.data(), "evaluate",
Q_RETURN_ARG(QScriptValue, result),
Q_ARG(const QString&, command),
Q_ARG(const QString&, consoleFileName));
}
return result;
});
_executeWatcher.setFuture(future);
}
QScriptValue JSConsole::executeCommandInWatcher(const QString& command) {
QScriptValue result;
BLOCKING_INVOKE_METHOD(_scriptEngine, "evaluate",
Q_RETURN_ARG(QScriptValue, result),
Q_ARG(const QString&, command),
Q_ARG(const QString&, _consoleFileName));
return result;
}
void JSConsole::commandFinished() {
QScriptValue result = _executeWatcher.result();

View file

@ -17,6 +17,7 @@
#include <QFutureWatcher>
#include <QObject>
#include <QWidget>
#include <QSharedPointer>
#include "ui_console.h"
#include "ScriptEngine.h"
@ -29,10 +30,10 @@ const int CONSOLE_HEIGHT = 200;
class JSConsole : public QWidget {
Q_OBJECT
public:
JSConsole(QWidget* parent, ScriptEngine* scriptEngine = NULL);
JSConsole(QWidget* parent, const QSharedPointer<ScriptEngine>& scriptEngine = QSharedPointer<ScriptEngine>());
~JSConsole();
void setScriptEngine(ScriptEngine* scriptEngine = NULL);
void setScriptEngine(const QSharedPointer<ScriptEngine>& scriptEngine = QSharedPointer<ScriptEngine>());
void clear();
public slots:
@ -58,17 +59,14 @@ private:
void setToNextCommandInHistory();
void setToPreviousCommandInHistory();
void resetCurrentCommandHistory();
QScriptValue executeCommandInWatcher(const QString& command);
QFutureWatcher<QScriptValue> _executeWatcher;
Ui::Console* _ui;
int _currentCommandInHistory;
QString _savedHistoryFilename;
QList<QString> _commandHistory;
// Keeps track if the script engine is created inside the JSConsole
bool _ownScriptEngine;
QString _rootCommand;
ScriptEngine* _scriptEngine;
QSharedPointer<ScriptEngine> _scriptEngine;
static const QString _consoleFileName;
};

View file

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

View file

@ -24,12 +24,12 @@ TestingDialog::TestingDialog(QWidget* parent) :
_console->setFixedHeight(TESTING_CONSOLE_HEIGHT);
auto _engines = DependencyManager::get<ScriptEngines>();
_engine = _engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath);
_engine.reset(_engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath));
_console->setScriptEngine(_engine);
connect(_engine, &ScriptEngine::finished, this, &TestingDialog::onTestingFinished);
connect(_engine.data(), &ScriptEngine::finished, this, &TestingDialog::onTestingFinished);
}
void TestingDialog::onTestingFinished(const QString& scriptPath) {
_engine = nullptr;
_console->setScriptEngine(nullptr);
_engine.reset();
_console->setScriptEngine();
}

View file

@ -29,7 +29,7 @@ public:
private:
std::unique_ptr<JSConsole> _console;
ScriptEngine* _engine;
QSharedPointer<ScriptEngine> _engine;
};
#endif

View file

@ -0,0 +1,271 @@
//
// ContextOverlayInterface.cpp
// interface/src/ui/overlays
//
// Created by Zach Fox on 2017-07-14.
// Copyright 2017 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 "ContextOverlayInterface.h"
#include "Application.h"
#include <EntityTreeRenderer.h>
static const float CONTEXT_OVERLAY_TABLET_OFFSET = 30.0f; // Degrees
static const float CONTEXT_OVERLAY_TABLET_ORIENTATION = 210.0f; // Degrees
static const float CONTEXT_OVERLAY_TABLET_DISTANCE = 0.65F; // Meters
ContextOverlayInterface::ContextOverlayInterface() {
// "context_overlay" debug log category disabled by default.
// Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable
// if you'd like to enable/disable certain categories.
// More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories
QLoggingCategory::setFilterRules(QStringLiteral("hifi.context_overlay.debug=false"));
_entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
_hmdScriptingInterface = DependencyManager::get<HMDScriptingInterface>();
_tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
_entityPropertyFlags += PROP_POSITION;
_entityPropertyFlags += PROP_ROTATION;
_entityPropertyFlags += PROP_MARKETPLACE_ID;
_entityPropertyFlags += PROP_DIMENSIONS;
_entityPropertyFlags += PROP_REGISTRATION_POINT;
// initially, set _enabled to match the switch. Later we enable/disable via the getter/setters
// if we are in edit or pal (for instance). Note this is temporary, as we expect to enable this all
// the time after getting edge highlighting, etc...
_enabled = _settingSwitch.get();
auto entityTreeRenderer = DependencyManager::get<EntityTreeRenderer>().data();
connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&)));
connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverEnterEntity(const EntityItemID&, const PointerEvent&)));
connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverLeaveEntity(const EntityItemID&, const PointerEvent&)));
connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() {
if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) {
QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID();
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::quat cameraOrientation = qApp->getCamera().getOrientation();
QVariantMap props;
props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_OFFSET, 0.0f))) * (CONTEXT_OVERLAY_TABLET_DISTANCE * (cameraOrientation * Vectors::FRONT))));
props.insert("orientation", quatToVariant(cameraOrientation * glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_ORIENTATION, 0.0f)))));
qApp->getOverlays().editOverlay(tabletFrameID, props);
_contextOverlayJustClicked = false;
}
});
}
static const uint32_t LEFT_HAND_HW_ID = 1;
static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 };
static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters
static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters
static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims
static const float CONTEXT_OVERLAY_FAR_SIZE = 0.08f; // in meters, same x and y dims
static const float CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE = 20.0f;
static const float CONTEXT_OVERLAY_UNHOVERED_ALPHA = 0.85f;
static const float CONTEXT_OVERLAY_HOVERED_ALPHA = 1.0f;
static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f;
static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f;
static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f;
static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f;
static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f;
void ContextOverlayInterface::setEnabled(bool enabled) {
// only enable/disable if the setting in 'on'. If it is 'off',
// make sure _enabled is always false.
if (_settingSwitch.get()) {
_enabled = enabled;
} else {
_enabled = false;
}
}
bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {
if (_enabled && event.getButton() == PointerEvent::SecondaryButton) {
if (contextOverlayFilterPassed(entityItemID)) {
qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID;
// Add all necessary variables to the stack
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags);
glm::vec3 cameraPosition = qApp->getCamera().getPosition();
float distanceFromCameraToEntity = glm::distance(entityProperties.getPosition(), cameraPosition);
glm::vec3 entityDimensions = entityProperties.getDimensions();
glm::vec3 entityPosition = entityProperties.getPosition();
glm::vec3 contextOverlayPosition = entityProperties.getPosition();
glm::vec2 contextOverlayDimensions;
// Update the position of the overlay if the registration point of the entity
// isn't default
if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) {
glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f);
entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions()));
}
enableEntityHighlight(entityItemID);
AABox boundingBox = AABox(entityPosition - (entityDimensions / 2.0f), entityDimensions * 2.0f);
// Update the cached Entity Marketplace ID
_entityMarketplaceID = entityProperties.getMarketplaceID();
if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) {
disableEntityHighlight(_currentEntityWithContextOverlay);
}
// Update the cached "Current Entity with Context Overlay" variable
setCurrentEntityWithContextOverlay(entityItemID);
// Here, we determine the position and dimensions of the Context Overlay.
if (boundingBox.contains(cameraPosition)) {
// If the camera is inside the box...
// ...position the Context Overlay 1 meter in front of the camera.
contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT);
contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition);
} else if (distanceFromCameraToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) {
// Else if the entity is too close to the camera...
// ...rotate the Context Overlay to the right of the entity.
// This makes it easy to inspect things you're holding.
float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE;
if (event.getID() == LEFT_HAND_HW_ID) {
offsetAngle *= -1;
}
contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityPosition - cameraPosition)) + cameraPosition;
contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition);
} else {
// Else, place the Context Overlay some offset away from the entity's bounding
// box in the direction of the camera.
glm::vec3 direction = glm::normalize(entityPosition - cameraPosition);
float distance;
BoxFace face;
glm::vec3 normal;
boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal);
contextOverlayPosition = (cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_FAR_OFFSET;
contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition);
}
// Finally, setup and draw the Context Overlay
if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) {
_contextOverlay = std::make_shared<Image3DOverlay>();
_contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA);
_contextOverlay->setPulseMin(CONTEXT_OVERLAY_UNHOVERED_PULSEMIN);
_contextOverlay->setPulseMax(CONTEXT_OVERLAY_UNHOVERED_PULSEMAX);
_contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE);
_contextOverlay->setIgnoreRayIntersection(false);
_contextOverlay->setDrawInFront(true);
_contextOverlay->setURL(PathUtils::resourcesPath() + "images/inspect-icon.png");
_contextOverlay->setIsFacingAvatar(true);
_contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay);
}
_contextOverlay->setPosition(contextOverlayPosition);
_contextOverlay->setDimensions(contextOverlayDimensions);
_contextOverlay->setRotation(entityProperties.getRotation());
_contextOverlay->setVisible(true);
return true;
}
} else {
if (!_currentEntityWithContextOverlay.isNull()) {
return destroyContextOverlay(_currentEntityWithContextOverlay, event);
}
return false;
}
return false;
}
bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) {
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags);
return (entityProperties.getMarketplaceID().length() != 0);
}
bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {
if (_contextOverlayID != UNKNOWN_OVERLAY_ID) {
qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID;
disableEntityHighlight(entityItemID);
setCurrentEntityWithContextOverlay(QUuid());
_entityMarketplaceID.clear();
// Destroy the Context Overlay
qApp->getOverlays().deleteOverlay(_contextOverlayID);
_contextOverlay = NULL;
_contextOverlayID = UNKNOWN_OVERLAY_ID;
return true;
}
return false;
}
bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) {
return ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent());
}
void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) {
qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID;
openMarketplace();
destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent());
_contextOverlayJustClicked = true;
}
}
void ContextOverlayInterface::contextOverlays_hoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event) {
if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) {
qCDebug(context_overlay) << "Started hovering over Context Overlay. Overlay ID:" << overlayID;
_contextOverlay->setColor(CONTEXT_OVERLAY_COLOR);
_contextOverlay->setColorPulse(0.0f); // pulse off
_contextOverlay->setPulsePeriod(0.0f); // pulse off
_contextOverlay->setAlpha(CONTEXT_OVERLAY_HOVERED_ALPHA);
}
}
void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event) {
if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) {
qCDebug(context_overlay) << "Stopped hovering over Context Overlay. Overlay ID:" << overlayID;
_contextOverlay->setColor(CONTEXT_OVERLAY_COLOR);
_contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE);
_contextOverlay->setPulsePeriod(CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD);
_contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA);
}
}
void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) {
if (contextOverlayFilterPassed(entityID)) {
enableEntityHighlight(entityID);
}
}
void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) {
if (_currentEntityWithContextOverlay != entityID) {
disableEntityHighlight(entityID);
}
}
static const QString MARKETPLACE_BASE_URL = "https://metaverse.highfidelity.com/marketplace/items/";
void ContextOverlayInterface::openMarketplace() {
// lets open the tablet and go to the current item in
// the marketplace (if the current entity has a
// marketplaceID)
if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) {
auto tablet = dynamic_cast<TabletProxy*>(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
// construct the url to the marketplace item
QString url = MARKETPLACE_BASE_URL + _entityMarketplaceID;
tablet->gotoWebScreen(url);
_hmdScriptingInterface->openTablet();
_isInMarketplaceInspectionMode = true;
}
}
void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) {
if (!qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) {
qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID;
qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true);
}
}
void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityItemID) {
if (qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) {
qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID;
qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false);
}
}

View file

@ -0,0 +1,85 @@
//
// ContextOverlayInterface.h
// interface/src/ui/overlays
//
// Created by Zach Fox on 2017-07-14.
// Copyright 2017 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
//
#pragma once
#ifndef hifi_ContextOverlayInterface_h
#define hifi_ContextOverlayInterface_h
#include <QtCore/QObject>
#include <QUuid>
#include <DependencyManager.h>
#include <PointerEvent.h>
#include <ui/TabletScriptingInterface.h>
#include "avatar/AvatarManager.h"
#include "EntityScriptingInterface.h"
#include "ui/overlays/Image3DOverlay.h"
#include "ui/overlays/Overlays.h"
#include "scripting/HMDScriptingInterface.h"
#include "EntityTree.h"
#include "ContextOverlayLogging.h"
/**jsdoc
* @namespace ContextOverlay
*/
class ContextOverlayInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay)
Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled)
Q_PROPERTY(bool isInMarketplaceInspectionMode READ getIsInMarketplaceInspectionMode WRITE setIsInMarketplaceInspectionMode)
QSharedPointer<EntityScriptingInterface> _entityScriptingInterface;
EntityPropertyFlags _entityPropertyFlags;
QSharedPointer<HMDScriptingInterface> _hmdScriptingInterface;
QSharedPointer<TabletScriptingInterface> _tabletScriptingInterface;
OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID };
std::shared_ptr<Image3DOverlay> _contextOverlay { nullptr };
public:
ContextOverlayInterface();
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; }
void setEnabled(bool enabled);
bool getEnabled() { return _enabled; }
bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; }
void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; }
public slots:
bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
bool destroyContextOverlay(const EntityItemID& entityItemID);
void contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event);
void contextOverlays_hoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event);
void contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event);
void contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event);
void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event);
bool contextOverlayFilterPassed(const EntityItemID& entityItemID);
private:
bool _verboseLogging { true };
bool _enabled { true };
QUuid _currentEntityWithContextOverlay{};
QString _entityMarketplaceID;
bool _contextOverlayJustClicked { false };
bool _isInMarketplaceInspectionMode { false };
Setting::Handle<bool> _settingSwitch { "inspectionMode", false };
void openMarketplace();
void enableEntityHighlight(const EntityItemID& entityItemID);
void disableEntityHighlight(const EntityItemID& entityItemID);
};
#endif // hifi_ContextOverlayInterface_h

View file

@ -1,6 +1,6 @@
//
// HoverOverlayLogging.cpp
// libraries/entities/src
// ContextOverlayLogging.cpp
// interface/src/ui/overlays
//
// Created by Zach Fox on 2017-07-17
// Copyright 2017 High Fidelity, Inc.
@ -9,6 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "HoverOverlayLogging.h"
#include "ContextOverlayLogging.h"
Q_LOGGING_CATEGORY(hover_overlay, "hifi.hover_overlay")
Q_LOGGING_CATEGORY(context_overlay, "hifi.context_overlay")

View file

@ -1,6 +1,6 @@
//
// HoverOverlayLogging.h
// libraries/entities/src
// ContextOverlayLogging.h
// interface/src/ui/overlays
//
// Created by Zach Fox on 2017-07-17
// Copyright 2017 High Fidelity, Inc.
@ -10,11 +10,11 @@
//
#pragma once
#ifndef hifi_HoverOverlayLogging_h
#define hifi_HoverOverlayLogging_h
#ifndef hifi_ContextOverlayLogging_h
#define hifi_ContextOverlayLogging_h
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(hover_overlay)
Q_DECLARE_LOGGING_CATEGORY(context_overlay)
#endif // hifi_HoverOverlayLogging_h
#endif // hifi_ContextOverlayLogging_h

View file

@ -22,7 +22,7 @@ Overlay::Overlay() :
_renderItemID(render::Item::INVALID_ITEM_ID),
_isLoaded(true),
_alpha(DEFAULT_ALPHA),
_pulse(0.0f),
_pulse(1.0f),
_pulseMax(0.0f),
_pulseMin(0.0f),
_pulsePeriod(1.0f),

View file

@ -130,7 +130,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??
@ -167,7 +167,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);
@ -301,8 +301,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));
@ -764,28 +764,28 @@ bool Overlays::isAddedOverlay(OverlayID id) {
return _overlaysHUD.contains(id) || _overlays3DHUD.contains(id) || _overlaysWorld.contains(id);
}
void Overlays::sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event) {
emit mousePressOnOverlay(overlayID, event);
void Overlays::sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
QMetaObject::invokeMethod(this, "mousePressOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event));
}
void Overlays::sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event) {
emit mouseReleaseOnOverlay(overlayID, event);
void Overlays::sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
QMetaObject::invokeMethod(this, "mouseReleaseOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event));
}
void Overlays::sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event) {
emit mouseMoveOnOverlay(overlayID, event);
void Overlays::sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
QMetaObject::invokeMethod(this, "mouseMoveOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event));
}
void Overlays::sendHoverEnterOverlay(OverlayID id, PointerEvent event) {
emit hoverEnterOverlay(id, event);
void Overlays::sendHoverEnterOverlay(const OverlayID& id, const PointerEvent& event) {
QMetaObject::invokeMethod(this, "hoverEnterOverlay", Q_ARG(OverlayID, id), Q_ARG(PointerEvent, event));
}
void Overlays::sendHoverOverOverlay(OverlayID id, 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(OverlayID id, 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() {
@ -834,7 +834,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.
@ -877,15 +877,20 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) {
}
}
PointerEvent Overlays::calculatePointerEvent(Overlay::Pointer overlay, PickRay ray,
PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray,
RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event,
PointerEvent::EventType eventType) {
auto overlay = std::dynamic_pointer_cast<Planar3DOverlay>(getOverlay(overlayID));
if (getOverlayType(overlayID) == "web3d") {
overlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(overlayID));
}
if (!overlay) {
return PointerEvent();
}
glm::vec3 position = overlay->getPosition();
glm::quat rotation = overlay->getRotation();
glm::vec2 dimensions = overlay->getSize();
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(overlay);
auto position = thisOverlay->getPosition();
auto rotation = thisOverlay->getRotation();
auto dimensions = thisOverlay->getSize();
glm::vec2 pos2D = projectOntoOverlayXYPlane(position, rotation, dimensions, ray, rayPickResult);
@ -933,13 +938,9 @@ bool Overlays::mousePressEvent(QMouseEvent* event) {
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
// Only Web overlays can have focus.
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(_currentClickingOnOverlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press);
emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent);
return true;
}
PointerEvent pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press);
emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent);
return true;
}
emit mousePressOffOverlay();
return false;
@ -953,13 +954,9 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) {
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
// Only Web overlays can have focus.
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(_currentClickingOnOverlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press);
emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent);
return true;
}
auto pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press);
emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent);
return true;
}
emit mouseDoublePressOffOverlay();
return false;
@ -971,13 +968,8 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) {
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
if (rayPickResult.intersects) {
// Only Web overlays can have focus.
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(rayPickResult.overlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Release);
emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent);
}
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release);
emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent);
}
_currentClickingOnOverlayID = UNKNOWN_OVERLAY_ID;
@ -990,40 +982,29 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
if (rayPickResult.intersects) {
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move);
emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent);
// Only Web overlays can have focus.
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(rayPickResult.overlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move);
emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent);
// If previously hovering over a different overlay then leave hover on that overlay.
if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) {
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(_currentHoverOverOverlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move);
emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent);
}
}
// If hovering over a new overlay then enter hover on that overlay.
if (rayPickResult.overlayID != _currentHoverOverOverlayID) {
emit hoverEnterOverlay(rayPickResult.overlayID, pointerEvent);
}
// Hover over current overlay.
emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent);
_currentHoverOverOverlayID = rayPickResult.overlayID;
// If previously hovering over a different overlay then leave hover on that overlay.
if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) {
auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move);
emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent);
}
// If hovering over a new overlay then enter hover on that overlay.
if (rayPickResult.overlayID != _currentHoverOverOverlayID) {
emit hoverEnterOverlay(rayPickResult.overlayID, pointerEvent);
}
// Hover over current overlay.
emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent);
_currentHoverOverOverlayID = rayPickResult.overlayID;
} else {
// If previously hovering an overlay then leave hover.
if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID) {
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(_currentHoverOverOverlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move);
emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent);
}
auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move);
emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent);
_currentHoverOverOverlayID = UNKNOWN_OVERLAY_ID;
}

View file

@ -134,7 +134,7 @@ public slots:
OverlayID cloneOverlay(OverlayID id);
/**jsdoc
* Edit an overlay's properties.
* Edit an overlay's properties.
*
* @function Overlays.editOverlay
* @param {Overlays.OverlayID} overlayID The ID of the overlay to edit.
@ -291,13 +291,13 @@ public slots:
#endif
void sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
void sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event);
void sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event);
void sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event);
void sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event);
void sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event);
void sendHoverEnterOverlay(OverlayID id, PointerEvent event);
void sendHoverOverOverlay(OverlayID id, PointerEvent event);
void sendHoverLeaveOverlay(OverlayID id, PointerEvent event);
void sendHoverEnterOverlay(const OverlayID& id, const PointerEvent& event);
void sendHoverOverOverlay(const OverlayID& id, const PointerEvent& event);
void sendHoverLeaveOverlay(const OverlayID& id, const PointerEvent& event);
OverlayID getKeyboardFocusOverlay();
void setKeyboardFocusOverlay(OverlayID id);
@ -326,7 +326,7 @@ signals:
private:
void cleanupOverlaysToDelete();
mutable QMutex _mutex;
mutable QMutex _mutex { QMutex::Recursive };
QMap<OverlayID, Overlay::Pointer> _overlaysHUD;
QMap<OverlayID, Overlay::Pointer> _overlays3DHUD;
QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
@ -344,7 +344,7 @@ private:
#endif
bool _enabled = true;
PointerEvent calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult,
PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult,
QMouseEvent* event, PointerEvent::EventType eventType);
OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID };

View file

@ -21,6 +21,7 @@ public:
Planar3DOverlay(const Planar3DOverlay* planar3DOverlay);
virtual AABox getBounds() const override;
virtual glm::vec2 getSize() const { return _dimensions; };
glm::vec2 getDimensions() const { return _dimensions; }
void setDimensions(float value) { _dimensions = glm::vec2(value); }

View file

@ -100,30 +100,14 @@ Web3DOverlay::~Web3DOverlay() {
}
_webSurface->pause();
_webSurface->disconnect(_connection);
QObject::disconnect(_mousePressConnection);
_mousePressConnection = QMetaObject::Connection();
QObject::disconnect(_mouseReleaseConnection);
_mouseReleaseConnection = QMetaObject::Connection();
QObject::disconnect(_mouseMoveConnection);
_mouseMoveConnection = QMetaObject::Connection();
QObject::disconnect(_hoverLeaveConnection);
_hoverLeaveConnection = QMetaObject::Connection();
QObject::disconnect(_emitScriptEventConnection);
_emitScriptEventConnection = QMetaObject::Connection();
QObject::disconnect(_webEventReceivedConnection);
_webEventReceivedConnection = QMetaObject::Connection();
// The lifetime of the QML surface MUST be managed by the main thread
// Additionally, we MUST use local variables copied by value, rather than
// member variables, since they would implicitly refer to a this that
// is no longer valid
auto webSurface = _webSurface;
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
DependencyManager::get<OffscreenQmlSurfaceCache>()->release(QML, webSurface);
});
auto overlays = &(qApp->getOverlays());
QObject::disconnect(overlays, &Overlays::mousePressOnOverlay, this, nullptr);
QObject::disconnect(overlays, &Overlays::mouseReleaseOnOverlay, this, nullptr);
QObject::disconnect(overlays, &Overlays::mouseMoveOnOverlay, this, nullptr);
QObject::disconnect(overlays, &Overlays::hoverLeaveOverlay, this, nullptr);
QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
DependencyManager::get<OffscreenQmlSurfaceCache>()->release(QML, _webSurface);
_webSurface.reset();
}
auto geometryCache = DependencyManager::get<GeometryCache>();
@ -153,6 +137,9 @@ QString Web3DOverlay::pickURL() {
void Web3DOverlay::loadSourceURL() {
if (!_webSurface) {
return;
}
QUrl sourceUrl(_url);
if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" ||
@ -205,6 +192,7 @@ void Web3DOverlay::loadSourceURL() {
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
_webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../");
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data());
// mark the TabletProxy object as cpp ownership.
@ -252,23 +240,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 +350,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 +421,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 +486,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,12 +589,15 @@ void Web3DOverlay::setScriptURL(const QString& scriptURL) {
_scriptURL = scriptURL;
if (_webSurface) {
AbstractViewStateInterface::instance()->postLambdaEvent([this, scriptURL] {
if (!_webSurface) {
return;
}
_webSurface->getRootItem()->setProperty("scriptURL", scriptURL);
});
}
}
glm::vec2 Web3DOverlay::getSize() {
glm::vec2 Web3DOverlay::getSize() const {
return _resolution / _dpi * INCHES_TO_METERS * getDimensions();
};
@ -631,9 +615,5 @@ Web3DOverlay* Web3DOverlay::createClone() const {
}
void Web3DOverlay::emitScriptEvent(const QVariant& message) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, message));
} else {
emit scriptEventReceived(message);
}
QMetaObject::invokeMethod(this, "scriptEventReceived", Q_ARG(QVariant, message));
}

View file

@ -50,7 +50,7 @@ public:
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
glm::vec2 getSize();
glm::vec2 getSize() const override;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) override;
@ -72,7 +72,6 @@ signals:
private:
InputMode _inputMode { Touch };
QSharedPointer<OffscreenQmlSurface> _webSurface;
QMetaObject::Connection _connection;
gpu::TexturePointer _texture;
QString _url;
QString _scriptURL;
@ -82,20 +81,14 @@ private:
bool _showKeyboardFocusHighlight{ true };
bool _pressed{ false };
bool _touchBeginAccepted { false };
std::map<uint32_t, QTouchEvent::TouchPoint> _activeTouchPoints;
QTouchDevice _touchDevice;
uint8_t _desiredMaxFPS { 10 };
uint8_t _currentMaxFPS { 0 };
bool _mayNeedResize { false };
QMetaObject::Connection _mousePressConnection;
QMetaObject::Connection _mouseReleaseConnection;
QMetaObject::Connection _mouseMoveConnection;
QMetaObject::Connection _hoverLeaveConnection;
QMetaObject::Connection _emitScriptEventConnection;
QMetaObject::Connection _webEventReceivedConnection;
};
#endif // hifi_Web3DOverlay_h

View file

@ -364,7 +364,7 @@ private:
AudioIOStats _stats;
AudioGate* _audioGate { nullptr };
bool _audioGateOpen { false };
bool _audioGateOpen { true };
AudioPositionGetter _positionGetter;
AudioOrientationGetter _orientationGetter;

View file

@ -151,11 +151,6 @@ glm::vec3 Avatar::getNeckPosition() const {
return _skeletonModel->getNeckPosition(neckPosition) ? neckPosition : getPosition();
}
glm::quat Avatar::getWorldAlignedOrientation () const {
return computeRotationFromBodyToWorldUp() * getOrientation();
}
AABox Avatar::getBounds() const {
if (!_skeletonModel->isRenderable() || _skeletonModel->needsFixupInScene()) {
// approximately 2m tall, scaled to user request.
@ -436,6 +431,11 @@ void Avatar::slamPosition(const glm::vec3& newPosition) {
_lastVelocity = glm::vec3(0.0f);
}
void Avatar::updateAttitude(const glm::quat& orientation) {
_skeletonModel->updateAttitude(orientation);
_worldUpDirection = orientation * Vectors::UNIT_Y;
}
void Avatar::applyPositionDelta(const glm::vec3& delta) {
setPosition(getPosition() + delta);
_positionDeltaAccumulator += delta;
@ -628,22 +628,6 @@ void Avatar::render(RenderArgs* renderArgs) {
}
}
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
glm::quat orientation = getOrientation();
glm::vec3 currentUp = orientation * IDENTITY_UP;
float angle = acosf(glm::clamp(glm::dot(currentUp, _worldUpDirection), -1.0f, 1.0f));
if (angle < EPSILON) {
return glm::quat();
}
glm::vec3 axis;
if (angle > 179.99f * RADIANS_PER_DEGREE) { // 180 degree rotation; must use another axis
axis = orientation * IDENTITY_RIGHT;
} else {
axis = glm::normalize(glm::cross(currentUp, _worldUpDirection));
}
return glm::angleAxis(angle * proportion, axis);
}
void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
_attachmentsToDelete.clear();
@ -915,17 +899,34 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const {
}
glm::quat Avatar::getAbsoluteDefaultJointRotationInObjectFrame(int index) const {
glm::quat rotation;
glm::quat rot = _skeletonModel->getRig().getAnimSkeleton()->getAbsoluteDefaultPose(index).rot();
return Quaternions::Y_180 * rot;
// To make this thread safe, we hold onto the model by smart ptr, which prevents it from being deleted while we are accessing it.
auto model = getSkeletonModel();
if (model) {
auto skeleton = model->getRig().getAnimSkeleton();
if (skeleton && index >= 0 && index < skeleton->getNumJoints()) {
// The rotation part of the geometry-to-rig transform is always identity so we can skip it.
// Y_180 is to convert from rig-frame into avatar-frame
return Quaternions::Y_180 * skeleton->getAbsoluteDefaultPose(index).rot();
}
}
return Quaternions::Y_180;
}
glm::vec3 Avatar::getAbsoluteDefaultJointTranslationInObjectFrame(int index) const {
glm::vec3 translation;
const Rig& rig = _skeletonModel->getRig();
glm::vec3 trans = rig.getAnimSkeleton()->getAbsoluteDefaultPose(index).trans();
glm::mat4 y180Mat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3());
return transformPoint(y180Mat * rig.getGeometryToRigTransform(), trans);
// To make this thread safe, we hold onto the model by smart ptr, which prevents it from being deleted while we are accessing it.
auto model = getSkeletonModel();
if (model) {
const Rig& rig = model->getRig();
auto skeleton = rig.getAnimSkeleton();
if (skeleton && index >= 0 && index < skeleton->getNumJoints()) {
// trans is in geometry frame.
glm::vec3 trans = skeleton->getAbsoluteDefaultPose(index).trans();
// Y_180 is to convert from rig-frame into avatar-frame
glm::mat4 geomToAvatarMat = Matrices::Y_180 * rig.getGeometryToRigTransform();
return transformPoint(geomToAvatarMat, trans);
}
}
return Vectors::ZERO;
}
glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
@ -1401,14 +1402,14 @@ glm::quat Avatar::getUncachedRightPalmRotation() const {
return rightPalmRotation;
}
void Avatar::setPosition(const glm::vec3& position) {
AvatarData::setPosition(position);
updateAttitude();
void Avatar::setPositionViaScript(const glm::vec3& position) {
setPosition(position);
updateAttitude(getOrientation());
}
void Avatar::setOrientation(const glm::quat& orientation) {
AvatarData::setOrientation(orientation);
updateAttitude();
void Avatar::setOrientationViaScript(const glm::quat& orientation) {
setOrientation(orientation);
updateAttitude(orientation);
}
void Avatar::updatePalms() {

View file

@ -112,8 +112,6 @@ public:
const Head* getHead() const { return static_cast<const Head*>(_headData); }
Head* getHead() { return static_cast<Head*>(_headData); }
glm::quat getWorldAlignedOrientation() const;
AABox getBounds() const;
/// Returns the distance to use as a LOD parameter.
@ -184,7 +182,7 @@ public:
void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const;
void slamPosition(const glm::vec3& position);
virtual void updateAttitude() override { _skeletonModel->updateAttitude(); }
virtual void updateAttitude(const glm::quat& orientation) override;
// Call this when updating Avatar position with a delta. This will allow us to
// _accurately_ measure position changes and compute the resulting velocity
@ -197,10 +195,8 @@ public:
void getCapsule(glm::vec3& start, glm::vec3& end, float& radius);
float computeMass();
using SpatiallyNestable::setPosition;
virtual void setPosition(const glm::vec3& position) override;
using SpatiallyNestable::setOrientation;
virtual void setOrientation(const glm::quat& orientation) override;
void setPositionViaScript(const glm::vec3& position) override;
void setOrientationViaScript(const glm::quat& orientation) override;
// these call through to the SpatiallyNestable versions, but they are here to expose these to javascript.
Q_INVOKABLE virtual const QUuid getParentID() const override { return SpatiallyNestable::getParentID(); }
@ -240,7 +236,7 @@ public:
bool hasNewJointData() const { return _hasNewJointData; }
float getBoundingRadius() const;
void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene);
void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene);
bool isInScene() const { return render::Item::isValidID(_renderItemID); }
@ -303,7 +299,6 @@ protected:
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
void measureMotionDerivatives(float deltaTime);
float getSkeletonHeight() const;

View file

@ -118,16 +118,16 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
_rig.updateFromEyeParameters(eyeParams);
}
void SkeletonModel::updateAttitude() {
void SkeletonModel::updateAttitude(const glm::quat& orientation) {
setTranslation(_owningAvatar->getSkeletonPosition());
setRotation(_owningAvatar->getOrientation() * Quaternions::Y_180);
setRotation(orientation * Quaternions::Y_180);
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale());
}
// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
// but just before head has been simulated.
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
updateAttitude();
updateAttitude(_owningAvatar->getOrientation());
if (fullUpdate) {
setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients());

View file

@ -35,7 +35,7 @@ public:
void simulate(float deltaTime, bool fullUpdate = true) override;
void updateRig(float deltaTime, glm::mat4 parentTransform) override;
void updateAttitude();
void updateAttitude(const glm::quat& orientation);
/// Returns the index of the left hand joint, or -1 if not found.
int getLeftHandJointIndex() const { return isActive() ? getFBXGeometry().leftHandJointIndex : -1; }

View file

@ -91,9 +91,6 @@ AvatarData::AvatarData() :
_targetVelocity(0.0f),
_density(DEFAULT_AVATAR_DENSITY)
{
setBodyPitch(0.0f);
setBodyYaw(-90.0f);
setBodyRoll(0.0f);
}
AvatarData::~AvatarData() {
@ -110,23 +107,6 @@ const QUrl& AvatarData::defaultFullAvatarModelUrl() {
return _defaultFullAvatarModelUrl;
}
// There are a number of possible strategies for this set of tools through endRender, below.
void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) {
bool success;
Transform trans = getTransform(success);
if (!success) {
qCWarning(avatars) << "Warning -- AvatarData::nextAttitude failed";
return;
}
trans.setTranslation(position);
trans.setRotation(orientation);
SpatiallyNestable::setTransform(trans, success);
if (!success) {
qCWarning(avatars) << "Warning -- AvatarData::nextAttitude failed";
}
updateAttitude();
}
void AvatarData::setTargetScale(float targetScale) {
auto newValue = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
if (_targetScale != newValue) {
@ -2100,6 +2080,7 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) {
currentBasis = std::make_shared<Transform>(Transform::fromJson(json[JSON_AVATAR_BASIS]));
}
glm::quat orientation;
if (json.contains(JSON_AVATAR_RELATIVE)) {
// During playback you can either have the recording basis set to the avatar current state
// meaning that all playback is relative to this avatars starting position, or
@ -2111,12 +2092,14 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) {
auto relativeTransform = Transform::fromJson(json[JSON_AVATAR_RELATIVE]);
auto worldTransform = currentBasis->worldTransform(relativeTransform);
setPosition(worldTransform.getTranslation());
setOrientation(worldTransform.getRotation());
orientation = worldTransform.getRotation();
} else {
// We still set the position in the case that there is no movement.
setPosition(currentBasis->getTranslation());
setOrientation(currentBasis->getRotation());
orientation = currentBasis->getRotation();
}
setOrientation(orientation);
updateAttitude(orientation);
// Do after avatar orientation because head look-at needs avatar orientation.
if (json.contains(JSON_AVATAR_HEAD)) {
@ -2234,11 +2217,11 @@ void AvatarData::setBodyRoll(float bodyRoll) {
setOrientation(glm::quat(glm::radians(eulerAngles)));
}
void AvatarData::setPosition(const glm::vec3& position) {
void AvatarData::setPositionViaScript(const glm::vec3& position) {
SpatiallyNestable::setPosition(position);
}
void AvatarData::setOrientation(const glm::quat& orientation) {
void AvatarData::setOrientationViaScript(const glm::quat& orientation) {
SpatiallyNestable::setOrientation(orientation);
}

View file

@ -351,14 +351,14 @@ public:
class AvatarData : public QObject, public SpatiallyNestable {
Q_OBJECT
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPositionViaScript)
Q_PROPERTY(float scale READ getTargetScale WRITE setTargetScale)
Q_PROPERTY(glm::vec3 handPosition READ getHandPosition WRITE setHandPosition)
Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw)
Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch)
Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll)
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation)
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientationViaScript)
Q_PROPERTY(glm::quat headOrientation READ getHeadOrientation WRITE setHeadOrientation)
Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch)
Q_PROPERTY(float headYaw READ getHeadYaw WRITE setHeadYaw)
@ -440,13 +440,10 @@ public:
float getBodyRoll() const;
void setBodyRoll(float bodyRoll);
using SpatiallyNestable::setPosition;
virtual void setPosition(const glm::vec3& position) override;
using SpatiallyNestable::setOrientation;
virtual void setOrientation(const glm::quat& orientation) override;
virtual void setPositionViaScript(const glm::vec3& position);
virtual void setOrientationViaScript(const glm::quat& orientation);
void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time.
virtual void updateAttitude() {} // Tell skeleton mesh about changes
virtual void updateAttitude(const glm::quat& orientation) {}
glm::quat getHeadOrientation() const {
lazyInitHeadData();

View file

@ -101,6 +101,23 @@ namespace controller {
makePosePair(Action::RIGHT_HAND_PINKY3, "RightHandPinky3"),
makePosePair(Action::RIGHT_HAND_PINKY4, "RightHandPinky4"),
makePosePair(Action::TRACKED_OBJECT_00, "TrackedObject00"),
makePosePair(Action::TRACKED_OBJECT_01, "TrackedObject01"),
makePosePair(Action::TRACKED_OBJECT_02, "TrackedObject02"),
makePosePair(Action::TRACKED_OBJECT_03, "TrackedObject03"),
makePosePair(Action::TRACKED_OBJECT_04, "TrackedObject04"),
makePosePair(Action::TRACKED_OBJECT_05, "TrackedObject05"),
makePosePair(Action::TRACKED_OBJECT_06, "TrackedObject06"),
makePosePair(Action::TRACKED_OBJECT_07, "TrackedObject07"),
makePosePair(Action::TRACKED_OBJECT_08, "TrackedObject08"),
makePosePair(Action::TRACKED_OBJECT_09, "TrackedObject09"),
makePosePair(Action::TRACKED_OBJECT_10, "TrackedObject10"),
makePosePair(Action::TRACKED_OBJECT_11, "TrackedObject11"),
makePosePair(Action::TRACKED_OBJECT_12, "TrackedObject12"),
makePosePair(Action::TRACKED_OBJECT_13, "TrackedObject13"),
makePosePair(Action::TRACKED_OBJECT_14, "TrackedObject14"),
makePosePair(Action::TRACKED_OBJECT_15, "TrackedObject15"),
makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"),
makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"),

View file

@ -146,6 +146,23 @@ enum class Action {
RIGHT_HAND_PINKY3,
RIGHT_HAND_PINKY4,
TRACKED_OBJECT_00,
TRACKED_OBJECT_01,
TRACKED_OBJECT_02,
TRACKED_OBJECT_03,
TRACKED_OBJECT_04,
TRACKED_OBJECT_05,
TRACKED_OBJECT_06,
TRACKED_OBJECT_07,
TRACKED_OBJECT_08,
TRACKED_OBJECT_09,
TRACKED_OBJECT_10,
TRACKED_OBJECT_11,
TRACKED_OBJECT_12,
TRACKED_OBJECT_13,
TRACKED_OBJECT_14,
TRACKED_OBJECT_15,
NUM_ACTIONS,
};

View file

@ -166,6 +166,23 @@ Input::NamedVector StandardController::getAvailableInputs() const {
makePair(DD, "Down"),
makePair(DL, "Left"),
makePair(DR, "Right"),
makePair(TRACKED_OBJECT_00, "TrackedObject00"),
makePair(TRACKED_OBJECT_01, "TrackedObject01"),
makePair(TRACKED_OBJECT_02, "TrackedObject02"),
makePair(TRACKED_OBJECT_03, "TrackedObject03"),
makePair(TRACKED_OBJECT_04, "TrackedObject04"),
makePair(TRACKED_OBJECT_05, "TrackedObject05"),
makePair(TRACKED_OBJECT_06, "TrackedObject06"),
makePair(TRACKED_OBJECT_07, "TrackedObject07"),
makePair(TRACKED_OBJECT_08, "TrackedObject08"),
makePair(TRACKED_OBJECT_09, "TrackedObject09"),
makePair(TRACKED_OBJECT_10, "TrackedObject10"),
makePair(TRACKED_OBJECT_11, "TrackedObject11"),
makePair(TRACKED_OBJECT_12, "TrackedObject12"),
makePair(TRACKED_OBJECT_13, "TrackedObject13"),
makePair(TRACKED_OBJECT_14, "TrackedObject14"),
makePair(TRACKED_OBJECT_15, "TrackedObject15")
};
return availableInputs;
}

View file

@ -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")

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -26,7 +26,6 @@
#include <PerfStat.h>
#include <SceneScriptingInterface.h>
#include <ScriptEngine.h>
#include <HoverOverlayInterface.h>
#include "RenderableEntityItem.h"
@ -453,8 +452,6 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons
void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) {
auto hoverOverlayInterface = DependencyManager::get<HoverOverlayInterface>().data();
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity);
connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity);
@ -464,12 +461,8 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS
connect(this, &EntityTreeRenderer::clickReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickReleaseOnEntity);
connect(this, &EntityTreeRenderer::hoverEnterEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity);
connect(this, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), hoverOverlayInterface, SLOT(createHoverOverlay(const EntityItemID&, const PointerEvent&)));
connect(this, &EntityTreeRenderer::hoverOverEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity);
connect(this, &EntityTreeRenderer::hoverLeaveEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity);
connect(this, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), hoverOverlayInterface, SLOT(destroyHoverOverlay(const EntityItemID&, const PointerEvent&)));
connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity);
connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity);
@ -682,7 +675,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
PickRay ray = _viewState->computePickRay(event->x(), event->y());
bool precisionPicking = false; // for mouse moves we do not do precision picking
bool precisionPicking = true; // for mouse moves we do precision picking
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
if (rayPickResult.intersects) {

View file

@ -372,6 +372,21 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
_model->updateRenderItems();
}
// this simple logic should say we set showingEntityHighlight to true whenever we are in marketplace mode and we have a marketplace id, or
// whenever we are not set to none and shouldHighlight is true.
bool showingEntityHighlight = ((bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_MARKETPLACE_MODE) && getMarketplaceID().length() != 0) ||
(args->_outlineFlags != RenderArgs::RENDER_OUTLINE_NONE && getShouldHighlight());
if (showingEntityHighlight) {
static glm::vec4 yellowColor(1.0f, 1.0f, 0.0f, 1.0f);
gpu::Batch& batch = *args->_batch;
bool success;
auto shapeTransform = getTransformToCenter(success);
if (success) {
batch.setModelTransform(shapeTransform); // we want to include the scale as well
DependencyManager::get<GeometryCache>()->renderWireCubeInstance(args, batch, yellowColor);
}
}
if (!hasModel() || (_model && _model->didVisualGeometryRequestFail())) {
static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
gpu::Batch& batch = *args->_batch;

View file

@ -82,6 +82,80 @@ bool RenderableShapeEntityItem::isTransparent() {
}
}
void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
// This will be called whenever DIRTY_SHAPE flag (set by dimension change, etc)
// is set.
const glm::vec3 entityDimensions = getDimensions();
switch (_shape){
case entity::Shape::Quad:
case entity::Shape::Cube: {
_collisionShapeType = SHAPE_TYPE_BOX;
}
break;
case entity::Shape::Sphere: {
float diameter = entityDimensions.x;
const float MIN_DIAMETER = 0.001f;
const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;
if (diameter > MIN_DIAMETER
&& fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR
&& fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) {
_collisionShapeType = SHAPE_TYPE_SPHERE;
}
else {
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
}
}
break;
case entity::Shape::Cylinder: {
_collisionShapeType = SHAPE_TYPE_CYLINDER_Y;
// TODO WL21389: determine if rotation is axis-aligned
//const Transform::Quat & rot = _transform.getRotation();
// TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and
// hull ( or dimensions, need circular cross section)
// Should allow for minor variance along axes?
}
break;
case entity::Shape::Triangle:
case entity::Shape::Hexagon:
case entity::Shape::Octagon:
case entity::Shape::Circle:
case entity::Shape::Tetrahedron:
case entity::Shape::Octahedron:
case entity::Shape::Dodecahedron:
case entity::Shape::Icosahedron:
case entity::Shape::Cone: {
//TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later)
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
}
break;
case entity::Shape::Torus:
{
// Not in GeometryCache::buildShapes, unsupported.
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
//TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support)
}
break;
default:{
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
}
break;
}
EntityItem::computeShapeInfo(info);
}
// This value specifes how the shape should be treated by physics calculations.
ShapeType RenderableShapeEntityItem::getShapeType() const {
return _collisionShapeType;
}
void RenderableShapeEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableShapeEntityItem::render");
//Q_ASSERT(getType() == EntityTypes::Shape);

View file

@ -28,9 +28,18 @@ public:
bool isTransparent() override;
virtual void computeShapeInfo(ShapeInfo& info) override;
ShapeType getShapeType() const override;
private:
std::unique_ptr<Procedural> _procedural { nullptr };
//! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain
//! prior functionality where new or unsupported shapes are treated as
//! ellipsoids.
ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID };
SIMPLE_RENDERABLE();
};

View file

@ -54,6 +54,7 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type,
EntityItemProperties entityProperties = entity->getProperties();
entityProperties.merge(properties);
std::lock_guard<std::mutex> lock(_mutex);
QScriptValue scriptProperties = EntityItemNonDefaultPropertiesToScriptValue(&_scriptEngine, entityProperties);
QVariant variantProperties = scriptProperties.toVariant();
QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties);

View file

@ -14,6 +14,8 @@
#include <OctreeEditPacketSender.h>
#include <mutex>
#include "EntityItem.h"
#include "AvatarData.h"
@ -49,6 +51,7 @@ private:
EntityItemID entityItemID, const EntityItemProperties& properties);
private:
std::mutex _mutex;
AvatarData* _myAvatar { nullptr };
QScriptEngine _scriptEngine;
};

View file

@ -133,6 +133,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_LOCKED;
requestedProperties += PROP_USER_DATA;
requestedProperties += PROP_MARKETPLACE_ID;
requestedProperties += PROP_SHOULD_HIGHLIGHT;
requestedProperties += PROP_NAME;
requestedProperties += PROP_HREF;
requestedProperties += PROP_DESCRIPTION;
@ -278,6 +279,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked());
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData());
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID());
APPEND_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, getShouldHighlight());
APPEND_ENTITY_PROPERTY(PROP_NAME, getName());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL());
APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
@ -829,6 +831,10 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
}
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT) {
READ_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, bool, setShouldHighlight);
}
READ_ENTITY_PROPERTY(PROP_NAME, QString, setName);
READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
@ -1355,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;
}
@ -1424,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 {
@ -1635,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();
}
}
@ -2808,6 +2809,20 @@ void EntityItem::setMarketplaceID(const QString& value) {
});
}
bool EntityItem::getShouldHighlight() const {
bool result;
withReadLock([&] {
result = _shouldHighlight;
});
return result;
}
void EntityItem::setShouldHighlight(const bool value) {
withWriteLock([&] {
_shouldHighlight = value;
});
}
uint32_t EntityItem::getDirtyFlags() const {
uint32_t result;
withReadLock([&] {

View file

@ -316,6 +316,9 @@ public:
QString getMarketplaceID() const;
void setMarketplaceID(const QString& value);
bool getShouldHighlight() const;
void setShouldHighlight(const bool value);
// TODO: get rid of users of getRadius()...
float getRadius() const;
@ -532,6 +535,7 @@ protected:
QString _userData;
SimulationOwner _simulationOwner;
QString _marketplaceID;
bool _shouldHighlight { false };
QString _name;
QString _href; //Hyperlink href
QString _description; //Hyperlink description

View file

@ -289,6 +289,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_RADIUS_START, radiusStart);
CHECK_PROPERTY_CHANGE(PROP_RADIUS_FINISH, radiusFinish);
CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID);
CHECK_PROPERTY_CHANGE(PROP_SHOULD_HIGHLIGHT, shouldHighlight);
CHECK_PROPERTY_CHANGE(PROP_NAME, name);
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode);
CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl);
@ -406,6 +407,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MARKETPLACE_ID, marketplaceID);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHOULD_HIGHLIGHT, shouldHighlight);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL);
@ -982,6 +984,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_PROPERTY_TO_MAP(PROP_RADIUS_START, RadiusStart, radiusStart, float);
ADD_PROPERTY_TO_MAP(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float);
ADD_PROPERTY_TO_MAP(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString);
ADD_PROPERTY_TO_MAP(PROP_SHOULD_HIGHLIGHT, ShouldHighlight, shouldHighlight, bool);
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor);
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float);
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float);
@ -1334,6 +1337,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape());
}
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID());
APPEND_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, properties.getShouldHighlight());
APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL());
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData());
@ -1632,6 +1636,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
}
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHOULD_HIGHLIGHT, bool, setShouldHighlight);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData);
@ -1746,6 +1751,7 @@ void EntityItemProperties::markAllChanged() {
//_alphaFinishChanged = true;
_marketplaceIDChanged = true;
_shouldHighlightChanged = true;
_keyLight.markAllChanged();
@ -2157,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();
}

View file

@ -86,8 +86,9 @@ public:
EntityPropertyFlags getChangedProperties() const;
bool parentDependentPropertyChanged() const; // was there a changed in a property that requires parent info to interpret?
bool parentRelatedPropertyChanged() const; // parentDependentPropertyChanged or parentID or parentJointIndex
bool transformChanged() const;
bool parentRelatedPropertyChanged() const;
bool queryAACubeRelatedPropertyChanged() const;
AABox getAABox() const;
@ -170,6 +171,7 @@ public:
DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH);
DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL);
DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID);
DEFINE_PROPERTY_REF(PROP_SHOULD_HIGHLIGHT, ShouldHighlight, shouldHighlight, bool, ENTITY_ITEM_DEFAULT_SHOULD_HIGHLIGHT);
DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup);
DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE);
DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA);

View file

@ -27,6 +27,7 @@ const glm::vec3 ENTITY_ITEM_HALF_VEC3 = glm::vec3(0.5f);
const bool ENTITY_ITEM_DEFAULT_LOCKED = false;
const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString("");
const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString("");
const bool ENTITY_ITEM_DEFAULT_SHOULD_HIGHLIGHT = false;
const QUuid ENTITY_ITEM_DEFAULT_SIMULATOR_ID = QUuid();
const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f;

View file

@ -78,6 +78,7 @@ enum EntityPropertyList {
PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities
PROP_MARKETPLACE_ID, // all entities
PROP_SHOULD_HIGHLIGHT, // all entities
PROP_ACCELERATION, // all entities
PROP_SIMULATION_OWNER, // formerly known as PROP_SIMULATOR_ID
PROP_NAME, // all entities

View file

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

View file

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

View file

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

View file

@ -1,38 +0,0 @@
//
// HoverOverlayInterface.cpp
// libraries/entities/src
//
// Created by Zach Fox on 2017-07-14.
// Copyright 2017 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 "HoverOverlayInterface.h"
HoverOverlayInterface::HoverOverlayInterface() {
// "hover_overlay" debug log category disabled by default.
// Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable
// if you'd like to enable/disable certain categories.
// More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories
QLoggingCategory::setFilterRules(QStringLiteral("hifi.hover_overlay.debug=false"));
}
void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {
qCDebug(hover_overlay) << "Creating Hover Overlay on top of entity with ID: " << entityItemID;
setCurrentHoveredEntity(entityItemID);
}
void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID) {
HoverOverlayInterface::createHoverOverlay(entityItemID, PointerEvent());
}
void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {
qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID;
setCurrentHoveredEntity(QUuid());
}
void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID) {
HoverOverlayInterface::destroyHoverOverlay(entityItemID, PointerEvent());
}

View file

@ -1,49 +0,0 @@
//
// HoverOverlayInterface.h
// libraries/entities/src
//
// Created by Zach Fox on 2017-07-14.
// Copyright 2017 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
//
#pragma once
#ifndef hifi_HoverOverlayInterface_h
#define hifi_HoverOverlayInterface_h
#include <QtCore/QObject>
#include <QUuid>
#include <DependencyManager.h>
#include <PointerEvent.h>
#include "EntityTree.h"
#include "HoverOverlayLogging.h"
/**jsdoc
* @namespace HoverOverlay
*/
class HoverOverlayInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(QUuid currentHoveredEntity READ getCurrentHoveredEntity WRITE setCurrentHoveredEntity)
public:
HoverOverlayInterface();
Q_INVOKABLE QUuid getCurrentHoveredEntity() { return _currentHoveredEntity; }
void setCurrentHoveredEntity(const QUuid& entityID) { _currentHoveredEntity = entityID; }
public slots:
void createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
void createHoverOverlay(const EntityItemID& entityItemID);
void destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
void destroyHoverOverlay(const EntityItemID& entityItemID);
private:
bool _verboseLogging { true };
QUuid _currentHoveredEntity{};
};
#endif // hifi_HoverOverlayInterface_h

View file

@ -160,12 +160,6 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
}
// This value specifes how the shape should be treated by physics calculations.
// For now, all polys will act as spheres
ShapeType ShapeEntityItem::getShapeType() const {
return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID;
}
void ShapeEntityItem::setColor(const rgbColor& value) {
memcpy(_color, value, sizeof(rgbColor));
}
@ -223,10 +217,12 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const
void ShapeEntityItem::debugDump() const {
quint64 now = usecTimestampNow();
qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " shape:" << stringFromShape(_shape);
qCDebug(entities) << " name:" << _name;
qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )";
qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
qCDebug(entities) << " position:" << debugTreeVector(getPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
qCDebug(entities) << "SHAPE EntityItem Ptr:" << this;
}

View file

@ -84,7 +84,6 @@ public:
QColor getQColor() const;
void setColor(const QColor& value);
ShapeType getShapeType() const override;
bool shouldBePhysical() const override { return !isDead(); }
bool supportsDetailedRayIntersection() const override;

View file

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

View file

@ -236,6 +236,7 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Shift), "Shift"));
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString()));
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString()));
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Tab), QKeySequence(Qt::Key_Tab).toString()));
availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton"));
availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton"));

View file

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

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

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

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

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

View file

@ -62,7 +62,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityEdit:
case PacketType::EntityData:
case PacketType::EntityPhysics:
return VERSION_ENTITIES_BULLET_DYNAMICS;
return VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT;
case PacketType::EntityQuery:
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
case PacketType::AvatarIdentity:

View file

@ -218,6 +218,7 @@ const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67;
const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68;
const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69;
const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70;
const PacketVersion VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT = 71;
enum class EntityQueryPacketVersion: PacketVersion {
JSONFilter = 18,

View file

@ -445,7 +445,7 @@ void CharacterController::handleChangedCollisionGroup() {
void CharacterController::updateUpAxis(const glm::quat& rotation) {
_currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS);
if (_state != State::Hover && _rigidBody) {
if (_rigidBody) {
_rigidBody->setGravity(_gravity * _currentUp);
}
}

View file

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

View file

@ -17,7 +17,7 @@
#include "BulletUtil.h"
// These are the same normalized directions used by the btShapeHull class.
// 12 points for the face centers of a duodecohedron plus another 30 points
// 12 points for the face centers of a dodecahedron plus another 30 points
// for the midpoints the edges, for a total of 42.
const uint32_t NUM_UNIT_SPHERE_DIRECTIONS = 42;
static const btVector3 _unitSphereDirections[NUM_UNIT_SPHERE_DIRECTIONS] = {
@ -288,6 +288,38 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
shape = new btCapsuleShape(radius, height);
}
break;
case SHAPE_TYPE_CAPSULE_X: {
glm::vec3 halfExtents = info.getHalfExtents();
float radius = halfExtents.y;
float height = 2.0f * halfExtents.x;
shape = new btCapsuleShapeX(radius, height);
}
break;
case SHAPE_TYPE_CAPSULE_Z: {
glm::vec3 halfExtents = info.getHalfExtents();
float radius = halfExtents.x;
float height = 2.0f * halfExtents.z;
shape = new btCapsuleShapeZ(radius, height);
}
break;
case SHAPE_TYPE_CYLINDER_X: {
const glm::vec3 halfExtents = info.getHalfExtents();
const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z);
shape = new btCylinderShapeX(btHalfExtents);
}
break;
case SHAPE_TYPE_CYLINDER_Z: {
const glm::vec3 halfExtents = info.getHalfExtents();
const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z);
shape = new btCylinderShapeZ(btHalfExtents);
}
break;
case SHAPE_TYPE_CYLINDER_Y: {
const glm::vec3 halfExtents = info.getHalfExtents();
const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z);
shape = new btCylinderShape(btHalfExtents);
}
break;
case SHAPE_TYPE_COMPOUND:
case SHAPE_TYPE_SIMPLE_HULL: {
const ShapeInfo::PointCollection& pointCollection = info.getPointCollection();

View file

@ -63,6 +63,12 @@ namespace render {
public:
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE, SECONDARY_CAMERA_RENDER_MODE };
enum DisplayMode { MONO, STEREO_MONITOR, STEREO_HMD };
enum OutlineFlags {
RENDER_OUTLINE_NONE = 0,
RENDER_OUTLINE_WIREFRAMES = 1,
RENDER_OUTLINE_MARKETPLACE_MODE = 2,
RENDER_OUTLINE_SHADER = 4
};
enum DebugFlags {
RENDER_DEBUG_NONE = 0,
RENDER_DEBUG_HULLS = 1
@ -112,6 +118,7 @@ namespace render {
int _boundaryLevelAdjust { 0 };
RenderMode _renderMode { DEFAULT_RENDER_MODE };
DisplayMode _displayMode { MONO };
OutlineFlags _outlineFlags{ RENDER_OUTLINE_NONE };
DebugFlags _debugFlags { RENDER_DEBUG_NONE };
gpu::Batch* _batch = nullptr;

View file

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

View file

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

View file

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

View file

@ -29,6 +29,7 @@ void ShapeInfo::clear() {
}
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
//TODO WL21389: Does this need additional cases and handling added?
_url = "";
_type = type;
setHalfExtents(halfExtents);
@ -55,6 +56,9 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
}
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
//TODO WL21389: Should this pointlist clearance added in case
// this is a re-purposed instance?
// See https://github.com/highfidelity/hifi/pull/11024#discussion_r128885491
_url = "";
_type = SHAPE_TYPE_BOX;
setHalfExtents(halfExtents);
@ -62,6 +66,7 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) {
}
void ShapeInfo::setSphere(float radius) {
//TODO WL21389: See comment in setBox regarding clearance
_url = "";
_type = SHAPE_TYPE_SPHERE;
radius = glm::max(radius, MIN_HALF_EXTENT);
@ -70,12 +75,14 @@ void ShapeInfo::setSphere(float radius) {
}
void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) {
//TODO WL21389: May need to skip resetting type here.
_pointCollection = pointCollection;
_type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE;
_doubleHashKey.clear();
}
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
//TODO WL21389: See comment in setBox regarding clearance
_url = "";
_type = SHAPE_TYPE_CAPSULE_Y;
radius = glm::max(radius, MIN_HALF_EXTENT);
@ -117,6 +124,7 @@ int ShapeInfo::getLargestSubshapePointCount() const {
}
float ShapeInfo::computeVolume() const {
//TODO WL21389: Add support for other ShapeTypes( CYLINDER_X, CYLINDER_Y, etc).
const float DEFAULT_VOLUME = 1.0f;
float volume = DEFAULT_VOLUME;
switch(_type) {
@ -136,7 +144,10 @@ float ShapeInfo::computeVolume() const {
}
case SHAPE_TYPE_CAPSULE_Y: {
float radius = _halfExtents.x;
volume = PI * radius * radius * (2.0f * (_halfExtents.y - _halfExtents.x) + 4.0f * radius / 3.0f);
// Need to offset halfExtents.y by x to account for the system treating
// the y extent of the capsule as the cylindrical height + spherical radius.
float cylinderHeight = 2.0f * (_halfExtents.y - _halfExtents.x);
volume = PI * radius * radius * (cylinderHeight + 4.0f * radius / 3.0f);
break;
}
default:
@ -147,6 +158,7 @@ float ShapeInfo::computeVolume() const {
}
bool ShapeInfo::contains(const glm::vec3& point) const {
//TODO WL21389: Add support for other ShapeTypes like Ellipsoid/Compound.
switch(_type) {
case SHAPE_TYPE_SPHERE:
return glm::length(point) <= _halfExtents.x;
@ -191,6 +203,7 @@ bool ShapeInfo::contains(const glm::vec3& point) const {
}
const DoubleHashKey& ShapeInfo::getHash() const {
//TODO WL21389: Need to include the pointlist for SIMPLE_HULL in hash
// NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) {
bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET;

View file

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

View file

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

View file

@ -127,7 +127,7 @@ private:
glm::mat4 _projection;
// derived
glm::vec3 _position;
glm::vec3 _position { 0.0f, 0.0f, 0.0f };
glm::quat _orientation;
bool _isKeepLookingAt{ false };
glm::vec3 _lookingAt;

View file

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

View file

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

View file

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

View file

@ -187,6 +187,7 @@ TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent)
if (QThread::currentThread() != qApp->thread()) {
qCWarning(uiLogging) << "Creating tablet proxy on wrong thread " << _name;
}
connect(this, &TabletProxy::tabletShownChanged, this, &TabletProxy::onTabletShown);
}
TabletProxy::~TabletProxy() {
@ -194,6 +195,7 @@ TabletProxy::~TabletProxy() {
if (QThread::currentThread() != thread()) {
qCWarning(uiLogging) << "Destroying tablet proxy on wrong thread" << _name;
}
disconnect(this, &TabletProxy::tabletShownChanged, this, &TabletProxy::onTabletShown);
}
void TabletProxy::setToolbarMode(bool toolbarMode) {
@ -208,12 +210,13 @@ void TabletProxy::setToolbarMode(bool toolbarMode) {
_toolbarMode = toolbarMode;
auto offscreenUi = DependencyManager::get<OffscreenUi>();
if (toolbarMode) {
removeButtonsFromHomeScreen();
addButtonsToToolbar();
// create new desktop window
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto tabletRootWindow = new TabletRootWindow();
tabletRootWindow->initQml(QVariantMap());
auto quickItem = tabletRootWindow->asQuickItem();
@ -228,16 +231,23 @@ void TabletProxy::setToolbarMode(bool toolbarMode) {
connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml);
} else {
removeButtonsFromToolbar();
addButtonsToHomeScreen();
if (_currentPathLoaded == TABLET_SOURCE_URL) {
addButtonsToHomeScreen();
} else {
loadHomeScreen(true);
}
//check if running scripts window opened and save it for reopen in Tablet
if (offscreenUi->isVisible("RunningScripts")) {
offscreenUi->hide("RunningScripts");
_showRunningScripts = true;
}
// destroy desktop window
if (_desktopWindow) {
_desktopWindow->deleteLater();
_desktopWindow = nullptr;
}
}
loadHomeScreen(true);
emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL));
}
static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* buttonProxy) {
@ -313,6 +323,13 @@ void TabletProxy::emitWebEvent(const QVariant& msg) {
emit webEventReceived(msg);
}
void TabletProxy::onTabletShown() {
if (_tabletShown && _showRunningScripts) {
_showRunningScripts = false;
pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml");
}
}
bool TabletProxy::isPathLoaded(const QVariant& path) {
if (QThread::currentThread() != thread()) {
bool result = false;
@ -362,9 +379,16 @@ void TabletProxy::setQmlTabletRoot(OffscreenQmlSurface* qmlOffscreenSurface) {
});
if (_initialScreen) {
pushOntoStack(_initialPath);
if (!_showRunningScripts) {
pushOntoStack(_initialPath);
}
_initialScreen = false;
}
if (_showRunningScripts) {
//show Tablet. Make sure, setShown implemented in TabletRoot.qml
QMetaObject::invokeMethod(_qmlTabletRoot, "setShown", Q_ARG(const QVariant&, QVariant(true)));
}
} else {
removeButtonsFromHomeScreen();
_state = State::Uninitialized;
@ -463,14 +487,14 @@ void TabletProxy::loadQMLSource(const QVariant& path) {
}
if (root) {
if (_state != State::QML) {
removeButtonsFromHomeScreen();
QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path));
_state = State::QML;
removeButtonsFromHomeScreen(); //works only in Tablet
QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path));
_state = State::QML;
if (path != _currentPathLoaded) {
emit screenChanged(QVariant("QML"), path);
_currentPathLoaded = path;
QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));
}
_currentPathLoaded = path;
QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));
} else {
qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null";
}
@ -483,6 +507,11 @@ bool TabletProxy::pushOntoStack(const QVariant& path) {
return result;
}
//set landscape off when pushing menu items while in Create mode
if (_landscape) {
setLandscape(false);
}
QObject* root = nullptr;
if (!_toolbarMode && _qmlTabletRoot) {
root = _qmlTabletRoot;
@ -501,7 +530,7 @@ bool TabletProxy::pushOntoStack(const QVariant& path) {
qCDebug(uiLogging) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null";
}
return root;
return (root != nullptr);
}
void TabletProxy::popFromStack() {
@ -825,7 +854,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) {

Some files were not shown because too many files have changed in this diff Show more