Merge branch 'master' of github.com:highfidelity/hifi into fixCameraMode

This commit is contained in:
NissimHadar 2018-05-24 17:40:21 -07:00
commit 9ed8c04097
113 changed files with 1513 additions and 554 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/). 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 ### 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 ### 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). 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. 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 ### Step 9. Testing Interface
@ -66,7 +68,7 @@ Create another environment variable (see Step #4)
* Set "Variable name": `_NO_DEBUG_HEAP` * Set "Variable name": `_NO_DEBUG_HEAP`
* Set "Variable value": `1` * 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. 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 baseFolder = new File(HIFI_ANDROID_PRECOMPILED)
def appDir = new File(projectDir, 'app') def appDir = new File(projectDir, 'app')
def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a') 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 qtChecksum='04599670ccca84bd2b15f6915568eb2d'
def qtVersionId='PeoqzN31n.YvLfs9JE2SgHgZ4.IaKAlt' def qtVersionId='PeoqzN31n.YvLfs9JE2SgHgZ4.IaKAlt'
if (Os.isFamily(Os.FAMILY_MAC)) { 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' qtChecksum='4b02de9d67d6bfb202355a808d2d9c59'
qtVersionId='HygCmtMLPYioyil0DfXckGVzhw2SXZA9' qtVersionId='HygCmtMLPYioyil0DfXckGVzhw2SXZA9'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) { } 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' qtChecksum='c3e25db64002d0f43cf565e0ef708911'
qtVersionId='HeVObSVLCBoc7yY7He1oBMvPIH0VkClT' qtVersionId='HeVObSVLCBoc7yY7He1oBMvPIH0VkClT'
} }
@ -88,58 +88,58 @@ def packages = [
checksum: qtChecksum, checksum: qtChecksum,
], ],
bullet: [ bullet: [
file: 'bullet-2.83_armv8-libcpp.tgz', file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/bullet-2.88_armv8-libcpp.tgz',
versionId: 'ljb7v.1IjVRqyopUKVDbVnLA4z88J8Eo', versionId: 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg',
checksum: '2c558d604fce337f5eba3eb7ec1252fd', checksum: '81642779ccb110f8c7338e8739ac38a0',
], ],
draco: [ draco: [
file: 'draco_armv8-libcpp.tgz', file: 'https://hifi-public.s3.amazonaws.com/austin/android/draco_armv8-libcpp.tgz',
versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m', versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m',
checksum: '617a80d213a5ec69fbfa21a1f2f738cd', checksum: '617a80d213a5ec69fbfa21a1f2f738cd',
], ],
glad: [ glad: [
file: 'glad_armv8-libcpp.zip', file: 'https://hifi-public.s3.amazonaws.com/austin/android/glad_armv8-libcpp.zip',
versionId: 'Q9szthzeye8fFyAA.cY26Lgn2B8kezEE', versionId: 'Q9szthzeye8fFyAA.cY26Lgn2B8kezEE',
checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449', checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449',
], ],
glm: [ glm: [
file: 'glm-0.9.8.tgz', file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/glm-0.9.8.5-patched.tgz',
versionId: 'BlkJNwaYV2Gfy5XwMeU7K0uzPDRKFMt2', versionId: 'cskfMoJrFlAeqI3WPxemyO_Cxt7rT9EJ',
checksum: 'd2b42cee31d2bc17bab6ce69e6b3f30a', checksum: '067b5fe16b220b5b1a1039ba51b062ae',
], ],
gvr: [ gvr: [
file: 'gvrsdk_v1.101.0.tgz', file: 'https://hifi-public.s3.amazonaws.com/austin/android/gvrsdk_v1.101.0.tgz',
versionId: 'UTberAIFraEfF9IVjoV66u1DTPTopgeY', versionId: 'UTberAIFraEfF9IVjoV66u1DTPTopgeY',
checksum: '57fd02baa069176ba18597a29b6b4fc7', checksum: '57fd02baa069176ba18597a29b6b4fc7',
], ],
nvtt: [ nvtt: [
file: 'nvtt_armv8-libcpp.zip', file: 'https://hifi-public.s3.amazonaws.com/austin/android/nvtt_armv8-libcpp.zip',
versionId: 'vLqrqThvpq4gp75BHMAqO6HhfTXaa0An', versionId: 'vLqrqThvpq4gp75BHMAqO6HhfTXaa0An',
checksum: 'eb46d0b683e66987190ed124aabf8910', checksum: 'eb46d0b683e66987190ed124aabf8910',
sharedLibFolder: 'lib', sharedLibFolder: 'lib',
includeLibs: ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'], includeLibs: ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'],
], ],
openssl: [ 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', versionId: 'DmahmSGFS4ltpHyTdyQvv35WOeUOiib9',
checksum: 'cabb681fbccd79594f65fcc266e02f32', checksum: 'cabb681fbccd79594f65fcc266e02f32',
], ],
polyvox: [ polyvox: [
file: 'polyvox_armv8-libcpp.tgz', file: 'https://hifi-public.s3.amazonaws.com/austin/android/polyvox_armv8-libcpp.tgz',
versionId: 'LDJtzMTvdm4SAc2KYg8Cg6uwWk4Vq3e3', versionId: 'LDJtzMTvdm4SAc2KYg8Cg6uwWk4Vq3e3',
checksum: '349ad5b72aaf2749ca95d847e60c5314', checksum: '349ad5b72aaf2749ca95d847e60c5314',
sharedLibFolder: 'lib', sharedLibFolder: 'lib',
includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'], includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'],
], ],
tbb: [ 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', versionId: 'YZliDD8.Menh1IVXKEuLPeO3xAjJ1UdF',
checksum: '20768f298f53b195e71b414b0ae240c4', checksum: '20768f298f53b195e71b414b0ae240c4',
sharedLibFolder: 'lib/release', sharedLibFolder: 'lib/release',
includeLibs: ['libtbb.so', 'libtbbmalloc.so'], includeLibs: ['libtbb.so', 'libtbbmalloc.so'],
], ],
hifiAC: [ hifiAC: [
file: 'libplugins_libhifiCodec.zip', file: 'https://hifi-public.s3.amazonaws.com/austin/android/libplugins_libhifiCodec.zip',
versionId: 'mzKhsRCgVmloqq5bvE.0IwYK1NjGQc_G', versionId: 'mzKhsRCgVmloqq5bvE.0IwYK1NjGQc_G',
checksum: '9412a8e12c88a4096c1fc843bb9fe52d', checksum: '9412a8e12c88a4096c1fc843bb9fe52d',
sharedLibFolder: '', sharedLibFolder: '',
@ -150,15 +150,15 @@ def packages = [
def scribeLocalFile='scribe' + EXEC_SUFFIX 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 scribeChecksum='ca4b904f52f4f993c29175ba96798fa6'
def scribeVersion='wgpf4dB2Ltzg4Lb2jJ4nPFsHoDkmK_OO' def scribeVersion='wgpf4dB2Ltzg4Lb2jJ4nPFsHoDkmK_OO'
if (Os.isFamily(Os.FAMILY_MAC)) { 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' scribeChecksum='72db9d32d4e1e50add755570ac5eb749'
scribeVersion='o_NbPrktzEYtBkQf3Tn7zc1nZWzM52w6' scribeVersion='o_NbPrktzEYtBkQf3Tn7zc1nZWzM52w6'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) { } 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' scribeChecksum='678e43d290c90fda670c6fefe038a06d'
scribeVersion='GCCJxlmd2irvNOFWfZR0U1UCLHndHQrC' 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(); auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, packetReceiver.registerListenerForTypes({ PacketType::EntityAdd,
PacketType::EntityClone,
PacketType::EntityEdit, PacketType::EntityEdit,
PacketType::EntityErase, PacketType::EntityErase,
PacketType::EntityPhysics, PacketType::EntityPhysics,

View file

@ -17,8 +17,8 @@ include(ExternalProject)
if (WIN32) if (WIN32)
ExternalProject_Add( ExternalProject_Add(
${EXTERNAL_NAME} ${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz
URL_MD5 03051bf112dcc78ddd296f9cab38fd68 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 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_DOWNLOAD 1
LOG_CONFIGURE 1 LOG_CONFIGURE 1
@ -28,8 +28,8 @@ if (WIN32)
else () else ()
ExternalProject_Add( ExternalProject_Add(
${EXTERNAL_NAME} ${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz
URL_MD5 03051bf112dcc78ddd296f9cab38fd68 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 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_DOWNLOAD 1
LOG_CONFIGURE 1 LOG_CONFIGURE 1

View file

@ -3,8 +3,8 @@ set(EXTERNAL_NAME glm)
include(ExternalProject) include(ExternalProject)
ExternalProject_Add( ExternalProject_Add(
${EXTERNAL_NAME} ${EXTERNAL_NAME}
URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.zip URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.5-patched.zip
URL_MD5 579ac77a3110befa3244d68c0ceb7281 URL_MD5 7d39ecc1cea275427534c3cfd6dd63f0
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTERNAL_ARGS} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTERNAL_ARGS}
LOG_DOWNLOAD 1 LOG_DOWNLOAD 1
@ -18,4 +18,4 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) 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( ExternalProject_Add(
${EXTERNAL_NAME} ${EXTERNAL_NAME}
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC67-v4.zip URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC68.zip
URL_MD5 ba32aed18bfeaac4ccaf5ebb8ea3e804 URL_MD5 a068f74d4045e257cfa7926fe6e38ad5
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_COMMAND "" BUILD_COMMAND ""
INSTALL_COMMAND "" INSTALL_COMMAND ""

View file

@ -479,7 +479,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
limitedNodeList->killNodeWithUUID(existingNodeID); limitedNodeList->killNodeWithUUID(existingNodeID);
} }
// add the connecting node (or re-use the matched one from eachNodeBreakable above) // add the connecting node
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection); SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
// set the edit rights for this user // set the edit rights for this user
@ -508,26 +508,22 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
return newNode; return newNode;
} }
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection, SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) {
QUuid nodeID) {
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr; HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID); SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
if (connectedPeer) { if (connectedPeer && connectedPeer->getActiveSocket()) {
// this user negotiated a connection with us via ICE, so re-use their ICE client ID // set their discovered socket to whatever the activated socket on the network peer object was
nodeID = nodeConnection.connectUUID; discoveredSocket = *connectedPeer->getActiveSocket();
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();
}
} }
// 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>(); auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
Node::LocalID newLocalID = findOrCreateLocalID(nodeID); Node::LocalID newLocalID = findOrCreateLocalID(nodeID);
@ -541,6 +537,15 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node
return newNode; 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, bool DomainGatekeeper::verifyUserSignature(const QString& username,
const QByteArray& usernameSignature, const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr) { const HifiSockAddr& senderSockAddr) {

View file

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

View file

@ -1017,15 +1017,22 @@ void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> mess
sendingNode->setPublicSocket(nodeRequestData.publicSockAddr); sendingNode->setPublicSocket(nodeRequestData.publicSockAddr);
sendingNode->setLocalSocket(nodeRequestData.localSockAddr); sendingNode->setLocalSocket(nodeRequestData.localSockAddr);
// update the NodeInterestSet in case there have been any changes
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(sendingNode->getLinkedData()); 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 // guard against patched agents asking to hear about other agents
auto safeInterestSet = nodeRequestData.interestList.toSet(); auto safeInterestSet = nodeRequestData.interestList.toSet();
if (sendingNode->getType() == NodeType::Agent) { if (sendingNode->getType() == NodeType::Agent) {
safeInterestSet.remove(NodeType::Agent); safeInterestSet.remove(NodeType::Agent);
} }
// update the NodeInterestSet in case there have been any changes
nodeData->setNodeInterestSet(safeInterestSet); nodeData->setNodeInterestSet(safeInterestSet);
// update the connecting hostname in case it has changed // update the connecting hostname in case it has changed
@ -2945,7 +2952,7 @@ void DomainServer::nodeAdded(SharedNodePointer node) {
void DomainServer::nodeKilled(SharedNodePointer node) { void DomainServer::nodeKilled(SharedNodePointer node) {
// if this peer connected via ICE then remove them from our ICE peers hash // 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()); 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) { SharedAssignmentPointer DomainServer::dequeueMatchingAssignment(const QUuid& assignmentUUID, NodeType_t nodeType) {
@ -3163,18 +3172,23 @@ void DomainServer::handleKillNode(SharedNodePointer nodeToKill) {
const QUuid& nodeUUID = nodeToKill->getUUID(); const QUuid& nodeUUID = nodeToKill->getUUID();
limitedNodeList->killNodeWithUUID(nodeUUID); 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->reset();
removedNodePacket->write(nodeUUID.toRfc4122()); removedNodePacket->write(disconnectedNode->getUUID().toRfc4122());
// broadcast out the DomainServerRemovedNode message // 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 // 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](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(); unsigned int countConnectedUsers();
void handleKillNode(SharedNodePointer nodeToKill); void handleKillNode(SharedNodePointer nodeToKill);
void broadcastNodeDisconnect(const SharedNodePointer& disconnnectedNode);
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr); void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr);

View file

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

View file

@ -717,7 +717,7 @@ private:
* <tr><td><code>NavigationFocused</code></td><td>number</td><td>number</td><td><em>Not used.</em></td></tr> * <tr><td><code>NavigationFocused</code></td><td>number</td><td>number</td><td><em>Not used.</em></td></tr>
* </tbody> * </tbody>
* </table> * </table>
* @typedef Controller.Hardware-Application * @typedef {object} Controller.Hardware-Application
*/ */
static const QString STATE_IN_HMD = "InHMD"; static const QString STATE_IN_HMD = "InHMD";

View file

@ -651,7 +651,7 @@ private:
quint64 _lastFaceTrackerUpdate; quint64 _lastFaceTrackerUpdate;
render::ScenePointer _main3DScene{ new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) }; 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 gpu::ContextPointer _gpuContext; // initialized during window creation
mutable QMutex _renderArgsMutex{ QMutex::Recursive }; mutable QMutex _renderArgsMutex{ QMutex::Recursive };

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. // FIXME: JSDoc 3.5.5 doesn't augment @property definitions. The following definition is repeated in Camera.h.
/**jsdoc /**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. * entity mode.
*/ */
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity) Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)

View file

@ -160,7 +160,7 @@ QUuid AvatarMotionState::getSimulatorID() const {
} }
// virtual // 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; group = BULLET_COLLISION_GROUP_OTHER_AVATAR;
mask = Physics::getDefaultCollisionMask(group); mask = Physics::getDefaultCollisionMask(group);
} }

View file

@ -65,7 +65,7 @@ public:
void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; } 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; virtual float getMass() const override;

View file

@ -121,7 +121,7 @@ class MyAvatar : public Avatar {
* while flying. * while flying.
* @property {number} hmdRollControlDeadZone=8 - The amount of HMD roll, in degrees, required before your avatar turns if * @property {number} hmdRollControlDeadZone=8 - The amount of HMD roll, in degrees, required before your avatar turns if
* <code>hmdRollControlEnabled</code> is enabled. * <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. * 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} 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> * @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; SharedSoundPointer _collisionSound;
MyCharacterController _characterController; MyCharacterController _characterController;
int16_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR }; int32_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR };
AvatarWeakPointer _lookAtTargetAvatar; AvatarWeakPointer _lookAtTargetAvatar;
glm::vec3 _targetAvatarPosition; glm::vec3 _targetAvatarPosition;

