mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-30 16:23:15 +02:00
Merge branch 'master' into 21801
This commit is contained in:
commit
7a9e3388fa
170 changed files with 3781 additions and 1766 deletions
CMakeLists.txt
assignment-client/src
assets
avatars
entities
scripts
cmake
externals/serverless-content
macros
templates
domain-server/src
interface
CMakeLists.txt
resources
controllers
html/img
images
qml
+android
AudioScopeUI.qmlcontrols-uit
controls
dialogs
hifi
windows
serverless
src
Application.cppApplication.hDiscoverabilityManager.cppLODManager.cppMenu.cpp
avatar
commerce
raypick
scripting
AssetMappingsScriptingInterface.cppAssetMappingsScriptingInterface.hAudioDevices.cppAudioDevices.hSelectionScriptingInterface.cppSelectionScriptingInterface.hWindowScriptingInterface.cppWindowScriptingInterface.h
ui
libraries
animation/src
controllers/src/controllers
display-plugins/src/display-plugins
entities-renderer/src
entities/src
EntityItem.cppEntityItem.hEntityItemProperties.hEntityScriptingInterface.cppEntityScriptingInterface.hEntitySimulation.cppEntitySimulation.hEntityTree.cppMaterialEntityItem.cppSimpleEntitySimulation.cppSimpleEntitySimulation.hSimulationOwner.cppSimulationOwner.hUpdateEntityOperator.cpp
gpu-gl-common/src/gpu/gl
|
@ -39,6 +39,7 @@ set(BUILD_TOOLS_OPTION ON)
|
|||
set(BUILD_INSTALLER_OPTION ON)
|
||||
set(GLES_OPTION OFF)
|
||||
set(DISABLE_QML_OPTION OFF)
|
||||
set(DOWNLOAD_SERVERLESS_CONTENT_OPTION OFF)
|
||||
|
||||
if (ANDROID OR UWP)
|
||||
set(BUILD_SERVER_OPTION OFF)
|
||||
|
@ -74,6 +75,11 @@ option(BUILD_INSTALLER "Build installer" ${BUILD_INSTALLER_OPTION})
|
|||
option(USE_GLES "Use OpenGL ES" ${GLES_OPTION})
|
||||
option(DISABLE_QML "Disable QML" ${DISABLE_QML_OPTION})
|
||||
option(DISABLE_KTX_CACHE "Disable KTX Cache" OFF)
|
||||
option(
|
||||
DOWNLOAD_SERVERLESS_CONTENT
|
||||
"Download and setup default serverless content beside Interface"
|
||||
${DOWNLOAD_SERVERLESS_CONTENT_OPTION}
|
||||
)
|
||||
|
||||
set(PLATFORM_QT_GL OpenGL)
|
||||
|
||||
|
@ -88,12 +94,13 @@ foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS})
|
|||
list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}")
|
||||
endforeach()
|
||||
|
||||
MESSAGE(STATUS "Build server: " ${BUILD_SERVER})
|
||||
MESSAGE(STATUS "Build client: " ${BUILD_CLIENT})
|
||||
MESSAGE(STATUS "Build tests: " ${BUILD_TESTS})
|
||||
MESSAGE(STATUS "Build tools: " ${BUILD_TOOLS})
|
||||
MESSAGE(STATUS "Build installer: " ${BUILD_INSTALLER})
|
||||
MESSAGE(STATUS "GL ES: " ${USE_GLES})
|
||||
MESSAGE(STATUS "Build server: " ${BUILD_SERVER})
|
||||
MESSAGE(STATUS "Build client: " ${BUILD_CLIENT})
|
||||
MESSAGE(STATUS "Build tests: " ${BUILD_TESTS})
|
||||
MESSAGE(STATUS "Build tools: " ${BUILD_TOOLS})
|
||||
MESSAGE(STATUS "Build installer: " ${BUILD_INSTALLER})
|
||||
MESSAGE(STATUS "GL ES: " ${USE_GLES})
|
||||
MESSAGE(STATUS "DL serverless content: " ${DOWNLOAD_SERVERLESS_CONTENT})
|
||||
|
||||
if (DISABLE_QML)
|
||||
MESSAGE(STATUS "QML disabled!")
|
||||
|
|
|
@ -1486,16 +1486,16 @@ std::pair<bool, AssetMeta> AssetServer::readMetaFile(AssetUtils::AssetHash hash)
|
|||
if (error.error == QJsonParseError::NoError && doc.isObject()) {
|
||||
auto root = doc.object();
|
||||
|
||||
auto bakeVersion = root[BAKE_VERSION_KEY].toInt(-1);
|
||||
auto bakeVersion = root[BAKE_VERSION_KEY];
|
||||
auto failedLastBake = root[FAILED_LAST_BAKE_KEY];
|
||||
auto lastBakeErrors = root[LAST_BAKE_ERRORS_KEY];
|
||||
|
||||
if (bakeVersion != -1
|
||||
if (bakeVersion.isDouble()
|
||||
&& failedLastBake.isBool()
|
||||
&& lastBakeErrors.isString()) {
|
||||
|
||||
AssetMeta meta;
|
||||
meta.bakeVersion = bakeVersion;
|
||||
meta.bakeVersion = bakeVersion.toInt();
|
||||
meta.failedLastBake = failedLastBake.toBool();
|
||||
meta.lastBakeErrors = lastBakeErrors.toString();
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ uint64_t AvatarMixerClientData::getLastOtherAvatarEncodeTime(QUuid otherAvatar)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void AvatarMixerClientData::setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time) {
|
||||
void AvatarMixerClientData::setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, uint64_t time) {
|
||||
std::unordered_map<QUuid, uint64_t>::iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar);
|
||||
if (itr != _lastOtherAvatarEncodeTime.end()) {
|
||||
itr->second = time;
|
||||
|
|
|
@ -113,7 +113,7 @@ public:
|
|||
ViewFrustum getViewFrustum() const { return _currentViewFrustum; }
|
||||
|
||||
uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
|
||||
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time);
|
||||
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, uint64_t time);
|
||||
|
||||
QVector<JointData>& getLastOtherAvatarSentJoints(QUuid otherAvatar) {
|
||||
auto& lastOtherAvatarSentJoints = _lastOtherAvatarSentJoints[otherAvatar];
|
||||
|
|
|
@ -470,7 +470,6 @@ void EntityServer::startDynamicDomainVerification() {
|
|||
// Delete the entity if it doesn't pass static certificate verification
|
||||
tree->deleteEntity(i.value(), true);
|
||||
} else {
|
||||
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
|
@ -490,9 +489,13 @@ void EntityServer::startDynamicDomainVerification() {
|
|||
|
||||
if (networkReply->error() == QNetworkReply::NoError) {
|
||||
if (jsonObject["domain_id"].toString() != thisDomainID) {
|
||||
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
|
||||
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << i.value();
|
||||
tree->deleteEntity(i.value(), true);
|
||||
if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) {
|
||||
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
|
||||
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << i.value();
|
||||
tree->deleteEntity(i.value(), true);
|
||||
} else {
|
||||
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << i.value();
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value();
|
||||
}
|
||||
|
|
|
@ -105,8 +105,6 @@ EntityScriptServer::~EntityScriptServer() {
|
|||
static const QString ENTITY_SCRIPT_SERVER_LOGGING_NAME = "entity-script-server";
|
||||
|
||||
void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
|
||||
// about each other.
|
||||
if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) {
|
||||
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
|
@ -119,8 +117,6 @@ void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<Rec
|
|||
}
|
||||
|
||||
void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
|
||||
// about each other.
|
||||
if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) {
|
||||
MessageID messageID;
|
||||
message->readPrimitive(&messageID);
|
||||
|
@ -190,15 +186,14 @@ void EntityScriptServer::updateEntityPPS() {
|
|||
}
|
||||
|
||||
void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
|
||||
// about each other.
|
||||
bool canRezAny = senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified();
|
||||
bool enable = false;
|
||||
message->readPrimitive(&enable);
|
||||
|
||||
auto senderUUID = senderNode->getUUID();
|
||||
auto it = _logListeners.find(senderUUID);
|
||||
|
||||
if (enable && senderNode->getCanRez()) {
|
||||
if (enable && canRezAny) {
|
||||
if (it == std::end(_logListeners)) {
|
||||
_logListeners.insert(senderUUID);
|
||||
qCInfo(entity_script_server) << "Node" << senderUUID << "subscribed to log stream";
|
||||
|
|
16
cmake/externals/serverless-content/CMakeLists.txt
vendored
Normal file
16
cmake/externals/serverless-content/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
include(ExternalProject)
|
||||
|
||||
set(EXTERNAL_NAME serverless-content)
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC66-v2.zip
|
||||
URL_MD5 d76bdb3e2bf7ae5d20115bd97b0c44a8
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for IDE users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
|
@ -14,12 +14,24 @@ macro(GENERATE_INSTALLERS)
|
|||
|
||||
set(CPACK_MODULE_PATH ${CPACK_MODULE_PATH} "${HF_CMAKE_DIR}/templates")
|
||||
|
||||
set(_DISPLAY_NAME ${BUILD_ORGANIZATION})
|
||||
|
||||
if (CLIENT_ONLY)
|
||||
set(_PACKAGE_NAME_EXTRA "-Interface")
|
||||
set(INSTALLER_TYPE "client_only")
|
||||
string(REGEX REPLACE "High Fidelity" "High Fidelity Interface" _DISPLAY_NAME ${BUILD_ORGANIZATION})
|
||||
elseif (SERVER_ONLY)
|
||||
set(_PACKAGE_NAME_EXTRA "-Sandbox")
|
||||
set(INSTALLER_TYPE "server_only")
|
||||
string(REGEX REPLACE "High Fidelity" "High Fidelity Sandbox" _DISPLAY_NAME ${BUILD_ORGANIZATION})
|
||||
else ()
|
||||
set(_DISPLAY_NAME ${BUILD_ORGANIZATION})
|
||||
set(INSTALLER_TYPE "full")
|
||||
endif ()
|
||||
|
||||
set(CPACK_PACKAGE_NAME ${_DISPLAY_NAME})
|
||||
set(CPACK_PACKAGE_VENDOR "High Fidelity")
|
||||
set(CPACK_PACKAGE_VERSION ${BUILD_VERSION})
|
||||
set(CPACK_PACKAGE_FILE_NAME "HighFidelity-Beta-${BUILD_VERSION}")
|
||||
set(CPACK_PACKAGE_FILE_NAME "HighFidelity-Beta${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}")
|
||||
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
|
||||
set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME})
|
||||
if (PR_BUILD)
|
||||
|
|
|
@ -73,6 +73,11 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
add_definitions(-DDEV_BUILD)
|
||||
endif ()
|
||||
|
||||
if (DEPLOY_PACKAGE)
|
||||
# for deployed packages always grab the serverless content
|
||||
set(DOWNLOAD_SERVERLESS_CONTENT ON)
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
set(DMG_SUBFOLDER_NAME "${BUILD_ORGANIZATION}")
|
||||
|
||||
|
|
|
@ -48,3 +48,4 @@ set(UNINSTALLER_HEADER_IMAGE "@UNINSTALLER_HEADER_IMAGE@")
|
|||
set(ADD_REMOVE_ICON_PATH "@ADD_REMOVE_ICON_PATH@")
|
||||
set(SERVER_COMPONENT_CONDITIONAL "@SERVER_COMPONENT_CONDITIONAL@")
|
||||
set(CLIENT_COMPONENT_CONDITIONAL "@CLIENT_COMPONENT_CONDITIONAL@")
|
||||
set(INSTALLER_TYPE "@INSTALLER_TYPE@")
|
||||
|
|
|
@ -710,11 +710,9 @@ Function PostInstallOptionsPage
|
|||
!insertmacro SetInstallOption $ServerStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED}
|
||||
${EndIf}
|
||||
|
||||
${If} @SERVER_COMPONENT_CONDITIONAL@
|
||||
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Perform a clean install (Delete older settings and content)"
|
||||
Pop $CleanInstallCheckbox
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
${EndIf}
|
||||
${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
|
||||
|
@ -809,10 +807,8 @@ Function ReadPostInstallOptions
|
|||
${NSD_GetState} $LaunchClientNowCheckbox $LaunchClientNowState
|
||||
${EndIf}
|
||||
|
||||
${If} @CLIENT_COMPONENT_CONDITIONAL@
|
||||
; check if the user asked for a clean install
|
||||
${NSD_GetState} $CleanInstallCheckbox $CleanInstallState
|
||||
${EndIf}
|
||||
; check if the user asked for a clean install
|
||||
${NSD_GetState} $CleanInstallCheckbox $CleanInstallState
|
||||
FunctionEnd
|
||||
|
||||
Function HandlePostInstallOptions
|
||||
|
@ -856,13 +852,23 @@ Function HandlePostInstallOptions
|
|||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
${If} @CLIENT_COMPONENT_CONDITIONAL@
|
||||
; check if the user asked for a clean install
|
||||
${If} $CleanInstallState == ${BST_CHECKED}
|
||||
SetShellVarContext current
|
||||
RMDir /r "$APPDATA\@BUILD_ORGANIZATION@"
|
||||
RMDir /r "$LOCALAPPDATA\@BUILD_ORGANIZATION@"
|
||||
; check if the user asked for a clean install
|
||||
${If} $CleanInstallState == ${BST_CHECKED}
|
||||
SetShellVarContext current
|
||||
|
||||
${If} @SERVER_COMPONENT_CONDITIONAL@
|
||||
RMDir /r "$APPDATA\@BUILD_ORGANIZATION@\Server Console"
|
||||
RMDir /r "$APPDATA\@BUILD_ORGANIZATION@\assignment-client"
|
||||
RMDir /r "$APPDATA\@BUILD_ORGANIZATION@\domain-server"
|
||||
Delete "$APPDATA\@BUILD_ORGANIZATION@\domain-server.json"
|
||||
${EndIf}
|
||||
|
||||
${If} @CLIENT_COMPONENT_CONDITIONAL@
|
||||
Delete "$APPDATA\@BUILD_ORGANIZATION@\Interface\AccountInfo.bin"
|
||||
Delete "$APPDATA\@BUILD_ORGANIZATION@\Interface.json"
|
||||
${EndIf}
|
||||
|
||||
RMDir /r "$LOCALAPPDATA\@BUILD_ORGANIZATION@"
|
||||
${EndIf}
|
||||
|
||||
${If} @PR_BUILD@ == 1
|
||||
|
@ -976,6 +982,13 @@ Section "-Core installation"
|
|||
;Store installation folder
|
||||
WriteRegStr HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
|
||||
|
||||
;Write some information about this install to the installation folder
|
||||
FileOpen $0 "$INSTDIR\installer.ini" w
|
||||
FileWrite $0 "type=@INSTALLER_TYPE@$\r$\n"
|
||||
FileWrite $0 "campaign=$CampaignName$\r$\n"
|
||||
FileWrite $0 "exepath=$EXEPATH$\r$\n"
|
||||
FileClose $0
|
||||
|
||||
;Package the signed uninstaller produced by the inner loop
|
||||
!ifndef INNER
|
||||
; this packages the signed uninstaller
|
||||
|
|
|
@ -1042,41 +1042,7 @@ void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> mess
|
|||
|
||||
bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) {
|
||||
auto nodeAData = static_cast<DomainServerNodeData*>(nodeA->getLinkedData());
|
||||
auto nodeBData = static_cast<DomainServerNodeData*>(nodeB->getLinkedData());
|
||||
|
||||
// if we have no linked data for node A then B can't possibly be in the interest set
|
||||
if (!nodeAData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// first check if the general interest set A contains the type for B
|
||||
if (nodeAData->getNodeInterestSet().contains(nodeB->getType())) {
|
||||
// given that there is a match in the general interest set, do any special checks
|
||||
|
||||
// (1/19/17) Agents only need to connect to Entity Script Servers to perform administrative tasks
|
||||
// related to entity server scripts. Only agents with rez permissions should be doing that, so
|
||||
// if the agent does not have those permissions, we do not want them and the server to incur the
|
||||
// overhead of connecting to one another. Additionally we exclude agents that do not care about the
|
||||
// Entity Script Server and won't attempt to connect to it.
|
||||
|
||||
bool isAgentWithoutRights = nodeA->getType() == NodeType::Agent
|
||||
&& nodeB->getType() == NodeType::EntityScriptServer
|
||||
&& !nodeA->getCanRez() && !nodeA->getCanRezTmp()
|
||||
&& !nodeA->getCanRezCertified() && !nodeA->getCanRezTmpCertified();
|
||||
|
||||
if (isAgentWithoutRights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isScriptServerForIneffectiveAgent =
|
||||
(nodeA->getType() == NodeType::EntityScriptServer && nodeB->getType() == NodeType::Agent)
|
||||
&& ((nodeBData && !nodeBData->getNodeInterestSet().contains(NodeType::EntityScriptServer))
|
||||
|| (!nodeB->getCanRez() && !nodeB->getCanRezTmp() && !nodeB->getCanRezCertified() && !nodeB->getCanRezTmpCertified()));
|
||||
|
||||
return !isScriptServerForIneffectiveAgent;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return nodeAData && nodeAData->getNodeInterestSet().contains(nodeB->getType());
|
||||
}
|
||||
|
||||
unsigned int DomainServer::countConnectedUsers() {
|
||||
|
@ -3476,4 +3442,4 @@ void DomainServer::handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMes
|
|||
if (node->getCanReplaceContent()) {
|
||||
handleOctreeFileReplacement(message->readAll());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ endif()
|
|||
list(APPEND GENERATE_QRC_DEPENDS ${RESOURCES_RCC})
|
||||
add_custom_target(resources ALL DEPENDS ${GENERATE_QRC_DEPENDS})
|
||||
|
||||
|
||||
# set a default root dir for each of our optional externals if it was not passed
|
||||
set(OPTIONAL_EXTERNALS "LeapMotion")
|
||||
|
||||
|
@ -314,35 +313,41 @@ if (APPLE)
|
|||
)
|
||||
|
||||
set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources")
|
||||
|
||||
set(RESOURCES_DEV_DIR "$<TARGET_FILE_DIR:${TARGET_NAME}>/../Resources")
|
||||
|
||||
# copy script files beside the executable
|
||||
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/scripts"
|
||||
"$<TARGET_FILE_DIR:${TARGET_NAME}>/../Resources/scripts"
|
||||
"${RESOURCES_DEV_DIR}/scripts"
|
||||
)
|
||||
|
||||
# call the fixup_interface macro to add required bundling commands for installation
|
||||
fixup_interface()
|
||||
|
||||
else()
|
||||
set(INTERFACE_EXEC_DIR "$<TARGET_FILE_DIR:${TARGET_NAME}>")
|
||||
set(RESOURCES_DEV_DIR "${INTERFACE_EXEC_DIR}/resources")
|
||||
|
||||
# copy the resources files beside the executable
|
||||
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
|
||||
"${RESOURCES_RCC}"
|
||||
"$<TARGET_FILE_DIR:interface>"
|
||||
"${RESOURCES_RCC}"
|
||||
"${INTERFACE_EXEC_DIR}"
|
||||
# FIXME, the edit script code loads HTML from the scripts folder
|
||||
# which in turn relies on CSS that refers to the fonts. In theory
|
||||
# we should be able to modify the CSS to reference the QRC path to
|
||||
# the ttf files, but doing so generates a CORS policy violation,
|
||||
# so we have to retain a copy of the fonts outside of the resources binary
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${PROJECT_SOURCE_DIR}/resources/fonts"
|
||||
"$<TARGET_FILE_DIR:${TARGET_NAME}>/resources/fonts"
|
||||
"${PROJECT_SOURCE_DIR}/resources/fonts"
|
||||
"${RESOURCES_DEV_DIR}/fonts"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/scripts"
|
||||
"$<TARGET_FILE_DIR:${TARGET_NAME}>/scripts"
|
||||
"${CMAKE_SOURCE_DIR}/scripts"
|
||||
"${INTERFACE_EXEC_DIR}/scripts"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
|
||||
"${PROJECT_SOURCE_DIR}/resources/serverless/tutorial.json"
|
||||
"${RESOURCES_DEV_DIR}/serverless/tutorial.json"
|
||||
)
|
||||
|
||||
# link target to external libraries
|
||||
|
@ -368,7 +373,6 @@ else()
|
|||
endif()
|
||||
|
||||
if (SCRIPTS_INSTALL_DIR)
|
||||
|
||||
# setup install of scripts beside interface executable
|
||||
install(
|
||||
DIRECTORY "${CMAKE_SOURCE_DIR}/scripts/"
|
||||
|
@ -377,6 +381,19 @@ if (SCRIPTS_INSTALL_DIR)
|
|||
)
|
||||
endif()
|
||||
|
||||
if (DOWNLOAD_SERVERLESS_CONTENT)
|
||||
add_dependency_external_projects(serverless-content)
|
||||
|
||||
ExternalProject_Get_Property(serverless-content SOURCE_DIR)
|
||||
|
||||
# for dev builds, copy the serverless content to the resources folder
|
||||
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${SOURCE_DIR}"
|
||||
"${RESOURCES_DEV_DIR}/serverless"
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/resources/qml\"")
|
||||
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
{
|
||||
"name": "Keyboard/Mouse to Actions",
|
||||
"channels": [
|
||||
|
||||
{ "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.A", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.D", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.E", "when": "Keyboard.Shift", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.05 } ] },
|
||||
{ "from": "Keyboard.C", "when": "Keyboard.Shift", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.05 } ] },
|
||||
{ "from": "Keyboard.S", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" },
|
||||
{ "from": "Keyboard.W", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" },
|
||||
{ "from": "Keyboard.E", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.Q", "to": "Actions.LATERAL_LEFT" },
|
||||
|
||||
|
||||
{ "comment" : "Mouse turn need to be small continuous increments",
|
||||
|
@ -44,9 +39,24 @@
|
|||
]
|
||||
},
|
||||
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A", "Keyboard.Left" ],
|
||||
["Keyboard.D", "Keyboard.Right"]
|
||||
["Keyboard.Left" ],
|
||||
["Keyboard.Right"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.InHMD", "Application.SnapTurn", "!Keyboard.Shift"],
|
||||
"to": "Actions.StepYaw",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "pulse", "interval": 0.5, "resetOnZero": true },
|
||||
{ "type": "scale", "scale": 22.5 }
|
||||
]
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A"],
|
||||
["Keyboard.D"]
|
||||
]
|
||||
},
|
||||
"when": [ "Application.InHMD", "Application.SnapTurn" ],
|
||||
|
@ -59,26 +69,39 @@
|
|||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"],
|
||||
["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"]
|
||||
["Keyboard.Left"],
|
||||
["Keyboard.Right"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraFirstPerson", "!Keyboard.Shift"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Left"],
|
||||
["Keyboard.Right"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraThirdPerson", "!Keyboard.Shift"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A", "Keyboard.TouchpadLeft"],
|
||||
["Keyboard.D", "Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraFirstPerson",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"],
|
||||
["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"]
|
||||
["Keyboard.A", "Keyboard.TouchpadLeft"],
|
||||
["Keyboard.D", "Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraThirdPerson",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
{ "from": { "makeAxis" : [ ["Keyboard.A"], ["Keyboard.D"] ] },
|
||||
"when": "Application.CameraFSM",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
|
||||
"when": "Keyboard.RightMouseButton",
|
||||
"to": "Actions.Yaw",
|
||||
|
@ -90,14 +113,10 @@
|
|||
|
||||
{ "from": "Keyboard.W", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.S", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" },
|
||||
{ "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" },
|
||||
{ "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" },
|
||||
{ "from": "Keyboard.Left", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.Right", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.Down", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" },
|
||||
{ "from": "Keyboard.Up", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" },
|
||||
{ "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.Down", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
|
@ -128,7 +147,7 @@
|
|||
{ "from": "Keyboard.MouseWheelLeft", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.02 } ]},
|
||||
{ "from": "Keyboard.MouseWheelRight", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.02 } ]},
|
||||
|
||||
{ "from": "Keyboard.Space", "to": "Actions.SHIFT" },
|
||||
{ "from": "Keyboard.Space", "to": "Actions.VERTICAL_UP" },
|
||||
{ "from": "Keyboard.R", "to": "Actions.ACTION1" },
|
||||
{ "from": "Keyboard.T", "to": "Actions.ACTION2" },
|
||||
{ "from": "Keyboard.Tab", "to": "Actions.ContextMenu" }
|
||||
|
|
|
@ -5,8 +5,25 @@
|
|||
|
||||
{ "from": "TouchscreenVirtualPad.LX", "when": "!Application.CameraIndependent", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Actions.TranslateX" },
|
||||
|
||||
{ "from": "TouchscreenVirtualPad.RX", "when": "!Application.CameraIndependent", "filters": [ {"type": "deadZone", "min": 0.05} , "invert" ], "to": "Actions.Yaw" },
|
||||
{ "from": "TouchscreenVirtualPad.JUMP_BUTTON_PRESS", "when": "!Application.CameraIndependent", "to": "Actions.VERTICAL_UP" },
|
||||
|
||||
{ "from": "TouchscreenVirtualPad.RX", "when": "!Application.CameraIndependent",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.000 },
|
||||
{ "type": "scale", "scale": 0.06 },
|
||||
"invert"
|
||||
],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": "TouchscreenVirtualPad.RY", "when": "!Application.CameraIndependent",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.000 },
|
||||
{ "type": "scale", "scale": 0.06 },
|
||||
"invert"
|
||||
],
|
||||
"to": "Actions.Pitch"
|
||||
}
|
||||
|
||||
{ "from": "TouchscreenVirtualPad.RY", "when": "!Application.CameraIndependent", "filters": [ {"type": "deadZone", "min": 0.05}, "invert" ], "to": "Actions.Pitch" }
|
||||
]
|
||||
}
|
||||
|
|
Binary file not shown.
Before ![]() (image error) Size: 215 KiB After ![]() (image error) Size: 241 KiB ![]() ![]() |
BIN
interface/resources/images/fly.png
Normal file
BIN
interface/resources/images/fly.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 11 KiB |
|
@ -67,6 +67,10 @@ Item {
|
|||
fill: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
QmlHifi.WindowHeader {
|
||||
id: header
|
||||
iconSource: "../../../icons/goto-i.svg"
|
||||
|
|
|
@ -163,10 +163,18 @@ TextField {
|
|||
text: textField.label
|
||||
colorScheme: textField.colorScheme
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Binding on anchors.right {
|
||||
when: parent.right
|
||||
value: parent.right
|
||||
}
|
||||
Binding on wrapMode {
|
||||
when: parent.right
|
||||
value: Text.WordWrap
|
||||
}
|
||||
|
||||
anchors.bottom: parent.top
|
||||
anchors.bottomMargin: 3
|
||||
wrapMode: Text.WordWrap
|
||||
visible: label != ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,9 +122,21 @@ Item {
|
|||
newViewRequestedCallback(request)
|
||||
}
|
||||
|
||||
// Prior to 5.10, the WebEngineView loading property is true during initial page loading and then stays false
|
||||
// as in-page javascript adds more html content. However, in 5.10 there is a bug such that adding html turns
|
||||
// loading true, and never turns it false again. safeLoading provides a workaround, but it should be removed
|
||||
// when QT fixes this.
|
||||
property bool safeLoading: false
|
||||
property bool loadingLatched: false
|
||||
property var loadingRequest: null
|
||||
onLoadingChanged: {
|
||||
flick.onLoadingChanged(loadRequest)
|
||||
loadingChangedCallback(loadRequest)
|
||||
webViewCore.loadingRequest = loadRequest;
|
||||
webViewCore.safeLoading = webViewCore.loading && !loadingLatched;
|
||||
webViewCore.loadingLatched |= webViewCore.loading;
|
||||
}
|
||||
onSafeLoadingChanged: {
|
||||
flick.onLoadingChanged(webViewCore.loadingRequest)
|
||||
loadingChangedCallback(webViewCore.loadingRequest)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +145,7 @@ Item {
|
|||
x: flick.width/2 - width/2
|
||||
y: flick.height/2 - height/2
|
||||
source: "../../icons/loader-snake-64-w.gif"
|
||||
visible: webViewCore.loading && /^(http.*|)$/i.test(webViewCore.url.toString())
|
||||
visible: webViewCore.safeLoading && /^(http.*|)$/i.test(webViewCore.url.toString())
|
||||
playing: visible
|
||||
z: 10000
|
||||
}
|
||||
|
|
|
@ -270,7 +270,11 @@ ModalWindow {
|
|||
onTriggered: {
|
||||
root.result = null;
|
||||
root.canceled();
|
||||
root.destroy();
|
||||
// FIXME we are leaking memory to avoid a crash
|
||||
// root.destroy();
|
||||
|
||||
root.disableFade = true
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,7 +296,11 @@ ModalWindow {
|
|||
}
|
||||
root.result = JSON.stringify(result);
|
||||
root.selected(root.result);
|
||||
root.destroy();
|
||||
// FIXME we are leaking memory to avoid a crash
|
||||
// root.destroy();
|
||||
|
||||
root.disableFade = true
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,7 +169,11 @@ ModalWindow {
|
|||
shortcut: Qt.Key_Escape
|
||||
onTriggered: {
|
||||
root.canceled();
|
||||
root.destroy();
|
||||
// FIXME we are leaking memory to avoid a crash
|
||||
// root.destroy();
|
||||
|
||||
root.disableFade = true
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
Action {
|
||||
|
@ -179,7 +183,11 @@ ModalWindow {
|
|||
onTriggered: {
|
||||
root.result = items ? comboBox.currentText : textResult.text
|
||||
root.selected(root.result);
|
||||
root.destroy();
|
||||
// FIXME we are leaking memory to avoid a crash
|
||||
// root.destroy();
|
||||
|
||||
root.disableFade = true
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,10 @@ Item {
|
|||
width: parent ? parent.width : 0
|
||||
height: parent ? parent.height : 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: android.color.gradientTop }
|
||||
GradientStop { position: 1.0; color: android.color.gradientBottom }
|
||||
|
|
|
@ -118,7 +118,7 @@ Item {
|
|||
tabletRoot.playButtonClickSound();
|
||||
}*/
|
||||
}
|
||||
onEntered: {
|
||||
onPressed: {
|
||||
button.isEntered = true;
|
||||
button.entered();
|
||||
if (button.isActive) {
|
||||
|
@ -127,7 +127,7 @@ Item {
|
|||
button.state = "hover state";
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
onReleased: {
|
||||
button.isEntered = false;
|
||||
button.exited()
|
||||
if (button.isActive) {
|
||||
|
|
|
@ -24,6 +24,7 @@ Windows.ScrollingWindow {
|
|||
objectName: "AssetServer"
|
||||
title: "Asset Browser"
|
||||
resizable: true
|
||||
opacity: parent.opacity
|
||||
destroyOnHidden: true
|
||||
implicitWidth: 384; implicitHeight: 640
|
||||
minSize: Qt.vector2d(200, 300)
|
||||
|
@ -38,6 +39,7 @@ Windows.ScrollingWindow {
|
|||
property var assetMappingsModel: Assets.mappingModel;
|
||||
property var currentDirectory;
|
||||
property var selectedItemCount: treeView.selection.selectedIndexes.length;
|
||||
property int updatesCount: 0; // this is used for notifying model-dependent bindings about model updates
|
||||
|
||||
Settings {
|
||||
category: "Overlay.AssetServer"
|
||||
|
@ -50,6 +52,9 @@ Windows.ScrollingWindow {
|
|||
ApplicationInterface.uploadRequest.connect(uploadClicked);
|
||||
assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError);
|
||||
assetMappingsModel.autoRefreshEnabled = true;
|
||||
assetMappingsModel.updated.connect(function() {
|
||||
++updatesCount;
|
||||
});
|
||||
|
||||
reload();
|
||||
}
|
||||
|
@ -57,7 +62,7 @@ Windows.ScrollingWindow {
|
|||
Component.onDestruction: {
|
||||
assetMappingsModel.autoRefreshEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
function letterbox(headerGlyph, headerText, message) {
|
||||
letterboxMessage.headerGlyph = headerGlyph;
|
||||
letterboxMessage.headerText = headerText;
|
||||
|
@ -144,7 +149,7 @@ Windows.ScrollingWindow {
|
|||
|
||||
function canAddToWorld(path) {
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i];
|
||||
|
||||
|
||||
if (selectedItemCount > 1) {
|
||||
return false;
|
||||
}
|
||||
|
@ -153,8 +158,8 @@ Windows.ScrollingWindow {
|
|||
return total | new RegExp(current).test(path);
|
||||
}, false);
|
||||
}
|
||||
|
||||
function canRename() {
|
||||
|
||||
function canRename() {
|
||||
if (treeView.selection.hasSelection && selectedItemCount == 1) {
|
||||
return true;
|
||||
} else {
|
||||
|
@ -198,7 +203,7 @@ Windows.ScrollingWindow {
|
|||
var SHAPE_TYPE_STATIC_MESH = 3;
|
||||
var SHAPE_TYPE_BOX = 4;
|
||||
var SHAPE_TYPE_SPHERE = 5;
|
||||
|
||||
|
||||
var SHAPE_TYPES = [];
|
||||
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
|
||||
|
@ -206,7 +211,7 @@ Windows.ScrollingWindow {
|
|||
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
||||
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
|
||||
|
||||
|
||||
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_SIMPLE_COMPOUND;
|
||||
var DYNAMIC_DEFAULT = false;
|
||||
var prompt = desktop.customInputDialog({
|
||||
|
@ -348,14 +353,14 @@ Windows.ScrollingWindow {
|
|||
}
|
||||
function deleteFile(index) {
|
||||
var paths = [];
|
||||
|
||||
|
||||
if (!index) {
|
||||
for (var i = 0; i < selectedItemCount; ++i) {
|
||||
index = treeView.selection.selectedIndexes[i];
|
||||
paths[i] = assetProxyModel.data(index, 0x100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!paths) {
|
||||
return;
|
||||
}
|
||||
|
@ -364,13 +369,13 @@ Windows.ScrollingWindow {
|
|||
var items = selectedItemCount.toString();
|
||||
var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101);
|
||||
var typeString = isFolder ? 'folder' : 'file';
|
||||
|
||||
|
||||
if (selectedItemCount > 1) {
|
||||
modalMessage = "You are about to delete " + items + " items \nDo you want to continue?";
|
||||
} else {
|
||||
modalMessage = "You are about to delete the following " + typeString + ":\n" + paths + "\nDo you want to continue?";
|
||||
}
|
||||
|
||||
|
||||
var object = desktop.messageBox({
|
||||
icon: hifi.icons.question,
|
||||
buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No,
|
||||
|
@ -475,11 +480,11 @@ Windows.ScrollingWindow {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
width: pane.contentWidth
|
||||
height: pane.height
|
||||
|
||||
|
||||
// The letterbox used for popup messages
|
||||
LetterboxMessage {
|
||||
id: letterboxMessage;
|
||||
|
@ -541,7 +546,7 @@ Windows.ScrollingWindow {
|
|||
anchors.margins: hifi.dimensions.contentMargin.x + 2 // Extra for border
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
|
||||
treeModel: assetProxyModel
|
||||
selectionMode: SelectionMode.ExtendedSelection
|
||||
headerVisible: true
|
||||
|
@ -561,9 +566,13 @@ Windows.ScrollingWindow {
|
|||
id: bakedColumn
|
||||
title: "Use Baked?"
|
||||
role: "baked"
|
||||
width: 100
|
||||
width: 170
|
||||
}
|
||||
|
||||
|
||||
onSortIndicatorOrderChanged: {
|
||||
Assets.sortProxyModel(sortIndicatorColumn, sortIndicatorOrder);
|
||||
}
|
||||
|
||||
itemDelegate: Loader {
|
||||
id: itemDelegateLoader
|
||||
|
||||
|
@ -599,7 +608,7 @@ Windows.ScrollingWindow {
|
|||
|
||||
}
|
||||
sourceComponent: getComponent()
|
||||
|
||||
|
||||
Component {
|
||||
id: labelComponent
|
||||
FiraSansSemiBold {
|
||||
|
@ -608,15 +617,15 @@ Windows.ScrollingWindow {
|
|||
color: colorScheme == hifi.colorSchemes.light
|
||||
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
|
||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||
|
||||
|
||||
horizontalAlignment: styleData.column === 1 ? TextInput.AlignHCenter : TextInput.AlignLeft
|
||||
|
||||
|
||||
elide: Text.ElideMiddle
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: true
|
||||
|
||||
|
@ -638,7 +647,7 @@ Windows.ScrollingWindow {
|
|||
color: colorScheme == hifi.colorSchemes.light
|
||||
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
|
||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||
|
||||
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
|
||||
|
@ -725,7 +734,7 @@ Windows.ScrollingWindow {
|
|||
size: hifi.fontSizes.tableText
|
||||
color: colorScheme == hifi.colorSchemes.light ? hifi.colors.black : hifi.colors.lightGrayText
|
||||
}
|
||||
|
||||
|
||||
Timer {
|
||||
id: showTimer
|
||||
interval: 1000
|
||||
|
@ -744,7 +753,7 @@ Windows.ScrollingWindow {
|
|||
treeLabelToolTip.visible = false;
|
||||
}
|
||||
}// End_OF( treeLabelToolTip )
|
||||
|
||||
|
||||
MouseArea {
|
||||
propagateComposedEvents: true
|
||||
anchors.fill: parent
|
||||
|
@ -802,7 +811,7 @@ Windows.ScrollingWindow {
|
|||
anchors.left: treeView.left
|
||||
anchors.right: treeView.right
|
||||
anchors.bottom: uploadSection.top
|
||||
|
||||
|
||||
RalewayRegular {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
|
@ -846,13 +855,18 @@ Windows.ScrollingWindow {
|
|||
|
||||
checked = Qt.binding(isChecked);
|
||||
}
|
||||
|
||||
function getStatus() {
|
||||
// kind of hack for ensuring getStatus() will be re-evaluated on updatesCount changes
|
||||
return updatesCount, assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
||||
}
|
||||
|
||||
function isEnabled() {
|
||||
if (!treeView.selection.hasSelection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
||||
var status = getStatus();
|
||||
if (status === "--") {
|
||||
return false;
|
||||
}
|
||||
|
@ -870,18 +884,18 @@ Windows.ScrollingWindow {
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
function isChecked() {
|
||||
if (!treeView.selection.hasSelection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
||||
var status = getStatus();
|
||||
return isEnabled() && status !== "Not Baked";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: infoGlyph.size;
|
||||
|
@ -905,7 +919,7 @@ Windows.ScrollingWindow {
|
|||
"What is baking?",
|
||||
"Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}// End_OF( infoRow )
|
||||
|
||||
HifiControls.ContentSection {
|
||||
|
|
|
@ -96,6 +96,7 @@ Rectangle {
|
|||
root.activeView = "checkoutFailure";
|
||||
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned, result.message);
|
||||
} else {
|
||||
root.certificateId = result.data.certificate_id;
|
||||
root.itemHref = result.data.download_url;
|
||||
if (result.data.categories.indexOf("Wearables") > -1) {
|
||||
root.itemType = "wearable";
|
||||
|
@ -188,7 +189,7 @@ Rectangle {
|
|||
onItemHrefChanged: {
|
||||
if (root.itemHref.indexOf(".fst") > -1) {
|
||||
root.itemType = "avatar";
|
||||
} else if (root.itemHref.indexOf('.json.gz') > -1) {
|
||||
} else if (root.itemHref.indexOf('.json.gz') > -1 || root.itemHref.indexOf('.content.zip') > -1) {
|
||||
root.itemType = "contentSet";
|
||||
} else if (root.itemHref.indexOf('.app.json') > -1) {
|
||||
root.itemType = "app";
|
||||
|
@ -772,7 +773,7 @@ Rectangle {
|
|||
lightboxPopup.button1text = "CANCEL";
|
||||
lightboxPopup.button1method = "root.visible = false;"
|
||||
lightboxPopup.button2text = "CONFIRM";
|
||||
lightboxPopup.button2method = "Commerce.replaceContentSet('" + root.itemHref + "');" +
|
||||
lightboxPopup.button2method = "Commerce.replaceContentSet('" + root.itemHref + "', '" + root.certificateId + "');" +
|
||||
"root.visible = false;rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start();" +
|
||||
"UserActivityLogger.commerceEntityRezzed('" + root.itemId + "', 'checkout', '" + root.itemType + "');";
|
||||
lightboxPopup.visible = true;
|
||||
|
@ -1160,14 +1161,14 @@ Rectangle {
|
|||
function authSuccessStep() {
|
||||
if (!root.debugCheckoutSuccess) {
|
||||
root.activeView = "checkoutMain";
|
||||
} else {
|
||||
root.activeView = "checkoutSuccess";
|
||||
root.ownershipStatusReceived = false;
|
||||
Commerce.alreadyOwned(root.itemId);
|
||||
root.availableUpdatesReceived = false;
|
||||
Commerce.getAvailableUpdates(root.itemId);
|
||||
root.balanceReceived = false;
|
||||
Commerce.balance();
|
||||
} else {
|
||||
root.activeView = "checkoutSuccess";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -564,7 +564,7 @@ Item {
|
|||
onClicked: {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
if (root.itemType === "contentSet") {
|
||||
sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref});
|
||||
sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref, certID: root.certificateId});
|
||||
} else if (root.itemType === "avatar") {
|
||||
sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref});
|
||||
} else if (root.itemType === "app") {
|
||||
|
|
|
@ -486,7 +486,7 @@ Rectangle {
|
|||
lightboxPopup.button1text = "CANCEL";
|
||||
lightboxPopup.button1method = "root.visible = false;"
|
||||
lightboxPopup.button2text = "CONFIRM";
|
||||
lightboxPopup.button2method = "Commerce.replaceContentSet('" + msg.itemHref + "'); root.visible = false;";
|
||||
lightboxPopup.button2method = "Commerce.replaceContentSet('" + msg.itemHref + "', '" + msg.certID + "'); root.visible = false;";
|
||||
lightboxPopup.visible = true;
|
||||
} else if (msg.method === "showChangeAvatarLightbox") {
|
||||
lightboxPopup.titleText = "Change Avatar";
|
||||
|
@ -792,7 +792,7 @@ Rectangle {
|
|||
currentItemType = "avatar";
|
||||
} else if (currentCategories.indexOf("Wearables") > -1) {
|
||||
currentItemType = "wearable";
|
||||
} else if (currentRootFileUrl.endsWith('.json.gz')) {
|
||||
} else if (currentRootFileUrl.endsWith('.json.gz') || currentRootFileUrl.endsWith('.content.zip')) {
|
||||
currentItemType = "contentSet";
|
||||
} else if (currentRootFileUrl.endsWith('.app.json')) {
|
||||
currentItemType = "app";
|
||||
|
|
|
@ -24,6 +24,18 @@ Item {
|
|||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
|
||||
// This will cause a bug -- if you bring up passphrase selection in HUD mode while
|
||||
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
|
||||
// HMD preview will stay off.
|
||||
// TODO: Fix this unlikely bug
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
sendSignalToWallet({method: 'disableHmdPreview'});
|
||||
} else {
|
||||
sendSignalToWallet({method: 'maybeEnableHmdPreview'});
|
||||
}
|
||||
}
|
||||
|
||||
// Username Text
|
||||
RalewayRegular {
|
||||
|
|
|
@ -68,10 +68,6 @@ Item {
|
|||
propagateComposedEvents: false;
|
||||
hoverEnabled: true;
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
sendSignalToParent({method: 'maybeEnableHmdPreview'});
|
||||
}
|
||||
|
||||
// This will cause a bug -- if you bring up passphrase selection in HUD mode while
|
||||
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
|
||||
|
|
|
@ -61,9 +61,6 @@ Item {
|
|||
if (root.shouldImmediatelyFocus) {
|
||||
focusFirstTextField();
|
||||
}
|
||||
sendMessageToLightbox({method: 'disableHmdPreview'});
|
||||
} else {
|
||||
sendMessageToLightbox({method: 'maybeEnableHmdPreview'});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,17 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
// This will cause a bug -- if you bring up security image selection in HUD mode while
|
||||
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
|
||||
// HMD preview will stay off.
|
||||
// TODO: Fix this unlikely bug
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
sendSignalToWallet({method: 'disableHmdPreview'});
|
||||
} else {
|
||||
sendSignalToWallet({method: 'maybeEnableHmdPreview'});
|
||||
}
|
||||
}
|
||||
|
||||
// Security Image
|
||||
Item {
|
||||
|
|
|
@ -25,18 +25,6 @@ Item {
|
|||
|
||||
id: root;
|
||||
property alias currentIndex: securityImageGrid.currentIndex;
|
||||
|
||||
// This will cause a bug -- if you bring up security image selection in HUD mode while
|
||||
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
|
||||
// HMD preview will stay off.
|
||||
// TODO: Fix this unlikely bug
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
sendSignalToWallet({method: 'disableHmdPreview'});
|
||||
} else {
|
||||
sendSignalToWallet({method: 'maybeEnableHmdPreview'});
|
||||
}
|
||||
}
|
||||
|
||||
SecurityImageModel {
|
||||
id: gridModel;
|
||||
|
|
|
@ -237,7 +237,7 @@ Rectangle {
|
|||
} else {
|
||||
sendToScript(msg);
|
||||
}
|
||||
} else if (msg.method === 'maybeEnableHmdPreview') {
|
||||
} else {
|
||||
sendToScript(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,12 @@ Item {
|
|||
var currentStepNumber = root.activeView.substring(5);
|
||||
UserActivityLogger.commerceWalletSetupProgress(timestamp, root.setupAttemptID,
|
||||
Math.round((timestamp - root.startingTimestamp)/1000), currentStepNumber, root.setupStepNames[currentStepNumber - 1]);
|
||||
|
||||
if (root.activeView === "step_2" || root.activeView === "step_3") {
|
||||
sendSignalToWallet({method: 'disableHmdPreview'});
|
||||
} else {
|
||||
sendSignalToWallet({method: 'maybeEnableHmdPreview'});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -441,7 +447,7 @@ Item {
|
|||
}
|
||||
Item {
|
||||
id: choosePassphraseContainer;
|
||||
visible: root.hasShownSecurityImageTip && root.activeView === "step_3";
|
||||
visible: root.activeView === "step_3";
|
||||
// Anchors
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.topMargin: 30;
|
||||
|
@ -451,10 +457,7 @@ Item {
|
|||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
sendSignalToWallet({method: 'disableHmdPreview'});
|
||||
Commerce.getWalletAuthenticatedStatus();
|
||||
} else {
|
||||
sendSignalToWallet({method: 'maybeEnableHmdPreview'});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ ScrollingWindow {
|
|||
resizable: true
|
||||
destroyOnHidden: false
|
||||
implicitWidth: 424
|
||||
opacity: parent.opacity
|
||||
implicitHeight: isHMD ? 695 : 728
|
||||
minSize: Qt.vector2d(424, 300)
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ Rectangle {
|
|||
property var assetMappingsModel: Assets.mappingModel;
|
||||
property var currentDirectory;
|
||||
property var selectedItemCount: treeView.selection.selectedIndexes.length;
|
||||
property int updatesCount: 0; // this is used for notifying model-dependent bindings about model updates
|
||||
|
||||
Settings {
|
||||
category: "Overlay.AssetServer"
|
||||
|
@ -51,6 +52,9 @@ Rectangle {
|
|||
ApplicationInterface.uploadRequest.connect(uploadClicked);
|
||||
assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError);
|
||||
assetMappingsModel.autoRefreshEnabled = true;
|
||||
assetMappingsModel.updated.connect(function() {
|
||||
++updatesCount;
|
||||
});
|
||||
|
||||
reload();
|
||||
}
|
||||
|
@ -58,7 +62,7 @@ Rectangle {
|
|||
Component.onDestruction: {
|
||||
assetMappingsModel.autoRefreshEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
function letterbox(headerGlyph, headerText, message) {
|
||||
letterboxMessage.headerGlyph = headerGlyph;
|
||||
letterboxMessage.headerText = headerText;
|
||||
|
@ -66,7 +70,7 @@ Rectangle {
|
|||
letterboxMessage.visible = true;
|
||||
letterboxMessage.popupRadius = 0;
|
||||
}
|
||||
|
||||
|
||||
function errorMessageBox(message) {
|
||||
return tabletRoot.messageBox({
|
||||
icon: hifi.icons.warning,
|
||||
|
@ -145,7 +149,7 @@ Rectangle {
|
|||
|
||||
function canAddToWorld(path) {
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i];
|
||||
|
||||
|
||||
if (selectedItemCount > 1) {
|
||||
return false;
|
||||
}
|
||||
|
@ -154,8 +158,8 @@ Rectangle {
|
|||
return total | new RegExp(current).test(path);
|
||||
}, false);
|
||||
}
|
||||
|
||||
function canRename() {
|
||||
|
||||
function canRename() {
|
||||
if (treeView.selection.hasSelection && selectedItemCount == 1) {
|
||||
return true;
|
||||
} else {
|
||||
|
@ -199,7 +203,7 @@ Rectangle {
|
|||
var SHAPE_TYPE_STATIC_MESH = 3;
|
||||
var SHAPE_TYPE_BOX = 4;
|
||||
var SHAPE_TYPE_SPHERE = 5;
|
||||
|
||||
|
||||
var SHAPE_TYPES = [];
|
||||
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
|
||||
|
@ -207,7 +211,7 @@ Rectangle {
|
|||
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
||||
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
|
||||
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
|
||||
|
||||
|
||||
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_SIMPLE_COMPOUND;
|
||||
var DYNAMIC_DEFAULT = false;
|
||||
var prompt = tabletRoot.customInputDialog({
|
||||
|
@ -349,14 +353,14 @@ Rectangle {
|
|||
}
|
||||
function deleteFile(index) {
|
||||
var paths = [];
|
||||
|
||||
|
||||
if (!index) {
|
||||
for (var i = 0; i < selectedItemCount; ++i) {
|
||||
index = treeView.selection.selectedIndexes[i];
|
||||
paths[i] = assetProxyModel.data(index, 0x100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!paths) {
|
||||
return;
|
||||
}
|
||||
|
@ -365,7 +369,7 @@ Rectangle {
|
|||
var items = selectedItemCount.toString();
|
||||
var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101);
|
||||
var typeString = isFolder ? 'folder' : 'file';
|
||||
|
||||
|
||||
if (selectedItemCount > 1) {
|
||||
modalMessage = "You are about to delete " + items + " items \nDo you want to continue?";
|
||||
} else {
|
||||
|
@ -476,7 +480,7 @@ Rectangle {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The letterbox used for popup messages
|
||||
LetterboxMessage {
|
||||
id: letterboxMessage;
|
||||
|
@ -540,7 +544,7 @@ Rectangle {
|
|||
anchors.margins: hifi.dimensions.contentMargin.x + 2 // Extra for border
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
|
||||
treeModel: assetProxyModel
|
||||
selectionMode: SelectionMode.ExtendedSelection
|
||||
headerVisible: true
|
||||
|
@ -560,9 +564,13 @@ Rectangle {
|
|||
id: bakedColumn
|
||||
title: "Use Baked?"
|
||||
role: "baked"
|
||||
width: 100
|
||||
width: 170
|
||||
}
|
||||
|
||||
|
||||
onSortIndicatorOrderChanged: {
|
||||
Assets.sortProxyModel(sortIndicatorColumn, sortIndicatorOrder);
|
||||
}
|
||||
|
||||
itemDelegate: Loader {
|
||||
id: itemDelegateLoader
|
||||
|
||||
|
@ -598,7 +606,7 @@ Rectangle {
|
|||
|
||||
}
|
||||
sourceComponent: getComponent()
|
||||
|
||||
|
||||
Component {
|
||||
id: labelComponent
|
||||
FiraSansSemiBold {
|
||||
|
@ -607,15 +615,15 @@ Rectangle {
|
|||
color: colorScheme == hifi.colorSchemes.light
|
||||
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
|
||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||
|
||||
|
||||
horizontalAlignment: styleData.column === 1 ? TextInput.AlignHCenter : TextInput.AlignLeft
|
||||
|
||||
|
||||
elide: Text.ElideMiddle
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: true
|
||||
|
||||
|
@ -637,7 +645,7 @@ Rectangle {
|
|||
color: colorScheme == hifi.colorSchemes.light
|
||||
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
|
||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||
|
||||
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
|
||||
|
@ -724,7 +732,7 @@ Rectangle {
|
|||
size: hifi.fontSizes.tableText
|
||||
color: colorScheme == hifi.colorSchemes.light ? hifi.colors.black : hifi.colors.lightGrayText
|
||||
}
|
||||
|
||||
|
||||
Timer {
|
||||
id: showTimer
|
||||
interval: 1000
|
||||
|
@ -743,7 +751,7 @@ Rectangle {
|
|||
treeLabelToolTip.visible = false;
|
||||
}
|
||||
}// End_OF( treeLabelToolTip )
|
||||
|
||||
|
||||
MouseArea {
|
||||
propagateComposedEvents: true
|
||||
anchors.fill: parent
|
||||
|
@ -801,7 +809,7 @@ Rectangle {
|
|||
anchors.left: treeView.left
|
||||
anchors.right: treeView.right
|
||||
anchors.bottomMargin: hifi.dimensions.contentSpacing.y
|
||||
|
||||
|
||||
RalewayRegular {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
|
@ -845,13 +853,18 @@ Rectangle {
|
|||
|
||||
checked = Qt.binding(isChecked);
|
||||
}
|
||||
|
||||
|
||||
function getStatus() {
|
||||
// kind of hack for ensuring getStatus() will be re-evaluated on updatesCount changes
|
||||
return updatesCount, assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
||||
}
|
||||
|
||||
function isEnabled() {
|
||||
if (!treeView.selection.hasSelection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
||||
var status = getStatus();
|
||||
if (status === "--") {
|
||||
return false;
|
||||
}
|
||||
|
@ -869,18 +882,18 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
function isChecked() {
|
||||
if (!treeView.selection.hasSelection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105);
|
||||
return isEnabled() && status !== "Not Baked";
|
||||
}
|
||||
var status = getStatus();
|
||||
return isEnabled() && status !== "Not Baked";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: infoGlyph.size;
|
||||
|
@ -904,7 +917,7 @@ Rectangle {
|
|||
"What is baking?",
|
||||
"Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}// End_OF( infoRow )
|
||||
|
||||
HifiControls.TabletContentSection {
|
||||
|
|
|
@ -71,6 +71,14 @@ Rectangle {
|
|||
onAccepted: {
|
||||
newModelDialog.keyboardEnabled = false;
|
||||
}
|
||||
|
||||
onTextChanged : {
|
||||
if (modelURL.text.length === 0){
|
||||
button1.enabled = false;
|
||||
} else {
|
||||
button1.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
@ -200,6 +208,7 @@ Rectangle {
|
|||
id: button1
|
||||
text: qsTr("Add")
|
||||
z: -1
|
||||
enabled: false
|
||||
onClicked: {
|
||||
newModelDialog.sendToScript({
|
||||
method: "newModelDialogAdd",
|
||||
|
|
|
@ -113,7 +113,6 @@ StackView {
|
|||
id: addressBarDialog
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
width: parent.width
|
||||
|
@ -142,7 +141,10 @@ StackView {
|
|||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
onHostChanged: updateLocationTextTimer.restart();
|
||||
onHostChanged: {
|
||||
updateLocationTextTimer.restart();
|
||||
DialogsManager.hideAddressBar();
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: navBar
|
||||
|
@ -401,11 +403,10 @@ StackView {
|
|||
addressLine.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
raised: parent.keyboardEnabled
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
|
@ -413,7 +414,7 @@ StackView {
|
|||
right: parent.right
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function updateLocationText(enteringAddress) {
|
||||
|
|
|
@ -39,7 +39,7 @@ FocusScope {
|
|||
// If someone directly set the visibility to false
|
||||
// toggle it back on and use the targetVisible flag to transition
|
||||
// via fading.
|
||||
if ((!visible && fadeTargetProperty != 0.0) || (visible && fadeTargetProperty == 0.0)) {
|
||||
if (!disableFade && ((!visible && fadeTargetProperty != 0.0) || (visible && fadeTargetProperty == 0.0))) {
|
||||
var target = visible;
|
||||
visible = !visible;
|
||||
fadeTargetProperty = target ? 1.0 : 0.0;
|
||||
|
|
18
interface/resources/serverless/tutorial.json
Normal file
18
interface/resources/serverless/tutorial.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"Entities": [
|
||||
{
|
||||
"type": "Box",
|
||||
"dimensions": {
|
||||
"x": 20,
|
||||
"y": 1,
|
||||
"z": 20
|
||||
},
|
||||
"position" : {
|
||||
"x": 0,
|
||||
"y": -12,
|
||||
"z": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 84
|
||||
}
|
|
@ -353,7 +353,7 @@ static const QString WEB_VIEW_TAG = "noDownload=true";
|
|||
static const QString ZIP_EXTENSION = ".zip";
|
||||
static const QString CONTENT_ZIP_EXTENSION = ".content.zip";
|
||||
|
||||
static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f;
|
||||
static const float MIRROR_FULLSCREEN_DISTANCE = 0.789f;
|
||||
|
||||
static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND;
|
||||
|
||||
|
@ -941,7 +941,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true),
|
||||
_preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME),
|
||||
_scaleMirror(1.0f),
|
||||
_rotateMirror(0.0f),
|
||||
_mirrorYawOffset(0.0f),
|
||||
_raiseMirror(0.0f),
|
||||
_enableProcessOctreeThread(true),
|
||||
_lastNackTime(usecTimestampNow()),
|
||||
|
@ -959,8 +959,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
|
||||
setProperty(hifi::properties::CRASHED, _previousSessionCrashed);
|
||||
|
||||
_entityClipboard->setIsServerlessMode(true);
|
||||
|
||||
{
|
||||
const QString TEST_SCRIPT = "--testScript";
|
||||
const QString TRACE_FILE = "--traceFile";
|
||||
|
@ -2813,8 +2811,9 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
|||
}
|
||||
else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_thirdPersonHMDCameraBoomValid= false;
|
||||
|
||||
if (isHMDMode()) {
|
||||
auto mirrorBodyOrientation = myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f));
|
||||
auto mirrorBodyOrientation = myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _mirrorYawOffset, 0.0f));
|
||||
|
||||
glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix());
|
||||
// Mirror HMD yaw and roll
|
||||
|
@ -2837,12 +2836,15 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
|||
+ mirrorBodyOrientation * hmdOffset);
|
||||
}
|
||||
else {
|
||||
_myCamera.setOrientation(myAvatar->getWorldOrientation()
|
||||
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
const float YAW_SPEED = TWO_PI / 5.0f;
|
||||
float deltaYaw = userInputMapper->getActionState(controller::Action::YAW) * YAW_SPEED * deltaTime;
|
||||
_mirrorYawOffset += deltaYaw;
|
||||
_myCamera.setOrientation(myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _mirrorYawOffset, 0.0f)));
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
+ glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0)
|
||||
+ (myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) *
|
||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
||||
+ (myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, _mirrorYawOffset, 0.0f))) *
|
||||
glm::vec3(0.0f, 0.0f, -1.0f) * myAvatar->getBoomLength() * _scaleMirror);
|
||||
}
|
||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
}
|
||||
|
@ -3044,7 +3046,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
|
||||
static const QString SENT_TO_PREVIOUS_LOCATION = "previous_location";
|
||||
static const QString SENT_TO_ENTRY = "entry";
|
||||
static const QString SENT_TO_SANDBOX = "sandbox";
|
||||
|
||||
QString sentTo;
|
||||
|
||||
|
@ -3053,15 +3054,8 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
#if !defined(Q_OS_ANDROID)
|
||||
showHelp();
|
||||
#endif
|
||||
if (sandboxIsRunning) {
|
||||
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
|
||||
DependencyManager::get<AddressManager>()->goToLocalSandbox();
|
||||
sentTo = SENT_TO_SANDBOX;
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
sentTo = SENT_TO_ENTRY;
|
||||
}
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
sentTo = SENT_TO_ENTRY;
|
||||
firstRun.set(false);
|
||||
|
||||
} else {
|
||||
|
@ -3400,8 +3394,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
} else {
|
||||
setFullscreen(nullptr);
|
||||
}
|
||||
} else {
|
||||
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -3463,13 +3455,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_F: {
|
||||
if (isOption) {
|
||||
_physicsEngine->dumpNextStats();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_Asterisk:
|
||||
Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox);
|
||||
break;
|
||||
|
@ -3489,22 +3474,25 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
case Qt::Key_S:
|
||||
if (isShifted && isMeta && !isOption) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings);
|
||||
} else if (!isOption && !isShifted && isMeta) {
|
||||
AudioInjectorOptions options;
|
||||
options.localOnly = true;
|
||||
options.stereo = true;
|
||||
|
||||
if (_snapshotSoundInjector) {
|
||||
_snapshotSoundInjector->setOptions(options);
|
||||
_snapshotSoundInjector->restart();
|
||||
} else {
|
||||
QByteArray samples = _snapshotSound->getByteArray();
|
||||
_snapshotSoundInjector = AudioInjector::playSound(samples, options);
|
||||
}
|
||||
takeSnapshot(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_P: {
|
||||
AudioInjectorOptions options;
|
||||
options.localOnly = true;
|
||||
options.stereo = true;
|
||||
|
||||
if (_snapshotSoundInjector) {
|
||||
_snapshotSoundInjector->setOptions(options);
|
||||
_snapshotSoundInjector->restart();
|
||||
} else {
|
||||
QByteArray samples = _snapshotSound->getByteArray();
|
||||
_snapshotSoundInjector = AudioInjector::playSound(samples, options);
|
||||
}
|
||||
takeSnapshot(true);
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_Apostrophe: {
|
||||
if (isMeta) {
|
||||
auto cursor = Cursor::Manager::instance().getCursor();
|
||||
|
@ -3528,38 +3516,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
Menu::getInstance()->triggerOption(MenuOption::Chat);
|
||||
break;
|
||||
|
||||
case Qt::Key_Up:
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
if (!isShifted) {
|
||||
_scaleMirror *= 0.95f;
|
||||
} else {
|
||||
_raiseMirror += 0.05f;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Down:
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
if (!isShifted) {
|
||||
_scaleMirror *= 1.05f;
|
||||
} else {
|
||||
_raiseMirror -= 0.05f;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Left:
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_rotateMirror += PI / 20.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Right:
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_rotateMirror -= PI / 20.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case Qt::Key_I:
|
||||
if (isShifted) {
|
||||
|
@ -4715,7 +4671,7 @@ void Application::init() {
|
|||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
|
||||
// connect the _entityCollisionSystem to our EntityTreeRenderer since that's what handles running entity scripts
|
||||
connect(_entitySimulation.get(), &EntitySimulation::entityCollisionWithEntity,
|
||||
connect(_entitySimulation.get(), &PhysicalEntitySimulation::entityCollisionWithEntity,
|
||||
getEntities().data(), &EntityTreeRenderer::entityCollisionWithEntity);
|
||||
|
||||
// connect the _entities (EntityTreeRenderer) to our script engine's EntityScriptingInterface for firing
|
||||
|
@ -4929,8 +4885,10 @@ void Application::cameraMenuChanged() {
|
|||
auto menu = Menu::getInstance();
|
||||
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
if (!isHMDMode() && _myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
_mirrorYawOffset = 0.0f;
|
||||
_myCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers
|
||||
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT);
|
||||
}
|
||||
} else if (menu->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
|
||||
|
@ -5210,7 +5168,7 @@ void Application::update(float deltaTime) {
|
|||
// FIXME can we drop drive keys and just have the avatar read the action states directly?
|
||||
myAvatar->clearDriveKeys();
|
||||
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
|
||||
if (!_controllerScriptingInterface->areActionsCaptured()) {
|
||||
if (!_controllerScriptingInterface->areActionsCaptured() && _myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X));
|
||||
|
@ -5223,6 +5181,7 @@ void Application::update(float deltaTime) {
|
|||
myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
|
||||
}
|
||||
|
||||
myAvatar->setSprintMode((bool)userInputMapper->getActionState(controller::Action::SPRINT));
|
||||
static const std::vector<controller::Action> avatarControllerActions = {
|
||||
controller::Action::LEFT_HAND,
|
||||
controller::Action::RIGHT_HAND,
|
||||
|
@ -5307,11 +5266,13 @@ void Application::update(float deltaTime) {
|
|||
{
|
||||
PROFILE_RANGE(simulation_physics, "PreStep");
|
||||
PerformanceTimer perfTimer("preStep)");
|
||||
static VectorOfMotionStates motionStates;
|
||||
_entitySimulation->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
_entitySimulation->deleteObjectsRemovedFromPhysics();
|
||||
{
|
||||
const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics();
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
_entitySimulation->deleteObjectsRemovedFromPhysics();
|
||||
}
|
||||
|
||||
VectorOfMotionStates motionStates;
|
||||
getEntities()->getTree()->withReadLock([&] {
|
||||
_entitySimulation->getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
|
@ -5325,7 +5286,7 @@ void Application::update(float deltaTime) {
|
|||
|
||||
_entitySimulation->applyDynamicChanges();
|
||||
|
||||
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
|
||||
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
avatarManager->getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
|
@ -6277,8 +6238,9 @@ bool Application::canAcceptURL(const QString& urlString) const {
|
|||
|
||||
bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
|
||||
QUrl url(urlString);
|
||||
if (isDomainURL(url)) {
|
||||
// this is a URL for a domain, either hifi:// or serverless - have the AddressManager handle it
|
||||
|
||||
if (url.scheme() == URL_SCHEME_HIFI) {
|
||||
// this is a hifi URL - have the AddressManager handle it
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString",
|
||||
Qt::AutoConnection, Q_ARG(const QString&, urlString));
|
||||
return true;
|
||||
|
@ -7488,7 +7450,7 @@ DisplayPluginPointer Application::getActiveDisplayPlugin() const {
|
|||
return _displayPlugin;
|
||||
}
|
||||
|
||||
if (!_displayPlugin) {
|
||||
if (!_aboutToQuit && !_displayPlugin) {
|
||||
const_cast<Application*>(this)->updateDisplayMode();
|
||||
Q_ASSERT(_displayPlugin);
|
||||
}
|
||||
|
|
|
@ -575,7 +575,7 @@ private:
|
|||
Setting::Handle<QString> _preferredCursor;
|
||||
|
||||
float _scaleMirror;
|
||||
float _rotateMirror;
|
||||
float _mirrorYawOffset;
|
||||
float _raiseMirror;
|
||||
|
||||
QSet<int> _keysPressed;
|
||||
|
|
|
@ -129,7 +129,7 @@ void DiscoverabilityManager::updateLocation() {
|
|||
|
||||
// Update Steam
|
||||
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
||||
steamClient->updateLocation(domainHandler.getHostname(), addressManager->currentFacingShareableAddress());
|
||||
steamClient->updateLocation(domainHandler.getHostname(), addressManager->currentFacingPublicAddress());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,10 +86,6 @@ void LODManager::autoAdjustLOD(float realTimeDelta) {
|
|||
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
|
||||
}
|
||||
qCDebug(interfaceapp) << "adjusting LOD DOWN"
|
||||
<< "fps =" << currentFPS
|
||||
<< "targetFPS =" << getLODDecreaseFPS()
|
||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||
emit LODDecreased();
|
||||
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
||||
// to provide an FPS just above the decrease threshold. It will drift close to its
|
||||
|
@ -111,10 +107,6 @@ void LODManager::autoAdjustLOD(float realTimeDelta) {
|
|||
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
|
||||
}
|
||||
qCDebug(interfaceapp) << "adjusting LOD UP"
|
||||
<< "fps =" << currentFPS
|
||||
<< "targetFPS =" << getLODDecreaseFPS()
|
||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||
emit LODIncreased();
|
||||
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
||||
// to provide an FPS just below the increase threshold. It will drift close to its
|
||||
|
|
|
@ -229,21 +229,21 @@ Menu::Menu() {
|
|||
|
||||
// View > First Person
|
||||
auto firstPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
|
||||
viewMenu, MenuOption::FirstPerson, Qt::CTRL | Qt::Key_F,
|
||||
viewMenu, MenuOption::FirstPerson, Qt::Key_1,
|
||||
true, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
||||
// View > Third Person
|
||||
auto thirdPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
|
||||
viewMenu, MenuOption::ThirdPerson, Qt::CTRL | Qt::Key_G,
|
||||
viewMenu, MenuOption::ThirdPerson, Qt::Key_3,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
thirdPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
||||
// View > Mirror
|
||||
auto viewMirrorAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
|
||||
viewMenu, MenuOption::FullscreenMirror, Qt::CTRL | Qt::Key_H,
|
||||
viewMenu, MenuOption::FullscreenMirror, Qt::Key_2,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
|
|
@ -67,8 +67,8 @@ using namespace std;
|
|||
|
||||
const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f;
|
||||
|
||||
const float YAW_SPEED_DEFAULT = 100.0f; // degrees/sec
|
||||
const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec
|
||||
const float YAW_SPEED_DEFAULT = 75.0f; // degrees/sec
|
||||
const float PITCH_SPEED_DEFAULT = 50.0f; // degrees/sec
|
||||
|
||||
const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed
|
||||
const float MIN_AVATAR_SPEED = 0.05f;
|
||||
|
@ -2186,7 +2186,6 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
glm::vec3 direction = forward + right;
|
||||
if (state == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
// we can fly --> support vertical motion
|
||||
glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP;
|
||||
direction += up;
|
||||
}
|
||||
|
@ -2204,10 +2203,11 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
|
||||
if (state == CharacterController::State::Hover) {
|
||||
// we're flying --> complex acceleration curve that builds on top of current motor speed and caps at some max speed
|
||||
|
||||
float motorSpeed = glm::length(_actionMotorVelocity);
|
||||
float finalMaxMotorSpeed = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_FLYING_SPEED;
|
||||
float finalMaxMotorSpeed = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_FLYING_SPEED * _walkSpeedScalar;
|
||||
float speedGrowthTimescale = 2.0f;
|
||||
float speedIncreaseFactor = 1.8f;
|
||||
float speedIncreaseFactor = 1.8f * _walkSpeedScalar;
|
||||
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale, 0.0f, 1.0f) * speedIncreaseFactor;
|
||||
const float maxBoostSpeed = getSensorToWorldScale() * MAX_BOOST_SPEED;
|
||||
|
||||
|
@ -2223,11 +2223,11 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
_actionMotorVelocity = motorSpeed * direction;
|
||||
} else {
|
||||
// we're interacting with a floor --> simple horizontal speed and exponential decay
|
||||
_actionMotorVelocity = getSensorToWorldScale() * _walkSpeed.get() * direction;
|
||||
_actionMotorVelocity = getSensorToWorldScale() * (_walkSpeed.get() * _walkSpeedScalar) * direction;
|
||||
}
|
||||
|
||||
float boomChange = getDriveKey(ZOOM);
|
||||
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
|
||||
_boomLength += 4.0f * _boomLength * boomChange + boomChange * boomChange;
|
||||
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
|
||||
}
|
||||
|
||||
|
@ -2760,6 +2760,18 @@ bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::triggerVerticalRecenter() {
|
||||
_follow.setForceActivateVertical(true);
|
||||
}
|
||||
|
||||
void MyAvatar::triggerHorizontalRecenter() {
|
||||
_follow.setForceActivateHorizontal(true);
|
||||
}
|
||||
|
||||
void MyAvatar::triggerRotationRecenter() {
|
||||
_follow.setForceActivateRotation(true);
|
||||
}
|
||||
|
||||
// old school meat hook style
|
||||
glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
||||
glm::vec3 headPosition;
|
||||
|
@ -2816,7 +2828,11 @@ float MyAvatar::getUserEyeHeight() const {
|
|||
}
|
||||
|
||||
float MyAvatar::getWalkSpeed() const {
|
||||
return _walkSpeed.get();
|
||||
return _walkSpeed.get() * _walkSpeedScalar;
|
||||
}
|
||||
|
||||
void MyAvatar::setSprintMode(bool sprint) {
|
||||
_walkSpeedScalar = sprint ? AVATAR_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR;
|
||||
}
|
||||
|
||||
void MyAvatar::setWalkSpeed(float value) {
|
||||
|
@ -2953,7 +2969,9 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
|
|||
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
|
||||
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
|
||||
|
||||
return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
|
||||
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
|
@ -2970,6 +2988,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
|
|||
const float MAX_FORWARD_LEAN = 0.15f;
|
||||
const float MAX_BACKWARD_LEAN = 0.1f;
|
||||
|
||||
|
||||
if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
|
||||
return true;
|
||||
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) {
|
||||
|
@ -2977,6 +2996,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar,
|
|||
}
|
||||
|
||||
return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
|
||||
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
|
@ -2984,6 +3004,7 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co
|
|||
const float CYLINDER_BOTTOM = -1.5f;
|
||||
|
||||
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
|
||||
|
||||
return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
|
||||
}
|
||||
|
||||
|
@ -3001,6 +3022,19 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
|||
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||
activate(Vertical);
|
||||
}
|
||||
} else {
|
||||
if (!isActive(Rotation) && getForceActivateRotation()) {
|
||||
activate(Rotation);
|
||||
setForceActivateRotation(false);
|
||||
}
|
||||
if (!isActive(Horizontal) && getForceActivateHorizontal()) {
|
||||
activate(Horizontal);
|
||||
setForceActivateHorizontal(false);
|
||||
}
|
||||
if (!isActive(Vertical) && getForceActivateVertical()) {
|
||||
activate(Vertical);
|
||||
setForceActivateVertical(false);
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * desiredBodyMatrix;
|
||||
|
@ -3050,6 +3084,30 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co
|
|||
}
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::getForceActivateRotation() const {
|
||||
return _forceActivateRotation;
|
||||
}
|
||||
|
||||
void MyAvatar::FollowHelper::setForceActivateRotation(bool val) {
|
||||
_forceActivateRotation = val;
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::getForceActivateVertical() const {
|
||||
return _forceActivateVertical;
|
||||
}
|
||||
|
||||
void MyAvatar::FollowHelper::setForceActivateVertical(bool val) {
|
||||
_forceActivateVertical = val;
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::getForceActivateHorizontal() const {
|
||||
return _forceActivateHorizontal;
|
||||
}
|
||||
|
||||
void MyAvatar::FollowHelper::setForceActivateHorizontal(bool val) {
|
||||
_forceActivateHorizontal = val;
|
||||
}
|
||||
|
||||
float MyAvatar::getAccelerationEnergy() {
|
||||
glm::vec3 velocity = getWorldVelocity();
|
||||
int changeInVelocity = abs(velocity.length() - priorVelocity.length());
|
||||
|
|
|
@ -395,6 +395,7 @@ public:
|
|||
// Set what driving keys are being pressed to control thrust levels
|
||||
void clearDriveKeys();
|
||||
void setDriveKey(DriveKeys key, float val);
|
||||
void setSprintMode(bool sprint);
|
||||
float getDriveKey(DriveKeys key) const;
|
||||
Q_INVOKABLE float getRawDriveKey(DriveKeys key) const;
|
||||
void relayDriveKeysToCharacterController();
|
||||
|
@ -403,6 +404,32 @@ public:
|
|||
Q_INVOKABLE void enableDriveKey(DriveKeys key);
|
||||
Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const;
|
||||
|
||||
/**jsdoc
|
||||
*The triggerVerticalRecenter function activates one time the recentering
|
||||
*behaviour in the vertical direction. This call is only takes effect when the property
|
||||
*MyAvatar.hmdLeanRecenterEnabled is set to false.
|
||||
*@function MyAvatar.triggerVerticalRecenter
|
||||
*
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
*The triggerHorizontalRecenter function activates one time the recentering behaviour
|
||||
*in the horizontal direction. This call is only takes effect when the property
|
||||
*MyAvatar.hmdLeanRecenterEnabled is set to false.
|
||||
*@function MyAvatar.triggerHorizontalRecenter
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
*The triggerRotationRecenter function activates one time the recentering behaviour
|
||||
*in the rotation of the root of the avatar. This call is only takes effect when the property
|
||||
*MyAvatar.hmdLeanRecenterEnabled is set to false.
|
||||
*@function MyAvatar.triggerRotationRecenter
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void triggerVerticalRecenter();
|
||||
Q_INVOKABLE void triggerHorizontalRecenter();
|
||||
Q_INVOKABLE void triggerRotationRecenter();
|
||||
|
||||
eyeContactTarget getEyeContactTarget();
|
||||
|
||||
const MyHead* getMyHead() const;
|
||||
|
@ -802,6 +829,15 @@ private:
|
|||
bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||
void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput);
|
||||
glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix);
|
||||
bool getForceActivateRotation() const;
|
||||
void setForceActivateRotation(bool val);
|
||||
bool getForceActivateVertical() const;
|
||||
void setForceActivateVertical(bool val);
|
||||
bool getForceActivateHorizontal() const;
|
||||
void setForceActivateHorizontal(bool val);
|
||||
std::atomic<bool> _forceActivateRotation{ false };
|
||||
std::atomic<bool> _forceActivateVertical{ false };
|
||||
std::atomic<bool> _forceActivateHorizontal{ false };
|
||||
};
|
||||
FollowHelper _follow;
|
||||
|
||||
|
@ -836,7 +872,9 @@ private:
|
|||
std::map<controller::Action, controller::Pose> _controllerPoseMap;
|
||||
mutable std::mutex _controllerPoseMapMutex;
|
||||
|
||||
bool _hmdLeanRecenterEnabled = true;
|
||||
bool _hmdLeanRecenterEnabled { true };
|
||||
bool _sprint { false };
|
||||
|
||||
AnimPose _prePhysicsRoomPose;
|
||||
std::mutex _holdActionsMutex;
|
||||
std::vector<AvatarActionHold*> _holdActions;
|
||||
|
@ -866,6 +904,7 @@ private:
|
|||
|
||||
// max unscaled forward movement speed
|
||||
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
|
||||
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
|
||||
};
|
||||
|
||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||
|
|
|
@ -293,7 +293,7 @@ void Ledger::account() {
|
|||
// The api/failResponse is called just for the side effect of logging.
|
||||
void Ledger::updateLocationSuccess(QNetworkReply& reply) { apiResponse("updateLocation", reply); }
|
||||
void Ledger::updateLocationFailure(QNetworkReply& reply) { failResponse("updateLocation", reply); }
|
||||
void Ledger::updateLocation(const QString& asset_id, const QString location, const bool controlledFailure) {
|
||||
void Ledger::updateLocation(const QString& asset_id, const QString& location, const bool& alsoUpdateSiblings, const bool controlledFailure) {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
|
||||
uint walletStatus = walletScriptingInterface->getWalletStatus();
|
||||
|
@ -308,6 +308,9 @@ void Ledger::updateLocation(const QString& asset_id, const QString location, con
|
|||
QJsonObject transaction;
|
||||
transaction["certificate_id"] = asset_id;
|
||||
transaction["place_name"] = location;
|
||||
if (alsoUpdateSiblings) {
|
||||
transaction["also_update_siblings"] = true;
|
||||
}
|
||||
QJsonDocument transactionDoc{ transaction };
|
||||
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
||||
signedSend("transaction", transactionString, key, "location", "updateLocationSuccess", "updateLocationFailure", controlledFailure);
|
||||
|
|
|
@ -31,7 +31,7 @@ public:
|
|||
void inventory(const QStringList& keys);
|
||||
void history(const QStringList& keys, const int& pageNumber);
|
||||
void account();
|
||||
void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false);
|
||||
void updateLocation(const QString& asset_id, const QString& location, const bool& alsoUpdateSiblings = false, const bool controlledFailure = false);
|
||||
void certificateInfo(const QString& certificateId);
|
||||
void transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage);
|
||||
void transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage);
|
||||
|
|
|
@ -190,7 +190,9 @@ void QmlCommerce::transferHfcToUsername(const QString& username, const int& amou
|
|||
ledger->transferHfcToUsername(key, username, amount, optionalMessage);
|
||||
}
|
||||
|
||||
void QmlCommerce::replaceContentSet(const QString& itemHref) {
|
||||
void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
ledger->updateLocation(certificateID, DependencyManager::get<AddressManager>()->getPlaceName(), true);
|
||||
qApp->replaceDomainContent(itemHref);
|
||||
QJsonObject messageProperties = {
|
||||
{ "status", "SuccessfulRequestToReplaceContent" },
|
||||
|
|
|
@ -84,7 +84,7 @@ protected:
|
|||
Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage);
|
||||
Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage);
|
||||
|
||||
Q_INVOKABLE void replaceContentSet(const QString& itemHref);
|
||||
Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID);
|
||||
|
||||
Q_INVOKABLE QString getInstalledApps();
|
||||
Q_INVOKABLE bool installApp(const QString& appHref);
|
||||
|
|
|
@ -81,26 +81,14 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta
|
|||
});
|
||||
}
|
||||
|
||||
void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) {
|
||||
if (!id.isNull() && props.isValid()) {
|
||||
QVariantMap propMap = props.toMap();
|
||||
propMap.remove("visible");
|
||||
qApp->getOverlays().editOverlay(id, propMap);
|
||||
}
|
||||
}
|
||||
PickResultPointer LaserPointer::getVisualPickResult(const PickResultPointer& pickResult) {
|
||||
PickResultPointer visualPickResult = pickResult;
|
||||
auto rayPickResult = std::static_pointer_cast<RayPickResult>(visualPickResult);
|
||||
IntersectionType type = rayPickResult ? rayPickResult->type : IntersectionType::NONE;
|
||||
|
||||
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState) {
|
||||
if (!renderState.getStartID().isNull()) {
|
||||
QVariantMap startProps;
|
||||
startProps.insert("position", vec3toVariant(pickRay.origin));
|
||||
startProps.insert("visible", true);
|
||||
startProps.insert("ignoreRayIntersection", renderState.doesStartIgnoreRays());
|
||||
qApp->getOverlays().editOverlay(renderState.getStartID(), startProps);
|
||||
}
|
||||
glm::vec3 endVec;
|
||||
if (((defaultState || !_lockEnd) && _lockEndObject.id.isNull()) || type == IntersectionType::HUD) {
|
||||
endVec = pickRay.origin + pickRay.direction * distance;
|
||||
} else {
|
||||
if (type != IntersectionType::HUD) {
|
||||
glm::vec3 endVec;
|
||||
PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant) : PickRay();
|
||||
if (!_lockEndObject.id.isNull()) {
|
||||
glm::vec3 pos;
|
||||
glm::quat rot;
|
||||
|
@ -122,17 +110,54 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
|
|||
}
|
||||
const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f);
|
||||
endVec = pos + rot * (dim * (DEFAULT_REGISTRATION_POINT - registrationPoint));
|
||||
} else {
|
||||
glm::vec3 direction = endVec - pickRay.origin;
|
||||
float distance = glm::distance(pickRay.origin, endVec);
|
||||
glm::vec3 normalizedDirection = glm::normalize(direction);
|
||||
|
||||
rayPickResult->type = _lockEndObject.isOverlay ? IntersectionType::OVERLAY : IntersectionType::ENTITY;
|
||||
rayPickResult->objectID = _lockEndObject.id;
|
||||
rayPickResult->intersection = endVec;
|
||||
rayPickResult->distance = distance;
|
||||
rayPickResult->surfaceNormal = -normalizedDirection;
|
||||
rayPickResult->pickVariant["direction"] = vec3toVariant(normalizedDirection);
|
||||
} else if (type != IntersectionType::NONE && _lockEnd) {
|
||||
if (type == IntersectionType::ENTITY) {
|
||||
endVec = DependencyManager::get<EntityScriptingInterface>()->getEntityTransform(objectID)[3];
|
||||
endVec = DependencyManager::get<EntityScriptingInterface>()->getEntityTransform(rayPickResult->objectID)[3];
|
||||
} else if (type == IntersectionType::OVERLAY) {
|
||||
endVec = vec3FromVariant(qApp->getOverlays().getProperty(objectID, "position").value);
|
||||
endVec = vec3FromVariant(qApp->getOverlays().getProperty(rayPickResult->objectID, "position").value);
|
||||
} else if (type == IntersectionType::AVATAR) {
|
||||
endVec = DependencyManager::get<AvatarHashMap>()->getAvatar(objectID)->getPosition();
|
||||
endVec = DependencyManager::get<AvatarHashMap>()->getAvatar(rayPickResult->objectID)->getPosition();
|
||||
}
|
||||
glm::vec3 direction = endVec - pickRay.origin;
|
||||
float distance = glm::distance(pickRay.origin, endVec);
|
||||
glm::vec3 normalizedDirection = glm::normalize(direction);
|
||||
rayPickResult->intersection = endVec;
|
||||
rayPickResult->distance = distance;
|
||||
rayPickResult->surfaceNormal = -normalizedDirection;
|
||||
rayPickResult->pickVariant["direction"] = vec3toVariant(normalizedDirection);
|
||||
}
|
||||
}
|
||||
|
||||
return visualPickResult;
|
||||
}
|
||||
|
||||
void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) {
|
||||
if (!id.isNull() && props.isValid()) {
|
||||
QVariantMap propMap = props.toMap();
|
||||
propMap.remove("visible");
|
||||
qApp->getOverlays().editOverlay(id, propMap);
|
||||
}
|
||||
}
|
||||
|
||||
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay) {
|
||||
if (!renderState.getStartID().isNull()) {
|
||||
QVariantMap startProps;
|
||||
startProps.insert("position", vec3toVariant(pickRay.origin));
|
||||
startProps.insert("visible", true);
|
||||
startProps.insert("ignoreRayIntersection", renderState.doesStartIgnoreRays());
|
||||
qApp->getOverlays().editOverlay(renderState.getStartID(), startProps);
|
||||
}
|
||||
glm::vec3 endVec = pickRay.origin + pickRay.direction * distance;
|
||||
|
||||
QVariant end = vec3toVariant(endVec);
|
||||
if (!renderState.getPathID().isNull()) {
|
||||
QVariantMap pathProps;
|
||||
|
@ -195,15 +220,15 @@ void LaserPointer::updateVisuals(const PickResultPointer& pickResult) {
|
|||
IntersectionType type = rayPickResult ? rayPickResult->type : IntersectionType::NONE;
|
||||
if (_enabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
|
||||
(type != IntersectionType::NONE || _laserLength > 0.0f || !_lockEndObject.id.isNull())) {
|
||||
PickRay pickRay(rayPickResult->pickVariant);
|
||||
PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant): PickRay();
|
||||
QUuid uid = rayPickResult->objectID;
|
||||
float distance = _laserLength > 0.0f ? _laserLength : rayPickResult->distance;
|
||||
updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay, false);
|
||||
updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay);
|
||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||
} else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
||||
disableRenderState(_renderStates[_currentRenderState]);
|
||||
PickRay pickRay = rayPickResult ? PickRay(rayPickResult->pickVariant) : PickRay();
|
||||
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay, true);
|
||||
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay);
|
||||
} else if (!_currentRenderState.empty()) {
|
||||
disableRenderState(_renderStates[_currentRenderState]);
|
||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||
|
@ -386,4 +411,4 @@ glm::vec2 LaserPointer::findPos2D(const PickedObject& pickedObject, const glm::v
|
|||
default:
|
||||
return glm::vec2(NAN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ public:
|
|||
protected:
|
||||
PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) override;
|
||||
|
||||
PickResultPointer getVisualPickResult(const PickResultPointer& pickResult) override;
|
||||
PickedObject getHoveredObject(const PickResultPointer& pickResult) override;
|
||||
Pointer::Buttons getPressedButtons(const PickResultPointer& pickResult) override;
|
||||
|
||||
|
@ -102,7 +103,7 @@ private:
|
|||
LockEndObject _lockEndObject;
|
||||
|
||||
void updateRenderStateOverlay(const OverlayID& id, const QVariant& props);
|
||||
void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState);
|
||||
void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay);
|
||||
void disableRenderState(const RenderState& renderState);
|
||||
|
||||
struct TriggerState {
|
||||
|
|
|
@ -80,7 +80,7 @@ void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping,
|
|||
auto result = offscreenUi->inputDialog(OffscreenUi::ICON_INFORMATION, "Specify Asset Path",
|
||||
dropEvent ? dropHelpText : helpText, mapping);
|
||||
|
||||
if (!result.isValid()) {
|
||||
if (!result.isValid() || result.toString() == "") {
|
||||
completedCallback.call({ -1 });
|
||||
return;
|
||||
}
|
||||
|
@ -152,6 +152,10 @@ void AssetMappingsScriptingInterface::deleteMappings(QStringList paths, QJSValue
|
|||
request->start();
|
||||
}
|
||||
|
||||
void AssetMappingsScriptingInterface::sortProxyModel(int column, Qt::SortOrder order) {
|
||||
_proxyModel.sort(column, order);
|
||||
}
|
||||
|
||||
void AssetMappingsScriptingInterface::getAllMappings(QJSValue callback) {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
auto request = assetClient->createGetAllMappingsRequest();
|
||||
|
@ -287,7 +291,7 @@ void AssetMappingModel::refresh() {
|
|||
item->setData(parts[i], Qt::UserRole + 2);
|
||||
item->setData("atp:" + fullPath, Qt::UserRole + 3);
|
||||
item->setData(fullPath, Qt::UserRole + 4);
|
||||
|
||||
|
||||
if (lastItem) {
|
||||
lastItem->appendRow(item);
|
||||
} else {
|
||||
|
|
|
@ -82,6 +82,7 @@ public:
|
|||
Q_INVOKABLE void getAllMappings(QJSValue callback = QJSValue());
|
||||
Q_INVOKABLE void renameMapping(QString oldPath, QString newPath, QJSValue callback = QJSValue());
|
||||
Q_INVOKABLE void setBakingEnabled(QStringList paths, bool enabled, QJSValue callback = QJSValue());
|
||||
Q_INVOKABLE void sortProxyModel(int column, Qt::SortOrder order = Qt::AscendingOrder);
|
||||
|
||||
protected:
|
||||
QSet<AssetRequest*> _pendingRequests;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <plugins/DisplayPlugin.h>
|
||||
|
@ -182,7 +183,6 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) {
|
|||
}
|
||||
|
||||
void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD) {
|
||||
auto oldDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
|
||||
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
|
||||
selectedDevice = device;
|
||||
|
||||
|
@ -200,32 +200,137 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD
|
|||
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
|
||||
}
|
||||
|
||||
void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices, bool isHMD) {
|
||||
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
|
||||
// Function returns 'strings similarity' as a number. The lesser number - the more similar strings are. Absolutely equal strings should return 0.
|
||||
// Optimized version kindly provided by Ken
|
||||
int levenshteinDistance(const QString& s1, const QString& s2) {
|
||||
const int m = s1.size();
|
||||
const int n = s2.size();
|
||||
|
||||
const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName;
|
||||
if (m == 0) {
|
||||
return n;
|
||||
}
|
||||
if (n == 0) {
|
||||
return m;
|
||||
}
|
||||
|
||||
auto cost = (int*)alloca((n + 1) * sizeof(int));
|
||||
|
||||
for (int j = 0; j <= n; j++) {
|
||||
cost[j] = j;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m; i++) {
|
||||
|
||||
int prev = i;
|
||||
cost[0] = i + 1;
|
||||
|
||||
for (int j = 0; j < n; j++) {
|
||||
|
||||
int temp = cost[j + 1];
|
||||
cost[j + 1] = (s1[i] == s2[j]) ? prev : std::min(cost[j], std::min(temp, prev)) + 1;
|
||||
prev = temp;
|
||||
}
|
||||
}
|
||||
return cost[n];
|
||||
}
|
||||
|
||||
std::shared_ptr<scripting::AudioDevice> getSimilarDevice(const QString& deviceName, const QList<std::shared_ptr<scripting::AudioDevice>>& devices) {
|
||||
|
||||
int minDistance = INT_MAX;
|
||||
int minDistanceIndex = 0;
|
||||
|
||||
for (auto i = 0; i < devices.length(); ++i) {
|
||||
auto distance = levenshteinDistance(deviceName, devices[i]->info.deviceName());
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
minDistanceIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return devices[minDistanceIndex];
|
||||
}
|
||||
|
||||
void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) {
|
||||
beginResetModel();
|
||||
|
||||
_devices.clear();
|
||||
QList<std::shared_ptr<AudioDevice>> newDevices;
|
||||
bool hmdIsSelected = false;
|
||||
bool desktopIsSelected = false;
|
||||
|
||||
foreach(const QAudioDeviceInfo& deviceInfo, devices) {
|
||||
for (bool isHMD : {false, true}) {
|
||||
auto &backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName;
|
||||
if (deviceInfo.deviceName() == backupSelectedDeviceName) {
|
||||
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
|
||||
selectedDevice = deviceInfo;
|
||||
backupSelectedDeviceName.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach(const QAudioDeviceInfo& deviceInfo, devices) {
|
||||
AudioDevice device;
|
||||
bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop;
|
||||
device.info = deviceInfo;
|
||||
device.display = device.info.deviceName()
|
||||
.replace("High Definition", "HD")
|
||||
.remove("Device")
|
||||
.replace(" )", ")");
|
||||
if (!selectedDevice.isNull()) {
|
||||
isSelected = (device.info == selectedDevice);
|
||||
} else {
|
||||
//no selected device for context. fallback to saved
|
||||
isSelected = (device.info.deviceName() == savedDeviceName);
|
||||
|
||||
for (bool isHMD : {false, true}) {
|
||||
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
|
||||
bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop;
|
||||
|
||||
if (!selectedDevice.isNull()) {
|
||||
isSelected = (device.info == selectedDevice);
|
||||
}
|
||||
else {
|
||||
//no selected device for context. fallback to saved
|
||||
const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName;
|
||||
isSelected = (device.info.deviceName() == savedDeviceName);
|
||||
}
|
||||
|
||||
if (isSelected) {
|
||||
if (isHMD) {
|
||||
hmdIsSelected = isSelected;
|
||||
} else {
|
||||
desktopIsSelected = isSelected;
|
||||
}
|
||||
|
||||
// check if this device *is not* in old devices list - it means it was just re-plugged so needs to be selected explicitly
|
||||
bool isNewDevice = true;
|
||||
for (auto& oldDevice : _devices) {
|
||||
if (oldDevice->info.deviceName() == device.info.deviceName()) {
|
||||
isNewDevice = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNewDevice) {
|
||||
emit selectedDevicePlugged(device.info, isHMD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "adding audio device:" << device.display << device.selectedDesktop << device.selectedHMD << _mode;
|
||||
_devices.push_back(newDevice(device));
|
||||
newDevices.push_back(newDevice(device));
|
||||
}
|
||||
|
||||
if (!newDevices.isEmpty()) {
|
||||
if (!hmdIsSelected) {
|
||||
_backupSelectedHMDDeviceName = !_selectedHMDDevice.isNull() ? _selectedHMDDevice.deviceName() : _hmdSavedDeviceName;
|
||||
auto device = getSimilarDevice(_backupSelectedHMDDeviceName, newDevices);
|
||||
device->selectedHMD = true;
|
||||
emit selectedDevicePlugged(device->info, true);
|
||||
}
|
||||
if (!desktopIsSelected) {
|
||||
_backupSelectedDesktopDeviceName = !_selectedDesktopDevice.isNull() ? _selectedDesktopDevice.deviceName() : _desktopSavedDeviceName;
|
||||
auto device = getSimilarDevice(_backupSelectedDesktopDeviceName, newDevices);
|
||||
device->selectedDesktop = true;
|
||||
emit selectedDevicePlugged(device->info, false);
|
||||
}
|
||||
}
|
||||
|
||||
_devices.swap(newDevices);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
|
@ -271,12 +376,10 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) {
|
|||
// connections are made after client is initialized, so we must also fetch the devices
|
||||
const QList<QAudioDeviceInfo>& devicesInput = client->getAudioDevices(QAudio::AudioInput);
|
||||
const QList<QAudioDeviceInfo>& devicesOutput = client->getAudioDevices(QAudio::AudioOutput);
|
||||
//setup HMD devices
|
||||
_inputs.onDevicesChanged(devicesInput, true);
|
||||
_outputs.onDevicesChanged(devicesOutput, true);
|
||||
//setup Desktop devices
|
||||
_inputs.onDevicesChanged(devicesInput, false);
|
||||
_outputs.onDevicesChanged(devicesOutput, false);
|
||||
|
||||
//setup devices
|
||||
_inputs.onDevicesChanged(devicesInput);
|
||||
_outputs.onDevicesChanged(devicesOutput);
|
||||
}
|
||||
|
||||
AudioDevices::~AudioDevices() {}
|
||||
|
@ -375,11 +478,19 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceI
|
|||
|
||||
//set devices for both contexts
|
||||
if (mode == QAudio::AudioInput) {
|
||||
_inputs.onDevicesChanged(devices, _contextIsHMD);
|
||||
_inputs.onDevicesChanged(devices, !_contextIsHMD);
|
||||
_inputs.onDevicesChanged(devices);
|
||||
|
||||
static std::once_flag onceAfterInputDevicesChanged;
|
||||
std::call_once(onceAfterInputDevicesChanged, [&] { // we only want 'selectedDevicePlugged' signal to be handled after initial list of input devices was populated
|
||||
connect(&_inputs, &AudioDeviceList::selectedDevicePlugged, this, &AudioDevices::chooseInputDevice);
|
||||
});
|
||||
} else { // if (mode == QAudio::AudioOutput)
|
||||
_outputs.onDevicesChanged(devices, _contextIsHMD);
|
||||
_outputs.onDevicesChanged(devices, !_contextIsHMD);
|
||||
_outputs.onDevicesChanged(devices);
|
||||
|
||||
static std::once_flag onceAfterOutputDevicesChanged;
|
||||
std::call_once(onceAfterOutputDevicesChanged, [&] { // we only want 'selectedDevicePlugged' signal to be handled after initial list of output devices was populated
|
||||
connect(&_outputs, &AudioDeviceList::selectedDevicePlugged, this, &AudioDevices::chooseOutputDevice);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,10 +51,11 @@ public:
|
|||
|
||||
signals:
|
||||
void deviceChanged(const QAudioDeviceInfo& device);
|
||||
void selectedDevicePlugged(const QAudioDeviceInfo& device, bool isHMD);
|
||||
|
||||
protected slots:
|
||||
void onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD);
|
||||
void onDevicesChanged(const QList<QAudioDeviceInfo>& devices, bool isHMD);
|
||||
void onDevicesChanged(const QList<QAudioDeviceInfo>& devices);
|
||||
|
||||
protected:
|
||||
friend class AudioDevices;
|
||||
|
@ -64,6 +65,8 @@ protected:
|
|||
const QAudio::Mode _mode;
|
||||
QAudioDeviceInfo _selectedDesktopDevice;
|
||||
QAudioDeviceInfo _selectedHMDDevice;
|
||||
QString _backupSelectedDesktopDeviceName;
|
||||
QString _backupSelectedHMDDeviceName;
|
||||
QList<std::shared_ptr<AudioDevice>> _devices;
|
||||
QString _hmdSavedDeviceName;
|
||||
QString _desktopSavedDeviceName;
|
||||
|
@ -117,13 +120,13 @@ public:
|
|||
AudioDevices(bool& contextIsHMD);
|
||||
virtual ~AudioDevices();
|
||||
|
||||
void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
|
||||
signals:
|
||||
void nop();
|
||||
|
||||
private slots:
|
||||
void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
|
||||
void onContextChanged(const QString& context);
|
||||
void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device,
|
||||
const QAudioDeviceInfo& previousDevice, bool isHMD);
|
||||
|
|
|
@ -110,7 +110,7 @@ bool SelectionScriptingInterface::enableListHighlight(const QString& listName, c
|
|||
}
|
||||
|
||||
if (!(*highlightStyle).isBoundToList()) {
|
||||
setupHandler(listName);
|
||||
enableListToScene(listName);
|
||||
(*highlightStyle).setBoundToList(true);
|
||||
}
|
||||
|
||||
|
@ -134,6 +134,7 @@ bool SelectionScriptingInterface::disableListHighlight(const QString& listName)
|
|||
auto highlightStyle = _highlightStyleMap.find(listName);
|
||||
if (highlightStyle != _highlightStyleMap.end()) {
|
||||
if ((*highlightStyle).isBoundToList()) {
|
||||
disableListToScene(listName);
|
||||
}
|
||||
|
||||
_highlightStyleMap.erase(highlightStyle);
|
||||
|
@ -172,6 +173,18 @@ render::HighlightStyle SelectionScriptingInterface::getHighlightStyle(const QStr
|
|||
}
|
||||
}
|
||||
|
||||
bool SelectionScriptingInterface::enableListToScene(const QString& listName) {
|
||||
setupHandler(listName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SelectionScriptingInterface::disableListToScene(const QString& listName) {
|
||||
removeHandler(listName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T> bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) {
|
||||
{
|
||||
QWriteLocker lock(&_selectionListsLock);
|
||||
|
@ -303,6 +316,15 @@ void SelectionScriptingInterface::setupHandler(const QString& selectionName) {
|
|||
(*handler)->initialize(selectionName);
|
||||
}
|
||||
|
||||
void SelectionScriptingInterface::removeHandler(const QString& selectionName) {
|
||||
QWriteLocker lock(&_selectionHandlersLock);
|
||||
auto handler = _handlerMap.find(selectionName);
|
||||
if (handler != _handlerMap.end()) {
|
||||
delete handler.value();
|
||||
_handlerMap.erase(handler);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionScriptingInterface::onSelectedItemsListChanged(const QString& listName) {
|
||||
{
|
||||
QWriteLocker lock(&_selectionHandlersLock);
|
||||
|
@ -456,4 +478,4 @@ QVariantMap SelectionHighlightStyle::toVariantMap() const {
|
|||
properties["isOutlineSmooth"] = _style._isOutlineSmooth;
|
||||
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,13 +160,16 @@ public:
|
|||
* If the Selection doesn't exist, it will be created.
|
||||
* All objects in the list will be displayed with the highlight effect as specified from the highlightStyle.
|
||||
* The function can be called several times with different values in the style to modify it.
|
||||
*
|
||||
*
|
||||
* @function Selection.enableListHighlight
|
||||
* @param listName {string} name of the selection
|
||||
* @param highlightStyle {jsObject} highlight style fields (see Selection.getListHighlightStyle for a detailed description of the highlightStyle).
|
||||
* @returns {bool} true if the selection was successfully enabled for highlight.
|
||||
*
|
||||
* Note: This function will implicitly call Selection.enableListToScene
|
||||
*/
|
||||
Q_INVOKABLE bool enableListHighlight(const QString& listName, const QVariantMap& highlightStyle);
|
||||
|
||||
/**jsdoc
|
||||
* Disable highlighting for the named selection.
|
||||
* If the Selection doesn't exist or wasn't enabled for highliting then nothing happens simply returning false.
|
||||
|
@ -174,8 +177,30 @@ public:
|
|||
* @function Selection.disableListHighlight
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {bool} true if the selection was successfully disabled for highlight, false otherwise.
|
||||
*
|
||||
* Note: This function will implicitly call Selection.disableListToScene
|
||||
*/
|
||||
Q_INVOKABLE bool disableListHighlight(const QString& listName);
|
||||
/**jsdoc
|
||||
* Enable scene selection for the named selection.
|
||||
* If the Selection doesn't exist, it will be created.
|
||||
* All objects in the list will be sent to a scene selection.
|
||||
*
|
||||
* @function Selection.enableListToScene
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {bool} true if the selection was successfully enabled on the scene.
|
||||
*/
|
||||
Q_INVOKABLE bool enableListToScene(const QString& listName);
|
||||
/**jsdoc
|
||||
* Disable scene selection for the named selection.
|
||||
* If the Selection doesn't exist or wasn't enabled on the scene then nothing happens simply returning false.
|
||||
*
|
||||
* @function Selection.disableListToScene
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {bool} true if the selection was successfully disabled on the scene, false otherwise.
|
||||
*/
|
||||
Q_INVOKABLE bool disableListToScene(const QString& listName);
|
||||
|
||||
/**jsdoc
|
||||
* Query the highlight style values for the named selection.
|
||||
* If the Selection doesn't exist or hasn't been highlight enabled yet, it will return an empty object.
|
||||
|
@ -223,9 +248,9 @@ private:
|
|||
template <class T> bool removeFromGameplayObjects(const QString& listName, T idToRemove);
|
||||
|
||||
void setupHandler(const QString& selectionName);
|
||||
void removeHandler(const QString& selectionName);
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_SelectionScriptingInterface_h
|
||||
|
|
|
@ -78,13 +78,17 @@ void WindowScriptingInterface::setFocus() {
|
|||
});
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::raiseMainWindow() {
|
||||
void WindowScriptingInterface::raise() {
|
||||
// It's forbidden to call raise() from another thread.
|
||||
qApp->postLambdaEvent([] {
|
||||
qApp->raise();
|
||||
});
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::raiseMainWindow() {
|
||||
raise();
|
||||
}
|
||||
|
||||
/// Display an alert box
|
||||
/// \param const QString& message message to display
|
||||
/// \return QScriptValue::UndefinedValue
|
||||
|
|
|
@ -68,9 +68,16 @@ public slots:
|
|||
*/
|
||||
void setFocus();
|
||||
|
||||
/**jsdoc
|
||||
* Raise the Interface window if it is minimized. If raised, the window gains focus.
|
||||
* @function Window.raise
|
||||
*/
|
||||
void raise();
|
||||
|
||||
/**jsdoc
|
||||
* Raise the Interface window if it is minimized. If raised, the window gains focus.
|
||||
* @function Window.raiseMainWindow
|
||||
* @deprecated Use {@link Window.raise|raise} instead.
|
||||
*/
|
||||
void raiseMainWindow();
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
|
|||
QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename) {
|
||||
|
||||
// adding URL to snapshot
|
||||
QUrl currentURL = DependencyManager::get<AddressManager>()->currentShareableAddress();
|
||||
QUrl currentURL = DependencyManager::get<AddressManager>()->currentPublicAddress();
|
||||
shot.setText(URL, currentURL.toString());
|
||||
|
||||
QString username = DependencyManager::get<AccountManager>()->getAccountInfo().getUsername();
|
||||
|
|
|
@ -65,7 +65,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) {
|
|||
|
||||
} else {
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(true, contents);
|
||||
delete this;
|
||||
this->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,23 +75,27 @@ void SnapshotUploader::uploadFailure(QNetworkReply& reply) {
|
|||
if (replyString.size() == 0) {
|
||||
replyString = reply.errorString();
|
||||
}
|
||||
replyString = replyString.left(1000); // Only print first 1000 characters of error
|
||||
qDebug() << "Snapshot upload reply error (truncated):" << replyString;
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(true, replyString); // maybe someday include _inWorldLocation, _filename?
|
||||
delete this;
|
||||
this->deleteLater();
|
||||
}
|
||||
|
||||
void SnapshotUploader::createStorySuccess(QNetworkReply& reply) {
|
||||
QString replyString = reply.readAll();
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(false, replyString);
|
||||
delete this;
|
||||
this->deleteLater();
|
||||
}
|
||||
|
||||
void SnapshotUploader::createStoryFailure(QNetworkReply& reply) {
|
||||
QString replyString = reply.readAll();
|
||||
qDebug() << "Error " << reply.errorString() << " uploading snapshot " << _pathname << " from " << _inWorldLocation;
|
||||
qDebug() << "Error " << reply.errorString() << " uploading snapshot story " << _pathname << " from " << _inWorldLocation;
|
||||
if (replyString.size() == 0) {
|
||||
replyString = reply.errorString();
|
||||
}
|
||||
replyString = replyString.left(1000); // Only print first 1000 characters of error
|
||||
qDebug() << "Snapshot story upload reply error (truncated):" << replyString;
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(true, replyString);
|
||||
delete this;
|
||||
this->deleteLater();
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,9 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
|||
_entityPropertyFlags += PROP_OWNING_AVATAR_ID;
|
||||
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>().data();
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, &ContextOverlayInterface::createOrDestroyContextOverlay);
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::clickDownOnEntity, this, &ContextOverlayInterface::clickDownOnEntity);
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::holdingClickOnEntity, this, &ContextOverlayInterface::holdingClickOnEntity);
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, &ContextOverlayInterface::mouseReleaseOnEntity);
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity, this, &ContextOverlayInterface::contextOverlays_hoverEnterEntity);
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity, this, &ContextOverlayInterface::contextOverlays_hoverLeaveEntity);
|
||||
connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() {
|
||||
|
@ -97,6 +99,31 @@ void ContextOverlayInterface::setEnabled(bool enabled) {
|
|||
_enabled = enabled;
|
||||
}
|
||||
|
||||
void ContextOverlayInterface::clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||
if (_enabled && event.getButton() == PointerEvent::SecondaryButton && contextOverlayFilterPassed(entityItemID)) {
|
||||
_mouseDownEntity = entityItemID;
|
||||
_mouseDownEntityTimestamp = usecTimestampNow();
|
||||
} else {
|
||||
if (!_currentEntityWithContextOverlay.isNull()) {
|
||||
disableEntityHighlight(_currentEntityWithContextOverlay);
|
||||
destroyContextOverlay(_currentEntityWithContextOverlay, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const float CONTEXT_OVERLAY_CLICK_HOLD_TIME_MSEC = 400.0f;
|
||||
void ContextOverlayInterface::holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||
if (!_mouseDownEntity.isNull() && ((usecTimestampNow() - _mouseDownEntityTimestamp) > (CONTEXT_OVERLAY_CLICK_HOLD_TIME_MSEC * USECS_PER_MSEC))) {
|
||||
_mouseDownEntity = EntityItemID();
|
||||
}
|
||||
}
|
||||
|
||||
void ContextOverlayInterface::mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||
if (_enabled && event.getButton() == PointerEvent::SecondaryButton && contextOverlayFilterPassed(entityItemID) && _mouseDownEntity == entityItemID) {
|
||||
createOrDestroyContextOverlay(entityItemID, event);
|
||||
}
|
||||
}
|
||||
|
||||
bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||
if (_enabled && event.getButton() == PointerEvent::SecondaryButton) {
|
||||
if (contextOverlayFilterPassed(entityItemID)) {
|
||||
|
|
|
@ -64,6 +64,10 @@ signals:
|
|||
void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay);
|
||||
|
||||
public slots:
|
||||
void clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
|
||||
bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
bool destroyContextOverlay(const EntityItemID& entityItemID);
|
||||
|
@ -84,6 +88,8 @@ private:
|
|||
};
|
||||
bool _verboseLogging{ true };
|
||||
bool _enabled { true };
|
||||
EntityItemID _mouseDownEntity{};
|
||||
quint64 _mouseDownEntityTimestamp;
|
||||
EntityItemID _currentEntityWithContextOverlay{};
|
||||
EntityItemID _lastInspectedEntity{};
|
||||
QString _entityMarketplaceID;
|
||||
|
|
|
@ -54,6 +54,7 @@ Overlays::Overlays() {
|
|||
}
|
||||
|
||||
void Overlays::cleanupAllOverlays() {
|
||||
_shuttingDown = true;
|
||||
QMap<OverlayID, Overlay::Pointer> overlaysHUD;
|
||||
QMap<OverlayID, Overlay::Pointer> overlaysWorld;
|
||||
{
|
||||
|
@ -147,6 +148,10 @@ void Overlays::enable() {
|
|||
// Note, can't be invoked by scripts, but can be called by the InterfaceParentFinder
|
||||
// class on packet processing threads
|
||||
Overlay::Pointer Overlays::getOverlay(OverlayID id) const {
|
||||
if (_shuttingDown) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (_overlaysHUD.contains(id)) {
|
||||
return _overlaysHUD[id];
|
||||
|
@ -157,6 +162,10 @@ Overlay::Pointer Overlays::getOverlay(OverlayID id) const {
|
|||
}
|
||||
|
||||
OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) {
|
||||
if (_shuttingDown) {
|
||||
return UNKNOWN_OVERLAY_ID;
|
||||
}
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayID result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
|
@ -261,6 +270,10 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties)
|
|||
}
|
||||
|
||||
OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) {
|
||||
if (_shuttingDown) {
|
||||
return UNKNOWN_OVERLAY_ID;
|
||||
}
|
||||
|
||||
OverlayID thisID = OverlayID(QUuid::createUuid());
|
||||
overlay->setOverlayID(thisID);
|
||||
overlay->setStackOrder(_stackOrder++);
|
||||
|
@ -283,6 +296,10 @@ OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) {
|
|||
}
|
||||
|
||||
OverlayID Overlays::cloneOverlay(OverlayID id) {
|
||||
if (_shuttingDown) {
|
||||
return UNKNOWN_OVERLAY_ID;
|
||||
}
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayID result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
|
@ -301,6 +318,10 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
|
|||
}
|
||||
|
||||
bool Overlays::editOverlay(OverlayID id, const QVariant& properties) {
|
||||
if (_shuttingDown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto thisOverlay = getOverlay(id);
|
||||
if (!thisOverlay) {
|
||||
return false;
|
||||
|
@ -320,6 +341,10 @@ bool Overlays::editOverlay(OverlayID id, const QVariant& properties) {
|
|||
}
|
||||
|
||||
bool Overlays::editOverlays(const QVariant& propertiesById) {
|
||||
if (_shuttingDown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool defer2DOverlays = QThread::currentThread() != thread();
|
||||
|
||||
QVariantMap deferrred;
|
||||
|
@ -351,6 +376,10 @@ bool Overlays::editOverlays(const QVariant& propertiesById) {
|
|||
}
|
||||
|
||||
void Overlays::deleteOverlay(OverlayID id) {
|
||||
if (_shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "deleteOverlay", Q_ARG(OverlayID, id));
|
||||
return;
|
||||
|
@ -374,6 +403,9 @@ void Overlays::deleteOverlay(OverlayID id) {
|
|||
}
|
||||
|
||||
QString Overlays::getOverlayType(OverlayID overlayId) {
|
||||
if (_shuttingDown) {
|
||||
return "";
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QString result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
|
@ -388,8 +420,23 @@ QString Overlays::getOverlayType(OverlayID overlayId) {
|
|||
return "";
|
||||
}
|
||||
|
||||
QObject* Overlays::getOverlayObject(OverlayID id) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QObject* result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(OverlayID, id));
|
||||
return result;
|
||||
}
|
||||
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
if (thisOverlay) {
|
||||
return qobject_cast<QObject*>(&(*thisOverlay));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
||||
if (!_enabled) {
|
||||
if (_shuttingDown || !_enabled) {
|
||||
return UNKNOWN_OVERLAY_ID;
|
||||
}
|
||||
|
||||
|
|
|
@ -235,6 +235,50 @@ public slots:
|
|||
*/
|
||||
QString getOverlayType(OverlayID overlayId);
|
||||
|
||||
/**jsdoc
|
||||
* Get the overlay script object. In particular, this is useful for accessing the event bridge for a <code>web3d</code>
|
||||
* overlay.
|
||||
* @function Overlays.getOverlayObject
|
||||
* @param {Uuid} overlayID - The ID of the overlay to get the script object of.
|
||||
* @returns {object} The script object for the overlay if found.
|
||||
* @example <caption>Receive "hello" messages from a <code>web3d</code> overlay.</caption>
|
||||
* // HTML file: name "web3d.html".
|
||||
* <!DOCTYPE html>
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>HELLO</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* <h1>HELLO</h1></h1>
|
||||
* <script>
|
||||
* setInterval(function () {
|
||||
* EventBridge.emitWebEvent("hello");
|
||||
* }, 2000);
|
||||
* </script>
|
||||
* </body>
|
||||
* </html>
|
||||
*
|
||||
* // Script file.
|
||||
* var web3dOverlay = Overlays.addOverlay("web3d", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.5, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* url: Script.resolvePath("web3d.html"),
|
||||
* alpha: 1.0
|
||||
* });
|
||||
*
|
||||
* function onWebEventReceived(event) {
|
||||
* print("onWebEventReceived() : " + JSON.stringify(event));
|
||||
* }
|
||||
*
|
||||
* overlayObject = Overlays.getOverlayObject(web3dOverlay);
|
||||
* overlayObject.webEventReceived.connect(onWebEventReceived);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Overlays.deleteOverlay(web3dOverlay);
|
||||
* });
|
||||
*/
|
||||
QObject* getOverlayObject(OverlayID id);
|
||||
|
||||
/**jsdoc
|
||||
* Get the ID of the 2D overlay at a particular point on the screen or HUD.
|
||||
* @function Overlays.getOverlayAtPoint
|
||||
|
@ -680,6 +724,7 @@ private:
|
|||
unsigned int _stackOrder { 1 };
|
||||
|
||||
bool _enabled = true;
|
||||
std::atomic<bool> _shuttingDown{ false };
|
||||
|
||||
PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult,
|
||||
QMouseEvent* event, PointerEvent::EventType eventType);
|
||||
|
|
|
@ -39,18 +39,20 @@ void QmlOverlay::buildQmlElement(const QUrl& url) {
|
|||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->load(url, [=](QQmlContext* context, QObject* object) {
|
||||
QQuickItem* rawPtr = dynamic_cast<QQuickItem*>(object);
|
||||
// Create a shared ptr with a custom deleter lambda, that calls deleteLater
|
||||
_qmlElement = std::shared_ptr<QQuickItem>(rawPtr, [](QQuickItem* ptr) {
|
||||
if (ptr) {
|
||||
ptr->deleteLater();
|
||||
}
|
||||
});
|
||||
_qmlElement = dynamic_cast<QQuickItem*>(object);
|
||||
connect(_qmlElement, &QObject::destroyed, this, &QmlOverlay::qmlElementDestroyed);
|
||||
});
|
||||
}
|
||||
|
||||
void QmlOverlay::qmlElementDestroyed() {
|
||||
_qmlElement = nullptr;
|
||||
}
|
||||
|
||||
QmlOverlay::~QmlOverlay() {
|
||||
_qmlElement.reset();
|
||||
if (_qmlElement) {
|
||||
_qmlElement->deleteLater();
|
||||
}
|
||||
_qmlElement = nullptr;
|
||||
}
|
||||
|
||||
// QmlOverlay replaces Overlay's properties with those defined in the QML file used but keeps Overlay2D's properties.
|
||||
|
@ -62,15 +64,13 @@ void QmlOverlay::setProperties(const QVariantMap& properties) {
|
|||
|
||||
Overlay2D::setProperties(properties);
|
||||
auto bounds = _bounds;
|
||||
std::weak_ptr<QQuickItem> weakQmlElement = _qmlElement;
|
||||
// check to see if qmlElement still exists
|
||||
auto qmlElement = weakQmlElement.lock();
|
||||
if (qmlElement) {
|
||||
qmlElement->setX(bounds.left());
|
||||
qmlElement->setY(bounds.top());
|
||||
qmlElement->setWidth(bounds.width());
|
||||
qmlElement->setHeight(bounds.height());
|
||||
QMetaObject::invokeMethod(qmlElement.get(), "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties));
|
||||
if (_qmlElement) {
|
||||
_qmlElement->setX(bounds.left());
|
||||
_qmlElement->setY(bounds.top());
|
||||
_qmlElement->setWidth(bounds.width());
|
||||
_qmlElement->setHeight(bounds.height());
|
||||
QMetaObject::invokeMethod(_qmlElement, "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,10 +32,11 @@ public:
|
|||
void render(RenderArgs* args) override;
|
||||
|
||||
private:
|
||||
Q_INVOKABLE void qmlElementDestroyed();
|
||||
Q_INVOKABLE void buildQmlElement(const QUrl& url);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<QQuickItem> _qmlElement;
|
||||
QQuickItem* _qmlElement{ nullptr };
|
||||
};
|
||||
|
||||
#endif // hifi_QmlOverlay_h
|
||||
|
|
|
@ -1246,6 +1246,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) {
|
||||
|
||||
const bool ENABLE_POLE_VECTORS = false;
|
||||
const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
||||
|
||||
int hipsIndex = indexOfJoint("Hips");
|
||||
|
@ -1268,7 +1269,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
int handJointIndex = _animSkeleton->nameToJointIndex("LeftHand");
|
||||
int armJointIndex = _animSkeleton->nameToJointIndex("LeftArm");
|
||||
int elbowJointIndex = _animSkeleton->nameToJointIndex("LeftForeArm");
|
||||
if (!leftArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||
if (ENABLE_POLE_VECTORS && !leftArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, true);
|
||||
|
||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||
|
@ -1315,7 +1316,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
int handJointIndex = _animSkeleton->nameToJointIndex("RightHand");
|
||||
int armJointIndex = _animSkeleton->nameToJointIndex("RightArm");
|
||||
int elbowJointIndex = _animSkeleton->nameToJointIndex("RightForeArm");
|
||||
if (!rightArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||
if (ENABLE_POLE_VECTORS && !rightArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
|
||||
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, false);
|
||||
|
||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||
|
@ -1555,18 +1556,21 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
|||
updateFeet(leftFootEnabled, rightFootEnabled,
|
||||
params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot]);
|
||||
|
||||
|
||||
if (headEnabled) {
|
||||
// Blend IK chains toward the joint limit centers, this should stablize head and hand ik.
|
||||
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToLimitCenterPoses);
|
||||
} else {
|
||||
// Blend IK chains toward the UnderPoses, so some of the animaton motion is present in the IK solution.
|
||||
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToUnderPoses);
|
||||
}
|
||||
|
||||
// if the hips or the feet are being controlled.
|
||||
if (hipsEnabled || rightFootEnabled || leftFootEnabled) {
|
||||
// for more predictable IK solve from the center of the joint limits, not from the underpose
|
||||
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToLimitCenterPoses);
|
||||
|
||||
// replace the feet animation with the default pose, this is to prevent unexpected toe wiggling.
|
||||
_animVars.set("defaultPoseOverlayAlpha", 1.0f);
|
||||
_animVars.set("defaultPoseOverlayBoneSet", (int)AnimOverlay::BothFeetBoneSet);
|
||||
} else {
|
||||
// augment the IK with the underPose.
|
||||
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToUnderPoses);
|
||||
|
||||
// feet should follow source animation
|
||||
_animVars.unset("defaultPoseOverlayAlpha");
|
||||
_animVars.unset("defaultPoseOverlayBoneSet");
|
||||
|
|
|
@ -183,6 +183,7 @@ namespace controller {
|
|||
makeButtonPair(Action::ACTION2, "ACTION2"),
|
||||
makeButtonPair(Action::CONTEXT_MENU, "CONTEXT_MENU"),
|
||||
makeButtonPair(Action::TOGGLE_MUTE, "TOGGLE_MUTE"),
|
||||
makeButtonPair(Action::SPRINT, "SPRINT")
|
||||
};
|
||||
return availableInputs;
|
||||
}
|
||||
|
|
|
@ -174,6 +174,7 @@ enum class Action {
|
|||
TRACKED_OBJECT_13,
|
||||
TRACKED_OBJECT_14,
|
||||
TRACKED_OBJECT_15,
|
||||
SPRINT,
|
||||
|
||||
NUM_ACTIONS
|
||||
};
|
||||
|
|
|
@ -6,10 +6,43 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// NOTE: we don't need to include this header unless/until we add additional symbols.
|
||||
// By removing this header we prevent these warnings on windows:
|
||||
//
|
||||
// warning LNK4221: This object file does not define any previously undefined public symbols,
|
||||
// so it will not be used by any link operation that consumes this library
|
||||
//
|
||||
//#include "JSEndpoint.h"
|
||||
#include "JSEndpoint.h"
|
||||
#include "../../Logging.h"
|
||||
|
||||
using namespace controller;
|
||||
|
||||
QString formatException(const QJSValue& exception) {
|
||||
QString note { "UncaughtException" };
|
||||
QString result;
|
||||
|
||||
const auto message = exception.toString();
|
||||
const auto fileName = exception.property("fileName").toString();
|
||||
const auto lineNumber = exception.property("lineNumber").toString();
|
||||
const auto stacktrace = exception.property("stack").toString();
|
||||
|
||||
const QString SCRIPT_EXCEPTION_FORMAT = "[%0] %1 in %2:%3";
|
||||
const QString SCRIPT_BACKTRACE_SEP = "\n ";
|
||||
|
||||
result = QString(SCRIPT_EXCEPTION_FORMAT).arg(note, message, fileName, lineNumber);
|
||||
if (!stacktrace.isEmpty()) {
|
||||
result += QString("\n[Backtrace]%1%2").arg(SCRIPT_BACKTRACE_SEP).arg(stacktrace);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float JSEndpoint::peek() const {
|
||||
QJSValue result = _callable.call();
|
||||
if (result.isError()) {
|
||||
qCDebug(controllers).noquote() << formatException(result);
|
||||
return 0.0f;
|
||||
} else {
|
||||
return (float)result.toNumber();
|
||||
}
|
||||
}
|
||||
|
||||
void JSEndpoint::apply(float newValue, const Pointer& source) {
|
||||
QJSValue result = _callable.call(QJSValueList({ QJSValue(newValue) }));
|
||||
if (result.isError()) {
|
||||
qCDebug(controllers).noquote() << formatException(result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,16 +24,11 @@ public:
|
|||
: Endpoint(Input::INVALID_INPUT), _callable(callable) {
|
||||
}
|
||||
|
||||
virtual float peek() const override {
|
||||
return (float)const_cast<JSEndpoint*>(this)->_callable.call().toNumber();
|
||||
}
|
||||
|
||||
virtual void apply(float newValue, const Pointer& source) override {
|
||||
_callable.call(QJSValueList({ QJSValue(newValue) }));
|
||||
}
|
||||
virtual float peek() const override;
|
||||
virtual void apply(float newValue, const Pointer& source) override;
|
||||
|
||||
private:
|
||||
QJSValue _callable;
|
||||
mutable QJSValue _callable;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
#include "ScriptEndpoint.h"
|
||||
#include "../../Logging.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
|
||||
|
@ -14,6 +15,25 @@
|
|||
|
||||
using namespace controller;
|
||||
|
||||
QString formatException(const QScriptValue& exception) {
|
||||
QString note { "UncaughtException" };
|
||||
QString result;
|
||||
|
||||
const auto message = exception.toString();
|
||||
const auto fileName = exception.property("fileName").toString();
|
||||
const auto lineNumber = exception.property("lineNumber").toString();
|
||||
const auto stacktrace = exception.property("stack").toString();
|
||||
|
||||
const QString SCRIPT_EXCEPTION_FORMAT = "[%0] %1 in %2:%3";
|
||||
const QString SCRIPT_BACKTRACE_SEP = "\n ";
|
||||
|
||||
result = QString(SCRIPT_EXCEPTION_FORMAT).arg(note, message, fileName, lineNumber);
|
||||
if (!stacktrace.isEmpty()) {
|
||||
result += QString("\n[Backtrace]%1%2").arg(SCRIPT_BACKTRACE_SEP).arg(stacktrace);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float ScriptEndpoint::peek() const {
|
||||
const_cast<ScriptEndpoint*>(this)->updateValue();
|
||||
return _lastValueRead;
|
||||
|
@ -26,10 +46,11 @@ void ScriptEndpoint::updateValue() {
|
|||
}
|
||||
|
||||
QScriptValue result = _callable.call();
|
||||
|
||||
// If the callable ever returns a non-number, we assume it's a pose
|
||||
// and start reporting ourselves as a pose.
|
||||
if (result.isNumber()) {
|
||||
if (result.isError()) {
|
||||
// print JavaScript exception
|
||||
qCDebug(controllers).noquote() << formatException(result);
|
||||
_lastValueRead = 0.0f;
|
||||
} else if (result.isNumber()) {
|
||||
_lastValueRead = (float)_callable.call().toNumber();
|
||||
} else {
|
||||
Pose::fromScriptValue(result, _lastPoseRead);
|
||||
|
@ -52,8 +73,12 @@ void ScriptEndpoint::internalApply(float value, int sourceID) {
|
|||
Q_ARG(int, sourceID));
|
||||
return;
|
||||
}
|
||||
_callable.call(QScriptValue(),
|
||||
QScriptValue result = _callable.call(QScriptValue(),
|
||||
QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) }));
|
||||
if (result.isError()) {
|
||||
// print JavaScript exception
|
||||
qCDebug(controllers).noquote() << formatException(result);
|
||||
}
|
||||
}
|
||||
|
||||
Pose ScriptEndpoint::peekPose() const {
|
||||
|
@ -67,6 +92,10 @@ void ScriptEndpoint::updatePose() {
|
|||
return;
|
||||
}
|
||||
QScriptValue result = _callable.call();
|
||||
if (result.isError()) {
|
||||
// print JavaScript exception
|
||||
qCDebug(controllers).noquote() << formatException(result);
|
||||
}
|
||||
Pose::fromScriptValue(result, _lastPoseRead);
|
||||
}
|
||||
|
||||
|
@ -85,6 +114,10 @@ void ScriptEndpoint::internalApply(const Pose& newPose, int sourceID) {
|
|||
Q_ARG(int, sourceID));
|
||||
return;
|
||||
}
|
||||
_callable.call(QScriptValue(),
|
||||
QScriptValue result = _callable.call(QScriptValue(),
|
||||
QScriptValueList({ Pose::toScriptValue(_callable.engine(), newPose), QScriptValue(sourceID) }));
|
||||
if (result.isError()) {
|
||||
// print JavaScript exception
|
||||
qCDebug(controllers).noquote() << formatException(result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,11 +25,11 @@ static const QString FULLSCREEN = "Fullscreen";
|
|||
|
||||
void Basic2DWindowOpenGLDisplayPlugin::customizeContext() {
|
||||
#if defined(Q_OS_ANDROID)
|
||||
qreal dpi = getFullscreenTarget()->physicalDotsPerInch();
|
||||
_virtualPadPixelSize = dpi * VirtualPad::Manager::BASE_DIAMETER_PIXELS / VirtualPad::Manager::DPI;
|
||||
|
||||
auto iconPath = PathUtils::resourcesPath() + "images/analog_stick.png";
|
||||
auto image = QImage(iconPath);
|
||||
qreal dpi = getFullscreenTarget()->physicalDotsPerInch();
|
||||
_virtualPadPixelSize = dpi * VirtualPad::Manager::PIXEL_SIZE / VirtualPad::Manager::DPI;
|
||||
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
@ -69,6 +69,29 @@ void Basic2DWindowOpenGLDisplayPlugin::customizeContext() {
|
|||
_virtualPadStickBaseTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
_virtualPadStickBaseTexture->setAutoGenerateMips(true);
|
||||
}
|
||||
|
||||
_virtualPadJumpBtnPixelSize = dpi * VirtualPad::Manager::JUMP_BTN_FULL_PIXELS / VirtualPad::Manager::DPI;
|
||||
iconPath = PathUtils::resourcesPath() + "images/fly.png";
|
||||
image = QImage(iconPath);
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
image = image.scaled(_virtualPadJumpBtnPixelSize, _virtualPadJumpBtnPixelSize, Qt::KeepAspectRatio);
|
||||
image = image.mirrored();
|
||||
|
||||
_virtualPadJumpBtnTexture = gpu::Texture::createStrict(
|
||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
||||
image.width(), image.height(),
|
||||
gpu::Texture::MAX_NUM_MIPS,
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
|
||||
_virtualPadJumpBtnTexture->setSource("virtualPad jump");
|
||||
auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha();
|
||||
_virtualPadJumpBtnTexture->setUsage(usage.build());
|
||||
_virtualPadJumpBtnTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||
_virtualPadJumpBtnTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
_virtualPadJumpBtnTexture->setAutoGenerateMips(true);
|
||||
}
|
||||
#endif
|
||||
Parent::customizeContext();
|
||||
}
|
||||
|
@ -124,6 +147,20 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() {
|
|||
batch.setViewportTransform(ivec4(uvec2(0), getRecommendedRenderSize()));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
|
||||
// render stick head
|
||||
auto jumpTransform = DependencyManager::get<CompositorHelper>()->getPoint2DTransform(virtualPadManager.getJumpButtonPosition(),
|
||||
_virtualPadJumpBtnPixelSize, _virtualPadJumpBtnPixelSize);
|
||||
render([&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setProjectionTransform(mat4());
|
||||
batch.setPipeline(_cursorPipeline);
|
||||
batch.setResourceTexture(0, _virtualPadJumpBtnTexture);
|
||||
batch.resetViewTransform();
|
||||
batch.setModelTransform(jumpTransform);
|
||||
batch.setViewportTransform(ivec4(uvec2(0), getRecommendedRenderSize()));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
Parent::compositeExtra();
|
||||
|
|
|
@ -46,5 +46,8 @@ private:
|
|||
gpu::TexturePointer _virtualPadStickTexture;
|
||||
gpu::TexturePointer _virtualPadStickBaseTexture;
|
||||
qreal _virtualPadPixelSize;
|
||||
|
||||
gpu::TexturePointer _virtualPadJumpBtnTexture;
|
||||
qreal _virtualPadJumpBtnPixelSize;
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -135,10 +135,8 @@ void EntityRenderer::makeStatusGetters(const EntityItemPointer& entity, Item::St
|
|||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> make_renderer(const EntityItemPointer& entity) {
|
||||
T* rawResult = new T(entity);
|
||||
|
||||
// We want to use deleteLater so that renderer destruction gets pushed to the main thread
|
||||
return std::shared_ptr<T>(rawResult, std::bind(&QObject::deleteLater, rawResult));
|
||||
return std::shared_ptr<T>(new T(entity), [](T* ptr) { ptr->deleteLater(); });
|
||||
}
|
||||
|
||||
EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _entity(entity) {
|
||||
|
|
|
@ -131,6 +131,8 @@ ItemKey ShapeEntityRenderer::getKey() {
|
|||
withReadLock([&] {
|
||||
if (isTransparent()) {
|
||||
builder.withTransparent();
|
||||
} else if (_canCastShadow) {
|
||||
builder.withShadowCaster();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ static const QString WEB_ENTITY_QML = "controls/WebEntityView.qml";
|
|||
|
||||
const float METERS_TO_INCHES = 39.3701f;
|
||||
static uint32_t _currentWebCount{ 0 };
|
||||
// Don't allow more than 100 concurrent web views
|
||||
// Don't allow more than 20 concurrent web views
|
||||
static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 20;
|
||||
// If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer
|
||||
static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND;
|
||||
|
@ -88,8 +88,14 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe
|
|||
return true;
|
||||
}
|
||||
|
||||
if (uvec2(getWindowSize(entity)) != toGlm(_webSurface->size())) {
|
||||
return true;
|
||||
{
|
||||
QSharedPointer<OffscreenQmlSurface> webSurface;
|
||||
withReadLock([&] {
|
||||
webSurface = _webSurface;
|
||||
});
|
||||
if (webSurface && uvec2(getWindowSize(entity)) != toGlm(webSurface->size())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_lastSourceUrl != entity->getSourceUrl()) {
|
||||
|
@ -108,9 +114,15 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe
|
|||
}
|
||||
|
||||
bool WebEntityRenderer::needsRenderUpdate() const {
|
||||
if (!_webSurface) {
|
||||
// If we have rendered recently, and there is no web surface, we're going to create one
|
||||
return true;
|
||||
{
|
||||
QSharedPointer<OffscreenQmlSurface> webSurface;
|
||||
withReadLock([&] {
|
||||
webSurface = _webSurface;
|
||||
});
|
||||
if (!webSurface) {
|
||||
// If we have rendered recently, and there is no web surface, we're going to create one
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Parent::needsRenderUpdate();
|
||||
|
|
|
@ -1911,7 +1911,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
|
|||
}
|
||||
}
|
||||
|
||||
void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) {
|
||||
void EntityItem::setSimulationOwner(const QUuid& id, uint8_t priority) {
|
||||
if (wantTerseEditLogging() && (id != _simulationOwner.getID() || priority != _simulationOwner.getPriority())) {
|
||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << id << priority;
|
||||
}
|
||||
|
@ -1942,7 +1942,7 @@ void EntityItem::clearSimulationOwnership() {
|
|||
|
||||
}
|
||||
|
||||
void EntityItem::setPendingOwnershipPriority(quint8 priority, const quint64& timestamp) {
|
||||
void EntityItem::setPendingOwnershipPriority(uint8_t priority, const quint64& timestamp) {
|
||||
_simulationOwner.setPendingPriority(priority, timestamp);
|
||||
}
|
||||
|
||||
|
@ -2962,13 +2962,6 @@ void EntityItem::retrieveMarketplacePublicKey() {
|
|||
}
|
||||
|
||||
void EntityItem::preDelete() {
|
||||
// clear out any left-over actions
|
||||
EntityTreeElementPointer element = _element; // use local copy of _element for logic below
|
||||
EntityTreePointer entityTree = element ? element->getTree() : nullptr;
|
||||
EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr;
|
||||
if (simulation) {
|
||||
clearActions(simulation);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {
|
||||
|
|
|
@ -304,14 +304,14 @@ public:
|
|||
|
||||
// FIXME not thread safe?
|
||||
const SimulationOwner& getSimulationOwner() const { return _simulationOwner; }
|
||||
void setSimulationOwner(const QUuid& id, quint8 priority);
|
||||
void setSimulationOwner(const QUuid& id, uint8_t priority);
|
||||
void setSimulationOwner(const SimulationOwner& owner);
|
||||
void promoteSimulationPriority(quint8 priority);
|
||||
void promoteSimulationPriority(uint8_t priority);
|
||||
|
||||
quint8 getSimulationPriority() const { return _simulationOwner.getPriority(); }
|
||||
uint8_t getSimulationPriority() const { return _simulationOwner.getPriority(); }
|
||||
QUuid getSimulatorID() const { return _simulationOwner.getID(); }
|
||||
void clearSimulationOwnership();
|
||||
void setPendingOwnershipPriority(quint8 priority, const quint64& timestamp);
|
||||
void setPendingOwnershipPriority(uint8_t priority, const quint64& timestamp);
|
||||
uint8_t getPendingOwnershipPriority() const { return _simulationOwner.getPendingPriority(); }
|
||||
void rememberHasSimulationOwnershipBid() const;
|
||||
|
||||
|
|
|
@ -326,7 +326,7 @@ public:
|
|||
void clearSimulationOwner();
|
||||
void setSimulationOwner(const QUuid& id, uint8_t priority);
|
||||
void setSimulationOwner(const QByteArray& data);
|
||||
void promoteSimulationPriority(quint8 priority) { _simulationOwner.promotePriority(priority); }
|
||||
void promoteSimulationPriority(uint8_t priority) { _simulationOwner.promotePriority(priority); }
|
||||
|
||||
void setActionDataDirty() { _actionDataChanged = true; }
|
||||
|
||||
|
|
|
@ -254,17 +254,6 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent);
|
||||
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
|
||||
|
||||
auto dimensions = propertiesWithSimID.getDimensions();
|
||||
float volume = dimensions.x * dimensions.y * dimensions.z;
|
||||
auto density = propertiesWithSimID.getDensity();
|
||||
auto newVelocity = propertiesWithSimID.getVelocity().length();
|
||||
float cost = calculateCost(density * volume, 0, newVelocity);
|
||||
cost *= costMultiplier;
|
||||
|
||||
if (cost > _currentAvatarEnergy) {
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
EntityItemID id = EntityItemID(QUuid::createUuid());
|
||||
|
||||
// If we have a local entity tree set, then also update it.
|
||||
|
@ -295,9 +284,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
|
||||
// queue the packet
|
||||
if (success) {
|
||||
emit debitEnergySource(cost);
|
||||
queueEntityMessage(PacketType::EntityAdd, id, propertiesWithSimID);
|
||||
|
||||
return id;
|
||||
} else {
|
||||
return QUuid();
|
||||
|
@ -378,27 +365,9 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
|
||||
EntityItemProperties properties = scriptSideProperties;
|
||||
|
||||
auto dimensions = properties.getDimensions();
|
||||
float volume = dimensions.x * dimensions.y * dimensions.z;
|
||||
auto density = properties.getDensity();
|
||||
auto newVelocity = properties.getVelocity().length();
|
||||
float oldVelocity = { 0.0f };
|
||||
|
||||
EntityItemID entityID(id);
|
||||
if (!_entityTree) {
|
||||
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
|
||||
|
||||
//if there is no local entity entity tree, no existing velocity, use 0.
|
||||
float cost = calculateCost(density * volume, oldVelocity, newVelocity);
|
||||
cost *= costMultiplier;
|
||||
|
||||
if (cost > _currentAvatarEnergy) {
|
||||
return QUuid();
|
||||
} else {
|
||||
//debit the avatar energy and continue
|
||||
emit debitEnergySource(cost);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
// If we have a local entity tree set, then also update it.
|
||||
|
@ -420,9 +389,6 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
// All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them.
|
||||
// If any of these changed, pull any missing properties from the entity.
|
||||
|
||||
//existing entity, retrieve old velocity for check down below
|
||||
oldVelocity = entity->getWorldVelocity().length();
|
||||
|
||||
if (!scriptSideProperties.parentIDChanged()) {
|
||||
properties.setParentID(entity->getParentID());
|
||||
}
|
||||
|
@ -442,23 +408,11 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
properties.setClientOnly(entity->getClientOnly());
|
||||
properties.setOwningAvatarID(entity->getOwningAvatarID());
|
||||
properties = convertPropertiesFromScriptSemantics(properties, properties.getScalesWithParent());
|
||||
|
||||
float cost = calculateCost(density * volume, oldVelocity, newVelocity);
|
||||
cost *= costMultiplier;
|
||||
|
||||
if (cost > _currentAvatarEnergy) {
|
||||
updatedEntity = false;
|
||||
} else {
|
||||
//debit the avatar energy and continue
|
||||
updatedEntity = _entityTree->updateEntity(entityID, properties);
|
||||
if (updatedEntity) {
|
||||
emit debitEnergySource(cost);
|
||||
}
|
||||
}
|
||||
updatedEntity = _entityTree->updateEntity(entityID, properties);
|
||||
});
|
||||
|
||||
// FIXME: We need to figure out a better way to handle this. Allowing these edits to go through potentially
|
||||
// breaks avatar energy and entities that are parented.
|
||||
// breaks entities that are parented.
|
||||
//
|
||||
// To handle cases where a script needs to edit an entity with a _known_ entity id but doesn't exist
|
||||
// in the local entity tree, we need to allow those edits to go through to the server.
|
||||
|
@ -577,21 +531,6 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
|
|||
return;
|
||||
}
|
||||
|
||||
auto dimensions = entity->getScaledDimensions();
|
||||
float volume = dimensions.x * dimensions.y * dimensions.z;
|
||||
auto density = entity->getDensity();
|
||||
auto velocity = entity->getWorldVelocity().length();
|
||||
float cost = calculateCost(density * volume, velocity, 0);
|
||||
cost *= costMultiplier;
|
||||
|
||||
if (cost > _currentAvatarEnergy) {
|
||||
shouldDelete = false;
|
||||
return;
|
||||
} else {
|
||||
//debit the avatar energy and continue
|
||||
emit debitEnergySource(cost);
|
||||
}
|
||||
|
||||
if (entity->getLocked()) {
|
||||
shouldDelete = false;
|
||||
} else {
|
||||
|
@ -1812,23 +1751,6 @@ void EntityScriptingInterface::emitScriptEvent(const EntityItemID& entityID, con
|
|||
}
|
||||
}
|
||||
|
||||
float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) {
|
||||
return std::abs(mass * (newVelocity - oldVelocity));
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::setCurrentAvatarEnergy(float energy) {
|
||||
// qCDebug(entities) << "NEW AVATAR ENERGY IN ENTITY SCRIPTING INTERFACE: " << energy;
|
||||
_currentAvatarEnergy = energy;
|
||||
}
|
||||
|
||||
float EntityScriptingInterface::getCostMultiplier() {
|
||||
return costMultiplier;
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::setCostMultiplier(float value) {
|
||||
costMultiplier = value;
|
||||
}
|
||||
|
||||
// TODO move this someplace that makes more sense...
|
||||
bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, const glm::vec3& dimensions,
|
||||
const glm::vec3& start, const glm::vec3& end, float radius) {
|
||||
|
|
|
@ -94,8 +94,6 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
* Interface has displayed and so knows about.
|
||||
*
|
||||
* @namespace Entities
|
||||
* @property {number} currentAvatarEnergy - <strong>Deprecated</strong>
|
||||
* @property {number} costMultiplier - <strong>Deprecated</strong>
|
||||
* @property {Uuid} keyboardFocusEntity - Get or set the {@link Entities.EntityType|Web} entity that has keyboard focus.
|
||||
* If no entity has keyboard focus, get returns <code>null</code>; set to <code>null</code> or {@link Uuid|Uuid.NULL} to
|
||||
* clear keyboard focus.
|
||||
|
@ -104,8 +102,6 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(float currentAvatarEnergy READ getCurrentAvatarEnergy WRITE setCurrentAvatarEnergy)
|
||||
Q_PROPERTY(float costMultiplier READ getCostMultiplier WRITE setCostMultiplier)
|
||||
Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity)
|
||||
|
||||
friend EntityPropertyMetadataRequest;
|
||||
|
@ -126,7 +122,6 @@ public:
|
|||
void setEntityTree(EntityTreePointer modelTree);
|
||||
EntityTreePointer getEntityTree() { return _entityTree; }
|
||||
void setEntitiesScriptEngine(QSharedPointer<EntitiesScriptEngineProvider> engine);
|
||||
float calculateCost(float mass, float oldVelocity, float newVelocity);
|
||||
|
||||
void resetActivityTracking();
|
||||
ActivityTracking getActivityTracking() const { return _activityTracking; }
|
||||
|
@ -1834,14 +1829,6 @@ signals:
|
|||
*/
|
||||
void clearingEntities();
|
||||
|
||||
/**jsdoc
|
||||
* @function Entities.debitEnergySource
|
||||
* @param {number} value - The amount to debit.
|
||||
* @returns {Signal}
|
||||
* @deprecated This function is deprecated and will soon be removed.
|
||||
*/
|
||||
void debitEnergySource(float value);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered in when a script in a {@link Entities.EntityType|Web} entity's Web page script sends an event over the
|
||||
* script's <code>EventBridge</code>.
|
||||
|
@ -1882,14 +1869,8 @@ private:
|
|||
QSharedPointer<EntitiesScriptEngineProvider> _entitiesScriptEngine;
|
||||
|
||||
bool _bidOnSimulationOwnership { false };
|
||||
float _currentAvatarEnergy = { FLT_MAX };
|
||||
float getCurrentAvatarEnergy() { return _currentAvatarEnergy; }
|
||||
void setCurrentAvatarEnergy(float energy);
|
||||
|
||||
ActivityTracking _activityTracking;
|
||||
float costMultiplier = { 0.01f };
|
||||
float getCostMultiplier();
|
||||
void setCostMultiplier(float value);
|
||||
};
|
||||
|
||||
#endif // hifi_EntityScriptingInterface_h
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
void EntitySimulation::setEntityTree(EntityTreePointer tree) {
|
||||
if (_entityTree && _entityTree != tree) {
|
||||
_mortalEntities.clear();
|
||||
_nextExpiry = quint64(-1);
|
||||
_nextExpiry = std::numeric_limits<uint64_t>::max();
|
||||
_entitiesToUpdate.clear();
|
||||
_entitiesToSort.clear();
|
||||
_simpleKinematicEntities.clear();
|
||||
|
@ -30,7 +30,7 @@ void EntitySimulation::setEntityTree(EntityTreePointer tree) {
|
|||
|
||||
void EntitySimulation::updateEntities() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
quint64 now = usecTimestampNow();
|
||||
uint64_t now = usecTimestampNow();
|
||||
|
||||
// these methods may accumulate entries in _entitiesToBeDeleted
|
||||
expireMortalEntities(now);
|
||||
|
@ -40,18 +40,14 @@ void EntitySimulation::updateEntities() {
|
|||
sortEntitiesThatMoved();
|
||||
}
|
||||
|
||||
void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
||||
void EntitySimulation::takeDeadEntities(SetOfEntities& entitiesToDelete) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
for (auto entity : _entitiesToDelete) {
|
||||
// push this entity onto the external list
|
||||
entitiesToDelete.push_back(entity);
|
||||
}
|
||||
_entitiesToDelete.clear();
|
||||
entitiesToDelete.swap(_deadEntities);
|
||||
_deadEntities.clear();
|
||||
}
|
||||
|
||||
void EntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
// remove from all internal lists except _entitiesToDelete
|
||||
// remove from all internal lists except _deadEntities
|
||||
_mortalEntities.remove(entity);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_entitiesToSort.remove(entity);
|
||||
|
@ -67,42 +63,23 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
|
|||
QMutexLocker lock(&_mutex);
|
||||
entity->clearActions(getThisPointer());
|
||||
removeEntityInternal(entity);
|
||||
_entitiesToDelete.insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void EntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||
if (entity->isMovingRelativeToParent() && !entity->getPhysicsInfo()) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
}
|
||||
}
|
||||
|
||||
void EntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
if (entity->isMovingRelativeToParent() && !entity->getPhysicsInfo()) {
|
||||
int numKinematicEntities = _simpleKinematicEntities.size();
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
if (numKinematicEntities != _simpleKinematicEntities.size()) {
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
if (entity->getElement()) {
|
||||
_deadEntities.insert(entity);
|
||||
}
|
||||
} else {
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// protected
|
||||
void EntitySimulation::expireMortalEntities(const quint64& now) {
|
||||
void EntitySimulation::expireMortalEntities(uint64_t now) {
|
||||
if (now > _nextExpiry) {
|
||||
PROFILE_RANGE_EX(simulation_physics, "ExpireMortals", 0xffff00ff, (uint64_t)_mortalEntities.size());
|
||||
// only search for expired entities if we expect to find one
|
||||
_nextExpiry = quint64(-1);
|
||||
_nextExpiry = std::numeric_limits<uint64_t>::max();
|
||||
QMutexLocker lock(&_mutex);
|
||||
SetOfEntities::iterator itemItr = _mortalEntities.begin();
|
||||
while (itemItr != _mortalEntities.end()) {
|
||||
EntityItemPointer entity = *itemItr;
|
||||
quint64 expiry = entity->getExpiry();
|
||||
uint64_t expiry = entity->getExpiry();
|
||||
if (expiry < now) {
|
||||
itemItr = _mortalEntities.erase(itemItr);
|
||||
entity->die();
|
||||
|
@ -122,7 +99,7 @@ void EntitySimulation::expireMortalEntities(const quint64& now) {
|
|||
}
|
||||
|
||||
// protected
|
||||
void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
||||
void EntitySimulation::callUpdateOnEntitiesThatNeedIt(uint64_t now) {
|
||||
PerformanceTimer perfTimer("updatingEntities");
|
||||
QMutexLocker lock(&_mutex);
|
||||
SetOfEntities::iterator itemItr = _entitiesToUpdate.begin();
|
||||
|
@ -176,7 +153,7 @@ void EntitySimulation::addEntity(EntityItemPointer entity) {
|
|||
entity->deserializeActions();
|
||||
if (entity->isMortal()) {
|
||||
_mortalEntities.insert(entity);
|
||||
quint64 expiry = entity->getExpiry();
|
||||
uint64_t expiry = entity->getExpiry();
|
||||
if (expiry < _nextExpiry) {
|
||||
_nextExpiry = expiry;
|
||||
}
|
||||
|
@ -207,7 +184,6 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) {
|
|||
// Although it is not the responsibility of the EntitySimulation to sort the tree for EXTERNAL changes
|
||||
// it IS responsibile for triggering deletes for entities that leave the bounds of the domain, hence
|
||||
// we must check for that case here, however we rely on the change event to have set DIRTY_POSITION flag.
|
||||
bool wasRemoved = false;
|
||||
uint32_t dirtyFlags = entity->getDirtyFlags();
|
||||
if (dirtyFlags & Simulation::DIRTY_POSITION) {
|
||||
AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE);
|
||||
|
@ -217,50 +193,45 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) {
|
|||
qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
|
||||
entity->die();
|
||||
prepareEntityForDelete(entity);
|
||||
wasRemoved = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!wasRemoved) {
|
||||
if (dirtyFlags & Simulation::DIRTY_LIFETIME) {
|
||||
if (entity->isMortal()) {
|
||||
_mortalEntities.insert(entity);
|
||||
quint64 expiry = entity->getExpiry();
|
||||
if (expiry < _nextExpiry) {
|
||||
_nextExpiry = expiry;
|
||||
}
|
||||
} else {
|
||||
_mortalEntities.remove(entity);
|
||||
|
||||
if (dirtyFlags & Simulation::DIRTY_LIFETIME) {
|
||||
if (entity->isMortal()) {
|
||||
_mortalEntities.insert(entity);
|
||||
uint64_t expiry = entity->getExpiry();
|
||||
if (expiry < _nextExpiry) {
|
||||
_nextExpiry = expiry;
|
||||
}
|
||||
entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME);
|
||||
}
|
||||
if (entity->needsToCallUpdate()) {
|
||||
_entitiesToUpdate.insert(entity);
|
||||
} else {
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_mortalEntities.remove(entity);
|
||||
}
|
||||
changeEntityInternal(entity);
|
||||
entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME);
|
||||
}
|
||||
if (entity->needsToCallUpdate()) {
|
||||
_entitiesToUpdate.insert(entity);
|
||||
} else {
|
||||
_entitiesToUpdate.remove(entity);
|
||||
}
|
||||
changeEntityInternal(entity);
|
||||
}
|
||||
|
||||
void EntitySimulation::clearEntities() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_mortalEntities.clear();
|
||||
_nextExpiry = quint64(-1);
|
||||
_nextExpiry = std::numeric_limits<uint64_t>::max();
|
||||
_entitiesToUpdate.clear();
|
||||
_entitiesToSort.clear();
|
||||
_simpleKinematicEntities.clear();
|
||||
|
||||
clearEntitiesInternal();
|
||||
|
||||
for (auto entity : _allEntities) {
|
||||
entity->setSimulated(false);
|
||||
entity->die();
|
||||
}
|
||||
_allEntities.clear();
|
||||
_entitiesToDelete.clear();
|
||||
_deadEntities.clear();
|
||||
}
|
||||
|
||||
void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
||||
void EntitySimulation::moveSimpleKinematics(uint64_t now) {
|
||||
PROFILE_RANGE_EX(simulation_physics, "MoveSimples", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size());
|
||||
SetOfEntities::iterator itemItr = _simpleKinematicEntities.begin();
|
||||
while (itemItr != _simpleKinematicEntities.end()) {
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#ifndef hifi_EntitySimulation_h
|
||||
#define hifi_EntitySimulation_h
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QSet>
|
||||
#include <QVector>
|
||||
|
@ -43,9 +45,8 @@ const int DIRTY_SIMULATION_FLAGS =
|
|||
Simulation::DIRTY_SIMULATOR_ID;
|
||||
|
||||
class EntitySimulation : public QObject, public std::enable_shared_from_this<EntitySimulation> {
|
||||
Q_OBJECT
|
||||
public:
|
||||
EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL), _nextExpiry(quint64(-1)) { }
|
||||
EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL), _nextExpiry(std::numeric_limits<uint64_t>::max()) { }
|
||||
virtual ~EntitySimulation() { setEntityTree(NULL); }
|
||||
|
||||
inline EntitySimulationPointer getThisPointer() const {
|
||||
|
@ -57,8 +58,6 @@ public:
|
|||
|
||||
void updateEntities();
|
||||
|
||||
// friend class EntityTree;
|
||||
|
||||
virtual void addDynamic(EntityDynamicPointer dynamic);
|
||||
virtual void removeDynamic(const QUuid dynamicID);
|
||||
virtual void removeDynamics(QList<QUuid> dynamicIDsToRemove);
|
||||
|
@ -74,29 +73,26 @@ public:
|
|||
|
||||
void clearEntities();
|
||||
|
||||
void moveSimpleKinematics(const quint64& now);
|
||||
void moveSimpleKinematics(uint64_t now);
|
||||
|
||||
EntityTreePointer getEntityTree() { return _entityTree; }
|
||||
|
||||
virtual void takeEntitiesToDelete(VectorOfEntities& entitiesToDelete);
|
||||
virtual void takeDeadEntities(SetOfEntities& entitiesToDelete);
|
||||
|
||||
/// \param entity pointer to EntityItem that needs to be put on the entitiesToDelete list and removed from others.
|
||||
virtual void prepareEntityForDelete(EntityItemPointer entity);
|
||||
|
||||
signals:
|
||||
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
|
||||
protected:
|
||||
// These pure virtual methods are protected because they are not to be called will-nilly. The base class
|
||||
// calls them in the right places.
|
||||
virtual void updateEntitiesInternal(const quint64& now) = 0;
|
||||
virtual void addEntityInternal(EntityItemPointer entity);
|
||||
virtual void removeEntityInternal(EntityItemPointer entity) = 0;
|
||||
virtual void changeEntityInternal(EntityItemPointer entity);
|
||||
virtual void updateEntitiesInternal(uint64_t now) = 0;
|
||||
virtual void addEntityInternal(EntityItemPointer entity) = 0;
|
||||
virtual void removeEntityInternal(EntityItemPointer entity);
|
||||
virtual void changeEntityInternal(EntityItemPointer entity) = 0;
|
||||
virtual void clearEntitiesInternal() = 0;
|
||||
|
||||
void expireMortalEntities(const quint64& now);
|
||||
void callUpdateOnEntitiesThatNeedIt(const quint64& now);
|
||||
void expireMortalEntities(uint64_t now);
|
||||
void callUpdateOnEntitiesThatNeedIt(uint64_t now);
|
||||
virtual void sortEntitiesThatMoved();
|
||||
|
||||
QMutex _mutex{ QMutex::Recursive };
|
||||
|
@ -108,7 +104,7 @@ protected:
|
|||
QMutex _dynamicsMutex { QMutex::Recursive };
|
||||
|
||||
protected:
|
||||
SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete)
|
||||
SetOfEntities _deadEntities;
|
||||
|
||||
private:
|
||||
void moveSimpleKinematics();
|
||||
|
@ -120,11 +116,10 @@ private:
|
|||
// An entity may be in more than one list.
|
||||
SetOfEntities _allEntities; // tracks all entities added the simulation
|
||||
SetOfEntities _mortalEntities; // entities that have an expiry
|
||||
quint64 _nextExpiry;
|
||||
uint64_t _nextExpiry;
|
||||
|
||||
|
||||
SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update()
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_EntitySimulation_h
|
||||
|
|
|
@ -94,7 +94,6 @@ OctreeElementPointer EntityTree::createNewElement(unsigned char* octalCode) {
|
|||
void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
|
||||
emit clearingEntities();
|
||||
|
||||
// this would be a good place to clean up our entities...
|
||||
if (_simulation) {
|
||||
_simulation->clearEntities();
|
||||
}
|
||||
|
@ -260,7 +259,7 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check to see if we need to simulate this entity..
|
||||
if (_simulation) {
|
||||
_simulation->addEntity(entity);
|
||||
|
@ -425,8 +424,8 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
|||
if (!childEntity) {
|
||||
continue;
|
||||
}
|
||||
EntityTreeElementPointer containingElement = childEntity->getElement();
|
||||
if (!containingElement) {
|
||||
EntityTreeElementPointer childContainingElement = childEntity->getElement();
|
||||
if (!childContainingElement) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -440,7 +439,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
|||
addToNeedsParentFixupList(childEntity);
|
||||
}
|
||||
|
||||
UpdateEntityOperator theChildOperator(getThisPointer(), containingElement, childEntity, queryCube);
|
||||
UpdateEntityOperator theChildOperator(getThisPointer(), childContainingElement, childEntity, queryCube);
|
||||
recurseTreeWithOperator(&theChildOperator);
|
||||
foreach (SpatiallyNestablePointer childChild, childEntity->getChildren()) {
|
||||
if (childChild && childChild->getNestableType() == NestableType::Entity) {
|
||||
|
@ -453,12 +452,13 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
|||
|
||||
uint32_t newFlags = entity->getDirtyFlags() & ~preFlags;
|
||||
if (newFlags) {
|
||||
if (_simulation) {
|
||||
if (entity->isSimulated()) {
|
||||
assert((bool)_simulation);
|
||||
if (newFlags & DIRTY_SIMULATION_FLAGS) {
|
||||
_simulation->changeEntity(entity);
|
||||
}
|
||||
} else {
|
||||
// normally the _simulation clears ALL updateFlags, but since there is none we do it explicitly
|
||||
// normally the _simulation clears ALL dirtyFlags, but when not possible we do it explicitly
|
||||
entity->clearDirtyFlags();
|
||||
}
|
||||
}
|
||||
|
@ -469,7 +469,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
|||
if (entityScriptBefore != entityScriptAfter || reload) {
|
||||
emitEntityScriptChanging(entity->getEntityItemID(), reload); // the entity script has changed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this final containingElement check should eventually be removed (or wrapped in an #ifdef DEBUG).
|
||||
if (!entity->getElement()) {
|
||||
|
@ -551,8 +551,6 @@ void EntityTree::setSimulation(EntitySimulationPointer simulation) {
|
|||
assert(simulation->getEntityTree().get() == this);
|
||||
}
|
||||
if (_simulation && _simulation != simulation) {
|
||||
// It's important to clearEntities() on the simulation since taht will update each
|
||||
// EntityItem::_simulationState correctly so as to not confuse the next _simulation.
|
||||
_simulation->clearEntities();
|
||||
}
|
||||
_simulation = simulation;
|
||||
|
@ -650,7 +648,7 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool i
|
|||
emit deletingEntityPointer(existingEntity.get());
|
||||
}
|
||||
|
||||
if (theOperator.getEntities().size() > 0) {
|
||||
if (!theOperator.getEntities().empty()) {
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
processRemovedEntities(theOperator);
|
||||
_isDirty = true;
|
||||
|
@ -692,7 +690,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
trackDeletedEntity(theEntity->getEntityItemID());
|
||||
}
|
||||
|
||||
if (_simulation) {
|
||||
if (theEntity->isSimulated()) {
|
||||
_simulation->prepareEntityForDelete(theEntity);
|
||||
}
|
||||
}
|
||||
|
@ -1688,7 +1686,7 @@ void EntityTree::releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncod
|
|||
}
|
||||
|
||||
void EntityTree::entityChanged(EntityItemPointer entity) {
|
||||
if (_simulation) {
|
||||
if (entity->isSimulated()) {
|
||||
_simulation->changeEntity(entity);
|
||||
}
|
||||
}
|
||||
|
@ -1802,13 +1800,13 @@ void EntityTree::update(bool simulate) {
|
|||
_simulation->updateEntities();
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "Deletes");
|
||||
VectorOfEntities pendingDeletes;
|
||||
_simulation->takeEntitiesToDelete(pendingDeletes);
|
||||
if (pendingDeletes.size() > 0) {
|
||||
SetOfEntities deadEntities;
|
||||
_simulation->takeDeadEntities(deadEntities);
|
||||
if (!deadEntities.empty()) {
|
||||
// translate into list of ID's
|
||||
QSet<EntityItemID> idsToDelete;
|
||||
|
||||
for (auto entity : pendingDeletes) {
|
||||
for (auto entity : deadEntities) {
|
||||
idsToDelete.insert(entity->getEntityItemID());
|
||||
}
|
||||
|
||||
|
|
|
@ -267,8 +267,11 @@ void MaterialEntityItem::setOwningAvatarID(const QUuid& owningAvatarID) {
|
|||
|
||||
void MaterialEntityItem::removeMaterial() {
|
||||
graphics::MaterialPointer material = getMaterial();
|
||||
if (!material) {
|
||||
return;
|
||||
}
|
||||
QUuid parentID = getClientOnly() ? getOwningAvatarID() : getParentID();
|
||||
if (!material || parentID.isNull()) {
|
||||
if (parentID.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -336,4 +339,4 @@ void MaterialEntityItem::update(const quint64& now) {
|
|||
}
|
||||
|
||||
EntityItem::update(now);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "EntityItem.h"
|
||||
#include "EntitiesLogging.h"
|
||||
|
||||
const quint64 MAX_OWNERLESS_PERIOD = 2 * USECS_PER_SECOND;
|
||||
const uint64_t MAX_OWNERLESS_PERIOD = 2 * USECS_PER_SECOND;
|
||||
|
||||
void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
|
@ -33,7 +33,7 @@ void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) {
|
|||
if (entity->getDynamic() && entity->hasLocalVelocity()) {
|
||||
// it is still moving dynamically --> add to orphaned list
|
||||
_entitiesThatNeedSimulationOwner.insert(entity);
|
||||
quint64 expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
||||
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
||||
if (expiry < _nextOwnerlessExpiry) {
|
||||
_nextOwnerlessExpiry = expiry;
|
||||
}
|
||||
|
@ -50,15 +50,15 @@ void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) {
|
|||
}
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
|
||||
void SimpleEntitySimulation::updateEntitiesInternal(uint64_t now) {
|
||||
if (now > _nextOwnerlessExpiry) {
|
||||
// search for ownerless objects that have expired
|
||||
QMutexLocker lock(&_mutex);
|
||||
_nextOwnerlessExpiry = -1;
|
||||
_nextOwnerlessExpiry = std::numeric_limits<uint64_t>::max();
|
||||
SetOfEntities::iterator itemItr = _entitiesThatNeedSimulationOwner.begin();
|
||||
while (itemItr != _entitiesThatNeedSimulationOwner.end()) {
|
||||
EntityItemPointer entity = *itemItr;
|
||||
quint64 expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
||||
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
||||
if (expiry < now) {
|
||||
// no simulators have volunteered ownership --> remove from list
|
||||
itemItr = _entitiesThatNeedSimulationOwner.erase(itemItr);
|
||||
|
@ -85,14 +85,18 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
|
|||
}
|
||||
|
||||
void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||
EntitySimulation::addEntityInternal(entity);
|
||||
if (entity->isMovingRelativeToParent() && !entity->getPhysicsInfo()) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
}
|
||||
if (!entity->getSimulatorID().isNull()) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesWithSimulationOwner.insert(entity);
|
||||
} else if (entity->getDynamic() && entity->hasLocalVelocity()) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesThatNeedSimulationOwner.insert(entity);
|
||||
quint64 expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
||||
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
||||
if (expiry < _nextOwnerlessExpiry) {
|
||||
_nextOwnerlessExpiry = expiry;
|
||||
}
|
||||
|
@ -101,19 +105,29 @@ void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
|||
|
||||
void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||
EntitySimulation::removeEntityInternal(entity);
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesWithSimulationOwner.remove(entity);
|
||||
_entitiesThatNeedSimulationOwner.remove(entity);
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||
EntitySimulation::changeEntityInternal(entity);
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
if (entity->isMovingRelativeToParent() && !entity->getPhysicsInfo()) {
|
||||
int numKinematicEntities = _simpleKinematicEntities.size();
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
if (numKinematicEntities != _simpleKinematicEntities.size()) {
|
||||
entity->setLastSimulated(usecTimestampNow());
|
||||
}
|
||||
} else {
|
||||
_simpleKinematicEntities.remove(entity);
|
||||
}
|
||||
}
|
||||
if (entity->getSimulatorID().isNull()) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_entitiesWithSimulationOwner.remove(entity);
|
||||
if (entity->getDynamic() && entity->hasLocalVelocity()) {
|
||||
_entitiesThatNeedSimulationOwner.insert(entity);
|
||||
quint64 expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
||||
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
||||
if (expiry < _nextOwnerlessExpiry) {
|
||||
_nextOwnerlessExpiry = expiry;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
void clearOwnership(const QUuid& ownerID);
|
||||
|
||||
protected:
|
||||
virtual void updateEntitiesInternal(const quint64& now) override;
|
||||
virtual void updateEntitiesInternal(uint64_t now) override;
|
||||
virtual void addEntityInternal(EntityItemPointer entity) override;
|
||||
virtual void removeEntityInternal(EntityItemPointer entity) override;
|
||||
virtual void changeEntityInternal(EntityItemPointer entity) override;
|
||||
|
@ -38,7 +38,7 @@ protected:
|
|||
|
||||
SetOfEntities _entitiesWithSimulationOwner;
|
||||
SetOfEntities _entitiesThatNeedSimulationOwner;
|
||||
quint64 _nextOwnerlessExpiry { 0 };
|
||||
uint64_t _nextOwnerlessExpiry { 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_SimpleEntitySimulation_h
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
const quint8 PENDING_STATE_NOTHING = 0;
|
||||
const quint8 PENDING_STATE_TAKE = 1;
|
||||
const quint8 PENDING_STATE_RELEASE = 2;
|
||||
const uint8_t PENDING_STATE_NOTHING = 0;
|
||||
const uint8_t PENDING_STATE_TAKE = 1;
|
||||
const uint8_t PENDING_STATE_RELEASE = 2;
|
||||
|
||||
// static
|
||||
const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1;
|
||||
|
@ -33,7 +33,7 @@ SimulationOwner::SimulationOwner() :
|
|||
{
|
||||
}
|
||||
|
||||
SimulationOwner::SimulationOwner(const QUuid& id, quint8 priority) :
|
||||
SimulationOwner::SimulationOwner(const QUuid& id, uint8_t priority) :
|
||||
_id(id),
|
||||
_expiry(0),
|
||||
_pendingBidTimestamp(0),
|
||||
|
@ -67,11 +67,11 @@ void SimulationOwner::clear() {
|
|||
_pendingState = PENDING_STATE_NOTHING;
|
||||
}
|
||||
|
||||
void SimulationOwner::setPriority(quint8 priority) {
|
||||
void SimulationOwner::setPriority(uint8_t priority) {
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
void SimulationOwner::promotePriority(quint8 priority) {
|
||||
void SimulationOwner::promotePriority(uint8_t priority) {
|
||||
if (priority > _priority) {
|
||||
_priority = priority;
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ bool SimulationOwner::setID(const QUuid& id) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool SimulationOwner::set(const QUuid& id, quint8 priority) {
|
||||
bool SimulationOwner::set(const QUuid& id, uint8_t priority) {
|
||||
uint8_t oldPriority = _priority;
|
||||
setPriority(priority);
|
||||
return setID(id) || oldPriority != _priority;
|
||||
|
@ -101,22 +101,22 @@ bool SimulationOwner::set(const SimulationOwner& owner) {
|
|||
return setID(owner._id) || oldPriority != _priority;
|
||||
}
|
||||
|
||||
void SimulationOwner::setPendingPriority(quint8 priority, const quint64& timestamp) {
|
||||
void SimulationOwner::setPendingPriority(uint8_t priority, uint64_t timestamp) {
|
||||
_pendingBidPriority = priority;
|
||||
_pendingBidTimestamp = timestamp;
|
||||
_pendingState = (_pendingBidPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE;
|
||||
}
|
||||
|
||||
void SimulationOwner::updateExpiry() {
|
||||
const quint64 OWNERSHIP_LOCKOUT_EXPIRY = USECS_PER_SECOND / 5;
|
||||
const uint64_t OWNERSHIP_LOCKOUT_EXPIRY = 200 * USECS_PER_MSEC;
|
||||
_expiry = usecTimestampNow() + OWNERSHIP_LOCKOUT_EXPIRY;
|
||||
}
|
||||
|
||||
bool SimulationOwner::pendingRelease(const quint64& timestamp) {
|
||||
bool SimulationOwner::pendingRelease(uint64_t timestamp) {
|
||||
return _pendingBidPriority == 0 && _pendingState == PENDING_STATE_RELEASE && _pendingBidTimestamp >= timestamp;
|
||||
}
|
||||
|
||||
bool SimulationOwner::pendingTake(const quint64& timestamp) {
|
||||
bool SimulationOwner::pendingTake(uint64_t timestamp) {
|
||||
return _pendingBidPriority > 0 && _pendingState == PENDING_STATE_TAKE && _pendingBidTimestamp >= timestamp;
|
||||
}
|
||||
|
||||
|
@ -142,7 +142,7 @@ void SimulationOwner::test() {
|
|||
|
||||
{ // test set constructor
|
||||
QUuid id = QUuid::createUuid();
|
||||
quint8 priority = 128;
|
||||
uint8_t priority = 128;
|
||||
SimulationOwner simOwner(id, priority);
|
||||
if (simOwner.isNull()) {
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner should NOT be NULL" << std::endl;
|
||||
|
@ -164,7 +164,7 @@ void SimulationOwner::test() {
|
|||
|
||||
{ // test set()
|
||||
QUuid id = QUuid::createUuid();
|
||||
quint8 priority = 1;
|
||||
uint8_t priority = 1;
|
||||
SimulationOwner simOwner;
|
||||
simOwner.set(id, priority);
|
||||
if (simOwner.isNull()) {
|
||||
|
|
|
@ -18,20 +18,88 @@
|
|||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
// Simulation observers will bid to simulate unowned active objects at the lowest possible priority
|
||||
// which is VOLUNTEER. If the server accepts a VOLUNTEER bid it will automatically bump it
|
||||
// to RECRUIT priority so that other volunteers don't accidentally take over.
|
||||
const quint8 VOLUNTEER_SIMULATION_PRIORITY = 0x01;
|
||||
const quint8 RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1;
|
||||
// HighFidelity uses a distributed physics simulation where multiple "participants" simulate portions
|
||||
// of the same world. When portions overlap only one participant is allowed to be the authority for any
|
||||
// particular object. For a simulated entity the authoritative participant is called the simulation "owner" and
|
||||
// their duty is to send transform/velocity updates for the entity to the central entity-server.
|
||||
// The entity-server relays updates to other participants who apply them as "state synchronization"
|
||||
// to their own simulation.
|
||||
//
|
||||
// Participants acquire ownership by sending a "bid" to the entity-server. The bid is a properties update:
|
||||
// {
|
||||
// "simulationOwner": { "ownerID" : sessionID, "priority" : priority },
|
||||
// transform/velocity properties
|
||||
// }
|
||||
//
|
||||
// The entity-server is the authority as to who owns what and may reject a bid.
|
||||
// The rules for handling a bid are as follows:
|
||||
//
|
||||
// (1) A bid may be refused for special ownership restrictions, but otherwise...
|
||||
//
|
||||
// (2) A bid at higher priority is accepted
|
||||
//
|
||||
// (3) A bid at equal priority is rejected if receieved within a grace-period (200msec)
|
||||
// of the last ownership transition, otherwise it is accepted
|
||||
//
|
||||
// (4) The current owner is the only participant allowed to clear ownership (entity-server can override).
|
||||
//
|
||||
// (5) The current owner is the only participant allowed to adjust priority (entity-server can override).
|
||||
//
|
||||
// (6) If an owner does not update the transform or velocities of an owned entity within some period
|
||||
// (5 seconds) then ownership is cleared and the entity's velocities are zeroed. This to handle
|
||||
// the case when an owner drops off the network.
|
||||
//
|
||||
// The priority of a participant's bid depends on how "interested" it is in the entity's motion. The rules
|
||||
// for bidding are as follows:
|
||||
//
|
||||
// (7) A participant (almost) never assumes that a bid is accepted by the entity-server. It packs the
|
||||
// simulation owner and priority as if they really did change but doesn't actually modify them
|
||||
// locally. Thus, if the bid packet is lost the participant will re-send after some period.
|
||||
// The participant only updates its knowledge of who owns what when it recieves an update from the
|
||||
// entity-server. An exception is when the participant creates a moving entity: it assumes it starts
|
||||
// off owning any moving entities it creates.
|
||||
//
|
||||
// (8) When an unowned entity becomes active in the physics simulation the participant will
|
||||
// start a timer and if the entity is still unowned after some period (0.5 seconds)
|
||||
// it will bid at priority = VOLUNTEER (=2). The entity-server never grants ownership at VOLUNTEER
|
||||
// priority: when a VOLUNTEER bid is accepted the entity-server always promotes the priority to
|
||||
// RECRUIT (VOLUNTEER + 1); this to avoid a race-condition which might rapidly transition ownership
|
||||
// when multiple participants (with variable ping-times to the server) bid simultaneously for a
|
||||
// recently activated entity.
|
||||
//
|
||||
// (9) When a participant changes an entity's transform/velocity it will bid at priority = POKE (=127)
|
||||
//
|
||||
// (10) When an entity touches MyAvatar the participant it will bid at priority = POKE.
|
||||
//
|
||||
// (11) When a participant grabs an entity it will bid at priority = GRAB (=128).
|
||||
//
|
||||
// (12) When entityA, locally owned at priority = N, collides with an unowned entityB the owner will
|
||||
// also bid for entityB at priority = N-1 (or VOLUNTEER, whichever is larger).
|
||||
//
|
||||
// (13) When an entity comes to rest and is deactivated in the physics simulation the owner will
|
||||
// send an update to: clear their ownerhsip, set priority to zero, and set the object's
|
||||
// velocities to be zero. As per a normal bid, the owner does NOT assume that its ownership
|
||||
// has been cleared until it hears from the entity-server. This, if the packet is lost the
|
||||
// owner will re-send after some period.
|
||||
//
|
||||
// (14) When an entity's ownership priority drops below VOLUNTEER other participants may bid for it
|
||||
// immediately at priority = VOLUNTEER.
|
||||
//
|
||||
// (15) When an entity is still active but the owner no longer wants to own it, it will drop its priority
|
||||
// to YIELD (=1, less than VOLUNTEER) thereby signalling to other participants to bid for it.
|
||||
//
|
||||
const uint8_t YIELD_SIMULATION_PRIORITY = 1;
|
||||
const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1;
|
||||
const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1;
|
||||
|
||||
// When poking objects with scripts an observer will bid at SCRIPT_EDIT priority.
|
||||
const quint8 SCRIPT_GRAB_SIMULATION_PRIORITY = 0x80;
|
||||
const quint8 SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1;
|
||||
const quint8 AVATAR_ENTITY_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY + 1;
|
||||
const uint8_t SCRIPT_GRAB_SIMULATION_PRIORITY = 128;
|
||||
const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1;
|
||||
const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY + 1;
|
||||
|
||||
// PERSONAL priority (needs a better name) is the level at which a simulation observer owns its own avatar
|
||||
// which really just means: things that collide with it will be bid at a priority level one lower
|
||||
const quint8 PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
||||
const uint8_t PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
||||
|
||||
|
||||
class SimulationOwner {
|
||||
|
@ -39,25 +107,25 @@ public:
|
|||
static const int NUM_BYTES_ENCODED;
|
||||
|
||||
SimulationOwner();
|
||||
SimulationOwner(const QUuid& id, quint8 priority);
|
||||
SimulationOwner(const QUuid& id, uint8_t priority);
|
||||
|
||||
const QUuid& getID() const { return _id; }
|
||||
const quint64& getExpiry() const { return _expiry; }
|
||||
quint8 getPriority() const { return _priority; }
|
||||
const uint64_t& getExpiry() const { return _expiry; }
|
||||
uint8_t getPriority() const { return _priority; }
|
||||
|
||||
QByteArray toByteArray() const;
|
||||
bool fromByteArray(const QByteArray& data);
|
||||
|
||||
void clear();
|
||||
|
||||
void setPriority(quint8 priority);
|
||||
void promotePriority(quint8 priority);
|
||||
void setPriority(uint8_t priority);
|
||||
void promotePriority(uint8_t priority);
|
||||
|
||||
// return true if id is changed
|
||||
bool setID(const QUuid& id);
|
||||
bool set(const QUuid& id, quint8 priority);
|
||||
bool set(const QUuid& id, uint8_t priority);
|
||||
bool set(const SimulationOwner& owner);
|
||||
void setPendingPriority(quint8 priority, const quint64& timestamp);
|
||||
void setPendingPriority(uint8_t priority, uint64_t timestamp);
|
||||
|
||||
bool isNull() const { return _id.isNull(); }
|
||||
bool matchesValidID(const QUuid& id) const { return _id == id && !_id.isNull(); }
|
||||
|
@ -67,11 +135,11 @@ public:
|
|||
bool hasExpired() const { return usecTimestampNow() > _expiry; }
|
||||
|
||||
uint8_t getPendingPriority() const { return _pendingBidPriority; }
|
||||
bool pendingRelease(const quint64& timestamp); // return true if valid pending RELEASE
|
||||
bool pendingTake(const quint64& timestamp); // return true if valid pending TAKE
|
||||
bool pendingRelease(uint64_t timestamp); // return true if valid pending RELEASE
|
||||
bool pendingTake(uint64_t timestamp); // return true if valid pending TAKE
|
||||
void clearCurrentOwner();
|
||||
|
||||
bool operator>=(quint8 priority) const { return _priority >= priority; }
|
||||
bool operator>=(uint8_t priority) const { return _priority >= priority; }
|
||||
bool operator==(const SimulationOwner& other) { return (_id == other._id && _priority == other._priority); }
|
||||
|
||||
bool operator!=(const SimulationOwner& other);
|
||||
|
@ -84,11 +152,11 @@ public:
|
|||
|
||||
private:
|
||||
QUuid _id; // owner
|
||||
quint64 _expiry; // time when ownership can transition at equal priority
|
||||
quint64 _pendingBidTimestamp; // time when pending bid was set
|
||||
quint8 _priority; // priority of current owner
|
||||
quint8 _pendingBidPriority; // priority at which we'd like to own it
|
||||
quint8 _pendingState; // NOTHING, TAKE, or RELEASE
|
||||
uint64_t _expiry; // time when ownership can transition at equal priority
|
||||
uint64_t _pendingBidTimestamp; // time when pending bid was set
|
||||
uint8_t _priority; // priority of current owner
|
||||
uint8_t _pendingBidPriority; // priority at which we'd like to own it
|
||||
uint8_t _pendingState; // NOTHING, TAKE, or RELEASE
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -288,7 +288,7 @@ OctreeElementPointer UpdateEntityOperator::possiblyCreateChildAt(const OctreeEle
|
|||
int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntityBox);
|
||||
|
||||
if (childIndex == indexOfChildContainingNewEntity) {
|
||||
return element->addChildAtIndex(childIndex);;
|
||||
return element->addChildAtIndex(childIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
|||
(&::gpu::gl::GLBackend::do_setUniformBuffer),
|
||||
(&::gpu::gl::GLBackend::do_setResourceBuffer),
|
||||
(&::gpu::gl::GLBackend::do_setResourceTexture),
|
||||
(&::gpu::gl::GLBackend::do_setResourceTextureTable),
|
||||
(&::gpu::gl::GLBackend::do_setResourceFramebufferSwapChainTexture),
|
||||
|
||||
(&::gpu::gl::GLBackend::do_setFramebuffer),
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
#endif
|
||||
|
||||
|
||||
|
||||
// Let these be configured by the one define picked above
|
||||
#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE
|
||||
#define GPU_STEREO_DRAWCALL_DOUBLED
|
||||
|
@ -102,6 +101,12 @@ public:
|
|||
static const int MAX_NUM_RESOURCE_TEXTURES = 16;
|
||||
size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; }
|
||||
|
||||
// Texture Tables offers 2 dedicated slot (taken from the ubo slots)
|
||||
static const int MAX_NUM_RESOURCE_TABLE_TEXTURES = 2;
|
||||
static const int RESOURCE_TABLE_TEXTURE_SLOT_OFFSET = TRANSFORM_CAMERA_SLOT + 1;
|
||||
size_t getMaxNumResourceTextureTables() const { return MAX_NUM_RESOURCE_TABLE_TEXTURES; }
|
||||
|
||||
|
||||
// Draw Stage
|
||||
virtual void do_draw(const Batch& batch, size_t paramOffset) = 0;
|
||||
virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0;
|
||||
|
@ -130,6 +135,7 @@ public:
|
|||
// Resource Stage
|
||||
virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_setResourceTextureTable(const Batch& batch, size_t paramOffset);
|
||||
virtual void do_setResourceFramebufferSwapChainTexture(const Batch& batch, size_t paramOffset) final;
|
||||
|
||||
// Pipeline Stage
|
||||
|
@ -230,6 +236,10 @@ protected:
|
|||
|
||||
void recycle() const override;
|
||||
|
||||
// FIXME instead of a single flag, create a features struct similar to
|
||||
// https://www.khronos.org/registry/vulkan/specs/1.0/man/html/VkPhysicalDeviceFeatures.html
|
||||
virtual bool supportsBindless() const { return false; }
|
||||
|
||||
static const size_t INVALID_OFFSET = (size_t)-1;
|
||||
bool _inRenderTransferPass { false };
|
||||
int32_t _uboAlignment { 0 };
|
||||
|
@ -389,6 +399,10 @@ protected:
|
|||
virtual bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) = 0;
|
||||
virtual void releaseResourceBuffer(uint32_t slot) = 0;
|
||||
|
||||
// Helper function that provides common code used by do_setResourceTexture and
|
||||
// do_setResourceTextureTable (in non-bindless mode)
|
||||
void bindResourceTexture(uint32_t slot, const TexturePointer& texture);
|
||||
|
||||
// update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s
|
||||
void releaseResourceTexture(uint32_t slot);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//
|
||||
//
|
||||
// GLBackendPipeline.cpp
|
||||
// libraries/gpu/src/gpu
|
||||
//
|
||||
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "GLBackend.h"
|
||||
#include <gpu/TextureTable.h>
|
||||
|
||||
#include "GLShared.h"
|
||||
#include "GLPipeline.h"
|
||||
#include "GLShader.h"
|
||||
|
@ -247,8 +249,11 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) {
|
|||
return;
|
||||
}
|
||||
|
||||
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
||||
const auto& resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
||||
bindResourceTexture(slot, resourceTexture);
|
||||
}
|
||||
|
||||
void GLBackend::bindResourceTexture(uint32_t slot, const TexturePointer& resourceTexture) {
|
||||
if (!resourceTexture) {
|
||||
releaseResourceTexture(slot);
|
||||
return;
|
||||
|
@ -306,6 +311,19 @@ void GLBackend::setResourceTexture(unsigned int slot, const TexturePointer& reso
|
|||
}
|
||||
}
|
||||
|
||||
void GLBackend::do_setResourceTextureTable(const Batch& batch, size_t paramOffset) {
|
||||
const auto& textureTablePointer = batch._textureTables.get(batch._params[paramOffset]._uint);
|
||||
if (!textureTablePointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& textureTable = *textureTablePointer;
|
||||
const auto& textures = textureTable.getTextures();
|
||||
for (GLuint slot = 0; slot < textures.size(); ++slot) {
|
||||
bindResourceTexture(slot, textures[slot]);
|
||||
}
|
||||
}
|
||||
|
||||
int GLBackend::ResourceStageState::findEmptyTextureSlot() const {
|
||||
// start from the end of the slots, try to find an empty one that can be used
|
||||
for (auto i = MAX_NUM_RESOURCE_TEXTURES - 1; i > 0; i--) {
|
||||
|
@ -315,4 +333,3 @@ int GLBackend::ResourceStageState::findEmptyTextureSlot() const {
|
|||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,11 @@ R"SHADER(
|
|||
#endif
|
||||
};
|
||||
|
||||
// TextureTable specific defines
|
||||
static const std::string textureTableVersion {
|
||||
"#extension GL_ARB_bindless_texture : require\n#define GPU_TEXTURE_TABLE_BINDLESS\n"
|
||||
};
|
||||
|
||||
// Versions specific of the shader
|
||||
static const std::array<std::string, GLShader::NumVersions> VERSION_DEFINES { {
|
||||
"",
|
||||
|
@ -96,9 +101,9 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co
|
|||
auto& shaderObject = shaderObjects[version];
|
||||
|
||||
std::string shaderDefines = getBackendShaderHeader() + "\n"
|
||||
+ (supportsBindless() ? textureTableVersion : "\n")
|
||||
+ DOMAIN_DEFINES[shader.getType()] + "\n"
|
||||
+ VERSION_DEFINES[version];
|
||||
|
||||
if (handler) {
|
||||
bool retest = true;
|
||||
std::string currentSrc = shaderSource;
|
||||
|
@ -120,7 +125,7 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co
|
|||
compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message);
|
||||
}
|
||||
|
||||
if (!compilationLogs[version].compiled) {
|
||||
if (!compilationLogs[version].compiled) {
|
||||
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str();
|
||||
shader.setCompilationLogs(compilationLogs);
|
||||
return nullptr;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue