3
0
Fork 0
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:
David Rowe 2018-04-12 09:48:51 +12:00
commit 7a9e3388fa
170 changed files with 3781 additions and 1766 deletions
CMakeLists.txt
assignment-client/src
cmake
domain-server/src
interface
libraries

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

After

(image error) Size: 11 KiB

View file

@ -67,6 +67,10 @@ Item {
fill: parent
}
MouseArea {
anchors.fill: parent
}
QmlHifi.WindowHeader {
id: header
iconSource: "../../../icons/goto-i.svg"

View file

@ -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 != ""
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -61,9 +61,6 @@ Item {
if (root.shouldImmediatelyFocus) {
focusFirstTextField();
}
sendMessageToLightbox({method: 'disableHmdPreview'});
} else {
sendMessageToLightbox({method: 'maybeEnableHmdPreview'});
}
}

View file

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

View file

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

View file

@ -237,7 +237,7 @@ Rectangle {
} else {
sendToScript(msg);
}
} else if (msg.method === 'maybeEnableHmdPreview') {
} else {
sendToScript(msg);
}
}

View file

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

View file

@ -25,6 +25,7 @@ ScrollingWindow {
resizable: true
destroyOnHidden: false
implicitWidth: 424
opacity: parent.opacity
implicitHeight: isHMD ? 695 : 728
minSize: Qt.vector2d(424, 300)

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,18 @@
{
"Entities": [
{
"type": "Box",
"dimensions": {
"x": 20,
"y": 1,
"z": 20
},
"position" : {
"x": 0,
"y": -12,
"z": 0
}
}
],
"Version": 84
}

View file

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

View file

@ -575,7 +575,7 @@ private:
Setting::Handle<QString> _preferredCursor;
float _scaleMirror;
float _rotateMirror;
float _mirrorYawOffset;
float _raiseMirror;
QSet<int> _keysPressed;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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" },

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -174,6 +174,7 @@ enum class Action {
TRACKED_OBJECT_13,
TRACKED_OBJECT_14,
TRACKED_OBJECT_15,
SPRINT,
NUM_ACTIONS
};

View file

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

View file

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

View file

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

View file

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

View file

@ -46,5 +46,8 @@ private:
gpu::TexturePointer _virtualPadStickTexture;
gpu::TexturePointer _virtualPadStickBaseTexture;
qreal _virtualPadPixelSize;
gpu::TexturePointer _virtualPadJumpBtnTexture;
qreal _virtualPadJumpBtnPixelSize;
#endif
};

View file

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

View file

@ -131,6 +131,8 @@ ItemKey ShapeEntityRenderer::getKey() {
withReadLock([&] {
if (isTransparent()) {
builder.withTransparent();
} else if (_canCastShadow) {
builder.withShadowCaster();
}
});

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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()) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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()) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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