View file

@ -22,21 +22,22 @@
* @hifi-interface * @hifi-interface
* @hifi-client-entity * @hifi-client-entity
* *
* @property PICK_NOTHING {number} A filter flag. Don't intersect with anything. <em>Read-only.</em> * @property {number} PICK_NOTHING 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 {number} PICK_ENTITIES 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 {number} PICK_OVERLAYS 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 {number} PICK_AVATARS 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 {number} PICK_HUD 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 {number} PICK_COARSE 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 {number} PICK_INCLUDE_INVISIBLE 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_INCLUDE_NONCOLLIDABLE A filter flag. Include non-collidable objects when intersecting.
* <em>Read-only.</em> * <em>Read-only.</em>
* @property PICK_ALL_INTERSECTIONS {number} <em>Read-only.</em> * @property {number} PICK_ALL_INTERSECTIONS <em>Read-only.</em>
* @property INTERSECTED_NONE {number} An intersection type. Intersected nothing with the given filter flags. <em>Read-only.</em> * @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags.
* @property INTERSECTED_ENTITY {number} An intersection type. Intersected an entity. <em>Read-only.</em> * <em>Read-only.</em>
* @property INTERSECTED_OVERLAY {number} An intersection type. Intersected an overlay. <em>Read-only.</em> * @property {number} INTERSECTED_ENTITY An intersection type. Intersected an entity. <em>Read-only.</em>
* @property INTERSECTED_AVATAR {number} An intersection type. Intersected an avatar. <em>Read-only.</em> * @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an overlay. <em>Read-only.</em>
* @property INTERSECTED_HUD {number} An intersection type. Intersected the HUD sphere. <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> * @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 /**jsdoc
* An intersection result for a Ray Pick. * An intersection result for a Ray Pick.
* *
* @typedef {Object} RayPickResult * @typedef {object} RayPickResult
* @property {number} type The intersection type. * @property {number} type The intersection type.
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE) * @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. * @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
@ -113,7 +114,7 @@ public:
/**jsdoc /**jsdoc
* An intersection result for a Stylus Pick. * An intersection result for a Stylus Pick.
* *
* @typedef {Object} StylusPickResult * @typedef {object} StylusPickResult
* @property {number} type The intersection type. * @property {number} type The intersection type.
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE) * @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. * @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}, * 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. * but with an additional distance field.
* *
* @typedef {Object} Pointers.DefaultRayPointerRenderState * @typedef {object} Pointers.DefaultRayPointerRenderState
* @augments Pointers.RayPointerRenderState * @augments Pointers.RayPointerRenderState
* @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined. * @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined.
*/ */
/**jsdoc /**jsdoc
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something. * 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 {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). * @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. * An overlay to represent the beginning of the Ray Pointer, if desired.
@ -87,7 +87,7 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
/**jsdoc /**jsdoc
* A trigger mechanism for Ray Pointers. * 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 {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, * @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". * 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> * <tr> <td><strong>RestoreDefaults</strong></td> <td><code>0x8000000</code></td> <td>"Restore Defaults"</td> </tr>
* </tbody> * </tbody>
* </table> * </table>
* @typedef Window.MessageBoxButton * @typedef {number} Window.MessageBoxButton
*/ */
int WindowScriptingInterface::createMessageBox(QString title, QString text, int buttons, int defaultButton) { int WindowScriptingInterface::createMessageBox(QString title, QString text, int buttons, int defaultButton) {
auto messageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text, auto messageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text,

View file

@ -470,7 +470,7 @@ public slots:
* </tr> * </tr>
* </tbody> * </tbody>
* </table> * </table>
* @typedef Window.DisplayTexture * @typedef {string} Window.DisplayTexture
*/ */
bool setDisplayTexture(const QString& name); bool setDisplayTexture(const QString& name);
@ -523,16 +523,21 @@ public slots:
int openMessageBox(QString title, QString text, int buttons, int defaultButton); int openMessageBox(QString title, QString text, int buttons, int defaultButton);
/**jsdoc /**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 * @function Window.openUrl
* @param {string} url - The resource to open * @param {string} url - The URL to open.
*/ */
void openUrl(const QUrl& url); void openUrl(const QUrl& url);
/**jsdoc /**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 * @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); void openAndroidActivity(const QString& activityName, const bool backToScene);

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 {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} surfaceNormal - The normal of the overlay surface at the intersection point.
* @property {Vec3} intersection - The position of 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 { class RayToOverlayIntersectionResult {
public: public:
@ -482,7 +482,7 @@ public slots:
/**jsdoc /**jsdoc
* Check if there is an overlay of a given ID. * Check if there is an overlay of a given ID.
* @function Overlays.isAddedOverly * @function Overlays.isAddedOverlay
* @param {Uuid} overlayID - The ID to check. * @param {Uuid} overlayID - The ID to check.
* @returns {boolean} <code>true</code> if an overlay with the given ID exists, <code>false</code> otherwise. * @returns {boolean} <code>true</code> if an overlay with the given ID exists, <code>false</code> otherwise.
*/ */

View file

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

View file

@ -77,6 +77,17 @@ private:
typedef QSharedPointer<Sound> SharedSoundPointer; 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 { class SoundScriptingInterface : public QObject {
Q_OBJECT Q_OBJECT
@ -90,6 +101,10 @@ public:
bool isReady() const { return _sound->isReady(); } bool isReady() const { return _sound->isReady(); }
float getDuration() { return _sound->getDuration(); } float getDuration() { return _sound->getDuration(); }
/**jsdoc
* @function SoundObject.ready
* @returns {Signal}
*/
signals: signals:
void ready(); void ready();

View file

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

View file

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

View file

@ -578,8 +578,7 @@ public:
* @param {Quat} rotation - The rotation of the joint relative to its parent. * @param {Quat} rotation - The rotation of the joint relative to its parent.
* @param {Vec3} translation - The translation 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 /> * @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" /> * <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/images/t-pose.png" /></caption>
* </caption>
* // Set all joint translations and rotations to defaults. * // Set all joint translations and rotations to defaults.
* var i, length, rotation, translation; * var i, length, rotation, translation;
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
@ -680,8 +679,7 @@ public:
* @param {string} name - The name of the joint. * @param {string} name - The name of the joint.
* @param {Quat} rotation - The rotation of the joint relative to its parent. * @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 /> * @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" * <img alt="Avatar in T-pose with arm rotated" src="https://docs.highfidelity.com/images/armpose.png" /></caption>
* src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/armpose.png" /></caption>
* // Set all joint translations and rotations to defaults. * // Set all joint translations and rotations to defaults.
* var i, length, rotation, translation; * var i, length, rotation, translation;
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { * 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. * @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 * @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 /> * the head and body or you will see the neck stretched.<br />
* <img alt="Avatar with neck stretched" * <img alt="Avatar with neck stretched" src="https://docs.highfidelity.com/images/stretched-neck.png" /></caption>
* src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/stretched-neck.png" /></caption>
* // Stretch your avatar's neck. * // Stretch your avatar's neck.
* MyAvatar.setJointTranslation("Neck", { x: 0, y: 25, z: 0 }); * 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 * @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}. * 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 /> * @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" /> * <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/images/armpose.png" /></caption>
* </caption>
* // Set all joint translations and rotations to defaults. * // Set all joint translations and rotations to defaults.
* var i, length, rotation, translation; * var i, length, rotation, translation;
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {

View file

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

View file

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

View file

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

View file

@ -46,7 +46,7 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b
/**jsdoc /**jsdoc
* The AnimationProperties are used to configure an animation. * 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 {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} 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. * @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); 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 queueEraseEntityMessage(const EntityItemID& entityItemID);
void queueCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID);
// My server type is the model server // My server type is the model server
virtual char getMyNodeType() const override { return NodeType::EntityServer; } 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_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; return requestedProperties;
} }
@ -288,6 +295,13 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, getQueryAACube()); APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, getQueryAACube());
APPEND_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, getLastEditedBy()); 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, appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
requestedProperties, requestedProperties,
propertyFlags, 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_VISIBLE, bool, setVisible);
READ_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow); READ_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow);
READ_ENTITY_PROPERTY(PROP_COLLISIONLESS, bool, setCollisionless); 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_DYNAMIC, bool, setDynamic);
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked);
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData); 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_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, bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
propertyFlags, overwriteLocalData, somethingChanged); propertyFlags, overwriteLocalData, somethingChanged);
@ -1275,6 +1296,13 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy); 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; properties._defaultSettings = false;
return properties; return properties;
@ -1382,6 +1410,13 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy); 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()) { if (updateQueryAACube()) {
somethingChanged = true; somethingChanged = true;
} }
@ -1814,7 +1849,7 @@ void EntityItem::setCollisionless(bool value) {
}); });
} }
void EntityItem::setCollisionMask(uint8_t value) { void EntityItem::setCollisionMask(uint16_t value) {
withWriteLock([&] { withWriteLock([&] {
if ((_collisionMask & ENTITY_COLLISION_MASK_DEFAULT) != (value & ENTITY_COLLISION_MASK_DEFAULT)) { if ((_collisionMask & ENTITY_COLLISION_MASK_DEFAULT) != (value & ENTITY_COLLISION_MASK_DEFAULT)) {
_collisionMask = (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) { if (_collisionless) {
group = BULLET_COLLISION_GROUP_COLLISIONLESS; group = BULLET_COLLISION_GROUP_COLLISIONLESS;
mask = 0; mask = 0;
@ -1892,7 +1927,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
group = BULLET_COLLISION_GROUP_STATIC; group = BULLET_COLLISION_GROUP_STATIC;
} }
uint8_t userMask = getCollisionMask(); uint16_t userMask = getCollisionMask();
if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) != if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) !=
(bool)(userMask & USER_COLLISION_GROUP_OTHER_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)) { if ((bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; 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; return result;
} }
uint8_t EntityItem::getCollisionMask() const { uint16_t EntityItem::getCollisionMask() const {
uint8_t result; uint16_t result;
withReadLock([&] { withReadLock([&] {
result = _collisionMask; result = _collisionMask;
}); });
@ -2975,3 +3010,118 @@ std::unordered_map<std::string, graphics::MultiMaterial> EntityItem::getMaterial
} }
return toReturn; 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; bool getCollisionless() const;
void setCollisionless(bool value); void setCollisionless(bool value);
uint8_t getCollisionMask() const; uint16_t getCollisionMask() const;
void setCollisionMask(uint8_t value); 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; bool getDynamic() const;
void setDynamic(bool value); void setDynamic(bool value);
@ -341,6 +341,19 @@ public:
quint32 getStaticCertificateVersion() const; quint32 getStaticCertificateVersion() const;
void setStaticCertificateVersion(const quint32&); 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()... // TODO: get rid of users of getRadius()...
float getRadius() const; float getRadius() const;
@ -494,6 +507,11 @@ public:
void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; } void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; }
uint64_t getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; } 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: signals:
void requestRenderUpdate(); void requestRenderUpdate();
@ -562,7 +580,7 @@ protected:
bool _visible { ENTITY_ITEM_DEFAULT_VISIBLE }; bool _visible { ENTITY_ITEM_DEFAULT_VISIBLE };
bool _canCastShadow{ ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW }; bool _canCastShadow{ ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW };
bool _collisionless { ENTITY_ITEM_DEFAULT_COLLISIONLESS }; 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 _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC };
bool _locked { ENTITY_ITEM_DEFAULT_LOCKED }; bool _locked { ENTITY_ITEM_DEFAULT_LOCKED };
QString _userData { ENTITY_ITEM_DEFAULT_USER_DATA }; 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 _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: private:
std::unordered_map<std::string, graphics::MultiMaterial> _materials; std::unordered_map<std::string, graphics::MultiMaterial> _materials;
std::mutex _materialsLock; std::mutex _materialsLock;

View file

@ -130,7 +130,7 @@ void buildStringToMaterialMappingModeLookup() {
addMaterialMappingMode(PROJECTED); addMaterialMappingMode(PROJECTED);
} }
QString getCollisionGroupAsString(uint8_t group) { QString getCollisionGroupAsString(uint16_t group) {
switch (group) { switch (group) {
case USER_COLLISION_GROUP_DYNAMIC: case USER_COLLISION_GROUP_DYNAMIC:
return "dynamic"; return "dynamic";
@ -146,7 +146,7 @@ QString getCollisionGroupAsString(uint8_t group) {
return ""; return "";
} }
uint8_t getCollisionGroupAsBitMask(const QStringRef& name) { uint16_t getCollisionGroupAsBitMask(const QStringRef& name) {
if (0 == name.compare(QString("dynamic"))) { if (0 == name.compare(QString("dynamic"))) {
return USER_COLLISION_GROUP_DYNAMIC; return USER_COLLISION_GROUP_DYNAMIC;
} else if (0 == name.compare(QString("static"))) { } else if (0 == name.compare(QString("static"))) {
@ -164,7 +164,7 @@ uint8_t getCollisionGroupAsBitMask(const QStringRef& name) {
QString EntityItemProperties::getCollisionMaskAsString() const { QString EntityItemProperties::getCollisionMaskAsString() const {
QString maskString(""); QString maskString("");
for (int i = 0; i < NUM_USER_COLLISION_GROUPS; ++i) { for (int i = 0; i < NUM_USER_COLLISION_GROUPS; ++i) {
uint8_t group = 0x01 << i; uint16_t group = 0x0001 << i;
if (group & _collisionMask) { if (group & _collisionMask) {
maskString.append(getCollisionGroupAsString(group)); maskString.append(getCollisionGroupAsString(group));
maskString.append(','); maskString.append(',');
@ -175,7 +175,7 @@ QString EntityItemProperties::getCollisionMaskAsString() const {
void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) { void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) {
QVector<QStringRef> groups = maskString.splitRef(','); QVector<QStringRef> groups = maskString.splitRef(',');
uint8_t mask = 0x00; uint16_t mask = 0x0000;
for (auto groupName : groups) { for (auto groupName : groups) {
mask |= getCollisionGroupAsBitMask(groupName); mask |= getCollisionGroupAsBitMask(groupName);
} }
@ -436,6 +436,13 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_DPI, dpi); CHECK_PROPERTY_CHANGE(PROP_DPI, dpi);
CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); 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 += _animation.getChangedProperties();
changedProperties += _keyLight.getChangedProperties(); changedProperties += _keyLight.getChangedProperties();
changedProperties += _ambientLight.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 * @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. * 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} 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|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.EntityType|Zone} entity with <code>castShadows</code> enabled in its
* {@link Entities.EntityProperties-Zone|keyLight} property. * {@link Entities.EntityProperties-Zone|keyLight} property.
@ -1391,7 +1398,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
/**jsdoc /**jsdoc
* The axis-aligned bounding box of an entity. * 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} 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} tfl - The top far left (maximum axes values) corner of the AA box.
* @property {Vec3} center - The center 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_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_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 // Rendering info
if (!skipDefaults && !strictSemantics) { if (!skipDefaults && !strictSemantics) {
QScriptValue renderInfo = engine->newObject(); 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(localRenderAlpha, float, setLocalRenderAlpha);
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionless, bool, setCollisionless); COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionless, bool, setCollisionless);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ignoreForCollisions, bool, setCollisionless, getCollisionless); // legacy support 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_ENUM(collidesWith, CollisionMask);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(collisionsWillMove, bool, setDynamic, getDynamic); // legacy support COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(collisionsWillMove, bool, setDynamic, getDynamic); // legacy support
COPY_PROPERTY_FROM_QSCRIPTVALUE(dynamic, bool, setDynamic); 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(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(); _lastEdited = usecTimestampNow();
} }
@ -1793,6 +1814,13 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(dpi); 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(); _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_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 // FIXME - these are not yet handled
//ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); //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_ENTITY_INSTANCE_NUMBER, properties.getEntityInstanceNumber());
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID()); APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID());
APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, properties.getStaticCertificateVersion()); 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) { 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_VISIBLE, bool, setVisible);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow); 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_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_DYNAMIC, bool, setDynamic);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData); 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_CERTIFICATE_ID, QString, setCertificateID);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion); 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; return valid;
} }
@ -2780,6 +2827,52 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
return true; 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() { void EntityItemProperties::markAllChanged() {
_lastEditedByChanged = true; _lastEditedByChanged = true;
_simulationOwnerChanged = true; _simulationOwnerChanged = true;
@ -2941,6 +3034,13 @@ void EntityItemProperties::markAllChanged() {
_dpiChanged = true; _dpiChanged = true;
_relayParentJointsChanged = true; _relayParentJointsChanged = true;
_cloneableChanged = true;
_cloneLifetimeChanged = true;
_cloneLimitChanged = true;
_cloneDynamicChanged = true;
_cloneAvatarEntityChanged = true;
_cloneOriginIDChanged = true;
} }
// The minimum bounding box for the entity. // The minimum bounding box for the entity.
@ -3373,6 +3473,25 @@ QList<QString> EntityItemProperties::listChangedProperties() {
out += "isUVModeStretch"; 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); getAnimation().listChangedProperties(out);
getKeyLight().listChangedProperties(out); getKeyLight().listChangedProperties(out);
getAmbientLight().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. // 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())); 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_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_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING);
DEFINE_PROPERTY(PROP_COLLISIONLESS, Collisionless, collisionless, bool, ENTITY_ITEM_DEFAULT_COLLISIONLESS); 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_DYNAMIC, Dynamic, dynamic, bool, ENTITY_ITEM_DEFAULT_DYNAMIC);
DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool, LightEntityItem::DEFAULT_IS_SPOTLIGHT); DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool, LightEntityItem::DEFAULT_IS_SPOTLIGHT);
DEFINE_PROPERTY(PROP_INTENSITY, Intensity, intensity, float, LightEntityItem::DEFAULT_INTENSITY); 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_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_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 getComponentModeString(uint32_t mode);
static QString getComponentModeAsString(uint32_t mode); static QString getComponentModeAsString(uint32_t mode);
@ -294,6 +301,8 @@ public:
QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties); QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties);
static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer); 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, static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes,
EntityItemID& entityID, EntityItemProperties& properties); EntityItemID& entityID, EntityItemProperties& properties);
@ -369,6 +378,8 @@ public:
bool verifyStaticCertificateProperties(); bool verifyStaticCertificateProperties();
static bool verifySignature(const QString& key, const QByteArray& text, const QByteArray& signature); static bool verifySignature(const QString& key, const QByteArray& text, const QByteArray& signature);
void convertToCloneProperties(const EntityItemID& entityIDToClone);
protected: protected:
QString getCollisionMaskAsString() const; QString getCollisionMaskAsString() const;
void setCollisionMaskFromString(const QString& maskString); 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, AmbientLightMode, ambientLightMode, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, SkyboxMode, skyboxMode, ""); 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, VoxelVolumeSize, voxelVolumeSize, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, ""); 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_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 #endif // hifi_EntityItemPropertiesDefaults_h

View file

@ -204,6 +204,13 @@ enum EntityPropertyList {
PROP_CERTIFICATE_ID, PROP_CERTIFICATE_ID,
PROP_STATIC_CERTIFICATE_VERSION, 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_HAZE_MODE,
PROP_KEYLIGHT_COLOR, PROP_KEYLIGHT_COLOR,

View file

@ -258,33 +258,9 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent); propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent);
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
EntityItemID id = EntityItemID(QUuid::createUuid()); EntityItemID id;
// If we have a local entity tree set, then also update it. // If we have a local entity tree set, then also update it.
bool success = true; bool success = addLocalEntityCopy(propertiesWithSimID, id);
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;
}
});
}
// queue the packet // queue the packet
if (success) { 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, QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& textures,
const QString& shapeType, bool dynamic, bool collisionless, const QString& shapeType, bool dynamic, bool collisionless,
const glm::vec3& position, const glm::vec3& gravity) { const glm::vec3& position, const glm::vec3& gravity) {
@ -320,6 +327,28 @@ QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QStrin
return addEntity(properties); 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) { EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) {
EntityPropertyFlags noSpecificProperties; EntityPropertyFlags noSpecificProperties;
return getEntityProperties(identity, 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, 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); 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 /**jsdoc
* Get the properties of an entity. * Get the properties of an entity.
* @function Entities.getEntityProperties * @function Entities.getEntityProperties
@ -1875,6 +1885,7 @@ private:
bool polyVoxWorker(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor); bool polyVoxWorker(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor); bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
bool addLocalEntityCopy(EntityItemProperties& propertiesWithSimID, EntityItemID& id, bool isClone = false);
EntityItemPointer checkForTreeEntityAndTypeMatch(const QUuid& entityID, EntityItemPointer checkForTreeEntityAndTypeMatch(const QUuid& entityID,
EntityTypes::EntityType entityType = EntityTypes::Unknown); EntityTypes::EntityType entityType = EntityTypes::Unknown);

View file

@ -65,6 +65,7 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
removeEntityInternal(entity); removeEntityInternal(entity);
if (entity->getElement()) { if (entity->getElement()) {
_deadEntities.insert(entity); _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 // we handle these types of "edit" packets
switch (packetType) { switch (packetType) {
case PacketType::EntityAdd: case PacketType::EntityAdd:
case PacketType::EntityClone:
case PacketType::EntityEdit: case PacketType::EntityEdit:
case PacketType::EntityErase: case PacketType::EntityErase:
case PacketType::EntityPhysics: case PacketType::EntityPhysics:
@ -492,7 +493,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
return true; 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; EntityItemPointer result = NULL;
EntityItemProperties props = properties; EntityItemProperties props = properties;
@ -504,7 +505,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
if (!properties.getClientOnly() && getIsClient() && if (!properties.getClientOnly() && getIsClient() &&
!nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() && !nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() &&
!nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain) { !nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain && !isClone) {
return nullptr; return nullptr;
} }
@ -592,6 +593,7 @@ void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ign
return; return;
} }
cleanupCloneIDs(entityID);
unhookChildAvatar(entityID); unhookChildAvatar(entityID);
emit deletingEntity(entityID); emit deletingEntity(entityID);
emit deletingEntityPointer(existingEntity.get()); 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) { void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool ignoreWarnings) {
// NOTE: callers must lock the tree before using this method // NOTE: callers must lock the tree before using this method
DeleteEntityOperator theOperator(getThisPointer()); DeleteEntityOperator theOperator(getThisPointer());
@ -653,6 +677,7 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool i
} }
// tell our delete operator about this entityID // tell our delete operator about this entityID
cleanupCloneIDs(entityID);
unhookChildAvatar(entityID); unhookChildAvatar(entityID);
theOperator.addEntityIDToDeleteList(entityID); theOperator.addEntityIDToDeleteList(entityID);
emit deletingEntity(entityID); emit deletingEntity(entityID);
@ -1392,6 +1417,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
int processedBytes = 0; int processedBytes = 0;
bool isAdd = false; bool isAdd = false;
bool isClone = false;
// we handle these types of "edit" packets // we handle these types of "edit" packets
switch (message.getType()) { switch (message.getType()) {
case PacketType::EntityErase: { case PacketType::EntityErase: {
@ -1400,6 +1426,9 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
break; break;
} }
case PacketType::EntityClone:
isClone = true; // fall through to next case
// FALLTHRU
case PacketType::EntityAdd: case PacketType::EntityAdd:
isAdd = true; // fall through to next case isAdd = true; // fall through to next case
// FALLTHRU // FALLTHRU
@ -1422,8 +1451,22 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
EntityItemProperties properties; EntityItemProperties properties;
startDecode = usecTimestampNow(); startDecode = usecTimestampNow();
bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes, bool validEditPacket = false;
entityItemID, properties); 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(); endDecode = usecTimestampNow();
EntityItemPointer existingEntity; EntityItemPointer existingEntity;
@ -1491,24 +1534,26 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
} }
if ((isAdd || properties.lifetimeChanged()) && if (!isClone) {
((!senderNode->getCanRez() && senderNode->getCanRezTmp()) || if ((isAdd || properties.lifetimeChanged()) &&
(!senderNode->getCanRezCertified() && senderNode->getCanRezTmpCertified()))) { ((!senderNode->getCanRez() && senderNode->getCanRezTmp()) ||
// this node is only allowed to rez temporary entities. if need be, cap the lifetime. (!senderNode->getCanRezCertified() && senderNode->getCanRezTmpCertified()))) {
if (properties.getLifetime() == ENTITY_ITEM_IMMORTAL_LIFETIME || // this node is only allowed to rez temporary entities. if need be, cap the lifetime.
properties.getLifetime() > _maxTmpEntityLifetime) { if (properties.getLifetime() == ENTITY_ITEM_IMMORTAL_LIFETIME ||
properties.setLifetime(_maxTmpEntityLifetime); 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); 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 // 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 // an existing entity... handle appropriately
if (validEditPacket) { if (validEditPacket) {
@ -1566,17 +1611,32 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
} else if (isAdd) { } else if (isAdd) {
bool failedAdd = !allowed; bool failedAdd = !allowed;
bool isCertified = !properties.getCertificateID().isEmpty(); bool isCertified = !properties.getCertificateID().isEmpty();
bool isCloneable = properties.getCloneable();
int cloneLimit = properties.getCloneLimit();
if (!allowed) { if (!allowed) {
qCDebug(entities) << "Filtered entity add. ID:" << entityItemID; qCDebug(entities) << "Filtered entity add. ID:" << entityItemID;
} else if (!isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) { } else if (!isClone && !isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
failedAdd = true; failedAdd = true;
qCDebug(entities) << "User without 'uncertified rez rights' [" << senderNode->getUUID() qCDebug(entities) << "User without 'uncertified rez rights' [" << senderNode->getUUID()
<< "] attempted to add an uncertified entity with ID:" << entityItemID; << "] 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; failedAdd = true;
qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID() qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID()
<< "] attempted to add a certified entity with ID:" << entityItemID; << "] 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 { } else {
if (isClone) {
properties.convertToCloneProperties(entityIDToClone);
}
// this is a new entity... assign a new entityID // this is a new entity... assign a new entityID
properties.setCreated(properties.getLastEdited()); properties.setCreated(properties.getLastEdited());
properties.setLastEditedBy(senderNode->getUUID()); 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) { if (newEntity) {
newEntity->markAsChangedOnServer(); newEntity->markAsChangedOnServer();
notifyNewlyCreatedEntity(*newEntity, senderNode); notifyNewlyCreatedEntity(*newEntity, senderNode);
startLogging = usecTimestampNow(); startLogging = usecTimestampNow();
if (wantEditLogging()) { if (wantEditLogging()) {
qCDebug(entities) << "User [" << senderNode->getUUID() << "] added entity. ID:" qCDebug(entities) << "User [" << senderNode->getUUID() << "] added entity. ID:"
@ -1927,6 +1992,7 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo
if (shouldEraseEntity(entityID, sourceNode)) { if (shouldEraseEntity(entityID, sourceNode)) {
entityItemIDsToDelete << entityItemID; entityItemIDsToDelete << entityItemID;
cleanupCloneIDs(entityItemID);
} }
} }
deleteEntities(entityItemIDsToDelete, true, true); deleteEntities(entityItemIDsToDelete, true, true);
@ -1976,6 +2042,7 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons
if (shouldEraseEntity(entityID, sourceNode)) { if (shouldEraseEntity(entityID, sourceNode)) {
entityItemIDsToDelete << entityItemID; entityItemIDsToDelete << entityItemID;
cleanupCloneIDs(entityItemID);
} }
} }
@ -2322,6 +2389,8 @@ bool EntityTree::readFromMap(QVariantMap& map) {
return false; return false;
} }
QMap<QUuid, QVector<QUuid>> cloneIDs;
bool success = true; bool success = true;
foreach (QVariant entityVariant, entitiesQList) { foreach (QVariant entityVariant, entitiesQList) {
// QVariantMap --> QScriptValue --> EntityItemProperties --> Entity // 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); EntityItemPointer entity = addEntity(entityItemID, properties);
if (!entity) { if (!entity) {
qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType();
success = false; 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; return success;

View file

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

View file

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

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_RED_SIGNED:
case gpu::Semantic::COMPRESSED_EAC_XY: case gpu::Semantic::COMPRESSED_EAC_XY:
case gpu::Semantic::COMPRESSED_EAC_XY_SIGNED: 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; return false;
default: default:
return true; return true;

View file

@ -33,6 +33,8 @@ using namespace gpu::gl45;
bool GL45Backend::supportedTextureFormat(const gpu::Element& format) { bool GL45Backend::supportedTextureFormat(const gpu::Element& format) {
switch (format.getSemantic()) { 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_RGB:
case gpu::Semantic::COMPRESSED_ETC2_SRGB: case gpu::Semantic::COMPRESSED_ETC2_SRGB:
case gpu::Semantic::COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA: 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> * moved down. The data value is how far the average position of all touch points moved.</td></tr>
* </tbody> * </tbody>
* </table> * </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> * @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 { controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInputs() const {

View file

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

View file

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

View file

@ -138,7 +138,7 @@ public:
* </tr> * </tr>
* </tbody> * </tbody>
* </table> * </table>
* @typedef location.LookupTrigger * @typedef {number} location.LookupTrigger
*/ */
enum LookupTrigger { enum LookupTrigger {
UserInput, UserInput,
@ -184,7 +184,7 @@ public slots:
/**jsdoc /**jsdoc
* Go to a specified metaverse address. * Go to a specified metaverse address.
* @function location.handleLookupString * @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>"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>). * <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. * @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> * </tr>
* </tbody> * </tbody>
* </table> * </table>
* @typedef Window.ConnectionRefusedReason * @typedef {number} Window.ConnectionRefusedReason
*/ */
enum class ConnectionRefusedReason : uint8_t { enum class ConnectionRefusedReason : uint8_t {
Unknown, Unknown,

View file

@ -87,7 +87,7 @@ private:
class ScriptableResource : public QObject { class ScriptableResource : public QObject {
/**jsdoc /**jsdoc
* @constructor Resource * @class ResourceObject
* *
* @hifi-interface * @hifi-interface
* @hifi-client-entity * @hifi-client-entity
@ -97,11 +97,6 @@ class ScriptableResource : public QObject {
* @property {string} url - URL of this resource. * @property {string} url - URL of this resource.
* @property {Resource.State} state - Current loading state. * @property {Resource.State} state - Current loading state.
*/ */
/**jsdoc
* @namespace Resource
* @variation 0
* @property {Resource.State} State
*/
Q_OBJECT Q_OBJECT
Q_PROPERTY(QUrl url READ getURL) Q_PROPERTY(QUrl url READ getURL)
Q_PROPERTY(int state READ getState NOTIFY stateChanged) Q_PROPERTY(int state READ getState NOTIFY stateChanged)
@ -109,8 +104,7 @@ class ScriptableResource : public QObject {
public: public:
/**jsdoc /**jsdoc
* @name Resource.State * @typedef {object} Resource.State
* @static
* @property {number} QUEUED - The resource is queued up, waiting to be loaded. * @property {number} QUEUED - The resource is queued up, waiting to be loaded.
* @property {number} LOADING - The resource is downloading. * @property {number} LOADING - The resource is downloading.
* @property {number} LOADED - The resource has finished downloaded by is not complete. * @property {number} LOADED - The resource has finished downloaded by is not complete.
@ -131,7 +125,7 @@ public:
/**jsdoc /**jsdoc
* Release this resource. * Release this resource.
* @function Resource#release * @function ResourceObject#release
*/ */
Q_INVOKABLE void release(); Q_INVOKABLE void release();
@ -146,7 +140,7 @@ signals:
/**jsdoc /**jsdoc
* Triggered when download progress for this resource has changed. * Triggered when download progress for this resource has changed.
* @function Resource#progressChanged * @function ResourceObject#progressChanged
* @param {number} bytesReceived - Byytes downloaded so far. * @param {number} bytesReceived - Byytes downloaded so far.
* @param {number} bytesTotal - Total number of bytes in the resource. * @param {number} bytesTotal - Total number of bytes in the resource.
* @returns {Signal} * @returns {Signal}
@ -155,7 +149,7 @@ signals:
/**jsdoc /**jsdoc
* Triggered when resource loading state has changed. * Triggered when resource loading state has changed.
* @function Resource#stateChanged * @function ResourceObject#stateChanged
* @param {Resource.State} state - New state. * @param {Resource.State} state - New state.
* @returns {Signal} * @returns {Signal}
*/ */
@ -262,7 +256,7 @@ protected slots:
* @function ResourceCache.prefetch * @function ResourceCache.prefetch
* @param {string} url - URL of the resource to prefetch. * @param {string} url - URL of the resource to prefetch.
* @param {object} [extra=null] * @param {object} [extra=null]
* @returns {Resource} * @returns {ResourceObject}
*/ */
// Prefetches a resource to be held by the QScriptEngine. // Prefetches a resource to be held by the QScriptEngine.
// Left as a protected member so subclasses can overload prefetch // Left as a protected member so subclasses can overload prefetch
@ -275,8 +269,9 @@ protected slots:
* @param {string} url - URL of the resource to load. * @param {string} url - URL of the resource to load.
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails. * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
* @param {} [extra=null] * @param {} [extra=null]
* @returns {Resource} * @returns {object}
*/ */
// FIXME: The return type is not recognized by JavaScript.
/// Loads a resource from the specified URL and returns it. /// Loads a resource from the specified URL and returns it.
/// If the caller is on a different thread than the ResourceCache, /// If the caller is on a different thread than the ResourceCache,
/// returns an empty smart pointer and loads its asynchronously. /// returns an empty smart pointer and loads its asynchronously.

View file

@ -29,10 +29,11 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::DomainList: case PacketType::DomainList:
return static_cast<PacketVersion>(DomainListVersion::GetMachineFingerprintFromUUIDSupport); return static_cast<PacketVersion>(DomainListVersion::GetMachineFingerprintFromUUIDSupport);
case PacketType::EntityAdd: case PacketType::EntityAdd:
case PacketType::EntityClone:
case PacketType::EntityEdit: case PacketType::EntityEdit:
case PacketType::EntityData: case PacketType::EntityData:
case PacketType::EntityPhysics: case PacketType::EntityPhysics:
return static_cast<PacketVersion>(EntityVersion::MaterialData); return static_cast<PacketVersion>(EntityVersion::CollisionMask16Bytes);
case PacketType::EntityQuery: case PacketType::EntityQuery:
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums); return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
case PacketType::AvatarIdentity: case PacketType::AvatarIdentity:

View file

@ -131,6 +131,8 @@ public:
OctreeDataFileReply, OctreeDataFileReply,
OctreeDataPersist, OctreeDataPersist,
EntityClone,
NUM_PACKET_TYPE NUM_PACKET_TYPE
}; };
@ -232,7 +234,9 @@ enum class EntityVersion : PacketVersion {
SoftEntities, SoftEntities,
MaterialEntities, MaterialEntities,
ShadowControl, ShadowControl,
MaterialData MaterialData,
CloneableData,
CollisionMask16Bytes
}; };
enum class EntityScriptCallMethodVersion : PacketVersion { enum class EntityScriptCallMethodVersion : PacketVersion {

View file

@ -109,7 +109,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
} }
_dynamicsWorld = nullptr; _dynamicsWorld = nullptr;
} }
int16_t collisionGroup = computeCollisionGroup(); int32_t collisionGroup = computeCollisionGroup();
if (_rigidBody) { if (_rigidBody) {
updateMassProperties(); updateMassProperties();
} }
@ -325,7 +325,7 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar
_ghost.setWorldTransform(_rigidBody->getWorldTransform()); _ghost.setWorldTransform(_rigidBody->getWorldTransform());
} }
void CharacterController::jump() { void CharacterController::jump(const btVector3& dir) {
_pendingFlags |= PENDING_FLAG_JUMP; _pendingFlags |= PENDING_FLAG_JUMP;
} }
@ -352,7 +352,7 @@ static const char* stateToStr(CharacterController::State state) {
#endif // #ifdef DEBUG_STATE_CHANGE #endif // #ifdef DEBUG_STATE_CHANGE
void CharacterController::updateCurrentGravity() { void CharacterController::updateCurrentGravity() {
int16_t collisionGroup = computeCollisionGroup(); int32_t collisionGroup = computeCollisionGroup();
if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_currentGravity = 0.0f; _currentGravity = 0.0f;
} else { } else {
@ -433,7 +433,7 @@ void CharacterController::setCollisionless(bool collisionless) {
} }
} }
int16_t CharacterController::computeCollisionGroup() const { int32_t CharacterController::computeCollisionGroup() const {
if (_collisionless) { if (_collisionless) {
return _collisionlessAllowed ? BULLET_COLLISION_GROUP_COLLISIONLESS : BULLET_COLLISION_GROUP_MY_AVATAR; return _collisionlessAllowed ? BULLET_COLLISION_GROUP_COLLISIONLESS : BULLET_COLLISION_GROUP_MY_AVATAR;
} else { } else {
@ -446,7 +446,7 @@ void CharacterController::handleChangedCollisionGroup() {
// ATM the easiest way to update collision groups is to remove/re-add the RigidBody // ATM the easiest way to update collision groups is to remove/re-add the RigidBody
if (_dynamicsWorld) { if (_dynamicsWorld) {
_dynamicsWorld->removeRigidBody(_rigidBody); _dynamicsWorld->removeRigidBody(_rigidBody);
int16_t collisionGroup = computeCollisionGroup(); int32_t collisionGroup = computeCollisionGroup();
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
} }
_pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP; _pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP;
@ -538,7 +538,7 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
btScalar angle = motor.rotation.getAngle(); btScalar angle = motor.rotation.getAngle();
btVector3 velocity = worldVelocity.rotate(axis, -angle); btVector3 velocity = worldVelocity.rotate(axis, -angle);
int16_t collisionGroup = computeCollisionGroup(); int32_t collisionGroup = computeCollisionGroup();
if (collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS || if (collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ||
_state == State::Hover || motor.hTimescale == motor.vTimescale) { _state == State::Hover || motor.hTimescale == motor.vTimescale) {
// modify velocity // modify velocity
@ -679,7 +679,7 @@ void CharacterController::updateState() {
btVector3 rayStart = _position; btVector3 rayStart = _position;
btScalar rayLength = _radius; btScalar rayLength = _radius;
int16_t collisionGroup = computeCollisionGroup(); int32_t collisionGroup = computeCollisionGroup();
if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) {
rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT; rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT;
} else { } else {

View file

@ -70,7 +70,7 @@ public:
virtual void preStep(btCollisionWorld *collisionWorld) override; virtual void preStep(btCollisionWorld *collisionWorld) override;
virtual void playerStep(btCollisionWorld *collisionWorld, btScalar dt) override; virtual void playerStep(btCollisionWorld *collisionWorld, btScalar dt) override;
virtual bool canJump() const override { assert(false); return false; } // never call this virtual bool canJump() const override { assert(false); return false; } // never call this
virtual void jump() override; virtual void jump(const btVector3& dir = btVector3(0.0f, 0.0f, 0.0f)) override;
virtual bool onGround() const override; virtual bool onGround() const override;
void clearMotors(); void clearMotors();
@ -120,7 +120,7 @@ public:
bool isStuck() const { return _isStuck; } bool isStuck() const { return _isStuck; }
void setCollisionless(bool collisionless); void setCollisionless(bool collisionless);
int16_t computeCollisionGroup() const; int32_t computeCollisionGroup() const;
void handleChangedCollisionGroup(); void handleChangedCollisionGroup();
bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);

View file

@ -29,13 +29,13 @@ CharacterGhostObject::~CharacterGhostObject() {
} }
} }
void CharacterGhostObject::setCollisionGroupAndMask(int16_t group, int16_t mask) { void CharacterGhostObject::setCollisionGroupAndMask(int32_t group, int32_t mask) {
_collisionFilterGroup = group; _collisionFilterGroup = group;
_collisionFilterMask = mask; _collisionFilterMask = mask;
// TODO: if this probe is in the world reset ghostObject overlap cache // TODO: if this probe is in the world reset ghostObject overlap cache
} }
void CharacterGhostObject::getCollisionGroupAndMask(int16_t& group, int16_t& mask) const { void CharacterGhostObject::getCollisionGroupAndMask(int32_t& group, int32_t& mask) const {
group = _collisionFilterGroup; group = _collisionFilterGroup;
mask = _collisionFilterMask; mask = _collisionFilterMask;
} }

View file

@ -28,8 +28,8 @@ public:
CharacterGhostObject() { } CharacterGhostObject() { }
~CharacterGhostObject(); ~CharacterGhostObject();
void setCollisionGroupAndMask(int16_t group, int16_t mask); void setCollisionGroupAndMask(int32_t group, int32_t mask);
void getCollisionGroupAndMask(int16_t& group, int16_t& mask) const; void getCollisionGroupAndMask(int32_t& group, int32_t& mask) const;
void setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight); void setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight);
void setUpDirection(const btVector3& up); void setUpDirection(const btVector3& up);
@ -54,8 +54,8 @@ protected:
btScalar _radius { 0.0f }; btScalar _radius { 0.0f };
btConvexHullShape* _characterShape { nullptr }; // input, shape of character btConvexHullShape* _characterShape { nullptr }; // input, shape of character
CharacterGhostShape* _ghostShape { nullptr }; // internal, shape whose Aabb is used for overlap cache CharacterGhostShape* _ghostShape { nullptr }; // internal, shape whose Aabb is used for overlap cache
int16_t _collisionFilterGroup { 0 }; int32_t _collisionFilterGroup { 0 };
int16_t _collisionFilterMask { 0 }; int32_t _collisionFilterMask { 0 };
bool _inWorld { false }; // internal, was added to world bool _inWorld { false }; // internal, was added to world
}; };

View file

@ -779,7 +779,7 @@ QString EntityMotionState::getName() const {
} }
// virtual // virtual
void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const { void EntityMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const {
_entity->computeCollisionGroupAndFinalMask(group, mask); _entity->computeCollisionGroupAndFinalMask(group, mask);
} }

View file

@ -84,7 +84,7 @@ public:
virtual QString getName() const override; virtual QString getName() const override;
virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override; virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
bool shouldSendBid(); bool shouldSendBid();
bool isLocallyOwned() const override; bool isLocallyOwned() const override;

View file

@ -154,7 +154,7 @@ public:
virtual QString getName() const { return ""; } virtual QString getName() const { return ""; }
virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const = 0; virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const = 0;
bool isActive() const { return _body ? _body->isActive() : false; } bool isActive() const { return _body ? _body->isActive() : false; }

View file

@ -148,7 +148,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
body->setFlags(BT_DISABLE_WORLD_GRAVITY); body->setFlags(BT_DISABLE_WORLD_GRAVITY);
motionState->updateBodyMaterialProperties(); motionState->updateBodyMaterialProperties();
int16_t group, mask; int32_t group, mask;
motionState->computeCollisionGroupAndMask(group, mask); motionState->computeCollisionGroupAndMask(group, mask);
_dynamicsWorld->addRigidBody(body, group, mask); _dynamicsWorld->addRigidBody(body, group, mask);

View file

@ -181,7 +181,7 @@ class DebugAmbientOcclusionConfig : public render::Job::Config {
Q_PROPERTY(bool showCursorPixel MEMBER showCursorPixel NOTIFY dirty) Q_PROPERTY(bool showCursorPixel MEMBER showCursorPixel NOTIFY dirty)
Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty) Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty)
public: public:
DebugAmbientOcclusionConfig() : render::Job::Config(true) {} DebugAmbientOcclusionConfig() : render::Job::Config(false) {}
bool showCursorPixel{ false }; bool showCursorPixel{ false };
glm::vec2 debugCursorTexcoord{ 0.5f, 0.5f }; glm::vec2 debugCursorTexcoord{ 0.5f, 0.5f };

View file

@ -122,6 +122,9 @@ static const gpu::Element TEXCOORD4_ELEMENT { gpu::VEC4, gpu::FLOAT, gpu::XYZW }
static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT; static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT;
static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT; static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT;
static gpu::Stream::FormatPointer INSTANCED_SOLID_FADE_STREAM_FORMAT; static gpu::Stream::FormatPointer INSTANCED_SOLID_FADE_STREAM_FORMAT;
static gpu::Stream::FormatPointer WIRE_STREAM_FORMAT;
static gpu::Stream::FormatPointer INSTANCED_WIRE_STREAM_FORMAT;
static gpu::Stream::FormatPointer INSTANCED_WIRE_FADE_STREAM_FORMAT;
static const uint SHAPE_VERTEX_STRIDE = sizeof(GeometryCache::ShapeVertex); // position, normal, texcoords, tangent static const uint SHAPE_VERTEX_STRIDE = sizeof(GeometryCache::ShapeVertex); // position, normal, texcoords, tangent
static const uint SHAPE_NORMALS_OFFSET = offsetof(GeometryCache::ShapeVertex, normal); static const uint SHAPE_NORMALS_OFFSET = offsetof(GeometryCache::ShapeVertex, normal);
@ -690,6 +693,38 @@ gpu::Stream::FormatPointer& getInstancedSolidFadeStreamFormat() {
return INSTANCED_SOLID_FADE_STREAM_FORMAT; return INSTANCED_SOLID_FADE_STREAM_FORMAT;
} }
gpu::Stream::FormatPointer& getWireStreamFormat() {
if (!WIRE_STREAM_FORMAT) {
WIRE_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT);
WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT);
}
return WIRE_STREAM_FORMAT;
}
gpu::Stream::FormatPointer& getInstancedWireStreamFormat() {
if (!INSTANCED_WIRE_STREAM_FORMAT) {
INSTANCED_WIRE_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT);
INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT);
INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
}
return INSTANCED_WIRE_STREAM_FORMAT;
}
gpu::Stream::FormatPointer& getInstancedWireFadeStreamFormat() {
if (!INSTANCED_WIRE_FADE_STREAM_FORMAT) {
INSTANCED_WIRE_FADE_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT);
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT);
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD2, gpu::Stream::TEXCOORD2, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD3, gpu::Stream::TEXCOORD3, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD4, gpu::Stream::TEXCOORD4, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
}
return INSTANCED_WIRE_FADE_STREAM_FORMAT;
}
QHash<SimpleProgramKey, gpu::PipelinePointer> GeometryCache::_simplePrograms; QHash<SimpleProgramKey, gpu::PipelinePointer> GeometryCache::_simplePrograms;
gpu::ShaderPointer GeometryCache::_simpleShader; gpu::ShaderPointer GeometryCache::_simpleShader;
@ -827,7 +862,7 @@ void GeometryCache::renderShape(gpu::Batch& batch, Shape shape) {
} }
void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape) { void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape) {
batch.setInputFormat(getSolidStreamFormat()); batch.setInputFormat(getWireStreamFormat());
_shapes[shape].drawWire(batch); _shapes[shape].drawWire(batch);
} }
@ -839,7 +874,7 @@ void GeometryCache::renderShape(gpu::Batch& batch, Shape shape, const glm::vec4&
} }
void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color) { void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color) {
batch.setInputFormat(getSolidStreamFormat()); batch.setInputFormat(getWireStreamFormat());
// Color must be set after input format // Color must be set after input format
batch._glColor4f(color.r, color.g, color.b, color.a); batch._glColor4f(color.r, color.g, color.b, color.a);
_shapes[shape].drawWire(batch); _shapes[shape].drawWire(batch);
@ -857,7 +892,7 @@ void GeometryCache::renderShapeInstances(gpu::Batch& batch, Shape shape, size_t
} }
void GeometryCache::renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) { void GeometryCache::renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) {
batch.setInputFormat(getInstancedSolidStreamFormat()); batch.setInputFormat(getInstancedWireStreamFormat());
setupBatchInstance(batch, colorBuffer); setupBatchInstance(batch, colorBuffer);
_shapes[shape].drawWireInstances(batch, count); _shapes[shape].drawWireInstances(batch, count);
} }
@ -883,7 +918,7 @@ void GeometryCache::renderFadeShapeInstances(gpu::Batch& batch, Shape shape, siz
void GeometryCache::renderWireFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, void GeometryCache::renderWireFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer,
gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3) { gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3) {
batch.setInputFormat(getInstancedSolidFadeStreamFormat()); batch.setInputFormat(getInstancedWireFadeStreamFormat());
setupBatchFadeInstance(batch, colorBuffer, fadeBuffer1, fadeBuffer2, fadeBuffer3); setupBatchFadeInstance(batch, colorBuffer, fadeBuffer1, fadeBuffer2, fadeBuffer3);
_shapes[shape].drawWireInstances(batch, count); _shapes[shape].drawWireInstances(batch, count);
} }

View file

@ -195,7 +195,7 @@ class DebugLightClustersConfig : public render::Job::Config {
Q_PROPERTY(bool doDrawClusterFromDepth MEMBER doDrawClusterFromDepth NOTIFY dirty) Q_PROPERTY(bool doDrawClusterFromDepth MEMBER doDrawClusterFromDepth NOTIFY dirty)
Q_PROPERTY(bool doDrawContent MEMBER doDrawContent NOTIFY dirty) Q_PROPERTY(bool doDrawContent MEMBER doDrawContent NOTIFY dirty)
public: public:
DebugLightClustersConfig() : render::Job::Config(true){} DebugLightClustersConfig() : render::Job::Config(false){}
bool doDrawGrid{ false }; bool doDrawGrid{ false };

View file

@ -149,7 +149,7 @@ class DebugSubsurfaceScatteringConfig : public render::Job::Config {
Q_PROPERTY(bool showCursorPixel MEMBER showCursorPixel NOTIFY dirty) Q_PROPERTY(bool showCursorPixel MEMBER showCursorPixel NOTIFY dirty)
Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty) Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty)
public: public:
DebugSubsurfaceScatteringConfig() : render::Job::Config(true) {} DebugSubsurfaceScatteringConfig() : render::Job::Config(false) {}
bool showProfile{ false }; bool showProfile{ false };
bool showLUT{ false }; bool showLUT{ false };

