mirror of
https://github.com/overte-org/overte.git
synced 2025-04-15 16:02:08 +02:00
Merge branch 'master' into scriptvec3
This commit is contained in:
commit
67662df760
223 changed files with 3234 additions and 3165 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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}
|
||||
)
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
22
cmake/macros/TargetHifiAudioCodec.cmake
Normal file
22
cmake/macros/TargetHifiAudioCodec.cmake
Normal 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()
|
|
@ -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/>
|
||||
|
|
|
@ -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@")
|
||||
|
|
|
@ -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
|
||||
|
|
24
docs/LICENSE_highlight.js.txt
Normal file
24
docs/LICENSE_highlight.js.txt
Normal 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
23
docs/LICENSE_markdeep.txt
Normal 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.
|
280
docs/interface/raypick/pick-parenting.md.html
Normal file
280
docs/interface/raypick/pick-parenting.md.html
Normal 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
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
170
docs/markdeep_apidoc.css
Normal 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;
|
||||
}
|
||||
|
BIN
interface/resources/images/interstitialPage/button.png
Normal file
BIN
interface/resources/images/interstitialPage/button.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
BIN
interface/resources/images/interstitialPage/button_back.png
Normal file
BIN
interface/resources/images/interstitialPage/button_back.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
BIN
interface/resources/images/interstitialPage/button_hover.png
Normal file
BIN
interface/resources/images/interstitialPage/button_hover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
interface/resources/images/interstitialPage/button_tryAgain.png
Normal file
BIN
interface/resources/images/interstitialPage/button_tryAgain.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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);
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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 =
|
||||
|
|
|
@ -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}.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -20,7 +20,10 @@ namespace gpu {
|
|||
|
||||
~GL41Buffer() {
|
||||
if (_texBuffer) {
|
||||
glDeleteTextures(1, &_texBuffer);
|
||||
auto backend = _backend.lock();
|
||||
if (backend) {
|
||||
backend->releaseTexture(_texBuffer, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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)@>
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue