Merge branch 'master' into scriptvec3

This commit is contained in:
Sam Gondelman 2018-10-08 12:59:34 -07:00 committed by GitHub
commit 67662df760
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
223 changed files with 3234 additions and 3165 deletions

View file

@ -68,6 +68,7 @@ if (USE_GLES AND (NOT ANDROID))
set(DISABLE_QML_OPTION ON)
endif()
option(BUILD_CLIENT "Build client components" ${BUILD_CLIENT_OPTION})
option(BUILD_SERVER "Build server components" ${BUILD_SERVER_OPTION})
option(BUILD_TESTS "Build tests" ${BUILD_TESTS_OPTION})
@ -162,7 +163,6 @@ if (BUILD_SERVER)
set_target_properties(domain-server PROPERTIES FOLDER "Apps")
add_subdirectory(ice-server)
set_target_properties(ice-server PROPERTIES FOLDER "Apps")
add_subdirectory(server-console)
endif()
if (BUILD_CLIENT)
@ -174,6 +174,7 @@ endif()
if (BUILD_CLIENT OR BUILD_SERVER)
add_subdirectory(plugins)
add_subdirectory(server-console)
endif()
# BUILD_TOOLS option will be handled inside the tools's CMakeLists.txt because 'scribe' tool is required for build anyway

View file