View file

@ -36,12 +36,11 @@ public:
} }
}; };
Engine::Engine() : Task(EngineTask::JobModel::create("Engine")), RenderEngine::RenderEngine() : Engine(EngineTask::JobModel::create("Engine"), std::make_shared<RenderContext>())
_renderContext(std::make_shared<RenderContext>())
{ {
} }
void Engine::load() { void RenderEngine::load() {
auto config = getConfiguration(); auto config = getConfiguration();
const QString configFile= "config/render.json"; const QString configFile= "config/render.json";

View file

@ -25,7 +25,7 @@ namespace render {
class RenderContext : public task::JobContext { class RenderContext : public task::JobContext {
public: public:
RenderContext() : task::JobContext(trace_render()) {} RenderContext() : task::JobContext() {}
virtual ~RenderContext() {} virtual ~RenderContext() {}
RenderArgs* args; RenderArgs* args;
@ -33,7 +33,9 @@ namespace render {
}; };
using RenderContextPointer = std::shared_ptr<RenderContext>; using RenderContextPointer = std::shared_ptr<RenderContext>;
Task_DeclareTypeAliases(RenderContext) Task_DeclareCategoryTimeProfilerClass(RenderTimeProfiler, trace_render);
Task_DeclareTypeAliases(RenderContext, RenderTimeProfiler)
// Versions of the COnfig integrating a gpu & batch timer // Versions of the COnfig integrating a gpu & batch timer
class GPUJobConfig : public JobConfig { class GPUJobConfig : public JobConfig {
@ -57,10 +59,10 @@ namespace render {
class GPUTaskConfig : public TaskConfig { class GPUTaskConfig : public TaskConfig {
Q_OBJECT Q_OBJECT
Q_PROPERTY(double gpuRunTime READ getGPURunTime) Q_PROPERTY(double gpuRunTime READ getGPURunTime)
Q_PROPERTY(double batchRunTime READ getBatchRunTime) Q_PROPERTY(double batchRunTime READ getBatchRunTime)
double _msGPURunTime { 0.0 }; double _msGPURunTime { 0.0 };
double _msBatchRunTime { 0.0 }; double _msBatchRunTime { 0.0 };
public: public:
@ -80,32 +82,25 @@ namespace render {
// The render engine holds all render tasks, and is itself a render task. // The render engine holds all render tasks, and is itself a render task.
// State flows through tasks to jobs via the render and scene contexts - // State flows through tasks to jobs via the render and scene contexts -
// the engine should not be known from its jobs. // the engine should not be known from its jobs.
class Engine : public Task { class RenderEngine : public Engine {
public: public:
Engine(); RenderEngine();
~Engine() = default; ~RenderEngine() = default;
// Load any persisted settings, and set up the presets // Load any persisted settings, and set up the presets
// This should be run after adding all jobs, and before building ui // This should be run after adding all jobs, and before building ui
void load(); void load();
// Register the scene // Register the scene
void registerScene(const ScenePointer& scene) { _renderContext->_scene = scene; } void registerScene(const ScenePointer& scene) { _context->_scene = scene; }
// acces the RenderContext // acces the RenderContext
RenderContextPointer getRenderContext() const { return _renderContext; } RenderContextPointer getRenderContext() const { return _context; }
// Render a frame
// Must have a scene registered and a context set
void run() { assert(_renderContext); Task::run(_renderContext); }
protected: protected:
RenderContextPointer _renderContext;
void run(const RenderContextPointer& context) override { assert(_renderContext); Task::run(_renderContext); }
}; };
using EnginePointer = std::shared_ptr<Engine>; using EnginePointer = std::shared_ptr<RenderEngine>;
} }

View file

@ -122,6 +122,7 @@ public:
Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); }
Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } Builder& withDeformed() { _flags.set(DEFORMED); return (*this); }
Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }
Builder& withVisible() { _flags.reset(INVISIBLE); return (*this); }
Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); } Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
Builder& withLayered() { _flags.set(LAYERED); return (*this); } Builder& withLayered() { _flags.set(LAYERED); return (*this); }
Builder& withMetaCullGroup() { _flags.set(META_CULL_GROUP); return (*this); } Builder& withMetaCullGroup() { _flags.set(META_CULL_GROUP); return (*this); }

View file

@ -21,7 +21,7 @@
namespace render { namespace render {
class Engine; class RenderEngine;
class Scene; class Scene;
// Transaction is the mechanism to make any change to the scene. // Transaction is the mechanism to make any change to the scene.
@ -236,7 +236,7 @@ protected:
StageMap _stages; StageMap _stages;
friend class Engine; friend class RenderEngine;
}; };
typedef std::shared_ptr<Scene> ScenePointer; typedef std::shared_ptr<Scene> ScenePointer;

View file

@ -121,7 +121,7 @@ public:
/**jsdoc /**jsdoc
* A set of properties that can be passed to {@link Assets.getAsset}. * A set of properties that can be passed to {@link Assets.getAsset}.
* @typedef {Object} Assets.GetOptions * @typedef {object} Assets.GetOptions
* @property {string} [url] an "atp:" style URL, hash, or relative mapped path to fetch * @property {string} [url] an "atp:" style URL, hash, or relative mapped path to fetch
* @property {string} [responseType=text] the desired reponse type (text | arraybuffer | json) * @property {string} [responseType=text] the desired reponse type (text | arraybuffer | json)
* @property {boolean} [decompress=false] whether to attempt gunzip decompression on the fetched data * @property {boolean} [decompress=false] whether to attempt gunzip decompression on the fetched data
@ -137,7 +137,7 @@ public:
/**jsdoc /**jsdoc
* Result value returned by {@link Assets.getAsset}. * Result value returned by {@link Assets.getAsset}.
* @typedef {Object} Assets~getAssetResult * @typedef {object} Assets~getAssetResult
* @property {string} [url] the resolved "atp:" style URL for the fetched asset * @property {string} [url] the resolved "atp:" style URL for the fetched asset
* @property {string} [hash] the resolved hash for the fetched asset * @property {string} [hash] the resolved hash for the fetched asset
* @property {string|ArrayBuffer|Object} [response] response data (possibly converted per .responseType value) * @property {string|ArrayBuffer|Object} [response] response data (possibly converted per .responseType value)
@ -159,7 +159,7 @@ public:
/**jsdoc /**jsdoc
* A set of properties that can be passed to {@link Assets.putAsset}. * A set of properties that can be passed to {@link Assets.putAsset}.
* @typedef {Object} Assets.PutOptions * @typedef {object} Assets.PutOptions
* @property {ArrayBuffer|string} [data] byte buffer or string value representing the new asset's content * @property {ArrayBuffer|string} [data] byte buffer or string value representing the new asset's content
* @property {string} [path=null] ATP path mapping to automatically create (upon successful upload to hash) * @property {string} [path=null] ATP path mapping to automatically create (upon successful upload to hash)
* @property {boolean} [compress=false] whether to gzip compress data before uploading * @property {boolean} [compress=false] whether to gzip compress data before uploading
@ -174,7 +174,7 @@ public:
/**jsdoc /**jsdoc
* Result value returned by {@link Assets.putAsset}. * Result value returned by {@link Assets.putAsset}.
* @typedef {Object} Assets~putAssetResult * @typedef {object} Assets~putAssetResult
* @property {string} [url] the resolved "atp:" style URL for the uploaded asset (based on .path if specified, otherwise on the resulting ATP hash) * @property {string} [url] the resolved "atp:" style URL for the uploaded asset (based on .path if specified, otherwise on the resulting ATP hash)
* @property {string} [path] the uploaded asset's resulting ATP path (or undefined if no path mapping was assigned) * @property {string} [path] the uploaded asset's resulting ATP path (or undefined if no path mapping was assigned)
* @property {string} [hash] the uploaded asset's resulting ATP hash * @property {string} [hash] the uploaded asset's resulting ATP hash

View file

@ -43,7 +43,7 @@
* @hifi-server-entity * @hifi-server-entity
* @hifi-assignment-client * @hifi-assignment-client
* *
* @property IDENTITY {Quat} <code>{ x: 0, y: 0, z: 0, w: 1 }</code> : The identity rotation, i.e., no rotation. * @property {Quat} IDENTITY - <code>{ x: 0, y: 0, z: 0, w: 1 }</code> : The identity rotation, i.e., no rotation.
* <em>Read-only.</em> * <em>Read-only.</em>
* @example <caption>Print the <code>IDENTITY</code> value.</caption> * @example <caption>Print the <code>IDENTITY</code> value.</caption>
* print(JSON.stringify(Quat.IDENTITY)); // { x: 0, y: 0, z: 0, w: 1 } * print(JSON.stringify(Quat.IDENTITY)); // { x: 0, y: 0, z: 0, w: 1 }

View file

@ -21,7 +21,7 @@
namespace SceneScripting { namespace SceneScripting {
/**jsdoc /**jsdoc
* @typedef Scene.Stage.Location * @typedef {object} Scene.Stage.Location
* @property {number} longitude * @property {number} longitude
* @property {number} latitude * @property {number} latitude
* @property {number} altitude * @property {number} altitude
@ -49,7 +49,7 @@ namespace SceneScripting {
using LocationPointer = std::unique_ptr<Location>; using LocationPointer = std::unique_ptr<Location>;
/**jsdoc /**jsdoc
* @typedef Scene.Stage.Time * @typedef {object} Scene.Stage.Time
* @property {number} hour * @property {number} hour
* @property {number} day * @property {number} day
*/ */
@ -73,7 +73,7 @@ namespace SceneScripting {
using TimePointer = std::unique_ptr<Time>; using TimePointer = std::unique_ptr<Time>;
/**jsdoc /**jsdoc
* @typedef Scene.Stage.KeyLight * @typedef {object} Scene.Stage.KeyLight
* @property {Vec3} color * @property {Vec3} color
* @property {number} intensity * @property {number} intensity
* @property {number} ambientIntensity * @property {number} ambientIntensity

View file

@ -558,6 +558,16 @@ static void scriptableResourceFromScriptValue(const QScriptValue& value, Scripta
resource = static_cast<ScriptableResourceRawPtr>(value.toQObject()); resource = static_cast<ScriptableResourceRawPtr>(value.toQObject());
} }
/**jsdoc
* @namespace Resource
*
* @hifi-interface
* @hifi-client-entity
* @hifi-server-entity
* @hifi-assignment-client
*
* @property {Resource.State} State
*/
static QScriptValue createScriptableResourcePrototype(ScriptEnginePointer engine) { static QScriptValue createScriptableResourcePrototype(ScriptEnginePointer engine) {
auto prototype = engine->newObject(); auto prototype = engine->newObject();

View file

@ -30,7 +30,7 @@
* @hifi-server-entity * @hifi-server-entity
* @hifi-assignment-client * @hifi-assignment-client
* *
* @property NULL {Uuid} The null UUID, <code>{00000000-0000-0000-0000-000000000000}</code>. * @property {Uuid} NULL - The null UUID, <code>{00000000-0000-0000-0000-000000000000}</code>.
*/ */
/// Scriptable interface for a UUID helper class object. Used exclusively in the JavaScript API /// Scriptable interface for a UUID helper class object. Used exclusively in the JavaScript API

View file

@ -42,8 +42,7 @@
/**jsdoc /**jsdoc
* The Vec3 API facilities for generating and manipulating 3-dimensional vectors. High Fidelity uses a right-handed * The Vec3 API facilities for generating and manipulating 3-dimensional vectors. High Fidelity uses a right-handed
* Cartesian coordinate system where the y-axis is the "up" and the negative z-axis is the "front" direction. * Cartesian coordinate system where the y-axis is the "up" and the negative z-axis is the "front" direction.
* <img alt="High Fidelity coordinate system" * <img alt="High Fidelity coordinate system" src="https://docs.highfidelity.com/images/opengl-coord-system.jpg" />
* src="https://docs.highfidelity.com/user/pages/06.api-reference/43.vec3/opengl-coord-system.jpg" />
* *
* @namespace Vec3 * @namespace Vec3
* @variation 0 * @variation 0

View file

@ -34,13 +34,13 @@ enum CollisionFilterGroups {
* *
*/ */
const int16_t BULLET_COLLISION_GROUP_STATIC = 1 << 0; const int32_t BULLET_COLLISION_GROUP_STATIC = 1 << 0;
const int16_t BULLET_COLLISION_GROUP_DYNAMIC = 1 << 1; const int32_t BULLET_COLLISION_GROUP_DYNAMIC = 1 << 1;
const int16_t BULLET_COLLISION_GROUP_KINEMATIC = 1 << 2; const int32_t BULLET_COLLISION_GROUP_KINEMATIC = 1 << 2;
const int16_t BULLET_COLLISION_GROUP_MY_AVATAR = 1 << 3; const int32_t BULLET_COLLISION_GROUP_MY_AVATAR = 1 << 3;
const int16_t BULLET_COLLISION_GROUP_OTHER_AVATAR = 1 << 4; const int32_t BULLET_COLLISION_GROUP_OTHER_AVATAR = 1 << 4;
// ... // ...
const int16_t BULLET_COLLISION_GROUP_COLLISIONLESS = 1 << 14; const int32_t BULLET_COLLISION_GROUP_COLLISIONLESS = 1 << 31;
/* Note: In order for objectA to collide with objectB at the filter stage /* Note: In order for objectA to collide with objectB at the filter stage
@ -48,21 +48,21 @@ const int16_t BULLET_COLLISION_GROUP_COLLISIONLESS = 1 << 14;
*/ */
// the default collision mask is: collides with everything except collisionless // the default collision mask is: collides with everything except collisionless
const int16_t BULLET_COLLISION_MASK_DEFAULT = ~ BULLET_COLLISION_GROUP_COLLISIONLESS; const int32_t BULLET_COLLISION_MASK_DEFAULT = ~ BULLET_COLLISION_GROUP_COLLISIONLESS;
// STATIC does not collide with itself (as optimization of physics simulation) // STATIC does not collide with itself (as optimization of physics simulation)
const int16_t BULLET_COLLISION_MASK_STATIC = ~ (BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_KINEMATIC | BULLET_COLLISION_GROUP_STATIC); const int32_t BULLET_COLLISION_MASK_STATIC = ~ (BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_KINEMATIC | BULLET_COLLISION_GROUP_STATIC);
const int16_t BULLET_COLLISION_MASK_DYNAMIC = BULLET_COLLISION_MASK_DEFAULT; const int32_t BULLET_COLLISION_MASK_DYNAMIC = BULLET_COLLISION_MASK_DEFAULT;
const int16_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_STATIC; const int32_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_STATIC;
// MY_AVATAR does not collide with itself // MY_AVATAR does not collide with itself
const int16_t BULLET_COLLISION_MASK_MY_AVATAR = ~(BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_MY_AVATAR); const int32_t BULLET_COLLISION_MASK_MY_AVATAR = ~(BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_MY_AVATAR);
const int16_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_MASK_DEFAULT; const int32_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_MASK_DEFAULT;
// COLLISIONLESS gets an empty mask. // COLLISIONLESS gets an empty mask.
const int16_t BULLET_COLLISION_MASK_COLLISIONLESS = 0; const int32_t BULLET_COLLISION_MASK_COLLISIONLESS = 0;
/**jsdoc /**jsdoc
* <p>An entity may collide with the following types of items:</p> * <p>An entity may collide with the following types of items:</p>
@ -72,35 +72,35 @@ const int16_t BULLET_COLLISION_MASK_COLLISIONLESS = 0;
* </thead> * </thead>
* <tbody> * <tbody>
* <tr><td><code>1</code></td><td>Static entities &mdash; non-dynamic entities with no velocity.</td></tr> * <tr><td><code>1</code></td><td>Static entities &mdash; non-dynamic entities with no velocity.</td></tr>
* <tr><td><code>2</code></td><td>Dynamic entities &mdash; entities that have their <code>dynamic</code> property set to * <tr><td><code>2</code></td><td>Dynamic entities &mdash; entities that have their <code>dynamic</code> property set to
* <code>true</code>.</td></tr> * <code>true</code>.</td></tr>
* <tr><td><code>4</code></td><td>Kinematic entities &mdash; non-dynamic entities with velocity.</td></tr> * <tr><td><code>4</code></td><td>Kinematic entities &mdash; non-dynamic entities with velocity.</td></tr>
* <tr><td><code>8</code></td><td>My avatar.</td></tr> * <tr><td><code>8</code></td><td>My avatar.</td></tr>
* <tr><td><code>16</code></td><td>Other avatars.</td></tr> * <tr><td><code>16</code></td><td>Other avatars.</td></tr>
* </tbody> * </tbody>
* </table> * </table>
* <p>The values for the collision types that are enabled are added together to give the CollisionMask value. For example, a * <p>The values for the collision types that are enabled are added together to give the CollisionMask value. For example, a
* value of <code>31</code> means that an entity will collide with all item types.</p> * value of <code>31</code> means that an entity will collide with all item types.</p>
* @typedef {number} Entities.CollisionMask * @typedef {number} Entities.CollisionMask
*/ */
// The USER collision groups are exposed to script and can be used to generate per-object collision masks. // The USER collision groups are exposed to script and can be used to generate per-object collision masks.
// They are not necessarily the same as the BULLET_COLLISION_GROUPS, but we start them off with matching numbers. // They are not necessarily the same as the BULLET_COLLISION_GROUPS, but we start them off with matching numbers.
const uint8_t USER_COLLISION_GROUP_STATIC = 1 << 0; const uint16_t USER_COLLISION_GROUP_STATIC = 1 << 0;
const uint8_t USER_COLLISION_GROUP_DYNAMIC = 1 << 1; const uint16_t USER_COLLISION_GROUP_DYNAMIC = 1 << 1;
const uint8_t USER_COLLISION_GROUP_KINEMATIC = 1 << 2; const uint16_t USER_COLLISION_GROUP_KINEMATIC = 1 << 2;
const uint8_t USER_COLLISION_GROUP_MY_AVATAR = 1 << 3; const uint16_t USER_COLLISION_GROUP_MY_AVATAR = 1 << 3;
const uint8_t USER_COLLISION_GROUP_OTHER_AVATAR = 1 << 4; const uint16_t USER_COLLISION_GROUP_OTHER_AVATAR = 1 << 4;
const uint8_t ENTITY_COLLISION_MASK_DEFAULT = const uint16_t ENTITY_COLLISION_MASK_DEFAULT =
USER_COLLISION_GROUP_STATIC | USER_COLLISION_GROUP_STATIC |
USER_COLLISION_GROUP_DYNAMIC | USER_COLLISION_GROUP_DYNAMIC |
USER_COLLISION_GROUP_KINEMATIC | USER_COLLISION_GROUP_KINEMATIC |
USER_COLLISION_GROUP_MY_AVATAR | USER_COLLISION_GROUP_MY_AVATAR |
USER_COLLISION_GROUP_OTHER_AVATAR; USER_COLLISION_GROUP_OTHER_AVATAR;
const uint8_t USER_COLLISION_MASK_AVATARS = USER_COLLISION_GROUP_MY_AVATAR | USER_COLLISION_GROUP_OTHER_AVATAR; const uint16_t USER_COLLISION_MASK_AVATARS = USER_COLLISION_GROUP_MY_AVATAR | USER_COLLISION_GROUP_OTHER_AVATAR;
const int NUM_USER_COLLISION_GROUPS = 5; const int32_t NUM_USER_COLLISION_GROUPS = 5;
#endif // hifi_PhysicsCollisionGroups_h #endif // hifi_PhysicsCollisionGroups_h

View file

@ -61,7 +61,7 @@ glm::quat computeBulletRotationStep(const glm::vec3& angularVelocity, float time
} }
/* end Bullet code derivation*/ /* end Bullet code derivation*/
int16_t Physics::getDefaultCollisionMask(int16_t group) { int32_t Physics::getDefaultCollisionMask(int32_t group) {
switch(group) { switch(group) {
case BULLET_COLLISION_GROUP_STATIC: case BULLET_COLLISION_GROUP_STATIC:
return BULLET_COLLISION_MASK_STATIC; return BULLET_COLLISION_MASK_STATIC;

View file

@ -31,7 +31,7 @@ const float KINEMATIC_ANGULAR_SPEED_THRESHOLD = 0.008f; // ~0.5 deg/sec
glm::quat computeBulletRotationStep(const glm::vec3& angularVelocity, float timeStep); glm::quat computeBulletRotationStep(const glm::vec3& angularVelocity, float timeStep);
namespace Physics { namespace Physics {
int16_t getDefaultCollisionMask(int16_t group); int32_t getDefaultCollisionMask(int32_t group);
void setSessionUUID(const QUuid& sessionID); void setSessionUUID(const QUuid& sessionID);
const QUuid& getSessionUUID(); const QUuid& getSessionUUID();

View file

@ -44,13 +44,14 @@ class Camera : public QObject {
* @hifi-interface * @hifi-interface
* @hifi-client-entity * @hifi-client-entity
* *
* @property position {Vec3} The position of the camera. You can set this value only when the camera is in independent mode. * @property {Vec3} position - The position of the camera. You can set this value only when the camera is in independent
* @property orientation {Quat} The orientation of the camera. You can set this value only when the camera is in independent
* mode. * mode.
* @property mode {Camera.Mode} The camera mode. * @property {Quat} orientation - The orientation of the camera. You can set this value only when the camera is in
* @property frustum {ViewFrustum} The camera frustum. * independent mode.
* @property cameraEntity {Uuid} The ID of the entity that is used for the camera position and orientation when the camera * @property {Camera.Mode} mode - The camera mode.
* is in entity mode. * @property {ViewFrustum} frustum - The camera frustum.
* @property {Uuid} cameraEntity - The ID of the entity that is used for the camera position and orientation when the
* camera is in entity mode.
*/ */
// FIXME: The cameraEntity property definition is copied from FancyCamera.h. // FIXME: The cameraEntity property definition is copied from FancyCamera.h.
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)

View file

@ -12,6 +12,8 @@
#ifndef hifi_task_Config_h #ifndef hifi_task_Config_h
#define hifi_task_Config_h #define hifi_task_Config_h
#include <chrono>
#include <QtCore/qobject.h> #include <QtCore/qobject.h>
#include <QtCore/qjsondocument.h> #include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h> #include <QtCore/qjsonobject.h>
@ -117,11 +119,19 @@ public:
*/ */
Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); emit loaded(); } Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); emit loaded(); }
Q_INVOKABLE QObject* getConfig(const QString& name) { return nullptr; }
// Running Time measurement // Running Time measurement
// The new stats signal is emitted once per run time of a job when stats (cpu runtime) are updated // The new stats signal is emitted once per run time of a job when stats (cpu runtime) are updated
void setCPURunTime(double mstime) { _msCPURunTime = mstime; emit newStats(); } void setCPURunTime(const std::chrono::nanoseconds& runtime) { _msCPURunTime = std::chrono::duration<double, std::milli>(runtime).count(); emit newStats(); }
double getCPURunTime() const { return _msCPURunTime; } double getCPURunTime() const { return _msCPURunTime; }
// Describe the node graph data connections of the associated Job/Task
Q_INVOKABLE virtual bool isTask() const { return false; }
Q_INVOKABLE virtual QObjectList getSubConfigs() const { return QObjectList(); }
Q_INVOKABLE virtual int getNumSubs() const { return 0; }
Q_INVOKABLE virtual QObject* getSubConfig(int i) const { return nullptr; }
public slots: public slots:
/**jsdoc /**jsdoc
@ -151,6 +161,8 @@ signals:
void dirtyEnabled(); void dirtyEnabled();
}; };
using QConfigPointer = std::shared_ptr<JobConfig>;
class TConfigProxy { class TConfigProxy {
public: public:
using Config = JobConfig; using Config = JobConfig;
@ -173,9 +185,10 @@ public:
using Persistent = PersistentConfig<TaskConfig>; using Persistent = PersistentConfig<TaskConfig>;
TaskConfig() = default ; TaskConfig() = default;
TaskConfig(bool enabled) : JobConfig(enabled) {} TaskConfig(bool enabled) : JobConfig(enabled) {}
/**jsdoc /**jsdoc
* @function Render.getConfig * @function Render.getConfig
* @param {string} name * @param {string} name
@ -212,6 +225,21 @@ public:
return root->findChild<typename T::Config*>(tokens.front()); return root->findChild<typename T::Config*>(tokens.front());
} }
Q_INVOKABLE bool isTask() const override { return true; }
Q_INVOKABLE QObjectList getSubConfigs() const override {
auto list = findChildren<JobConfig*>(QRegExp(".*"), Qt::FindDirectChildrenOnly);
QObjectList returned;
for (int i = 0; i < list.size(); i++) {
returned.push_back(list[i]);
}
return returned;
}
Q_INVOKABLE int getNumSubs() const override { return getSubConfigs().size(); }
Q_INVOKABLE QObject* getSubConfig(int i) const override {
auto subs = getSubConfigs();
return ((i < 0 || i >= subs.size()) ? nullptr : subs[i]);
}
void connectChildConfig(QConfigPointer childConfig, const std::string& name); void connectChildConfig(QConfigPointer childConfig, const std::string& name);
void transferChildrenConfigs(QConfigPointer source); void transferChildrenConfigs(QConfigPointer source);
@ -225,8 +253,6 @@ public slots:
void refresh(); void refresh();
}; };
using QConfigPointer = std::shared_ptr<QObject>;
} }
#endif // hifi_task_Config_h #endif // hifi_task_Config_h

View file

@ -12,9 +12,7 @@
using namespace task; using namespace task;
JobContext::JobContext(const QLoggingCategory& category) : JobContext::JobContext() {
profileCategory(category) {
assert(&category);
} }
JobContext::~JobContext() { JobContext::~JobContext() {

View file

@ -15,20 +15,15 @@
#include "Config.h" #include "Config.h"
#include "Varying.h" #include "Varying.h"
#include "SettingHandle.h"
#include <Profile.h>
#include <PerfStat.h>
namespace task { namespace task {
class JobConcept; class JobConcept;
template <class JC> class JobT; template <class JC, class TP> class JobT;
template <class JC> class TaskT; template <class JC, class TP> class TaskT;
class JobNoIO {}; class JobNoIO {};
// Task Flow control class is a simple per value object used to communicate flow control commands trhough the graph of tasks. // Task Flow control class is a simple per value object used to communicate flow control commands trhough the graph of tasks.
// From within the Job::Run function, you can access it from the JobCOntext and issue commands which will be picked up by the Task calling for the Job run. // From within the Job::Run function, you can access it from the JobContext and issue commands which will be picked up by the Task calling for the Job run.
// This is first introduced to provide a way to abort all the work from within a task job. see the "abortTask" call // This is first introduced to provide a way to abort all the work from within a task job. see the "abortTask" call
class TaskFlow { class TaskFlow {
public: public:
@ -55,11 +50,10 @@ protected:
// The JobContext can be derived to add more global state to it that Jobs can access // The JobContext can be derived to add more global state to it that Jobs can access
class JobContext { class JobContext {
public: public:
JobContext(const QLoggingCategory& category); JobContext();
virtual ~JobContext(); virtual ~JobContext();
std::shared_ptr<JobConfig> jobConfig { nullptr }; std::shared_ptr<JobConfig> jobConfig { nullptr };
const QLoggingCategory& profileCategory;
// Task flow control // Task flow control
TaskFlow taskFlow{}; TaskFlow taskFlow{};
@ -80,10 +74,11 @@ public:
virtual const Varying getInput() const { return Varying(); } virtual const Varying getInput() const { return Varying(); }
virtual const Varying getOutput() const { return Varying(); } virtual const Varying getOutput() const { return Varying(); }
virtual Varying& editInput() = 0;
virtual QConfigPointer& getConfiguration() { return _config; } virtual QConfigPointer& getConfiguration() { return _config; }
virtual void applyConfiguration() = 0; virtual void applyConfiguration() = 0;
void setCPURunTime(double mstime) { std::static_pointer_cast<Config>(_config)->setCPURunTime(mstime); } void setCPURunTime(const std::chrono::nanoseconds& runtime) { std::static_pointer_cast<Config>(_config)->setCPURunTime(runtime); }
QConfigPointer _config; QConfigPointer _config;
protected: protected:
@ -114,10 +109,11 @@ template <class T, class JC, class I, class O> void jobRun(T& data, const JC& jo
data.run(jobContext, input, output); data.run(jobContext, input, output);
} }
template <class JC> template <class JC, class TP>
class Job { class Job {
public: public:
using Context = JC; using Context = JC;
using TimeProfiler = TP;
using ContextPointer = std::shared_ptr<Context>; using ContextPointer = std::shared_ptr<Context>;
using Config = JobConfig; using Config = JobConfig;
using None = JobNoIO; using None = JobNoIO;
@ -143,6 +139,7 @@ public:
const Varying getInput() const override { return _input; } const Varying getInput() const override { return _input; }
const Varying getOutput() const override { return _output; } const Varying getOutput() const override { return _output; }
Varying& editInput() override { return _input; }
template <class... A> template <class... A>
Model(const std::string& name, const Varying& input, QConfigPointer config, A&&... args) : Model(const std::string& name, const Varying& input, QConfigPointer config, A&&... args) :
@ -160,7 +157,7 @@ public:
void applyConfiguration() override { void applyConfiguration() override {
Duration profileRange(trace_render(), ("configure::" + JobConcept::getName()).c_str()); TimeProfiler probe(("configure::" + JobConcept::getName()));
jobConfigure(_data, *std::static_pointer_cast<C>(Concept::_config)); jobConfigure(_data, *std::static_pointer_cast<C>(Concept::_config));
} }
@ -185,6 +182,9 @@ public:
QConfigPointer& getConfiguration() const { return _concept->getConfiguration(); } QConfigPointer& getConfiguration() const { return _concept->getConfiguration(); }
void applyConfiguration() { return _concept->applyConfiguration(); } void applyConfiguration() { return _concept->applyConfiguration(); }
template <class I> void feedInput(const I& in) { _concept->editInput().template edit<I>() = in; }
template <class I, class S> void feedInput(int index, const S& inS) { (_concept->editInput().template editN<I>(index)).template edit<S>() = inS; }
template <class T> T& edit() { template <class T> T& edit() {
auto concept = std::static_pointer_cast<typename T::JobModel>(_concept); auto concept = std::static_pointer_cast<typename T::JobModel>(_concept);
assert(concept); assert(concept);
@ -198,14 +198,10 @@ public:
} }
virtual void run(const ContextPointer& jobContext) { virtual void run(const ContextPointer& jobContext) {
PerformanceTimer perfTimer(getName().c_str()); TimeProfiler probe(getName());
// NOTE: rather than use the PROFILE_RANGE macro, we create a Duration manually auto startTime = std::chrono::high_resolution_clock::now();
Duration profileRange(jobContext->profileCategory, ("run::" + getName()).c_str());
auto start = usecTimestampNow();
_concept->run(jobContext); _concept->run(jobContext);
_concept->setCPURunTime((std::chrono::high_resolution_clock::now() - startTime));
_concept->setCPURunTime((double)(usecTimestampNow() - start) / 1000.0);
} }
protected: protected:
@ -220,13 +216,14 @@ protected:
// The build method is where child Jobs can be added internally to the task // The build method is where child Jobs can be added internally to the task
// where the input of the task can be setup to feed the child jobs // where the input of the task can be setup to feed the child jobs
// and where the output of the task is defined // and where the output of the task is defined
template <class JC> template <class JC, class TP>
class Task : public Job<JC> { class Task : public Job<JC, TP> {
public: public:
using Context = JC; using Context = JC;
using TimeProfiler = TP;
using ContextPointer = std::shared_ptr<Context>; using ContextPointer = std::shared_ptr<Context>;
using Config = TaskConfig; using Config = TaskConfig;
using JobType = Job<JC>; using JobType = Job<JC, TP>;
using None = typename JobType::None; using None = typename JobType::None;
using Concept = typename JobType::Concept; using Concept = typename JobType::Concept;
using ConceptPointer = typename JobType::ConceptPointer; using ConceptPointer = typename JobType::ConceptPointer;
@ -242,6 +239,8 @@ public:
const Varying getInput() const override { return _input; } const Varying getInput() const override { return _input; }
const Varying getOutput() const override { return _output; } const Varying getOutput() const override { return _output; }
Varying& editInput() override { return _input; }
typename Jobs::iterator editJob(std::string name) { typename Jobs::iterator editJob(std::string name) {
typename Jobs::iterator jobIt; typename Jobs::iterator jobIt;
for (jobIt = _jobs.begin(); jobIt != _jobs.end(); ++jobIt) { for (jobIt = _jobs.begin(); jobIt != _jobs.end(); ++jobIt) {
@ -295,7 +294,7 @@ public:
auto model = std::make_shared<TaskModel>(name, input, std::make_shared<C>()); auto model = std::make_shared<TaskModel>(name, input, std::make_shared<C>());
{ {
Duration profileRange(trace_render(), ("build::" + model->getName()).c_str()); TimeProfiler probe("build::" + model->getName());
model->_data.build(*(model), model->_input, model->_output, std::forward<A>(args)...); model->_data.build(*(model), model->_input, model->_output, std::forward<A>(args)...);
} }
// Recreate the Config to use the templated type // Recreate the Config to use the templated type
@ -330,7 +329,7 @@ public:
} }
void applyConfiguration() override { void applyConfiguration() override {
Duration profileRange(trace_render(), ("configure::" + JobConcept::getName()).c_str()); TimeProfiler probe("configure::" + JobConcept::getName());
jobConfigure(_data, *std::static_pointer_cast<C>(Concept::_config)); jobConfigure(_data, *std::static_pointer_cast<C>(Concept::_config));
for (auto& job : TaskConcept::_jobs) { for (auto& job : TaskConcept::_jobs) {
job.applyConfiguration(); job.applyConfiguration();
@ -370,15 +369,44 @@ public:
protected: protected:
}; };
template <class JC, class TP>
class Engine : public Task<JC, TP> {
public:
using Context = JC;
using ContextPointer = std::shared_ptr<Context>;
using Config = TaskConfig;
using TaskType = Task<JC, TP>;
using ConceptPointer = typename TaskType::ConceptPointer;
Engine(const ConceptPointer& concept, const ContextPointer& context) : TaskType(concept), _context(context) {}
~Engine() = default;
void reset(const ContextPointer& context) { _context = context; }
void run() {
if (_context) {
run(_context);
}
}
protected:
void run(const ContextPointer& jobContext) override {
TaskType::run(_context);
}
ContextPointer _context;
};
} }
#define Task_DeclareTypeAliases(ContextType, TimeProfiler) \
#define Task_DeclareTypeAliases(ContextType) \
using JobConfig = task::JobConfig; \ using JobConfig = task::JobConfig; \
using TaskConfig = task::TaskConfig; \ using TaskConfig = task::TaskConfig; \
template <class T> using PersistentConfig = task::PersistentConfig<T>; \ template <class T> using PersistentConfig = task::PersistentConfig<T>; \
using Job = task::Job<ContextType>; \ using Job = task::Job<ContextType, TimeProfiler>; \
using Task = task::Task<ContextType>; \ using Task = task::Task<ContextType, TimeProfiler>; \
using Engine = task::Engine<ContextType, TimeProfiler>; \
using Varying = task::Varying; \ using Varying = task::Varying; \
template < typename T0, typename T1 > using VaryingSet2 = task::VaryingSet2<T0, T1>; \ template < typename T0, typename T1 > using VaryingSet2 = task::VaryingSet2<T0, T1>; \
template < typename T0, typename T1, typename T2 > using VaryingSet3 = task::VaryingSet3<T0, T1, T2>; \ template < typename T0, typename T1, typename T2 > using VaryingSet3 = task::VaryingSet3<T0, T1, T2>; \
@ -389,4 +417,16 @@ protected:
template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7 > using VaryingSet8 = task::VaryingSet8<T0, T1, T2, T3, T4, T5, T6, T7>; \ template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7 > using VaryingSet8 = task::VaryingSet8<T0, T1, T2, T3, T4, T5, T6, T7>; \
template < class T, int NUM > using VaryingArray = task::VaryingArray<T, NUM>; template < class T, int NUM > using VaryingArray = task::VaryingArray<T, NUM>;
#include <Profile.h>
#include <PerfStat.h>
#define Task_DeclareCategoryTimeProfilerClass(className, category) \
class className : public PerformanceTimer { \
public: \
className(const std::string& label) : PerformanceTimer(label.c_str()), profileRange(category(), label.c_str()) {} \
Duration profileRange; \
};
#endif // hifi_task_Task_h #endif // hifi_task_Task_h

View file

@ -20,7 +20,7 @@ public:
/**jsdoc /**jsdoc
* Creates a new button, adds it to this and returns it. * Creates a new button, adds it to this and returns it.
* @function QmlFragmentClass#addButton * @function QmlFragmentClass#addButton
* @param properties {Object} button properties * @param properties {object} button properties
* @returns {TabletButtonProxy} * @returns {TabletButtonProxy}
*/ */
Q_INVOKABLE QObject* addButton(const QVariant& properties); Q_INVOKABLE QObject* addButton(const QVariant& properties);

View file

@ -493,7 +493,7 @@ protected:
int _stableOrder; int _stableOrder;
/**jsdoc /**jsdoc
* @typedef TabletButtonProxy.ButtonProperties * @typedef {object} TabletButtonProxy.ButtonProperties
* @property {string} icon - URL to button icon. (50 x 50) * @property {string} icon - URL to button icon. (50 x 50)
* @property {string} hoverIcon - URL to button icon, displayed during mouse hover. (50 x 50) * @property {string} hoverIcon - URL to button icon, displayed during mouse hover. (50 x 50)
* @property {string} activeHoverIcon - URL to button icon used when button is active, and during mouse hover. (50 x 50) * @property {string} activeHoverIcon - URL to button icon used when button is active, and during mouse hover. (50 x 50)

View file

@ -442,7 +442,7 @@ void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) {
* <tr><td><code>RightHand</code></td><td>number</td><td>{@link Pose}</td><td>right hand pose.</td></tr> * <tr><td><code>RightHand</code></td><td>number</td><td>{@link Pose}</td><td>right hand pose.</td></tr>
* </tbody> * </tbody>
* </table> * </table>
* @typedef Controller.Hardware-OculusTouch * @typedef {object} Controller.Hardware-OculusTouch
*/ */
controller::Input::NamedVector OculusControllerManager::TouchDevice::getAvailableInputs() const { controller::Input::NamedVector OculusControllerManager::TouchDevice::getAvailableInputs() const {
using namespace controller; using namespace controller;

View file

@ -1297,7 +1297,7 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu
* <tr><td><code>TrackedObject15</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 15 pose.</td></tr> * <tr><td><code>TrackedObject15</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 15 pose.</td></tr>
* </tbody> * </tbody>
* </table> * </table>
* @typedef Controller.Hardware-Vive * @typedef {object} Controller.Hardware-Vive
*/ */
controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const { controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const {
using namespace controller; using namespace controller;

View file

@ -0,0 +1,82 @@
//
// Job Engine & Task...
// jet.js
//
// Created by Sam Gateau, 2018/03/28
// 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
//
"use strict";
// traverse task tree
function task_traverse(root, functor, depth) {
if (root.isTask()) {
depth++;
for (var i = 0; i <root.getNumSubs(); i++) {
var sub = root.getSubConfig(i);
if (functor(sub, depth, i)) {
task_traverse(sub, functor, depth, 0)
}
}
}
}
function task_traverseTree(root, functor) {
if (functor(root, 0, 0)) {
task_traverse(root, functor, 0)
}
}
// Access job properties
// return all the properties of a job
function job_propKeys(job) {
var keys = Object.keys(job)
var propKeys = [];
for (var k=0; k < keys.length;k++) {
// Filter for relevant property
var key = keys[k]
if ((typeof job[key]) !== "function") {
if ((key !== "objectName") && (key !== "cpuRunTime") && (key !== "enabled")) {
propKeys.push(keys[k]);
}
}
}
return propKeys;
}
// Use this function to create a functor that will fill the specifed array with one entry name per task and job and it s rank
function job_list_functor(jobList, maxDepth) {
if (maxDepth === undefined) maxDepth = 100
return function (job, depth, index) {
jobList.push(job.objectName);
return depth < maxDepth;
}
}
// Use this function to create a functor that will print the content of the Job visited calling the specified 'printout' function
function job_print_functor(printout, showProps, maxDepth) {
if (maxDepth === undefined) maxDepth = 100
return function (job, depth, index) {
var tab = " "
var depthTab = "";
for (var d = 0; d < depth; d++) { depthTab += tab }
printout(depthTab + index + " " + job.objectName + " " + (job.enabled ? "on " : "off ") + job.cpuRunTime + "ms")
if (showProps) {
var keys = job_propKeys(job);
for (var p=0; p < keys.length;p++) {
var prop = job[keys[p]]
printout(depthTab + tab + tab + typeof prop + " " + keys[p] + " " + prop);
}
}
return depth < maxDepth;
}
}
// Expose functions for regular js including this files through the 'Jet' object
/*Jet = {}
Jet.task_traverse = task_traverse
Jet.task_traverseTree = task_traverseTree
Jet.job_propKeys = job_propKeys
Jet.job_print_functor = job_print_functor
*/

View file

@ -0,0 +1,45 @@
//
// jet/TaskList.qml
//
// Created by Sam Gateau, 2018/03/28
// 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.7
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import "qrc:///qml/styles-uit"
import "qrc:///qml/controls-uit" as HifiControls
import "../jet.js" as Jet
Rectangle {
HifiConstants { id: hifi;}
color: hifi.colors.baseGray;
id: root
// width: parent ? parent.width : 200
// height: parent ? parent.height : 400
property var rootConfig : Workload
Original.TextArea {
id: textArea
width: parent.width
height: parent.height
text: ""
}
Component.onCompleted: {
var message = ""
var functor = Jet.job_print_functor(function (line) { message += line + "\n"; }, false);
Jet.task_traverseTree(rootConfig, functor);
textArea.append(message);
}
function clearWindow() {
textArea.remove(0,textArea.length);
}
}

View file

@ -0,0 +1,119 @@
//
// jet/TaskListView.qml
//
// Created by Sam Gateau, 2018/05/09
// 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.7
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import "qrc:///qml/styles-uit"
import "qrc:///qml/controls-uit" as HifiControls
import "../jet.js" as Jet
Rectangle {
HifiConstants { id: hifi;}
color: hifi.colors.baseGray;
id: root;
property var rootConfig : Workload
property var myArray : []
Component.onCompleted: {
var message = ""
var maxDepth = 3;
var jobTreePath = []
var jobsRoot;
var functor = function (job, depth, index) {
var newItem = {"name": job.objectName, "level": depth, "index": index, "subNode": [], "init": depth < maxDepth, "path": ""}
if (depth == 0) {
jobsModel.append(newItem)
jobsRoot = jobsModel.get(0).subNode;
} else {
if (jobTreePath.length < depth) {
var node = jobsRoot;
var path;
for (var n = 0; n < jobTreePath.length; n++) {
newItem.path += (n > 0 ? "." : "") + node.get(jobTreePath[n]).name
node = node.get(jobTreePath[n]).subNode
}
node.append(newItem)
jobTreePath.push(0);
} else if (jobTreePath.length >= depth) {
var node = jobsRoot;
for (var n = 0; n < (depth - 1); n++) {
newItem.path += (n > 0 ? "." : "") + node.get(jobTreePath[n]).name
node = node.get(jobTreePath[n]).subNode
}
node.append(newItem)
jobTreePath[depth-1] = index;
while (jobTreePath.length > depth) {
jobTreePath.pop();
}
}
}
return true;
}
Jet.task_traverseTree(rootConfig, functor);
}
ListModel {
id: jobsModel
}
Component {
id: objRecursiveDelegate
Column {
id: objRecursiveColumn
clip: true
visible: model.init
MouseArea {
width: objRow.implicitWidth
height: objRow.implicitHeight
onDoubleClicked: {
for(var i = 1; i < parent.children.length - 1; ++i) {
parent.children[i].visible = !parent.children[i].visible
}
}
Row {
id: objRow
Item {
height: 1
width: model.level * 15
}
HifiControls.CheckBox {
property var config: root.rootConfig.getConfig(model.path + "." + model.name);
text: (objRecursiveColumn.children.length > 2 ?
objRecursiveColumn.children[1].visible ?
qsTr("- ") : qsTr("+ ") : qsTr(" ")) + model.name + " ms=" + config.cpuRunTime.toFixed(2)
checked: config.enabled
}
}
}
Repeater {
model: subNode
delegate: objRecursiveDelegate
}
}
}
Original.ScrollView {
anchors.fill: parent
ListView {
id: theView
model: jobsModel
delegate: objRecursiveDelegate
}
}
}

View file

@ -0,0 +1,2 @@
TaskList 1.0 TaskList.qml
TaskViewList 1.0 TaskViewList.qml

View file

@ -23,13 +23,22 @@ Item {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
height: 24 height: 24
property var labelAreaWidthScale: 0.5
property bool integral: false property bool integral: false
property var config property var config
property string property property string property
property alias label: labelControl.text
property alias min: sliderControl.minimumValue property alias min: sliderControl.minimumValue
property alias max: sliderControl.maximumValue property alias max: sliderControl.maximumValue
property alias label: labelControl.text
property bool showLabel: true
property bool showValue: true
signal valueChanged(real value) signal valueChanged(real value)
Component.onCompleted: { Component.onCompleted: {
@ -41,20 +50,12 @@ Item {
HifiControls.Label { HifiControls.Label {
id: labelControl id: labelControl
text: root.label text: root.label
enabled: true enabled: root.showLabel
anchors.left: root.left anchors.left: root.left
anchors.right: root.horizontalCenter width: root.width * root.labelAreaWidthScale
anchors.verticalCenter: root.verticalCenter anchors.verticalCenter: root.verticalCenter
} }
HifiControls.Label {
id: labelValue
text: sliderControl.value.toFixed(root.integral ? 0 : 2)
anchors.right: root.right
anchors.bottom: root.bottom
anchors.bottomMargin: 0
}
Binding { Binding {
id: bindingControl id: bindingControl
target: root.config target: root.config
@ -66,7 +67,7 @@ Item {
HifiControls.Slider { HifiControls.Slider {
id: sliderControl id: sliderControl
stepSize: root.integral ? 1.0 : 0.0 stepSize: root.integral ? 1.0 : 0.0
anchors.left: root.horizontalCenter anchors.left: labelControl.right
anchors.right: root.right anchors.right: root.right
anchors.rightMargin: 0 anchors.rightMargin: 0
anchors.top: root.top anchors.top: root.top
@ -74,4 +75,17 @@ Item {
onValueChanged: { root.valueChanged(value) } onValueChanged: { root.valueChanged(value) }
} }
HifiControls.Label {
id: labelValue
enabled: root.showValue
text: sliderControl.value.toFixed(root.integral ? 0 : 2)
anchors.right: labelControl.right
anchors.rightMargin: 5
anchors.verticalCenter: root.verticalCenter
}
} }

View file

@ -36,9 +36,9 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
spacing: 20 spacing: 5
Column { Column {
spacing: 10 spacing: 5
// padding: 10 // padding: 10
Repeater { Repeater {
model: [ model: [
@ -61,7 +61,7 @@ Rectangle {
Column { Column {
spacing: 10 spacing: 5
Repeater { Repeater {
model: [ model: [
"Obscurance:LightingModel:enableObscurance", "Obscurance:LightingModel:enableObscurance",
@ -81,7 +81,7 @@ Rectangle {
} }
Column { Column {
spacing: 10 spacing: 5
Repeater { Repeater {
model: [ model: [
"Ambient:LightingModel:enableAmbientLight", "Ambient:LightingModel:enableAmbientLight",
@ -105,7 +105,7 @@ Rectangle {
Column { Column {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
spacing: 10 spacing: 5
Repeater { Repeater {
model: [ "Tone Mapping Exposure:ToneMapping:exposure:5.0:-5.0" model: [ "Tone Mapping Exposure:ToneMapping:exposure:5.0:-5.0"
] ]
@ -211,9 +211,9 @@ Rectangle {
Separator {} Separator {}
Row { Row {
spacing: 10 spacing: 5
Column { Column {
spacing: 10 spacing: 5
HifiControls.CheckBox { HifiControls.CheckBox {
boxSize: 20 boxSize: 20
@ -254,7 +254,7 @@ Rectangle {
} }
Column { Column {
spacing: 10 spacing: 5
HifiControls.CheckBox { HifiControls.CheckBox {
boxSize: 20 boxSize: 20
text: "Metas" text: "Metas"
@ -275,6 +275,13 @@ Rectangle {
} }
} }
} }
Separator {}
HifiControls.Button {
text: "Engine"
// activeFocusOnPress: false
onClicked: {
sendToScript({method: "openEngineView"});
}
}
} }
//}
} }

View file

@ -0,0 +1,13 @@
function openEngineTaskView() {
// Set up the qml ui
var qml = Script.resolvePath('engineInspector.qml');
var window = new OverlayWindow({
title: 'Render Engine',
source: qml,
width: 300,
height: 400
});
window.setPosition(200, 50);
//window.closed.connect(function() { Script.stop(); });
}
openEngineTaskView();

View file

@ -0,0 +1,30 @@
//
// deferredLighting.qml
//
// Created by Sam Gateau on 6/6/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import "qrc:///qml/styles-uit"
import "qrc:///qml/controls-uit" as HifiControls
import "../lib/jet/qml" as Jet
Item {
HifiConstants { id: hifi;}
id: render;
anchors.fill: parent
property var mainViewTask: Render.getConfig("RenderMainView")
Jet.TaskListView {
rootConfig: Render
anchors.fill: render
}
}

View file

@ -64,9 +64,6 @@
button.editProperties({isActive: onLuciScreen}); button.editProperties({isActive: onLuciScreen});
wireEventBridge(onLuciScreen); wireEventBridge(onLuciScreen);
} }
function fromQml(message) {
}
button.clicked.connect(onClicked); button.clicked.connect(onClicked);
tablet.screenChanged.connect(onScreenChanged); tablet.screenChanged.connect(onScreenChanged);
@ -82,14 +79,6 @@
Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); }); Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); });
Script.scriptEnding.connect(function () {
if (onLuciScreen) {
tablet.gotoHomeScreen();
}
button.clicked.disconnect(onClicked);
tablet.screenChanged.disconnect(onScreenChanged);
tablet.removeButton(button);
});
function setDebugCursor(x, y) { function setDebugCursor(x, y) {
nx = (x / Window.innerWidth); nx = (x / Window.innerWidth);
@ -98,4 +87,46 @@
Render.getConfig("RenderMainView").getConfig("Antialiasing").debugCursorTexcoord = { x: nx, y: ny }; Render.getConfig("RenderMainView").getConfig("Antialiasing").debugCursorTexcoord = { x: nx, y: ny };
} }
function fromQml(message) {
switch (message.method) {
case "openEngineView":
openEngineTaskView();
break;
}
}
var engineInspectorView = null
function openEngineTaskView() {
if (engineInspectorView == null) {
var qml = Script.resolvePath('engineInspector.qml');
var window = new OverlayWindow({
title: 'Render Engine',
source: qml,
width: 300,
height: 400
});
window.setPosition(200, 50);
engineInspectorView = window
window.closed.connect(function() { engineInspectorView = null; });
} else {
engineInspectorView.setPosition(200, 50);
}
}
Script.scriptEnding.connect(function () {
if (onLuciScreen) {
tablet.gotoHomeScreen();
}
button.clicked.disconnect(onClicked);
tablet.screenChanged.disconnect(onScreenChanged);
tablet.removeButton(button);
if (engineInspectorView !== null) {
engineInspectorView.close()
}
});
}()); }());

View file

@ -413,8 +413,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
this.cloneHotspot = function(props, controllerData) { this.cloneHotspot = function(props, controllerData) {
if (entityIsCloneable(props)) { if (entityIsCloneable(props)) {
var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; var cloneID = cloneEntity(props);
var cloneID = cloneEntity(props, worldEntityProps);
return cloneID; return cloneID;
} }

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