Merge branch 'master' of github.com:highfidelity/hifi into infinite-scroll

This commit is contained in:
howard-stearns 2018-05-29 16:14:22 -07:00
commit a84829392f
175 changed files with 3494 additions and 1858 deletions

View file

@ -9,15 +9,17 @@ Note: The prerequisites will require about 10 GB of space on your drive. You wil
If you dont have Community or Professional edition of Visual Studio 2017, download [Visual Studio Community 2017](https://www.visualstudio.com/downloads/).
When selecting components, check "Desktop development with C++." Also check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)" on the Summary toolbar on the right.
When selecting components, check "Desktop development with C++." Also on the right on the Summary toolbar, check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)".
### Step 2. Installing CMake
Download and install the latest version of CMake 3.9. Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). Make sure to check "Add CMake to system PATH for all users" when prompted during installation.
Download and install the latest version of CMake 3.9.
Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.9 Version page](https://cmake.org/files/v3.9/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted.
### Step 3. Installing Qt
Download and install the [Qt Online Installer](https://www.qt.io/download-open-source/?hsCtaTracking=f977210e-de67-475f-a32b-65cec207fd03%7Cd62710cd-e1db-46aa-8d4d-2f1c1ffdacea). While installing, you only need to have the following components checked under Qt 5.10.1: "msvc2017 64-bit", "Qt WebEngine", and "Qt Script (Deprecated)".
Download and install the [Qt Open Source Online Installer](https://www.qt.io/download-open-source/?hsCtaTracking=f977210e-de67-475f-a32b-65cec207fd03%7Cd62710cd-e1db-46aa-8d4d-2f1c1ffdacea). While installing, you only need to have the following components checked under Qt 5.10.1: "msvc2017 64-bit", "Qt WebEngine", and "Qt Script (Deprecated)".
Note: Installing the Sources is optional but recommended if you have room for them (~2GB).
@ -56,9 +58,9 @@ Where `%HIFI_DIR%` is the directory for the highfidelity repository.
Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio.
Change the Solution Configuration (next to the green play button) from "Debug" to "Release" for best performance.
Change the Solution Configuration (menu ribbon under the menu bar, next to the green play button) from "Debug" to "Release" for best performance.
Run `Build > Build Solution`.
Run from the menu bar `Build > Build Solution`.
### Step 9. Testing Interface
@ -66,7 +68,7 @@ Create another environment variable (see Step #4)
* Set "Variable name": `_NO_DEBUG_HEAP`
* Set "Variable value": `1`
In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run `Debug > Start Debugging`.
In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run from the menu bar `Debug > Start Debugging`.
Now, you should have a full build of High Fidelity and be able to run the Interface using Visual Studio. Please check our [Docs](https://wiki.highfidelity.com/wiki/Main_Page) for more information regarding the programming workflow.

View file

@ -66,17 +66,17 @@ ext {
def baseFolder = new File(HIFI_ANDROID_PRECOMPILED)
def appDir = new File(projectDir, 'app')
def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a')
def baseUrl = 'https://hifi-public.s3.amazonaws.com/austin/android/'
def baseUrl = ''
def qtFile='qt-5.9.3_linux_armv8-libcpp_openssl.tgz'
def qtFile='https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_linux_armv8-libcpp_openssl.tgz'
def qtChecksum='04599670ccca84bd2b15f6915568eb2d'
def qtVersionId='PeoqzN31n.YvLfs9JE2SgHgZ4.IaKAlt'
if (Os.isFamily(Os.FAMILY_MAC)) {
qtFile = 'qt-5.9.3_osx_armv8-libcpp_openssl.tgz'
qtFile = 'https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_osx_armv8-libcpp_openssl.tgz'
qtChecksum='4b02de9d67d6bfb202355a808d2d9c59'
qtVersionId='HygCmtMLPYioyil0DfXckGVzhw2SXZA9'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
qtFile = 'qt-5.9.3_win_armv8-libcpp_openssl.tgz'
qtFile = 'https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_win_armv8-libcpp_openssl.tgz'
qtChecksum='c3e25db64002d0f43cf565e0ef708911'
qtVersionId='HeVObSVLCBoc7yY7He1oBMvPIH0VkClT'
}
@ -88,58 +88,58 @@ def packages = [
checksum: qtChecksum,
],
bullet: [
file: 'bullet-2.83_armv8-libcpp.tgz',
versionId: 'ljb7v.1IjVRqyopUKVDbVnLA4z88J8Eo',
checksum: '2c558d604fce337f5eba3eb7ec1252fd',
file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/bullet-2.88_armv8-libcpp.tgz',
versionId: 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg',
checksum: '81642779ccb110f8c7338e8739ac38a0',
],
draco: [
file: 'draco_armv8-libcpp.tgz',
file: 'https://hifi-public.s3.amazonaws.com/austin/android/draco_armv8-libcpp.tgz',
versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m',
checksum: '617a80d213a5ec69fbfa21a1f2f738cd',
],
glad: [
file: 'glad_armv8-libcpp.zip',
file: 'https://hifi-public.s3.amazonaws.com/austin/android/glad_armv8-libcpp.zip',
versionId: 'Q9szthzeye8fFyAA.cY26Lgn2B8kezEE',
checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449',
],
glm: [
file: 'glm-0.9.8.tgz',
versionId: 'BlkJNwaYV2Gfy5XwMeU7K0uzPDRKFMt2',
checksum: 'd2b42cee31d2bc17bab6ce69e6b3f30a',
file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/glm-0.9.8.5-patched.tgz',
versionId: 'cskfMoJrFlAeqI3WPxemyO_Cxt7rT9EJ',
checksum: '067b5fe16b220b5b1a1039ba51b062ae',
],
gvr: [
file: 'gvrsdk_v1.101.0.tgz',
file: 'https://hifi-public.s3.amazonaws.com/austin/android/gvrsdk_v1.101.0.tgz',
versionId: 'UTberAIFraEfF9IVjoV66u1DTPTopgeY',
checksum: '57fd02baa069176ba18597a29b6b4fc7',
],
nvtt: [
file: 'nvtt_armv8-libcpp.zip',
file: 'https://hifi-public.s3.amazonaws.com/austin/android/nvtt_armv8-libcpp.zip',
versionId: 'vLqrqThvpq4gp75BHMAqO6HhfTXaa0An',
checksum: 'eb46d0b683e66987190ed124aabf8910',
sharedLibFolder: 'lib',
includeLibs: ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'],
],
openssl: [
file: 'openssl-1.1.0g_armv8.tgz',
file: 'https://hifi-public.s3.amazonaws.com/austin/android/openssl-1.1.0g_armv8.tgz',
versionId: 'DmahmSGFS4ltpHyTdyQvv35WOeUOiib9',
checksum: 'cabb681fbccd79594f65fcc266e02f32',
],
polyvox: [
file: 'polyvox_armv8-libcpp.tgz',
file: 'https://hifi-public.s3.amazonaws.com/austin/android/polyvox_armv8-libcpp.tgz',
versionId: 'LDJtzMTvdm4SAc2KYg8Cg6uwWk4Vq3e3',
checksum: '349ad5b72aaf2749ca95d847e60c5314',
sharedLibFolder: 'lib',
includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'],
],
tbb: [
file: 'tbb-2018_U1_armv8_libcpp.tgz',
file: 'https://hifi-public.s3.amazonaws.com/austin/android/tbb-2018_U1_armv8_libcpp.tgz',
versionId: 'YZliDD8.Menh1IVXKEuLPeO3xAjJ1UdF',
checksum: '20768f298f53b195e71b414b0ae240c4',
sharedLibFolder: 'lib/release',
includeLibs: ['libtbb.so', 'libtbbmalloc.so'],
],
hifiAC: [
file: 'libplugins_libhifiCodec.zip',
file: 'https://hifi-public.s3.amazonaws.com/austin/android/libplugins_libhifiCodec.zip',
versionId: 'mzKhsRCgVmloqq5bvE.0IwYK1NjGQc_G',
checksum: '9412a8e12c88a4096c1fc843bb9fe52d',
sharedLibFolder: '',
@ -150,15 +150,15 @@ def packages = [
def scribeLocalFile='scribe' + EXEC_SUFFIX
def scribeFile='scribe_linux_x86_64'
def scribeFile='https://hifi-public.s3.amazonaws.com/austin/android/scribe_linux_x86_64'
def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6'
def scribeVersion='wgpf4dB2Ltzg4Lb2jJ4nPFsHoDkmK_OO'
if (Os.isFamily(Os.FAMILY_MAC)) {
scribeFile = 'scribe_osx_x86_64'
scribeFile = 'https://hifi-public.s3.amazonaws.com/austin/android/scribe_osx_x86_64'
scribeChecksum='72db9d32d4e1e50add755570ac5eb749'
scribeVersion='o_NbPrktzEYtBkQf3Tn7zc1nZWzM52w6'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
scribeFile = 'scribe_win32_x86_64.exe'
scribeFile = 'https://hifi-public.s3.amazonaws.com/austin/android/scribe_win32_x86_64.exe'
scribeChecksum='678e43d290c90fda670c6fefe038a06d'
scribeVersion='GCCJxlmd2irvNOFWfZR0U1UCLHndHQrC'
}
@ -608,4 +608,4 @@ task testElf (dependsOn: 'externalNativeBuildDebug') {
}
}
}
*/
*/

View file

@ -44,6 +44,7 @@ EntityServer::EntityServer(ReceivedMessage& message) :
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListenerForTypes({ PacketType::EntityAdd,
PacketType::EntityClone,
PacketType::EntityEdit,
PacketType::EntityErase,
PacketType::EntityPhysics,

View file

@ -17,8 +17,8 @@ include(ExternalProject)
if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz
URL_MD5 03051bf112dcc78ddd296f9cab38fd68
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz
URL_MD5 0a6876607ebe83e227427215f15946fd
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 -DUSE_DX11=0
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
@ -28,8 +28,8 @@ if (WIN32)
else ()
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz
URL_MD5 03051bf112dcc78ddd296f9cab38fd68
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz
URL_MD5 0a6876607ebe83e227427215f15946fd
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0
LOG_DOWNLOAD 1
LOG_CONFIGURE 1

View file

@ -3,8 +3,8 @@ set(EXTERNAL_NAME glm)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.zip
URL_MD5 579ac77a3110befa3244d68c0ceb7281
URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.5-patched.zip
URL_MD5 7d39ecc1cea275427534c3cfd6dd63f0
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTERNAL_ARGS}
LOG_DOWNLOAD 1
@ -18,4 +18,4 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glm include directories")
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glm include directories")

View file

@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC67-v4.zip
URL_MD5 ba32aed18bfeaac4ccaf5ebb8ea3e804
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC68.zip
URL_MD5 a068f74d4045e257cfa7926fe6e38ad5
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -73,6 +73,8 @@ macro(SET_PACKAGING_PARAMETERS)
add_definitions(-DDEV_BUILD)
endif ()
string(TIMESTAMP BUILD_TIME "%d/%m/%Y")
if (DEPLOY_PACKAGE)
# for deployed packages always grab the serverless content
set(DOWNLOAD_SERVERLESS_CONTENT ON)

View file

@ -26,4 +26,6 @@ namespace BuildInfo {
const QString VERSION = "@BUILD_VERSION@";
const QString BUILD_BRANCH = "@BUILD_BRANCH@";
const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@";
const QString BUILD_TIME = "@BUILD_TIME@";
}

View file

@ -479,7 +479,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
limitedNodeList->killNodeWithUUID(existingNodeID);
}
// add the connecting node (or re-use the matched one from eachNodeBreakable above)
// add the connecting node
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
// set the edit rights for this user
@ -508,26 +508,22 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
return newNode;
}
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
QUuid nodeID) {
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) {
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
if (connectedPeer) {
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
nodeID = nodeConnection.connectUUID;
if (connectedPeer->getActiveSocket()) {
// set their discovered socket to whatever the activated socket on the network peer object was
discoveredSocket = *connectedPeer->getActiveSocket();
}
} else {
// we got a connectUUID we didn't recognize, either use the hinted node ID or randomly generate a new one
if (nodeID.isNull()) {
nodeID = QUuid::createUuid();
}
if (connectedPeer && connectedPeer->getActiveSocket()) {
// set their discovered socket to whatever the activated socket on the network peer object was
discoveredSocket = *connectedPeer->getActiveSocket();
}
// create a new node ID for the verified connecting node
auto nodeID = QUuid::createUuid();
// add a mapping from connection node ID to ICE peer ID
// so that we can remove the ICE peer once we see this node connect
_nodeToICEPeerIDs.insert(nodeID, nodeConnection.connectUUID);
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
Node::LocalID newLocalID = findOrCreateLocalID(nodeID);
@ -541,6 +537,15 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node
return newNode;
}
void DomainGatekeeper::cleanupICEPeerForNode(const QUuid& nodeID) {
// remove this node ID from our node to ICE peer ID map
// and the associated ICE peer (if it still exists)
auto icePeerID = _nodeToICEPeerIDs.take(nodeID);
if (!icePeerID.isNull()) {
_icePeers.remove(icePeerID);
}
}
bool DomainGatekeeper::verifyUserSignature(const QString& username,
const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr) {

View file

@ -39,8 +39,8 @@ public:
void addPendingAssignedNode(const QUuid& nodeUUID, const QUuid& assignmentUUID,
const QUuid& walletUUID, const QString& nodeVersion);
QUuid assignmentUUIDForPendingAssignment(const QUuid& tempUUID);
void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); }
void cleanupICEPeerForNode(const QUuid& nodeID);
Node::LocalID findOrCreateLocalID(const QUuid& uuid);
@ -77,8 +77,7 @@ private:
SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection,
const QString& username,
const QByteArray& usernameSignature);
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
QUuid nodeID = QUuid());
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection);
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr);
@ -101,6 +100,10 @@ private:
std::unordered_map<QUuid, PendingAssignedNodeData> _pendingAssignedNodes;
QHash<QUuid, SharedNetworkPeer> _icePeers;
using ConnectingNodeID = QUuid;
using ICEPeerID = QUuid;
QHash<ConnectingNodeID, ICEPeerID> _nodeToICEPeerIDs;
QHash<QString, QUuid> _connectionTokenHash;

View file

@ -1017,15 +1017,22 @@ void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> mess
sendingNode->setPublicSocket(nodeRequestData.publicSockAddr);
sendingNode->setLocalSocket(nodeRequestData.localSockAddr);
// update the NodeInterestSet in case there have been any changes
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
if (!nodeData->hasCheckedIn()) {
nodeData->setHasCheckedIn(true);
// on first check in, make sure we've cleaned up any ICE peer for this node
_gatekeeper.cleanupICEPeerForNode(sendingNode->getUUID());
}
// guard against patched agents asking to hear about other agents
auto safeInterestSet = nodeRequestData.interestList.toSet();
if (sendingNode->getType() == NodeType::Agent) {
safeInterestSet.remove(NodeType::Agent);
}
// update the NodeInterestSet in case there have been any changes
nodeData->setNodeInterestSet(safeInterestSet);
// update the connecting hostname in case it has changed
@ -2945,7 +2952,7 @@ void DomainServer::nodeAdded(SharedNodePointer node) {
void DomainServer::nodeKilled(SharedNodePointer node) {
// if this peer connected via ICE then remove them from our ICE peers hash
_gatekeeper.removeICEPeer(node->getUUID());
_gatekeeper.cleanupICEPeerForNode(node->getUUID());
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
@ -2978,6 +2985,8 @@ void DomainServer::nodeKilled(SharedNodePointer node) {
}
}
}
broadcastNodeDisconnect(node);
}
SharedAssignmentPointer DomainServer::dequeueMatchingAssignment(const QUuid& assignmentUUID, NodeType_t nodeType) {
@ -3163,18 +3172,23 @@ void DomainServer::handleKillNode(SharedNodePointer nodeToKill) {
const QUuid& nodeUUID = nodeToKill->getUUID();
limitedNodeList->killNodeWithUUID(nodeUUID);
}
static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID);
void DomainServer::broadcastNodeDisconnect(const SharedNodePointer& disconnectedNode) {
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID, true);
removedNodePacket->reset();
removedNodePacket->write(nodeUUID.toRfc4122());
removedNodePacket->write(disconnectedNode->getUUID().toRfc4122());
// broadcast out the DomainServerRemovedNode message
limitedNodeList->eachMatchingNode([this, &nodeToKill](const SharedNodePointer& otherNode) -> bool {
limitedNodeList->eachMatchingNode([this, &disconnectedNode](const SharedNodePointer& otherNode) -> bool {
// only send the removed node packet to nodes that care about the type of node this was
return isInInterestSet(otherNode, nodeToKill);
return isInInterestSet(otherNode, disconnectedNode);
}, [&limitedNodeList](const SharedNodePointer& otherNode){
limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode);
auto removedNodePacketCopy = NLPacket::createCopy(*removedNodePacket);
limitedNodeList->sendPacket(std::move(removedNodePacketCopy), *otherNode);
});
}

View file

@ -165,6 +165,7 @@ private:
unsigned int countConnectedUsers();
void handleKillNode(SharedNodePointer nodeToKill);
void broadcastNodeDisconnect(const SharedNodePointer& disconnnectedNode);
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr);

View file