@ -7,6 +7,8 @@ target_bullet()
set(INTERFACE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../interface")
add_subdirectory("${INTERFACE_DIR}" "libraries/interface")
include_directories("${INTERFACE_DIR}/src")
set(HIFI_CODEC_PLUGIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../plugins/hifiCodec")
add_subdirectory("${HIFI_CODEC_PLUGIN_DIR}" "libraries/hifiCodecPlugin")
target_link_libraries(native-lib android log m interface)

View file

@ -80,8 +80,10 @@ android {
if (Os.isFamily(Os.FAMILY_UNIX)) {
def uploadDumpSymsTask = rootProject.getTasksByName("uploadBreakpadDumpSyms${variant.name.capitalize()}", false).first()
def runDumpSymsTask = rootProject.getTasksByName("runBreakpadDumpSyms${variant.name.capitalize()}", false).first()
def renameHifiACTask = rootProject.getTasksByName("renameHifiACTask${variant.name.capitalize()}", false).first()
runDumpSymsTask.dependsOn(task)
variant.assemble.dependsOn(uploadDumpSymsTask)
variant.mergeResources.dependsOn(renameHifiACTask)
}
}

View file

@ -143,11 +143,9 @@ def packages = [
includeLibs: ['libtbb.so', 'libtbbmalloc.so'],
],
hifiAC: [
file: 'libplugins_libhifiCodec.zip',
versionId: 'i31pW.qNbvFOXRxbyiJUxg3sphaFNmZU',
checksum: '9412a8e12c88a4096c1fc843bb9fe52d',
sharedLibFolder: '',
includeLibs: ['libplugins_libhifiCodec.so']
baseUrl: 'http://s3.amazonaws.com/hifi-public/dependencies/',
file: 'codecSDK-android_armv8-2.0.zip',
checksum: '1cbef929675818fc64c4101b72f84a6a'
],
etc2comp: [
file: 'etc2comp-patched-armv8-libcpp.tgz',
@ -367,7 +365,8 @@ task downloadDependencies {
doLast {
packages.each { entry ->
def filename = entry.value['file'];
def url = baseUrl + filename;
def dependencyBaseUrl = entry.value['baseUrl']
def url = (dependencyBaseUrl?.trim() ? dependencyBaseUrl : baseUrl) + filename;
if (entry.value.containsKey('versionId')) {
url = url + '?versionId=' + entry.value['versionId']
}
@ -668,6 +667,21 @@ task uploadBreakpadDumpSymsRelease(type:io.github.httpbuilderng.http.HttpTask, d
}
}
task renameHifiACTaskDebug() {
doLast {
def sourceFile = new File("${appDir}/build/intermediates/cmake/debug/obj/arm64-v8a/","libhifiCodec.so")
def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so")
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
}
}
task renameHifiACTaskRelease(type: Copy) {
doLast {
def sourceFile = new File("${appDir}/build/intermediates/cmake/release/obj/arm64-v8a/","libhifiCodec.so")
def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so")
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
}
}
// FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution.
// See the comment on the qtBundle task above
/*

View file

@ -112,6 +112,11 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
AvatarTraits::TraitWireSize traitSize;
message.readPrimitive(&traitSize);
if (traitSize < -1 || traitSize > message.getBytesLeftToRead()) {
qWarning() << "Refusing to process simple trait of size" << traitSize << "from" << message.getSenderSockAddr();
break;
}
if (packetTraitVersion > _lastReceivedTraitVersions[traitType]) {
_avatar->processTrait(traitType, message.read(traitSize));
_lastReceivedTraitVersions[traitType] = packetTraitVersion;
@ -128,26 +133,41 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
} else {
AvatarTraits::TraitInstanceID instanceID = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
if (message.getBytesLeftToRead() == 0) {
qWarning () << "Received an instanced trait with no size from" << message.getSenderSockAddr();
break;
}
AvatarTraits::TraitWireSize traitSize;
message.readPrimitive(&traitSize);
auto& instanceVersionRef = _lastReceivedTraitVersions.getInstanceValueRef(traitType, instanceID);
if (traitSize < -1 || traitSize > message.getBytesLeftToRead()) {
qWarning() << "Refusing to process instanced trait of size" << traitSize << "from" << message.getSenderSockAddr();
break;
}
if (packetTraitVersion > instanceVersionRef) {
if (traitSize == AvatarTraits::DELETED_TRAIT_SIZE) {
_avatar->processDeletedTraitInstance(traitType, instanceID);
if (traitType == AvatarTraits::AvatarEntity) {
auto& instanceVersionRef = _lastReceivedTraitVersions.getInstanceValueRef(traitType, instanceID);
// to track a deleted instance but keep version information
// the avatar mixer uses the negative value of the sent version
instanceVersionRef = -packetTraitVersion;
if (packetTraitVersion > instanceVersionRef) {
if (traitSize == AvatarTraits::DELETED_TRAIT_SIZE) {
_avatar->processDeletedTraitInstance(traitType, instanceID);
// to track a deleted instance but keep version information
// the avatar mixer uses the negative value of the sent version
instanceVersionRef = -packetTraitVersion;
} else {
_avatar->processTraitInstance(traitType, instanceID, message.read(traitSize));
instanceVersionRef = packetTraitVersion;
}
anyTraitsChanged = true;
} else {
_avatar->processTraitInstance(traitType, instanceID, message.read(traitSize));
instanceVersionRef = packetTraitVersion;
message.seek(message.getPosition() + traitSize);
}
anyTraitsChanged = true;
} else {
message.seek(message.getPosition() + traitSize);
qWarning() << "Refusing to process traits packet with instanced trait of unprocessable type from" << message.getSenderSockAddr();
break;
}
}
}

View file

@ -152,6 +152,7 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
});
if (!isDeleted && (sentInstanceIt == sentIDValuePairs.end() || receivedVersion > sentInstanceIt->value)) {
// this instance version exists and has never been sent or is newer so we need to send it
bytesWritten += sendingAvatar->packTraitInstance(traitType, instanceID, traitsPacketList, receivedVersion);
@ -161,6 +162,7 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
sentIDValuePairs.emplace_back(instanceID, receivedVersion);
}
} else if (isDeleted && sentInstanceIt != sentIDValuePairs.end() && absoluteReceivedVersion > sentInstanceIt->value) {
// this instance version was deleted and we haven't sent the delete to this client yet
bytesWritten += AvatarTraits::packInstancedTraitDelete(traitType, instanceID, traitsPacketList, absoluteReceivedVersion);
@ -180,6 +182,7 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
listeningNodeData->setLastOtherAvatarTraitsSendPoint(otherNodeLocalID, timeOfLastTraitsChange);
}
return bytesWritten;
}

View file

@ -66,8 +66,8 @@ macro(install_beside_console)
install(CODE "
set(MACOSX_BUNDLE_EXECUTABLE_NAME domain-server)
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.server-components)
set(MACOSX_BUNDLE_BUNDLE_NAME Sandbox\\ Components)
configure_file(${HF_CMAKE_DIR}/templates/MacOSXBundleSandboxComponentsInfo.plist.in ${ESCAPED_BUNDLE_NAME}/Contents/Info.plist)
set(MACOSX_BUNDLE_BUNDLE_NAME Console\\ Components)
configure_file(${HF_CMAKE_DIR}/templates/MacOSXBundleConsoleComponentsInfo.plist.in ${ESCAPED_BUNDLE_NAME}/Contents/Info.plist)
execute_process(COMMAND ${MACDEPLOYQT_COMMAND} ${ESCAPED_BUNDLE_NAME} -verbose=2 -executable=${ESCAPED_EXECUTABLE_NAME})"
COMPONENT ${SERVER_COMPONENT}
)

View file

@ -18,7 +18,7 @@ macro(SET_PACKAGING_PARAMETERS)
set(BUILD_GLOBAL_SERVICES "DEVELOPMENT")
set(USE_STABLE_GLOBAL_SERVICES 0)
set(BUILD_NUMBER 0)
set(APP_USER_MODEL_ID "com.highfidelity.sandbox-dev")
set(APP_USER_MODEL_ID "com.highfidelity.console-dev")
set_from_env(RELEASE_TYPE RELEASE_TYPE "DEV")
set_from_env(RELEASE_NUMBER RELEASE_NUMBER "")
@ -37,6 +37,7 @@ macro(SET_PACKAGING_PARAMETERS)
set(BUILD_VERSION ${RELEASE_NUMBER})
set(BUILD_ORGANIZATION "High Fidelity")
set(HIGH_FIDELITY_PROTOCOL "hifi")
set(HIGH_FIDELITY_APP_PROTOCOL "hifiapp")
set(INTERFACE_BUNDLE_NAME "Interface")
set(INTERFACE_ICON_PREFIX "interface")
@ -142,7 +143,12 @@ macro(SET_PACKAGING_PARAMETERS)
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(CONSOLE_EXEC_NAME "Sandbox.app")
if (CLIENT_ONLY)
set(CONSOLE_EXEC_NAME "Console.app")
else ()
set(CONSOLE_EXEC_NAME "Sandbox.app")
endif()
set(CONSOLE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${CONSOLE_EXEC_NAME}")
set(CONSOLE_APP_CONTENTS "${CONSOLE_INSTALL_APP_PATH}/Contents")
@ -176,16 +182,19 @@ macro(SET_PACKAGING_PARAMETERS)
# shortcut names
if (PRODUCTION_BUILD)
set(INTERFACE_SHORTCUT_NAME "High Fidelity Interface")
set(CONSOLE_SHORTCUT_NAME "Sandbox")
set(APP_USER_MODEL_ID "com.highfidelity.sandbox")
set(CONSOLE_SHORTCUT_NAME "Console")
set(SANDBOX_SHORTCUT_NAME "Sandbox")
set(APP_USER_MODEL_ID "com.highfidelity.console")
else ()
set(INTERFACE_SHORTCUT_NAME "High Fidelity Interface - ${BUILD_VERSION_NO_SHA}")
set(CONSOLE_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION_NO_SHA}")
set(CONSOLE_SHORTCUT_NAME "Console - ${BUILD_VERSION_NO_SHA}")
set(SANDBOX_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION_NO_SHA}")
endif ()
set(INTERFACE_HF_SHORTCUT_NAME "${INTERFACE_SHORTCUT_NAME}")
set(CONSOLE_HF_SHORTCUT_NAME "High Fidelity ${CONSOLE_SHORTCUT_NAME}")
set(SANDBOX_HF_SHORTCUT_NAME "High Fidelity ${SANDBOX_SHORTCUT_NAME}")
set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "High Fidelity")
set(PRE_SANDBOX_CONSOLE_SHORTCUT_NAME "Server Console")

View file

@ -0,0 +1,22 @@
#
# Copyright 2018 High Fidelity, Inc.
# Created by Gabriel Calero and Cristian Duarte on 2018/10/05
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_HIFIAUDIOCODEC)
if (ANDROID)
set(HIFIAC_INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/hifiAC/codecSDK)
set(HIFIAC_LIB_DIR "${HIFIAC_INSTALL_DIR}/Release")
set(HIFIAC_INCLUDE_DIRS "${HIFIAC_INSTALL_DIR}/include" CACHE TYPE INTERNAL)
list(APPEND HIFIAC_LIBS "${HIFIAC_LIB_DIR}/libaudio.a")
set(HIFIAC_LIBRARIES ${HIFIAC_LIBS} CACHE TYPE INTERNAL)
else()
add_dependency_external_projects(hifiAudioCodec)
target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAUDIOCODEC_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${HIFIAUDIOCODEC_LIBRARIES})
endif()
target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAC_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${HIFIAC_LIBRARIES})
endmacro()

View file

@ -42,6 +42,14 @@
<string>hifi</string>
</array>
</dict>
<dict>
<key>CFBundleURLName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME} APP URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>hifiapp</string>
</array>
</dict>
</array>
<key>NSHighResolutionCapable</key>
<true/>

View file

@ -13,10 +13,12 @@ set(INTERFACE_DISPLAY_NAME "Interface")
set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SHORTCUT_NAME@")
set(INTERFACE_HF_SHORTCUT_NAME "@INTERFACE_HF_SHORTCUT_NAME@")
set(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe")
set(CONSOLE_DISPLAY_NAME "Sandbox")
set(CONSOLE_DISPLAY_NAME "Console")
set(CONSOLE_INSTALL_SUBDIR "@CONSOLE_INSTALL_DIR@")
set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SHORTCUT_NAME@")
set(CONSOLE_HF_SHORTCUT_NAME "@CONSOLE_HF_SHORTCUT_NAME@")
set(SANDBOX_SHORTCUT_NAME "@SANDBOX_SHORTCUT_NAME@")
set(SANDBOX_HF_SHORTCUT_NAME "@SANDBOX_HF_SHORTCUT_NAME@")
set(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@")
set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@")
set(PRE_SANDBOX_CONSOLE_SHORTCUT_NAME "@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@")
@ -25,6 +27,7 @@ set(DS_EXEC_NAME "@DS_EXEC_NAME@")
set(AC_DISPLAY_NAME "Assignment Client")
set(AC_EXEC_NAME "@AC_EXEC_NAME@")
set(HIGH_FIDELITY_PROTOCOL "@HIGH_FIDELITY_PROTOCOL@")
set(HIGH_FIDELITY_APP_PROTOCOL "@HIGH_FIDELITY_APP_PROTOCOL@")
set(PRODUCTION_BUILD "@PRODUCTION_BUILD@")
set(PR_BUILD "@PR_BUILD@")
set(BUILD_ORGANIZATION "@BUILD_ORGANIZATION@")

View file

@ -405,6 +405,14 @@ Var GAClientID
Section "-Previous Install Cleanup"
; Remove the resources folder so we don't end up including removed QML files
RMDir /r "$INSTDIR\resources"
; delete old assignment-client and domain-server so they're no longer present
; in client only installs.
Delete "$INSTDIR\@DS_EXEC_NAME@"
Delete "$INSTDIR\@AC_EXEC_NAME@"
; delete interface so it's not there for server-only installs
Delete "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@"
SectionEnd
@CPACK_NSIS_INSTALLATION_TYPES@
@ -532,9 +540,9 @@ SectionEnd
Var PostInstallDialog
Var DesktopClientCheckbox
Var DesktopServerCheckbox
Var ServerStartupCheckbox
Var LaunchServerNowCheckbox
Var DesktopConsoleCheckbox
Var ConsoleStartupCheckbox
Var LaunchConsoleNowCheckbox
Var LaunchClientNowCheckbox
Var CleanInstallCheckbox
Var CurrentOffset
@ -747,32 +755,20 @@ Function PostInstallOptionsPage
${EndIf}
${If} @SERVER_COMPONENT_CONDITIONAL@
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @SANDBOX_HF_SHORTCUT_NAME@"
${Else}
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @CONSOLE_HF_SHORTCUT_NAME@"
Pop $DesktopServerCheckbox
IntOp $CurrentOffset $CurrentOffset + 15
; set the checkbox state depending on what is present in the registry
!insertmacro SetInstallOption $DesktopServerCheckbox @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ ${BST_UNCHECKED}
${EndIf}
${If} @SERVER_COMPONENT_CONDITIONAL@
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ after install"
Pop $LaunchServerNowCheckbox
; set the checkbox state depending on what is present in the registry
!insertmacro SetInstallOption $LaunchServerNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
${StrContains} $substringResult "/forceNoLaunchServer" $CMDLINE
${IfNot} $substringResult == ""
${NSD_SetState} $LaunchServerNowCheckbox ${BST_UNCHECKED}
${EndIf}
IntOp $CurrentOffset $CurrentOffset + 15
${EndIf}
Pop $DesktopConsoleCheckbox
IntOp $CurrentOffset $CurrentOffset + 15
; set the checkbox state depending on what is present in the registry
!insertmacro SetInstallOption $DesktopConsoleCheckbox @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ ${BST_UNCHECKED}
${If} @CLIENT_COMPONENT_CONDITIONAL@
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @INTERFACE_HF_SHORTCUT_NAME@ after install"
Pop $LaunchClientNowCheckbox
IntOp $CurrentOffset $CurrentOffset + 30
IntOp $CurrentOffset $CurrentOffset + 15
; set the checkbox state depending on what is present in the registry
!insertmacro SetInstallOption $LaunchClientNowCheckbox @CLIENT_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
@ -783,27 +779,44 @@ Function PostInstallOptionsPage
${EndIf}
${If} @SERVER_COMPONENT_CONDITIONAL@
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ on startup"
Pop $ServerStartupCheckbox
IntOp $CurrentOffset $CurrentOffset + 15
; set the checkbox state depending on what is present in the registry
!insertmacro SetInstallOption $ServerStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED}
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @SANDBOX_HF_SHORTCUT_NAME@ after install"
${Else}
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ after install"
${EndIf}
Pop $LaunchConsoleNowCheckbox
; set the checkbox state depending on what is present in the registry
!insertmacro SetInstallOption $LaunchConsoleNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
${StrContains} $substringResult "/forceNoLaunchServer" $CMDLINE
${IfNot} $substringResult == ""
${NSD_SetState} $LaunchConsoleNowCheckbox ${BST_UNCHECKED}
${EndIf}
IntOp $CurrentOffset $CurrentOffset + 30
${If} @SERVER_COMPONENT_CONDITIONAL@
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @SANDBOX_HF_SHORTCUT_NAME@ on startup"
${Else}
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ on startup"
${EndIf}
Pop $ConsoleStartupCheckbox
IntOp $CurrentOffset $CurrentOffset + 15
; set the checkbox state depending on what is present in the registry
!insertmacro SetInstallOption $ConsoleStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED}
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Perform a clean install (Delete older settings and content)"
Pop $CleanInstallCheckbox
IntOp $CurrentOffset $CurrentOffset + 15
${If} @PR_BUILD@ == 1
; a PR build defaults all install options expect LaunchServerNowCheckbox, LaunchClientNowCheckbox and the settings copy to unchecked
; a PR build defaults all install options expect LaunchConsoleNowCheckbox, LaunchClientNowCheckbox and the settings copy to unchecked
${If} @CLIENT_COMPONENT_CONDITIONAL@
${NSD_SetState} $DesktopClientCheckbox ${BST_UNCHECKED}
${EndIf}
${If} @SERVER_COMPONENT_CONDITIONAL@
${NSD_SetState} $DesktopServerCheckbox ${BST_UNCHECKED}
${NSD_SetState} $ServerStartupCheckbox ${BST_UNCHECKED}
${NSD_SetState} $DesktopConsoleCheckbox ${BST_UNCHECKED}
${NSD_SetState} $ConsoleStartupCheckbox ${BST_UNCHECKED}
${EndIf}
; push the offset
@ -824,9 +837,9 @@ FunctionEnd
!macroend
Var DesktopClientState
Var DesktopServerState
Var ServerStartupState
Var LaunchServerNowState
Var DesktopConsoleState
Var ConsoleStartupState
Var LaunchConsoleNowState
Var LaunchClientNowState
Var CopyFromProductionState
Var CleanInstallState
@ -842,11 +855,11 @@ Function ReadInstallTypes
StrCpy $Express "1"
StrCpy $DesktopClientState ${BST_CHECKED}
StrCpy $ServerStartupState ${BST_CHECKED}
StrCpy $LaunchServerNowState ${BST_CHECKED}
StrCpy $ConsoleStartupState ${BST_CHECKED}
StrCpy $LaunchConsoleNowState ${BST_CHECKED}
StrCpy $LaunchClientNowState ${BST_CHECKED}
StrCpy $CleanInstallState ${BST_UNCHECKED}
StrCpy $DesktopServerState ${BST_UNCHECKED}
StrCpy $DesktopConsoleState ${BST_UNCHECKED}
${If} @PR_BUILD@ == 1
StrCpy $CopyFromProductionState ${BST_UNCHECKED}
@ -860,28 +873,25 @@ Function ReadInstallTypes
FunctionEnd
Function ReadPostInstallOptions
; check if the user asked for a desktop shortcut to console
${NSD_GetState} $DesktopConsoleCheckbox $DesktopConsoleState
; check if the user asked to have console launched every startup
${NSD_GetState} $ConsoleStartupCheckbox $ConsoleStartupState
${If} @CLIENT_COMPONENT_CONDITIONAL@
; check if the user asked for a desktop shortcut to High Fidelity
${NSD_GetState} $DesktopClientCheckbox $DesktopClientState
${EndIf}
${If} @SERVER_COMPONENT_CONDITIONAL@
; check if the user asked for a desktop shortcut to Sandbox
${NSD_GetState} $DesktopServerCheckbox $DesktopServerState
; check if the user asked to have Sandbox launched every startup
${NSD_GetState} $ServerStartupCheckbox $ServerStartupState
${EndIf}
${If} @PR_BUILD@ == 1
; check if we need to copy settings/content from production for this PR build
${NSD_GetState} $CopyFromProductionCheckbox $CopyFromProductionState
${EndIf}
${If} @SERVER_COMPONENT_CONDITIONAL@
; check if we need to launch the server post-install
${NSD_GetState} $LaunchServerNowCheckbox $LaunchServerNowState
${EndIf}
; check if we need to launch the console post-install
${NSD_GetState} $LaunchConsoleNowCheckbox $LaunchConsoleNowState
${If} @CLIENT_COMPONENT_CONDITIONAL@
; check if we need to launch the client post-install
@ -893,6 +903,31 @@ Function ReadPostInstallOptions
FunctionEnd
Function HandlePostInstallOptions
${If} @SERVER_COMPONENT_CONDITIONAL@
; check if the user asked for a desktop shortcut to the console
${If} $DesktopConsoleState == ${BST_CHECKED}
Delete "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk"
CreateShortCut "$DESKTOP\@SANDBOX_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
!insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ YES
; Set appUserModelId
ApplicationID::Set "$DESKTOP\@SANDBOX_HF_SHORTCUT_NAME@.lnk" "@APP_USER_MODEL_ID@"
${Else}
!insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO
${EndIf}
${Else}
; check if the user asked for a desktop shortcut to the console
${If} $DesktopConsoleState == ${BST_CHECKED}
Delete "$DESKTOP\@SANDBOX_HF_SHORTCUT_NAME@.lnk"
CreateShortCut "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
!insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ YES
; Set appUserModelId
ApplicationID::Set "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "@APP_USER_MODEL_ID@"
${Else}
!insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO
${EndIf}
${EndIf}
${If} @CLIENT_COMPONENT_CONDITIONAL@
; check if the user asked for a desktop shortcut to High Fidelity
${If} $DesktopClientState == ${BST_CHECKED}
@ -901,38 +936,25 @@ Function HandlePostInstallOptions
${Else}
!insertmacro WriteInstallOption @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ NO
${EndIf}
${EndIf}
${If} @SERVER_COMPONENT_CONDITIONAL@
; check if the user asked for a desktop shortcut to Sandbox
${If} $DesktopServerState == ${BST_CHECKED}
CreateShortCut "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
!insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ YES
; Set appUserModelId
ApplicationID::Set "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "@APP_USER_MODEL_ID@"
${Else}
!insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO
${EndIf}
; check if the user asked to have Console launched every startup
${If} $ConsoleStartupState == ${BST_CHECKED}
; in case we added a shortcut in the global context, pull that now
SetShellVarContext all
Delete "$SMSTARTUP\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk"
; make a startup shortcut in this user's current context
; use the console shortcut name regardless of server/interface install
SetShellVarContext current
CreateShortCut "$SMSTARTUP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
; 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
SetShellVarContext all
Delete "$SMSTARTUP\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk"
; reset the shell var context back
SetShellVarContext all
; make a startup shortcut in this user's current context
SetShellVarContext current
CreateShortCut "$SMSTARTUP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
; reset the shell var context back
SetShellVarContext all
!insertmacro WriteInstallOption @CONSOLE_STARTUP_REG_KEY@ YES
${Else}
!insertmacro WriteInstallOption @CONSOLE_STARTUP_REG_KEY@ NO
${EndIf}
!insertmacro WriteInstallOption @CONSOLE_STARTUP_REG_KEY@ YES
${Else}
!insertmacro WriteInstallOption @CONSOLE_STARTUP_REG_KEY@ NO
${EndIf}
; check if the user asked for a clean install
@ -982,32 +1004,38 @@ Function HandlePostInstallOptions
${EndIf}
${EndIf}
${If} @SERVER_COMPONENT_CONDITIONAL@
${AndIf} $LaunchServerNowState == ${BST_CHECKED}
!insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ YES
; both launches use the explorer trick in case the user has elevated permissions for the installer
${If} $LaunchClientNowState == ${BST_CHECKED}
${If} $LaunchClientNowState == ${BST_CHECKED}
!insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ YES
; create shortcut with ARGUMENTS
CreateShortCut "$TEMP\SandboxShortcut.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@" "-- --launchInterface"
Exec '"$WINDIR\explorer.exe" "$TEMP\SandboxShortcut.lnk"'
${Else}
${Else}
!insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ NO
${EndIf}
${If} $LaunchConsoleNowState == ${BST_CHECKED}
!insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ YES
${Else}
!insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ NO
${EndIf}
${If} $LaunchConsoleNowState == ${BST_CHECKED}
${If} @SERVER_COMPONENT_CONDITIONAL@
${AndIf} $LaunchClientNowState == ${BST_CHECKED}
${AndIf} @CLIENT_COMPONENT_CONDITIONAL@
; both launches use the explorer trick in case the user has elevated permissions for the installer
; create shortcut with ARGUMENTS
CreateShortCut "$TEMP\ConsoleShortcut.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@" "-- --launchInterface"
Exec '"$WINDIR\explorer.exe" "$TEMP\ConsoleShortcut.lnk"'
${Else}
Exec '"$WINDIR\explorer.exe" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"'
${EndIf}
${EndIf}
${ElseIf} @CLIENT_COMPONENT_CONDITIONAL@
!insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ NO
; launch uses the explorer trick in case the user has elevated permissions for the installer
${If} $LaunchClientNowState == ${BST_CHECKED}
!insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ YES
${If} $LaunchClientNowState == ${BST_CHECKED}
${AndIf} @CLIENT_COMPONENT_CONDITIONAL@
${Unless} $LaunchConsoleNowState == ${BST_CHECKED}
${OrUnless} @SERVER_COMPONENT_CONDITIONAL@
; launch uses the explorer trick in case the user has elevated permissions for the installer
Exec '"$WINDIR\explorer.exe" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@"'
${Else}
!insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ NO
${EndIf}
${EndIf}
FunctionEnd
@ -1164,8 +1192,16 @@ Section "-Core installation"
${EndIf}
; Conditional handling for server console shortcut
${If} @SERVER_COMPONENT_CONDITIONAL@
; handling for server console shortcut
Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk"
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_SHORTCUT_NAME@.lnk" \
"$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
; Set appUserModelId
ApplicationID::Set "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_SHORTCUT_NAME@.lnk" "@APP_USER_MODEL_ID@"
${Else}
; handling for interface only console shortcut
Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_SHORTCUT_NAME@.lnk"
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk" \
"$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
; Set appUserModelId
@ -1376,12 +1412,15 @@ Section "Uninstall"
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@INTERFACE_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@SANDBOX_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@INTERFACE_HF_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@SANDBOX_HF_SHORTCUT_NAME@.lnk"
; if it exists, delete the startup shortcut for the current user
SetShellVarContext current
Delete "$SMSTARTUP\@CONSOLE_HF_SHORTCUT_NAME@.lnk"
Delete "$SMSTARTUP\@SANDBOX_HF_SHORTCUT_NAME@.lnk"
SetShellVarContext all
@CPACK_NSIS_DELETE_ICONS@
@ -1414,6 +1453,7 @@ Section "Uninstall"
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@INTERFACE_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@SANDBOX_SHORTCUT_NAME@.lnk"
@CPACK_NSIS_DELETE_ICONS_EXTRA@
;Delete empty start menu parent diretories

View file

@ -0,0 +1,24 @@
Copyright (c) 2006, Ivan Sagalaev
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
* Neither the name of highlight.js nor the names of its contributors may be used to
endorse or promote products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

23
docs/LICENSE_markdeep.txt Normal file
View file

@ -0,0 +1,23 @@
Copyright 2015-2017, Morgan McGuire
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,280 @@
<meta charset="utf-8">
**Pick Parenting**
# What is a pick?
A pick calculates spatial information about the world on on the client.
Picks are immutable to allow efficient use by scripts. There are currently four types of picks:
- Ray pick - Finds the first intersected object along a straight path
- Parabola pick - Finds the first intersected object along a parabolic path
- Stylus pick - Finds the distance between a point and the XZ planes of a list of whitelisted objects
- Collision pick - Uses a volume to check for collisions in the physics engine
With the exception of collision picks, all picks use the visible appearance of the object to check for collisions.
# What is parenting?
Parenting allows an object's position, orientation, and scale (where applicable) to be calculated to another object. This collection of state defines the transform of the object.
Pick parenting allows the pick's transform to be calculated relative to another object, without creating a new pick.
# What can be parented to what?
This object... | | Entity | My Avatar | 3D Overlay | Pick^1 | Pointer^1
---------------------|----------------|--------|-----------|------------|--------|-----------
Can be parented to...| Entity | yes | yes | yes | yes | yes
| My Avatar | yes | no | yes | yes | yes
| Other Avatar | yes | yes | yes | yes | yes
| 3D Overlay | yes | yes | yes | yes | yes
| Pick | no | no | no | yes | yes
| Pointer | no | no | no | no | no
| The Mouse | no | no | no | yes | yes
- ^1 Excluding stylus, which can only be parented to My Avatar's hands for now
# How pick parenting works
This section describes what happens when picks are parented to things.
Since pointers use picks to calculate their results, the rules below
also apply when pointers are parented to things.
## Parent transform exposed
The way a pick is moved/rescaled depends on its parent. In particular,
parenting to a pick actually parents to the pick's result, which
has no notion of scale. A pick can also be transformed relative to
a joint on a model, such as an avatar's hand.
Parent type: | Entity/Avatar | 3D Overlay | Pick
--------------------------|-----------------|--------------|--------
Has position | yes | yes | yes
Has orientation | yes | yes | no
Has scale | yes | yes | no
Can have joints | yes | no | no
## Entity or overlay parent
Parenting a pick to an entity or overlay works similarly to
parenting entities to other things. However, unlike entities,
picks with scale will rescale their dimensions when
their parent rescales.
************************************************************************
* May have a joint, otherwise parent to the parent's origin *
* | *
* scale ^ +--+------+ ^ --+ orientation *
* determined / / | /| |orientation \ offset *
* from / / v / | | v *
* dimensions / / o /..|...................... ^ *
* +-> +---------+ | ------------. . / *
* | | | / position +----> */ <----+ *
* | | | / offset child *
* | | |/ ^ transform*
* v +---------+ / <---+ *
* . +-> dimensions *
* . . . . . . . . . . . . . . | relative *
* . . . v to parent *
************************************************************************
!!! WARNING
Skew is not supported for collision picks. Scaling a parent entity or
overlay non-uniformly can lead to inaccurate shapes for the child
collision pick.
## Avatar parent
A pick parented to an avatar behaves like a wearable. It will maintain
its position relative to some point on the avatar's body. If the pick
has scale (currently only collision picks), then the pick will rescale
when the avatar rescales.
****************************************************************
* Avatar .---. hand ^ -. *
* | | | joint / | *
* '-----> | | \ /orientation | *
* '-+-' +-> o | *
* | / ^ position |relative *
* +---------' +----- |avatar *
* | |scale *
* | if not a joint, |factor *
* | parent to the |(default: 1) *
* | avatar's origin | *
* |\ | |uniform scale *
* | \ | | ^ *
* | \ | | +-> *
* | o \ <-+ -' v *
****************************************************************
***************************************************************
* orientation offset *
* <-----+ *
* ^ \ *
* \ *
* \ child *
* Avatar .---. hand .......... * <-- transform *
* | | | joint . ^ | *
* '-----> | | \ . / v *
* '-+-' +-> o ---------+ ^ *
* | / position +-> scale *
* +---------' offset v relative *
* | to avatar *
***************************************************************
## Pick parent
Picks can also be parented to other picks.
When this is done, the child pick is actually parented to
the parent pick's result, not the pick itself.
A pick parented to another pick will have its position changed,
but not its orientation or scale.
### Ray pick parent
**********************************
* position *
* o *
* \ \ orientation *
* \ \ *
* \ v . . . . *
* \ . *
* \ . result transform *
* * <-- no scale *
* . no orientation *
* . *
**********************************
### Parabola pick parent
**************************************************************
* .------. acceleration *
* + + | ^ *
* speed x ^ / \ . | | *
* orientation / / \ . v | *
* / / * y axis to rotate *
* | . ^ acceleration with *
* o . | *
* position result transform *
* no scale *
* no orientation *
**************************************************************
### Stylus pick parent
********************************************
* *
* *
* . *
* . *
* . *
* .---. avatar * *
* | | hand / ^. *
* | | \ / \ . *
* '-+-' +-> o \ *
* | / result transform *
* +---------' no scale *
* | no orientation *
********************************************
### Collision pick parent
********************************************
* *
* .---. *
* | | <-- collision pick *
* | | *
* | *<-+--- result transform at *
* | | collision pick position *
* | | no scale *
* . . . '---' . . no orientation *
* *
********************************************
# Effect of scale on picks
Scale affects the position and shape of picks, which in turn affects the pick result.
Scale currently does not affect the max distance of a pick.
## Ray and stylus pick scaling
Rescaling the parent of a ray pick or stylus pick can result in a translation of the pick.
***************************************
* before after *
* *
* pick *
* pick +--------------+ ^ *
* ^ | |/ *
* +-----+/ | o *
* | o | | *
* | | | | *
* +-----+ | | *
* parent | | *
* +--------------+ *
* parent *
* *
***************************************
## Parabola pick scaling
***************************************************************************
* before after after *
* (scaleWithParent (scaleWithParent *
* is false) is true) *
* *
* .----. *
* + + *
* .--. / \ *
* + + / \ *
* .--. / \ / \ *
* + + /pick \ /pick \ *
* / \ +-------o +-------o *
* /pick \ | | | | *
* +---o | | | | *
* | | | | | | *
* +---+ +-------+ +-------+ *
* parent parent parent *
***************************************************************************
## Collision pick scaling
Collision picks use the full transform of their parent (position, orientation, and scale/dimensions).
When first created, a collision pick's transform is defined in world space.
As the parent rescales, the collision pick rescales proportionally.
The collision pick's threshold also rescales. The change is proportional to the largest
dimension of the parent. So, if the largest dimension of the parent was 3.0 and is now 6.0,
the threshold doubles.
**************************************************************************************
* *
* before after after *
* (scaleWithParent (scaleWithParent *
* is false) is true) *
* *
* pick *
* +------------+ *
* | | *
* pick | ........ | *
* +-----+ | . . | *
* |.....| | . . | *
* |. .| theshold | . . | *
* pick |.....|___ | | . . | *
* +-----+ +-----+--- <-+ | ........ +___ *
* |.....| | | theshold *
* |. .| theshold +------------+--- *
* |.....|___ | *
* +-----+--- <-+ *
* +----------+ +----------+ *
* +---+ / \ / \ *
* +-----+ +--------------+ +--------------+ *
* parent parent parent *
* *
**************************************************************************************
<style class="fallback">body{visibility:hidden}</style><script>markdeepOptions={tocStyle:'medium'};</script>
<link rel="stylesheet" href="../../markdeep_apidoc.css?">
<!-- Markdeep: --><style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style><script src="../../markdeep.min.js"></script><script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>

7
docs/markdeep.min.js vendored Normal file

File diff suppressed because one or more lines are too long

170
docs/markdeep_apidoc.css Normal file
View file

@ -0,0 +1,170 @@
/* Custom stylesheet for API documentation by Aras Pranckevičius, http://aras-p.info/
and tweaked by Morgan McGuire.
Licensed as public domain or BSD 2-clause, whichever is more convenient for you.
Originally from https://github.com/aras-p/markdeep-docs-style */
body {
max-width: 50em;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
text-align: left;
margin: 1.5em;
padding: 0 1em;
}
/* if screen is wide enough, put table of contents on the right side */
@media screen and (min-width: 64em) {
.md .longTOC, .md .mediumTOC, .md .shortTOC {
max-width: 20em;
left: 54em;
display:block;
position: fixed;
top:0;
bottom:0;
overflow-y:scroll;
margin-top:0;
margin-bottom:0;
padding-top:1em;
}
}
/* for narrow screens or print, hide table of contents */
@media screen and (max-width: 64em) {
.md .longTOC, .md .mediumTOC, .md .shortTOC { display: none; }
}
@media print {
.md .longTOC, .md .mediumTOC, .md .shortTOC { display: none; }
body { max-width: 100%; }
}
/* reset heading/link fonts to that of body */
.md a,
.md div.title, contents, .md .tocHeader,
.md h1, .md h2, .md h3, .md h4, .md h5, .md h6,
.md .nonumberh1, .md .nonumberh2, .md .nonumberh3, .md .nonumberh4, .md .nonumberh5, .md .nonumberh6,
.md .shortTOC, .md .mediumTOC, .md .longTOC {
font-family: inherit;
}
.md div.title {
margin: 0.4em 0 0 0;
padding: 0;
text-align: inherit;
}
.md div.subtitle {
text-align: inherit;
}
/* faint border below headings */
.md h1, .md h2, .md h3, .md h4,
.md .nonumberh1, .md .nonumberh2, .md .nonumberh3, .md .nonumberh4 {
border-bottom: 1px solid rgba(0,0,0,.1);
}
/* heading font styles */
.md h1, .md .nonumberh1, .md div.title {
font-size: 150%;
font-weight: 600;
color: rgba(0,0,0,.7);
}
.md h2, .md .nonumberh2 {
font-size: 120%;
font-weight: 400;
color: rgba(0,0,0,.9);
}
.md h3, .md .nonumberh3 {
font-size: 110%;
font-weight: 400;
color: rgba(0,0,0,.7);
}
/* no numbering of headings */
.md h1:before, .md h2:before, .md h3:before, .md h4:before { content: none; }
/* link styling */
.md a:link, .md a:visited {
color: #3f51b5;
}
/* inline and block code */
.md code, .md pre.listing {
background-color: rgba(0,0,0,.05);
padding: 0.1em 0.2em;
border-radius: 0.15em;
}
.md pre.listing code {
background-color: transparent;
padding: 0;
border: none;
}
/* table of contents styling; make all 3 forms of it look the same */
.md .longTOC, .md .mediumTOC, .md .shortTOC {
font-size: inherit;
line-height: 120%;
margin: 1em 0;
padding: .4rem;
border-left: .1rem solid #3f51b5;
}
.md .tocHeader {
margin: 0;
padding: 0;
border: none;
font-size: inherit;
}
.md .tocNumber {
display: none;
}
.md .longTOC .level1, .md .mediumTOC .level1, .md .shortTOC .level1 {
font-weight: inherit;
padding: 0;
margin: 0;
}
.md .longTOC p, .md .mediumTOC p, .md .shortTOC p {
overflow: hidden;
text-overflow: ellipsis;
}
.md .longTOC center, .md .mediumTOC center, .md .shortTOC center, .md .tocHeader {
text-align: left;
}
.md .longTOC b, .md .mediumTOC b, .md .shortTOC b {
font-weight: 400;
}
.md .longTOC center b, .md .mediumTOC center b, .md .shortTOC center b {
font-weight: bold;
}
.md .longTOC a, .md .mediumTOC a, .md .shortTOC a {
color: black;
}
.md .longTOC .level1, .md .mediumTOC .level1, .md .shortTOC .level1,
.md .longTOC .level2, .md .mediumTOC .level2, .md .shortTOC .level2,
.md .longTOC .level3, .md .mediumTOC .level3, .md .shortTOC .level3 {
white-space: nowrap;
margin: 0;
padding: 0;
font-size: 90%;
}
/* tables; use fainter colors than regular markdeep style */
.md table.table {
font-size: 90%;
}
.md table.table th {
border: none;
background-color: #ccc;
color: rgba(0,0,0,.6);
}
.md table.table tr, .md table.table td {
border-color: #eee;
}
.md table.table tr:nth-child(even) {
background-color: #f4f4f4;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -100,6 +100,25 @@ Rectangle {
wearablesModel.setProperty(wearableIndex, 'properties', wearableModelItemProperties);
}
function entityHasAvatarJoints(entityID) {
var hasAvatarJoint = false;
var props = Entities.getEntityProperties(entityID);
var avatarJointsCount = MyAvatar.getJointNames().length;
if (props && avatarJointsCount >= 0 ) {
var entityJointNames = Entities.getJointNames(entityID);
for (var index = 0; index < entityJointNames.length; index++) {
var avatarJointIndex = MyAvatar.getJointIndex(entityJointNames[index]);
if (avatarJointIndex >= 0) {
hasAvatarJoint = true;
break;
}
}
}
return hasAvatarJoint;
}
function getCurrentWearable() {
return wearablesCombobox.currentIndex !== -1 ? wearablesCombobox.model.get(wearablesCombobox.currentIndex) : null;
}
@ -109,6 +128,7 @@ Rectangle {
var wearable = wearablesCombobox.model.get(i);
if (wearable.id === entityID) {
wearablesCombobox.currentIndex = i;
softWearableTimer.restart();
break;
}
}
@ -118,6 +138,7 @@ Rectangle {
visible = false;
adjustWearablesClosed(status, avatarName);
}
HifiConstants { id: hifi }
@ -130,6 +151,20 @@ Rectangle {
hoverEnabled: true;
}
Timer {
id: softWearableTimer
interval: 1000
running: false
repeat: true
onTriggered: {
var currentWearable = getCurrentWearable();
var soft = currentWearable ? currentWearable.relayParentJoints : false;
var softEnabled = currentWearable ? entityHasAvatarJoints(currentWearable.id) : false;
isSoft.set(soft);
isSoft.enabled = softEnabled;
}
}
Column {
anchors.top: parent.top
anchors.topMargin: 12
@ -247,13 +282,13 @@ Rectangle {
var rotation = currentWearable ? currentWearable.localRotationAngles : { x : 0, y : 0, z : 0 };
var scale = currentWearable ? currentWearable.dimensions.x / currentWearable.naturalDimensions.x : 1.0;
var joint = currentWearable ? currentWearable.parentJointIndex : -1;
var soft = currentWearable ? currentWearable.relayParentJoints : false;
softWearableTimer.restart();
positionVector.set(position);
rotationVector.set(rotation);
scalespinner.set(scale);
jointsCombobox.set(joint);
isSoft.set(soft);
if (currentWearable) {
wearableSelected(currentWearable.id);

File diff suppressed because it is too large Load diff

View file

@ -122,6 +122,7 @@
#include <RecordingScriptingInterface.h>
#include <UpdateSceneTask.h>
#include <RenderViewTask.h>
#include <render/EngineStats.h>
#include <SecondaryCamera.h>
#include <ResourceCache.h>
#include <ResourceRequest.h>
@ -215,7 +216,8 @@
#include <raypick/LaserPointerScriptingInterface.h>
#include <raypick/PickScriptingInterface.h>
#include <raypick/PointerScriptingInterface.h>
#include <raypick/MouseRayPick.h>
#include <raypick/RayPick.h>
#include <raypick/MouseTransformNode.h>
#include <FadeEffect.h>
@ -364,7 +366,6 @@ static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SI
static const uint32_t INVALID_FRAME = UINT32_MAX;
static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation
static const float INITIAL_QUERY_RADIUS = 10.0f; // priority radius for entities before physics enabled
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
@ -2286,8 +2287,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
});
// Setup the mouse ray pick and related operators
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickID(DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, std::make_shared<MouseRayPick>(
PickFilter(PickScriptingInterface::PICK_ENTITIES() | PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE()), 0.0f, true)));
{
auto mouseRayPick = std::make_shared<RayPick>(Vectors::ZERO, Vectors::UP, PickFilter(PickScriptingInterface::PICK_ENTITIES() | PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE()), 0.0f, true);
mouseRayPick->parentTransform = std::make_shared<MouseTransformNode>();
mouseRayPick->setJointState(PickQuery::JOINT_STATE_MOUSE);
auto mouseRayPickID = DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, mouseRayPick);
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickID(mouseRayPickID);
}
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([](unsigned int rayPickID) {
RayToEntityIntersectionResult entityResult;
entityResult.intersects = false;
@ -5377,8 +5383,8 @@ void Application::resetPhysicsReadyInformation() {
// collision information of nearby entities to make running bullet be safe.
_fullSceneReceivedCounter = 0;
_fullSceneCounterAtLastPhysicsCheck = 0;
_nearbyEntitiesCountAtLastPhysicsCheck = 0;
_nearbyEntitiesStabilityCount = 0;
_gpuTextureMemSizeStabilityCount = 0;
_gpuTextureMemSizeAtLastCheck = 0;
_physicsEnabled = false;
_octreeProcessor.startEntitySequence();
}
@ -5617,18 +5623,21 @@ void Application::update(float deltaTime) {
// for nearby entities before starting bullet up.
quint64 now = usecTimestampNow();
if (isServerlessMode() || _octreeProcessor.isLoadSequenceComplete()) {
// we've received a new full-scene octree stats packet, or it's been long enough to try again anyway
_lastPhysicsCheckTime = now;
_fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter;
_lastQueriedViews.clear(); // Force new view.
bool enableInterstitial = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
if (gpuTextureMemSizeStable() || !enableInterstitial) {
// we've received a new full-scene octree stats packet, or it's been long enough to try again anyway
_lastPhysicsCheckTime = now;
_fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter;
_lastQueriedViews.clear(); // Force new view.
// process octree stats packets are sent in between full sends of a scene (this isn't currently true).
// We keep physics disabled until we've received a full scene and everything near the avatar in that
// scene is ready to compute its collision shape.
if (getMyAvatar()->isReadyForPhysics()) {
_physicsEnabled = true;
setIsInterstitialMode(false);
getMyAvatar()->updateMotionBehaviorFromMenu();
// process octree stats packets are sent in between full sends of a scene (this isn't currently true).
// We keep physics disabled until we've received a full scene and everything near the avatar in that
// scene is ready to compute its collision shape.
if (getMyAvatar()->isReadyForPhysics()) {
_physicsEnabled = true;
setIsInterstitialMode(false);
getMyAvatar()->updateMotionBehaviorFromMenu();
}
}
}
} else if (domainLoadingInProgress) {
@ -5915,6 +5924,10 @@ void Application::update(float deltaTime) {
// update the rendering without any simulation
getEntities()->update(false);
}
// remove recently dead avatarEntities
SetOfEntities deadAvatarEntities;
_entitySimulation->takeDeadAvatarEntities(deadAvatarEntities);
avatarManager->removeDeadAvatarEntities(deadAvatarEntities);
}
// AvatarManager update
@ -6239,6 +6252,8 @@ int Application::sendNackPackets() {
missingSequenceNumbers = sequenceNumberStats.getMissingSet();
});
_isMissingSequenceNumbers = (missingSequenceNumbers.size() != 0);
// construct nack packet(s) for this node
foreach(const OCTREE_PACKET_SEQUENCE& missingNumber, missingSequenceNumbers) {
nackPacketList->writePrimitive(missingNumber);
@ -6265,9 +6280,19 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
const bool isModifiedQuery = !_physicsEnabled;
if (isModifiedQuery) {
// Create modified view that is a simple sphere.
bool interstitialModeEnabled = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
ConicalViewFrustum sphericalView;
sphericalView.setSimpleRadius(INITIAL_QUERY_RADIUS);
_octreeQuery.setConicalViews({ sphericalView });
if (interstitialModeEnabled) {
ConicalViewFrustum farView;
farView.set(_viewFrustum);
_octreeQuery.setConicalViews({ sphericalView, farView });
} else {
_octreeQuery.setConicalViews({ sphericalView });
}
_octreeQuery.setOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE);
static constexpr float MIN_LOD_ADJUST = -20.0f;
_octreeQuery.setBoundaryLevelAdjust(MIN_LOD_ADJUST);
@ -6579,69 +6604,23 @@ void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNode
}
}
bool Application::nearbyEntitiesAreReadyForPhysics() {
// this is used to avoid the following scenario:
// A table has some items sitting on top of it. The items are at rest, meaning they aren't active in bullet.
// Someone logs in close to the table. They receive information about the items on the table before they
// receive information about the table. The items are very close to the avatar's capsule, so they become
// activated in bullet. This causes them to fall to the floor, because the table's shape isn't yet in bullet.
EntityTreePointer entityTree = getEntities()->getTree();
if (!entityTree) {
return false;
}
bool Application::gpuTextureMemSizeStable() {
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
auto renderStats = renderConfig->getConfig<render::EngineStats>("Stats");
// We don't want to use EntityTree::findEntities(AABox, ...) method because that scan will snarf parented entities
// whose bounding boxes cannot be computed (it is too loose for our purposes here). Instead we manufacture
// custom filters and use the general-purpose EntityTree::findEntities(filter, ...)
QVector<EntityItemPointer> entities;
AABox avatarBox(getMyAvatar()->getWorldPosition() - glm::vec3(PHYSICS_READY_RANGE), glm::vec3(2 * PHYSICS_READY_RANGE));
// create two functions that use avatarBox (entityScan and elementScan), the second calls the first
std::function<bool (EntityItemPointer&)> entityScan = [=](EntityItemPointer& entity) {
if (entity->shouldBePhysical()) {
bool success = false;
AABox entityBox = entity->getAABox(success);
// important: bail for entities that cannot supply a valid AABox
return success && avatarBox.touches(entityBox);
}
return false;
};
std::function<bool(const OctreeElementPointer&, void*)> elementScan = [&](const OctreeElementPointer& element, void* unused) {
if (element->getAACube().touches(avatarBox)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->getEntities(entityScan, entities);
return true;
}
return false;
};
qint64 textureResourceGPUMemSize = renderStats->textureResourceGPUMemSize;
qint64 texturePopulatedGPUMemSize = renderStats->textureResourcePopulatedGPUMemSize;
qint64 textureTransferSize = renderStats->texturePendingGPUTransferSize;
entityTree->withReadLock([&] {
// Pass the second function to the general-purpose EntityTree::findEntities()
// which will traverse the tree, apply the two filter functions (to element, then to entities)
// as it traverses. The end result will be a list of entities that match.
entityTree->findEntities(elementScan, entities);
});
uint32_t nearbyCount = entities.size();
if (nearbyCount == _nearbyEntitiesCountAtLastPhysicsCheck) {
_nearbyEntitiesStabilityCount++;
if (_gpuTextureMemSizeAtLastCheck == textureResourceGPUMemSize) {
_gpuTextureMemSizeStabilityCount++;
} else {
_nearbyEntitiesStabilityCount = 0;
_gpuTextureMemSizeStabilityCount = 0;
}
_nearbyEntitiesCountAtLastPhysicsCheck = nearbyCount;
_gpuTextureMemSizeAtLastCheck = textureResourceGPUMemSize;
const uint32_t MINIMUM_NEARBY_ENTITIES_STABILITY_COUNT = 3;
if (_nearbyEntitiesStabilityCount >= MINIMUM_NEARBY_ENTITIES_STABILITY_COUNT) {
// We've seen the same number of nearby entities for several stats packets in a row. assume we've got all
// the local entities.
bool result = true;
foreach (EntityItemPointer entity, entities) {
if (entity->shouldBePhysical() && !entity->isReadyToComputeShape()) {
HIFI_FCDEBUG(interfaceapp(), "Physics disabled until entity loads: " << entity->getID() << entity->getName());
// don't break here because we want all the relevant entities to start their downloads
result = false;
}
}
return result;
if (_gpuTextureMemSizeStabilityCount >= _minimumGPUTextureMemSizeStabilityCount) {
return (textureResourceGPUMemSize == texturePopulatedGPUMemSize) && (textureTransferSize == 0);
}
return false;
}

View file

@ -183,6 +183,8 @@ public:
// passes, mirror window passes, etc
void copyDisplayViewFrustum(ViewFrustum& viewOut) const;
bool isMissingSequenceNumbers() { return _isMissingSequenceNumbers; }
const ConicalViewFrustums& getConicalViews() const override { return _conicalViews; }
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
@ -230,6 +232,8 @@ public:
float getSettingConstrainToolbarPosition() { return _constrainToolbarPosition.get(); }
void setSettingConstrainToolbarPosition(bool setting);
Q_INVOKABLE void setMinimumGPUTextureMemStabilityCount(int stabilityCount) { _minimumGPUTextureMemSizeStabilityCount = stabilityCount; }
NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; }
virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
@ -525,7 +529,7 @@ private:
bool importFromZIP(const QString& filePath);
bool importImage(const QString& urlString);
bool nearbyEntitiesAreReadyForPhysics();
bool gpuTextureMemSizeStable();
int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode);
void trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket);
@ -581,6 +585,8 @@ private:
QElapsedTimer _lastTimeUpdated;
QElapsedTimer _lastTimeRendered;
int _minimumGPUTextureMemSizeStabilityCount { 30 };
ShapeManager _shapeManager;
PhysicalEntitySimulationPointer _entitySimulation;
PhysicsEnginePointer _physicsEngine;
@ -709,6 +715,8 @@ private:
bool _fakedMouseEvent { false };
bool _isMissingSequenceNumbers { false };
void checkChangeCursor();
mutable QMutex _changeCursorLock { QMutex::Recursive };
Qt::CursorShape _desiredCursor{ Qt::BlankCursor };
@ -719,8 +727,10 @@ private:
std::atomic<uint32_t> _fullSceneReceivedCounter { 0 }; // how many times have we received a full-scene octree stats packet
uint32_t _fullSceneCounterAtLastPhysicsCheck { 0 }; // _fullSceneReceivedCounter last time we checked physics ready
uint32_t _nearbyEntitiesCountAtLastPhysicsCheck { 0 }; // how many in-range entities last time we checked physics ready
uint32_t _nearbyEntitiesStabilityCount { 0 }; // how many times has _nearbyEntitiesCountAtLastPhysicsCheck been the same
qint64 _gpuTextureMemSizeStabilityCount { 0 };
qint64 _gpuTextureMemSizeAtLastCheck { 0 };
quint64 _lastPhysicsCheckTime { usecTimestampNow() }; // when did we last check to see if physics was ready
bool _keyboardDeviceHasFocus { true };

View file

@ -275,7 +275,11 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
if (inView && avatar->hasNewJointData()) {
numAvatarsUpdated++;
}
avatar->_transit.update(deltaTime, avatar->_globalPosition, _transitConfig);
auto transitStatus = avatar->_transit.update(deltaTime, avatar->_globalPosition, _transitConfig);
if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT || transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) {
avatar->_transit.reset();
avatar->setIsNewAvatar(false);
}
avatar->simulate(deltaTime, inView);
avatar->updateRenderItem(renderTransaction);
avatar->updateSpaceProxy(workloadTransaction);
@ -448,6 +452,17 @@ void AvatarManager::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction
transaction.clear();
}
void AvatarManager::removeDeadAvatarEntities(const SetOfEntities& deadEntities) {
for (auto entity : deadEntities) {
QUuid sessionID = entity->getOwningAvatarID();
AvatarSharedPointer avatar = getAvatarBySessionID(sessionID);
if (avatar) {
const bool REQUIRES_REMOVAL_FROM_TREE = false;
avatar->clearAvatarEntity(entity->getID(), REQUIRES_REMOVAL_FROM_TREE);
}
}
}
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
auto avatar = std::static_pointer_cast<OtherAvatar>(removedAvatar);
{

View file

@ -26,6 +26,7 @@
#include <avatars-renderer/ScriptAvatar.h>
#include <AudioInjector.h>
#include <workload/Space.h>
#include <EntitySimulation.h> // for SetOfEntities
#include "AvatarMotionState.h"
#include "MyAvatar.h"
@ -187,6 +188,7 @@ public:
void queuePhysicsChange(const OtherAvatarPointer& avatar);
void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction);
void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction);
void removeDeadAvatarEntities(const SetOfEntities& deadEntities);
public slots:
/**jsdoc

View file

@ -137,7 +137,7 @@ MyAvatar::MyAvatar(QThread* thread) :
_useSnapTurnSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "useSnapTurn", _useSnapTurn),
_userHeightSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userHeight", DEFAULT_AVATAR_HEIGHT),
_flyingHMDSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "flyingHMD", _flyingPrefHMD),
_avatarEntityCountSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" << "size", _flyingPrefHMD)
_avatarEntityCountSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" << "size", 0)
{
_clientTraitsHandler = std::unique_ptr<ClientTraitsHandler>(new ClientTraitsHandler(this));

View file

@ -10,8 +10,11 @@
//
#include "SafeLanding.h"
#include <SharedUtil.h>
#include "EntityTreeRenderer.h"
#include "ModelEntityItem.h"
#include "RenderableModelEntityItem.h"
#include "InterfaceLogging.h"
#include "Application.h"
@ -39,6 +42,7 @@ void SafeLanding::startEntitySequence(QSharedPointer<EntityTreeRenderer> entityT
_entityTree = entityTree;
_trackedEntities.clear();
_trackingEntities = true;
_maxTrackedEntityCount = 0;
connect(std::const_pointer_cast<EntityTree>(_entityTree).get(),
&EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity);
connect(std::const_pointer_cast<EntityTree>(_entityTree).get(),
@ -47,6 +51,7 @@ void SafeLanding::startEntitySequence(QSharedPointer<EntityTreeRenderer> entityT
_sequenceNumbers.clear();
_initialStart = INVALID_SEQUENCE;
_initialEnd = INVALID_SEQUENCE;
_startTime = usecTimestampNow();
EntityTreeRenderer::setEntityLoadingPriorityFunction(&ElevatedPriority);
}
}
@ -55,6 +60,7 @@ void SafeLanding::stopEntitySequence() {
Locker lock(_lock);
_trackingEntities = false;
_maxTrackedEntityCount = 0;
_trackedEntityStabilityCount = 0;
_initialStart = INVALID_SEQUENCE;
_initialEnd = INVALID_SEQUENCE;
_trackedEntities.clear();
@ -66,18 +72,17 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) {
Locker lock(_lock);
EntityItemPointer entity = _entityTree->findEntityByID(entityID);
if (entity) {
if (entity && entity->getCreated() < _startTime) {
_trackedEntities.emplace(entityID, entity);
int trackedEntityCount = (int)_trackedEntities.size();
if (trackedEntityCount > _maxTrackedEntityCount) {
_maxTrackedEntityCount = trackedEntityCount;
_trackedEntityStabilityCount = 0;
}
qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName();
}
} else {
qCDebug(interfaceapp) << "Safe Landing: Null Entity: " << entityID;
}
}
@ -104,10 +109,10 @@ void SafeLanding::noteReceivedsequenceNumber(int sequenceNumber) {
bool SafeLanding::isLoadSequenceComplete() {
if (isEntityLoadingComplete() && isSequenceNumbersComplete()) {
Locker lock(_lock);
_trackedEntities.clear();
_initialStart = INVALID_SEQUENCE;
_initialEnd = INVALID_SEQUENCE;
_entityTree = nullptr;
_trackingEntities = false; // Don't track anything else that comes in.
EntityTreeRenderer::setEntityLoadingPriorityFunction(StandardPriority);
}
@ -116,11 +121,18 @@ bool SafeLanding::isLoadSequenceComplete() {
float SafeLanding::loadingProgressPercentage() {
Locker lock(_lock);
static const int MINIMUM_TRACKED_ENTITY_STABILITY_COUNT = 15;
float entityReadyPercentage = 0.0f;
if (_maxTrackedEntityCount > 0) {
return ((_maxTrackedEntityCount - _trackedEntities.size()) / (float)_maxTrackedEntityCount);
entityReadyPercentage = ((_maxTrackedEntityCount - _trackedEntities.size()) / (float)_maxTrackedEntityCount);
}
return 0.0f;
if (_trackedEntityStabilityCount < MINIMUM_TRACKED_ENTITY_STABILITY_COUNT) {
entityReadyPercentage *= 0.20f;
}
return entityReadyPercentage;
}
bool SafeLanding::isSequenceNumbersComplete() {
@ -130,11 +142,16 @@ bool SafeLanding::isSequenceNumbersComplete() {
_initialEnd + SEQUENCE_MODULO - _initialStart;
auto startIter = _sequenceNumbers.find(_initialStart);
auto endIter = _sequenceNumbers.find(_initialEnd - 1);
bool missingSequenceNumbers = qApp->isMissingSequenceNumbers();
if (sequenceSize == 0 ||
(startIter != _sequenceNumbers.end()
&& endIter != _sequenceNumbers.end()
&& distance(startIter, endIter) == sequenceSize - 1) ) {
_trackingEntities = false; // Don't track anything else that comes in.
&& ((distance(startIter, endIter) == sequenceSize - 1) || !missingSequenceNumbers))) {
bool enableInterstitial = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
if (!enableInterstitial) {
_trackingEntities = false; // Don't track anything else that comes in.
}
return true;
}
}
@ -145,13 +162,13 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) {
if (entity && !entity->getCollisionless()) {
const auto& entityType = entity->getType();
if (entityType == EntityTypes::Model) {
ModelEntityItem * modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity).get();
RenderableModelEntityItem * modelEntity = std::dynamic_pointer_cast<RenderableModelEntityItem>(entity).get();
static const std::set<ShapeType> downloadedCollisionTypes
{ SHAPE_TYPE_COMPOUND, SHAPE_TYPE_SIMPLE_COMPOUND, SHAPE_TYPE_STATIC_MESH, SHAPE_TYPE_SIMPLE_HULL };
bool hasAABox;
entity->getAABox(hasAABox);
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
return (!entity->shouldBePhysical() || entity->isReadyToComputeShape());
return (!entity->shouldBePhysical() || entity->isReadyToComputeShape() || modelEntity->computeShapeFailedToLoad());
}
}
}
@ -166,16 +183,20 @@ bool SafeLanding::isEntityLoadingComplete() {
auto entityTree = qApp->getEntities();
auto entityMapIter = _trackedEntities.begin();
bool enableInterstitial = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
while (entityMapIter != _trackedEntities.end()) {
auto entity = entityMapIter->second;
bool isVisuallyReady = true;
Settings settings;
bool enableInterstitial = settings.value("enableIntersitialMode", false).toBool();
if (enableInterstitial) {
isVisuallyReady = (entity->isVisuallyReady() || !entityTree->renderableForEntityId(entityMapIter->first));
auto entityRenderable = entityTree->renderableForEntityId(entityMapIter->first);
if (!entityRenderable) {
entityTree->addingEntity(entityMapIter->first);
}
isVisuallyReady = entity->isVisuallyReady() || (!entityRenderable && !entity->isParentPathComplete());
}
if (isEntityPhysicsReady(entity) && isVisuallyReady) {
@ -188,6 +209,12 @@ bool SafeLanding::isEntityLoadingComplete() {
entityMapIter++;
}
}
if (enableInterstitial) {
_trackedEntityStabilityCount++;
}
return _trackedEntities.empty();
}

View file

@ -52,6 +52,9 @@ private:
int _initialStart { INVALID_SEQUENCE };
int _initialEnd { INVALID_SEQUENCE };
int _maxTrackedEntityCount { 0 };
int _trackedEntityStabilityCount { 0 };
quint64 _startTime { 0 };
struct SequenceLessThan {
bool operator()(const int& a, const int& b) const;

View file

@ -345,9 +345,9 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha
}
}
CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) :
Pick(filter, maxDistance, enabled),
_mathPick(collisionRegion),
CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, bool scaleWithParent, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) :
Pick(collisionRegion, filter, maxDistance, enabled),
_scaleWithParent(scaleWithParent),
_physicsEngine(physicsEngine) {
if (collisionRegion.shouldComputeShapeInfo()) {
_cachedResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(collisionRegion.modelURL);
@ -361,9 +361,15 @@ CollisionRegion CollisionPick::getMathematicalPick() const {
if (parentTransform) {
Transform parentTransformValue = parentTransform->getTransform();
mathPick.transform = parentTransformValue.worldTransform(mathPick.transform);
glm::vec3 scale = parentTransformValue.getScale();
float largestDimension = glm::max(glm::max(scale.x, scale.y), scale.z);
mathPick.threshold *= largestDimension;
if (_scaleWithParent) {
glm::vec3 scale = parentTransformValue.getScale();
float largestDimension = glm::max(glm::max(scale.x, scale.y), scale.z);
mathPick.threshold *= largestDimension;
} else {
// We need to undo parent scaling after-the-fact because the parent's scale was needed to calculate this mathPick's position
mathPick.transform.setScale(_mathPick.transform.getScale());
}
}
return mathPick;
}
@ -424,5 +430,7 @@ PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick)
}
Transform CollisionPick::getResultTransform() const {
return Transform(getMathematicalPick().transform);
Transform transform;
transform.setTranslation(_mathPick.transform.getTranslation());
return transform;
}

View file

@ -47,7 +47,7 @@ public:
class CollisionPick : public Pick<CollisionRegion> {
public:
CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine);
CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, bool scaleWithParent, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine);
CollisionRegion getMathematicalPick() const override;
PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override {
@ -67,7 +67,8 @@ protected:
void computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
void filterIntersections(std::vector<ContactTestResult>& intersections) const;
CollisionRegion _mathPick;
bool _scaleWithParent;
PhysicsEnginePointer _physicsEngine;
QSharedPointer<GeometryResource> _cachedResource;

View file

@ -1,43 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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 "JointParabolaPick.h"
#include "avatar/AvatarManager.h"
JointParabolaPick::JointParabolaPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset,
float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar, bool scaleWithAvatar, PickFilter& filter, float maxDistance, bool enabled) :
ParabolaPick(speed, accelerationAxis, rotateAccelerationWithAvatar, scaleWithAvatar, filter, maxDistance, enabled),
_jointName(jointName),
_posOffset(posOffset),
_dirOffset(dirOffset)
{
}
PickParabola JointParabolaPick::getMathematicalPick() const {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
int jointIndex = myAvatar->getJointIndex(QString::fromStdString(_jointName));
bool useAvatarHead = _jointName == "Avatar";
const int INVALID_JOINT = -1;
if (jointIndex != INVALID_JOINT || useAvatarHead) {
glm::vec3 jointPos = useAvatarHead ? myAvatar->getHeadPosition() : myAvatar->getAbsoluteJointTranslationInObjectFrame(jointIndex);
glm::quat jointRot = useAvatarHead ? myAvatar->getHeadOrientation() : myAvatar->getAbsoluteJointRotationInObjectFrame(jointIndex);
glm::vec3 avatarPos = myAvatar->getWorldPosition();
glm::quat avatarRot = myAvatar->getWorldOrientation();
glm::vec3 pos = useAvatarHead ? jointPos : avatarPos + (avatarRot * jointPos);
glm::quat rot = useAvatarHead ? jointRot * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT) : avatarRot * jointRot;
// Apply offset
pos = pos + (rot * (myAvatar->getSensorToWorldScale() * _posOffset));
glm::vec3 dir = glm::normalize(rot * glm::normalize(_dirOffset));
return PickParabola(pos, getSpeed() * dir, getAcceleration());
}
return PickParabola();
}

View file

@ -1,32 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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_JointParabolaPick_h
#define hifi_JointParabolaPick_h
#include "ParabolaPick.h"
class JointParabolaPick : public ParabolaPick {
public:
JointParabolaPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset,
float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar, bool scaleWithAvatar,
PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickParabola getMathematicalPick() const override;
bool isLeftHand() const override { return (_jointName == "_CONTROLLER_LEFTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); }
bool isRightHand() const override { return (_jointName == "_CONTROLLER_RIGHTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"); }
private:
std::string _jointName;
glm::vec3 _posOffset;
glm::vec3 _dirOffset;
};
#endif // hifi_JointParabolaPick_h

View file

@ -1,45 +0,0 @@
//
// JointRayPick.cpp
// interface/src/raypick
//
// Created by Sam Gondelman 7/11/2017
// 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 "JointRayPick.h"
#include "avatar/AvatarManager.h"
JointRayPick::JointRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const PickFilter& filter, float maxDistance, bool enabled) :
RayPick(filter, maxDistance, enabled),
_jointName(jointName),
_posOffset(posOffset),
_dirOffset(dirOffset)
{
}
PickRay JointRayPick::getMathematicalPick() const {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
int jointIndex = myAvatar->getJointIndex(QString::fromStdString(_jointName));
bool useAvatarHead = _jointName == "Avatar";
const int INVALID_JOINT = -1;
if (jointIndex != INVALID_JOINT || useAvatarHead) {
glm::vec3 jointPos = useAvatarHead ? myAvatar->getHeadPosition() : myAvatar->getAbsoluteJointTranslationInObjectFrame(jointIndex);
glm::quat jointRot = useAvatarHead ? myAvatar->getHeadOrientation() : myAvatar->getAbsoluteJointRotationInObjectFrame(jointIndex);
glm::vec3 avatarPos = myAvatar->getWorldPosition();
glm::quat avatarRot = myAvatar->getWorldOrientation();
glm::vec3 pos = useAvatarHead ? jointPos : avatarPos + (avatarRot * jointPos);
glm::quat rot = useAvatarHead ? jointRot * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT) : avatarRot * jointRot;
// Apply offset
pos = pos + (rot * (myAvatar->getSensorToWorldScale() * _posOffset));
glm::vec3 dir = glm::normalize(rot * glm::normalize(_dirOffset));
return PickRay(pos, dir);
}
return PickRay();
}

View file

@ -1,33 +0,0 @@
//
// JointRayPick.h
// interface/src/raypick
//
// Created by Sam Gondelman 7/11/2017
// 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
//
#ifndef hifi_JointRayPick_h
#define hifi_JointRayPick_h
#include "RayPick.h"
class JointRayPick : public RayPick {
public:
JointRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickRay getMathematicalPick() const override;
bool isLeftHand() const override { return (_jointName == "_CONTROLLER_LEFTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); }
bool isRightHand() const override { return (_jointName == "_CONTROLLER_RIGHTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"); }
private:
std::string _jointName;
glm::vec3 _posOffset;
glm::vec3 _dirOffset;
};
#endif // hifi_JointRayPick_h

View file

@ -14,13 +14,14 @@
#include "avatar/AvatarManager.h"
#include <DependencyManager.h>
#include "PickManager.h"
#include "RayPick.h"
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover,
const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalTime, bool centerEndY, bool lockEnd,
bool distanceScaleEnd, bool scaleWithAvatar, bool enabled) :
bool distanceScaleEnd, bool scaleWithParent, bool enabled) :
PathPointer(PickQuery::Ray, rayProps, renderStates, defaultRenderStates, hover, triggers, faceAvatar, followNormal, followNormalTime,
centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled)
centerEndY, lockEnd, distanceScaleEnd, scaleWithParent, enabled)
{
}
@ -146,9 +147,9 @@ void LaserPointer::RenderState::disable() {
}
}
void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) {
StartEndRenderState::update(origin, end, surfaceNormal, scaleWithAvatar, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult);
StartEndRenderState::update(origin, end, surfaceNormal, parentScale, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult);
QVariant endVariant = vec3toVariant(end);
if (!getPathID().isNull()) {
QVariantMap pathProps;
@ -156,9 +157,7 @@ void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3&
pathProps.insert("end", endVariant);
pathProps.insert("visible", true);
pathProps.insert("ignoreRayIntersection", doesPathIgnoreRays());
if (scaleWithAvatar) {
pathProps.insert("lineWidth", getLineWidth() * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale());
}
pathProps.insert("lineWidth", getLineWidth() * parentScale);
qApp->getOverlays().editOverlay(getPathID(), pathProps);
}
}

View file

@ -24,12 +24,12 @@ public:
const OverlayID& getPathID() const { return _pathID; }
const bool& doesPathIgnoreRays() const { return _pathIgnoreRays; }
void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; }
const float& getLineWidth() const { return _lineWidth; }
void setLineWidth(float width) { _lineWidth = width; }
float getLineWidth() const { return _lineWidth; }
void cleanup() override;
void disable() override;
void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) override;
private:
@ -40,7 +40,7 @@ public:
};
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers,
bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithParent, bool enabled);
QVariantMap toVariantMap() const override;

View file

@ -1,28 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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 "MouseParabolaPick.h"
#include "Application.h"
#include "display-plugins/CompositorHelper.h"
MouseParabolaPick::MouseParabolaPick(float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar,
bool scaleWithAvatar, const PickFilter& filter, float maxDistance, bool enabled) :
ParabolaPick(speed, accelerationAxis, rotateAccelerationWithAvatar, scaleWithAvatar, filter, maxDistance, enabled)
{
}
PickParabola MouseParabolaPick::getMathematicalPick() const {
QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition();
if (position.isValid()) {
QVariantMap posMap = position.toMap();
PickRay pickRay = qApp->getCamera().computePickRay(posMap["x"].toFloat(), posMap["y"].toFloat());
return PickParabola(pickRay.origin, getSpeed() * pickRay.direction, getAcceleration());
}
return PickParabola();
}

View file

@ -1,24 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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_MouseParabolaPick_h
#define hifi_MouseParabolaPick_h
#include "ParabolaPick.h"
class MouseParabolaPick : public ParabolaPick {
public:
MouseParabolaPick(float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar, bool scaleWithAvatar,
const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickParabola getMathematicalPick() const override;
bool isMouse() const override { return true; }
};
#endif // hifi_MouseParabolaPick_h

View file

@ -1,29 +0,0 @@
//
// MouseRayPick.cpp
// interface/src/raypick
//
// Created by Sam Gondelman 7/19/2017
// 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 "MouseRayPick.h"
#include "Application.h"
#include "display-plugins/CompositorHelper.h"
MouseRayPick::MouseRayPick(const PickFilter& filter, float maxDistance, bool enabled) :
RayPick(filter, maxDistance, enabled)
{
}
PickRay MouseRayPick::getMathematicalPick() const {
QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition();
if (position.isValid()) {
QVariantMap posMap = position.toMap();
return qApp->getCamera().computePickRay(posMap["x"].toFloat(), posMap["y"].toFloat());
}
return PickRay();
}

View file

@ -1,26 +0,0 @@
//
// MouseRayPick.h
// interface/src/raypick
//
// Created by Sam Gondelman 7/19/2017
// 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
//
#ifndef hifi_MouseRayPick_h
#define hifi_MouseRayPick_h
#include "RayPick.h"
class MouseRayPick : public RayPick {
public:
MouseRayPick(const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickRay getMathematicalPick() const override;
bool isMouse() const override { return true; }
};
#endif // hifi_MouseRayPick_h

View file

@ -15,6 +15,46 @@
#include "DependencyManager.h"
#include "PickManager.h"
ParabolaPick::ParabolaPick(const glm::vec3& position, const glm::vec3& direction, float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar, bool rotateAccelerationWithParent, bool scaleWithParent, const PickFilter& filter, float maxDistance, bool enabled) :
Pick(PickParabola(position, speed * direction, accelerationAxis), filter, maxDistance, enabled),
_rotateAccelerationWithAvatar(rotateAccelerationWithAvatar),
_rotateAccelerationWithParent(rotateAccelerationWithParent),
_scaleWithParent(scaleWithParent),
_speed(speed) {
}
PickParabola ParabolaPick::getMathematicalPick() const {
if (!parentTransform) {
PickParabola mathPick = _mathPick;
if (_rotateAccelerationWithAvatar) {
mathPick.acceleration = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * mathPick.acceleration;
}
return mathPick;
}
Transform currentParentTransform = parentTransform->getTransform();
glm::vec3 position = currentParentTransform.transform(_mathPick.origin);
glm::vec3 velocity = _mathPick.velocity;
if (_scaleWithParent) {
velocity = currentParentTransform.transformDirection(velocity);
} else {
glm::vec3 transformedVelocity = currentParentTransform.transformDirection(velocity);
velocity = glm::normalize(transformedVelocity) * _speed;
}
glm::vec3 acceleration = _mathPick.acceleration;
if (_scaleWithParent) {
acceleration *= currentParentTransform.getScale();
}
if (_rotateAccelerationWithAvatar) {
acceleration = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * acceleration;
} else if (_rotateAccelerationWithParent) {
acceleration = currentParentTransform.getRotation() * acceleration;
}
return PickParabola(position, velocity, acceleration);
}
PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) {
if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
@ -60,18 +100,6 @@ PickResultPointer ParabolaPick::getHUDIntersection(const PickParabola& pick) {
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
}
float ParabolaPick::getSpeed() const {
return (_scaleWithAvatar ? DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale() * _speed : _speed);
}
glm::vec3 ParabolaPick::getAcceleration() const {
float scale = (_scaleWithAvatar ? DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale() : 1.0f);
if (_rotateAccelerationWithAvatar) {
return scale * (DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * _accelerationAxis);
}
return scale * _accelerationAxis;
}
Transform ParabolaPick::getResultTransform() const {
PickResultPointer result = getPrevPickResult();
if (!result) {

View file

@ -74,9 +74,9 @@ public:
class ParabolaPick : public Pick<PickParabola> {
public:
ParabolaPick(float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar, bool scaleWithAvatar, const PickFilter& filter, float maxDistance, bool enabled) :
Pick(filter, maxDistance, enabled), _speed(speed), _accelerationAxis(accelerationAxis), _rotateAccelerationWithAvatar(rotateAccelerationWithAvatar),
_scaleWithAvatar(scaleWithAvatar) {}
ParabolaPick(const glm::vec3& position, const glm::vec3& direction, float speed, const glm::vec3& acceleration, bool rotateAccelerationWithAvatar, bool rotateAccelerationWithParent, bool scaleWithParent, const PickFilter& filter, float maxDistance, bool enabled);
PickParabola getMathematicalPick() const override;
PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { return std::make_shared<ParabolaPickResult>(pickVariant); }
PickResultPointer getEntityIntersection(const PickParabola& pick) override;
@ -86,13 +86,11 @@ public:
Transform getResultTransform() const override;
protected:
float _speed;
glm::vec3 _accelerationAxis;
bool _rotateAccelerationWithAvatar;
bool _scaleWithAvatar;
float getSpeed() const;
glm::vec3 getAcceleration() const;
bool _rotateAccelerationWithParent;
bool _scaleWithParent;
// Cached magnitude of _mathPick.velocity
float _speed;
};
#endif // hifi_ParabolaPick_h

View file

@ -204,9 +204,9 @@ void ParabolaPointer::RenderState::editParabola(const glm::vec3& color, float al
}
}
void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) {
StartEndRenderState::update(origin, end, surfaceNormal, scaleWithAvatar, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult);
StartEndRenderState::update(origin, end, surfaceNormal, parentScale, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult);
auto parabolaPickResult = std::static_pointer_cast<ParabolaPickResult>(pickResult);
if (parabolaPickResult && render::Item::isValidID(_pathID)) {
render::Transaction transaction;
@ -216,7 +216,7 @@ void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::ve
glm::vec3 velocity = parabola.velocity;
glm::vec3 acceleration = parabola.acceleration;
float parabolicDistance = distance > 0.0f ? distance : parabolaPickResult->parabolicDistance;
float width = scaleWithAvatar ? getPathWidth() * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale() : getPathWidth();
float width = getPathWidth() * parentScale;
transaction.updateItem<ParabolaRenderItem>(_pathID, [origin, velocity, acceleration, parabolicDistance, width](ParabolaRenderItem& item) {
item.setVisible(true);
item.setOrigin(origin);

View file

@ -79,7 +79,7 @@ public:
};
RenderState() {}
RenderState(const OverlayID& startID, const OverlayID& endID, const glm::vec3& pathColor, float pathAlpha, float pathWidth,
RenderState(const OverlayID& startID, const OverlayID& endID, const glm::vec3& pathColor, float pathAlpha, float parentScale,
bool isVisibleInSecondaryCamera, bool drawInFront, bool pathEnabled);
void setPathWidth(float width) { _pathWidth = width; }
@ -87,7 +87,7 @@ public:
void cleanup() override;
void disable() override;
void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) override;
void editParabola(const glm::vec3& color, float alpha, float width, bool isVisibleInSecondaryCamera, bool drawInFront, bool enabled);

View file

@ -17,7 +17,7 @@
PathPointer::PathPointer(PickQuery::PickType type, const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
bool hover, const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd,
bool distanceScaleEnd, bool scaleWithAvatar, bool enabled) :
bool distanceScaleEnd, bool scaleWithParent, bool enabled) :
Pointer(DependencyManager::get<PickScriptingInterface>()->createPick(type, rayProps), enabled, hover),
_renderStates(renderStates),
_defaultRenderStates(defaultRenderStates),
@ -28,7 +28,7 @@ PathPointer::PathPointer(PickQuery::PickType type, const QVariant& rayProps, con
_centerEndY(centerEndY),
_lockEnd(lockEnd),
_distanceScaleEnd(distanceScaleEnd),
_scaleWithAvatar(scaleWithAvatar)
_scaleWithParent(scaleWithParent)
{
for (auto& state : _renderStates) {
if (!enabled || state.first != _currentRenderState) {
@ -146,12 +146,18 @@ void PathPointer::updateVisuals(const PickResultPointer& pickResult) {
IntersectionType type = getPickedObjectType(pickResult);
auto renderState = _renderStates.find(_currentRenderState);
auto defaultRenderState = _defaultRenderStates.find(_currentRenderState);
float parentScale = 1.0f;
if (_enabled && _scaleWithParent) {
glm::vec3 dimensions = DependencyManager::get<PickManager>()->getParentTransform(_pickUID).getScale();
parentScale = glm::max(glm::max(dimensions.x, dimensions.y), dimensions.z);
}
if (_enabled && !_currentRenderState.empty() && renderState != _renderStates.end() &&
(type != IntersectionType::NONE || _pathLength > 0.0f)) {
glm::vec3 origin = getPickOrigin(pickResult);
glm::vec3 end = getPickEnd(pickResult, _pathLength);
glm::vec3 surfaceNormal = getPickedObjectNormal(pickResult);
renderState->second->update(origin, end, surfaceNormal, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, _faceAvatar,
renderState->second->update(origin, end, surfaceNormal, parentScale, _distanceScaleEnd, _centerEndY, _faceAvatar,
_followNormal, _followNormalStrength, _pathLength, pickResult);
if (defaultRenderState != _defaultRenderStates.end() && defaultRenderState->second.second->isEnabled()) {
defaultRenderState->second.second->disable();
@ -162,7 +168,7 @@ void PathPointer::updateVisuals(const PickResultPointer& pickResult) {
}
glm::vec3 origin = getPickOrigin(pickResult);
glm::vec3 end = getPickEnd(pickResult, defaultRenderState->second.first);
defaultRenderState->second.second->update(origin, end, Vectors::UP, _scaleWithAvatar, _distanceScaleEnd, _centerEndY,
defaultRenderState->second.second->update(origin, end, Vectors::UP, parentScale, _distanceScaleEnd, _centerEndY,
_faceAvatar, _followNormal, _followNormalStrength, defaultRenderState->second.first, pickResult);
} else if (!_currentRenderState.empty()) {
if (renderState != _renderStates.end() && renderState->second->isEnabled()) {
@ -281,15 +287,13 @@ void StartEndRenderState::disable() {
_enabled = false;
}
void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) {
if (!getStartID().isNull()) {
QVariantMap startProps;
startProps.insert("position", vec3toVariant(origin));
startProps.insert("visible", true);
if (scaleWithAvatar) {
startProps.insert("dimensions", vec3toVariant(getStartDim() * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale()));
}
startProps.insert("dimensions", vec3toVariant(getStartDim() * parentScale));
startProps.insert("ignoreRayIntersection", doesStartIgnoreRays());
qApp->getOverlays().editOverlay(getStartID(), startProps);
}
@ -300,8 +304,8 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end,
if (distanceScaleEnd) {
dim = getEndDim() * glm::distance(origin, end);
endProps.insert("dimensions", vec3toVariant(dim));
} else if (scaleWithAvatar) {
dim = getEndDim() * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale();
} else {
dim = getEndDim() * parentScale;
endProps.insert("dimensions", vec3toVariant(dim));
}

View file

@ -45,7 +45,7 @@ public:
virtual void cleanup();
virtual void disable();
virtual void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
virtual void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult);
bool isEnabled() const { return _enabled; }
@ -74,7 +74,7 @@ class PathPointer : public Pointer {
public:
PathPointer(PickQuery::PickType type, const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
bool hover, const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd,
bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
bool distanceScaleEnd, bool scaleWithParent, bool enabled);
virtual ~PathPointer();
void setRenderState(const std::string& state) override;
@ -98,7 +98,7 @@ protected:
bool _centerEndY;
bool _lockEnd;
bool _distanceScaleEnd;
bool _scaleWithAvatar;
bool _scaleWithParent;
LockEndObject _lockEndObject;
struct TriggerState {

View file

@ -14,13 +14,9 @@
#include "Application.h"
#include <PickManager.h>
#include "StaticRayPick.h"
#include "JointRayPick.h"
#include "MouseRayPick.h"
#include "RayPick.h"
#include "StylusPick.h"
#include "StaticParabolaPick.h"
#include "JointParabolaPick.h"
#include "MouseParabolaPick.h"
#include "ParabolaPick.h"
#include "CollisionPick.h"
#include "SpatialParentFinder.h"
@ -56,9 +52,9 @@ unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type,
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
* @property {string} [joint] Only for Joint or Mouse Ray Picks. If "Mouse", it will create a Ray Pick that follows the system mouse, in desktop or HMD.
* If "Avatar", it will create a Joint Ray Pick that follows your avatar's head. Otherwise, it will create a Joint Ray Pick that follows the given joint, if it
* exists on your current avatar.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick.
* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar.
* @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Ray Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral
* @property {Vec3} [dirOffset=Vec3.UP] Only for Joint Ray Picks. A local joint direction offset. x = upward, y = forward, z = lateral
* @property {Vec3} [position] Only for Static Ray Picks. The world-space origin of the ray.
@ -82,38 +78,29 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) {
maxDistance = propMap["maxDistance"].toFloat();
}
if (propMap["joint"].isValid()) {
std::string jointName = propMap["joint"].toString().toStdString();
if (jointName != "Mouse") {
// x = upward, y = forward, z = lateral
glm::vec3 posOffset = Vectors::ZERO;
if (propMap["posOffset"].isValid()) {
posOffset = vec3FromVariant(propMap["posOffset"]);
}
glm::vec3 dirOffset = Vectors::UP;
if (propMap["dirOffset"].isValid()) {
dirOffset = vec3FromVariant(propMap["dirOffset"]);
}
return DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, std::make_shared<JointRayPick>(jointName, posOffset, dirOffset, filter, maxDistance, enabled));
} else {
return DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, std::make_shared<MouseRayPick>(filter, maxDistance, enabled));
}
} else if (propMap["position"].isValid()) {
glm::vec3 position = vec3FromVariant(propMap["position"]);
glm::vec3 direction = -Vectors::UP;
if (propMap["direction"].isValid()) {
direction = vec3FromVariant(propMap["direction"]);
}
return DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, std::make_shared<StaticRayPick>(position, direction, filter, maxDistance, enabled));
glm::vec3 position = Vectors::ZERO;
if (propMap["position"].isValid()) {
position = vec3FromVariant(propMap["position"]);
} else if (propMap["posOffset"].isValid()) {
position = vec3FromVariant(propMap["posOffset"]);
}
return PickManager::INVALID_PICK_ID;
// direction has two defaults to ensure compatibility with older scripts
// Joint ray picks had default direction = Vec3.UP
// Static ray picks had default direction = -Vec3.UP
glm::vec3 direction = propMap["joint"].isValid() ? Vectors::UP : -Vectors::UP;
if (propMap["orientation"].isValid()) {
direction = quatFromVariant(propMap["orientation"]) * Vectors::UP;
} else if (propMap["direction"].isValid()) {
direction = vec3FromVariant(propMap["direction"]);
} else if (propMap["dirOffset"].isValid()) {
direction = vec3FromVariant(propMap["dirOffset"]);
}
auto rayPick = std::make_shared<RayPick>(position, direction, filter, maxDistance, enabled);
setParentTransform(rayPick, propMap);
return DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, rayPick);
}
/**jsdoc
@ -153,23 +140,25 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties
return DependencyManager::get<PickManager>()->addPick(PickQuery::Stylus, std::make_shared<StylusPick>(side, filter, maxDistance, enabled));
}
// NOTE: Laser pointer still uses scaleWithAvatar. Until scaleWithAvatar is also deprecated for pointers, scaleWithAvatar should not be removed from the pick API.
/**jsdoc
* A set of properties that can be passed to {@link Picks.createPick} to create a new Parabola Pick.
* @typedef {object} Picks.ParabolaPickProperties
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
* @property {string} [joint] Only for Joint or Mouse Parabola Picks. If "Mouse", it will create a Parabola Pick that follows the system mouse, in desktop or HMD.
* If "Avatar", it will create a Joint Parabola Pick that follows your avatar's head. Otherwise, it will create a Joint Parabola Pick that follows the given joint, if it
* exists on your current avatar.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick.
* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar.
* @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Parabola Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral
* @property {Vec3} [dirOffset=Vec3.UP] Only for Joint Parabola Picks. A local joint direction offset. x = upward, y = forward, z = lateral
* @property {Vec3} [position] Only for Static Parabola Picks. The world-space origin of the parabola segment.
* @property {Vec3} [direction=-Vec3.FRONT] Only for Static Parabola Picks. The world-space direction of the parabola segment.
* @property {number} [speed=1] The initial speed of the parabola, i.e. the initial speed of the projectile whose trajectory defines the parabola.
* @property {Vec3} [accelerationAxis=-Vec3.UP] The acceleration of the parabola, i.e. the acceleration of the projectile whose trajectory defines the parabola, both magnitude and direction.
* @property {boolean} [rotateAccelerationWithAvatar=true] Whether or not the acceleration axis should rotate with your avatar's local Y axis.
* @property {boolean} [scaleWithAvatar=false] If true, the velocity and acceleration of the Pick will scale linearly with your avatar.
* @property {boolean} [rotateAccelerationWithAvatar=true] Whether or not the acceleration axis should rotate with the avatar's local Y axis.
* @property {boolean} [rotateAccelerationWithParent=false] Whether or not the acceleration axis should rotate with the parent's local Y axis, if available.
* @property {boolean} [scaleWithParent=true] If true, the velocity and acceleration of the Pick will scale linearly with the parent, if available. scaleWithAvatar is an alias but is deprecated.
*/
unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properties) {
QVariantMap propMap = properties.toMap();
@ -204,48 +193,37 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti
rotateAccelerationWithAvatar = propMap["rotateAccelerationWithAvatar"].toBool();
}
bool scaleWithAvatar = false;
if (propMap["scaleWithAvatar"].isValid()) {
scaleWithAvatar = propMap["scaleWithAvatar"].toBool();
bool rotateAccelerationWithParent = false;
if (propMap["rotateAccelerationWithParent"].isValid()) {
rotateAccelerationWithParent = propMap["rotateAccelerationWithParent"].toBool();
}
if (propMap["joint"].isValid()) {
std::string jointName = propMap["joint"].toString().toStdString();
if (jointName != "Mouse") {
// x = upward, y = forward, z = lateral
glm::vec3 posOffset = Vectors::ZERO;
if (propMap["posOffset"].isValid()) {
posOffset = vec3FromVariant(propMap["posOffset"]);
}
glm::vec3 dirOffset = Vectors::UP;
if (propMap["dirOffset"].isValid()) {
dirOffset = vec3FromVariant(propMap["dirOffset"]);
}
return DependencyManager::get<PickManager>()->addPick(PickQuery::Parabola, std::make_shared<JointParabolaPick>(jointName, posOffset, dirOffset,
speed, accelerationAxis, rotateAccelerationWithAvatar,
scaleWithAvatar, filter, maxDistance, enabled));
} else {
return DependencyManager::get<PickManager>()->addPick(PickQuery::Parabola, std::make_shared<MouseParabolaPick>(speed, accelerationAxis, rotateAccelerationWithAvatar,
scaleWithAvatar, filter, maxDistance, enabled));
}
} else if (propMap["position"].isValid()) {
glm::vec3 position = vec3FromVariant(propMap["position"]);
glm::vec3 direction = -Vectors::FRONT;
if (propMap["direction"].isValid()) {
direction = vec3FromVariant(propMap["direction"]);
}
return DependencyManager::get<PickManager>()->addPick(PickQuery::Parabola, std::make_shared<StaticParabolaPick>(position, direction, speed, accelerationAxis,
rotateAccelerationWithAvatar, scaleWithAvatar,
filter, maxDistance, enabled));
bool scaleWithParent = true;
if (propMap["scaleWithParent"].isValid()) {
scaleWithParent = propMap["scaleWithParent"].toBool();
} else if (propMap["scaleWithAvatar"].isValid()) {
scaleWithParent = propMap["scaleWithAvatar"].toBool();
}
return PickManager::INVALID_PICK_ID;
glm::vec3 position = Vectors::ZERO;
glm::vec3 direction = propMap["joint"].isValid() ? Vectors::UP : -Vectors::FRONT;
if (propMap["position"].isValid()) {
position = vec3FromVariant(propMap["position"]);
} else if (propMap["posOffset"].isValid()) {
position = vec3FromVariant(propMap["posOffset"]);
}
if (propMap["orientation"].isValid()) {
direction = quatFromVariant(propMap["orientation"]) * Vectors::UP;
} else if (propMap["direction"].isValid()) {
direction = vec3FromVariant(propMap["direction"]);
} else if (propMap["dirOffset"].isValid()) {
direction = vec3FromVariant(propMap["dirOffset"]);
}
auto parabolaPick = std::make_shared<ParabolaPick>(position, direction, speed, accelerationAxis,
rotateAccelerationWithAvatar, rotateAccelerationWithParent, scaleWithParent, filter, maxDistance, enabled);
setParentTransform(parabolaPick, propMap);
return DependencyManager::get<PickManager>()->addPick(PickQuery::Parabola, parabolaPick);
}
/**jsdoc
@ -272,9 +250,10 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti
* The depth is measured in world space, but will scale with the parent if defined.
* @property {CollisionMask} [collisionGroup=8] - The type of object this collision pick collides as. Objects whose collision masks overlap with the pick's collision group
* will be considered colliding with the pick.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay.
* @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick.
* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar.
* @property {boolean} [scaleWithParent=true] If true, the collision pick's dimensions and threshold will adjust according to the scale of the parent.
*/
unsigned int PickScriptingInterface::createCollisionPick(const QVariant& properties) {
QVariantMap propMap = properties.toMap();
@ -294,9 +273,14 @@ unsigned int PickScriptingInterface::createCollisionPick(const QVariant& propert
maxDistance = propMap["maxDistance"].toFloat();
}
bool scaleWithParent = true;
if (propMap["scaleWithParent"].isValid()) {
scaleWithParent = propMap["scaleWithParent"].toBool();
}
CollisionRegion collisionRegion(propMap);
auto collisionPick = std::make_shared<CollisionPick>(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine());
collisionPick->parentTransform = createTransformNode(propMap);
auto collisionPick = std::make_shared<CollisionPick>(filter, maxDistance, enabled, scaleWithParent, collisionRegion, qApp->getPhysicsEngine());
setParentTransform(collisionPick, propMap);
return DependencyManager::get<PickManager>()->addPick(PickQuery::Collision, collisionPick);
}
@ -373,51 +357,63 @@ void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) {
DependencyManager::get<PickManager>()->setPerFrameTimeBudget(numUsecs);
}
std::shared_ptr<TransformNode> PickScriptingInterface::createTransformNode(const QVariantMap& propMap) {
if (propMap["parentID"].isValid()) {
QUuid parentUuid = propMap["parentID"].toUuid();
if (!parentUuid.isNull()) {
// Infer object type from parentID
// For now, assume a QUuuid is a SpatiallyNestable. This should change when picks are converted over to QUuids.
bool success;
std::weak_ptr<SpatiallyNestable> nestablePointer = DependencyManager::get<SpatialParentFinder>()->find(parentUuid, success, nullptr);
int parentJointIndex = 0;
if (propMap["parentJointIndex"].isValid()) {
parentJointIndex = propMap["parentJointIndex"].toInt();
}
auto sharedNestablePointer = nestablePointer.lock();
if (success && sharedNestablePointer) {
NestableType nestableType = sharedNestablePointer->getNestableType();
if (nestableType == NestableType::Avatar) {
return std::make_shared<AvatarTransformNode>(std::static_pointer_cast<Avatar>(sharedNestablePointer), parentJointIndex);
} else if (nestableType == NestableType::Overlay) {
return std::make_shared<OverlayTransformNode>(std::static_pointer_cast<Base3DOverlay>(sharedNestablePointer), parentJointIndex);
} else if (nestableType == NestableType::Entity) {
return std::make_shared<EntityTransformNode>(std::static_pointer_cast<EntityItem>(sharedNestablePointer), parentJointIndex);
} else {
return std::make_shared<NestableTransformNode>(nestablePointer, parentJointIndex);
}
}
}
void PickScriptingInterface::setParentTransform(std::shared_ptr<PickQuery> pick, const QVariantMap& propMap) {
QUuid parentUuid;
int parentJointIndex = 0;
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
unsigned int pickID = propMap["parentID"].toUInt();
if (pickID != 0) {
return std::make_shared<PickTransformNode>(pickID);
if (propMap["parentID"].isValid()) {
parentUuid = propMap["parentID"].toUuid();
if (propMap["parentJointIndex"].isValid()) {
parentJointIndex = propMap["parentJointIndex"].toInt();
}
}
if (propMap["joint"].isValid()) {
} else if (propMap["joint"].isValid()) {
QString joint = propMap["joint"].toString();
if (joint == "Mouse") {
return std::make_shared<MouseTransformNode>();
pick->parentTransform = std::make_shared<MouseTransformNode>();
pick->setJointState(PickQuery::JOINT_STATE_MOUSE);
return;
} else if (joint == "Avatar") {
return std::make_shared<MyAvatarHeadTransformNode>();
} else if (!joint.isNull()) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
int jointIndex = myAvatar->getJointIndex(joint);
return std::make_shared<AvatarTransformNode>(myAvatar, jointIndex);
pick->parentTransform = std::make_shared<MyAvatarHeadTransformNode>();
return;
} else {
parentUuid = myAvatar->getSessionUUID();
parentJointIndex = myAvatar->getJointIndex(joint);
}
}
return std::shared_ptr<TransformNode>();
if (parentUuid == myAvatar->getSessionUUID()) {
if (parentJointIndex == CONTROLLER_LEFTHAND_INDEX || parentJointIndex == CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX) {
pick->setJointState(PickQuery::JOINT_STATE_LEFT_HAND);
} else if (parentJointIndex == CONTROLLER_RIGHTHAND_INDEX || parentJointIndex == CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX) {
pick->setJointState(PickQuery::JOINT_STATE_RIGHT_HAND);
}
pick->parentTransform = std::make_shared<AvatarTransformNode>(myAvatar, parentJointIndex);
} else if (!parentUuid.isNull()) {
// Infer object type from parentID
// For now, assume a QUuid is a SpatiallyNestable. This should change when picks are converted over to QUuids.
bool success;
std::weak_ptr<SpatiallyNestable> nestablePointer = DependencyManager::get<SpatialParentFinder>()->find(parentUuid, success, nullptr);
auto sharedNestablePointer = nestablePointer.lock();
if (success && sharedNestablePointer) {
NestableType nestableType = sharedNestablePointer->getNestableType();
if (nestableType == NestableType::Avatar) {
pick->parentTransform = std::make_shared<AvatarTransformNode>(std::static_pointer_cast<Avatar>(sharedNestablePointer), parentJointIndex);
} else if (nestableType == NestableType::Overlay) {
pick->parentTransform = std::make_shared<OverlayTransformNode>(std::static_pointer_cast<Base3DOverlay>(sharedNestablePointer), parentJointIndex);
} else if (nestableType == NestableType::Entity) {
pick->parentTransform = std::make_shared<EntityTransformNode>(std::static_pointer_cast<EntityItem>(sharedNestablePointer), parentJointIndex);
} else {
pick->parentTransform = std::make_shared<NestableTransformNode>(nestablePointer, parentJointIndex);
}
}
} else {
unsigned int pickID = propMap["parentID"].toUInt();
if (pickID != 0) {
pick->parentTransform = std::make_shared<PickTransformNode>(pickID);
}
}
}

View file

@ -320,7 +320,7 @@ public slots:
static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; }
protected:
static std::shared_ptr<TransformNode> createTransformNode(const QVariantMap& propMap);
static void setParentTransform(std::shared_ptr<PickQuery> pick, const QVariantMap& propMap);
};
#endif // hifi_PickScriptingInterface_h

View file

@ -97,7 +97,7 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
* @property {boolean} [centerEndY=true] If false, the end of the Pointer will be moved up by half of its height.
* @property {boolean} [lockEnd=false] If true, the end of the Pointer will lock on to the center of the object at which the pointer is pointing.
* @property {boolean} [distanceScaleEnd=false] If true, the dimensions of the end of the Pointer will scale linearly with distance.
* @property {boolean} [scaleWithAvatar=false] If true, the width of the Pointer's path will scale linearly with your avatar's scale.
* @property {boolean} [scaleWithParent=false] If true, the width of the Pointer's path will scale linearly with the pick parent's scale. scaleWithAvatar is an alias but is deprecated.
* @property {boolean} [followNormal=false] If true, the end of the Pointer will rotate to follow the normal of the intersected surface.
* @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. <code>0-1</code>. If 0 or 1,
* the normal will follow exactly.
@ -134,9 +134,11 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
distanceScaleEnd = propertyMap["distanceScaleEnd"].toBool();
}
bool scaleWithAvatar = false;
if (propertyMap["scaleWithAvatar"].isValid()) {
scaleWithAvatar = propertyMap["scaleWithAvatar"].toBool();
bool scaleWithParent = false;
if (propertyMap["scaleWithParent"].isValid()) {
scaleWithParent = propertyMap["scaleWithParent"].toBool();
} else if (propertyMap["scaleWithAvatar"].isValid()) {
scaleWithParent = propertyMap["scaleWithAvatar"].toBool();
}
bool followNormal = false;
@ -207,7 +209,7 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<LaserPointer>(properties, renderStates, defaultRenderStates, hover, triggers,
faceAvatar, followNormal, followNormalStrength, centerEndY, lockEnd,
distanceScaleEnd, scaleWithAvatar, enabled));
distanceScaleEnd, scaleWithParent, enabled));
}
/**jsdoc
@ -249,7 +251,7 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
* @property {boolean} [centerEndY=true] If false, the end of the Pointer will be moved up by half of its height.
* @property {boolean} [lockEnd=false] If true, the end of the Pointer will lock on to the center of the object at which the pointer is pointing.
* @property {boolean} [distanceScaleEnd=false] If true, the dimensions of the end of the Pointer will scale linearly with distance.
* @property {boolean} [scaleWithAvatar=false] If true, the width of the Pointer's path will scale linearly with your avatar's scale.
* @property {boolean} [scaleWithParent=true] If true, the width of the Pointer's path will scale linearly with the pick parent's scale. scaleWithAvatar is an alias but is deprecated.
* @property {boolean} [followNormal=false] If true, the end of the Pointer will rotate to follow the normal of the intersected surface.
* @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. <code>0-1</code>. If 0 or 1,
* the normal will follow exactly.
@ -286,9 +288,11 @@ unsigned int PointerScriptingInterface::createParabolaPointer(const QVariant& pr
distanceScaleEnd = propertyMap["distanceScaleEnd"].toBool();
}
bool scaleWithAvatar = false;
if (propertyMap["scaleWithAvatar"].isValid()) {
scaleWithAvatar = propertyMap["scaleWithAvatar"].toBool();
bool scaleWithParent = true;
if (propertyMap["scaleWithParent"].isValid()) {
scaleWithParent = propertyMap["scaleWithParent"].toBool();
} else if (propertyMap["scaleWithAvatar"].isValid()) {
scaleWithParent = propertyMap["scaleWithAvatar"].toBool();
}
bool followNormal = false;
@ -359,7 +363,7 @@ unsigned int PointerScriptingInterface::createParabolaPointer(const QVariant& pr
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<ParabolaPointer>(properties, renderStates, defaultRenderStates, hover, triggers,
faceAvatar, followNormal, followNormalStrength, centerEndY, lockEnd, distanceScaleEnd,
scaleWithAvatar, enabled));
scaleWithParent, enabled));
}
void PointerScriptingInterface::editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const {

View file

@ -15,6 +15,17 @@
#include "DependencyManager.h"
#include "PickManager.h"
PickRay RayPick::getMathematicalPick() const {
if (!parentTransform) {
return _mathPick;
}
Transform currentParentTransform = parentTransform->getTransform();
glm::vec3 origin = currentParentTransform.transform(_mathPick.origin);
glm::vec3 direction = glm::normalize(currentParentTransform.transformDirection(_mathPick.direction));
return PickRay(origin, direction);
}
PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
RayToEntityIntersectionResult entityRes =

View file

@ -70,7 +70,11 @@ public:
class RayPick : public Pick<PickRay> {
public:
RayPick(const PickFilter& filter, float maxDistance, bool enabled) : Pick(filter, maxDistance, enabled) {}
RayPick(glm::vec3 position, glm::vec3 direction, const PickFilter& filter, float maxDistance, bool enabled) :
Pick(PickRay(position, direction), filter, maxDistance, enabled) {
}
PickRay getMathematicalPick() const override;
PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { return std::make_shared<RayPickResult>(pickVariant); }
PickResultPointer getEntityIntersection(const PickRay& pick) override;

View file

@ -16,10 +16,6 @@
#include <PickManager.h>
#include "StaticRayPick.h"
#include "JointRayPick.h"
#include "MouseRayPick.h"
unsigned int RayPickScriptingInterface::createRayPick(const QVariant& properties) {
return DependencyManager::get<PickScriptingInterface>()->createRayPick(properties);
}

View file

@ -1,19 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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 "StaticParabolaPick.h"
StaticParabolaPick::StaticParabolaPick(const glm::vec3& position, const glm::vec3& direction, float speed, const glm::vec3& accelerationAxis,
bool scaleWithAvatar, bool rotateAccelerationWithAvatar, const PickFilter& filter, float maxDistance, bool enabled) :
ParabolaPick(speed, accelerationAxis, rotateAccelerationWithAvatar, scaleWithAvatar, filter, maxDistance, enabled),
_position(position), _velocity(direction)
{
}
PickParabola StaticParabolaPick::getMathematicalPick() const {
return PickParabola(_position, getSpeed() * _velocity, getAcceleration());
}

View file

@ -1,26 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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_StaticParabolaPick_h
#define hifi_StaticParabolaPick_h
#include "ParabolaPick.h"
class StaticParabolaPick : public ParabolaPick {
public:
StaticParabolaPick(const glm::vec3& position, const glm::vec3& direction, float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar,
bool scaleWithAvatar, const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickParabola getMathematicalPick() const override;
private:
glm::vec3 _position;
glm::vec3 _velocity;
};
#endif // hifi_StaticParabolaPick_h

View file

@ -1,18 +0,0 @@
//
// Created by Sam Gondelman 7/11/2017
// 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 "StaticRayPick.h"
StaticRayPick::StaticRayPick(const glm::vec3& position, const glm::vec3& direction, const PickFilter& filter, float maxDistance, bool enabled) :
RayPick(filter, maxDistance, enabled),
_pickRay(position, direction)
{
}
PickRay StaticRayPick::getMathematicalPick() const {
return _pickRay;
}

View file

@ -1,25 +0,0 @@
//
// Created by Sam Gondelman 7/11/2017
// 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
//
#ifndef hifi_StaticRayPick_h
#define hifi_StaticRayPick_h
#include "RayPick.h"
class StaticRayPick : public RayPick {
public:
StaticRayPick(const glm::vec3& position, const glm::vec3& direction, const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickRay getMathematicalPick() const override;
private:
PickRay _pickRay;
};
#endif // hifi_StaticRayPick_h

View file

@ -65,8 +65,7 @@ bool StylusPickResult::checkOrFilterAgainstMaxDistance(float maxDistance) {
}
StylusPick::StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled) :
Pick(filter, maxDistance, enabled),
_side(side)
Pick(StylusTip(side), filter, maxDistance, enabled)
{
}
@ -130,9 +129,9 @@ static StylusTip getControllerWorldLocation(Side side) {
StylusTip StylusPick::getMathematicalPick() const {
StylusTip result;
if (qApp->getPreferAvatarFingerOverStylus()) {
result = getFingerWorldLocation(_side);
result = getFingerWorldLocation(_mathPick.side);
} else {
result = getControllerWorldLocation(_side);
result = getControllerWorldLocation(_mathPick.side);
}
return result;
}

View file

@ -68,11 +68,9 @@ public:
PickResultPointer getHUDIntersection(const StylusTip& pick) override;
Transform getResultTransform() const override;
bool isLeftHand() const override { return _side == Side::Left; }
bool isRightHand() const override { return _side == Side::Right; }
private:
const Side _side;
bool isLeftHand() const override { return _mathPick.side == Side::Left; }
bool isRightHand() const override { return _mathPick.side == Side::Right; }
bool isMouse() const override { return false; }
};
#endif // hifi_StylusPick_h

View file

@ -39,6 +39,7 @@ WindowScriptingInterface::WindowScriptingInterface() {
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &WindowScriptingInterface::disconnectedFromDomain);
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
connect(&domainHandler, &DomainHandler::redirectErrorStateChanged, this, &WindowScriptingInterface::redirectErrorStateChanged);
connect(qApp, &Application::svoImportRequested, [this](const QString& urlString) {
static const QMetaMethod svoImportRequestedSignal =

View file

@ -611,6 +611,14 @@ signals:
*/
void domainConnectionRefused(const QString& reasonMessage, int reasonCode, const QString& extraInfo);
/**jsdoc
* Triggered when you try to visit a domain but are redirected into the error state.
* @function Window.redirectErrorStateChanged
* @param {boolean} isInErrorState - If <code>true</code>, the user has been redirected to the error URL.
* @returns {Signal}
*/
void redirectErrorStateChanged(bool isInErrorState);
/**jsdoc
* Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot} with
* <code>includeAnimated = false</code> or {@link Window.takeSecondaryCameraSnapshot|takeSecondaryCameraSnapshot}.

View file

@ -118,14 +118,24 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av
float oneFrameDistance = glm::length(currentPosition - _lastPosition);
const float MAX_TRANSIT_DISTANCE = 30.0f;
float scaledMaxTransitDistance = MAX_TRANSIT_DISTANCE * _scale;
if (oneFrameDistance > config._triggerDistance && oneFrameDistance < scaledMaxTransitDistance && !_isTransiting) {
start(deltaTime, _lastPosition, currentPosition, config);
if (oneFrameDistance > config._triggerDistance && !_isTransiting) {
if (oneFrameDistance < scaledMaxTransitDistance) {
start(deltaTime, _lastPosition, currentPosition, config);
} else {
_lastPosition = currentPosition;
return Status::ABORT_TRANSIT;
}
}
_lastPosition = currentPosition;
_status = updatePosition(deltaTime);
return _status;
}
void AvatarTransit::reset() {
_lastPosition = _endPosition;
_currentPosition = _endPosition;
_isTransiting = false;
}
void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const AvatarTransit::TransitConfig& config) {
_startPosition = startPosition;
_endPosition = endPosition;
@ -510,9 +520,13 @@ void Avatar::relayJointDataToChildren() {
modelEntity->setLocalJointTranslation(jointIndex, jointTranslation);
}
}
Transform finalTransform;
Transform avatarTransform = _skeletonModel->getTransform();
avatarTransform.setScale(_skeletonModel->getScale());
modelEntity->setOverrideTransform(avatarTransform, _skeletonModel->getOffset());
Transform entityTransform = modelEntity->getLocalTransform();
Transform::mult(finalTransform, avatarTransform, entityTransform);
finalTransform.setScale(_skeletonModel->getScale());
modelEntity->setOverrideTransform(finalTransform, _skeletonModel->getOffset());
modelEntity->simulateRelayedJoints();
}
}

View file

@ -56,7 +56,8 @@ public:
IDLE = 0,
START_TRANSIT,
TRANSITING,
END_TRANSIT
END_TRANSIT,
ABORT_TRANSIT
};
enum EaseType {
@ -84,6 +85,7 @@ public:
glm::vec3 getEndPosition() { return _endPosition; }
float getTransitTime() { return _totalTime; }
void setScale(float scale) { _scale = scale; }
void reset();
private:
Status updatePosition(float deltaTime);

View file

@ -206,11 +206,13 @@ float AvatarData::getDistanceBasedMinRotationDOT(glm::vec3 viewerPosition) const
if (distance < AVATAR_DISTANCE_LEVEL_1) {
result = AVATAR_MIN_ROTATION_DOT;
} else if (distance < AVATAR_DISTANCE_LEVEL_2) {
result = ROTATION_CHANGE_15D;
result = ROTATION_CHANGE_2D;
} else if (distance < AVATAR_DISTANCE_LEVEL_3) {
result = ROTATION_CHANGE_45D;
result = ROTATION_CHANGE_4D;
} else if (distance < AVATAR_DISTANCE_LEVEL_4) {
result = ROTATION_CHANGE_90D;
result = ROTATION_CHANGE_6D;
} else if (distance < AVATAR_DISTANCE_LEVEL_5) {
result = ROTATION_CHANGE_15D;
}
return result;
}
@ -594,7 +596,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
// The dot product for larger rotations is a lower number.
// So if the dot() is less than the value, then the rotation is a larger angle of rotation
if (sendAll || last.rotationIsDefaultPose || (!cullSmallChanges && last.rotation != data.rotation)
|| (cullSmallChanges && glm::dot(last.rotation, data.rotation) < minRotationDOT) ) {
|| (cullSmallChanges && fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT) ) {
validity |= (1 << validityBit);
#ifdef WANT_DEBUG
rotationSentCount++;
@ -1802,15 +1804,24 @@ QUrl AvatarData::getWireSafeSkeletonModelURL() const {
qint64 AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination,
AvatarTraits::TraitVersion traitVersion) {
qint64 bytesWritten = 0;
bytesWritten += destination.writePrimitive(traitType);
if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) {
bytesWritten += destination.writePrimitive(traitVersion);
}
qint64 bytesWritten = 0;
if (traitType == AvatarTraits::SkeletonModelURL) {
QByteArray encodedSkeletonURL = getWireSafeSkeletonModelURL().toEncoded();
if (encodedSkeletonURL.size() > AvatarTraits::MAXIMUM_TRAIT_SIZE) {
qWarning() << "Refusing to pack simple trait" << traitType << "of size" << encodedSkeletonURL.size()
<< "bytes since it exceeds the maximum size" << AvatarTraits::MAXIMUM_TRAIT_SIZE << "bytes";
return 0;
}
bytesWritten += destination.writePrimitive(traitType);
if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) {
bytesWritten += destination.writePrimitive(traitVersion);
}
AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size();
bytesWritten += destination.writePrimitive(encodedURLSize);
@ -1825,14 +1836,6 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) {
qint64 bytesWritten = 0;
bytesWritten += destination.writePrimitive(traitType);
if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) {
bytesWritten += destination.writePrimitive(traitVersion);
}
bytesWritten += destination.write(traitInstanceID.toRfc4122());
if (traitType == AvatarTraits::AvatarEntity) {
// grab a read lock on the avatar entities and check for entity data for the given ID
QByteArray entityBinaryData;
@ -1843,6 +1846,20 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr
}
});
if (entityBinaryData.size() > AvatarTraits::MAXIMUM_TRAIT_SIZE) {
qWarning() << "Refusing to pack instanced trait" << traitType << "of size" << entityBinaryData.size()
<< "bytes since it exceeds the maximum size " << AvatarTraits::MAXIMUM_TRAIT_SIZE << "bytes";
return 0;
}
bytesWritten += destination.writePrimitive(traitType);
if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) {
bytesWritten += destination.writePrimitive(traitVersion);
}
bytesWritten += destination.write(traitInstanceID.toRfc4122());
if (!entityBinaryData.isNull()) {
AvatarTraits::TraitWireSize entityBinarySize = entityBinaryData.size();

View file

@ -309,16 +309,19 @@ const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02f;
const float AVATAR_MIN_ROTATION_DOT = 0.9999999f;
const float AVATAR_MIN_TRANSLATION = 0.0001f;
const float ROTATION_CHANGE_15D = 0.9914449f;
const float ROTATION_CHANGE_45D = 0.9238795f;
const float ROTATION_CHANGE_90D = 0.7071068f;
const float ROTATION_CHANGE_179D = 0.0087266f;
const float AVATAR_DISTANCE_LEVEL_1 = 10.0f;
const float AVATAR_DISTANCE_LEVEL_2 = 100.0f;
const float AVATAR_DISTANCE_LEVEL_3 = 1000.0f;
const float AVATAR_DISTANCE_LEVEL_4 = 10000.0f;
// quaternion dot products
const float ROTATION_CHANGE_2D = 0.99984770f; // 2 degrees
const float ROTATION_CHANGE_4D = 0.99939083f; // 4 degrees
const float ROTATION_CHANGE_6D = 0.99862953f; // 6 degrees
const float ROTATION_CHANGE_15D = 0.99144486f; // 15 degrees
const float ROTATION_CHANGE_179D = 0.00872653f; // 179 degrees
// rotation culling distance thresholds
const float AVATAR_DISTANCE_LEVEL_1 = 12.5f; // meters
const float AVATAR_DISTANCE_LEVEL_2 = 16.6f; // meters
const float AVATAR_DISTANCE_LEVEL_3 = 25.0f; // meters
const float AVATAR_DISTANCE_LEVEL_4 = 50.0f; // meters
const float AVATAR_DISTANCE_LEVEL_5 = 200.0f; // meters
// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found).
// This is the start location in the Sandbox (xyz: 6270, 211, 6000).
@ -1190,6 +1193,9 @@ public:
void setReplicaIndex(int replicaIndex) { _replicaIndex = replicaIndex; }
int getReplicaIndex() { return _replicaIndex; }
void setIsNewAvatar(bool isNewAvatar) { _isNewAvatar = isNewAvatar; }
bool getIsNewAvatar() { return _isNewAvatar; }
signals:
/**jsdoc
@ -1452,6 +1458,7 @@ protected:
bool _hasProcessedFirstIdentity { false };
float _density;
int _replicaIndex { 0 };
bool _isNewAvatar { true };
// null unless MyAvatar or ScriptableAvatar sending traits data to mixer
std::unique_ptr<ClientTraitsHandler> _clientTraitsHandler;

View file

@ -259,18 +259,20 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer<ReceivedMessag
if (isNewAvatar) {
QWriteLocker locker(&_hashLock);
_pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar });
avatar->setIsNewAvatar(true);
auto replicaIDs = _replicas.getReplicaIDs(sessionUUID);
for (auto replicaID : replicaIDs) {
auto replicaAvatar = addAvatar(replicaID, sendingNode);
replicaAvatar->setIsNewAvatar(true);
_replicas.addReplica(sessionUUID, replicaAvatar);
}
}
// have the matching (or new) avatar parse the data from the packet
int bytesRead = avatar->parseDataFromBuffer(byteArray);
message->seek(positionBeforeRead + bytesRead);
_replicas.parseDataFromBuffer(sessionUUID, byteArray);
return avatar;
} else {
@ -337,7 +339,7 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
// grab the last trait versions for this avatar
auto& lastProcessedVersions = _processedTraitVersions[avatarID];
while (traitType != AvatarTraits::NullTrait) {
while (traitType != AvatarTraits::NullTrait && message->getBytesLeftToRead() > 0) {
AvatarTraits::TraitVersion packetTraitVersion;
message->readPrimitive(&packetTraitVersion);
@ -378,7 +380,7 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
}
}
if (skipBinaryTrait) {
if (skipBinaryTrait && traitBinarySize > 0) {
// we didn't read this trait because it was older or because we didn't have an avatar to process it for
message->seek(message->getPosition() + traitBinarySize);
}

View file

@ -39,6 +39,7 @@ namespace AvatarTraits {
using TraitWireSize = int16_t;
const TraitWireSize DELETED_TRAIT_SIZE = -1;
const TraitWireSize MAXIMUM_TRAIT_SIZE = INT16_MAX;
inline qint64 packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination,
TraitVersion traitVersion = NULL_TRAIT_VERSION) {

View file

@ -310,10 +310,10 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
}
auto entityID = entity->getEntityItemID();
processedIds.insert(entityID);
auto renderable = EntityRenderer::addToScene(*this, entity, scene, transaction);
if (renderable) {
_entitiesInScene.insert({ entityID, renderable });
processedIds.insert(entityID);
}
}

View file

@ -315,6 +315,14 @@ void RenderableModelEntityItem::getCollisionGeometryResource() {
_compoundShapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
}
bool RenderableModelEntityItem::computeShapeFailedToLoad() {
if (!_compoundShapeResource) {
getCollisionGeometryResource();
}
return (_compoundShapeResource && _compoundShapeResource->isFailed());
}
void RenderableModelEntityItem::setShapeType(ShapeType type) {
ModelEntityItem::setShapeType(type);
if (getShapeType() == SHAPE_TYPE_COMPOUND) {
@ -343,7 +351,6 @@ void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
bool RenderableModelEntityItem::isReadyToComputeShape() const {
ShapeType type = getShapeType();
auto model = getModel();
if (type == SHAPE_TYPE_COMPOUND) {
if (!model || getCompoundShapeURL().isEmpty()) {

View file

@ -81,6 +81,7 @@ public:
virtual bool isReadyToComputeShape() const override;
virtual void computeShapeInfo(ShapeInfo& shapeInfo) override;
bool computeShapeFailedToLoad();
virtual bool contains(const glm::vec3& point) const override;
void stopModelOverrideIfNoParent();

View file

@ -104,6 +104,10 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
_networkTexture.reset();
});
}
withWriteLock([&] {
entity->setVisuallyReady(true);
});
} else {
bool textureNeedsUpdate = resultWithReadLock<bool>([&]{
return !_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures);
@ -113,6 +117,12 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
_networkTexture = DependencyManager::get<TextureCache>()->getTexture(_particleProperties.textures);
});
}
if (_networkTexture) {
withWriteLock([&] {
entity->setVisuallyReady(_networkTexture->isFailed() || _networkTexture->isLoaded());
});
}
}
void* key = (void*)this;

View file

@ -1754,8 +1754,9 @@ int EntityScriptingInterface::getJointIndex(const QUuid& entityID, const QString
return -1;
}
int result;
BLOCKING_INVOKE_METHOD(_entityTree.get(), "getJointIndex",
Q_RETURN_ARG(int, result), Q_ARG(QUuid, entityID), Q_ARG(QString, name));
_entityTree->withReadLock([&] {
result = _entityTree->getJointIndex(entityID, name);
});
return result;
}
@ -1764,8 +1765,9 @@ QStringList EntityScriptingInterface::getJointNames(const QUuid& entityID) {
return QStringList();
}
QStringList result;
BLOCKING_INVOKE_METHOD(_entityTree.get(), "getJointNames",
Q_RETURN_ARG(QStringList, result), Q_ARG(QUuid, entityID));
_entityTree->withReadLock([&] {
result = _entityTree->getJointNames(entityID);
});
return result;
}

View file

@ -104,7 +104,7 @@ protected:
QMutex _dynamicsMutex { QMutex::Recursive };
protected:
SetOfEntities _deadEntities;
SetOfEntities _deadEntities; // dead entities that might still be in the _entityTree
private:
void moveSimpleKinematics();

View file

@ -162,6 +162,7 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte
EntityItem(entityItemID)
{
_type = EntityTypes::ParticleEffect;
_visuallyReady = false;
}
void ParticleEffectEntityItem::setAlpha(float alpha) {
@ -747,4 +748,4 @@ particle::Properties ParticleEffectEntityItem::getParticleProperties() const {
}
return result;
}
}

View file

@ -128,7 +128,11 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) {
QString materialMapString = mapping.value("materialMap").toString();
QJsonDocument materialMapDocument = QJsonDocument::fromJson(materialMapString.toUtf8());
QJsonObject materialMap = materialMapDocument.object();
if (!materialMapString.isEmpty()) {
if (materialMapDocument.isEmpty() || materialMap.isEmpty()) {
qCDebug(modelformat) << "fbx Material Map found but did not produce valid JSON:" << materialMapString;
}
}
for (QHash<QString, FBXMaterial>::iterator it = _fbxMaterials.begin(); it != _fbxMaterials.end(); it++) {
FBXMaterial& material = (*it);

View file

@ -585,7 +585,6 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
FBXMesh& fbxMesh = extractedMesh;
graphics::MeshPointer mesh(new graphics::Mesh());
bool hasBlendShapes = !fbxMesh.blendshapes.empty();
int numVerts = extractedMesh.vertices.size();
if (!fbxMesh.normals.empty() && fbxMesh.tangents.empty()) {
@ -618,7 +617,6 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
qWarning() << "Unexpected tangents in " << url;
}
const auto normalsAndTangentsSize = normalsSize + tangentsSize;
const int normalsAndTangentsStride = 2 * normalElement.getSize();
// Color attrib
const auto colorElement = FBX_COLOR_ELEMENT;
@ -761,33 +759,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
bool interleavePositions = true;
bool interleaveNormalsTangents = true;
// TODO: We are using the same vertex format layout for all meshes because this is more efficient
// This work is going into rc73 release which is meant to be used for the SPot500 event and we are picking the format
// that works best for blendshaped and skinned meshes aka the avatars.
// We will improve this technique in a hot fix to 73.
hasBlendShapes = true;
// If has blend shapes allocate and assign buffers for pos and tangents now
if (hasBlendShapes) {
auto posBuffer = std::make_shared<gpu::Buffer>();
posBuffer->setData(positionsSize, (const gpu::Byte*) vertBuffer->getData() + positionsOffset);
vertexBufferStream->addBuffer(posBuffer, 0, positionElement.getSize());
auto normalsAndTangentsBuffer = std::make_shared<gpu::Buffer>();
normalsAndTangentsBuffer->setData(normalsAndTangentsSize, (const gpu::Byte*) vertBuffer->getData() + normalsAndTangentsOffset);
vertexBufferStream->addBuffer(normalsAndTangentsBuffer, 0, normalsAndTangentsStride);
// update channels and attribBuffer size accordingly
interleavePositions = false;
interleaveNormalsTangents = false;
tangentChannel = 1;
attribChannel = 2;
totalAttribBufferSize = totalVertsSize - positionsSize - normalsAndTangentsSize;
}
// Define the vertex format, compute the offset for each attributes as we append them to the vertex format
// Define the vertex format, compute the offset for each attributes as we append them to the vertex format
gpu::Offset bufOffset = 0;
if (positionsSize) {
vertexFormat->setAttribute(gpu::Stream::POSITION, posChannel, positionElement, bufOffset);

View file

@ -20,6 +20,9 @@
#include "nvToolsExt.h"
#endif
// Define the GPU_BATCH_DETAILED_TRACING to get detailed tracing of the commands during the batch executions
// #define GPU_BATCH_DETAILED_TRACING
#include <GPUIdent.h>
#include "GLTexture.h"
@ -271,6 +274,9 @@ void GLBackend::renderPassDraw(const Batch& batch) {
case Batch::COMMAND_drawIndexedInstanced:
case Batch::COMMAND_multiDrawIndirect:
case Batch::COMMAND_multiDrawIndexedIndirect: {
#ifdef GPU_BATCH_DETAILED_TRACING
PROFILE_RANGE(render_gpu_gl_detail, "drawcall");
#endif
// updates for draw calls
++_currentDraw;
updateInput();
@ -281,6 +287,94 @@ void GLBackend::renderPassDraw(const Batch& batch) {
(this->*(call))(batch, *offset);
break;
}
#ifdef GPU_BATCH_DETAILED_TRACING
//case Batch::COMMAND_setModelTransform:
//case Batch::COMMAND_setViewTransform:
//case Batch::COMMAND_setProjectionTransform:
case Batch::COMMAND_setProjectionJitter:
case Batch::COMMAND_setViewportTransform:
case Batch::COMMAND_setDepthRangeTransform:
{
PROFILE_RANGE(render_gpu_gl_detail, "transform");
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
break;
}
case Batch::COMMAND_clearFramebuffer:
{
PROFILE_RANGE(render_gpu_gl_detail, "clear");
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
break;
}
case Batch::COMMAND_blit:
{
PROFILE_RANGE(render_gpu_gl_detail, "blit");
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
break;
}
case Batch::COMMAND_setInputFormat:
case Batch::COMMAND_setInputBuffer:
case Batch::COMMAND_setIndexBuffer:
case Batch::COMMAND_setIndirectBuffer: {
PROFILE_RANGE(render_gpu_gl_detail, "input");
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
break;
}
case Batch::COMMAND_setStateBlendFactor:
case Batch::COMMAND_setStateScissorRect:
case Batch::COMMAND_setPipeline: {
PROFILE_RANGE(render_gpu_gl_detail, "pipeline");
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
break;
}
case Batch::COMMAND_setUniformBuffer:
{
PROFILE_RANGE(render_gpu_gl_detail, "ubo");
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
break;
}
case Batch::COMMAND_setResourceBuffer:
case Batch::COMMAND_setResourceTexture:
case Batch::COMMAND_setResourceTextureTable:
{
PROFILE_RANGE(render_gpu_gl_detail, "resource");
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
break;
}
case Batch::COMMAND_setResourceFramebufferSwapChainTexture:
case Batch::COMMAND_setFramebuffer:
case Batch::COMMAND_setFramebufferSwapChain:
{
PROFILE_RANGE(render_gpu_gl_detail, "framebuffer");
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
break;
}
case Batch::COMMAND_generateTextureMips:
{
PROFILE_RANGE(render_gpu_gl_detail, "genMipMaps");
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
break;
}
case Batch::COMMAND_beginQuery:
case Batch::COMMAND_endQuery:
case Batch::COMMAND_getQuery:
{
PROFILE_RANGE(render_gpu_gl_detail, "query");
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
break;
}
#endif
default: {
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
@ -294,6 +388,8 @@ void GLBackend::renderPassDraw(const Batch& batch) {
}
void GLBackend::render(const Batch& batch) {
PROFILE_RANGE(render_gpu_gl, batch.getName());
_transform._skybox = _stereo._skybox = batch.isSkyboxEnabled();
// Allow the batch to override the rendering stereo settings
// for things like full framebuffer copy operations (deferred lighting passes)

View file

@ -20,7 +20,10 @@ namespace gpu {
~GL41Buffer() {
if (_texBuffer) {
glDeleteTextures(1, &_texBuffer);
auto backend = _backend.lock();
if (backend) {
backend->releaseTexture(_texBuffer, 0);
}
}
}

View file

@ -61,9 +61,9 @@ GLBuffer* GL45Backend::syncGPUObject(const Buffer& buffer) {
bool GL45Backend::bindResourceBuffer(uint32_t slot, const BufferPointer& buffer) {
GLBuffer* object = syncGPUObject((*buffer));
if (object) {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, object->_id);
auto bo = getBufferIDUnsynced((*buffer));
if (bo) {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, bo);
(void)CHECK_GL_ERROR();

View file

@ -98,6 +98,8 @@ void Batch::clear() {
_name = nullptr;
_invalidModel = true;
_currentModel = Transform();
_drawcallUniform = 0;
_drawcallUniformReset = 0;
_projectionJitter = glm::vec2(0.0f);
_enableStereo = true;
_enableSkybox = false;
@ -112,6 +114,13 @@ size_t Batch::cacheData(size_t size, const void* data) {
return offset;
}
void Batch::setDrawcallUniform(uint16_t uniform) {
_drawcallUniform = uniform;
}
void Batch::setDrawcallUniformReset(uint16_t uniformReset) {
_drawcallUniformReset = uniformReset;
}
void Batch::draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex) {
ADD_COMMAND(draw);
@ -545,7 +554,8 @@ void Batch::captureDrawCallInfoImpl() {
}
auto& drawCallInfos = getDrawCallInfoBuffer();
drawCallInfos.emplace_back((uint16)_objects.size() - 1);
drawCallInfos.emplace_back((uint16)_objects.size() - 1, _drawcallUniform);
_drawcallUniform = _drawcallUniformReset;
}
void Batch::captureDrawCallInfo() {
@ -679,6 +689,8 @@ void Batch::_glColor4f(float red, float green, float blue, float alpha) {
}
void Batch::finishFrame(BufferUpdates& updates) {
PROFILE_RANGE(render_gpu, __FUNCTION__);
for (auto& mapItem : _namedData) {
auto& name = mapItem.first;
auto& instance = mapItem.second;
@ -707,6 +719,7 @@ void Batch::finishFrame(BufferUpdates& updates) {
}
void Batch::flush() {
PROFILE_RANGE(render_gpu, __FUNCTION__);
for (auto& mapItem : _namedData) {
auto& name = mapItem.first;
auto& instance = mapItem.second;

View file

@ -48,6 +48,7 @@ public:
using Index = uint16_t;
DrawCallInfo(Index idx) : index(idx) {}
DrawCallInfo(Index idx, Index user) : index(idx), unused(user) {}
Index index { 0 };
uint16_t unused { 0 }; // Reserved space for later
@ -95,6 +96,7 @@ public:
~Batch();
void setName(const char* name);
const char* getName() const { return _name; }
void clear();
// Batches may need to override the context level stereo settings
@ -110,6 +112,14 @@ public:
void enableSkybox(bool enable = true);
bool isSkyboxEnabled() const;
// Drawcall Uniform value
// One 16bit word uniform value is available during the drawcall
// its value must be set before each drawcall
void setDrawcallUniform(uint16 uniform);
// It is reset to the reset value between each drawcalls
// The reset value is 0 by default and can be changed as a batch state with this call
void setDrawcallUniformReset(uint16 resetUniform);
// Drawcalls
void draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex = 0);
void drawIndexed(Primitive primitiveType, uint32 numIndices, uint32 startIndex = 0);
@ -498,6 +508,9 @@ public:
NamedBatchDataMap _namedData;
uint16_t _drawcallUniform{ 0 };
uint16_t _drawcallUniformReset{ 0 };
glm::vec2 _projectionJitter{ 0.0f, 0.0f };
bool _enableStereo{ true };
bool _enableSkybox { false };

View file

@ -84,6 +84,7 @@ void Context::appendFrameBatch(const BatchPointer& batch) {
}
FramePointer Context::endFrame() {
PROFILE_RANGE(render_gpu, __FUNCTION__);
assert(_frameActive);
auto result = _currentFrame;
_currentFrame.reset();
@ -101,10 +102,12 @@ void Context::executeBatch(Batch& batch) const {
}
void Context::recycle() const {
PROFILE_RANGE(render_gpu, __FUNCTION__);
_backend->recycle();
}
void Context::consumeFrameUpdates(const FramePointer& frame) const {
PROFILE_RANGE(render_gpu, __FUNCTION__);
frame->preRender();
}

View file

@ -25,12 +25,14 @@ Frame::~Frame() {
}
void Frame::finish() {
PROFILE_RANGE(render_gpu, __FUNCTION__);
for (const auto& batch : batches) {
batch->finishFrame(bufferUpdates);
}
}
void Frame::preRender() {
PROFILE_RANGE(render_gpu, __FUNCTION__);
for (auto& update : bufferUpdates) {
update.apply();
}

View file

@ -214,6 +214,18 @@ TransformObject getTransformObject() {
}
<@endfunc@>
<@func transformModelToWorldAndEyeAndClipPos(cameraTransform, objectTransform, modelPos, worldPos, eyePos, clipPos)@>
{ // transformModelToEyeAndClipPos
vec4 eyeWAPos;
<$transformModelToEyeWorldAlignedPos($cameraTransform$, $objectTransform$, $modelPos$, eyeWAPos)$>
<$worldPos$> = vec4(eyeWAPos.xyz + <$cameraTransform$>._viewInverse[3].xyz, 1.0);
<$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos;
<$eyePos$> = vec4((<$cameraTransform$>._view * vec4(eyeWAPos.xyz, 0.0)).xyz, 1.0);
<$transformStereoClipsSpace($cameraTransform$, $clipPos$)$>
}
<@endfunc@>
<@func transformModelToEyePos(cameraTransform, objectTransform, modelPos, eyePos)@>
{ // transformModelToEyePos
vec4 eyeWAPos;

View file

@ -20,15 +20,11 @@ using namespace gpu;
Material::Material() :
_key(0),
_schemaBuffer(),
_texMapArrayBuffer(),
_textureMaps()
{
// created from nothing: create the Buffer to store the properties
Schema schema;
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema));
TexMapArraySchema TexMapArraySchema;
_texMapArrayBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(TexMapArraySchema), (const gpu::Byte*) &TexMapArraySchema));
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
}
Material::Material(const Material& material) :
@ -38,12 +34,8 @@ Material::Material(const Material& material) :
{
// copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer
Schema schema;
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema));
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
_schemaBuffer.edit<Schema>() = material._schemaBuffer.get<Schema>();
TexMapArraySchema texMapArraySchema;
_texMapArrayBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(TexMapArraySchema), (const gpu::Byte*) &texMapArraySchema));
_texMapArrayBuffer.edit<TexMapArraySchema>() = material._texMapArrayBuffer.get<TexMapArraySchema>();
}
Material& Material::operator= (const Material& material) {
@ -57,13 +49,9 @@ Material& Material::operator= (const Material& material) {
// copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer
Schema schema;
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema));
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
_schemaBuffer.edit<Schema>() = material._schemaBuffer.get<Schema>();
TexMapArraySchema texMapArraySchema;
_texMapArrayBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(TexMapArraySchema), (const gpu::Byte*) &texMapArraySchema));
_texMapArrayBuffer.edit<TexMapArraySchema>() = material._texMapArrayBuffer.get<TexMapArraySchema>();
return (*this);
}
@ -137,17 +125,17 @@ void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textur
resetOpacityMap();
// update the texcoord0 with albedo
_texMapArrayBuffer.edit<TexMapArraySchema>()._texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
_schemaBuffer.edit<Schema>()._texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
}
if (channel == MaterialKey::OCCLUSION_MAP) {
_texMapArrayBuffer.edit<TexMapArraySchema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
_schemaBuffer.edit<Schema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
}
if (channel == MaterialKey::LIGHTMAP_MAP) {
// update the texcoord1 with lightmap
_texMapArrayBuffer.edit<TexMapArraySchema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
_texMapArrayBuffer.edit<TexMapArraySchema>()._lightmapParams = (textureMap ? glm::vec4(textureMap->getLightmapOffsetScale(), 0.0, 0.0) : glm::vec4(0.0, 1.0, 0.0, 0.0));
_schemaBuffer.edit<Schema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
_schemaBuffer.edit<Schema>()._lightmapParams = (textureMap ? glm::vec4(textureMap->getLightmapOffsetScale(), 0.0, 0.0) : glm::vec4(0.0, 1.0, 0.0, 0.0));
}
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
@ -235,6 +223,6 @@ void Material::setTextureTransforms(const Transform& transform) {
}
}
for (int i = 0; i < NUM_TEXCOORD_TRANSFORMS; i++) {
_texMapArrayBuffer.edit<TexMapArraySchema>()._texcoordTransforms[i] = transform.getMatrix();
_schemaBuffer.edit<Schema>()._texcoordTransforms[i] = transform.getMatrix();
}
}

View file

@ -268,6 +268,9 @@ public:
typedef glm::vec3 Color;
// Texture Map Array Schema
static const int NUM_TEXCOORD_TRANSFORMS{ 2 };
typedef MaterialKey::MapChannel MapChannel;
typedef std::map<MapChannel, TextureMapPointer> TextureMaps;
typedef std::bitset<MaterialKey::NUM_MAP_CHANNELS> MapFlags;
@ -323,6 +326,11 @@ public:
// for alignment beauty, Material size == Mat4x4
// Texture Coord Transform Array
glm::mat4 _texcoordTransforms[NUM_TEXCOORD_TRANSFORMS];
glm::vec4 _lightmapParams{ 0.0, 1.0, 0.0, 0.0 };
Schema() {}
};
@ -340,17 +348,6 @@ public:
// conversion from legacy material properties to PBR equivalent
static float shininessToRoughness(float shininess) { return 1.0f - shininess / 100.0f; }
// Texture Map Array Schema
static const int NUM_TEXCOORD_TRANSFORMS{ 2 };
class TexMapArraySchema {
public:
glm::mat4 _texcoordTransforms[NUM_TEXCOORD_TRANSFORMS];
glm::vec4 _lightmapParams{ 0.0, 1.0, 0.0, 0.0 };
TexMapArraySchema() {}
};
const UniformBufferView& getTexMapArrayBuffer() const { return _texMapArrayBuffer; }
int getTextureCount() const { calculateMaterialInfo(); return _textureCount; }
size_t getTextureSize() const { calculateMaterialInfo(); return _textureSize; }
bool hasTextureInfo() const { return _hasCalculatedTextureInfo; }
@ -370,7 +367,6 @@ protected:
private:
mutable MaterialKey _key;
mutable UniformBufferView _schemaBuffer;
mutable UniformBufferView _texMapArrayBuffer;
mutable gpu::TextureTablePointer _textureTable{ std::make_shared<gpu::TextureTable>() };
TextureMaps _textureMaps;

View file

@ -13,6 +13,31 @@
<@include graphics/ShaderConstants.h@>
const int MAX_TEXCOORDS = 2;
struct TexMapArray {
mat4 _texcoordTransforms0;
mat4 _texcoordTransforms1;
vec4 _lightmapParams;
};
<@func declareMaterialTexMapArrayBuffer()@>
<@func evalTexMapArrayTexcoord0(texMapArray, inTexcoord0, outTexcoord0)@>
{
<$outTexcoord0$> = (<$texMapArray$>._texcoordTransforms0 * vec4(<$inTexcoord0$>.st, 0.0, 1.0)).st;
}
<@endfunc@>
<@func evalTexMapArrayTexcoord1(texMapArray, inTexcoord1, outTexcoord1)@>
{
<$outTexcoord1$> = (<$texMapArray$>._texcoordTransforms1 * vec4(<$inTexcoord1$>.st, 0.0, 1.0)).st;
}
<@endfunc@>
<@endfunc@>
// The material values (at least the material key) must be precisely bitwise accurate
// to what is provided by the uniform buffer, or the material key has the wrong bits
@ -25,11 +50,15 @@ struct Material {
layout(binding=GRAPHICS_BUFFER_MATERIAL) uniform materialBuffer {
Material _mat;
TexMapArray _texMapArray;
};
Material getMaterial() {
return _mat;
}
TexMapArray getTexMapArray() {
return _texMapArray;
}
vec3 getMaterialEmissive(Material m) { return m._emissiveOpacity.rgb; }
float getMaterialOpacity(Material m) { return m._emissiveOpacity.a; }

View file

@ -11,41 +11,7 @@
<@if not MODEL_MATERIAL_TEXTURES_SLH@>
<@def MODEL_MATERIAL_TEXTURES_SLH@>
<@include graphics/ShaderConstants.h@>
<@func declareMaterialTexMapArrayBuffer()@>
const int MAX_TEXCOORDS = 2;
struct TexMapArray {
// mat4 _texcoordTransforms[MAX_TEXCOORDS];
mat4 _texcoordTransforms0;
mat4 _texcoordTransforms1;
vec4 _lightmapParams;
};
layout(binding=GRAPHICS_BUFFER_TEXMAPARRAY) uniform texMapArrayBuffer {
TexMapArray _texMapArray;
};
TexMapArray getTexMapArray() {
return _texMapArray;
}
<@func evalTexMapArrayTexcoord0(texMapArray, inTexcoord0, outTexcoord0)@>
{
<$outTexcoord0$> = (<$texMapArray$>._texcoordTransforms0 * vec4(<$inTexcoord0$>.st, 0.0, 1.0)).st;
}
<@endfunc@>
<@func evalTexMapArrayTexcoord1(texMapArray, inTexcoord1, outTexcoord1)@>
{
<$outTexcoord1$> = (<$texMapArray$>._texcoordTransforms1 * vec4(<$inTexcoord1$>.st, 0.0, 1.0)).st;
}
<@endfunc@>
<@endfunc@>
<@include graphics/Material.slh@>
<@func declareMaterialTextures(withAlbedo, withRoughness, withNormal, withMetallic, withEmissive, withOcclusion, withScattering)@>

View file

@ -551,6 +551,11 @@ QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& textu
graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture,
image::TextureUsage::Type type, MapChannel channel) {
if (baseUrl.isEmpty()) {
return nullptr;
}
const auto url = getTextureUrl(baseUrl, fbxTexture);
const auto texture = DependencyManager::get<TextureCache>()->getTexture(url, type, fbxTexture.content, fbxTexture.maxNumPixels);
_textures[channel] = Texture { fbxTexture.name, texture };

View file

@ -161,8 +161,14 @@ void AddressManager::storeCurrentAddress() {
// be loaded over http(s)
// url.scheme() == URL_SCHEME_HTTP ||
// url.scheme() == URL_SCHEME_HTTPS ||
bool isInErrorState = DependencyManager::get<NodeList>()->getDomainHandler().isInErrorState();
if (isConnected()) {
currentAddressHandle.set(url);
if (isInErrorState) {
// save the last address visited before the problem url.
currentAddressHandle.set(lastAddress());
} else {
currentAddressHandle.set(url);
}
} else {
qCWarning(networking) << "Ignoring attempt to save current address because not connected to domain:" << url;
}
@ -861,6 +867,10 @@ void AddressManager::goToUser(const QString& username, bool shouldMatchOrientati
QByteArray(), nullptr, requestParams);
}
bool AddressManager::canGoBack() const {
return (_backStack.size() > 0);
}
void AddressManager::refreshPreviousLookup() {
// if we have a non-empty previous lookup, fire it again now (but don't re-store it in the history)
if (!_previousAPILookup.isEmpty()) {

View file

@ -254,6 +254,12 @@ public slots:
*/
void goToLastAddress() { handleUrl(_lastVisitedURL, LookupTrigger::AttemptedRefresh); }
/**jsdoc
* Returns if going back is possible.
* @function location.canGoBack
*/
bool canGoBack() const;
/**jsdoc
* Refresh the current address, e.g., after connecting to a domain in order to position the user to the desired location.
* @function location.refreshPreviousLookup

View file

@ -123,6 +123,7 @@ void DomainHandler::hardReset() {
softReset();
_isInErrorState = false;
emit redirectErrorStateChanged(_isInErrorState);
qCDebug(networking) << "Hard reset in NodeList DomainHandler.";
_pendingDomainID = QUuid();
@ -138,6 +139,11 @@ void DomainHandler::hardReset() {
_pendingPath.clear();
}
bool DomainHandler::isHardRefusal(int reasonCode) {
return (reasonCode == (int)ConnectionRefusedReason::ProtocolMismatch || reasonCode == (int)ConnectionRefusedReason::NotAuthorized ||
reasonCode == (int)ConnectionRefusedReason::TimedOut);
}
bool DomainHandler::getInterstitialModeEnabled() const {
return _interstitialModeSettingLock.resultWithReadLock<bool>([&] {
return _enableInterstitialMode.get();
@ -327,6 +333,7 @@ void DomainHandler::setIsConnected(bool isConnected) {
_isConnected = isConnected;
if (_isConnected) {
_lastDomainConnectionError = -1;
emit connectedToDomain(_domainURL);
if (_domainURL.scheme() == URL_SCHEME_HIFI && !_domainURL.host().isEmpty()) {
@ -358,9 +365,11 @@ void DomainHandler::loadedErrorDomain(std::map<QString, QString> namedPaths) {
void DomainHandler::setRedirectErrorState(QUrl errorUrl, QString reasonMessage, int reasonCode, const QString& extraInfo) {
_lastDomainConnectionError = reasonCode;
if (getInterstitialModeEnabled()) {
if (getInterstitialModeEnabled() && isHardRefusal(reasonCode)) {
_errorDomainURL = errorUrl;
_isInErrorState = true;
qCDebug(networking) << "Error connecting to domain: " << reasonMessage;
emit redirectErrorStateChanged(_isInErrorState);
emit redirectToErrorDomainURL(_errorDomainURL);
} else {
emit domainConnectionRefused(reasonMessage, reasonCode, extraInfo);

View file

@ -187,8 +187,6 @@ private slots:
signals:
void domainURLChanged(QUrl domainURL);
void domainConnectionErrorChanged(int reasonCode);
// NOTE: the emission of completedSocketDiscovery does not mean a connection to DS is established
// It means that, either from DNS lookup or ICE, we think we have a socket we can talk to DS on
void completedSocketDiscovery();
@ -205,6 +203,7 @@ signals:
void domainConnectionRefused(QString reasonMessage, int reason, const QString& extraInfo);
void redirectToErrorDomainURL(QUrl errorDomainURL);
void redirectErrorStateChanged(bool isInErrorState);
void limitOfSilentDomainCheckInsReached();
@ -213,6 +212,8 @@ private:
void sendDisconnectPacket();
void hardReset();
bool isHardRefusal(int reasonCode);
QUuid _uuid;
Node::LocalID _localID;
QUrl _domainURL;
@ -230,7 +231,7 @@ private:
QString _pendingPath;
QTimer _settingsTimer;
mutable ReadWriteLockable _interstitialModeSettingLock;
Setting::Handle<bool> _enableInterstitialMode{ "enableInterstitialMode", false };
Setting::Handle<bool> _enableInterstitialMode{ "enableInterstitialMode", true };
QSet<QString> _domainConnectionRefusals;
bool _hasCheckedForAccessToken { false };

View file

@ -332,7 +332,6 @@ void NodeList::sendDomainServerCheckIn() {
qCDebug(networking) << "Local domain-server port read from shared memory (or default) is" << domainPort;
_domainHandler.setPort(domainPort);
}
}
// check if we're missing a keypair we need to verify ourselves with the domain-server

View file

@ -79,6 +79,9 @@ void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
_deadEntities.insert(entity);
}
}
if (entity->getClientOnly()) {
_deadAvatarEntities.insert(entity);
}
}
void PhysicalEntitySimulation::removeOwnershipData(EntityMotionState* motionState) {
@ -123,6 +126,11 @@ void PhysicalEntitySimulation::takeDeadEntities(SetOfEntities& deadEntities) {
_deadEntities.clear();
}
void PhysicalEntitySimulation::takeDeadAvatarEntities(SetOfEntities& deadEntities) {
_deadAvatarEntities.swap(deadEntities);
_deadAvatarEntities.clear();
}
void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
// queue incoming changes: from external sources (script, EntityServer, etc) to physics engine
QMutexLocker lock(&_mutex);

View file

@ -60,6 +60,7 @@ public:
virtual void applyDynamicChanges() override;
virtual void takeDeadEntities(SetOfEntities& deadEntities) override;
void takeDeadAvatarEntities(SetOfEntities& deadEntities);
signals:
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
@ -112,6 +113,7 @@ private:
VectorOfEntityMotionStates _owned;
VectorOfEntityMotionStates _bids;
SetOfEntities _deadAvatarEntities;
workload::SpacePointer _space;
uint64_t _nextBidExpiry;
uint32_t _lastStepSendPackets { 0 };

View file

@ -171,6 +171,13 @@ public:
};
Q_ENUM(PickType)
enum JointState {
JOINT_STATE_NONE = 0,
JOINT_STATE_LEFT_HAND,
JOINT_STATE_RIGHT_HAND,
JOINT_STATE_MOUSE
};
void enable(bool enabled = true);
void disable() { enable(false); }
@ -211,9 +218,11 @@ public:
void setIgnoreItems(const QVector<QUuid>& items);
void setIncludeItems(const QVector<QUuid>& items);
virtual bool isLeftHand() const { return false; }
virtual bool isRightHand() const { return false; }
virtual bool isMouse() const { return false; }
virtual bool isLeftHand() const { return _jointState == JOINT_STATE_LEFT_HAND; }
virtual bool isRightHand() const { return _jointState == JOINT_STATE_RIGHT_HAND; }
virtual bool isMouse() const { return _jointState == JOINT_STATE_MOUSE; }
void setJointState(JointState jointState) { _jointState = jointState; }
virtual Transform getResultTransform() const = 0;
@ -227,13 +236,15 @@ private:
QVector<QUuid> _ignoreItems;
QVector<QUuid> _includeItems;
JointState _jointState { JOINT_STATE_NONE };
};
Q_DECLARE_METATYPE(PickQuery::PickType)
template<typename T>
class Pick : public PickQuery {
public:
Pick(const PickFilter& filter, const float maxDistance, const bool enabled) : PickQuery(filter, maxDistance, enabled) {}
Pick(const T& mathPick, const PickFilter& filter, const float maxDistance, const bool enabled) : PickQuery(filter, maxDistance, enabled), _mathPick(mathPick) {}
virtual ~Pick() {}
virtual T getMathematicalPick() const = 0;
@ -242,6 +253,9 @@ public:
virtual PickResultPointer getOverlayIntersection(const T& pick) = 0;
virtual PickResultPointer getAvatarIntersection(const T& pick) = 0;
virtual PickResultPointer getHUDIntersection(const T& pick) = 0;
protected:
T _mathPick;
};
namespace std {

View file

@ -90,6 +90,17 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector<QUuid>& includ
}
}
Transform PickManager::getParentTransform(unsigned int uid) const {
auto pick = findPick(uid);
if (pick) {
auto parentTransform = pick->parentTransform;
if (parentTransform) {
return parentTransform->getTransform();
}
}
return Transform();
}
Transform PickManager::getResultTransform(unsigned int uid) const {
auto pick = findPick(uid);
if (pick) {

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