@ -67,8 +67,11 @@ public:
const QString& getPlaceName() { return _placeName; }
void setPlaceName(const QString& placeName) { _placeName = placeName; }
bool wasAssigned() const { return _wasAssigned; };
bool wasAssigned() const { return _wasAssigned; }
void setWasAssigned(bool wasAssigned) { _wasAssigned = wasAssigned; }
bool hasCheckedIn() const { return _hasCheckedIn; }
void setHasCheckedIn(bool hasCheckedIn) { _hasCheckedIn = hasCheckedIn; }
private:
QJsonObject overrideValuesIfNeeded(const QJsonObject& newStats);
@ -94,6 +97,8 @@ private:
QString _placeName;
bool _wasAssigned { false };
bool _hasCheckedIn { false };
};
#endif // hifi_DomainServerNodeData_h

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -24,6 +24,7 @@ Slider {
property alias minimumValue: slider.from
property alias maximumValue: slider.to
property alias step: slider.stepSize
property bool tickmarksEnabled: false
height: hifi.fontSizes.textFieldInput + 14 // Match height of TextField control.

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

@ -149,17 +149,17 @@ FocusScope {
}
function isModalWindow(window) {
return window.modality !== Qt.NonModal;
return window.modality !== (typeof Qt !== 'undefined' ? Qt.NonModal : 0);
}
function getTopLevelWindows(predicate) {
return findMatchingChildren(desktop, function(child) {
return (isTopLevelWindow(child) && (!predicate || predicate(child)));
return d.findMatchingChildren(desktop, function(child) {
return (d.isTopLevelWindow(child) && (!predicate || predicate(child)));
});
}
function getDesktopWindow(item) {
return findParentMatching(item, isTopLevelWindow)
return d.findParentMatching(item, d.isTopLevelWindow)
}
function fixupZOrder(windows, basis, topWindow) {
@ -205,23 +205,23 @@ FocusScope {
function raiseWindow(targetWindow) {
var predicate;
var zBasis;
if (isModalWindow(targetWindow)) {
predicate = isModalWindow;
if (d.isModalWindow(targetWindow)) {
predicate = d.isModalWindow;
zBasis = zLevels.modal
} else if (isAlwaysOnTopWindow(targetWindow)) {
} else if (d.isAlwaysOnTopWindow(targetWindow)) {
predicate = function(window) {
return (isAlwaysOnTopWindow(window) && !isModalWindow(window));
return (d.isAlwaysOnTopWindow(window) && !d.isModalWindow(window));
}
zBasis = zLevels.top
} else {
predicate = function(window) {
return (!isAlwaysOnTopWindow(window) && !isModalWindow(window));
return (!d.isAlwaysOnTopWindow(window) && !d.isModalWindow(window));
}
zBasis = zLevels.normal
}
var windows = getTopLevelWindows(predicate);
fixupZOrder(windows, zBasis, targetWindow);
var windows = d.getTopLevelWindows(predicate);
d.fixupZOrder(windows, zBasis, targetWindow);
}
Component.onCompleted: {
@ -264,7 +264,7 @@ FocusScope {
}
function getRepositionChildren(predicate) {
return findMatchingChildren(desktop, function(child) {
return d.findMatchingChildren(desktop, function(child) {
return (child.shouldReposition === true && (!predicate || predicate(child)));
});
}

View file

@ -26,7 +26,7 @@ ScrollingWindow {
property var showCategories: []
minSize: Qt.vector2d(400, 500)
//HifiConstants { id: hifi }
HifiConstants { id: hifi }
function saveAll() {
for (var i = 0; i < sections.length; ++i) {
@ -96,9 +96,9 @@ ScrollingWindow {
anchors {
top: parent.top
right: parent.right
rightMargin: 10//hifi.dimensions.contentMargin.x
rightMargin: hifi.dimensions.contentMargin.x
}
spacing: 1//hifi.dimensions.contentSpacing.x
spacing: hifi.dimensions.contentSpacing.x
HifiControls.Button {
text: "Save changes"

View file

@ -0,0 +1,57 @@
//
// RadioButtonsPreference.qml
//
// Created by Cain Kilgore on 20th July 2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import "../../controls-uit"
Preference {
id: root
height: control.height + hifi.dimensions.controlInterlineHeight
Component.onCompleted: {
repeater.itemAt(preference.value).checked = true
}
function save() {
var value = 0;
for (var i = 0; i < repeater.count; i++) {
if (repeater.itemAt(i).checked) {
value = i;
break;
}
}
preference.value = value;
preference.save();
}
Row {
id: control
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
spacing: 5
Repeater {
id: repeater
model: preference.items.length
delegate: RadioButton {
text: preference.items[index]
anchors {
verticalCenter: parent.verticalCenter
}
colorScheme: hifi.colorSchemes.dark
}
}
}
}

View file

@ -74,6 +74,7 @@ Preference {
property var comboBoxBuilder: Component { ComboBoxPreference { } }
property var spinnerSliderBuilder: Component { SpinnerSliderPreference { } }
property var primaryHandBuilder: Component { PrimaryHandPreference { } }
property var radioButtonsBuilder: Component { RadioButtonsPreference { } }
property var preferences: []
property int checkBoxCount: 0
@ -140,6 +141,10 @@ Preference {
checkBoxCount++;
builder = primaryHandBuilder;
break;
case Preference.RadioButtons:
checkBoxCount++;
builder = radioButtonsBuilder;
break;
};
if (builder) {

View file

@ -53,6 +53,9 @@ Preference {
Slider {
id: slider
value: preference.value
minimumValue: preference.min
maximumValue: preference.max
step: preference.step
width: 130
anchors {
right: parent.right

View file

@ -36,6 +36,29 @@ OriginalDesktop.Desktop {
}
}
onHeightChanged: {
if (height > 100) {
adjustToolbarPosition();
}
}
function adjustToolbarPosition() {
var toolbar = getToolbar("com.highfidelity.interface.toolbar.system")
// check if Y position was changed, if toolbar height is assigned etc
// default position is adjusted to border width
var toolbarY = toolbar.settings.y > 6 ?
toolbar.settings.y :
desktop.height - (toolbar.height > 0 ? toolbar.height : 50) - 56
if (toolbar.settings.desktopHeight > 100 && desktop.height !== toolbar.settings.desktopHeight) {
toolbarY += desktop.height - toolbar.settings.desktopHeight
}
toolbar.y = toolbarY
toolbar.settings.y = toolbarY
toolbar.settings.desktopHeight = desktop.height
}
Component { id: toolbarBuilder; Toolbar { } }
// This used to create sysToolbar dynamically with a call to getToolbar() within onCompleted.
// Beginning with QT 5.6, this stopped working, as anything added to toolbars too early got
@ -47,7 +70,6 @@ OriginalDesktop.Desktop {
anchors.horizontalCenter: settings.constrainToolbarToCenterX ? desktop.horizontalCenter : undefined;
// Literal 50 is overwritten by settings from previous session, and sysToolbar.x comes from settings when not constrained.
x: sysToolbar.x
y: 50
buttonModel: tablet.buttons;
shown: tablet.toolbarMode;
}

View file

@ -0,0 +1,32 @@
//
// AssetDialog.qml
//
// Created by David Rowe on 18 Apr 2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.8
import "../../styles-uit"
import "../../windows"
ScrollingWindow {
id: root
resizable: false
implicitWidth: 480
implicitHeight: 706
objectName: "aboutDialog"
destroyOnCloseButton: true
destroyOnHidden: true
visible: true
minSize: Qt.vector2d(480, 706)
title: "About"
TabletAboutDialog {
width: pane.width
height: pane.height
}
}

View file

@ -7,7 +7,7 @@ PreferencesDialog {
id: root
objectName: "AvatarPreferencesDialog"
title: "Avatar Settings"
showCategories: [ "Avatar Basics", "Avatar Tuning", "Avatar Camera" ]
showCategories: [ "Avatar Basics", "Avatar Tuning" ]
property var settings: Settings {
category: root.objectName
property alias x: root.x

View file

@ -17,7 +17,7 @@ PreferencesDialog {
id: root
objectName: "GeneralPreferencesDialog"
title: "General Settings"
showCategories: ["UI", "Snapshots", "Privacy", "HMD", "Game Controller", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"]
showCategories: ["User Interface", "HMD", "Snapshots", "Privacy"]
property var settings: Settings {
category: root.objectName
property alias x: root.x

View file

@ -0,0 +1,29 @@
//
// AdvancedPreferencesDialog.qml
//
// Created by Brad Hefta-Gaub on 20 Jan 2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import Qt.labs.settings 1.0
import "../../dialogs"
PreferencesDialog {
id: root
objectName: "GraphicsPreferencesDialog"
title: "Graphics Settings"
showCategories: ["Graphics Quality" ]
property var settings: Settings {
category: root.objectName
property alias x: root.x
property alias y: root.y
property alias width: root.width
property alias height: root.height
}
}

View file

@ -79,7 +79,7 @@ ScrollingWindow {
interval: 1000
repeat: true
running: false
onTriggered: developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
onTriggered: developerMenuEnabled = MenuInterface.isOptionChecked("Developer Menu");
}
Component {
@ -98,7 +98,7 @@ ScrollingWindow {
Component.onCompleted: {
isHMD = HMD.active;
updateRunningScripts();
developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
developerMenuEnabled = MenuInterface.isOptionChecked("Developer Menu");
checkMenu.restart();
}

View file

@ -0,0 +1,122 @@
//
// TabletAssetDialog.qml
//
// Created by David Rowe on 18 Apr 2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import "../../styles-uit"
Rectangle {
width: 480
height: 706
color: "#404040"
Column {
x: 45
y: 30
spacing: 5
Image {
sourceSize.width: 295
sourceSize.height: 75
source: "../../../images/about-highfidelity.png"
}
Item { height: 30; width: 1 }
Column {
id: buildColumm
anchors.left: parent.left
anchors.leftMargin: 70
RalewayRegular {
text: "Build " + HiFiAbout.buildVersion
size: 16
color: "white"
}
RalewayRegular {
text: "Released " + HiFiAbout.buildDate
size: 16
color: "white"
}
}
Item { height: 10; width: 1 }
RalewayRegular {
text: "An open-source virtual reality platform."
size: 20
color: "white"
}
RalewayRegular {
textFormat: Text.StyledText
linkColor: "#00B4EF"
color: "white"
text: "<a href=\"https:/www.highfidelity.com\">www.highfidelity.com</a>."
size: 20
onLinkActivated: {
HiFiAbout.openUrl("https:/www.highfidelity.com");
}
}
Item { height: 40; width: 1 }
Row {
spacing: 5
Image {
sourceSize.width: 34
sourceSize.height: 25
source: "../../../images/about-qt.png"
MouseArea {
anchors.fill: parent
onClicked: {
HiFiAbout.openUrl("https://www.qt.io/");
}
}
}
RalewayRegular {
color: "white"
text: "Built using Qt " + HiFiAbout.qtVersion
size: 12
anchors.verticalCenter: parent.verticalCenter
}
Item { height: 1; width: 15 }
Image {
sourceSize.width: 70
sourceSize.height: 26
source: "../../../images/about-physics.png"
}
RalewayRegular {
color: "white"
text: "Physics powered by Bullet"
size: 12
anchors.verticalCenter: parent.verticalCenter
}
}
Item { height: 20; width: 1 }
RalewayRegular {
textFormat: Text.StyledText
linkColor: "#00B4EF"
color: "white"
text: "Blockchain technology from <a href=\"https://elementsproject.org/elements/\">Elements</a>."
size: 14
onLinkActivated: {
HiFiAbout.openUrl("https://elementsproject.org/elements/");
}
}
RalewayRegular {
color: "white"
text: "© 2018 High Fidelity. All rights reserved."
size: 14
}
RalewayRegular {
textFormat: Text.StyledText
color: "white"
linkColor: "#00B4EF"
text: "Distributed under the <a href=\"http://www.apache.org/licenses/LICENSE-2.0.html\">Apache License, Version 2.0.</a>."
size: 14
onLinkActivated: {
HiFiAbout.openUrl("http://www.apache.org/licenses/LICENSE-2.0.html");
}
}
}
}

View file

@ -65,7 +65,7 @@ Rectangle {
interval: 1000
repeat: true
running: false
onTriggered: developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
onTriggered: developerMenuEnabled = MenuInterface.isOptionChecked("Developer Menu");
}
Component {
@ -84,7 +84,7 @@ Rectangle {
Component.onCompleted: {
isHMD = HMD.active;
updateRunningScripts();
developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
developerMenuEnabled = MenuInterface.isOptionChecked("Developer Menu");
checkMenu.restart();
}

View file

@ -18,7 +18,7 @@ import "../../controls-uit" as HifiControls
Rectangle {
id: info
anchors.fill: parent
signal canceled()
signal restart()

View file

@ -8,222 +8,286 @@
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
import Qt.labs.settings 1.0
import "../../styles-uit"
import "../../controls"
import "../../controls-uit" as HifiControls
import "../../dialogs"
import "../../dialogs/preferences"
import "tabletWindows"
import "../audio"
StackView {
id: stack
initialItem: inputConfiguration
property alias messageVisible: imageMessageBox.visible
property alias selectedPlugin: box.currentText
Rectangle {
id: inputConfiguration
anchors.fill: parent
Item {
id: controllerSettings
height: parent.height
width: parent.width
HifiConstants { id: hifi }
HifiConstants { id: hifi }
color: hifi.colors.baseGray
TabBar {
id: bar
spacing: 0
anchors.top: controllerSettings.top
width: parent.width
height: 40
z: 10
property var pluginSettings: null
HifiControls.ImageMessageBox {
id: imageMessageBox
anchors.fill: parent
z: 2000
imageWidth: 442
imageHeight: 670
source: "../../../images/calibration-help.png"
}
Rectangle {
width: inputConfiguration.width
height: 1
color: hifi.colors.baseGrayShadow
x: -hifi.dimensions.contentMargin.x
}
RalewayRegular {
id: header
text: "Controller Settings"
size: 22
color: "white"
anchors.top: inputConfiguration.top
anchors.left: inputConfiguration.left
anchors.leftMargin: 20
anchors.topMargin: 20
}
Separator {
id: headerSeparator
width: inputConfiguration.width
anchors.top: header.bottom
anchors.topMargin: 10
}
HiFiGlyphs {
id: sourceGlyph
text: hifi.glyphs.source
size: 36
color: hifi.colors.blueHighlight
anchors.top: headerSeparator.bottom
anchors.left: inputConfiguration.left
anchors.leftMargin: 40
anchors.topMargin: 20
}
RalewayRegular {
id: configuration
text: "SELECT DEVICE"
size: 15
color: hifi.colors.lightGrayText
anchors.top: headerSeparator.bottom
anchors.left: sourceGlyph.right
anchors.leftMargin: 10
anchors.topMargin: 30
}
Row {
id: configRow
z: 999
anchors.top: sourceGlyph.bottom
anchors.topMargin: 20
anchors.left: sourceGlyph.left
anchors.leftMargin: 40
spacing: 10
HifiControls.ComboBox {
id: box
width: 160
z: 999
editable: true
colorScheme: hifi.colorSchemes.dark
model: inputPlugins()
label: ""
onCurrentIndexChanged: {
changeSource();
}
}
HifiControls.CheckBox {
id: checkBox
colorScheme: hifi.colorSchemes.dark
text: "show all input devices"
onClicked: {
inputPlugins();
changeSource();
}
TabButton {
height: parent.height
text: qsTr("Settings")
onClicked: {
stackView.clear();
stackView.push(controllerPreferencesComponent);
}
}
Separator {
id: configurationSeparator
z: 0
width: inputConfiguration.width
anchors.top: configRow.bottom
anchors.topMargin: 10
TabButton {
height: parent.height
text: qsTr("Calibration")
onClicked: {
stackView.clear();
stackView.push(inputConfigurationComponent);
}
}
}
HiFiGlyphs {
id: sliderGlyph
text: hifi.glyphs.sliders
size: 36
color: hifi.colors.blueHighlight
StackView {
id: stackView
anchors.top: bar.bottom
anchors.bottom: controllerSettings.bottom
anchors.left: controllerSettings.left
anchors.right: controllerSettings.right
anchors.top: configurationSeparator.bottom
anchors.left: inputConfiguration.left
anchors.leftMargin: 40
anchors.topMargin: 20
}
initialItem: controllerPreferencesComponent
}
RalewayRegular {
id: configurationHeader
text: "CONFIGURATION"
size: 15
color: hifi.colors.lightGrayText
Component {
id: inputConfigurationComponent
StackView {
id: stack
initialItem: inputConfiguration
property alias messageVisible: imageMessageBox.visible
property alias selectedPlugin: box.currentText
Rectangle {
id: inputConfiguration
anchors.fill: parent
HifiConstants { id: hifi }
color: hifi.colors.baseGray
property var pluginSettings: null
HifiControls.ImageMessageBox {
id: imageMessageBox
anchors.fill: parent
z: 2000
imageWidth: 442
imageHeight: 670
source: "../../../images/calibration-help.png"
}
Rectangle {
width: inputConfiguration.width
height: 1
color: hifi.colors.baseGrayShadow
x: -hifi.dimensions.contentMargin.x
}
RalewayRegular {
id: header
text: "Control Settings"
size: 22
color: "white"
anchors.top: inputConfiguration.top
anchors.left: inputConfiguration.left
anchors.leftMargin: 20
anchors.topMargin: 20
}
Separator {
id: headerSeparator
width: inputConfiguration.width
anchors.top: header.bottom
anchors.topMargin: 10
}
HiFiGlyphs {
id: sourceGlyph
text: hifi.glyphs.source
size: 36
color: hifi.colors.blueHighlight
anchors.top: headerSeparator.bottom
anchors.left: inputConfiguration.left
anchors.leftMargin: 40
anchors.topMargin: 20
}
RalewayRegular {
id: configuration
text: "SELECT DEVICE"
size: 15
color: hifi.colors.lightGrayText
anchors.top: configurationSeparator.bottom
anchors.left: sliderGlyph.right
anchors.leftMargin: 10
anchors.topMargin: 30
}
anchors.top: headerSeparator.bottom
anchors.left: sourceGlyph.right
anchors.leftMargin: 10
anchors.topMargin: 30
}
Loader {
id: loader
asynchronous: false
Row {
id: configRow
z: 999
anchors.top: sourceGlyph.bottom
anchors.topMargin: 20
anchors.left: sourceGlyph.left
anchors.leftMargin: 40
spacing: 10
HifiControls.ComboBox {
id: box
width: 160
z: 999
editable: true
colorScheme: hifi.colorSchemes.dark
model: inputPlugins()
label: ""
width: inputConfiguration.width
anchors.left: inputConfiguration.left
anchors.right: inputConfiguration.right
anchors.top: configurationHeader.bottom
anchors.topMargin: 10
anchors.bottom: inputConfiguration.bottom
onCurrentIndexChanged: {
changeSource();
}
}
source: InputConfiguration.configurationLayout(box.currentText);
onLoaded: {
if (loader.item.hasOwnProperty("pluginName")) {
if (box.currentText === "HTC Vive") {
loader.item.pluginName = "OpenVR";
} else {
loader.item.pluginName = box.currentText;
HifiControls.CheckBox {
id: checkBox
colorScheme: hifi.colorSchemes.dark
text: "show all input devices"
onClicked: {
inputPlugins();
changeSource();
}
}
}
if (loader.item.hasOwnProperty("displayInformation")) {
loader.item.displayConfiguration();
Separator {
id: configurationSeparator
z: 0
width: inputConfiguration.width
anchors.top: configRow.bottom
anchors.topMargin: 10
}
HiFiGlyphs {
id: sliderGlyph
text: hifi.glyphs.sliders
size: 36
color: hifi.colors.blueHighlight
anchors.top: configurationSeparator.bottom
anchors.left: inputConfiguration.left
anchors.leftMargin: 40
anchors.topMargin: 20
}
RalewayRegular {
id: configurationHeader
text: "CONFIGURATION"
size: 15
color: hifi.colors.lightGrayText
anchors.top: configurationSeparator.bottom
anchors.left: sliderGlyph.right
anchors.leftMargin: 10
anchors.topMargin: 30
}
Loader {
id: loader
asynchronous: false
width: inputConfiguration.width
anchors.left: inputConfiguration.left
anchors.right: inputConfiguration.right
anchors.top: configurationHeader.bottom
anchors.topMargin: 10
anchors.bottom: inputConfiguration.bottom
source: InputConfiguration.configurationLayout(box.currentText);
onLoaded: {
if (loader.item.hasOwnProperty("pluginName")) {
if (box.currentText === "HTC Vive") {
loader.item.pluginName = "OpenVR";
} else {
loader.item.pluginName = box.currentText;
}
}
if (loader.item.hasOwnProperty("displayInformation")) {
loader.item.displayConfiguration();
}
}
}
}
function inputPlugins() {
if (checkBox.checked) {
return InputConfiguration.inputPlugins();
} else {
return InputConfiguration.activeInputPlugins();
}
}
function initialize() {
changeSource();
}
function changeSource() {
loader.source = "";
var source = "";
if (box.currentText == "Vive") {
source = InputConfiguration.configurationLayout("OpenVR");
} else {
source = InputConfiguration.configurationLayout(box.currentText);
}
loader.source = source;
if (source === "") {
box.label = "(not configurable)";
} else {
box.label = "";
}
}
Timer {
id: timer
repeat: false
interval: 300
onTriggered: initialize()
}
Component.onCompleted: {
timer.start();
}
}
}
function inputPlugins() {
if (checkBox.checked) {
return InputConfiguration.inputPlugins();
} else {
return InputConfiguration.activeInputPlugins();
Component {
id: controllerPreferencesComponent
TabletPreferencesDialog {
anchors.fill: stackView
id: controllerPrefereneces
objectName: "TabletControllerPreferences"
showCategories: ["VR Movement", "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"]
}
}
function initialize() {
changeSource();
}
function changeSource() {
loader.source = "";
var source = "";
if (box.currentText == "Vive") {
source = InputConfiguration.configurationLayout("OpenVR");
} else {
source = InputConfiguration.configurationLayout(box.currentText);
}
loader.source = source;
if (source === "") {
box.label = "(not configurable)";
} else {
box.label = "";
}
}
Timer {
id: timer
repeat: false
interval: 300
onTriggered: initialize()
}
Component.onCompleted: {
timer.start();
}
}

View file

@ -32,6 +32,6 @@ StackView {
TabletPreferencesDialog {
id: root
objectName: "TabletGeneralPreferences"
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Game Controller", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"]
showCategories: ["User Interface", "HMD", "Snapshots", "Privacy"]
}
}

View file

@ -32,6 +32,6 @@ StackView {
TabletPreferencesDialog {
id: root
objectName: "TabletGraphicsPreferences"
showCategories: ["Graphics"]
showCategories: ["Graphics Quality"]
}
}

View file

@ -19,6 +19,7 @@ Item {
id: dialog
width: parent.width
height: parent.height
anchors.fill: parent
HifiConstants { id: hifi }
property var sections: []
@ -158,14 +159,15 @@ Item {
Rectangle {
id: footer
height: 40
height: dialog.height * 0.15
anchors.bottom: dialog.bottom
anchors {
bottom: keyboard.top
left: parent.left
right: parent.right
}
color: hifi.colors.baseGray
Row {

View file

@ -82,6 +82,7 @@ Preference {
property var comboBoxBuilder: Component { ComboBoxPreference { } }
property var spinnerSliderBuilder: Component { SpinnerSliderPreference { } }
property var primaryHandBuilder: Component { PrimaryHandPreference { } }
property var radioButtonsBuilder: Component { RadioButtonsPreference { } }
property var preferences: []
property int checkBoxCount: 0
@ -154,6 +155,10 @@ Preference {
checkBoxCount++;
builder = primaryHandBuilder;
break;
case Preference.RadioButtons:
checkBoxCount++;
builder = radioButtonsBuilder;
break;
};
if (builder) {

View file

@ -25,12 +25,24 @@ Window {
property bool horizontal: true
property real buttonSize: 50;
property alias settings: settings
Settings {
id: settings
category: "toolbar/" + window.objectName
property alias x: window.x
property alias y: window.y
property real y: 0
property real desktopHeight: 0 //placeholder for desktop height
}
onYChanged: {
//check if Y changed not due to Desktop size changed. ie. mouse manipulations
//otherwise it will be save by Desktop
if (desktop.height > 100 && desktop.height === settings.desktopHeight) {
settings.y = window.y
}
}
Component {
id: buttonComponent
ToolbarButton {

View file

@ -43,7 +43,7 @@ Item {
Text {
id: debugZ
visible: DebugQML
visible: (typeof DebugQML !== 'undefined') ? DebugQML : false
color: "red"
text: (window ? "Z: " + window.z : "") + " " + qmlFile
y: window ? window.height + 4 : 0

View file

@ -0,0 +1,69 @@
//
// AboutUtil.cpp
// interface/src
//
// Created by Vlad Stelmahovsky on 15/5/2018.
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDate>
#include <QLocale>
#include "AboutUtil.h"
#include "BuildInfo.h"
#include <ui/TabletScriptingInterface.h>
#include "DependencyManager.h"
#include "scripting/HMDScriptingInterface.h"
#include "Application.h"
#include <OffscreenQmlDialog.h>
AboutUtil::AboutUtil(QObject *parent) : QObject(parent) {
QLocale locale_;
m_DateConverted = QDate::fromString(BuildInfo::BUILD_TIME, "dd/MM/yyyy").
toString(locale_.dateFormat(QLocale::ShortFormat));
}
AboutUtil *AboutUtil::getInstance()
{
static AboutUtil instance;
return &instance;
}
QString AboutUtil::buildDate() const
{
return m_DateConverted;
}
QString AboutUtil::buildVersion() const
{
return BuildInfo::VERSION;
}
QString AboutUtil::qtVersion() const
{
return qVersion();
}
void AboutUtil::openUrl(const QString& url) const {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
auto hmd = DependencyManager::get<HMDScriptingInterface>();
auto offscreenUi = DependencyManager::get<OffscreenUi>();
if (tablet->getToolbarMode()) {
offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
newObject->setProperty("url", url);
});
} else {
if (!hmd->getShouldShowTablet() && !qApp->isHMDMode()) {
offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
newObject->setProperty("url", url);
});
} else {
tablet->gotoWebScreen(url);
}
}
}

42
interface/src/AboutUtil.h Normal file
View file

@ -0,0 +1,42 @@
//
// AboutUtil.h
// interface/src
//
// Created by Vlad Stelmahovsky on 15/5/2018.
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AboutUtil_h
#define hifi_AboutUtil_h
#include <QObject>
class AboutUtil : public QObject {
Q_OBJECT
Q_PROPERTY(QString buildDate READ buildDate CONSTANT)
Q_PROPERTY(QString buildVersion READ buildVersion CONSTANT)
Q_PROPERTY(QString qtVersion READ qtVersion CONSTANT)
AboutUtil(QObject* parent = nullptr);
public:
static AboutUtil* getInstance();
~AboutUtil() {}
QString buildDate() const;
QString buildVersion() const;
QString qtVersion() const;
public slots:
void openUrl(const QString &url) const;
private:
QString m_DateConverted;
};
#endif // hifi_AboutUtil_h

View file

@ -220,6 +220,8 @@
#include "webbrowser/WebBrowserSuggestionsEngine.h"
#include <DesktopPreviewProvider.h>
#include "AboutUtil.h"
#if defined(Q_OS_WIN)
#include <VersionHelpers.h>
@ -715,7 +717,7 @@ private:
* <tr><td><code>NavigationFocused</code></td><td>number</td><td>number</td><td><em>Not used.</em></td></tr>
* </tbody>
* </table>
* @typedef Controller.Hardware-Application
* @typedef {object} Controller.Hardware-Application
*/
static const QString STATE_IN_HMD = "InHMD";
@ -2741,7 +2743,7 @@ void Application::initializeRenderEngine() {
}
extern void setupPreferences();
static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, bool active);
static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active = false);
void Application::initializeUi() {
// Build a shared canvas / context for the Chromium processes
@ -2899,9 +2901,11 @@ void Application::initializeUi() {
std::stable_sort(displayPlugins.begin(), displayPlugins.end(),
[](const DisplayPluginPointer& a, const DisplayPluginPointer& b)->bool { return a->getGrouping() < b->getGrouping(); });
int dpIndex = 1;
// concatenate the groupings into a single list in the order: standard, advanced, developer
for(const auto& displayPlugin : displayPlugins) {
addDisplayPluginToMenu(displayPlugin, _displayPlugin == displayPlugin);
addDisplayPluginToMenu(displayPlugin, dpIndex, _displayPlugin == displayPlugin);
dpIndex++;
}
// after all plugins have been added to the menu, add a separator to the menu
@ -2993,6 +2997,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("Selection", DependencyManager::get<SelectionScriptingInterface>().data());
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
surfaceContext->setContextProperty("Wallet", DependencyManager::get<WalletScriptingInterface>().data());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance());
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
@ -3715,7 +3720,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (isShifted && isMeta) {
Menu::getInstance()->triggerOption(MenuOption::Log);
} else if (isMeta) {
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
auto dialogsManager = DependencyManager::get<DialogsManager>();
dialogsManager->toggleAddressBar();
} else if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::LodTools);
}
@ -3748,13 +3754,16 @@ void Application::keyPressEvent(QKeyEvent* event) {
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);
Setting::Handle<bool> notificationSounds{ MenuOption::NotificationSounds, true};
Setting::Handle<bool> notificationSoundSnapshot{ MenuOption::NotificationSoundsSnapshot, true};
if (notificationSounds.get() && notificationSoundSnapshot.get()) {
if (_snapshotSoundInjector) {
_snapshotSoundInjector->setOptions(options);
_snapshotSoundInjector->restart();
} else {
QByteArray samples = _snapshotSound->getByteArray();
_snapshotSoundInjector = AudioInjector::playSound(samples, options);
}
}
takeSnapshot(true);
break;
@ -4623,12 +4632,6 @@ void Application::idle() {
_overlayConductor.update(secondsSinceLastUpdate);
auto myAvatar = getMyAvatar();
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN);
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN));
cameraMenuChanged();
}
_gameLoopCounter.increment();
}
@ -5121,7 +5124,7 @@ void Application::toggleOverlays() {
void Application::setOverlaysVisible(bool visible) {
auto menu = Menu::getInstance();
menu->setIsOptionChecked(MenuOption::Overlays, true);
menu->setIsOptionChecked(MenuOption::Overlays, visible);
}
void Application::centerUI() {
@ -5175,6 +5178,21 @@ void Application::cameraModeChanged() {
cameraMenuChanged();
}
void Application::changeViewAsNeeded(float boomLength) {
// Switch between first and third person views as needed
// This is called when the boom length has changed
bool boomLengthGreaterThanMinimum = (boomLength > MyAvatar::ZOOM_MIN);
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON && boomLengthGreaterThanMinimum) {
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
cameraMenuChanged();
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON && !boomLengthGreaterThanMinimum) {
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true);
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, false);
cameraMenuChanged();
}
}
void Application::cameraMenuChanged() {
auto menu = Menu::getInstance();
@ -6606,6 +6624,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
scriptEngine->registerGlobalObject("Wallet", DependencyManager::get<WalletScriptingInterface>().data());
scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get<AddressManager>().data());
scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance());
qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue);
@ -6923,12 +6942,7 @@ void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const
if (onTablet) {
toggleTabletUI(true);
}
}
if (!onTablet) {
DependencyManager::get<OffscreenUi>()->show(widgetUrl, name);
}
if (tablet->getToolbarMode()) {
} else {
DependencyManager::get<OffscreenUi>()->show(widgetUrl, name);
}
}
@ -7550,7 +7564,6 @@ void Application::loadLODToolsDialog() {
}
}
void Application::loadEntityStatisticsDialog() {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET));
@ -7889,7 +7902,7 @@ DisplayPluginPointer Application::getActiveDisplayPlugin() const {
static const char* EXCLUSION_GROUP_KEY = "exclusionGroup";
static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, bool active) {
static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active) {
auto menu = Menu::getInstance();
QString name = displayPlugin->getName();
auto grouping = displayPlugin->getGrouping();
@ -7916,7 +7929,7 @@ static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, bo
}
auto parent = menu->getMenu(MenuOption::OutputMenu);
auto action = menu->addActionToQMenuAndActionHash(parent,
name, 0, qApp,
name, QKeySequence(Qt::CTRL + (Qt::Key_0 + index)), qApp,
SLOT(updateDisplayMode()),
QAction::NoRole, Menu::UNSPECIFIED_POSITION, groupingMenu);
@ -8194,7 +8207,6 @@ void Application::readArgumentsFromLocalSocket() const {
}
void Application::showDesktop() {
Menu::getInstance()->setIsOptionChecked(MenuOption::Overlays, true);
}
CompositorHelper& Application::getApplicationCompositor() const {

View file

@ -417,6 +417,8 @@ public slots:
void updateVerboseLogging();
void changeViewAsNeeded(float boomLength);
private slots:
void onDesktopRootItemCreated(QQuickItem* qmlContext);
void onDesktopRootContextCreated(QQmlContext* qmlContext);
@ -649,7 +651,7 @@ private:
quint64 _lastFaceTrackerUpdate;
render::ScenePointer _main3DScene{ new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) };
render::EnginePointer _renderEngine{ new render::Engine() };
render::EnginePointer _renderEngine{ new render::RenderEngine() };
gpu::ContextPointer _gpuContext; // initialized during window creation
mutable QMutex _renderArgsMutex{ QMutex::Recursive };

View file

@ -35,6 +35,8 @@ bool CrashHandler::checkForResetSettings(bool wasLikelyCrash, bool suppressPromp
QSettings settings;
settings.beginGroup("Developer");
QVariant displayCrashOptions = settings.value(MenuOption::DisplayCrashOptions);
settings.endGroup();
settings.beginGroup("Settings");
QVariant askToResetSettingsOption = settings.value(MenuOption::AskToResetSettings);
settings.endGroup();
bool askToResetSettings = askToResetSettingsOption.isValid() && askToResetSettingsOption.toBool();

View file

@ -25,7 +25,7 @@ class FancyCamera : public Camera {
// FIXME: JSDoc 3.5.5 doesn't augment @property definitions. The following definition is repeated in Camera.h.
/**jsdoc
* @property cameraEntity {Uuid} The ID of the entity that the camera position and orientation follow when the camera is in
* @property {Uuid} cameraEntity The ID of the entity that the camera position and orientation follow when the camera is in
* entity mode.
*/
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)

View file

@ -10,7 +10,7 @@
//
#include "Menu.h"
#include <QDesktopServices>
#include <QFileDialog>
#include <QMenuBar>
#include <QShortcut>
@ -51,6 +51,7 @@
#include "RenderShadowTask.h"
#include "AntialiasingEffect.h"
#include "scripting/SettingsScriptingInterface.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h"
#endif
@ -81,9 +82,6 @@ Menu::Menu() {
dialogsManager.data(), &DialogsManager::toggleLoginDialog);
}
// File > Help
addActionToQMenuAndActionHash(fileMenu, MenuOption::Help, 0, qApp, SLOT(showHelp()));
// File > Quit
addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp, SLOT(quit()), QAction::QuitRole);
@ -102,6 +100,22 @@ Menu::Menu() {
redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z);
addActionToQMenuAndActionHash(editMenu, redoAction);
editMenu->addSeparator();
// Edit > Cut
addActionToQMenuAndActionHash(editMenu, "Cut", Qt::CTRL | Qt::Key_X);
// Edit > Copy
addActionToQMenuAndActionHash(editMenu, "Copy", Qt::CTRL | Qt::Key_C);
// Edit > Paste
addActionToQMenuAndActionHash(editMenu, "Paste", Qt::CTRL | Qt::Key_V);
// Edit > Delete
addActionToQMenuAndActionHash(editMenu, "Delete", Qt::Key_Delete);
editMenu->addSeparator();
// Edit > Running Scripts
auto action = addActionToQMenuAndActionHash(editMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J);
connect(action, &QAction::triggered, [] {
@ -111,41 +125,9 @@ Menu::Menu() {
qApp->showDialog(widgetUrl, tabletUrl, name);
});
// Edit > Open and Run Script from File... [advanced]
addActionToQMenuAndActionHash(editMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O,
qApp, SLOT(loadDialog()),
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
// Edit > Open and Run Script from Url... [advanced]
addActionToQMenuAndActionHash(editMenu, MenuOption::LoadScriptURL,
Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog()),
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
auto scriptEngines = DependencyManager::get<ScriptEngines>();
// Edit > Stop All Scripts... [advanced]
addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0,
scriptEngines.data(), SLOT(stopAllScripts()),
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
// Edit > Reload All Scripts... [advanced]
action = addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R,
nullptr, nullptr,
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
connect(action, &QAction::triggered, [] {
DependencyManager::get<ScriptEngines>()->reloadAllScripts();
DependencyManager::get<OffscreenUi>()->clearCache();
});
// Edit > Console... [advanced]
addActionToQMenuAndActionHash(editMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J,
DependencyManager::get<StandAloneJSConsole>().data(),
SLOT(toggleConsole()),
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
editMenu->addSeparator();
// Edit > My Asset Server
// Edit > Asset Browser
auto assetServerAction = addActionToQMenuAndActionHash(editMenu, MenuOption::AssetServer,
Qt::CTRL | Qt::SHIFT | Qt::Key_A,
qApp, SLOT(showAssetServerWidget()));
@ -153,30 +135,20 @@ Menu::Menu() {
QObject::connect(nodeList.data(), &NodeList::canWriteAssetsChanged, assetServerAction, &QAction::setEnabled);
assetServerAction->setEnabled(nodeList->getThisNodeCanWriteAssets());
// Edit > Package Model... [advanced]
// Edit > Package Model as .fst...
addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0,
qApp, SLOT(packageModel()),
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
qApp, SLOT(packageModel()));
// Edit > Reload All Content
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
// Edit > Reload All Content [advanced]
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
// Avatar menu ----------------------------------
MenuWrapper* avatarMenu = addMenu("Avatar");
auto avatarManager = DependencyManager::get<AvatarManager>();
auto avatar = avatarManager->getMyAvatar();
// Avatar > Attachments...
action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments);
connect(action, &QAction::triggered, [] {
qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"),
QString("hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog");
});
// Avatar > Size
// Avatar > Size
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
// Avatar > Size > Increase
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::IncreaseAvatarSize,
@ -201,16 +173,15 @@ Menu::Menu() {
0, // QML Qt::Key_Apostrophe,
qApp, SLOT(resetSensors()));
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableAvatarCollisions, 0, true,
avatar.get(), SLOT(updateMotionBehaviorFromMenu()));
// Avatar > Attachments...
action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments);
connect(action, &QAction::triggered, [] {
qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"),
QString("hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog");
});
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableFlying, 0, true,
avatar.get(), SLOT(setFlyingEnabled(bool)));
// Avatar > AvatarBookmarks related menus -- Note: the AvatarBookmarks class adds its own submenus here.
auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>();
avatarBookmarks->setupMenus(this, avatarMenu);
// Display menu ----------------------------------
// FIXME - this is not yet matching Alan's spec because it doesn't have
// menus for "2D"/"3D" - we need to add support for detecting the appropriate
@ -249,19 +220,17 @@ Menu::Menu() {
viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
// View > Independent [advanced]
// View > Independent
auto viewIndependentAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::IndependentMode, 0,
false, qApp, SLOT(cameraMenuChanged()),
UNSPECIFIED_POSITION, "Advanced"));
false, qApp, SLOT(cameraMenuChanged())));
viewIndependentAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
// View > Entity Camera [advanced]
// View > Entity Camera
auto viewEntityCameraAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::CameraEntityMode, 0,
false, qApp, SLOT(cameraMenuChanged()),
UNSPECIFIED_POSITION, "Advanced"));
false, qApp, SLOT(cameraMenuChanged())));
viewEntityCameraAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
@ -269,54 +238,54 @@ Menu::Menu() {
// View > Center Player In View
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView,
0, true, qApp, SLOT(rotationModeChanged()),
UNSPECIFIED_POSITION, "Advanced");
0, true, qApp, SLOT(rotationModeChanged()));
// View > Overlays
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Overlays, 0, true);
// View > Enter First Person Mode in HMD
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPersonHMD, 0, true);
//TODO: Remove Navigation menu when these functions are included in GoTo menu
// Navigate menu ----------------------------------
MenuWrapper* navigateMenu = addMenu("Navigate");
// Navigate > Show Address Bar
addActionToQMenuAndActionHash(navigateMenu, MenuOption::AddressBar, Qt::CTRL | Qt::Key_L,
dialogsManager.data(), SLOT(toggleAddressBar()));
// Navigate > LocationBookmarks related menus -- Note: the LocationBookmarks class adds its own submenus here.
auto locationBookmarks = DependencyManager::get<LocationBookmarks>();
locationBookmarks->setupMenus(this, navigateMenu);
// Navigate > Copy Address [advanced]
// Navigate > Copy Address
auto addressManager = DependencyManager::get<AddressManager>();
addActionToQMenuAndActionHash(navigateMenu, MenuOption::CopyAddress, 0,
addressManager.data(), SLOT(copyAddress()),
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
addressManager.data(), SLOT(copyAddress()));
// Navigate > Copy Path [advanced]
// Navigate > Copy Path
addActionToQMenuAndActionHash(navigateMenu, MenuOption::CopyPath, 0,
addressManager.data(), SLOT(copyPath()),
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
addressManager.data(), SLOT(copyPath()));
// Settings menu ----------------------------------
MenuWrapper* settingsMenu = addMenu("Settings");
// Settings > Advance Menus
addCheckableActionToQMenuAndActionHash(settingsMenu, "Advanced Menus", 0, false, this, SLOT(toggleAdvancedMenus()));
// Settings > Developer Menus
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menus", 0, false, this, SLOT(toggleDeveloperMenus()));
// Settings > General...
action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, nullptr, nullptr, QAction::PreferencesRole);
action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_G, nullptr, nullptr, QAction::PreferencesRole);
connect(action, &QAction::triggered, [] {
qApp->showDialog(QString("hifi/dialogs/GeneralPreferencesDialog.qml"),
QString("hifi/tablet/TabletGeneralPreferences.qml"), "GeneralPreferencesDialog");
});
// Settings > Controls...
action = addActionToQMenuAndActionHash(settingsMenu, "Controls...");
connect(action, &QAction::triggered, [] {
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
auto hmd = DependencyManager::get<HMDScriptingInterface>();
tablet->loadQMLSource("hifi/tablet/ControllerSettings.qml");
if (!hmd->getShouldShowTablet()) {
hmd->toggleShouldShowTablet();
}
});
// Settings > Audio...
action = addActionToQMenuAndActionHash(settingsMenu, "Audio...");
connect(action, &QAction::triggered, [] {
static const QUrl widgetUrl("hifi/dialogs/Audio.qml");
@ -325,6 +294,13 @@ Menu::Menu() {
qApp->showDialog(widgetUrl, tabletUrl, name);
});
// Settings > Graphics...
action = addActionToQMenuAndActionHash(settingsMenu, "Graphics...");
connect(action, &QAction::triggered, [] {
qApp->showDialog(QString("hifi/dialogs/GraphicsPreferencesDialog.qml"),
QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog");
});
// Settings > Avatar...
action = addActionToQMenuAndActionHash(settingsMenu, "Avatar...");
connect(action, &QAction::triggered, [] {
@ -332,35 +308,11 @@ Menu::Menu() {
QString("hifi/tablet/TabletAvatarPreferences.qml"), "AvatarPreferencesDialog");
});
// Settings > LOD...
action = addActionToQMenuAndActionHash(settingsMenu, "LOD...");
connect(action, &QAction::triggered, [] {
qApp->showDialog(QString("hifi/dialogs/LodPreferencesDialog.qml"),
QString("hifi/tablet/TabletLodPreferences.qml"), "LodPreferencesDialog");
});
// Settings > Developer Menu
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menu", 0, false, this, SLOT(toggleDeveloperMenus()));
action = addActionToQMenuAndActionHash(settingsMenu, "Controller Settings...");
connect(action, &QAction::triggered, [] {
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
auto hmd = DependencyManager::get<HMDScriptingInterface>();
tablet->loadQMLSource("hifi/tablet/ControllerSettings.qml");
if (!hmd->getShouldShowTablet()) {
hmd->toggleShouldShowTablet();
}
});
// Settings > Control with Speech [advanced]
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
auto speechRecognizer = DependencyManager::get<SpeechRecognizer>();
QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(settingsMenu, MenuOption::ControlWithSpeech,
Qt::CTRL | Qt::SHIFT | Qt::Key_C,
speechRecognizer->getEnabled(),
speechRecognizer.data(),
SLOT(setEnabled(bool)),
UNSPECIFIED_POSITION, "Advanced");
connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
#endif
// Settings > Ask to Reset Settings
addCheckableActionToQMenuAndActionHash(settingsMenu, MenuOption::AskToResetSettings, 0, false);
// Developer menu ----------------------------------
MenuWrapper* developerMenu = addMenu("Developer", "Developer");
@ -408,7 +360,7 @@ Menu::Menu() {
if (mainViewShadowTaskConfig) {
if (action->isChecked()) {
mainViewShadowTaskConfig->setPreset("Enabled");
} else {
} else {
mainViewShadowTaskConfig->setPreset("None");
}
}
@ -761,12 +713,8 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraints, 0, false, qApp, SLOT(setShowBulletConstraints(bool)));
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraintLimits, 0, false, qApp, SLOT(setShowBulletConstraintLimits(bool)));
// Developer > Ask to Reset Settings
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AskToResetSettings, 0, false);
// Developer > Display Crash Options
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisplayCrashOptions, 0, true);
// Developer > Crash >>>
MenuWrapper* crashMenu = developerMenu->addMenu("Crash");
@ -803,6 +751,37 @@ Menu::Menu() {
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashNewFaultThreaded);
connect(action, &QAction::triggered, qApp, []() { std::thread(crash::newFault).join(); });
// Developer > Stats
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats);
// Settings > Enable Speech Control API
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
auto speechRecognizer = DependencyManager::get<SpeechRecognizer>();
QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::ControlWithSpeech,
Qt::CTRL | Qt::SHIFT | Qt::Key_C,
speechRecognizer->getEnabled(),
speechRecognizer.data(),
SLOT(setEnabled(bool)),
UNSPECIFIED_POSITION);
connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
#endif
// console
addActionToQMenuAndActionHash(developerMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J,
DependencyManager::get<StandAloneJSConsole>().data(),
SLOT(toggleConsole()),
QAction::NoRole,
UNSPECIFIED_POSITION);
// Developer > API Debugger
action = addActionToQMenuAndActionHash(developerMenu, "API Debugger");
connect(action, &QAction::triggered, [] {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/tools/currentAPI.js");
scriptEngines->loadScript(defaultScriptsLoc.toString());
});
// Developer > Log...
addActionToQMenuAndActionHash(developerMenu, MenuOption::Log, Qt::CTRL | Qt::SHIFT | Qt::Key_L,
qApp, SLOT(toggleLogDialog()));
@ -817,25 +796,6 @@ Menu::Menu() {
action = addActionToQMenuAndActionHash(developerMenu, "Script Log (HMD friendly)...", Qt::NoButton,
qApp, SLOT(showScriptLogs()));
// Developer > Stats
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats);
// Developer > Advanced Settings...
action = addActionToQMenuAndActionHash(developerMenu, "Advanced Preferences...");
connect(action, &QAction::triggered, [] {
qApp->showDialog(QString("hifi/dialogs/AdvancedPreferencesDialog.qml"),
QString("hifi/tablet/AdvancedPreferencesDialog.qml"), "AdvancedPreferencesDialog");
});
// Developer > API Debugger
action = addActionToQMenuAndActionHash(developerMenu, "API Debugger");
connect(action, &QAction::triggered, [] {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/tools/currentAPI.js");
scriptEngines->loadScript(defaultScriptsLoc.toString());
});
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::VerboseLogging, 0, false,
qApp, SLOT(updateVerboseLogging()));
@ -864,6 +824,51 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
#endif
// Help/Application menu ----------------------------------
MenuWrapper * helpMenu = addMenu("Help");
// Help > About High Fidelity
action = addActionToQMenuAndActionHash(helpMenu, "About High Fidelity");
connect(action, &QAction::triggered, [] {
qApp->showDialog(QString("hifi/dialogs/AboutDialog.qml"),
QString("hifi/dialogs/TabletAboutDialog.qml"), "AboutDialog");
});
helpMenu->addSeparator();
// Help > HiFi Docs
action = addActionToQMenuAndActionHash(helpMenu, "Online Documentation");
connect(action, &QAction::triggered, qApp, [] {
QDesktopServices::openUrl(QUrl("https://docs.highfidelity.com/"));
});
// Help > HiFi Forum
action = addActionToQMenuAndActionHash(helpMenu, "Online Forums");
connect(action, &QAction::triggered, qApp, [] {
QDesktopServices::openUrl(QUrl("https://forums.highfidelity.com/"));
});
// Help > Scripting Reference
action = addActionToQMenuAndActionHash(helpMenu, "Online Script Reference");
connect(action, &QAction::triggered, qApp, [] {
QDesktopServices::openUrl(QUrl("https://docs.highfidelity.com/api-reference"));
});
addActionToQMenuAndActionHash(helpMenu, "Controls Reference", 0, qApp, SLOT(showHelp()));
helpMenu->addSeparator();
// Help > Release Notes
action = addActionToQMenuAndActionHash(helpMenu, "Release Notes");
connect(action, &QAction::triggered, qApp, [] {
QDesktopServices::openUrl(QUrl("http://steamcommunity.com/games/390540/announcements/"));
});
// Help > Report a Bug!
action = addActionToQMenuAndActionHash(helpMenu, "Report a Bug!");
connect(action, &QAction::triggered, qApp, [] {
QDesktopServices::openUrl(QUrl("mailto:support@highfidelity.com"));
});
}
void Menu::addMenuItem(const MenuItemProperties& properties) {

View file

@ -32,7 +32,7 @@ namespace MenuOption {
const QString AnimDebugDrawAnimPose = "Debug Draw Animation";
const QString AnimDebugDrawDefaultPose = "Debug Draw Default Pose";
const QString AnimDebugDrawPosition= "Debug Draw Position";
const QString AskToResetSettings = "Ask To Reset Settings";
const QString AskToResetSettings = "Ask To Reset Settings on Start";
const QString AssetMigration = "ATP Asset Migration";
const QString AssetServer = "Asset Browser";
const QString Attachments = "Attachments...";
@ -59,7 +59,7 @@ namespace MenuOption {
const QString Collisions = "Collisions";
const QString Connexion = "Activate 3D Connexion Devices";
const QString Console = "Console...";
const QString ControlWithSpeech = "Control With Speech";
const QString ControlWithSpeech = "Enable Speech Control API";
const QString CopyAddress = "Copy Address to Clipboard";
const QString CopyPath = "Copy Path to Clipboard";
const QString CoupleEyelids = "Couple Eyelids";
@ -122,7 +122,7 @@ namespace MenuOption {
const QString LoadScript = "Open and Run Script File...";
const QString LoadScriptURL = "Open and Run Script from URL...";
const QString LodTools = "LOD Tools";
const QString Login = "Login / Sign Up";
const QString Login = "Login/Sign Up";
const QString Log = "Log";
const QString LogExtraTimings = "Log Extra Timing Details";
const QString LowVelocityFilter = "Low Velocity Filter";
@ -137,8 +137,8 @@ namespace MenuOption {
const QString OnlyDisplayTopTen = "Only Display Top Ten";
const QString OpenVrThreadedSubmit = "OpenVR Threaded Submit";
const QString OutputMenu = "Display";
const QString Overlays = "Overlays";
const QString PackageModel = "Package Model...";
const QString Overlays = "Show Overlays";
const QString PackageModel = "Package Model as .fst...";
const QString Pair = "Pair";
const QString PhysicsShowHulls = "Draw Collision Shapes";
const QString PhysicsShowOwned = "Highlight Simulation Ownership";
@ -196,7 +196,7 @@ namespace MenuOption {
const QString SimulateEyeTracking = "Simulate";
const QString SMIEyeTracking = "SMI Eye Tracking";
const QString SparseTextureManagement = "Enable Sparse Texture Management";
const QString Stats = "Stats";
const QString Stats = "Show Statistics";
const QString StopAllScripts = "Stop All Scripts";
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
const QString ThirdPerson = "Third Person";
@ -217,6 +217,9 @@ namespace MenuOption {
const QString Shadows = "Shadows";
const QString AntiAliasing = "Temporal Antialiasing (FXAA if disabled)";
const QString AmbientOcclusion = "Ambient Occlusion";
const QString NotificationSounds = "play_notification_sounds";
const QString NotificationSoundsSnapshot = "play_notification_sounds_snapshot";
const QString NotificationSoundsTablet = "play_notification_sounds_tablet";
}
#endif // hifi_Menu_h

View file

@ -160,7 +160,7 @@ QUuid AvatarMotionState::getSimulatorID() const {
}
// virtual
void AvatarMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const {
void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const {
group = BULLET_COLLISION_GROUP_OTHER_AVATAR;
mask = Physics::getDefaultCollisionMask(group);
}

View file

@ -65,7 +65,7 @@ public:
void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; }
virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override;
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
virtual float getMass() const override;

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 = 75.0f; // degrees/sec
const float PITCH_SPEED_DEFAULT = 50.0f; // degrees/sec
const float YAW_SPEED_DEFAULT = 100.0f; // degrees/sec
const float PITCH_SPEED_DEFAULT = 75.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;
@ -2245,9 +2245,15 @@ void MyAvatar::updateActionMotor(float deltaTime) {
_actionMotorVelocity = getSensorToWorldScale() * (_walkSpeed.get() * _walkSpeedScalar) * direction;
}
float previousBoomLength = _boomLength;
float boomChange = getDriveKey(ZOOM);
_boomLength += 4.0f * _boomLength * boomChange + boomChange * boomChange;
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
// May need to change view if boom length has changed
if (previousBoomLength != _boomLength) {
qApp->changeViewAsNeeded(_boomLength);
}
}
void MyAvatar::updatePosition(float deltaTime) {
@ -2654,7 +2660,6 @@ void MyAvatar::updateMotionBehaviorFromMenu() {
} else {
_motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
}
setCollisionsEnabled(menu->isOptionChecked(MenuOption::EnableAvatarCollisions));
setProperty("lookAtSnappingEnabled", menu->isOptionChecked(MenuOption::EnableLookAtSnapping));
}

View file

@ -121,7 +121,7 @@ class MyAvatar : public Avatar {
* while flying.
* @property {number} hmdRollControlDeadZone=8 - The amount of HMD roll, in degrees, required before your avatar turns if
* <code>hmdRollControlEnabled</code> is enabled.
* @property hmdRollControlRate {number} If hmdRollControlEnabled is true, this value determines the maximum turn rate of
* @property {number} hmdRollControlRate If hmdRollControlEnabled is true, this value determines the maximum turn rate of
* your avatar when rolling your HMD in degrees per second.
* @property {number} userHeight=1.75 - The height of the user in sensor space.
* @property {number} userEyeHeight=1.65 - The estimated height of the user's eyes in sensor space. <em>Read-only.</em>
@ -1402,7 +1402,7 @@ private:
SharedSoundPointer _collisionSound;
MyCharacterController _characterController;
int16_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR };
int32_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR };
AvatarWeakPointer _lookAtTargetAvatar;
glm::vec3 _targetAvatarPosition;

View file

@ -22,21 +22,22 @@
* @hifi-interface
* @hifi-client-entity
*
* @property PICK_NOTHING {number} A filter flag. Don't intersect with anything. <em>Read-only.</em>
* @property PICK_ENTITIES {number} A filter flag. Include entities when intersecting. <em>Read-only.</em>
* @property PICK_OVERLAYS {number} A filter flag. Include overlays when intersecting. <em>Read-only.</em>
* @property PICK_AVATARS {number} A filter flag. Include avatars when intersecting. <em>Read-only.</em>
* @property PICK_HUD {number} A filter flag. Include the HUD sphere when intersecting in HMD mode. <em>Read-only.</em>
* @property PICK_COARSE {number} A filter flag. Pick against coarse meshes, instead of exact meshes. <em>Read-only.</em>
* @property PICK_INCLUDE_INVISIBLE {number} A filter flag. Include invisible objects when intersecting. <em>Read-only.</em>
* @property PICK_INCLUDE_NONCOLLIDABLE {number} A filter flag. Include non-collidable objects when intersecting.
* @property {number} PICK_NOTHING A filter flag. Don't intersect with anything. <em>Read-only.</em>
* @property {number} PICK_ENTITIES A filter flag. Include entities when intersecting. <em>Read-only.</em>
* @property {number} PICK_OVERLAYS A filter flag. Include overlays when intersecting. <em>Read-only.</em>
* @property {number} PICK_AVATARS A filter flag. Include avatars when intersecting. <em>Read-only.</em>
* @property {number} PICK_HUD A filter flag. Include the HUD sphere when intersecting in HMD mode. <em>Read-only.</em>
* @property {number} PICK_COARSE A filter flag. Pick against coarse meshes, instead of exact meshes. <em>Read-only.</em>
* @property {number} PICK_INCLUDE_INVISIBLE A filter flag. Include invisible objects when intersecting. <em>Read-only.</em>
* @property {number} PICK_INCLUDE_NONCOLLIDABLE A filter flag. Include non-collidable objects when intersecting.
* <em>Read-only.</em>
* @property PICK_ALL_INTERSECTIONS {number} <em>Read-only.</em>
* @property INTERSECTED_NONE {number} An intersection type. Intersected nothing with the given filter flags. <em>Read-only.</em>
* @property INTERSECTED_ENTITY {number} An intersection type. Intersected an entity. <em>Read-only.</em>
* @property INTERSECTED_OVERLAY {number} An intersection type. Intersected an overlay. <em>Read-only.</em>
* @property INTERSECTED_AVATAR {number} An intersection type. Intersected an avatar. <em>Read-only.</em>
* @property INTERSECTED_HUD {number} An intersection type. Intersected the HUD sphere. <em>Read-only.</em>
* @property {number} PICK_ALL_INTERSECTIONS <em>Read-only.</em>
* @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags.
* <em>Read-only.</em>
* @property {number} INTERSECTED_ENTITY An intersection type. Intersected an entity. <em>Read-only.</em>
* @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an overlay. <em>Read-only.</em>
* @property {number} INTERSECTED_AVATAR An intersection type. Intersected an avatar. <em>Read-only.</em>
* @property {number} INTERSECTED_HUD An intersection type. Intersected the HUD sphere. <em>Read-only.</em>
* @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results. <em>Read-only.</em>
*/
@ -99,7 +100,7 @@ public:
/**jsdoc
* An intersection result for a Ray Pick.
*
* @typedef {Object} RayPickResult
* @typedef {object} RayPickResult
* @property {number} type The intersection type.
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE)
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
@ -113,7 +114,7 @@ public:
/**jsdoc
* An intersection result for a Stylus Pick.
*
* @typedef {Object} StylusPickResult
* @typedef {object} StylusPickResult
* @property {number} type The intersection type.
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE)
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.

View file

@ -68,14 +68,14 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is not intersecting something. Same as a {@link Pointers.RayPointerRenderState},
* but with an additional distance field.
*
* @typedef {Object} Pointers.DefaultRayPointerRenderState
* @typedef {object} Pointers.DefaultRayPointerRenderState
* @augments Pointers.RayPointerRenderState
* @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined.
*/
/**jsdoc
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something.
*
* @typedef {Object} Pointers.RayPointerRenderState
* @typedef {object} Pointers.RayPointerRenderState
* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
* @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
* An overlay to represent the beginning of the Ray Pointer, if desired.
@ -87,7 +87,7 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
/**jsdoc
* A trigger mechanism for Ray Pointers.
*
* @typedef {Object} Pointers.Trigger
* @typedef {object} Pointers.Trigger
* @property {Controller.Standard|Controller.Actions|function} action This can be a built-in Controller action, like Controller.Standard.LTClick, or a function that evaluates to >= 1.0 when you want to trigger <code>button</code>.
* @property {string} button Which button to trigger. "Primary", "Secondary", "Tertiary", and "Focus" are currently supported. Only "Primary" will trigger clicks on web surfaces. If "Focus" is triggered,
* it will try to set the entity or overlay focus to the object at which the Pointer is aimed. Buttons besides the first three will still trigger events, but event.button will be "None".

View file

@ -522,7 +522,7 @@ int WindowScriptingInterface::openMessageBox(QString title, QString text, int bu
* <tr> <td><strong>RestoreDefaults</strong></td> <td><code>0x8000000</code></td> <td>"Restore Defaults"</td> </tr>
* </tbody>
* </table>
* @typedef Window.MessageBoxButton
* @typedef {number} Window.MessageBoxButton
*/
int WindowScriptingInterface::createMessageBox(QString title, QString text, int buttons, int defaultButton) {
auto messageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text,

View file

@ -470,7 +470,7 @@ public slots:
* </tr>
* </tbody>
* </table>
* @typedef Window.DisplayTexture
* @typedef {string} Window.DisplayTexture
*/
bool setDisplayTexture(const QString& name);
@ -523,16 +523,21 @@ public slots:
int openMessageBox(QString title, QString text, int buttons, int defaultButton);
/**jsdoc
* Open the given resource in the Interface window or in a web browser depending on the url scheme
* Open a URL in the Interface window or other application, depending on the URL's scheme. If the URL starts with
* <code>hifi://</code> then that URL is navigated to in Interface, otherwise the URL is opened in the application the OS
* associates with the URL's scheme (e.g., a Web browser for <code>http://</code>).
* @function Window.openUrl
* @param {string} url - The resource to open
* @param {string} url - The URL to open.
*/
void openUrl(const QUrl& url);
/**jsdoc
* (Android only) Open the requested Activity and optionally back to the scene when the activity is done
* Open an Android activity and optionally return back to the scene when the activity is completed. <em>Android only.</em>
* @function Window.openAndroidActivity
* @param {string} activityName - The name of the activity to open. One of "Home", "Login" or "Privacy Policy"
* @param {string} activityName - The name of the activity to open: one of <code>"Home"</code>, <code>"Login"</code>, or
* <code>"Privacy Policy"</code>.
* @param {boolean} backToScene - If <code>true</code>, the user is automatically returned back to the scene when the
* activity is completed.
*/
void openAndroidActivity(const QString& activityName, const bool backToScene);

View file

@ -86,7 +86,7 @@ HMDToolsDialog::HMDToolsDialog(QWidget* parent) :
if (dialogsManager->getLodToolsDialog()) {
watchWindow(dialogsManager->getLodToolsDialog()->windowHandle());
}
connect(_switchModeButton, &QPushButton::clicked, [this]{
toggleHMDMode();
});

View file

@ -130,4 +130,3 @@ void LodToolsDialog::closeEvent(QCloseEvent* event) {
#endif
}

View file

@ -52,4 +52,4 @@ private:
QLabel* _feedback;
};
#endif // hifi_LodToolsDialog_h
#endif // hifi_LodToolsDialog_h

View file

@ -14,6 +14,7 @@
#include <ScriptEngines.h>
#include <OffscreenUi.h>
#include <Preferences.h>
#include <RenderShadowTask.h>
#include <display-plugins/CompositorHelper.h>
#include "Application.h"
@ -52,33 +53,100 @@ void setupPreferences() {
preferences->addPreference(preference);
}
// Graphics quality
static const QString GRAPHICS_QUALITY { "Graphics Quality" };
{
auto getter = [=]()->bool { return myAvatar->getSnapTurn(); };
auto setter = [=](bool value) { myAvatar->setSnapTurn(value); };
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Snap turn when in HMD", getter, setter));
}
{
auto getter = [=]()->bool { return myAvatar->getClearOverlayWhenMoving(); };
auto setter = [=](bool value) { myAvatar->setClearOverlayWhenMoving(value); };
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Clear overlays when moving", getter, setter));
static const float MAX_DESKTOP_FPS = 60;
static const float MAX_HMD_FPS = 90;
static const float MIN_FPS = 10;
static const float LOW = 0.25f;
static const float MEDIUM = 0.5f;
static const float HIGH = 0.75f;
auto getter = []()->float {
auto lodManager = DependencyManager::get<LODManager>();
bool inHMD = qApp->isHMDMode();
float increaseFPS = 0;
if (inHMD) {
increaseFPS = lodManager->getHMDLODDecreaseFPS();
} else {
increaseFPS = lodManager->getDesktopLODDecreaseFPS();
}
float maxFPS = inHMD ? MAX_HMD_FPS : MAX_DESKTOP_FPS;
float percentage = increaseFPS / maxFPS;
if (percentage >= HIGH) {
return LOW;
} else if (percentage >= LOW) {
return MEDIUM;
}
return HIGH;
};
auto setter = [](float value) {
static const float THRASHING_DIFFERENCE = 10;
auto lodManager = DependencyManager::get<LODManager>();
bool isLowestValue = value == LOW;
bool isHMDMode = qApp->isHMDMode();
float maxFPS = isHMDMode ? MAX_HMD_FPS : MAX_DESKTOP_FPS;
float desiredFPS = maxFPS - THRASHING_DIFFERENCE;
if (!isLowestValue) {
float calculatedFPS = (maxFPS - (maxFPS * value)) - THRASHING_DIFFERENCE;
desiredFPS = calculatedFPS < MIN_FPS ? MIN_FPS : calculatedFPS;
}
if (isHMDMode) {
lodManager->setHMDLODDecreaseFPS(desiredFPS);
} else {
lodManager->setDesktopLODDecreaseFPS(desiredFPS);
}
};
auto wodSlider = new SliderPreference(GRAPHICS_QUALITY, "World Detail", getter, setter);
wodSlider->setMin(0.25f);
wodSlider->setMax(0.75f);
wodSlider->setStep(0.25f);
preferences->addPreference(wodSlider);
auto getterShadow = []()->bool {
auto menu = Menu::getInstance();
return menu->isOptionChecked(MenuOption::Shadows);
};
auto setterShadow = [](bool value) {
auto menu = Menu::getInstance();
menu->setIsOptionChecked(MenuOption::Shadows, value);
};
preferences->addPreference(new CheckPreference(GRAPHICS_QUALITY, "Show Shadows", getterShadow, setterShadow));
}
// UI
static const QString UI_CATEGORY { "UI" };
static const QString UI_CATEGORY { "User Interface" };
{
auto getter = []()->bool { return qApp->getSettingConstrainToolbarPosition(); };
auto setter = [](bool value) { qApp->setSettingConstrainToolbarPosition(value); };
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Constrain Toolbar Position to Horizontal Center", getter, setter));
}
{
auto getter = []()->float { return qApp->getHMDTabletScale(); };
auto setter = [](float value) { qApp->setHMDTabletScale(value); };
auto preference = new SpinnerPreference(UI_CATEGORY, "HMD Tablet Scale %", getter, setter);
auto getter = []()->float { return qApp->getDesktopTabletScale(); };
auto setter = [](float value) { qApp->setDesktopTabletScale(value); };
auto preference = new SpinnerPreference(UI_CATEGORY, "Desktop Tablet Scale %", getter, setter);
preference->setMin(20);
preference->setMax(500);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return qApp->getHMDTabletScale(); };
auto setter = [](float value) { qApp->setHMDTabletScale(value); };
auto preference = new SpinnerPreference(UI_CATEGORY, "VR Tablet Scale %", getter, setter);
preference->setMin(20);
preference->setMax(500);
preferences->addPreference(preference);
}
{
auto getter = []()->bool { return qApp->getPreferStylusOverLaser(); };
@ -86,19 +154,24 @@ void setupPreferences() {
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Stylus Over Laser", getter, setter));
}
static const QString ADVANCED_UI_CATEGORY { "Advanced UI" };
{
auto getter = []()->float { return qApp->getDesktopTabletScale(); };
auto setter = [](float value) { qApp->setDesktopTabletScale(value); };
auto preference = new SpinnerPreference(ADVANCED_UI_CATEGORY, "Desktop Tablet Scale %", getter, setter);
preference->setMin(20);
preference->setMax(500);
preferences->addPreference(preference);
static const QString RETICLE_ICON_NAME = { Cursor::Manager::getIconName(Cursor::Icon::RETICLE) };
auto getter = []()->bool { return qApp->getPreferredCursor() == RETICLE_ICON_NAME; };
auto setter = [](bool value) { qApp->setPreferredCursor(value ? RETICLE_ICON_NAME : QString()); };
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Use reticle cursor instead of arrow", getter, setter));
}
{
auto getter = [=]()->bool { return myAvatar->getClearOverlayWhenMoving(); };
auto setter = [=](bool value) { myAvatar->setClearOverlayWhenMoving(value); };
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Clear overlays when moving", getter, setter));
}
static const QString VIEW_CATEGORY{ "View" };
{
auto getter = [=]()->float { return myAvatar->getRealWorldFieldOfView(); };
auto setter = [=](float value) { myAvatar->setRealWorldFieldOfView(value); };
auto preference = new SpinnerPreference(ADVANCED_UI_CATEGORY, "Real world vertical field of view (angular size of monitor)", getter, setter);
auto preference = new SpinnerPreference(VIEW_CATEGORY, "Real world vertical field of view (angular size of monitor)", getter, setter);
preference->setMin(1);
preference->setMax(180);
preferences->addPreference(preference);
@ -106,7 +179,7 @@ void setupPreferences() {
{
auto getter = []()->float { return qApp->getFieldOfView(); };
auto setter = [](float value) { qApp->setFieldOfView(value); };
auto preference = new SpinnerPreference(ADVANCED_UI_CATEGORY, "Vertical field of view", getter, setter);
auto preference = new SpinnerPreference(VIEW_CATEGORY, "Vertical field of view", getter, setter);
preference->setMin(1);
preference->setMax(180);
preference->setStep(1);
@ -122,12 +195,6 @@ void setupPreferences() {
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter));
}
*/
{
static const QString RETICLE_ICON_NAME = { Cursor::Manager::getIconName(Cursor::Icon::RETICLE) };
auto getter = []()->bool { return qApp->getPreferredCursor() == RETICLE_ICON_NAME; };
auto setter = [](bool value) { qApp->setPreferredCursor(value ? RETICLE_ICON_NAME : QString()); };
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Use reticle cursor instead of arrow", getter, setter));
}
// Snapshots
static const QString SNAPSHOTS { "Snapshots" };
@ -156,27 +223,6 @@ void setupPreferences() {
"this information you are helping to improve the product. ", getter, setter));
}
static const QString LOD_TUNING("Level of Detail Tuning");
{
auto getter = []()->float { return DependencyManager::get<LODManager>()->getDesktopLODDecreaseFPS(); };
auto setter = [](float value) { DependencyManager::get<LODManager>()->setDesktopLODDecreaseFPS(value); };
auto preference = new SpinnerPreference(LOD_TUNING, "Minimum desktop FPS", getter, setter);
preference->setMin(0);
preference->setMax(120);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return DependencyManager::get<LODManager>()->getHMDLODDecreaseFPS(); };
auto setter = [](float value) { DependencyManager::get<LODManager>()->setHMDLODDecreaseFPS(value); };
auto preference = new SpinnerPreference(LOD_TUNING, "Minimum HMD FPS", getter, setter);
preference->setMin(0);
preference->setMax(120);
preference->setStep(1);
preferences->addPreference(preference);
}
static const QString AVATAR_TUNING { "Avatar Tuning" };
{
auto getter = [=]()->QString { return myAvatar->getDominantHand(); };
@ -196,26 +242,7 @@ void setupPreferences() {
// which can't be changed across domain switches. Having these values loaded up when you load the Dialog each time
// is a way around this, therefore they're not specified here but in the QML.
}
{
auto getter = [=]()->float { return myAvatar->getUserHeight(); };
auto setter = [=](float value) { myAvatar->setUserHeight(value); };
auto preference = new SpinnerPreference(AVATAR_TUNING, "User height (meters)", getter, setter);
preference->setMin(1.0f);
preference->setMax(2.2f);
preference->setDecimals(3);
preference->setStep(0.001f);
preferences->addPreference(preference);
}
{
auto getter = []()->float { return DependencyManager::get<DdeFaceTracker>()->getEyeClosingThreshold(); };
auto setter = [](float value) { DependencyManager::get<DdeFaceTracker>()->setEyeClosingThreshold(value); };
preferences->addPreference(new SliderPreference(AVATAR_TUNING, "Camera binary eyelid threshold", getter, setter));
}
{
auto getter = []()->float { return FaceTracker::getEyeDeflection(); };
auto setter = [](float value) { FaceTracker::setEyeDeflection(value); };
preferences->addPreference(new SliderPreference(AVATAR_TUNING, "Face tracker eye deflection", getter, setter));
}
{
auto getter = [=]()->QString { return myAvatar->getAnimGraphOverrideUrl().toString(); };
auto setter = [=](const QString& value) { myAvatar->setAnimGraphOverrideUrl(QUrl(value)); };
@ -224,11 +251,65 @@ void setupPreferences() {
preferences->addPreference(preference);
}
static const QString AVATAR_CAMERA { "Avatar Camera" };
{
auto getter = [=]()->bool { return myAvatar->getCollisionsEnabled(); };
auto setter = [=](bool value) { myAvatar->setCollisionsEnabled(value); };
auto preference = new CheckPreference(AVATAR_TUNING, "Enable Avatar collisions", getter, setter);
preferences->addPreference(preference);
}
static const QString FACE_TRACKING{ "Face Tracking" };
{
auto getter = []()->float { return DependencyManager::get<DdeFaceTracker>()->getEyeClosingThreshold(); };
auto setter = [](float value) { DependencyManager::get<DdeFaceTracker>()->setEyeClosingThreshold(value); };
preferences->addPreference(new SliderPreference(FACE_TRACKING, "Eye Closing Threshold", getter, setter));
}
{
auto getter = []()->float { return FaceTracker::getEyeDeflection(); };
auto setter = [](float value) { FaceTracker::setEyeDeflection(value); };
preferences->addPreference(new SliderPreference(FACE_TRACKING, "Eye Deflection", getter, setter));
}
static const QString MOVEMENT{ "VR Movement" };
{
static const QString movementsControlChannel = QStringLiteral("Hifi-Advanced-Movement-Disabler");
auto getter = [=]()->bool { return myAvatar->useAdvancedMovementControls(); };
auto setter = [=](bool value) { myAvatar->setUseAdvancedMovementControls(value); };
preferences->addPreference(new CheckPreference(MOVEMENT,
QStringLiteral("Advanced movement for hand controllers"),
getter, setter));
}
{
auto getter = [=]()->bool { return myAvatar->getFlyingEnabled(); };
auto setter = [=](bool value) { myAvatar->setFlyingEnabled(value); };
preferences->addPreference(new CheckPreference(MOVEMENT, "Flying & jumping", getter, setter));
}
{
auto getter = [=]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };
auto setter = [=](int value) { myAvatar->setSnapTurn(value == 0); };
auto preference = new RadioButtonsPreference(MOVEMENT, "Snap turn / Smooth turn", getter, setter);
QStringList items;
items << "Snap turn" << "Smooth turn";
preference->setItems(items);
preferences->addPreference(preference);
}
{
auto getter = [=]()->float { return myAvatar->getUserHeight(); };
auto setter = [=](float value) { myAvatar->setUserHeight(value); };
auto preference = new SpinnerPreference(MOVEMENT, "User real-world height (meters)", getter, setter);
preference->setMin(1.0f);
preference->setMax(2.2f);
preference->setDecimals(3);
preference->setStep(0.001f);
preferences->addPreference(preference);
}
static const QString AVATAR_CAMERA{ "Mouse Sensitivity" };
{
auto getter = [=]()->float { return myAvatar->getPitchSpeed(); };
auto setter = [=](float value) { myAvatar->setPitchSpeed(value); };
auto preference = new SpinnerPreference(AVATAR_CAMERA, "Camera pitch speed (degrees/second)", getter, setter);
auto preference = new SpinnerPreference(AVATAR_CAMERA, "Pitch speed (degrees/second)", getter, setter);
preference->setMin(1.0f);
preference->setMax(360.0f);
preferences->addPreference(preference);
@ -236,7 +317,7 @@ void setupPreferences() {
{
auto getter = [=]()->float { return myAvatar->getYawSpeed(); };
auto setter = [=](float value) { myAvatar->setYawSpeed(value); };
auto preference = new SpinnerPreference(AVATAR_CAMERA, "Camera yaw speed (degrees/second)", getter, setter);
auto preference = new SpinnerPreference(AVATAR_CAMERA, "Yaw speed (degrees/second)", getter, setter);
preference->setMin(1.0f);
preference->setMax(360.0f);
preferences->addPreference(preference);

View file

@ -46,6 +46,13 @@ bool Billboard3DOverlay::applyTransformTo(Transform& transform, bool force) {
return transformChanged;
}
void Billboard3DOverlay::update(float duration) {
if (isFacingAvatar()) {
_renderVariableDirty = true;
}
Parent::update(duration);
}
Transform Billboard3DOverlay::evalRenderTransform() {
Transform transform = getTransform();
bool transformChanged = applyTransformTo(transform, true);

View file

@ -18,6 +18,7 @@
class Billboard3DOverlay : public Planar3DOverlay, public PanelAttachable, public Billboardable {
Q_OBJECT
using Parent = Planar3DOverlay;
public:
Billboard3DOverlay() {}
@ -26,6 +27,8 @@ public:
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
void update(float duration) override;
protected:
virtual bool applyTransformTo(Transform& transform, bool force = false) override;

View file

@ -51,11 +51,6 @@ void Image3DOverlay::update(float deltatime) {
_texture = DependencyManager::get<TextureCache>()->getTexture(_url);
_textureIsLoaded = false;
}
if (usecTimestampNow() > _transformExpiry) {
Transform transform = getTransform();
applyTransformTo(transform);
setTransform(transform);
}
Parent::update(deltatime);
}

View file

@ -53,7 +53,7 @@ const OverlayID UNKNOWN_OVERLAY_ID = OverlayID();
* @property {number} distance - The distance from the {@link PickRay} origin to the intersection point.
* @property {Vec3} surfaceNormal - The normal of the overlay surface at the intersection point.
* @property {Vec3} intersection - The position of the intersection point.
* @property {Object} extraInfo Additional intersection details, if available.
* @property {object} extraInfo Additional intersection details, if available.
*/
class RayToOverlayIntersectionResult {
public:
@ -482,7 +482,7 @@ public slots:
/**jsdoc
* Check if there is an overlay of a given ID.
* @function Overlays.isAddedOverly
* @function Overlays.isAddedOverlay
* @param {Uuid} overlayID - The ID to check.
* @returns {boolean} <code>true</code> if an overlay with the given ID exists, <code>false</code> otherwise.
*/

View file

@ -83,15 +83,6 @@ xColor Text3DOverlay::getBackgroundColor() {
return result;
}
void Text3DOverlay::update(float deltatime) {
if (usecTimestampNow() > _transformExpiry) {
Transform transform = getTransform();
applyTransformTo(transform);
setTransform(transform);
}
Parent::update(deltatime);
}
void Text3DOverlay::render(RenderArgs* args) {
if (!_renderVisible || !getParentVisible()) {
return; // do nothing if we're not visible
@ -306,13 +297,4 @@ QSizeF Text3DOverlay::textSize(const QString& text) const {
float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight;
return QSizeF(extents.x, extents.y) * pointToWorldScale;
}
bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance,
BoxFace &face, glm::vec3& surfaceNormal) {
Transform transform = getTransform();
applyTransformTo(transform, true);
setTransform(transform);
return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face, surfaceNormal);
}
}

View file

@ -30,8 +30,6 @@ public:
~Text3DOverlay();
virtual void render(RenderArgs* args) override;
virtual void update(float deltatime) override;
virtual const render::ShapeKey getShapeKey() override;
// getters
@ -60,9 +58,6 @@ public:
QSizeF textSize(const QString& test) const; // Meters
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) override;
virtual Text3DOverlay* createClone() const override;
private:

View file

@ -56,6 +56,8 @@
#include "ui/Snapshot.h"
#include "SoundCache.h"
#include "raypick/PointerScriptingInterface.h"
#include <display-plugins/CompositorHelper.h>
#include "AboutUtil.h"
static int MAX_WINDOW_SIZE = 4096;
static const float METERS_TO_INCHES = 39.3701f;
@ -256,6 +258,8 @@ void Web3DOverlay::setupQmlSurface() {
_webSurface->getSurfaceContext()->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this);
_webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface());
_webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance());
// Override min fps for tablet UI, for silky smooth scrolling
setMaxFPS(90);

View file

@ -70,7 +70,7 @@ public:
* @function AnimationCache.prefetch
* @param {string} url - URL of the resource to prefetch.
* @param {object} [extra=null]
* @returns {Resource}
* @returns {ResourceObject}
*/
/**jsdoc
@ -79,7 +79,7 @@ public:
* @param {string} url - URL of the resource to load.
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
* @param {} [extra=null]
* @returns {Resource}
* @returns {object}
*/
@ -87,7 +87,7 @@ public:
* Returns animation resource for particular animation.
* @function AnimationCache.getAnimation
* @param {string} url - URL to load.
* @returns {Resource} animation
* @returns {AnimationObject} animation
*/
Q_INVOKABLE AnimationPointer getAnimation(const QString& url) { return getAnimation(QUrl(url)); }
Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url);
@ -104,6 +104,17 @@ private:
Q_DECLARE_METATYPE(AnimationPointer)
/**jsdoc
* @class AnimationObject
*
* @hifi-interface
* @hifi-client-entity
* @hifi-server-entity
* @hifi-assignment-client
*
* @property {string[]} jointNames
* @property {FBXAnimationFrame[]} frames
*/
/// An animation loaded from the network.
class Animation : public Resource {
Q_OBJECT
@ -118,9 +129,16 @@ public:
virtual bool isLoaded() const override;
/**jsdoc
* @function AnimationObject.getJointNames
* @returns {string[]}
*/
Q_INVOKABLE QStringList getJointNames() const;
/**jsdoc
* @function AnimationObject.getFrames
* @returns {FBXAnimationFrame[]}
*/
Q_INVOKABLE QVector<FBXAnimationFrame> getFrames() const;
const QVector<FBXAnimationFrame>& getFramesReference() const;

View file

@ -77,6 +77,17 @@ private:
typedef QSharedPointer<Sound> SharedSoundPointer;
/**jsdoc
* @class SoundObject
*
* @hifi-interface
* @hifi-client-entity
* @hifi-server-entity
* @hifi-assignment-client
*
* @property {boolean} downloaded
* @property {number} duration
*/
class SoundScriptingInterface : public QObject {
Q_OBJECT
@ -90,6 +101,10 @@ public:
bool isReady() const { return _sound->isReady(); }
float getDuration() { return _sound->getDuration(); }
/**jsdoc
* @function SoundObject.ready
* @returns {Signal}
*/
signals:
void ready();

View file

@ -64,7 +64,7 @@ public:
* @function SoundCache.prefetch
* @param {string} url - URL of the resource to prefetch.
* @param {object} [extra=null]
* @returns {Resource}
* @returns {ResourceObject}
*/
/**jsdoc
@ -73,14 +73,14 @@ public:
* @param {string} url - URL of the resource to load.
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
* @param {} [extra=null]
* @returns {Resource}
* @returns {object}
*/
/**jsdoc
* @function SoundCache.getSound
* @param {string} url
* @returns {object}
* @returns {SoundObject}
*/
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
protected:

View file

@ -292,7 +292,7 @@ public:
*/
/**jsdoc
* Information about a single joint in an Avatar's skeleton hierarchy.
* @typedef MyAvatar.SkeletonJoint
* @typedef {object} MyAvatar.SkeletonJoint
* @property {string} name - Joint name.
* @property {number} index - Joint index.
* @property {number} parentIndex - Index of this joint's parent (-1 if no parent).

View file

@ -2363,7 +2363,7 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const
}
/**jsdoc
* @typedef AttachmentData
* @typedef {object} AttachmentData
* @property {string} modelUrl
* @property {string} jointName
* @property {Vec3} translation

View file

@ -578,8 +578,7 @@ public:
* @param {Quat} rotation - The rotation of the joint relative to its parent.
* @param {Vec3} translation - The translation of the joint relative to its parent.
* @example <caption>Set your avatar to it's default T-pose for a while.<br />
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/t-pose.png" />
* </caption>
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/images/t-pose.png" /></caption>
* // Set all joint translations and rotations to defaults.
* var i, length, rotation, translation;
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
@ -680,8 +679,7 @@ public:
* @param {string} name - The name of the joint.
* @param {Quat} rotation - The rotation of the joint relative to its parent.
* @example <caption>Set your avatar to its default T-pose then rotate its right arm.<br />
* <img alt="Avatar in T-pose with arm rotated"
* src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/armpose.png" /></caption>
* <img alt="Avatar in T-pose with arm rotated" src="https://docs.highfidelity.com/images/armpose.png" /></caption>
* // Set all joint translations and rotations to defaults.
* var i, length, rotation, translation;
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
@ -713,8 +711,7 @@ public:
* @param {Vec3} translation - The translation of the joint relative to its parent.
* @example <caption>Stretch your avatar's neck. Depending on the avatar you are using, you will either see a gap between
* the head and body or you will see the neck stretched.<br />
* <img alt="Avatar with neck stretched"
* src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/stretched-neck.png" /></caption>
* <img alt="Avatar with neck stretched" src="https://docs.highfidelity.com/images/stretched-neck.png" /></caption>
* // Stretch your avatar's neck.
* MyAvatar.setJointTranslation("Neck", { x: 0, y: 25, z: 0 });
*
@ -798,8 +795,7 @@ public:
* @param {Quat[]} jointRotations - The rotations for all joints in the avatar. The values are in the same order as the
* array returned by {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}.
* @example <caption>Set your avatar to its default T-pose then rotate its right arm.<br />
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/armpose.png" />
* </caption>
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/images/armpose.png" /></caption>
* // Set all joint translations and rotations to defaults.
* var i, length, rotation, translation;
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {

View file

@ -307,7 +307,7 @@ namespace controller {
* action.</td></tr>
* </tbody>
* </table>
* @typedef Controller.Actions
* @typedef {object} Controller.Actions
*/
// Device functions
Input::NamedVector ActionsDevice::getAvailableInputs() const {

View file

@ -79,7 +79,7 @@ enum Hand {
* {@link Controller.Hardware-Vive}.</td></tr>
* </tbody>
* </table>
* @typedef Controller.Hardware
* @typedef {object} Controller.Hardware
* @example <caption>List all the currently available <code>Controller.Hardware</code> properties.</caption>
* function printProperties(string, item) {
* print(string);

View file

@ -231,7 +231,7 @@ void StandardController::focusOutEvent() {
*
* </tbody>
* </table>
* @typedef Controller.Standard
* @typedef {object} Controller.Standard
*/
Input::NamedVector StandardController::getAvailableInputs() const {
static Input::NamedVector availableInputs {

View file

@ -46,7 +46,7 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b
/**jsdoc
* The AnimationProperties are used to configure an animation.
* @typedef Entities.AnimationProperties
* @typedef {object} Entities.AnimationProperties
* @property {string} url="" - The URL of the FBX file that has the animation.
* @property {number} fps=30 - The speed in frames/s that the animation is played at.
* @property {number} firstFrame=0 - The first frame to play in the animation.

View file

@ -154,3 +154,11 @@ void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityI
queueOctreeEditMessage(PacketType::EntityErase, bufferOut);
}
}
void EntityEditPacketSender::queueCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID) {
QByteArray bufferOut(NLPacket::maxPayloadSize(PacketType::EntityClone), 0);
if (EntityItemProperties::encodeCloneEntityMessage(entityIDToClone, newEntityID, bufferOut)) {
queueOctreeEditMessage(PacketType::EntityClone, bufferOut);
}
}

View file

@ -38,6 +38,7 @@ public:
void queueEraseEntityMessage(const EntityItemID& entityItemID);
void queueCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID);
// My server type is the model server
virtual char getMyNodeType() const override { return NodeType::EntityServer; }

View file

@ -124,6 +124,13 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_LAST_EDITED_BY;
requestedProperties += PROP_CLONEABLE;
requestedProperties += PROP_CLONE_LIFETIME;
requestedProperties += PROP_CLONE_LIMIT;
requestedProperties += PROP_CLONE_DYNAMIC;
requestedProperties += PROP_CLONE_AVATAR_ENTITY;
requestedProperties += PROP_CLONE_ORIGIN_ID;
return requestedProperties;
}
@ -288,6 +295,13 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, getQueryAACube());
APPEND_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, getLastEditedBy());
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, getCloneable());
APPEND_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, getCloneLifetime());
APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, getCloneLimit());
APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, getCloneDynamic());
APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, getCloneAvatarEntity());
APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, getCloneOriginID());
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
requestedProperties,
propertyFlags,
@ -803,7 +817,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible);
READ_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow);
READ_ENTITY_PROPERTY(PROP_COLLISIONLESS, bool, setCollisionless);
READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint8_t, setCollisionMask);
READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint16_t, setCollisionMask);
READ_ENTITY_PROPERTY(PROP_DYNAMIC, bool, setDynamic);
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked);
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
@ -848,6 +862,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, QUuid, setLastEditedBy);
READ_ENTITY_PROPERTY(PROP_CLONEABLE, bool, setCloneable);
READ_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, float, setCloneLifetime);
READ_ENTITY_PROPERTY(PROP_CLONE_LIMIT, float, setCloneLimit);
READ_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, bool, setCloneDynamic);
READ_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity);
READ_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID);
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
propertyFlags, overwriteLocalData, somethingChanged);
@ -1275,6 +1296,13 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneable, getCloneable);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneLifetime, getCloneLifetime);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneLimit, getCloneLimit);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneDynamic, getCloneDynamic);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneAvatarEntity, getCloneAvatarEntity);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneOriginID, getCloneOriginID);
properties._defaultSettings = false;
return properties;
@ -1382,6 +1410,13 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneable, setCloneable);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneLifetime, setCloneLifetime);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneLimit, setCloneLimit);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneDynamic, setCloneDynamic);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneAvatarEntity, setCloneAvatarEntity);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneOriginID, setCloneOriginID);
if (updateQueryAACube()) {
somethingChanged = true;
}
@ -1814,7 +1849,7 @@ void EntityItem::setCollisionless(bool value) {
});
}
void EntityItem::setCollisionMask(uint8_t value) {
void EntityItem::setCollisionMask(uint16_t value) {
withWriteLock([&] {
if ((_collisionMask & ENTITY_COLLISION_MASK_DEFAULT) != (value & ENTITY_COLLISION_MASK_DEFAULT)) {
_collisionMask = (value & ENTITY_COLLISION_MASK_DEFAULT);
@ -1879,7 +1914,7 @@ void EntityItem::setCreated(quint64 value) {
});
}
void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const {
void EntityItem::computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask) const {
if (_collisionless) {
group = BULLET_COLLISION_GROUP_COLLISIONLESS;
mask = 0;
@ -1892,7 +1927,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
group = BULLET_COLLISION_GROUP_STATIC;
}
uint8_t userMask = getCollisionMask();
uint16_t userMask = getCollisionMask();
if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) !=
(bool)(userMask & USER_COLLISION_GROUP_OTHER_AVATAR)) {
@ -1906,7 +1941,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
if ((bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
userMask &= ~USER_COLLISION_GROUP_MY_AVATAR;
}
mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask);
mask = Physics::getDefaultCollisionMask(group) & (int32_t)(userMask);
}
}
@ -2760,8 +2795,8 @@ bool EntityItem::getCollisionless() const {
return result;
}
uint8_t EntityItem::getCollisionMask() const {
uint8_t result;
uint16_t EntityItem::getCollisionMask() const {
uint16_t result;
withReadLock([&] {
result = _collisionMask;
});
@ -2975,3 +3010,118 @@ std::unordered_map<std::string, graphics::MultiMaterial> EntityItem::getMaterial
}
return toReturn;
}
bool EntityItem::getCloneable() const {
bool result;
withReadLock([&] {
result = _cloneable;
});
return result;
}
void EntityItem::setCloneable(bool value) {
withWriteLock([&] {
_cloneable = value;
});
}
float EntityItem::getCloneLifetime() const {
float result;
withReadLock([&] {
result = _cloneLifetime;
});
return result;
}
void EntityItem::setCloneLifetime(float value) {
withWriteLock([&] {
_cloneLifetime = value;
});
}
float EntityItem::getCloneLimit() const {
float result;
withReadLock([&] {
result = _cloneLimit;
});
return result;
}
void EntityItem::setCloneLimit(float value) {
withWriteLock([&] {
_cloneLimit = value;
});
}
bool EntityItem::getCloneDynamic() const {
bool result;
withReadLock([&] {
result = _cloneDynamic;
});
return result;
}
void EntityItem::setCloneDynamic(bool value) {
withWriteLock([&] {
_cloneDynamic = value;
});
}
bool EntityItem::getCloneAvatarEntity() const {
bool result;
withReadLock([&] {
result = _cloneAvatarEntity;
});
return result;
}
void EntityItem::setCloneAvatarEntity(bool value) {
withWriteLock([&] {
_cloneAvatarEntity = value;
});
}
const QUuid EntityItem::getCloneOriginID() const {
QUuid result;
withReadLock([&] {
result = _cloneOriginID;
});
return result;
}
void EntityItem::setCloneOriginID(const QUuid& value) {
withWriteLock([&] {
_cloneOriginID = value;
});
}
void EntityItem::addCloneID(const QUuid& cloneID) {
withWriteLock([&] {
if (!_cloneIDs.contains(cloneID)) {
_cloneIDs.append(cloneID);
}
});
}
void EntityItem::removeCloneID(const QUuid& cloneID) {
withWriteLock([&] {
int index = _cloneIDs.indexOf(cloneID);
if (index >= 0) {
_cloneIDs.removeAt(index);
}
});
}
const QVector<QUuid> EntityItem::getCloneIDs() const {
QVector<QUuid> result;
withReadLock([&] {
result = _cloneIDs;
});
return result;
}
void EntityItem::setCloneIDs(const QVector<QUuid>& cloneIDs) {
withWriteLock([&] {
_cloneIDs = cloneIDs;
});
}

View file

@ -288,10 +288,10 @@ public:
bool getCollisionless() const;
void setCollisionless(bool value);
uint8_t getCollisionMask() const;
void setCollisionMask(uint8_t value);
uint16_t getCollisionMask() const;
void setCollisionMask(uint16_t value);
void computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const;
void computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask) const;
bool getDynamic() const;
void setDynamic(bool value);
@ -341,6 +341,19 @@ public:
quint32 getStaticCertificateVersion() const;
void setStaticCertificateVersion(const quint32&);
bool getCloneable() const;
void setCloneable(bool value);
float getCloneLifetime() const;
void setCloneLifetime(float value);
float getCloneLimit() const;
void setCloneLimit(float value);
bool getCloneDynamic() const;
void setCloneDynamic(bool value);
bool getCloneAvatarEntity() const;
void setCloneAvatarEntity(bool value);
const QUuid getCloneOriginID() const;
void setCloneOriginID(const QUuid& value);
// TODO: get rid of users of getRadius()...
float getRadius() const;
@ -494,6 +507,11 @@ public:
void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; }
uint64_t getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; }
void addCloneID(const QUuid& cloneID);
void removeCloneID(const QUuid& cloneID);
const QVector<QUuid> getCloneIDs() const;
void setCloneIDs(const QVector<QUuid>& cloneIDs);
signals:
void requestRenderUpdate();
@ -562,7 +580,7 @@ protected:
bool _visible { ENTITY_ITEM_DEFAULT_VISIBLE };
bool _canCastShadow{ ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW };
bool _collisionless { ENTITY_ITEM_DEFAULT_COLLISIONLESS };
uint8_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT };
uint16_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT };
bool _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC };
bool _locked { ENTITY_ITEM_DEFAULT_LOCKED };
QString _userData { ENTITY_ITEM_DEFAULT_USER_DATA };
@ -648,6 +666,14 @@ protected:
bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera
bool _cloneable { ENTITY_ITEM_DEFAULT_CLONEABLE };
float _cloneLifetime { ENTITY_ITEM_DEFAULT_CLONE_LIFETIME };
float _cloneLimit { ENTITY_ITEM_DEFAULT_CLONE_LIMIT };
bool _cloneDynamic { ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC };
bool _cloneAvatarEntity { ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY };
QUuid _cloneOriginID;
QVector<QUuid> _cloneIDs;
private:
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
std::mutex _materialsLock;

View file

@ -130,7 +130,7 @@ void buildStringToMaterialMappingModeLookup() {
addMaterialMappingMode(PROJECTED);
}
QString getCollisionGroupAsString(uint8_t group) {
QString getCollisionGroupAsString(uint16_t group) {
switch (group) {
case USER_COLLISION_GROUP_DYNAMIC:
return "dynamic";
@ -146,7 +146,7 @@ QString getCollisionGroupAsString(uint8_t group) {
return "";
}
uint8_t getCollisionGroupAsBitMask(const QStringRef& name) {
uint16_t getCollisionGroupAsBitMask(const QStringRef& name) {
if (0 == name.compare(QString("dynamic"))) {
return USER_COLLISION_GROUP_DYNAMIC;
} else if (0 == name.compare(QString("static"))) {
@ -164,7 +164,7 @@ uint8_t getCollisionGroupAsBitMask(const QStringRef& name) {
QString EntityItemProperties::getCollisionMaskAsString() const {
QString maskString("");
for (int i = 0; i < NUM_USER_COLLISION_GROUPS; ++i) {
uint8_t group = 0x01 << i;
uint16_t group = 0x0001 << i;
if (group & _collisionMask) {
maskString.append(getCollisionGroupAsString(group));
maskString.append(',');
@ -175,7 +175,7 @@ QString EntityItemProperties::getCollisionMaskAsString() const {
void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) {
QVector<QStringRef> groups = maskString.splitRef(',');
uint8_t mask = 0x00;
uint16_t mask = 0x0000;
for (auto groupName : groups) {
mask |= getCollisionGroupAsBitMask(groupName);
}
@ -436,6 +436,13 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_DPI, dpi);
CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints);
CHECK_PROPERTY_CHANGE(PROP_CLONEABLE, cloneable);
CHECK_PROPERTY_CHANGE(PROP_CLONE_LIFETIME, cloneLifetime);
CHECK_PROPERTY_CHANGE(PROP_CLONE_LIMIT, cloneLimit);
CHECK_PROPERTY_CHANGE(PROP_CLONE_DYNAMIC, cloneDynamic);
CHECK_PROPERTY_CHANGE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity);
CHECK_PROPERTY_CHANGE(PROP_CLONE_ORIGIN_ID, cloneOriginID);
changedProperties += _animation.getChangedProperties();
changedProperties += _keyLight.getChangedProperties();
changedProperties += _ambientLight.getChangedProperties();
@ -479,7 +486,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* @property {boolean} locked=false - Whether or not the entity can be edited or deleted. If <code>true</code> then the
* entity's properties other than <code>locked</code> cannot be changed, and the entity cannot be deleted.
* @property {boolean} visible=true - Whether or not the entity is rendered. If <code>true</code> then the entity is rendered.
* @property {boolean} canCastShadows=true - Whether or not the entity casts shadows. Currently applicable only to
* @property {boolean} canCastShadow=true - Whether or not the entity can cast a shadow. Currently applicable only to
* {@link Entities.EntityType|Model} and {@link Entities.EntityType|Shape} entities. Shadows are cast if inside a
* {@link Entities.EntityType|Zone} entity with <code>castShadows</code> enabled in its
* {@link Entities.EntityProperties-Zone|keyLight} property.
@ -1391,7 +1398,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
/**jsdoc
* The axis-aligned bounding box of an entity.
* @typedef Entities.BoundingBox
* @typedef {object} Entities.BoundingBox
* @property {Vec3} brn - The bottom right near (minimum axes values) corner of the AA box.
* @property {Vec3} tfl - The top far left (maximum axes values) corner of the AA box.
* @property {Vec3} center - The center of the AA box.
@ -1430,6 +1437,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); // Gettable but not settable except at entity creation
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); // Gettable but not settable
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE, cloneable);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_LIFETIME, cloneLifetime);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_LIMIT, cloneLimit);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_DYNAMIC, cloneDynamic);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_ORIGIN_ID, cloneOriginID);
// Rendering info
if (!skipDefaults && !strictSemantics) {
QScriptValue renderInfo = engine->newObject();
@ -1506,7 +1520,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(localRenderAlpha, float, setLocalRenderAlpha);
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionless, bool, setCollisionless);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ignoreForCollisions, bool, setCollisionless, getCollisionless); // legacy support
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionMask, uint8_t, setCollisionMask);
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionMask, uint16_t, setCollisionMask);
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(collidesWith, CollisionMask);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(collisionsWillMove, bool, setDynamic, getDynamic); // legacy support
COPY_PROPERTY_FROM_QSCRIPTVALUE(dynamic, bool, setDynamic);
@ -1642,6 +1656,13 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI);
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneable, bool, setCloneable);
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneLifetime, float, setCloneLifetime);
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneLimit, float, setCloneLimit);
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneDynamic, bool, setCloneDynamic);
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneAvatarEntity, bool, setCloneAvatarEntity);
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneOriginID, QUuid, setCloneOriginID);
_lastEdited = usecTimestampNow();
}
@ -1793,6 +1814,13 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(dpi);
COPY_PROPERTY_IF_CHANGED(cloneable);
COPY_PROPERTY_IF_CHANGED(cloneLifetime);
COPY_PROPERTY_IF_CHANGED(cloneLimit);
COPY_PROPERTY_IF_CHANGED(cloneDynamic);
COPY_PROPERTY_IF_CHANGED(cloneAvatarEntity);
COPY_PROPERTY_IF_CHANGED(cloneOriginID);
_lastEdited = usecTimestampNow();
}
@ -2017,6 +2045,13 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t);
ADD_PROPERTY_TO_MAP(PROP_CLONEABLE, Cloneable, cloneable, bool);
ADD_PROPERTY_TO_MAP(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float);
ADD_PROPERTY_TO_MAP(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float);
ADD_PROPERTY_TO_MAP(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool);
ADD_PROPERTY_TO_MAP(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool);
ADD_PROPERTY_TO_MAP(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid);
// FIXME - these are not yet handled
//ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64);
@ -2331,6 +2366,12 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, properties.getEntityInstanceNumber());
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID());
APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, properties.getStaticCertificateVersion());
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, properties.getCloneable());
APPEND_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, properties.getCloneLifetime());
APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, properties.getCloneLimit());
APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, properties.getCloneDynamic());
APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, properties.getCloneAvatarEntity());
}
if (propertyCount > 0) {
@ -2535,7 +2576,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONLESS, bool, setCollisionless);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_MASK, uint8_t, setCollisionMask);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_MASK, uint16_t, setCollisionMask);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DYNAMIC, bool, setDynamic);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData);
@ -2701,6 +2742,12 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CERTIFICATE_ID, QString, setCertificateID);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE, bool, setCloneable);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_LIFETIME, float, setCloneLifetime);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_LIMIT, float, setCloneLimit);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_DYNAMIC, bool, setCloneDynamic);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity);
return valid;
}
@ -2780,6 +2827,52 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
return true;
}
bool EntityItemProperties::encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer) {
char* copyAt = buffer.data();
int outputLength = 0;
if (buffer.size() < (int)(NUM_BYTES_RFC4122_UUID * 2)) {
qCDebug(entities) << "ERROR - encodeCloneEntityMessage() called with buffer that is too small!";
return false;
}
memcpy(copyAt, entityIDToClone.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
copyAt += NUM_BYTES_RFC4122_UUID;
outputLength += NUM_BYTES_RFC4122_UUID;
memcpy(copyAt, newEntityID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
copyAt += NUM_BYTES_RFC4122_UUID;
outputLength += NUM_BYTES_RFC4122_UUID;
buffer.resize(outputLength);
return true;
}
bool EntityItemProperties::decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID) {
const unsigned char* packetData = (const unsigned char*)buffer.constData();
const unsigned char* dataAt = packetData;
size_t packetLength = buffer.size();
processedBytes = 0;
if (NUM_BYTES_RFC4122_UUID * 2 > packetLength) {
qCDebug(entities) << "EntityItemProperties::processEraseMessageDetails().... bailing because not enough bytes in buffer";
return false; // bail to prevent buffer overflow
}
QByteArray encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID);
entityIDToClone = QUuid::fromRfc4122(encodedID);
dataAt += encodedID.size();
processedBytes += encodedID.size();
encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID);
newEntityID = QUuid::fromRfc4122(encodedID);
dataAt += encodedID.size();
processedBytes += encodedID.size();
return true;
}
void EntityItemProperties::markAllChanged() {
_lastEditedByChanged = true;
_simulationOwnerChanged = true;
@ -2941,6 +3034,13 @@ void EntityItemProperties::markAllChanged() {
_dpiChanged = true;
_relayParentJointsChanged = true;
_cloneableChanged = true;
_cloneLifetimeChanged = true;
_cloneLimitChanged = true;
_cloneDynamicChanged = true;
_cloneAvatarEntityChanged = true;
_cloneOriginIDChanged = true;
}
// The minimum bounding box for the entity.
@ -3373,6 +3473,25 @@ QList<QString> EntityItemProperties::listChangedProperties() {
out += "isUVModeStretch";
}
if (cloneableChanged()) {
out += "cloneable";
}
if (cloneLifetimeChanged()) {
out += "cloneLifetime";
}
if (cloneLimitChanged()) {
out += "cloneLimit";
}
if (cloneDynamicChanged()) {
out += "cloneDynamic";
}
if (cloneAvatarEntityChanged()) {
out += "cloneAvatarEntity";
}
if (cloneOriginIDChanged()) {
out += "cloneOriginID";
}
getAnimation().listChangedProperties(out);
getKeyLight().listChangedProperties(out);
getAmbientLight().listChangedProperties(out);
@ -3536,3 +3655,18 @@ bool EntityItemProperties::verifyStaticCertificateProperties() {
// I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash.
return verifySignature(EntityItem::_marketplacePublicKey, getStaticCertificateHash(), QByteArray::fromBase64(getCertificateID().toUtf8()));
}
void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityIDToClone) {
setName(getName() + "-clone-" + entityIDToClone.toString());
setLocked(false);
setLifetime(getCloneLifetime());
setDynamic(getCloneDynamic());
setClientOnly(getCloneAvatarEntity());
setCreated(usecTimestampNow());
setLastEdited(usecTimestampNow());
setCloneable(ENTITY_ITEM_DEFAULT_CLONEABLE);
setCloneLifetime(ENTITY_ITEM_DEFAULT_CLONE_LIFETIME);
setCloneLimit(ENTITY_ITEM_DEFAULT_CLONE_LIMIT);
setCloneDynamic(ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC);
setCloneAvatarEntity(ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY);
}

View file

@ -148,7 +148,7 @@ public:
DEFINE_PROPERTY_REF(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3, ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY);
DEFINE_PROPERTY(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING);
DEFINE_PROPERTY(PROP_COLLISIONLESS, Collisionless, collisionless, bool, ENTITY_ITEM_DEFAULT_COLLISIONLESS);
DEFINE_PROPERTY(PROP_COLLISION_MASK, CollisionMask, collisionMask, uint8_t, ENTITY_COLLISION_MASK_DEFAULT);
DEFINE_PROPERTY(PROP_COLLISION_MASK, CollisionMask, collisionMask, uint16_t, ENTITY_COLLISION_MASK_DEFAULT);
DEFINE_PROPERTY(PROP_DYNAMIC, Dynamic, dynamic, bool, ENTITY_ITEM_DEFAULT_DYNAMIC);
DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool, LightEntityItem::DEFAULT_IS_SPOTLIGHT);
DEFINE_PROPERTY(PROP_INTENSITY, Intensity, intensity, float, LightEntityItem::DEFAULT_INTENSITY);
@ -272,6 +272,13 @@ public:
DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS);
DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS);
DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE);
DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME);
DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT);
DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC);
DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY);
DEFINE_PROPERTY_REF(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID);
static QString getComponentModeString(uint32_t mode);
static QString getComponentModeAsString(uint32_t mode);
@ -294,6 +301,8 @@ public:
QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties);
static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer);
static bool encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer);
static bool decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID);
static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes,
EntityItemID& entityID, EntityItemProperties& properties);
@ -369,6 +378,8 @@ public:
bool verifyStaticCertificateProperties();
static bool verifySignature(const QString& key, const QByteArray& text, const QByteArray& signature);
void convertToCloneProperties(const EntityItemID& entityIDToClone);
protected:
QString getCollisionMaskAsString() const;
void setCollisionMaskFromString(const QString& maskString);
@ -515,6 +526,13 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientLightMode, ambientLightMode, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, SkyboxMode, skyboxMode, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cloneable, cloneable, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLifetime, cloneLifetime, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLimit, cloneLimit, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneDynamic, cloneDynamic, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneAvatarEntity, cloneAvatarEntity, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneOriginID, cloneOriginID, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, "");

View file

@ -97,4 +97,11 @@ const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid();
const bool ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS = false;
const bool ENTITY_ITEM_DEFAULT_CLONEABLE = false;
const float ENTITY_ITEM_DEFAULT_CLONE_LIFETIME = 300.0f;
const int ENTITY_ITEM_DEFAULT_CLONE_LIMIT = 0;
const bool ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC = false;
const bool ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY = false;
const QUuid ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID = QUuid();
#endif // hifi_EntityItemPropertiesDefaults_h

View file

@ -204,6 +204,13 @@ enum EntityPropertyList {
PROP_CERTIFICATE_ID,
PROP_STATIC_CERTIFICATE_VERSION,
PROP_CLONEABLE,
PROP_CLONE_LIFETIME,
PROP_CLONE_LIMIT,
PROP_CLONE_DYNAMIC,
PROP_CLONE_AVATAR_ENTITY,
PROP_CLONE_ORIGIN_ID,
PROP_HAZE_MODE,
PROP_KEYLIGHT_COLOR,

View file

@ -258,33 +258,9 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent);
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
EntityItemID id = EntityItemID(QUuid::createUuid());
EntityItemID id;
// If we have a local entity tree set, then also update it.
bool success = true;
if (_entityTree) {
_entityTree->withWriteLock([&] {
EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);
if (entity) {
if (propertiesWithSimID.queryAACubeRelatedPropertyChanged()) {
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
bool success;
AACube queryAACube = entity->getQueryAACube(success);
if (success) {
propertiesWithSimID.setQueryAACube(queryAACube);
}
}
entity->setLastBroadcast(usecTimestampNow());
// since we're creating this object we will immediately volunteer to own its simulation
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
propertiesWithSimID.setLastEdited(entity->getLastEdited());
} else {
qCDebug(entities) << "script failed to add new Entity to local Octree";
success = false;
}
});
}
bool success = addLocalEntityCopy(propertiesWithSimID, id);
// queue the packet
if (success) {
@ -295,6 +271,37 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
}
}
bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properties, EntityItemID& id, bool isClone) {
bool success = true;
id = EntityItemID(QUuid::createUuid());
if (_entityTree) {
_entityTree->withWriteLock([&] {
EntityItemPointer entity = _entityTree->addEntity(id, properties, isClone);
if (entity) {
if (properties.queryAACubeRelatedPropertyChanged()) {
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
bool success;
AACube queryAACube = entity->getQueryAACube(success);
if (success) {
properties.setQueryAACube(queryAACube);
}
}
entity->setLastBroadcast(usecTimestampNow());
// since we're creating this object we will immediately volunteer to own its simulation
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
properties.setLastEdited(entity->getLastEdited());
} else {
qCDebug(entities) << "script failed to add new Entity to local Octree";
success = false;
}
});
}
return success;
}
QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& textures,
const QString& shapeType, bool dynamic, bool collisionless,
const glm::vec3& position, const glm::vec3& gravity) {
@ -320,6 +327,28 @@ QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QStrin
return addEntity(properties);
}
QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) {
EntityItemID newEntityID;
EntityItemProperties properties = getEntityProperties(entityIDToClone);
bool cloneAvatarEntity = properties.getCloneAvatarEntity();
properties.convertToCloneProperties(entityIDToClone);
if (cloneAvatarEntity) {
return addEntity(properties, true);
} else {
// setLastEdited timestamp to 0 to ensure this entity gets updated with the properties
// from the server-created entity, don't change this unless you know what you are doing
properties.setLastEdited(0);
bool success = addLocalEntityCopy(properties, newEntityID, true);
if (success) {
getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID);
return newEntityID;
} else {
return QUuid();
}
}
}
EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) {
EntityPropertyFlags noSpecificProperties;
return getEntityProperties(identity, noSpecificProperties);

View file

@ -224,6 +224,16 @@ public slots:
Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic,
bool collisionless, const glm::vec3& position, const glm::vec3& gravity);
/**jsdoc
* Request a clone of an entity. Only entities that have been marked as 'cloneable' will be able to be cloned using this method.
* A cloned entity has most of the properties of the orignal entity, and can be requested from clients that do not have rez permissions.
* The client requests a clone from the entity server, which returns back the entityID of a valid clone if the operation was allowed.
* @function Entities.cloneEntity
* @param {Uuid} entityIDToClone - the ID of the entity to clone
* @returns {Entities.EntityID} The ID of the newly created clone
*/
Q_INVOKABLE QUuid cloneEntity(QUuid entityIDToClone);
/**jsdoc
* Get the properties of an entity.
* @function Entities.getEntityProperties
@ -1875,6 +1885,7 @@ private:
bool polyVoxWorker(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
bool addLocalEntityCopy(EntityItemProperties& propertiesWithSimID, EntityItemID& id, bool isClone = false);
EntityItemPointer checkForTreeEntityAndTypeMatch(const QUuid& entityID,
EntityTypes::EntityType entityType = EntityTypes::Unknown);

View file

@ -65,6 +65,7 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
removeEntityInternal(entity);
if (entity->getElement()) {
_deadEntities.insert(entity);
_entityTree->cleanupCloneIDs(entity->getEntityItemID());
}
}
}

View file

@ -228,6 +228,7 @@ bool EntityTree::handlesEditPacketType(PacketType packetType) const {
// we handle these types of "edit" packets
switch (packetType) {
case PacketType::EntityAdd:
case PacketType::EntityClone:
case PacketType::EntityEdit:
case PacketType::EntityErase:
case PacketType::EntityPhysics:
@ -492,7 +493,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
return true;
}
EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone) {
EntityItemPointer result = NULL;
EntityItemProperties props = properties;
@ -504,7 +505,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
if (!properties.getClientOnly() && getIsClient() &&
!nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() &&
!nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain) {
!nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain && !isClone) {
return nullptr;
}
@ -592,6 +593,7 @@ void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ign
return;
}
cleanupCloneIDs(entityID);
unhookChildAvatar(entityID);
emit deletingEntity(entityID);
emit deletingEntityPointer(existingEntity.get());
@ -625,6 +627,28 @@ void EntityTree::unhookChildAvatar(const EntityItemID entityID) {
});
}
void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) {
EntityItemPointer entity = findEntityByEntityItemID(entityID);
if (entity) {
// remove clone ID from it's clone origin's clone ID list if clone origin exists
const QUuid& cloneOriginID = entity->getCloneOriginID();
if (!cloneOriginID.isNull()) {
EntityItemPointer cloneOrigin = findEntityByID(cloneOriginID);
if (cloneOrigin) {
cloneOrigin->removeCloneID(entityID);
}
}
// clear the clone origin ID on any clones that this entity had
const QVector<QUuid>& cloneIDs = entity->getCloneIDs();
foreach(const QUuid& cloneChildID, cloneIDs) {
EntityItemPointer cloneChild = findEntityByEntityItemID(cloneChildID);
if (cloneChild) {
cloneChild->setCloneOriginID(QUuid());
}
}
}
}
void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool ignoreWarnings) {
// NOTE: callers must lock the tree before using this method
DeleteEntityOperator theOperator(getThisPointer());
@ -653,6 +677,7 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool i
}
// tell our delete operator about this entityID
cleanupCloneIDs(entityID);
unhookChildAvatar(entityID);
theOperator.addEntityIDToDeleteList(entityID);
emit deletingEntity(entityID);
@ -1392,6 +1417,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
int processedBytes = 0;
bool isAdd = false;
bool isClone = false;
// we handle these types of "edit" packets
switch (message.getType()) {
case PacketType::EntityErase: {
@ -1400,6 +1426,9 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
break;
}
case PacketType::EntityClone:
isClone = true; // fall through to next case
// FALLTHRU
case PacketType::EntityAdd:
isAdd = true; // fall through to next case
// FALLTHRU
@ -1422,8 +1451,22 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
EntityItemProperties properties;
startDecode = usecTimestampNow();
bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes,
entityItemID, properties);
bool validEditPacket = false;
EntityItemID entityIDToClone;
EntityItemPointer entityToClone;
if (isClone) {
QByteArray buffer = QByteArray::fromRawData(reinterpret_cast<const char*>(editData), maxLength);
validEditPacket = EntityItemProperties::decodeCloneEntityMessage(buffer, processedBytes, entityIDToClone, entityItemID);
if (validEditPacket) {
entityToClone = findEntityByEntityItemID(entityIDToClone);
if (entityToClone) {
properties = entityToClone->getProperties();
}
}
} else {
validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes, entityItemID, properties);
}
endDecode = usecTimestampNow();
EntityItemPointer existingEntity;
@ -1491,24 +1534,26 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
}
if ((isAdd || properties.lifetimeChanged()) &&
((!senderNode->getCanRez() && senderNode->getCanRezTmp()) ||
(!senderNode->getCanRezCertified() && senderNode->getCanRezTmpCertified()))) {
// this node is only allowed to rez temporary entities. if need be, cap the lifetime.
if (properties.getLifetime() == ENTITY_ITEM_IMMORTAL_LIFETIME ||
properties.getLifetime() > _maxTmpEntityLifetime) {
properties.setLifetime(_maxTmpEntityLifetime);
if (!isClone) {
if ((isAdd || properties.lifetimeChanged()) &&
((!senderNode->getCanRez() && senderNode->getCanRezTmp()) ||
(!senderNode->getCanRezCertified() && senderNode->getCanRezTmpCertified()))) {
// this node is only allowed to rez temporary entities. if need be, cap the lifetime.
if (properties.getLifetime() == ENTITY_ITEM_IMMORTAL_LIFETIME ||
properties.getLifetime() > _maxTmpEntityLifetime) {
properties.setLifetime(_maxTmpEntityLifetime);
bumpTimestamp(properties);
}
}
if (isAdd && properties.getLocked() && !senderNode->isAllowedEditor()) {
// if a node can't change locks, don't allow it to create an already-locked entity -- automatically
// clear the locked property and allow the unlocked entity to be created.
properties.setLocked(false);
bumpTimestamp(properties);
}
}
if (isAdd && properties.getLocked() && !senderNode->isAllowedEditor()) {
// if a node can't change locks, don't allow it to create an already-locked entity -- automatically
// clear the locked property and allow the unlocked entity to be created.
properties.setLocked(false);
bumpTimestamp(properties);
}
// If we got a valid edit packet, then it could be a new entity or it could be an update to
// an existing entity... handle appropriately
if (validEditPacket) {
@ -1566,17 +1611,32 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
} else if (isAdd) {
bool failedAdd = !allowed;
bool isCertified = !properties.getCertificateID().isEmpty();
bool isCloneable = properties.getCloneable();
int cloneLimit = properties.getCloneLimit();
if (!allowed) {
qCDebug(entities) << "Filtered entity add. ID:" << entityItemID;
} else if (!isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
} else if (!isClone && !isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
failedAdd = true;
qCDebug(entities) << "User without 'uncertified rez rights' [" << senderNode->getUUID()
<< "] attempted to add an uncertified entity with ID:" << entityItemID;
} else if (isCertified && !senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) {
} else if (!isClone && isCertified && !senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) {
failedAdd = true;
qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID()
<< "] attempted to add a certified entity with ID:" << entityItemID;
} else if (isClone && isCertified) {
failedAdd = true;
qCDebug(entities) << "User attempted to clone certified entity from entity ID:" << entityIDToClone;
} else if (isClone && !isCloneable) {
failedAdd = true;
qCDebug(entities) << "User attempted to clone non-cloneable entity from entity ID:" << entityIDToClone;
} else if (isClone && entityToClone && entityToClone->getCloneIDs().size() >= cloneLimit && cloneLimit != 0) {
failedAdd = true;
qCDebug(entities) << "User attempted to clone entity ID:" << entityIDToClone << " which reached it's cloneable limit.";
} else {
if (isClone) {
properties.convertToCloneProperties(entityIDToClone);
}
// this is a new entity... assign a new entityID
properties.setCreated(properties.getLastEdited());
properties.setLastEditedBy(senderNode->getUUID());
@ -1597,10 +1657,15 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
}
}
if (newEntity && isClone) {
entityToClone->addCloneID(newEntity->getEntityItemID());
newEntity->setCloneOriginID(entityIDToClone);
}
if (newEntity) {
newEntity->markAsChangedOnServer();
notifyNewlyCreatedEntity(*newEntity, senderNode);
startLogging = usecTimestampNow();
if (wantEditLogging()) {
qCDebug(entities) << "User [" << senderNode->getUUID() << "] added entity. ID:"
@ -1927,6 +1992,7 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo
if (shouldEraseEntity(entityID, sourceNode)) {
entityItemIDsToDelete << entityItemID;
cleanupCloneIDs(entityItemID);
}
}
deleteEntities(entityItemIDsToDelete, true, true);
@ -1976,6 +2042,7 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons
if (shouldEraseEntity(entityID, sourceNode)) {
entityItemIDsToDelete << entityItemID;
cleanupCloneIDs(entityItemID);
}
}
@ -2322,6 +2389,8 @@ bool EntityTree::readFromMap(QVariantMap& map) {
return false;
}
QMap<QUuid, QVector<QUuid>> cloneIDs;
bool success = true;
foreach (QVariant entityVariant, entitiesQList) {
// QVariantMap --> QScriptValue --> EntityItemProperties --> Entity
@ -2409,11 +2478,43 @@ bool EntityTree::readFromMap(QVariantMap& map) {
}
}
// Convert old cloneable entities so they use cloneableData instead of userData
if (contentVersion < (int)EntityVersion::CloneableData) {
QJsonObject userData = QJsonDocument::fromJson(properties.getUserData().toUtf8()).object();
QJsonObject grabbableKey = userData["grabbableKey"].toObject();
QJsonValue cloneable = grabbableKey["cloneable"];
if (cloneable.isBool() && cloneable.toBool()) {
QJsonValue cloneLifetime = grabbableKey["cloneLifetime"];
QJsonValue cloneLimit = grabbableKey["cloneLimit"];
QJsonValue cloneDynamic = grabbableKey["cloneDynamic"];
QJsonValue cloneAvatarEntity = grabbableKey["cloneAvatarEntity"];
// This is cloneable, we need to convert the properties
properties.setCloneable(true);
properties.setCloneLifetime(cloneLifetime.toInt());
properties.setCloneLimit(cloneLimit.toInt());
properties.setCloneDynamic(cloneDynamic.toBool());
properties.setCloneAvatarEntity(cloneAvatarEntity.toBool());
}
}
EntityItemPointer entity = addEntity(entityItemID, properties);
if (!entity) {
qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType();
success = false;
}
const QUuid& cloneOriginID = entity->getCloneOriginID();
if (!cloneOriginID.isNull()) {
cloneIDs[cloneOriginID].push_back(entity->getEntityItemID());
}
}
for (const auto& entityID : cloneIDs.keys()) {
auto entity = findEntityByID(entityID);
if (entity) {
entity->setCloneIDs(cloneIDs.value(entityID));
}
}
return success;

View file

@ -110,13 +110,14 @@ public:
// The newer API...
void postAddEntity(EntityItemPointer entityItem);
EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone = false);
// use this method if you only know the entityID
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
// check if the avatar is a child of this entity, If so set the avatar parentID to null
void unhookChildAvatar(const EntityItemID entityID);
void cleanupCloneIDs(const EntityItemID& entityID);
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true);
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = true);

View file

@ -35,7 +35,7 @@ class ReadBitstreamToTreeParams;
* @property {Vec3} direction=0,-1,0 - The direction the light is shining.
* @property {boolean} castShadows=false - If <code>true</code> then shadows are cast. Shadows are cast by avatars, plus
* {@link Entities.EntityType|Model} and {@link Entities.EntityType|Shape} entities that have their
* <code>{@link Entities.EntityProperties|canCastShadows}</code> property set to <code>true</code>.
* <code>{@link Entities.EntityProperties|canCastShadow}</code> property set to <code>true</code>.
*/
class KeyLightPropertyGroup : public PropertyGroup {
public:

View file

@ -258,6 +258,11 @@ public:
QHash<QString, size_t> texcoordSetMap;
};
/**jsdoc
* @typedef {object} FBXAnimationFrame
* @property {Quat[]} rotations
* @property {Vec3[]} translations
*/
/// A single animation frame extracted from an FBX document.
class FBXAnimationFrame {
public:

View file

@ -1174,7 +1174,7 @@ bool GLTFReader::addArrayOfType(const QByteArray& bin, int byteOffset, int byteL
break;
}
case GLTFAccessorComponentType::UNSIGNED_INT: {
readArray<quint8>(bin, byteOffset, byteLength, outarray, accessorType);
readArray<quint32>(bin, byteOffset, byteLength, outarray, accessorType);
break;
}
case GLTFAccessorComponentType::UNSIGNED_SHORT: {

View file

@ -190,7 +190,7 @@ namespace GLTFBufferViewTarget {
struct GLTFBufferView {
int buffer; //required
int byteLength; //required
int byteOffset;
int byteOffset { 0 };
int target;
QMap<QString, bool> defined;
void dump() {
@ -470,7 +470,7 @@ namespace GLTFAccessorComponentType {
}
struct GLTFAccessor {
int bufferView;
int byteOffset;
int byteOffset { 0 };
int componentType; //required
int count; //required
int type; //required

View file

@ -31,6 +31,10 @@ bool GL41Backend::supportedTextureFormat(const gpu::Element& format) {
case gpu::Semantic::COMPRESSED_EAC_RED_SIGNED:
case gpu::Semantic::COMPRESSED_EAC_XY:
case gpu::Semantic::COMPRESSED_EAC_XY_SIGNED:
// The ARB_texture_compression_bptc extension is not supported on 4.1
// See https://www.g-truc.net/doc/OpenGL%204%20Hardware%20Matrix.pdf
case gpu::Semantic::COMPRESSED_BC6_RGB:
case gpu::Semantic::COMPRESSED_BC7_SRGBA:
return false;
default:
return true;

View file

@ -33,6 +33,8 @@ using namespace gpu::gl45;
bool GL45Backend::supportedTextureFormat(const gpu::Element& format) {
switch (format.getSemantic()) {
// ETC textures are actually required by the OpenGL spec as of 4.3, but aren't always supported by hardware
// They'll be recompressed by OpenGL, which will be slow or have poor quality, so disable them for now
case gpu::Semantic::COMPRESSED_ETC2_RGB:
case gpu::Semantic::COMPRESSED_ETC2_SRGB:
case gpu::Semantic::COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA:

View file

@ -279,7 +279,7 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic
* moved down. The data value is how far the average position of all touch points moved.</td></tr>
* </tbody>
* </table>
* @typedef Controller.Hardware-Keyboard
* @typedef {object} Controller.Hardware-Keyboard
* @todo <em>Currently, the mouse wheel in an ordinary mouse generates left/right wheel events instead of up/down.</em>
*/
controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInputs() const {

View file

@ -179,7 +179,7 @@ public:
* @function ModelCache.prefetch
* @param {string} url - URL of the resource to prefetch.
* @param {object} [extra=null]
* @returns {Resource}
* @returns {ResourceObject}
*/
/**jsdoc
@ -188,7 +188,7 @@ public:
* @param {string} url - URL of the resource to load.
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
* @param {} [extra=null]
* @returns {Resource}
* @returns {object}
*/

View file

@ -195,7 +195,7 @@ public:
* @function TextureCache.prefetch
* @param {string} url - URL of the resource to prefetch.
* @param {object} [extra=null]
* @returns {Resource}
* @returns {ResourceObject}
*/
/**jsdoc
@ -204,7 +204,7 @@ public:
* @param {string} url - URL of the resource to load.
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
* @param {} [extra=null]
* @returns {Resource}
* @returns {object}
*/
@ -261,7 +261,7 @@ protected:
* @param {string} url
* @param {number} type
* @param {number} [maxNumPixels=67108864]
* @returns {Resource}
* @returns {ResourceObject}
*/
// Overload ResourceCache::prefetch to allow specifying texture type for loads
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);

View file

@ -138,7 +138,7 @@ public:
* </tr>
* </tbody>
* </table>
* @typedef location.LookupTrigger
* @typedef {number} location.LookupTrigger
*/
enum LookupTrigger {
UserInput,
@ -184,7 +184,7 @@ public slots:
/**jsdoc
* Go to a specified metaverse address.
* @function location.handleLookupString
* @param {string} address - The address to go to: a <code>"hifi:/"<code> address, an IP address (e.g.,
* @param {string} address - The address to go to: a <code>"hifi://"<code> address, an IP address (e.g.,
* <code>"127.0.0.1"</code> or <code>"localhost"</code>), a domain name, a named path on a domain (starts with
* <code>"/"</code>), a position or position and orientation, or a user (starts with <code>"@"</code>).
* @param {boolean} fromSuggestions=false - Set to <code>true</code> if the address is obtained from the "Goto" dialog.

View file

@ -137,7 +137,7 @@ public:
* </tr>
* </tbody>
* </table>
* @typedef Window.ConnectionRefusedReason
* @typedef {number} Window.ConnectionRefusedReason
*/
enum class ConnectionRefusedReason : uint8_t {
Unknown,

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