diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 3222e75c66..5836d5bfb5 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -9,15 +9,17 @@ Note: The prerequisites will require about 10 GB of space on your drive. You wil If you don’t have Community or Professional edition of Visual Studio 2017, download [Visual Studio Community 2017](https://www.visualstudio.com/downloads/). -When selecting components, check "Desktop development with C++." Also check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)" on the Summary toolbar on the right. +When selecting components, check "Desktop development with C++." Also on the right on the Summary toolbar, check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)". ### Step 2. Installing CMake -Download and install the latest version of CMake 3.9. Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). Make sure to check "Add CMake to system PATH for all users" when prompted during installation. +Download and install the latest version of CMake 3.9. + +Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.9 Version page](https://cmake.org/files/v3.9/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted. ### Step 3. Installing Qt -Download and install the [Qt Online Installer](https://www.qt.io/download-open-source/?hsCtaTracking=f977210e-de67-475f-a32b-65cec207fd03%7Cd62710cd-e1db-46aa-8d4d-2f1c1ffdacea). While installing, you only need to have the following components checked under Qt 5.10.1: "msvc2017 64-bit", "Qt WebEngine", and "Qt Script (Deprecated)". +Download and install the [Qt Open Source Online Installer](https://www.qt.io/download-open-source/?hsCtaTracking=f977210e-de67-475f-a32b-65cec207fd03%7Cd62710cd-e1db-46aa-8d4d-2f1c1ffdacea). While installing, you only need to have the following components checked under Qt 5.10.1: "msvc2017 64-bit", "Qt WebEngine", and "Qt Script (Deprecated)". Note: Installing the Sources is optional but recommended if you have room for them (~2GB). @@ -56,9 +58,9 @@ Where `%HIFI_DIR%` is the directory for the highfidelity repository. Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio. -Change the Solution Configuration (next to the green play button) from "Debug" to "Release" for best performance. +Change the Solution Configuration (menu ribbon under the menu bar, next to the green play button) from "Debug" to "Release" for best performance. -Run `Build > Build Solution`. +Run from the menu bar `Build > Build Solution`. ### Step 9. Testing Interface @@ -66,7 +68,7 @@ Create another environment variable (see Step #4) * Set "Variable name": `_NO_DEBUG_HEAP` * Set "Variable value": `1` -In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run `Debug > Start Debugging`. +In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run from the menu bar `Debug > Start Debugging`. Now, you should have a full build of High Fidelity and be able to run the Interface using Visual Studio. Please check our [Docs](https://wiki.highfidelity.com/wiki/Main_Page) for more information regarding the programming workflow. diff --git a/android/app/build.gradle b/android/app/build.gradle index 70f7c622a0..46de9642d9 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -27,6 +27,14 @@ android { '-DDISABLE_KTX_CACHE=OFF' } } + signingConfigs { + release { + storeFile project.hasProperty("HIFI_ANDROID_KEYSTORE") ? file(HIFI_ANDROID_KEYSTORE) : null + storePassword project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") ? HIFI_ANDROID_KEYSTORE_PASSWORD : '' + keyAlias project.hasProperty("HIFI_ANDROID_KEY_ALIAS") ? HIFI_ANDROID_KEY_ALIAS : '' + keyPassword project.hasProperty("HIFI_ANDROID_KEY_PASSWORD") ? HIFI_ANDROID_KEY_PASSWORD : '' + } + } } compileOptions { @@ -38,6 +46,10 @@ android { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig project.hasProperty("HIFI_ANDROID_KEYSTORE") && + project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") && + project.hasProperty("HIFI_ANDROID_KEY_ALIAS") && + project.hasProperty("HIFI_ANDROID_KEY_PASSWORD")? signingConfigs.release : null } } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e105f5bccf..0b52046057 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -49,12 +49,6 @@ android:label="@string/app_name" android:launchMode="singleTop" > - - - - - - diff --git a/android/build.gradle b/android/build.gradle index 1dfef97f91..3719e548bc 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -66,17 +66,17 @@ ext { def baseFolder = new File(HIFI_ANDROID_PRECOMPILED) def appDir = new File(projectDir, 'app') def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a') -def baseUrl = 'https://hifi-public.s3.amazonaws.com/austin/android/' +def baseUrl = '' -def qtFile='qt-5.9.3_linux_armv8-libcpp_openssl.tgz' +def qtFile='https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_linux_armv8-libcpp_openssl.tgz' def qtChecksum='04599670ccca84bd2b15f6915568eb2d' def qtVersionId='PeoqzN31n.YvLfs9JE2SgHgZ4.IaKAlt' if (Os.isFamily(Os.FAMILY_MAC)) { - qtFile = 'qt-5.9.3_osx_armv8-libcpp_openssl.tgz' + qtFile = 'https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_osx_armv8-libcpp_openssl.tgz' qtChecksum='4b02de9d67d6bfb202355a808d2d9c59' qtVersionId='HygCmtMLPYioyil0DfXckGVzhw2SXZA9' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { - qtFile = 'qt-5.9.3_win_armv8-libcpp_openssl.tgz' + qtFile = 'https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_win_armv8-libcpp_openssl.tgz' qtChecksum='c3e25db64002d0f43cf565e0ef708911' qtVersionId='HeVObSVLCBoc7yY7He1oBMvPIH0VkClT' } @@ -88,58 +88,58 @@ def packages = [ checksum: qtChecksum, ], bullet: [ - file: 'bullet-2.83_armv8-libcpp.tgz', - versionId: 'ljb7v.1IjVRqyopUKVDbVnLA4z88J8Eo', - checksum: '2c558d604fce337f5eba3eb7ec1252fd', + file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/bullet-2.88_armv8-libcpp.tgz', + versionId: 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg', + checksum: '81642779ccb110f8c7338e8739ac38a0', ], draco: [ - file: 'draco_armv8-libcpp.tgz', + file: 'https://hifi-public.s3.amazonaws.com/austin/android/draco_armv8-libcpp.tgz', versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m', checksum: '617a80d213a5ec69fbfa21a1f2f738cd', ], glad: [ - file: 'glad_armv8-libcpp.zip', + file: 'https://hifi-public.s3.amazonaws.com/austin/android/glad_armv8-libcpp.zip', versionId: 'Q9szthzeye8fFyAA.cY26Lgn2B8kezEE', checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449', ], glm: [ - file: 'glm-0.9.8.tgz', - versionId: 'BlkJNwaYV2Gfy5XwMeU7K0uzPDRKFMt2', - checksum: 'd2b42cee31d2bc17bab6ce69e6b3f30a', + file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/glm-0.9.8.5-patched.tgz', + versionId: 'cskfMoJrFlAeqI3WPxemyO_Cxt7rT9EJ', + checksum: '067b5fe16b220b5b1a1039ba51b062ae', ], gvr: [ - file: 'gvrsdk_v1.101.0.tgz', + file: 'https://hifi-public.s3.amazonaws.com/austin/android/gvrsdk_v1.101.0.tgz', versionId: 'UTberAIFraEfF9IVjoV66u1DTPTopgeY', checksum: '57fd02baa069176ba18597a29b6b4fc7', ], nvtt: [ - file: 'nvtt_armv8-libcpp.zip', + file: 'https://hifi-public.s3.amazonaws.com/austin/android/nvtt_armv8-libcpp.zip', versionId: 'vLqrqThvpq4gp75BHMAqO6HhfTXaa0An', checksum: 'eb46d0b683e66987190ed124aabf8910', sharedLibFolder: 'lib', includeLibs: ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'], ], openssl: [ - file: 'openssl-1.1.0g_armv8.tgz', + file: 'https://hifi-public.s3.amazonaws.com/austin/android/openssl-1.1.0g_armv8.tgz', versionId: 'DmahmSGFS4ltpHyTdyQvv35WOeUOiib9', checksum: 'cabb681fbccd79594f65fcc266e02f32', ], polyvox: [ - file: 'polyvox_armv8-libcpp.tgz', + file: 'https://hifi-public.s3.amazonaws.com/austin/android/polyvox_armv8-libcpp.tgz', versionId: 'LDJtzMTvdm4SAc2KYg8Cg6uwWk4Vq3e3', checksum: '349ad5b72aaf2749ca95d847e60c5314', sharedLibFolder: 'lib', includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'], ], tbb: [ - file: 'tbb-2018_U1_armv8_libcpp.tgz', + file: 'https://hifi-public.s3.amazonaws.com/austin/android/tbb-2018_U1_armv8_libcpp.tgz', versionId: 'YZliDD8.Menh1IVXKEuLPeO3xAjJ1UdF', checksum: '20768f298f53b195e71b414b0ae240c4', sharedLibFolder: 'lib/release', includeLibs: ['libtbb.so', 'libtbbmalloc.so'], ], hifiAC: [ - file: 'libplugins_libhifiCodec.zip', + file: 'https://hifi-public.s3.amazonaws.com/austin/android/libplugins_libhifiCodec.zip', versionId: 'mzKhsRCgVmloqq5bvE.0IwYK1NjGQc_G', checksum: '9412a8e12c88a4096c1fc843bb9fe52d', sharedLibFolder: '', @@ -150,15 +150,15 @@ def packages = [ def scribeLocalFile='scribe' + EXEC_SUFFIX -def scribeFile='scribe_linux_x86_64' +def scribeFile='https://hifi-public.s3.amazonaws.com/austin/android/scribe_linux_x86_64' def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6' def scribeVersion='wgpf4dB2Ltzg4Lb2jJ4nPFsHoDkmK_OO' if (Os.isFamily(Os.FAMILY_MAC)) { - scribeFile = 'scribe_osx_x86_64' + scribeFile = 'https://hifi-public.s3.amazonaws.com/austin/android/scribe_osx_x86_64' scribeChecksum='72db9d32d4e1e50add755570ac5eb749' scribeVersion='o_NbPrktzEYtBkQf3Tn7zc1nZWzM52w6' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { - scribeFile = 'scribe_win32_x86_64.exe' + scribeFile = 'https://hifi-public.s3.amazonaws.com/austin/android/scribe_win32_x86_64.exe' scribeChecksum='678e43d290c90fda670c6fefe038a06d' scribeVersion='GCCJxlmd2irvNOFWfZR0U1UCLHndHQrC' } @@ -608,4 +608,4 @@ task testElf (dependsOn: 'externalNativeBuildDebug') { } } } -*/ \ No newline at end of file +*/ diff --git a/cmake/externals/bullet/CMakeLists.txt b/cmake/externals/bullet/CMakeLists.txt index 317e3302d9..91860a7470 100644 --- a/cmake/externals/bullet/CMakeLists.txt +++ b/cmake/externals/bullet/CMakeLists.txt @@ -17,8 +17,8 @@ include(ExternalProject) if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz - URL_MD5 03051bf112dcc78ddd296f9cab38fd68 + URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz + URL_MD5 0a6876607ebe83e227427215f15946fd CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 -DUSE_DX11=0 LOG_DOWNLOAD 1 LOG_CONFIGURE 1 @@ -28,8 +28,8 @@ if (WIN32) else () ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz - URL_MD5 03051bf112dcc78ddd296f9cab38fd68 + URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz + URL_MD5 0a6876607ebe83e227427215f15946fd CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 LOG_DOWNLOAD 1 LOG_CONFIGURE 1 diff --git a/cmake/externals/glm/CMakeLists.txt b/cmake/externals/glm/CMakeLists.txt index bc8089074f..0a83004438 100644 --- a/cmake/externals/glm/CMakeLists.txt +++ b/cmake/externals/glm/CMakeLists.txt @@ -3,8 +3,8 @@ set(EXTERNAL_NAME glm) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.zip - URL_MD5 579ac77a3110befa3244d68c0ceb7281 + URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.5-patched.zip + URL_MD5 7d39ecc1cea275427534c3cfd6dd63f0 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= ${EXTERNAL_ARGS} LOG_DOWNLOAD 1 @@ -18,4 +18,4 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glm include directories") \ No newline at end of file +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glm include directories") diff --git a/cmake/externals/serverless-content/CMakeLists.txt b/cmake/externals/serverless-content/CMakeLists.txt index cad6d40b49..aa1c59a86b 100644 --- a/cmake/externals/serverless-content/CMakeLists.txt +++ b/cmake/externals/serverless-content/CMakeLists.txt @@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC67-v4.zip - URL_MD5 ba32aed18bfeaac4ccaf5ebb8ea3e804 + URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC68.zip + URL_MD5 a068f74d4045e257cfa7926fe6e38ad5 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cmake/macros/SetupHifiTestCase.cmake b/cmake/macros/SetupHifiTestCase.cmake index b0edb41e36..017a0222f5 100644 --- a/cmake/macros/SetupHifiTestCase.cmake +++ b/cmake/macros/SetupHifiTestCase.cmake @@ -61,16 +61,21 @@ macro(SETUP_HIFI_TESTCASE) endif() endforeach() + # Find test classes to build into test executables. # Warn about any .cpp files that are *not* test classes (*Test[s].cpp), since those files will not be used. foreach (SRC_FILE ${TEST_PROJ_SRC_FILES}) string(REGEX MATCH ".+Tests?\\.cpp$" TEST_CPP_FILE ${SRC_FILE}) string(REGEX MATCH ".+\\.cpp$" NON_TEST_CPP_FILE ${SRC_FILE}) + string(REGEX MATCH ".+\\.qrc$" QRC_FILE ${SRC_FILE}) if (TEST_CPP_FILE) list(APPEND TEST_CASE_FILES ${TEST_CPP_FILE}) elseif (NON_TEST_CPP_FILE) message(WARNING "ignoring .cpp file (not a test class -- this will not be linked or compiled!): " ${NON_TEST_CPP_FILE}) endif () + if (QRC_FILE) + list(APPEND EXTRA_FILES ${QRC_FILE}) + endif() endforeach () if (TEST_CASE_FILES) @@ -88,7 +93,7 @@ macro(SETUP_HIFI_TESTCASE) # grab the implemenation and header files set(TARGET_SRCS ${TEST_FILE}) # only one source / .cpp file (the test class) - add_executable(${TARGET_NAME} ${TEST_FILE}) + add_executable(${TARGET_NAME} ${TEST_FILE} ${EXTRA_FILES}) add_test(${TARGET_NAME}-test ${TARGET_NAME}) set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index d78f0aaeb3..47b55bb5c2 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -479,7 +479,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect limitedNodeList->killNodeWithUUID(existingNodeID); } - // add the connecting node (or re-use the matched one from eachNodeBreakable above) + // add the connecting node SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection); // set the edit rights for this user @@ -508,26 +508,22 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect return newNode; } -SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection, - QUuid nodeID) { +SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) { HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr; SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID); - if (connectedPeer) { - // this user negotiated a connection with us via ICE, so re-use their ICE client ID - nodeID = nodeConnection.connectUUID; - - if (connectedPeer->getActiveSocket()) { - // set their discovered socket to whatever the activated socket on the network peer object was - discoveredSocket = *connectedPeer->getActiveSocket(); - } - } else { - // we got a connectUUID we didn't recognize, either use the hinted node ID or randomly generate a new one - if (nodeID.isNull()) { - nodeID = QUuid::createUuid(); - } + if (connectedPeer && connectedPeer->getActiveSocket()) { + // set their discovered socket to whatever the activated socket on the network peer object was + discoveredSocket = *connectedPeer->getActiveSocket(); } + // create a new node ID for the verified connecting node + auto nodeID = QUuid::createUuid(); + + // add a mapping from connection node ID to ICE peer ID + // so that we can remove the ICE peer once we see this node connect + _nodeToICEPeerIDs.insert(nodeID, nodeConnection.connectUUID); + auto limitedNodeList = DependencyManager::get(); Node::LocalID newLocalID = findOrCreateLocalID(nodeID); @@ -541,6 +537,15 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node return newNode; } +void DomainGatekeeper::cleanupICEPeerForNode(const QUuid& nodeID) { + // remove this node ID from our node to ICE peer ID map + // and the associated ICE peer (if it still exists) + auto icePeerID = _nodeToICEPeerIDs.take(nodeID); + if (!icePeerID.isNull()) { + _icePeers.remove(icePeerID); + } +} + bool DomainGatekeeper::verifyUserSignature(const QString& username, const QByteArray& usernameSignature, const HifiSockAddr& senderSockAddr) { diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 8402e58559..2cb9b4c8a9 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -39,8 +39,8 @@ public: void addPendingAssignedNode(const QUuid& nodeUUID, const QUuid& assignmentUUID, const QUuid& walletUUID, const QString& nodeVersion); QUuid assignmentUUIDForPendingAssignment(const QUuid& tempUUID); - - void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); } + + void cleanupICEPeerForNode(const QUuid& nodeID); Node::LocalID findOrCreateLocalID(const QUuid& uuid); @@ -77,8 +77,7 @@ private: SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection, const QString& username, const QByteArray& usernameSignature); - SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection, - QUuid nodeID = QUuid()); + SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection); bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature, const HifiSockAddr& senderSockAddr); @@ -101,6 +100,10 @@ private: std::unordered_map _pendingAssignedNodes; QHash _icePeers; + + using ConnectingNodeID = QUuid; + using ICEPeerID = QUuid; + QHash _nodeToICEPeerIDs; QHash _connectionTokenHash; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index baeac043e4..4e65df495c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1017,15 +1017,22 @@ void DomainServer::processListRequestPacket(QSharedPointer mess sendingNode->setPublicSocket(nodeRequestData.publicSockAddr); sendingNode->setLocalSocket(nodeRequestData.localSockAddr); - // update the NodeInterestSet in case there have been any changes DomainServerNodeData* nodeData = static_cast(sendingNode->getLinkedData()); + if (!nodeData->hasCheckedIn()) { + nodeData->setHasCheckedIn(true); + + // on first check in, make sure we've cleaned up any ICE peer for this node + _gatekeeper.cleanupICEPeerForNode(sendingNode->getUUID()); + } + // guard against patched agents asking to hear about other agents auto safeInterestSet = nodeRequestData.interestList.toSet(); if (sendingNode->getType() == NodeType::Agent) { safeInterestSet.remove(NodeType::Agent); } + // update the NodeInterestSet in case there have been any changes nodeData->setNodeInterestSet(safeInterestSet); // update the connecting hostname in case it has changed @@ -2945,7 +2952,7 @@ void DomainServer::nodeAdded(SharedNodePointer node) { void DomainServer::nodeKilled(SharedNodePointer node) { // if this peer connected via ICE then remove them from our ICE peers hash - _gatekeeper.removeICEPeer(node->getUUID()); + _gatekeeper.cleanupICEPeerForNode(node->getUUID()); DomainServerNodeData* nodeData = static_cast(node->getLinkedData()); @@ -2978,6 +2985,8 @@ void DomainServer::nodeKilled(SharedNodePointer node) { } } } + + broadcastNodeDisconnect(node); } SharedAssignmentPointer DomainServer::dequeueMatchingAssignment(const QUuid& assignmentUUID, NodeType_t nodeType) { @@ -3163,18 +3172,23 @@ void DomainServer::handleKillNode(SharedNodePointer nodeToKill) { const QUuid& nodeUUID = nodeToKill->getUUID(); limitedNodeList->killNodeWithUUID(nodeUUID); +} - static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID); +void DomainServer::broadcastNodeDisconnect(const SharedNodePointer& disconnectedNode) { + auto limitedNodeList = DependencyManager::get(); + + static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID, true); removedNodePacket->reset(); - removedNodePacket->write(nodeUUID.toRfc4122()); + removedNodePacket->write(disconnectedNode->getUUID().toRfc4122()); // broadcast out the DomainServerRemovedNode message - limitedNodeList->eachMatchingNode([this, &nodeToKill](const SharedNodePointer& otherNode) -> bool { + limitedNodeList->eachMatchingNode([this, &disconnectedNode](const SharedNodePointer& otherNode) -> bool { // only send the removed node packet to nodes that care about the type of node this was - return isInInterestSet(otherNode, nodeToKill); + return isInInterestSet(otherNode, disconnectedNode); }, [&limitedNodeList](const SharedNodePointer& otherNode){ - limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode); + auto removedNodePacketCopy = NLPacket::createCopy(*removedNodePacket); + limitedNodeList->sendPacket(std::move(removedNodePacketCopy), *otherNode); }); } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index b118008d3d..01adbd99a9 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -165,6 +165,7 @@ private: unsigned int countConnectedUsers(); void handleKillNode(SharedNodePointer nodeToKill); + void broadcastNodeDisconnect(const SharedNodePointer& disconnnectedNode); void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr); diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h index 6b8e9a1718..f465cceb96 100644 --- a/domain-server/src/DomainServerNodeData.h +++ b/domain-server/src/DomainServerNodeData.h @@ -67,8 +67,11 @@ public: const QString& getPlaceName() { return _placeName; } void setPlaceName(const QString& placeName) { _placeName = placeName; } - bool wasAssigned() const { return _wasAssigned; }; + bool wasAssigned() const { return _wasAssigned; } void setWasAssigned(bool wasAssigned) { _wasAssigned = wasAssigned; } + + bool hasCheckedIn() const { return _hasCheckedIn; } + void setHasCheckedIn(bool hasCheckedIn) { _hasCheckedIn = hasCheckedIn; } private: QJsonObject overrideValuesIfNeeded(const QJsonObject& newStats); @@ -94,6 +97,8 @@ private: QString _placeName; bool _wasAssigned { false }; + + bool _hasCheckedIn { false }; }; #endif // hifi_DomainServerNodeData_h diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index f94541897b..6743d08275 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -163,10 +163,18 @@ TextField { text: textField.label colorScheme: textField.colorScheme anchors.left: parent.left - anchors.right: parent.right + + Binding on anchors.right { + when: parent.right + value: parent.right + } + Binding on wrapMode { + when: parent.right + value: Text.WordWrap + } + anchors.bottom: parent.top anchors.bottomMargin: 3 - wrapMode: Text.WordWrap visible: label != "" } } diff --git a/interface/resources/qml/dialogs/+android/FileDialog.qml b/interface/resources/qml/dialogs/+android/FileDialog.qml index 548ab453a7..86e6e1ef6c 100644 --- a/interface/resources/qml/dialogs/+android/FileDialog.qml +++ b/interface/resources/qml/dialogs/+android/FileDialog.qml @@ -57,7 +57,7 @@ ModalWindow { property int iconSize: 40 property bool selectDirectory: false; - property bool showHidden: false; + property bool showHidden: true; // FIXME implement property bool multiSelect: false; property bool saveDialog: false; @@ -324,8 +324,10 @@ ModalWindow { showDirsFirst: true showDotAndDotDot: false showFiles: !root.selectDirectory + showHidden: root.showHidden Component.onCompleted: { showFiles = !root.selectDirectory + showHidden = root.showHidden } onFolderChanged: { diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 154d66378b..49bfe78434 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -58,7 +58,7 @@ ModalWindow { property int iconSize: 40 property bool selectDirectory: false; - property bool showHidden: false; + property bool showHidden: true; // FIXME implement property bool multiSelect: false; property bool saveDialog: false; @@ -325,8 +325,10 @@ ModalWindow { showDirsFirst: true showDotAndDotDot: false showFiles: !root.selectDirectory + showHidden: root.showHidden Component.onCompleted: { showFiles = !root.selectDirectory + showHidden = root.showHidden } onFolderChanged: { diff --git a/interface/resources/qml/dialogs/TabletFileDialog.qml b/interface/resources/qml/dialogs/TabletFileDialog.qml index db15337913..e7c93e6d8e 100644 --- a/interface/resources/qml/dialogs/TabletFileDialog.qml +++ b/interface/resources/qml/dialogs/TabletFileDialog.qml @@ -55,7 +55,7 @@ TabletModalWindow { property int iconSize: 40 property bool selectDirectory: false; - property bool showHidden: false; + property bool showHidden: true; // FIXME implement property bool multiSelect: false; property bool saveDialog: false; @@ -288,8 +288,10 @@ TabletModalWindow { showDirsFirst: true showDotAndDotDot: false showFiles: !root.selectDirectory + showHidden: root.showHidden Component.onCompleted: { showFiles = !root.selectDirectory + showHidden = root.showHidden } onFolderChanged: { diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml index 08b0104fce..f3f98f24e5 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml @@ -52,7 +52,7 @@ Rectangle { property int iconSize: 40 property bool selectDirectory: false; - property bool showHidden: false; + property bool showHidden: true; // FIXME implement property bool multiSelect: false; property bool saveDialog: false; @@ -280,8 +280,10 @@ Rectangle { showDirsFirst: true showDotAndDotDot: false showFiles: !root.selectDirectory + showHidden: root.showHidden Component.onCompleted: { showFiles = !root.selectDirectory + showHidden = root.showHidden } onFolderChanged: { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6a102f418b..a43c2fdc5f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -717,7 +717,7 @@ private: * NavigationFocusednumbernumberNot used. * * - * @typedef Controller.Hardware-Application + * @typedef {object} Controller.Hardware-Application */ static const QString STATE_IN_HMD = "InHMD"; @@ -2492,6 +2492,7 @@ void Application::cleanupBeforeQuit() { } _window->saveGeometry(); + _gpuContext->shutdown(); // Destroy third party processes after scripts have finished using them. #ifdef HAVE_DDE @@ -3011,9 +3012,11 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) { auto surfaceContext = DependencyManager::get()->getSurfaceContext(); surfaceContext->setContextProperty("Stats", Stats::getInstance()); +#if !defined(Q_OS_ANDROID) auto offscreenUi = DependencyManager::get(); auto qml = PathUtils::qmlUrl("AvatarInputsBar.qml"); offscreenUi->show(qml, "AvatarInputsBar"); +#endif } void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { @@ -4632,12 +4635,6 @@ void Application::idle() { _overlayConductor.update(secondsSinceLastUpdate); - auto myAvatar = getMyAvatar(); - if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); - Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); - cameraMenuChanged(); - } _gameLoopCounter.increment(); } @@ -5184,6 +5181,21 @@ void Application::cameraModeChanged() { cameraMenuChanged(); } +void Application::changeViewAsNeeded(float boomLength) { + // Switch between first and third person views as needed + // This is called when the boom length has changed + bool boomLengthGreaterThanMinimum = (boomLength > MyAvatar::ZOOM_MIN); + + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON && boomLengthGreaterThanMinimum) { + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false); + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true); + cameraMenuChanged(); + } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON && !boomLengthGreaterThanMinimum) { + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, false); + cameraMenuChanged(); + } +} void Application::cameraMenuChanged() { auto menu = Menu::getInstance(); diff --git a/interface/src/Application.h b/interface/src/Application.h index ee97077002..0fea476c07 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -417,6 +417,8 @@ public slots: void updateVerboseLogging(); + void changeViewAsNeeded(float boomLength); + private slots: void onDesktopRootItemCreated(QQuickItem* qmlContext); void onDesktopRootContextCreated(QQmlContext* qmlContext); @@ -649,7 +651,7 @@ private: quint64 _lastFaceTrackerUpdate; render::ScenePointer _main3DScene{ new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) }; - render::EnginePointer _renderEngine{ new render::Engine() }; + render::EnginePointer _renderEngine{ new render::RenderEngine() }; gpu::ContextPointer _gpuContext; // initialized during window creation mutable QMutex _renderArgsMutex{ QMutex::Recursive }; diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index bee21bad22..4ca073fb4f 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -25,7 +25,7 @@ class FancyCamera : public Camera { // FIXME: JSDoc 3.5.5 doesn't augment @property definitions. The following definition is repeated in Camera.h. /**jsdoc - * @property cameraEntity {Uuid} The ID of the entity that the camera position and orientation follow when the camera is in + * @property {Uuid} cameraEntity The ID of the entity that the camera position and orientation follow when the camera is in * entity mode. */ Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 094b3bb67b..2e9c9fdecd 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -104,6 +104,9 @@ void AvatarManager::updateMyAvatar(float deltaTime) { PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()"); _myAvatar->update(deltaTime); + render::Transaction transaction; + _myAvatar->updateRenderItem(transaction); + qApp->getMain3DScene()->enqueueTransaction(transaction); quint64 now = usecTimestampNow(); quint64 dt = now - _lastSendAvatarDataTime; diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index beb7e34439..6fc1bd8196 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -160,7 +160,7 @@ QUuid AvatarMotionState::getSimulatorID() const { } // virtual -void AvatarMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const { +void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const { group = BULLET_COLLISION_GROUP_OTHER_AVATAR; mask = Physics::getDefaultCollisionMask(group); } diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 73fb853312..2738aba8ee 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -65,7 +65,7 @@ public: void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; } - virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override; + virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override; virtual float getMass() const override; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5fb4e80b80..0783094e10 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1127,7 +1127,11 @@ void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) { } void MyAvatar::setEnableMeshVisible(bool isEnabled) { - _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); + return Avatar::setEnableMeshVisible(isEnabled); +} + +bool MyAvatar::getEnableMeshVisible() const { + return Avatar::getEnableMeshVisible(); } void MyAvatar::setEnableInverseKinematics(bool isEnabled) { @@ -1479,7 +1483,10 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _skeletonModelChangeCount++; int skeletonModelChangeCount = _skeletonModelChangeCount; Avatar::setSkeletonModelURL(skeletonModelURL); - _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); + _skeletonModel->setTagMask(render::hifi::TAG_NONE); + _skeletonModel->setGroupCulled(true); + _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene()); + _headBoneSet.clear(); _cauterizationNeedsUpdate = true; @@ -2054,14 +2061,12 @@ void MyAvatar::preDisplaySide(const RenderArgs* renderArgs) { _attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 || _attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 || _attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) { - uint8_t modelRenderTagBits = shouldDrawHead ? render::ItemKey::TAG_BITS_0 : render::ItemKey::TAG_BITS_NONE; - modelRenderTagBits |= render::ItemKey::TAG_BITS_1; - _attachmentModels[i]->setVisibleInScene(true, qApp->getMain3DScene(), - modelRenderTagBits, false); + uint8_t modelRenderTagBits = shouldDrawHead ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_SECONDARY_VIEW; - uint8_t castShadowRenderTagBits = render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1; - _attachmentModels[i]->setCanCastShadow(true, qApp->getMain3DScene(), - castShadowRenderTagBits, false); + _attachmentModels[i]->setTagMask(modelRenderTagBits); + _attachmentModels[i]->setGroupCulled(false); + _attachmentModels[i]->setCanCastShadow(true); + _attachmentModels[i]->setVisibleInScene(true, qApp->getMain3DScene()); } } } @@ -2245,9 +2250,15 @@ void MyAvatar::updateActionMotor(float deltaTime) { _actionMotorVelocity = getSensorToWorldScale() * (_walkSpeed.get() * _walkSpeedScalar) * direction; } + float previousBoomLength = _boomLength; float boomChange = getDriveKey(ZOOM); _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange; _boomLength = glm::clamp(_boomLength, ZOOM_MIN, ZOOM_MAX); + + // May need to change view if boom length has changed + if (previousBoomLength != _boomLength) { + qApp->changeViewAsNeeded(_boomLength); + } } void MyAvatar::updatePosition(float deltaTime) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index fa6a675d99..514dd9dcaf 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -121,7 +121,7 @@ class MyAvatar : public Avatar { * while flying. * @property {number} hmdRollControlDeadZone=8 - The amount of HMD roll, in degrees, required before your avatar turns if * hmdRollControlEnabled is enabled. - * @property hmdRollControlRate {number} If hmdRollControlEnabled is true, this value determines the maximum turn rate of + * @property {number} hmdRollControlRate If hmdRollControlEnabled is true, this value determines the maximum turn rate of * your avatar when rolling your HMD in degrees per second. * @property {number} userHeight=1.75 - The height of the user in sensor space. * @property {number} userEyeHeight=1.65 - The estimated height of the user's eyes in sensor space. Read-only. @@ -1159,7 +1159,7 @@ public slots: * @function MyAvatar.getEnableMeshVisible * @returns {boolean} true if your avatar's mesh is visible, otherwise false. */ - bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); } + bool getEnableMeshVisible() const override; /**jsdoc * Set whether or not your avatar mesh is visible. @@ -1171,7 +1171,7 @@ public slots: * MyAvatar.setEnableMeshVisible(true); * }, 10000); */ - void setEnableMeshVisible(bool isEnabled); + virtual void setEnableMeshVisible(bool isEnabled) override; /**jsdoc * @function MyAvatar.setEnableInverseKinematics @@ -1402,7 +1402,7 @@ private: SharedSoundPointer _collisionSound; MyCharacterController _characterController; - int16_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR }; + int32_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR }; AvatarWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index a39aa3a4a1..5ef5d27d74 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -22,21 +22,22 @@ * @hifi-interface * @hifi-client-entity * - * @property PICK_NOTHING {number} A filter flag. Don't intersect with anything. Read-only. - * @property PICK_ENTITIES {number} A filter flag. Include entities when intersecting. Read-only. - * @property PICK_OVERLAYS {number} A filter flag. Include overlays when intersecting. Read-only. - * @property PICK_AVATARS {number} A filter flag. Include avatars when intersecting. Read-only. - * @property PICK_HUD {number} A filter flag. Include the HUD sphere when intersecting in HMD mode. Read-only. - * @property PICK_COARSE {number} A filter flag. Pick against coarse meshes, instead of exact meshes. Read-only. - * @property PICK_INCLUDE_INVISIBLE {number} A filter flag. Include invisible objects when intersecting. Read-only. - * @property PICK_INCLUDE_NONCOLLIDABLE {number} A filter flag. Include non-collidable objects when intersecting. + * @property {number} PICK_NOTHING A filter flag. Don't intersect with anything. Read-only. + * @property {number} PICK_ENTITIES A filter flag. Include entities when intersecting. Read-only. + * @property {number} PICK_OVERLAYS A filter flag. Include overlays when intersecting. Read-only. + * @property {number} PICK_AVATARS A filter flag. Include avatars when intersecting. Read-only. + * @property {number} PICK_HUD A filter flag. Include the HUD sphere when intersecting in HMD mode. Read-only. + * @property {number} PICK_COARSE A filter flag. Pick against coarse meshes, instead of exact meshes. Read-only. + * @property {number} PICK_INCLUDE_INVISIBLE A filter flag. Include invisible objects when intersecting. Read-only. + * @property {number} PICK_INCLUDE_NONCOLLIDABLE A filter flag. Include non-collidable objects when intersecting. * Read-only. - * @property PICK_ALL_INTERSECTIONS {number} Read-only. - * @property INTERSECTED_NONE {number} An intersection type. Intersected nothing with the given filter flags. Read-only. - * @property INTERSECTED_ENTITY {number} An intersection type. Intersected an entity. Read-only. - * @property INTERSECTED_OVERLAY {number} An intersection type. Intersected an overlay. Read-only. - * @property INTERSECTED_AVATAR {number} An intersection type. Intersected an avatar. Read-only. - * @property INTERSECTED_HUD {number} An intersection type. Intersected the HUD sphere. Read-only. + * @property {number} PICK_ALL_INTERSECTIONS Read-only. + * @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags. + * Read-only. + * @property {number} INTERSECTED_ENTITY An intersection type. Intersected an entity. Read-only. + * @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an overlay. Read-only. + * @property {number} INTERSECTED_AVATAR An intersection type. Intersected an avatar. Read-only. + * @property {number} INTERSECTED_HUD An intersection type. Intersected the HUD sphere. Read-only. * @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results. Read-only. */ @@ -99,7 +100,7 @@ public: /**jsdoc * An intersection result for a Ray Pick. * - * @typedef {Object} RayPickResult + * @typedef {object} RayPickResult * @property {number} type The intersection type. * @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE) * @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections. @@ -113,7 +114,7 @@ public: /**jsdoc * An intersection result for a Stylus Pick. * - * @typedef {Object} StylusPickResult + * @typedef {object} StylusPickResult * @property {number} type The intersection type. * @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE) * @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections. diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index b7ac899c8d..4e953a5cb8 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -68,14 +68,14 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) * A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is not intersecting something. Same as a {@link Pointers.RayPointerRenderState}, * but with an additional distance field. * - * @typedef {Object} Pointers.DefaultRayPointerRenderState + * @typedef {object} Pointers.DefaultRayPointerRenderState * @augments Pointers.RayPointerRenderState * @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined. */ /**jsdoc * A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something. * - * @typedef {Object} Pointers.RayPointerRenderState + * @typedef {object} Pointers.RayPointerRenderState * @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} * @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). * An overlay to represent the beginning of the Ray Pointer, if desired. @@ -87,7 +87,7 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) /**jsdoc * A trigger mechanism for Ray Pointers. * - * @typedef {Object} Pointers.Trigger + * @typedef {object} Pointers.Trigger * @property {Controller.Standard|Controller.Actions|function} action This can be a built-in Controller action, like Controller.Standard.LTClick, or a function that evaluates to >= 1.0 when you want to trigger button. * @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". diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 700994c517..430441226f 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -186,3 +187,7 @@ void TestScriptingInterface::saveObject(QVariant variant, const QString& filenam file.write(jsonData); file.close(); } + +void TestScriptingInterface::showMaximized() { + qApp->getWindow()->showMaximized(); +} \ No newline at end of file diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index 5666417727..c47e39d1f3 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -27,70 +27,128 @@ public slots: /**jsdoc * Exits the application + * @function Test.quit */ void quit(); /**jsdoc * Waits for all texture transfers to be complete + * @function Test.waitForTextureIdle */ void waitForTextureIdle(); /**jsdoc * Waits for all pending downloads to be complete + * @function Test.waitForDownloadIdle */ void waitForDownloadIdle(); /**jsdoc * Waits for all file parsing operations to be complete + * @function Test.waitForProcessingIdle */ void waitForProcessingIdle(); /**jsdoc * Waits for all pending downloads, parsing and texture transfers to be complete + * @function Test.waitIdle */ void waitIdle(); + /**jsdoc + * Waits for establishment of connection to server + * @function Test.waitForConnection + * @param {int} maxWaitMs [default=10000] - Number of milliseconds to wait + */ bool waitForConnection(qint64 maxWaitMs = 10000); + /**jsdoc + * Waits a specific number of milliseconds + * @function Test.wait + * @param {int} milliseconds - Number of milliseconds to wait + */ void wait(int milliseconds); + /**jsdoc + * Waits for all pending downloads, parsing and texture transfers to be complete + * @function Test.loadTestScene + * @param {string} sceneFile - URL of scene to load + */ bool loadTestScene(QString sceneFile); + /**jsdoc + * Clears all caches + * @function Test.clear + */ void clear(); /**jsdoc * Start recording Chrome compatible tracing events * logRules can be used to specify a set of logging category rules to limit what gets captured + * @function Test.startTracing + * @param {string} logrules [defaultValue=""] - See implementation for explanation */ bool startTracing(QString logrules = ""); /**jsdoc * Stop recording Chrome compatible tracing events and serialize recorded events to a file * Using a filename with a .gz extension will automatically compress the output file + * @function Test.stopTracing + * @param {string} filename - Name of file to save to + * @returns {bool} True if successful. */ bool stopTracing(QString filename); + /**jsdoc + * Starts a specific trace event + * @function Test.startTraceEvent + * @param {string} name - Name of event + */ void startTraceEvent(QString name); + /**jsdoc + * Stop a specific name event + * Using a filename with a .gz extension will automatically compress the output file + * @function Test.endTraceEvent + * @param {string} filename - Name of event + */ void endTraceEvent(QString name); /**jsdoc * Write detailed timing stats of next physics stepSimulation() to filename + * @function Test.savePhysicsSimulationStats + * @param {string} filename - Name of file to save to */ void savePhysicsSimulationStats(QString filename); + /**jsdoc + * Profiles a specific function + * @function Test.savePhysicsSimulationStats + * @param {string} name - Name used to reference the function + * @param {function} function - Function to profile + */ Q_INVOKABLE void profileRange(const QString& name, QScriptValue function); /**jsdoc * Clear all caches (menu command Reload Content) + * @function Test.clearCaches */ void clearCaches(); /**jsdoc * Save a JSON object to a file in the test results location + * @function Test.saveObject + * @param {string} name - Name of the object + * @param {string} filename - Name of file to save to */ void saveObject(QVariant v, const QString& filename); + /**jsdoc + * Maximizes the window + * @function Test.showMaximized + */ + void showMaximized(); + private: bool waitForCondition(qint64 maxWaitMs, std::function condition); QString _testResultsLocation; diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 6f6e83842c..af9b5c8a46 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -522,7 +522,7 @@ int WindowScriptingInterface::openMessageBox(QString title, QString text, int bu * RestoreDefaults 0x8000000 "Restore Defaults" * * - * @typedef Window.MessageBoxButton + * @typedef {number} Window.MessageBoxButton */ int WindowScriptingInterface::createMessageBox(QString title, QString text, int buttons, int defaultButton) { auto messageBox = DependencyManager::get()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text, diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 1d06f33ec0..5ddbc30dd3 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -470,7 +470,7 @@ public slots: * * * - * @typedef Window.DisplayTexture + * @typedef {string} Window.DisplayTexture */ bool setDisplayTexture(const QString& name); @@ -523,16 +523,21 @@ public slots: int openMessageBox(QString title, QString text, int buttons, int defaultButton); /**jsdoc - * Open the given resource in the Interface window or in a web browser depending on the url scheme + * Open a URL in the Interface window or other application, depending on the URL's scheme. If the URL starts with + * hifi:// 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 http://). * @function Window.openUrl - * @param {string} url - The resource to open + * @param {string} url - The URL to open. */ void openUrl(const QUrl& url); /**jsdoc - * (Android only) Open the requested Activity and optionally back to the scene when the activity is done + * Open an Android activity and optionally return back to the scene when the activity is completed. Android only. * @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 "Home", "Login", or + * "Privacy Policy". + * @param {boolean} backToScene - If true, the user is automatically returned back to the scene when the + * activity is completed. */ void openAndroidActivity(const QString& activityName, const bool backToScene); diff --git a/interface/src/ui/overlays/Billboard3DOverlay.cpp b/interface/src/ui/overlays/Billboard3DOverlay.cpp index 960f0de095..ecade70ef8 100644 --- a/interface/src/ui/overlays/Billboard3DOverlay.cpp +++ b/interface/src/ui/overlays/Billboard3DOverlay.cpp @@ -46,6 +46,13 @@ bool Billboard3DOverlay::applyTransformTo(Transform& transform, bool force) { return transformChanged; } +void Billboard3DOverlay::update(float duration) { + if (isFacingAvatar()) { + _renderVariableDirty = true; + } + Parent::update(duration); +} + Transform Billboard3DOverlay::evalRenderTransform() { Transform transform = getTransform(); bool transformChanged = applyTransformTo(transform, true); diff --git a/interface/src/ui/overlays/Billboard3DOverlay.h b/interface/src/ui/overlays/Billboard3DOverlay.h index 6b3aa40451..174bc23bc8 100644 --- a/interface/src/ui/overlays/Billboard3DOverlay.h +++ b/interface/src/ui/overlays/Billboard3DOverlay.h @@ -18,6 +18,7 @@ class Billboard3DOverlay : public Planar3DOverlay, public PanelAttachable, public Billboardable { Q_OBJECT + using Parent = Planar3DOverlay; public: Billboard3DOverlay() {} @@ -26,6 +27,8 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; + void update(float duration) override; + protected: virtual bool applyTransformTo(Transform& transform, bool force = false) override; diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index df93245922..6e9946e935 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -51,11 +51,6 @@ void Image3DOverlay::update(float deltatime) { _texture = DependencyManager::get()->getTexture(_url); _textureIsLoaded = false; } - if (usecTimestampNow() > _transformExpiry) { - Transform transform = getTransform(); - applyTransformTo(transform); - setTransform(transform); - } Parent::update(deltatime); } diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 27e3bd0e2d..a541b3365f 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -103,10 +103,9 @@ void ModelOverlay::update(float deltatime) { if (_visibleDirty) { _visibleDirty = false; // don't show overlays in mirrors or spectator-cam unless _isVisibleInSecondaryCamera is true - _model->setVisibleInScene(getVisible(), scene, - render::ItemKey::TAG_BITS_0 | - (_isVisibleInSecondaryCamera ? render::ItemKey::TAG_BITS_1 : render::ItemKey::TAG_BITS_NONE), - false); + uint8_t modelRenderTagMask = (_isVisibleInSecondaryCamera ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW); + _model->setTagMask(modelRenderTagMask, scene); + _model->setVisibleInScene(getVisible(), scene); } if (_drawInFrontDirty) { _drawInFrontDirty = false; diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 2f27d50f7e..890d5178e5 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -132,7 +132,6 @@ private: namespace render { template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay); template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay); - template <> int payloadGetLayer(const Overlay::Pointer& overlay); template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args); template <> const ShapeKey shapeGetShapeKey(const Overlay::Pointer& overlay); template <> uint32_t metaFetchMetaSubItems(const Overlay::Pointer& overlay, ItemIDs& subItems); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 3ff782da99..3debf74f26 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -53,7 +53,7 @@ const OverlayID UNKNOWN_OVERLAY_ID = OverlayID(); * @property {number} distance - The distance from the {@link PickRay} origin to the intersection point. * @property {Vec3} surfaceNormal - The normal of the overlay surface at the intersection point. * @property {Vec3} intersection - The position of the intersection point. - * @property {Object} extraInfo Additional intersection details, if available. + * @property {object} extraInfo Additional intersection details, if available. */ class RayToOverlayIntersectionResult { public: @@ -482,7 +482,7 @@ public slots: /**jsdoc * Check if there is an overlay of a given ID. - * @function Overlays.isAddedOverly + * @function Overlays.isAddedOverlay * @param {Uuid} overlayID - The ID to check. * @returns {boolean} true if an overlay with the given ID exists, false otherwise. */ diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index 185547a333..901dd1df3e 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -35,14 +35,18 @@ namespace render { auto builder = ItemKey::Builder().withTypeShape(); if (overlay->is3D()) { auto overlay3D = std::static_pointer_cast(overlay); - if (overlay3D->getDrawInFront() || overlay3D->getDrawHUDLayer()) { - builder.withLayered(); + if (overlay3D->getDrawInFront()) { + builder.withLayer(render::hifi::LAYER_3D_FRONT); + } else if (overlay3D->getDrawHUDLayer()) { + builder.withLayer(render::hifi::LAYER_3D_HUD); } + if (overlay->isTransparent()) { builder.withTransparent(); } } else { builder.withViewSpace(); + builder.withLayer(render::hifi::LAYER_2D); } if (!overlay->getVisible()) { @@ -50,30 +54,14 @@ namespace render { } // always visible in primary view. if isVisibleInSecondaryCamera, also draw in secondary view - uint32_t viewTaskBits = render::ItemKey::TAG_BITS_0 | - (overlay->getIsVisibleInSecondaryCamera() ? render::ItemKey::TAG_BITS_1 : render::ItemKey::TAG_BITS_NONE); - - builder.withTagBits(viewTaskBits); + render::hifi::Tag viewTagBits = overlay->getIsVisibleInSecondaryCamera() ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW; + builder.withTagBits(viewTagBits); return builder.build(); } template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) { return overlay->getBounds(); } - template <> int payloadGetLayer(const Overlay::Pointer& overlay) { - if (overlay->is3D()) { - auto overlay3D = std::dynamic_pointer_cast(overlay); - if (overlay3D->getDrawInFront()) { - return Item::LAYER_3D_FRONT; - } else if (overlay3D->getDrawHUDLayer()) { - return Item::LAYER_3D_HUD; - } else { - return Item::LAYER_3D; - } - } else { - return Item::LAYER_2D; - } - } template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) { if (args) { overlay->render(args); @@ -83,7 +71,6 @@ namespace render { return overlay->getShapeKey(); } - template <> uint32_t metaFetchMetaSubItems(const Overlay::Pointer& overlay, ItemIDs& subItems) { return overlay->fetchMetaSubItems(subItems); } diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 9c920efb93..b128ce7df7 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -83,15 +83,6 @@ xColor Text3DOverlay::getBackgroundColor() { return result; } -void Text3DOverlay::update(float deltatime) { - if (usecTimestampNow() > _transformExpiry) { - Transform transform = getTransform(); - applyTransformTo(transform); - setTransform(transform); - } - Parent::update(deltatime); -} - void Text3DOverlay::render(RenderArgs* args) { if (!_renderVisible || !getParentVisible()) { return; // do nothing if we're not visible @@ -306,13 +297,4 @@ QSizeF Text3DOverlay::textSize(const QString& text) const { float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; return QSizeF(extents.x, extents.y) * pointToWorldScale; -} - -bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance, - BoxFace &face, glm::vec3& surfaceNormal) { - Transform transform = getTransform(); - applyTransformTo(transform, true); - setTransform(transform); - return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face, surfaceNormal); -} - +} \ No newline at end of file diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index daa5fdc804..21163101d0 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -30,8 +30,6 @@ public: ~Text3DOverlay(); virtual void render(RenderArgs* args) override; - virtual void update(float deltatime) override; - virtual const render::ShapeKey getShapeKey() override; // getters @@ -60,9 +58,6 @@ public: QSizeF textSize(const QString& test) const; // Meters - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal) override; - virtual Text3DOverlay* createClone() const override; private: diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index c678e3d2a2..8af818edc6 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -259,7 +259,6 @@ void Web3DOverlay::setupQmlSurface() { _webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this); _webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface()); - _webSurface->getSurfaceContext()->setContextProperty("desktop", DependencyManager::get()->getDesktop()); _webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance()); // Override min fps for tablet UI, for silky smooth scrolling diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index d8f8a13cde..4b0a8901f5 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -70,7 +70,7 @@ public: * @function AnimationCache.prefetch * @param {string} url - URL of the resource to prefetch. * @param {object} [extra=null] - * @returns {Resource} + * @returns {ResourceObject} */ /**jsdoc @@ -79,7 +79,7 @@ public: * @param {string} url - URL of the resource to load. * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails. * @param {} [extra=null] - * @returns {Resource} + * @returns {object} */ @@ -87,7 +87,7 @@ public: * Returns animation resource for particular animation. * @function AnimationCache.getAnimation * @param {string} url - URL to load. - * @returns {Resource} animation + * @returns {AnimationObject} animation */ Q_INVOKABLE AnimationPointer getAnimation(const QString& url) { return getAnimation(QUrl(url)); } Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url); @@ -104,6 +104,17 @@ private: Q_DECLARE_METATYPE(AnimationPointer) +/**jsdoc + * @class AnimationObject + * + * @hifi-interface + * @hifi-client-entity + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {string[]} jointNames + * @property {FBXAnimationFrame[]} frames + */ /// An animation loaded from the network. class Animation : public Resource { Q_OBJECT @@ -118,9 +129,16 @@ public: virtual bool isLoaded() const override; - + /**jsdoc + * @function AnimationObject.getJointNames + * @returns {string[]} + */ Q_INVOKABLE QStringList getJointNames() const; + /**jsdoc + * @function AnimationObject.getFrames + * @returns {FBXAnimationFrame[]} + */ Q_INVOKABLE QVector getFrames() const; const QVector& getFramesReference() const; diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 69dbf5a913..4cfdac7792 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -77,6 +77,17 @@ private: typedef QSharedPointer SharedSoundPointer; +/**jsdoc + * @class SoundObject + * + * @hifi-interface + * @hifi-client-entity + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {boolean} downloaded + * @property {number} duration + */ class SoundScriptingInterface : public QObject { Q_OBJECT @@ -90,6 +101,10 @@ public: bool isReady() const { return _sound->isReady(); } float getDuration() { return _sound->getDuration(); } +/**jsdoc + * @function SoundObject.ready + * @returns {Signal} + */ signals: void ready(); diff --git a/libraries/audio/src/SoundCache.h b/libraries/audio/src/SoundCache.h index 347f324353..4352b1d459 100644 --- a/libraries/audio/src/SoundCache.h +++ b/libraries/audio/src/SoundCache.h @@ -64,7 +64,7 @@ public: * @function SoundCache.prefetch * @param {string} url - URL of the resource to prefetch. * @param {object} [extra=null] - * @returns {Resource} + * @returns {ResourceObject} */ /**jsdoc @@ -73,14 +73,14 @@ public: * @param {string} url - URL of the resource to load. * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails. * @param {} [extra=null] - * @returns {Resource} + * @returns {object} */ /**jsdoc * @function SoundCache.getSound * @param {string} url - * @returns {object} + * @returns {SoundObject} */ Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); protected: diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index da829b23e4..5ce7637a3a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -52,10 +52,15 @@ const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { - return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1).withMetaCullGroup(); + ItemKey::Builder keyBuilder = ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::hifi::TAG_ALL_VIEWS).withMetaCullGroup(); + auto avatarPtr = static_pointer_cast(avatar); + if (!avatarPtr->getEnableMeshVisible()) { + keyBuilder.withInvisible(); + } + return keyBuilder.build(); } template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) { - return static_pointer_cast(avatar)->getBounds(); + return static_pointer_cast(avatar)->getRenderBounds(); } template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args) { auto avatarPtr = static_pointer_cast(avatar); @@ -164,6 +169,11 @@ AABox Avatar::getBounds() const { return _skeletonModel->getRenderableMeshBound(); } + +AABox Avatar::getRenderBounds() const { + return _renderBound; +} + void Avatar::animateScaleChanges(float deltaTime) { if (_isAnimatingScale) { @@ -569,16 +579,26 @@ static TextRenderer3D* textRenderer(TextRendererType type) { void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { auto avatarPayload = new render::Payload(self); - auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); - + auto avatarPayloadPointer = std::shared_ptr>(avatarPayload); if (_renderItemID == render::Item::INVALID_ITEM_ID) { _renderItemID = scene->allocateID(); } + // INitialize the _render bound as we are creating the avatar render item + _renderBound = getBounds(); transaction.resetItem(_renderItemID, avatarPayloadPointer); _skeletonModel->addToScene(scene, transaction); + _skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS); + _skeletonModel->setGroupCulled(true); + _skeletonModel->setCanCastShadow(true); + _skeletonModel->setVisibleInScene(_isMeshVisible, scene); + processMaterials(); for (auto& attachmentModel : _attachmentModels) { attachmentModel->addToScene(scene, transaction); + attachmentModel->setTagMask(render::hifi::TAG_ALL_VIEWS); + attachmentModel->setGroupCulled(false); + attachmentModel->setCanCastShadow(true); + attachmentModel->setVisibleInScene(_isMeshVisible, scene); } _mustFadeIn = true; @@ -637,7 +657,15 @@ void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointe void Avatar::updateRenderItem(render::Transaction& transaction) { if (render::Item::isValidID(_renderItemID)) { - transaction.updateItem>(_renderItemID, [](render::Payload& p) {}); + auto renderBound = getBounds(); + transaction.updateItem(_renderItemID, + [renderBound](AvatarData& avatar) { + auto avatarPtr = dynamic_cast(&avatar); + if (avatarPtr) { + avatarPtr->_renderBound = renderBound; + } + } + ); } } @@ -759,6 +787,18 @@ void Avatar::render(RenderArgs* renderArgs) { } } + +void Avatar::setEnableMeshVisible(bool isEnabled) { + if (_isMeshVisible != isEnabled) { + _isMeshVisible = isEnabled; + _needMeshVisibleSwitch = true; + } +} + +bool Avatar::getEnableMeshVisible() const { + return _isMeshVisible; +} + void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { bool canTryFade{ false }; @@ -770,6 +810,12 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) { _skeletonModel->removeFromScene(scene, transaction); _skeletonModel->addToScene(scene, transaction); + + _skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS); + _skeletonModel->setGroupCulled(true); + _skeletonModel->setCanCastShadow(true); + _skeletonModel->setVisibleInScene(_isMeshVisible, scene); + processMaterials(); canTryFade = true; _isAnimatingScale = true; @@ -778,9 +824,25 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) { attachmentModel->removeFromScene(scene, transaction); attachmentModel->addToScene(scene, transaction); + + attachmentModel->setTagMask(render::hifi::TAG_ALL_VIEWS); + attachmentModel->setGroupCulled(false); + attachmentModel->setCanCastShadow(true); + attachmentModel->setVisibleInScene(_isMeshVisible, scene); } } + if (_needMeshVisibleSwitch) { + _skeletonModel->setVisibleInScene(_isMeshVisible, scene); + for (auto attachmentModel : _attachmentModels) { + if (attachmentModel->isRenderable()) { + attachmentModel->setVisibleInScene(_isMeshVisible, scene); + } + } + updateRenderItem(transaction); + _needMeshVisibleSwitch = false; + } + if (_mustFadeIn && canTryFade) { // Do it now to be sure all the sub items are ready and the fade is sent to them too fade(transaction, render::Transition::USER_ENTER_DOMAIN); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 01114b5f6d..10c1d9ead2 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -74,7 +74,6 @@ public: virtual void instantiableAvatar() = 0; typedef render::Payload Payload; - typedef std::shared_ptr PayloadPointer; void init(); void updateAvatarEntities(); @@ -292,7 +291,7 @@ public: */ /**jsdoc * Information about a single joint in an Avatar's skeleton hierarchy. - * @typedef MyAvatar.SkeletonJoint + * @typedef {object} MyAvatar.SkeletonJoint * @property {string} name - Joint name. * @property {number} index - Joint index. * @property {number} parentIndex - Index of this joint's parent (-1 if no parent). @@ -322,6 +321,7 @@ public: bool hasNewJointData() const { return _hasNewJointData; } float getBoundingRadius() const; + AABox getRenderBounds() const; // THis call is accessible from rendering thread only to report the bounding box of the avatar during the frame. void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene); void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene); @@ -356,6 +356,10 @@ public: virtual void setAvatarEntityDataChanged(bool value) override; + // Show hide the model representation of the avatar + virtual void setEnableMeshVisible(bool isEnabled); + virtual bool getEnableMeshVisible() const; + void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override; void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override; @@ -532,6 +536,10 @@ protected: std::mutex _materialsLock; void processMaterials(); + + AABox _renderBound; + bool _isMeshVisible{ true }; + bool _needMeshVisibleSwitch{ true }; }; #endif // hifi_Avatar_h diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 428f86f0ab..de8c02f10e 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -35,7 +35,7 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : _useDualQuaternionSkinning = true; // Avatars all cast shadow - _canCastShadow = true; + setCanCastShadow(true); assert(_owningAvatar); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 7a28686f8c..48ef1fb881 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2363,7 +2363,7 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const } /**jsdoc - * @typedef AttachmentData + * @typedef {object} AttachmentData * @property {string} modelUrl * @property {string} jointName * @property {Vec3} translation diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 62a14ec51e..4946ce45b9 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -578,8 +578,7 @@ public: * @param {Quat} rotation - The rotation of the joint relative to its parent. * @param {Vec3} translation - The translation of the joint relative to its parent. * @example Set your avatar to it's default T-pose for a while.
- * Avatar in T-pose - * + * Avatar in T-pose * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { @@ -680,8 +679,7 @@ public: * @param {string} name - The name of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. * @example Set your avatar to its default T-pose then rotate its right arm.
- * Avatar in T-pose with arm rotated + * Avatar in T-pose with arm rotated * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { @@ -713,8 +711,7 @@ public: * @param {Vec3} translation - The translation of the joint relative to its parent. * @example 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.
- * Avatar with neck stretched + * Avatar with neck stretched * // Stretch your avatar's neck. * MyAvatar.setJointTranslation("Neck", { x: 0, y: 25, z: 0 }); * @@ -798,8 +795,7 @@ public: * @param {Quat[]} jointRotations - The rotations for all joints in the avatar. The values are in the same order as the * array returned by {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}. * @example Set your avatar to its default T-pose then rotate its right arm.
- * Avatar in T-pose - * + * Avatar in T-pose * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 978b0888ba..6923ef4b98 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -307,7 +307,7 @@ namespace controller { * action. * * - * @typedef Controller.Actions + * @typedef {object} Controller.Actions */ // Device functions Input::NamedVector ActionsDevice::getAvailableInputs() const { diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 30a58eb2f0..1e626e6a3c 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -79,7 +79,7 @@ enum Hand { * {@link Controller.Hardware-Vive}. * * - * @typedef Controller.Hardware + * @typedef {object} Controller.Hardware * @example List all the currently available Controller.Hardware properties. * function printProperties(string, item) { * print(string); diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 471943400d..e1733d2524 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -231,7 +231,7 @@ void StandardController::focusOutEvent() { * * * - * @typedef Controller.Standard + * @typedef {object} Controller.Standard */ Input::NamedVector StandardController::getAvailableInputs() const { static Input::NamedVector availableInputs { diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 354d3242a9..513f955e9e 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -46,6 +46,8 @@ const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE( +// OpenGLDisplayPlugin_present.frag + uniform sampler2D colorMap; in vec2 varTexCoord0; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index d6fc0ee6b7..ae4c13d96f 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -157,20 +157,20 @@ Item::Bound EntityRenderer::getBound() { return _bound; } -uint32_t EntityRenderer::getViewTagBits() const { - return render::ItemKey::TAG_BITS_0 | (_isVisibleInSecondaryCamera ? render::ItemKey::TAG_BITS_1 : render::ItemKey::TAG_BITS_NONE); +render::hifi::Tag EntityRenderer::getTagMask() const { + return _isVisibleInSecondaryCamera ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW; } ItemKey EntityRenderer::getKey() { if (isTransparent()) { - return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getViewTagBits()); + return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getTagMask()); } // This allows shapes to cast shadows if (_canCastShadow) { - return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(getViewTagBits()).withShadowCaster(); + return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(getTagMask()).withShadowCaster(); } else { - return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(getViewTagBits()); + return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(getTagMask()); } } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 248be8a5be..e1ce2ed39e 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -18,6 +18,7 @@ #include "AbstractViewStateInterface.h" #include "EntitiesRendererLogging.h" #include +#include class EntityTreeRenderer; @@ -74,7 +75,7 @@ protected: virtual Item::Bound getBound() override; virtual void render(RenderArgs* args) override final; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override; - virtual uint32_t getViewTagBits() const; + virtual render::hifi::Tag getTagMask() const; // Returns true if the item in question needs to have updateInScene called because of internal rendering state changes virtual bool needsRenderUpdate() const; diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index 094f4082cb..eabcb68e4f 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -43,7 +43,7 @@ void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& ItemKey MaterialEntityRenderer::getKey() { ItemKey::Builder builder; - builder.withTypeShape().withTagBits(getViewTagBits()); + builder.withTypeShape().withTagBits(getTagMask()); if (!_visible) { builder.withInvisible(); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index aafe3d1440..a91534668c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1060,9 +1060,9 @@ ModelEntityRenderer::ModelEntityRenderer(const EntityItemPointer& entity) : Pare void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed) { if (didVisualGeometryRequestSucceed) { - _itemKey = ItemKey::Builder().withTypeMeta().withTagBits(getViewTagBits()); + _itemKey = ItemKey::Builder().withTypeMeta().withTagBits(getTagMask()); } else { - _itemKey = ItemKey::Builder().withTypeMeta().withTypeShape().withTagBits(getViewTagBits()); + _itemKey = ItemKey::Builder().withTypeMeta().withTypeShape().withTagBits(getTagMask()); } } @@ -1070,11 +1070,11 @@ ItemKey ModelEntityRenderer::getKey() { return _itemKey; } -uint32_t ModelEntityRenderer::getViewTagBits() const { +render::hifi::Tag ModelEntityRenderer::getTagMask() const { // Default behavior for model is to not be visible in main view if cauterized (aka parented to the avatar's neck joint) return _cauterized ? - (_isVisibleInSecondaryCamera ? render::ItemKey::TAG_BITS_1 : render::ItemKey::TAG_BITS_NONE) : // draw in every view except the main one (view zero) (unless not visible in other views) - Parent::getViewTagBits(); // calculate which views to be shown in + (_isVisibleInSecondaryCamera ? render::hifi::TAG_SECONDARY_VIEW : render::hifi::TAG_NONE) : + Parent::getTagMask(); // calculate which views to be shown in } uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { @@ -1394,18 +1394,22 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->updateModelBounds(); entity->stopModelOverrideIfNoParent(); - uint32_t viewTaskBits = getViewTagBits(); - if (model->isVisible() != _visible || model->getViewTagBits() != viewTaskBits) { + render::hifi::Tag tagMask = getTagMask(); + if (model->isVisible() != _visible) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state // so most of the time we don't do anything in this function. - model->setVisibleInScene(_visible, scene, viewTaskBits, false); + model->setVisibleInScene(_visible, scene); + } + + if (model->getTagMask() != tagMask) { + model->setTagMask(tagMask, scene); } // TODO? early exit here when not visible? if (model->canCastShadow() != _canCastShadow) { - model->setCanCastShadow(_canCastShadow, scene, viewTaskBits, false); + model->setCanCastShadow(_canCastShadow, scene); } if (_needsCollisionGeometryUpdate) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 0553effc7c..a48a25b0b2 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -164,7 +164,7 @@ protected: void flagForCollisionGeometryUpdate(); void setCollisionMeshKey(const void* key); - uint32_t getViewTagBits() const override; + render::hifi::Tag getTagMask() const override; void setIsVisibleInSecondaryCamera(bool value) override; diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 7637fcf353..881c39c0bd 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -147,9 +147,9 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn ItemKey ParticleEffectEntityRenderer::getKey() { if (_visible) { - return ItemKey::Builder::transparentShape().withTagBits(getViewTagBits()); + return ItemKey::Builder::transparentShape().withTagBits(getTagMask()); } else { - return ItemKey::Builder().withInvisible().withTagBits(getViewTagBits()).build(); + return ItemKey::Builder().withInvisible().withTagBits(getTagMask()).build(); } } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index bbee59cd6b..7cab57123d 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -112,7 +112,7 @@ PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity) } ItemKey PolyLineEntityRenderer::getKey() { - return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getViewTagBits()); + return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getTagMask()); } ShapeKey PolyLineEntityRenderer::getShapeKey() { diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 6464a32989..7077ae799b 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -169,7 +169,7 @@ public: } protected: - virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape().withTagBits(getViewTagBits()); } + virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape().withTagBits(getTagMask()); } virtual ShapeKey getShapeKey() override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 65b5ca5740..69068b81d2 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -139,7 +139,7 @@ bool ShapeEntityRenderer::isTransparent() const { ItemKey ShapeEntityRenderer::getKey() { ItemKey::Builder builder; - builder.withTypeShape().withTypeMeta().withTagBits(getViewTagBits()); + builder.withTypeShape().withTypeMeta().withTagBits(getTagMask()); withReadLock([&] { if (isTransparent()) { diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index c3d5dc5bb9..c5035431f6 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -269,7 +269,7 @@ void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe ItemKey ZoneEntityRenderer::getKey() { - return ItemKey::Builder().withTypeMeta().withTagBits(getViewTagBits()).build(); + return ItemKey::Builder().withTypeMeta().withTagBits(getTagMask()).build(); } bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index 43c6b7a6a5..2db85eb7ac 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -46,7 +46,7 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b /**jsdoc * The AnimationProperties are used to configure an animation. - * @typedef Entities.AnimationProperties + * @typedef {object} Entities.AnimationProperties * @property {string} url="" - The URL of the FBX file that has the animation. * @property {number} fps=30 - The speed in frames/s that the animation is played at. * @property {number} firstFrame=0 - The first frame to play in the animation. diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 16a6249d91..70881fbc40 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -817,7 +817,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible); READ_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow); READ_ENTITY_PROPERTY(PROP_COLLISIONLESS, bool, setCollisionless); - READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint8_t, setCollisionMask); + READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint16_t, setCollisionMask); READ_ENTITY_PROPERTY(PROP_DYNAMIC, bool, setDynamic); READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData); @@ -1850,7 +1850,7 @@ void EntityItem::setCollisionless(bool value) { }); } -void EntityItem::setCollisionMask(uint8_t value) { +void EntityItem::setCollisionMask(uint16_t value) { withWriteLock([&] { if ((_collisionMask & ENTITY_COLLISION_MASK_DEFAULT) != (value & ENTITY_COLLISION_MASK_DEFAULT)) { _collisionMask = (value & ENTITY_COLLISION_MASK_DEFAULT); @@ -1915,7 +1915,7 @@ void EntityItem::setCreated(quint64 value) { }); } -void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const { +void EntityItem::computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask) const { if (_collisionless) { group = BULLET_COLLISION_GROUP_COLLISIONLESS; mask = 0; @@ -1928,7 +1928,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask group = BULLET_COLLISION_GROUP_STATIC; } - uint8_t userMask = getCollisionMask(); + uint16_t userMask = getCollisionMask(); if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) != (bool)(userMask & USER_COLLISION_GROUP_OTHER_AVATAR)) { @@ -1942,7 +1942,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask if ((bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } - mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); + mask = Physics::getDefaultCollisionMask(group) & (int32_t)(userMask); } } @@ -2818,8 +2818,8 @@ bool EntityItem::getCollisionless() const { return result; } -uint8_t EntityItem::getCollisionMask() const { - uint8_t result; +uint16_t EntityItem::getCollisionMask() const { + uint16_t result; withReadLock([&] { result = _collisionMask; }); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 6f5b1fdca2..0acf8dbbc1 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -291,10 +291,10 @@ public: bool getCollisionless() const; void setCollisionless(bool value); - uint8_t getCollisionMask() const; - void setCollisionMask(uint8_t value); + uint16_t getCollisionMask() const; + void setCollisionMask(uint16_t value); - void computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const; + void computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask) const; bool getDynamic() const; void setDynamic(bool value); @@ -584,7 +584,7 @@ protected: bool _isVisibleInSecondaryCamera { ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA }; bool _canCastShadow{ ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW }; bool _collisionless { ENTITY_ITEM_DEFAULT_COLLISIONLESS }; - uint8_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT }; + uint16_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT }; bool _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC }; bool _locked { ENTITY_ITEM_DEFAULT_LOCKED }; QString _userData { ENTITY_ITEM_DEFAULT_USER_DATA }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 1767f81ddb..be1f050339 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -130,7 +130,7 @@ void buildStringToMaterialMappingModeLookup() { addMaterialMappingMode(PROJECTED); } -QString getCollisionGroupAsString(uint8_t group) { +QString getCollisionGroupAsString(uint16_t group) { switch (group) { case USER_COLLISION_GROUP_DYNAMIC: return "dynamic"; @@ -146,7 +146,7 @@ QString getCollisionGroupAsString(uint8_t group) { return ""; } -uint8_t getCollisionGroupAsBitMask(const QStringRef& name) { +uint16_t getCollisionGroupAsBitMask(const QStringRef& name) { if (0 == name.compare(QString("dynamic"))) { return USER_COLLISION_GROUP_DYNAMIC; } else if (0 == name.compare(QString("static"))) { @@ -164,7 +164,7 @@ uint8_t getCollisionGroupAsBitMask(const QStringRef& name) { QString EntityItemProperties::getCollisionMaskAsString() const { QString maskString(""); for (int i = 0; i < NUM_USER_COLLISION_GROUPS; ++i) { - uint8_t group = 0x01 << i; + uint16_t group = 0x0001 << i; if (group & _collisionMask) { maskString.append(getCollisionGroupAsString(group)); maskString.append(','); @@ -175,7 +175,7 @@ QString EntityItemProperties::getCollisionMaskAsString() const { void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) { QVector groups = maskString.splitRef(','); - uint8_t mask = 0x00; + uint16_t mask = 0x0000; for (auto groupName : groups) { mask |= getCollisionGroupAsBitMask(groupName); } @@ -487,10 +487,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {boolean} locked=false - Whether or not the entity can be edited or deleted. If true then the * entity's properties other than locked cannot be changed, and the entity cannot be deleted. * @property {boolean} visible=true - Whether or not the entity is rendered. If true then the entity is rendered. - * @property {boolean} canCastShadows=true - Whether or not the entity casts shadows. Currently applicable only to + * @property {boolean} canCastShadow=true - Whether or not the entity can cast a shadow. Currently applicable only to * {@link Entities.EntityType|Model} and {@link Entities.EntityType|Shape} entities. Shadows are cast if inside a * {@link Entities.EntityType|Zone} entity with castShadows enabled in its * {@link Entities.EntityProperties-Zone|keyLight} property. + * @property {boolean} isVisibleInSecondaryCamera=true - Whether or not the entity is rendered in the secondary camera. If true then the entity is rendered. * * @property {Vec3} position=0,0,0 - The position of the entity. * @property {Quat} rotation=0,0,0,1 - The orientation of the entity with respect to world coordinates. @@ -1400,7 +1401,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool /**jsdoc * The axis-aligned bounding box of an entity. - * @typedef Entities.BoundingBox + * @typedef {object} Entities.BoundingBox * @property {Vec3} brn - The bottom right near (minimum axes values) corner of the AA box. * @property {Vec3} tfl - The top far left (maximum axes values) corner of the AA box. * @property {Vec3} center - The center of the AA box. @@ -1522,7 +1523,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(localRenderAlpha, float, setLocalRenderAlpha); COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionless, bool, setCollisionless); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ignoreForCollisions, bool, setCollisionless, getCollisionless); // legacy support - COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionMask, uint8_t, setCollisionMask); + COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionMask, uint16_t, setCollisionMask); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(collidesWith, CollisionMask); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(collisionsWillMove, bool, setDynamic, getDynamic); // legacy support COPY_PROPERTY_FROM_QSCRIPTVALUE(dynamic, bool, setDynamic); @@ -2581,7 +2582,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONLESS, bool, setCollisionless); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_MASK, uint8_t, setCollisionMask); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_MASK, uint16_t, setCollisionMask); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DYNAMIC, bool, setDynamic); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index f69a096a31..e46eb73910 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -148,7 +148,7 @@ public: DEFINE_PROPERTY_REF(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3, ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY); DEFINE_PROPERTY(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING); DEFINE_PROPERTY(PROP_COLLISIONLESS, Collisionless, collisionless, bool, ENTITY_ITEM_DEFAULT_COLLISIONLESS); - DEFINE_PROPERTY(PROP_COLLISION_MASK, CollisionMask, collisionMask, uint8_t, ENTITY_COLLISION_MASK_DEFAULT); + DEFINE_PROPERTY(PROP_COLLISION_MASK, CollisionMask, collisionMask, uint16_t, ENTITY_COLLISION_MASK_DEFAULT); DEFINE_PROPERTY(PROP_DYNAMIC, Dynamic, dynamic, bool, ENTITY_ITEM_DEFAULT_DYNAMIC); DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool, LightEntityItem::DEFAULT_IS_SPOTLIGHT); DEFINE_PROPERTY(PROP_INTENSITY, Intensity, intensity, float, LightEntityItem::DEFAULT_INTENSITY); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f751a4f8ed..98407a74c6 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1688,15 +1688,21 @@ QVector EntityScriptingInterface::getChildrenIDs(const QUuid& parentID) { if (!_entityTree) { return result; } - - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(parentID); - if (!entity) { - qCDebug(entities) << "EntityScriptingInterface::getChildrenIDs - no entity with ID" << parentID; - return result; - } - _entityTree->withReadLock([&] { - entity->forEachChild([&](SpatiallyNestablePointer child) { + QSharedPointer parentFinder = DependencyManager::get(); + if (!parentFinder) { + return; + } + bool success; + SpatiallyNestableWeakPointer parentWP = parentFinder->find(parentID, success); + if (!success) { + return; + } + SpatiallyNestablePointer parent = parentWP.lock(); + if (!parent) { + return; + } + parent->forEachChild([&](SpatiallyNestablePointer child) { result.push_back(child->getID()); }); }); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 9acbc13251..e63f3ad855 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -1226,12 +1226,11 @@ public slots: /**jsdoc - * Get the IDs of entities, overlays, and avatars that are directly parented to an entity. To get all descendants of an - * entity, recurse on the IDs returned by the function. + * Get the IDs of entities, overlays, and avatars that are directly parented to an entity, overlay, or avatar model. Recurse on the IDs returned by the function to get all descendants of an entity, overlay, or avatar. * @function Entities.getChildrenIDs - * @param {Uuid} parentID - The ID of the entity to get the children IDs of. + * @param {Uuid} parentID - The ID of the entity, overlay, or avatar to get the children IDs of. * @returns {Uuid[]} An array of entity, overlay, and avatar IDs that are parented directly to the parentID - * entity. Does not include children's children, etc. The array is empty if no children can be found or + * entity, overlay, or avatar. Does not include children's children, etc. The array is empty if no children can be found or * parentID cannot be found. * @example Report the children of an entity. * function createEntity(description, position, parent) { diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index 2be33787de..b966b78fc7 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -35,7 +35,7 @@ class ReadBitstreamToTreeParams; * @property {Vec3} direction=0,-1,0 - The direction the light is shining. * @property {boolean} castShadows=false - If true 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.EntityProperties|canCastShadows} property set to true. + * {@link Entities.EntityProperties|canCastShadow} property set to true. */ class KeyLightPropertyGroup : public PropertyGroup { public: diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 239908f86c..fc94236c96 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -258,6 +258,11 @@ public: QHash texcoordSetMap; }; +/**jsdoc + * @typedef {object} FBXAnimationFrame + * @property {Quat[]} rotations + * @property {Vec3[]} translations + */ /// A single animation frame extracted from an FBX document. class FBXAnimationFrame { public: diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index f322c2319e..1fa4b3873e 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -1174,7 +1174,7 @@ bool GLTFReader::addArrayOfType(const QByteArray& bin, int byteOffset, int byteL break; } case GLTFAccessorComponentType::UNSIGNED_INT: { - readArray(bin, byteOffset, byteLength, outarray, accessorType); + readArray(bin, byteOffset, byteLength, outarray, accessorType); break; } case GLTFAccessorComponentType::UNSIGNED_SHORT: { diff --git a/libraries/fbx/src/GLTFReader.h b/libraries/fbx/src/GLTFReader.h index 3554594768..28c1d8282f 100644 --- a/libraries/fbx/src/GLTFReader.h +++ b/libraries/fbx/src/GLTFReader.h @@ -190,7 +190,7 @@ namespace GLTFBufferViewTarget { struct GLTFBufferView { int buffer; //required int byteLength; //required - int byteOffset; + int byteOffset { 0 }; int target; QMap defined; void dump() { @@ -470,7 +470,7 @@ namespace GLTFAccessorComponentType { } struct GLTFAccessor { int bufferView; - int byteOffset; + int byteOffset { 0 }; int componentType; //required int count; //required int type; //required diff --git a/libraries/gl/src/gl/GLShaders.cpp b/libraries/gl/src/gl/GLShaders.cpp index ecd6fe3323..9bfe214fcf 100644 --- a/libraries/gl/src/gl/GLShaders.cpp +++ b/libraries/gl/src/gl/GLShaders.cpp @@ -2,15 +2,64 @@ #include "GLLogging.h" -namespace gl { +#include +#include +#include +#include +#include +#include + +using namespace gl; + +void Uniform::load(GLuint glprogram, int index) { + const GLint NAME_LENGTH = 256; + GLchar glname[NAME_LENGTH]; + GLint length = 0; + glGetActiveUniform(glprogram, index, NAME_LENGTH, &length, &size, &type, glname); + name = std::string(glname, length); + location = glGetUniformLocation(glprogram, glname); +} + +Uniforms gl::loadUniforms(GLuint glprogram) { + GLint uniformsCount = 0; + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); + + Uniforms result; + result.resize(uniformsCount); + for (int i = 0; i < uniformsCount; i++) { + result[i].load(glprogram, i); + } + return result; +} #ifdef SEPARATE_PROGRAM - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& message) { +bool gl::compileShader(GLenum shaderDomain, + const std::string& shaderSource, + GLuint& shaderObject, + GLuint& programObject, + std::string& message) { + return compileShader(shaderDomain, std::vector{ shaderSource }, shaderObject, programObject, message); +} #else - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& message) { +bool gl::compileShader(GLenum shaderDomain, const std::string& shaderSource, GLuint& shaderObject, std::string& message) { + return compileShader(shaderDomain, std::vector{ shaderSource }, shaderObject, message); +} #endif - if (shaderSource.empty()) { + +#ifdef SEPARATE_PROGRAM +bool gl::compileShader(GLenum shaderDomain, + const std::string& shaderSource, + GLuint& shaderObject, + GLuint& programObject, + std::string& message) { +#else +bool gl::compileShader(GLenum shaderDomain, + const std::vector& shaderSources, + GLuint& shaderObject, + std::string& message) { +#endif + if (shaderSources.empty()) { qCDebug(glLogging) << "GLShader::compileShader - no GLSL shader source code ? so failed to create"; return false; } @@ -23,9 +72,11 @@ namespace gl { } // Assign the source - const int NUM_SOURCE_STRINGS = 2; - const GLchar* srcstr[] = { defines.c_str(), shaderSource.c_str() }; - glShaderSource(glshader, NUM_SOURCE_STRINGS, srcstr, NULL); + std::vector cstrs; + for (const auto& str : shaderSources) { + cstrs.push_back(str.c_str()); + } + glShaderSource(glshader, static_cast(cstrs.size()), cstrs.data(), NULL); // Compile ! glCompileShader(glshader); @@ -66,7 +117,7 @@ namespace gl { qCCritical(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:"; int lineNumber = 0; - for (auto s : srcstr) { + for (const auto& s : cstrs) { QString str(s); QStringList lines = str.split("\n"); for (auto& line : lines) { @@ -142,7 +193,7 @@ namespace gl { return true; } -GLuint compileProgram(const std::vector& glshaders, std::string& message, std::vector& binary) { +GLuint gl::compileProgram(const std::vector& glshaders, std::string& message, CachedShader& cachedShader) { // A brand new program: GLuint glprogram = glCreateProgram(); if (!glprogram) { @@ -150,14 +201,21 @@ GLuint compileProgram(const std::vector& glshaders, std::string& message return 0; } - // glProgramParameteri(glprogram, GL_PROGRAM_, GL_TRUE); - // Create the program from the sub shaders - for (auto so : glshaders) { - glAttachShader(glprogram, so); - } + bool binaryLoaded = false; - // Link! - glLinkProgram(glprogram); + if (glshaders.empty() && cachedShader) { + glProgramBinary(glprogram, cachedShader.format, cachedShader.binary.data(), (GLsizei)cachedShader.binary.size()); + binaryLoaded = true; + } else { + // glProgramParameteri(glprogram, GL_PROGRAM_, GL_TRUE); + // Create the program from the sub shaders + for (auto so : glshaders) { + glAttachShader(glprogram, so); + } + + // Link! + glLinkProgram(glprogram); + } GLint linked = 0; glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); @@ -205,25 +263,73 @@ GLuint compileProgram(const std::vector& glshaders, std::string& message } // If linked get the binaries - if (linked) { + if (linked && !binaryLoaded) { GLint binaryLength = 0; glGetProgramiv(glprogram, GL_PROGRAM_BINARY_LENGTH, &binaryLength); - if (binaryLength > 0) { - GLint numBinFormats = 0; - glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numBinFormats); - if (numBinFormats > 0) { - binary.resize(binaryLength); - std::vector binFormats(numBinFormats); - glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, binFormats.data()); - - GLenum programBinFormat; - glGetProgramBinary(glprogram, binaryLength, NULL, &programBinFormat, binary.data()); - } + cachedShader.binary.resize(binaryLength); + glGetProgramBinary(glprogram, binaryLength, NULL, &cachedShader.format, cachedShader.binary.data()); } } return glprogram; } +const QString& getShaderCacheFile() { + static const QString SHADER_CACHE_FOLDER{ "shaders" }; + static const QString SHADER_CACHE_FILE_NAME{ "cache.json" }; + static const QString SHADER_CACHE_FILE = FileUtils::standardPath(SHADER_CACHE_FOLDER) + SHADER_CACHE_FILE_NAME; + return SHADER_CACHE_FILE; +} + +static const char* SHADER_JSON_TYPE_KEY = "type"; +static const char* SHADER_JSON_SOURCE_KEY = "source"; +static const char* SHADER_JSON_DATA_KEY = "data"; + +void gl::loadShaderCache(ShaderCache& cache) { + QString shaderCacheFile = getShaderCacheFile(); + if (QFileInfo(shaderCacheFile).exists()) { + QString json = FileUtils::readFile(shaderCacheFile); + auto root = QJsonDocument::fromJson(json.toUtf8()).object(); + for (const auto& qhash : root.keys()) { + auto programObject = root[qhash].toObject(); + QByteArray qbinary = QByteArray::fromBase64(programObject[SHADER_JSON_DATA_KEY].toString().toUtf8()); + std::string hash = qhash.toStdString(); + auto& cachedShader = cache[hash]; + cachedShader.binary.resize(qbinary.size()); + memcpy(cachedShader.binary.data(), qbinary.data(), qbinary.size()); + cachedShader.format = (GLenum)programObject[SHADER_JSON_TYPE_KEY].toInt(); + cachedShader.source = programObject[SHADER_JSON_SOURCE_KEY].toString().toStdString(); + } + } +} + +void gl::saveShaderCache(const ShaderCache& cache) { + QByteArray json; + { + QVariantMap variantMap; + for (const auto& entry : cache) { + const auto& key = entry.first; + const auto& type = entry.second.format; + const auto& binary = entry.second.binary; + QVariantMap qentry; + qentry[SHADER_JSON_TYPE_KEY] = QVariant(type); + qentry[SHADER_JSON_SOURCE_KEY] = QString(entry.second.source.c_str()); + qentry[SHADER_JSON_DATA_KEY] = QByteArray{ binary.data(), (int)binary.size() }.toBase64(); + variantMap[key.c_str()] = qentry; + } + json = QJsonDocument::fromVariant(variantMap).toJson(QJsonDocument::Indented); + } + + if (!json.isEmpty()) { + QString shaderCacheFile = getShaderCacheFile(); + QFile saveFile(shaderCacheFile); + saveFile.open(QFile::WriteOnly | QFile::Text | QFile::Truncate); + saveFile.write(json); + saveFile.close(); + } +} + +std::string gl::getShaderHash(const std::string& shaderSource) { + return QCryptographicHash::hash(QByteArray(shaderSource.c_str()), QCryptographicHash::Md5).toBase64().toStdString(); } diff --git a/libraries/gl/src/gl/GLShaders.h b/libraries/gl/src/gl/GLShaders.h index fc070d7659..e6c11b4eb3 100644 --- a/libraries/gl/src/gl/GLShaders.h +++ b/libraries/gl/src/gl/GLShaders.h @@ -14,15 +14,47 @@ #include #include +#include namespace gl { + + struct Uniform { + std::string name; + GLint size{ -1 }; + GLenum type{ GL_FLOAT }; + GLint location{ -1 }; + void load(GLuint glprogram, int index); + }; + + using Uniforms = std::vector; + + Uniforms loadUniforms(GLuint glprogram); + + struct CachedShader { + GLenum format{ 0 }; + std::string source; + std::vector binary; + inline operator bool() const { + return format != 0 && !binary.empty(); + } + }; + + using ShaderCache = std::unordered_map; + + std::string getShaderHash(const std::string& shaderSource); + void loadShaderCache(ShaderCache& cache); + void saveShaderCache(const ShaderCache& cache); + + #ifdef SEPARATE_PROGRAM - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& message); + bool compileShader(GLenum shaderDomain, const std::string& shaderSource, GLuint &shaderObject, GLuint &programObject, std::string& message); + bool compileShader(GLenum shaderDomain, const std::vector& shaderSources, GLuint &shaderObject, GLuint &programObject, std::string& message); #else - bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& message); + bool compileShader(GLenum shaderDomain, const std::string& shaderSource, GLuint &shaderObject, std::string& message); + bool compileShader(GLenum shaderDomain, const std::vector& shaderSources, GLuint &shaderObject, std::string& message); #endif - GLuint compileProgram(const std::vector& glshaders, std::string& message, std::vector& binary); + GLuint compileProgram(const std::vector& glshaders, std::string& message, CachedShader& binary); } diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index f484de57f1..2321342eb4 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -124,13 +124,16 @@ void GLBackend::init() { GLBackend::GLBackend() { _pipeline._cameraCorrectionBuffer._buffer->flush(); glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment); + initShaderBinaryCache(); } +GLBackend::~GLBackend() {} -GLBackend::~GLBackend() { +void GLBackend::shutdown() { killInput(); killTransform(); killTextureManagementStage(); + killShaderBinaryCache(); } void GLBackend::renderPassTransfer(const Batch& batch) { diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 32c75d0363..622c8f1081 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -71,6 +72,9 @@ public: virtual ~GLBackend(); + // Shutdown rendering and persist any required resources + void shutdown() override; + void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset = false); void render(const Batch& batch) final override; @@ -455,6 +459,13 @@ protected: virtual GLShader* compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler); virtual GLShader* compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler); virtual std::string getBackendShaderHeader() const = 0; + // For a program, this will return a string containing all the source files (without any + // backend headers or defines). For a vertex, fragment or geometry shader, this will + // return the fully customized shader with all the version and backend specific + // preprocessor directives + // The program string returned can be used as a key for a cache of shader binaries + // The shader strings can be reliably sent to the low level `compileShader` functions + virtual std::string getShaderSource(const Shader& shader, int version) final; virtual void makeProgramBindings(ShaderObject& shaderObject); class ElementResource { public: @@ -465,12 +476,12 @@ protected: ElementResource getFormatFromGLUniform(GLenum gltype); static const GLint UNUSED_SLOT {-1}; static bool isUnusedSlot(GLint binding) { return (binding == UNUSED_SLOT); } - virtual int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + virtual int makeUniformSlots(const ShaderObject& program, const Shader::BindingSet& slotBindings, Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers); - virtual int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers); - virtual int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers) = 0; - virtual int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs); - virtual int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs); + virtual int makeUniformBlockSlots(const ShaderObject& program, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers); + virtual int makeResourceBufferSlots(const ShaderObject& program, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers) = 0; + virtual int makeInputSlots(const ShaderObject& program, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs); + virtual int makeOutputSlots(const ShaderObject& program, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs); // Synchronize the state cache of this Backend with the actual real state of the GL Context @@ -489,6 +500,19 @@ protected: void resetStages(); + // Stores cached binary versions of the shaders for quicker startup on subsequent runs + // Note that shaders in the cache can still fail to load due to hardware or driver + // changes that invalidate the cached binary, in which case we fall back on compiling + // the source again + struct ShaderBinaryCache { + std::mutex _mutex; + std::vector _formats; + std::unordered_map _binaries; + } _shaderBinaryCache; + + virtual void initShaderBinaryCache(); + virtual void killShaderBinaryCache(); + struct TextureManagementStageState { bool _sparseCapable { false }; GLTextureTransferEnginePointer _transferEngine; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp index bf36c134de..af6a0df297 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp @@ -11,6 +11,8 @@ using namespace gpu; using namespace gpu::gl; +using CachedShader = ::gl::CachedShader; + // Shader domain static const size_t NUM_SHADER_DOMAINS = 3; @@ -68,9 +70,45 @@ static const std::array VERSION_DEFINES { { stereoVersion } }; +static std::string getShaderTypeString(Shader::Type type) { + switch (type) { + case Shader::Type::VERTEX: + return "vertex"; + case Shader::Type::PIXEL: + return "pixel"; + case Shader::Type::GEOMETRY: + return "geometry"; + case Shader::Type::PROGRAM: + return "program"; + default: + qFatal("Unexpected shader type %d", type); + Q_UNREACHABLE(); + } +} + +std::string GLBackend::getShaderSource(const Shader& shader, int version) { + if (shader.isProgram()) { + std::string result; + result.append("// VERSION " + std::to_string(version)); + for (const auto& subShader : shader.getShaders()) { + result.append("//-------- "); + result.append(getShaderTypeString(subShader->getType())); + result.append("\n"); + result.append(subShader->getSource().getCode()); + } + return result; + } + + std::string shaderDefines = getBackendShaderHeader() + "\n" + + (supportsBindless() ? textureTableVersion : "\n") + + DOMAIN_DEFINES[shader.getType()] + "\n" + + VERSION_DEFINES[version]; + + return shaderDefines + "\n" + shader.getSource().getCode(); +} + GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler) { // Any GLSLprogram ? normally yes... - const std::string& shaderSource = shader.getSource().getCode(); GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; GLShader::ShaderObjects shaderObjects; Shader::CompilationLogs compilationLogs(GLShader::NumVersions); @@ -78,11 +116,7 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co for (int version = 0; version < GLShader::NumVersions; version++) { auto& shaderObject = shaderObjects[version]; - - std::string shaderDefines = getBackendShaderHeader() + "\n" - + (supportsBindless() ? textureTableVersion : "\n") - + DOMAIN_DEFINES[shader.getType()] + "\n" - + VERSION_DEFINES[version]; + auto shaderSource = getShaderSource(shader, version); if (handler) { bool retest = true; std::string currentSrc = shaderSource; @@ -90,7 +124,7 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co // The retest bool is set to false as soon as the compilation succeed to wexit the while loop. // The handler tells us if we should retry or not while returning a modified version of the source. while (retest) { - bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderDefines, shaderObject.glshader, compilationLogs[version].message); + bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderObject.glshader, compilationLogs[version].message); compilationLogs[version].compiled = result; if (!result) { std::string newSrc; @@ -101,7 +135,7 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co } } } else { - compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message); + compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderObject.glshader, compilationLogs[version].message); } if (!compilationLogs[version].compiled) { @@ -120,43 +154,80 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co return object; } +std::atomic gpuBinaryShadersLoaded; + GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler) { if (!program.isProgram()) { return nullptr; } GLShader::ShaderObjects programObjects; - program.incrementCompilationAttempt(); Shader::CompilationLogs compilationLogs(GLShader::NumVersions); for (int version = 0; version < GLShader::NumVersions; version++) { auto& programObject = programObjects[version]; + auto programSource = getShaderSource(program, version); + auto hash = ::gl::getShaderHash(programSource); - // Let's go through every shaders and make sure they are ready to go - std::vector< GLuint > shaderGLObjects; - for (auto subShader : program.getShaders()) { - auto object = GLShader::sync((*this), *subShader, handler); - if (object) { - shaderGLObjects.push_back(object->_shaderObjects[version].glshader); - } else { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?"; - compilationLogs[version].compiled = false; - compilationLogs[version].message = std::string("Failed to compile, one of the shaders of the program is not compiled ?"); - program.setCompilationLogs(compilationLogs); - return nullptr; + CachedShader cachedBinary; + { + Lock shaderCacheLock{ _shaderBinaryCache._mutex }; + if (_shaderBinaryCache._binaries.count(hash) != 0) { + cachedBinary = _shaderBinaryCache._binaries[hash]; + } + } + + + GLuint glprogram = 0; + + // If we have a cached binary program, try to load it instead of compiling the individual shaders + if (cachedBinary) { + glprogram = ::gl::compileProgram({}, compilationLogs[version].message, cachedBinary); + if (0 != glprogram) { + ++gpuBinaryShadersLoaded; + } + } + + // If we have no program, then either no cached binary, or the binary failed to load (perhaps a GPU driver update invalidated the cache) + if (0 == glprogram) { + cachedBinary = CachedShader(); + { + std::unique_lock shaderCacheLock{ _shaderBinaryCache._mutex }; + _shaderBinaryCache._binaries.erase(hash); + } + // Let's go through every shaders and make sure they are ready to go + std::vector shaderGLObjects; + shaderGLObjects.reserve(program.getShaders().size()); + for (auto subShader : program.getShaders()) { + auto object = GLShader::sync((*this), *subShader, handler); + if (object) { + shaderGLObjects.push_back(object->_shaderObjects[version].glshader); + } else { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?"; + compilationLogs[version].compiled = false; + compilationLogs[version].message = std::string("Failed to compile, one of the shaders of the program is not compiled ?"); + program.setCompilationLogs(compilationLogs); + return nullptr; + } + } + + glprogram = ::gl::compileProgram(shaderGLObjects, compilationLogs[version].message, cachedBinary); + if (cachedBinary) { + cachedBinary.source = programSource; + std::unique_lock shaderCacheLock{ _shaderBinaryCache._mutex }; + _shaderBinaryCache._binaries[hash] = cachedBinary; } } - GLuint glprogram = ::gl::compileProgram(shaderGLObjects, compilationLogs[version].message, compilationLogs[version].binary); if (glprogram == 0) { qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str(); program.setCompilationLogs(compilationLogs); return nullptr; } + compilationLogs[version].compiled = true; programObject.glprogram = glprogram; - makeProgramBindings(programObject); } // Compilation feedback @@ -338,20 +409,15 @@ GLBackend::ElementResource GLBackend::getFormatFromGLUniform(GLenum gltype) { }; -int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, +int GLBackend::makeUniformSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings, Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { - GLint uniformsCount = 0; + auto& glprogram = shaderProgram.glprogram; - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); - - for (int i = 0; i < uniformsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - GLint location = glGetUniformLocation(glprogram, name); + for (const auto& uniform : shaderProgram.uniforms) { + const auto& type = uniform.type; + const auto& location = uniform.location; + const auto& size = uniform.size; + const auto& name = uniform.name; const GLint INVALID_UNIFORM_LOCATION = -1; // Try to make sense of the gltype @@ -359,8 +425,8 @@ int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slot // The uniform as a standard var type if (location != INVALID_UNIFORM_LOCATION) { + auto sname = uniform.name; // Let's make sure the name doesn't contains an array element - std::string sname(name); auto foundBracket = sname.find_first_of('['); if (foundBracket != std::string::npos) { // std::string arrayname = sname.substr(0, foundBracket); @@ -397,10 +463,11 @@ int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slot } } - return uniformsCount; + return static_cast(shaderProgram.uniforms.size()); } -int GLBackend::makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { +int GLBackend::makeUniformBlockSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { + const auto& glprogram = shaderProgram.glprogram; GLint buffersCount = 0; glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); @@ -479,7 +546,8 @@ int GLBackend::makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& return buffersCount; } -int GLBackend::makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { +int GLBackend::makeInputSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { + const auto& glprogram = shaderProgram.glprogram; GLint inputsCount = 0; glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); @@ -501,7 +569,7 @@ int GLBackend::makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBi return inputsCount; } -int GLBackend::makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { +int GLBackend::makeOutputSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { /* GLint outputsCount = 0; glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); @@ -525,67 +593,19 @@ void GLBackend::makeProgramBindings(ShaderObject& shaderObject) { if (!shaderObject.glprogram) { return; } - GLuint glprogram = shaderObject.glprogram; - GLint loc = -1; - - //Check for gpu specific attribute slotBindings - loc = glGetAttribLocation(glprogram, "inPosition"); - if (loc >= 0 && loc != gpu::Stream::POSITION) { - glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition"); - } - - loc = glGetAttribLocation(glprogram, "inNormal"); - if (loc >= 0 && loc != gpu::Stream::NORMAL) { - glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal"); - } - - loc = glGetAttribLocation(glprogram, "inColor"); - if (loc >= 0 && loc != gpu::Stream::COLOR) { - glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor"); - } - - loc = glGetAttribLocation(glprogram, "inTexCoord0"); - if (loc >= 0 && loc != gpu::Stream::TEXCOORD) { - glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0"); - } - - loc = glGetAttribLocation(glprogram, "inTangent"); - if (loc >= 0 && loc != gpu::Stream::TANGENT) { - glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent"); - } - - char attribName[] = "inTexCoordn"; - for (auto i = 0; i < 4; i++) { - auto streamId = gpu::Stream::TEXCOORD1 + i; - - attribName[strlen(attribName) - 1] = '1' + i; - loc = glGetAttribLocation(glprogram, attribName); - if (loc >= 0 && loc != streamId) { - glBindAttribLocation(glprogram, streamId, attribName); - } - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterIndex"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex"); - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterWeight"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight"); - } - - loc = glGetAttribLocation(glprogram, "_drawCallInfo"); - if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) { - glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo"); - } - - // Link again to take into account the assigned attrib location - glLinkProgram(glprogram); - - GLint linked = 0; - glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) { - qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; - } +} + + +void GLBackend::initShaderBinaryCache() { + GLint numBinFormats = 0; + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numBinFormats); + if (numBinFormats > 0) { + _shaderBinaryCache._formats.resize(numBinFormats); + glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, _shaderBinaryCache._formats.data()); + } + ::gl::loadShaderCache(_shaderBinaryCache._binaries); +} + +void GLBackend::killShaderBinaryCache() { + ::gl::saveShaderCache(_shaderBinaryCache._binaries); } diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLShader.cpp index 010a7c479c..0a527185ef 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLShader.cpp @@ -68,22 +68,23 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin for (int version = 0; version < GLShader::NumVersions; version++) { auto& shaderObject = object->_shaderObjects[version]; if (shaderObject.glprogram) { + shaderObject.uniforms = ::gl::loadUniforms(shaderObject.glprogram); Shader::SlotSet buffers; - backend.makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers); + backend.makeUniformBlockSlots(shaderObject, slotBindings, buffers); Shader::SlotSet uniforms; Shader::SlotSet textures; Shader::SlotSet samplers; - backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers); + backend.makeUniformSlots(shaderObject, slotBindings, uniforms, textures, samplers); Shader::SlotSet resourceBuffers; - backend.makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers); + backend.makeResourceBufferSlots(shaderObject, slotBindings, resourceBuffers); Shader::SlotSet inputs; - backend.makeInputSlots(shaderObject.glprogram, slotBindings, inputs); + backend.makeInputSlots(shaderObject, slotBindings, inputs); Shader::SlotSet outputs; - backend.makeOutputSlots(shaderObject.glprogram, slotBindings, outputs); + backend.makeOutputSlots(shaderObject, slotBindings, outputs); // Define the public slots only from the default version if (version == 0) { diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLShader.h b/libraries/gpu-gl-common/src/gpu/gl/GLShader.h index 3259982e93..0ba77e50c6 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLShader.h @@ -9,14 +9,17 @@ #define hifi_gpu_gl_GLShader_h #include "GLShared.h" +#include namespace gpu { namespace gl { struct ShaderObject { + using Uniforms = ::gl::Uniforms; GLuint glshader { 0 }; GLuint glprogram { 0 }; GLint transformCameraSlot { -1 }; GLint transformObjectSlot { -1 }; + Uniforms uniforms; }; class GLShader : public GPUObject { diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 23dcac0d8d..e840b9fe78 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -173,7 +173,7 @@ protected: std::string getBackendShaderHeader() const override; void makeProgramBindings(ShaderObject& shaderObject) override; - int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override; + int makeResourceBufferSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override; }; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp index 64c9033cf7..0fa1b1bf42 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp @@ -22,20 +22,13 @@ std::string GL41Backend::getBackendShaderHeader() const { return header; } -int GL41Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { +int GL41Backend::makeResourceBufferSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { GLint ssboCount = 0; - GLint uniformsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); - - for (int i = 0; i < uniformsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - GLint location = glGetUniformLocation(glprogram, name); + const auto& glprogram = shaderProgram.glprogram; + for (const auto& uniform : shaderProgram.uniforms) { + const auto& name = uniform.name; + const auto& type = uniform.type; + const auto& location = uniform.location; const GLint INVALID_UNIFORM_LOCATION = -1; // Try to make sense of the gltype diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 503e1df922..a255cc5878 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -31,6 +31,10 @@ bool GL41Backend::supportedTextureFormat(const gpu::Element& format) { case gpu::Semantic::COMPRESSED_EAC_RED_SIGNED: case gpu::Semantic::COMPRESSED_EAC_XY: case gpu::Semantic::COMPRESSED_EAC_XY_SIGNED: + // The ARB_texture_compression_bptc extension is not supported on 4.1 + // See https://www.g-truc.net/doc/OpenGL%204%20Hardware%20Matrix.pdf + case gpu::Semantic::COMPRESSED_BC6_RGB: + case gpu::Semantic::COMPRESSED_BC7_SRGBA: return false; default: return true; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 0db9271f57..cb7ddce930 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -274,7 +274,7 @@ protected: // Shader Stage std::string getBackendShaderHeader() const override; void makeProgramBindings(ShaderObject& shaderObject) override; - int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override; + int makeResourceBufferSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override; // Texture Management Stage void initTextureManagementStage() override; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp index 44e439df55..a95426a06d 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp @@ -27,7 +27,8 @@ std::string GL45Backend::getBackendShaderHeader() const { return header; } -int GL45Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { +int GL45Backend::makeResourceBufferSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { + const auto& glprogram = shaderProgram.glprogram; GLint buffersCount = 0; glGetProgramInterfaceiv(glprogram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &buffersCount); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 558f221705..fda7ac22dd 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -33,6 +33,8 @@ using namespace gpu::gl45; bool GL45Backend::supportedTextureFormat(const gpu::Element& format) { switch (format.getSemantic()) { + // ETC textures are actually required by the OpenGL spec as of 4.3, but aren't always supported by hardware + // They'll be recompressed by OpenGL, which will be slow or have poor quality, so disable them for now case gpu::Semantic::COMPRESSED_ETC2_RGB: case gpu::Semantic::COMPRESSED_ETC2_SRGB: case gpu::Semantic::COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA: diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h index cb8e4abb29..9656d29ac5 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h @@ -164,7 +164,7 @@ protected: std::string getBackendShaderHeader() const override; void makeProgramBindings(ShaderObject& shaderObject) override; - int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override; + int makeResourceBufferSlots(const ShaderObject& shaderObject, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override; }; diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp index 7e8056ba79..34caa97696 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp @@ -25,20 +25,15 @@ std::string GLESBackend::getBackendShaderHeader() const { return header; } -int GLESBackend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { +int GLESBackend::makeResourceBufferSlots(const ShaderObject& shaderObject, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { GLint ssboCount = 0; - GLint uniformsCount = 0; + GLint uniformsCount = 0; + const auto& glprogram = shaderObject.glprogram; - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); - - for (int i = 0; i < uniformsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - GLint location = glGetUniformLocation(glprogram, name); + for (const auto& uniform : shaderObject.uniforms) { + const auto& type = uniform.type; + const auto& location = uniform.location; + const auto& name = uniform.name; const GLint INVALID_UNIFORM_LOCATION = -1; // Try to make sense of the gltype diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 909ed23669..bb6b27626a 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -53,6 +53,13 @@ Context::~Context() { _batchPool.clear(); } +void Context::shutdown() { + if (_backend) { + _backend->shutdown(); + _backend.reset(); + } +} + const std::string& Context::getBackendVersion() const { return _backend->getVersion(); } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 4683f442e0..4560ea5526 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -54,6 +54,7 @@ class Backend { public: virtual ~Backend(){}; + virtual void shutdown() {} virtual const std::string& getVersion() const = 0; void setStereoState(const StereoState& stereo) { _stereo = stereo; } @@ -154,6 +155,7 @@ public: Context(); ~Context(); + void shutdown(); const std::string& getBackendVersion() const; void beginFrame(const glm::mat4& renderView = glm::mat4(), const glm::mat4& renderPose = glm::mat4()); diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index d898411006..fe92da1469 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -54,13 +54,11 @@ public: struct CompilationLog { std::string message; - std::vector binary; bool compiled{ false }; CompilationLog() {} CompilationLog(const CompilationLog& src) : message(src.message), - binary(src.binary), compiled(src.compiled) {} }; using CompilationLogs = std::vector; diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 8ecf527a14..650c9675a7 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -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. * * - * @typedef Controller.Hardware-Keyboard + * @typedef {object} Controller.Hardware-Keyboard * @todo Currently, the mouse wheel in an ordinary mouse generates left/right wheel events instead of up/down. */ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInputs() const { diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 7e911bc9bf..ee13d6666c 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -179,7 +179,7 @@ public: * @function ModelCache.prefetch * @param {string} url - URL of the resource to prefetch. * @param {object} [extra=null] - * @returns {Resource} + * @returns {ResourceObject} */ /**jsdoc @@ -188,7 +188,7 @@ public: * @param {string} url - URL of the resource to load. * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails. * @param {} [extra=null] - * @returns {Resource} + * @returns {object} */ diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 898f0e3d3a..bca64806c4 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -195,7 +195,7 @@ public: * @function TextureCache.prefetch * @param {string} url - URL of the resource to prefetch. * @param {object} [extra=null] - * @returns {Resource} + * @returns {ResourceObject} */ /**jsdoc @@ -204,7 +204,7 @@ public: * @param {string} url - URL of the resource to load. * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails. * @param {} [extra=null] - * @returns {Resource} + * @returns {object} */ @@ -261,7 +261,7 @@ protected: * @param {string} url * @param {number} type * @param {number} [maxNumPixels=67108864] - * @returns {Resource} + * @returns {ResourceObject} */ // Overload ResourceCache::prefetch to allow specifying texture type for loads Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index e70e3e26d0..05f0ec12b5 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -743,6 +744,9 @@ void AccountManager::generateNewKeypair(bool isUserKeypair, const QUuid& domainI return; } + // Ensure openssl/Qt config is set up. + QSslConfiguration::defaultConfiguration(); + // make sure we don't already have an outbound keypair generation request if (!_isWaitingForKeypairResponse) { _isWaitingForKeypairResponse = true; @@ -751,94 +755,75 @@ void AccountManager::generateNewKeypair(bool isUserKeypair, const QUuid& domainI qCDebug(networking) << "Clearing current private key in DataServerAccountInfo"; _accountInfo.setPrivateKey(QByteArray()); - // setup a new QThread to generate the keypair on, in case it takes a while - QThread* generateThread = new QThread(this); - generateThread->setObjectName("Account Manager Generator Thread"); - - // setup a keypair generator + // Create a runnable keypair generated to create an RSA pair and exit. RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator; if (!isUserKeypair) { - keypairGenerator->setDomainID(domainID); _accountInfo.setDomainID(domainID); } - // start keypair generation when the thread starts - connect(generateThread, &QThread::started, keypairGenerator, &RSAKeypairGenerator::generateKeypair); - // handle success or failure of keypair generation - connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this, &AccountManager::processGeneratedKeypair); - connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair, - this, &AccountManager::handleKeypairGenerationError); - - connect(keypairGenerator, &QObject::destroyed, generateThread, &QThread::quit); - connect(generateThread, &QThread::finished, generateThread, &QThread::deleteLater); - - keypairGenerator->moveToThread(generateThread); + connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this, + &AccountManager::processGeneratedKeypair); + connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair, this, + &AccountManager::handleKeypairGenerationError); qCDebug(networking) << "Starting worker thread to generate 2048-bit RSA keypair."; - generateThread->start(); + // Start on Qt's global thread pool. + QThreadPool::globalInstance()->start(keypairGenerator); } } -void AccountManager::processGeneratedKeypair() { +void AccountManager::processGeneratedKeypair(QByteArray publicKey, QByteArray privateKey) { qCDebug(networking) << "Generated 2048-bit RSA keypair. Uploading public key now."; - RSAKeypairGenerator* keypairGenerator = qobject_cast(sender()); + // hold the private key to later set our metaverse API account info if upload succeeds + _pendingPrivateKey = privateKey; - if (keypairGenerator) { - // hold the private key to later set our metaverse API account info if upload succeeds - _pendingPrivateKey = keypairGenerator->getPrivateKey(); + // upload the public key so data-web has an up-to-date key + const QString USER_PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key"; + const QString DOMAIN_PUBLIC_KEY_UPDATE_PATH = "api/v1/domains/%1/public_key"; - // upload the public key so data-web has an up-to-date key - const QString USER_PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key"; - const QString DOMAIN_PUBLIC_KEY_UPDATE_PATH = "api/v1/domains/%1/public_key"; - - QString uploadPath; - const auto& domainID = keypairGenerator->getDomainID(); - if (domainID.isNull()) { - uploadPath = USER_PUBLIC_KEY_UPDATE_PATH; - } else { - uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(domainID)); - } - - // setup a multipart upload to send up the public key - QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); - - QHttpPart publicKeyPart; - publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); - - publicKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader, - QVariant("form-data; name=\"public_key\"; filename=\"public_key\"")); - publicKeyPart.setBody(keypairGenerator->getPublicKey()); - requestMultiPart->append(publicKeyPart); - - if (!domainID.isNull()) { - const auto& key = getTemporaryDomainKey(domainID); - QHttpPart apiKeyPart; - publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); - apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader, - QVariant("form-data; name=\"api_key\"")); - apiKeyPart.setBody(key.toUtf8()); - requestMultiPart->append(apiKeyPart); - } - - // setup callback parameters so we know once the keypair upload has succeeded or failed - JSONCallbackParameters callbackParameters; - callbackParameters.jsonCallbackReceiver = this; - callbackParameters.jsonCallbackMethod = "publicKeyUploadSucceeded"; - callbackParameters.errorCallbackReceiver = this; - callbackParameters.errorCallbackMethod = "publicKeyUploadFailed"; - - sendRequest(uploadPath, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation, - callbackParameters, QByteArray(), requestMultiPart); - - keypairGenerator->deleteLater(); + QString uploadPath; + const auto& domainID = _accountInfo.getDomainID(); + if (domainID.isNull()) { + uploadPath = USER_PUBLIC_KEY_UPDATE_PATH; } else { - qCWarning(networking) << "Expected processGeneratedKeypair to be called by a live RSAKeypairGenerator" - << "but the casted sender is NULL. Will not process generated keypair."; + uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(domainID)); } + + // setup a multipart upload to send up the public key + QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + QHttpPart publicKeyPart; + publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); + + publicKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"public_key\"; filename=\"public_key\"")); + publicKeyPart.setBody(publicKey); + requestMultiPart->append(publicKeyPart); + + // Currently broken? We don't have the temporary domain key. + if (!domainID.isNull()) { + const auto& key = getTemporaryDomainKey(domainID); + QHttpPart apiKeyPart; + publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); + apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader, + QVariant("form-data; name=\"api_key\"")); + apiKeyPart.setBody(key.toUtf8()); + requestMultiPart->append(apiKeyPart); + } + + // setup callback parameters so we know once the keypair upload has succeeded or failed + JSONCallbackParameters callbackParameters; + callbackParameters.jsonCallbackReceiver = this; + callbackParameters.jsonCallbackMethod = "publicKeyUploadSucceeded"; + callbackParameters.errorCallbackReceiver = this; + callbackParameters.errorCallbackMethod = "publicKeyUploadFailed"; + + sendRequest(uploadPath, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation, + callbackParameters, QByteArray(), requestMultiPart); } void AccountManager::publicKeyUploadSucceeded(QNetworkReply& reply) { @@ -877,6 +862,4 @@ void AccountManager::handleKeypairGenerationError() { // reset our waiting state for keypair response _isWaitingForKeypairResponse = false; - - sender()->deleteLater(); } diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 88ebaf5656..9068966512 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -128,7 +128,7 @@ signals: private slots: void processReply(); void handleKeypairGenerationError(); - void processGeneratedKeypair(); + void processGeneratedKeypair(QByteArray publicKey, QByteArray privateKey); void publicKeyUploadSucceeded(QNetworkReply& reply); void publicKeyUploadFailed(QNetworkReply& reply); void generateNewKeypair(bool isUserKeypair = true, const QUuid& domainID = QUuid()); diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 7832b26c96..38eb7ee670 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -138,7 +138,7 @@ public: * * * - * @typedef location.LookupTrigger + * @typedef {number} location.LookupTrigger */ enum LookupTrigger { UserInput, @@ -184,7 +184,7 @@ public slots: /**jsdoc * Go to a specified metaverse address. * @function location.handleLookupString - * @param {string} address - The address to go to: a "hifi:/" address, an IP address (e.g., + * @param {string} address - The address to go to: a "hifi://" address, an IP address (e.g., * "127.0.0.1" or "localhost"), a domain name, a named path on a domain (starts with * "/"), a position or position and orientation, or a user (starts with "@"). * @param {boolean} fromSuggestions=false - Set to true if the address is obtained from the "Goto" dialog. diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 08908dbaf6..4d98391104 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -137,7 +137,7 @@ public: * * * - * @typedef Window.ConnectionRefusedReason + * @typedef {number} Window.ConnectionRefusedReason */ enum class ConnectionRefusedReason : uint8_t { Unknown, diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp index 8ca8b81ea3..e83615e3df 100644 --- a/libraries/networking/src/RSAKeypairGenerator.cpp +++ b/libraries/networking/src/RSAKeypairGenerator.cpp @@ -25,7 +25,10 @@ RSAKeypairGenerator::RSAKeypairGenerator(QObject* parent) : QObject(parent) { - +} + +void RSAKeypairGenerator::run() { + generateKeypair(); } void RSAKeypairGenerator::generateKeypair() { @@ -92,5 +95,5 @@ void RSAKeypairGenerator::generateKeypair() { OPENSSL_free(publicKeyDER); OPENSSL_free(privateKeyDER); - emit generatedKeypair(); + emit generatedKeypair(_publicKey, _privateKey); } diff --git a/libraries/networking/src/RSAKeypairGenerator.h b/libraries/networking/src/RSAKeypairGenerator.h index 36f4a9550b..552f12395b 100644 --- a/libraries/networking/src/RSAKeypairGenerator.h +++ b/libraries/networking/src/RSAKeypairGenerator.h @@ -13,25 +13,20 @@ #define hifi_RSAKeypairGenerator_h #include +#include #include -class RSAKeypairGenerator : public QObject { +class RSAKeypairGenerator : public QObject, public QRunnable { Q_OBJECT public: - RSAKeypairGenerator(QObject* parent = 0); + RSAKeypairGenerator(QObject* parent = nullptr); - void setDomainID(const QUuid& domainID) { _domainID = domainID; } - const QUuid& getDomainID() const { return _domainID; } - - const QByteArray& getPublicKey() const { return _publicKey; } - const QByteArray& getPrivateKey() const { return _privateKey; } - -public slots: + virtual void run() override; void generateKeypair(); signals: void errorGeneratingKeypair(); - void generatedKeypair(); + void generatedKeypair(QByteArray publicKey, QByteArray privateKey); private: QUuid _domainID; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 28266d0a7f..18e60ef5ef 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -218,8 +218,8 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) { } ResourceCache::ResourceCache(QObject* parent) : QObject(parent) { - auto nodeList = DependencyManager::get(); - if (nodeList) { + if (DependencyManager::isSet()) { + auto nodeList = DependencyManager::get(); auto& domainHandler = nodeList->getDomainHandler(); connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &ResourceCache::clearATPAssets, Qt::DirectConnection); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 18840cd11e..a4bd352563 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -87,7 +87,7 @@ private: class ScriptableResource : public QObject { /**jsdoc - * @constructor Resource + * @class ResourceObject * * @hifi-interface * @hifi-client-entity @@ -97,11 +97,6 @@ class ScriptableResource : public QObject { * @property {string} url - URL of this resource. * @property {Resource.State} state - Current loading state. */ - /**jsdoc - * @namespace Resource - * @variation 0 - * @property {Resource.State} State - */ Q_OBJECT Q_PROPERTY(QUrl url READ getURL) Q_PROPERTY(int state READ getState NOTIFY stateChanged) @@ -109,8 +104,7 @@ class ScriptableResource : public QObject { public: /**jsdoc - * @name Resource.State - * @static + * @typedef {object} Resource.State * @property {number} QUEUED - The resource is queued up, waiting to be loaded. * @property {number} LOADING - The resource is downloading. * @property {number} LOADED - The resource has finished downloaded by is not complete. @@ -131,7 +125,7 @@ public: /**jsdoc * Release this resource. - * @function Resource#release + * @function ResourceObject#release */ Q_INVOKABLE void release(); @@ -146,7 +140,7 @@ signals: /**jsdoc * Triggered when download progress for this resource has changed. - * @function Resource#progressChanged + * @function ResourceObject#progressChanged * @param {number} bytesReceived - Byytes downloaded so far. * @param {number} bytesTotal - Total number of bytes in the resource. * @returns {Signal} @@ -155,7 +149,7 @@ signals: /**jsdoc * Triggered when resource loading state has changed. - * @function Resource#stateChanged + * @function ResourceObject#stateChanged * @param {Resource.State} state - New state. * @returns {Signal} */ @@ -262,7 +256,7 @@ protected slots: * @function ResourceCache.prefetch * @param {string} url - URL of the resource to prefetch. * @param {object} [extra=null] - * @returns {Resource} + * @returns {ResourceObject} */ // Prefetches a resource to be held by the QScriptEngine. // 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} [fallback=""] - Fallback URL if load of the desired URL fails. * @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. /// If the caller is on a different thread than the ResourceCache, /// returns an empty smart pointer and loads its asynchronously. diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index c07c5905f6..b69733c18d 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::CloneableData); + return static_cast(EntityVersion::CollisionMask16Bytes); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 29d4efed42..5203a9d178 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -235,7 +235,8 @@ enum class EntityVersion : PacketVersion { MaterialEntities, ShadowControl, MaterialData, - CloneableData + CloneableData, + CollisionMask16Bytes }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 9b6e9fe7a0..64eda975cf 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -109,7 +109,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { } _dynamicsWorld = nullptr; } - int16_t collisionGroup = computeCollisionGroup(); + int32_t collisionGroup = computeCollisionGroup(); if (_rigidBody) { updateMassProperties(); } @@ -325,7 +325,7 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar _ghost.setWorldTransform(_rigidBody->getWorldTransform()); } -void CharacterController::jump() { +void CharacterController::jump(const btVector3& dir) { _pendingFlags |= PENDING_FLAG_JUMP; } @@ -352,7 +352,7 @@ static const char* stateToStr(CharacterController::State state) { #endif // #ifdef DEBUG_STATE_CHANGE void CharacterController::updateCurrentGravity() { - int16_t collisionGroup = computeCollisionGroup(); + int32_t collisionGroup = computeCollisionGroup(); if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { _currentGravity = 0.0f; } else { @@ -433,7 +433,7 @@ void CharacterController::setCollisionless(bool collisionless) { } } -int16_t CharacterController::computeCollisionGroup() const { +int32_t CharacterController::computeCollisionGroup() const { if (_collisionless) { return _collisionlessAllowed ? BULLET_COLLISION_GROUP_COLLISIONLESS : BULLET_COLLISION_GROUP_MY_AVATAR; } else { @@ -446,7 +446,7 @@ void CharacterController::handleChangedCollisionGroup() { // ATM the easiest way to update collision groups is to remove/re-add the RigidBody if (_dynamicsWorld) { _dynamicsWorld->removeRigidBody(_rigidBody); - int16_t collisionGroup = computeCollisionGroup(); + int32_t collisionGroup = computeCollisionGroup(); _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); } _pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP; @@ -538,7 +538,7 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel btScalar angle = motor.rotation.getAngle(); btVector3 velocity = worldVelocity.rotate(axis, -angle); - int16_t collisionGroup = computeCollisionGroup(); + int32_t collisionGroup = computeCollisionGroup(); if (collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS || _state == State::Hover || motor.hTimescale == motor.vTimescale) { // modify velocity @@ -679,7 +679,7 @@ void CharacterController::updateState() { btVector3 rayStart = _position; btScalar rayLength = _radius; - int16_t collisionGroup = computeCollisionGroup(); + int32_t collisionGroup = computeCollisionGroup(); if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT; } else { diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 96e479dcad..50db2bea12 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -70,7 +70,7 @@ public: virtual void preStep(btCollisionWorld *collisionWorld) override; virtual void playerStep(btCollisionWorld *collisionWorld, btScalar dt) override; 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; void clearMotors(); @@ -120,7 +120,7 @@ public: bool isStuck() const { return _isStuck; } void setCollisionless(bool collisionless); - int16_t computeCollisionGroup() const; + int32_t computeCollisionGroup() const; void handleChangedCollisionGroup(); bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); diff --git a/libraries/physics/src/CharacterGhostObject.cpp b/libraries/physics/src/CharacterGhostObject.cpp index 331485dd01..a771a52384 100755 --- a/libraries/physics/src/CharacterGhostObject.cpp +++ b/libraries/physics/src/CharacterGhostObject.cpp @@ -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; _collisionFilterMask = mask; // 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; mask = _collisionFilterMask; } diff --git a/libraries/physics/src/CharacterGhostObject.h b/libraries/physics/src/CharacterGhostObject.h index 1e4625c6f6..44ab5c938a 100755 --- a/libraries/physics/src/CharacterGhostObject.h +++ b/libraries/physics/src/CharacterGhostObject.h @@ -28,8 +28,8 @@ public: CharacterGhostObject() { } ~CharacterGhostObject(); - void setCollisionGroupAndMask(int16_t group, int16_t mask); - void getCollisionGroupAndMask(int16_t& group, int16_t& mask) const; + void setCollisionGroupAndMask(int32_t group, int32_t mask); + void getCollisionGroupAndMask(int32_t& group, int32_t& mask) const; void setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight); void setUpDirection(const btVector3& up); @@ -54,8 +54,8 @@ protected: btScalar _radius { 0.0f }; btConvexHullShape* _characterShape { nullptr }; // input, shape of character CharacterGhostShape* _ghostShape { nullptr }; // internal, shape whose Aabb is used for overlap cache - int16_t _collisionFilterGroup { 0 }; - int16_t _collisionFilterMask { 0 }; + int32_t _collisionFilterGroup { 0 }; + int32_t _collisionFilterMask { 0 }; bool _inWorld { false }; // internal, was added to world }; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 7a0ead3e0d..deb779b1f8 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -252,11 +252,9 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); float dt = (thisStep - _lastKinematicStep) * PHYSICS_ENGINE_FIXED_SUBSTEP; + _lastKinematicStep = thisStep; _entity->stepKinematicMotion(dt); - // bypass const-ness so we can remember the step - const_cast(this)->_lastKinematicStep = thisStep; - // and set the acceleration-matches-gravity count high so that if we send an update // it will use the correct acceleration for remote simulations _accelerationNearlyGravityCount = (uint8_t)(-1); @@ -779,7 +777,7 @@ QString EntityMotionState::getName() const { } // 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); } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 376cc5afe2..603130b5ff 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -84,7 +84,7 @@ public: 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 isLocallyOwned() const override; diff --git a/libraries/physics/src/ObjectDynamic.cpp b/libraries/physics/src/ObjectDynamic.cpp index 3341025a8f..28323a8b92 100644 --- a/libraries/physics/src/ObjectDynamic.cpp +++ b/libraries/physics/src/ObjectDynamic.cpp @@ -159,6 +159,13 @@ void ObjectDynamic::removeFromSimulation(EntitySimulationPointer simulation) con simulation->removeDynamic(myID); } +void ObjectDynamic::setOwnerEntity(const EntityItemPointer ownerEntity) { + if (!ownerEntity) { + activateBody(); + } + _ownerEntity = ownerEntity; +} + EntityItemPointer ObjectDynamic::getEntityByID(EntityItemID entityID) const { EntityItemPointer ownerEntity; withReadLock([&]{ diff --git a/libraries/physics/src/ObjectDynamic.h b/libraries/physics/src/ObjectDynamic.h index 7fdf2e323a..bfee79aca9 100644 --- a/libraries/physics/src/ObjectDynamic.h +++ b/libraries/physics/src/ObjectDynamic.h @@ -33,7 +33,7 @@ public: virtual void removeFromSimulation(EntitySimulationPointer simulation) const override; virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; } - virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; } + virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override; virtual void invalidate() {}; diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index e1cf5a4285..bb3c00bd5d 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -154,7 +154,7 @@ public: 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; } @@ -184,7 +184,7 @@ protected: btRigidBody* _body { nullptr }; float _density { 1.0f }; - uint32_t _lastKinematicStep; + mutable uint32_t _lastKinematicStep; bool _hasInternalKinematicChanges { false }; }; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 83ffa21a55..21b5b38b13 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -71,8 +71,8 @@ void PhysicsEngine::init() { } } -uint32_t PhysicsEngine::getNumSubsteps() { - return _numSubsteps; +uint32_t PhysicsEngine::getNumSubsteps() const { + return _dynamicsWorld->getNumSubsteps(); } // private @@ -148,7 +148,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { body->setFlags(BT_DISABLE_WORLD_GRAVITY); motionState->updateBodyMaterialProperties(); - int16_t group, mask; + int32_t group, mask; motionState->computeCollisionGroupAndMask(group, mask); _dynamicsWorld->addRigidBody(body, group, mask); @@ -329,13 +329,9 @@ void PhysicsEngine::stepSimulation() { PHYSICS_ENGINE_FIXED_SUBSTEP, onSubStep); if (numSubsteps > 0) { BT_PROFILE("postSimulation"); - _numSubsteps += (uint32_t)numSubsteps; - ObjectMotionState::setWorldSimulationStep(_numSubsteps); - if (_myAvatarController) { _myAvatarController->postSimulation(); } - _hasOutgoingChanges = true; } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 0dfe3a7a7c..2ac195956a 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -52,7 +52,7 @@ public: ~PhysicsEngine(); void init(); - uint32_t getNumSubsteps(); + uint32_t getNumSubsteps() const; void removeObjects(const VectorOfMotionStates& objects); void removeSetOfObjects(const SetOfMotionStates& objects); // only called during teardown @@ -135,7 +135,6 @@ private: CharacterController* _myAvatarController; uint32_t _numContactFrames = 0; - uint32_t _numSubsteps; bool _dumpNextStats { false }; bool _saveNextStats { false }; diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 3f24851dce..07d5ceb9ac 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -59,14 +59,11 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep } } - /*//process some debugging flags - if (getDebugDrawer()) { - btIDebugDraw* debugDrawer = getDebugDrawer(); - gDisableDeactivation = (debugDrawer->getDebugMode() & btIDebugDraw::DBG_NoDeactivation) != 0; - }*/ if (subSteps) { //clamp the number of substeps, to prevent simulation grinding spiralling down to a halt int clampedSimulationSteps = (subSteps > maxSubSteps)? maxSubSteps : subSteps; + _numSubsteps += clampedSimulationSteps; + ObjectMotionState::setWorldSimulationStep(_numSubsteps); saveKinematicState(fixedTimeStep*clampedSimulationSteps); @@ -98,28 +95,24 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep // call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState() void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) { btAssert(body); - if (body->getMotionState() && !body->isStaticObject()) { - //we need to call the update at least once, even for sleeping objects - //otherwise the 'graphics' transform never updates properly - ///@todo: add 'dirty' flag - //if (body->getActivationState() != ISLAND_SLEEPING) - { - if (body->isKinematicObject()) { - ObjectMotionState* objectMotionState = static_cast(body->getMotionState()); - if (objectMotionState->hasInternalKinematicChanges()) { - objectMotionState->clearInternalKinematicChanges(); - body->getMotionState()->setWorldTransform(body->getWorldTransform()); - } - return; - } - btTransform interpolatedTransform; - btTransformUtil::integrateTransform(body->getInterpolationWorldTransform(), - body->getInterpolationLinearVelocity(),body->getInterpolationAngularVelocity(), - (m_latencyMotionStateInterpolation && m_fixedTimeStep) ? m_localTime - m_fixedTimeStep : m_localTime*body->getHitFraction(), - interpolatedTransform); - body->getMotionState()->setWorldTransform(interpolatedTransform); + btAssert(body->getMotionState()); + + if (body->isKinematicObject()) { + ObjectMotionState* objectMotionState = static_cast(body->getMotionState()); + if (objectMotionState->hasInternalKinematicChanges()) { + // this is a special case where the kinematic motion has been updated by an Action + // so we supply the body's current transform to the MotionState + objectMotionState->clearInternalKinematicChanges(); + body->getMotionState()->setWorldTransform(body->getWorldTransform()); } + return; } + btTransform interpolatedTransform; + btTransformUtil::integrateTransform(body->getInterpolationWorldTransform(), + body->getInterpolationLinearVelocity(),body->getInterpolationAngularVelocity(), + (m_latencyMotionStateInterpolation && m_fixedTimeStep) ? m_localTime - m_fixedTimeStep : m_localTime*body->getHitFraction(), + interpolatedTransform); + body->getMotionState()->setWorldTransform(interpolatedTransform); } void ThreadSafeDynamicsWorld::synchronizeMotionStates() { @@ -164,24 +157,12 @@ void ThreadSafeDynamicsWorld::synchronizeMotionStates() { } void ThreadSafeDynamicsWorld::saveKinematicState(btScalar timeStep) { -///would like to iterate over m_nonStaticRigidBodies, but unfortunately old API allows -///to switch status _after_ adding kinematic objects to the world -///fix it for Bullet 3.x release DETAILED_PROFILE_RANGE(simulation_physics, "saveKinematicState"); BT_PROFILE("saveKinematicState"); - for (int i=0;igetActivationState() != ISLAND_SLEEPING) - { - if (body->isKinematicObject()) - { - //to calculate velocities next frame - body->saveKinematicState(timeStep); - } + for (int i=0;iisKinematicObject() && body->getActivationState() != ISLAND_SLEEPING) { + body->saveKinematicState(timeStep); } } } - - diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index 54c3ddb756..d8cee4d2de 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -37,6 +37,7 @@ public: btConstraintSolver* constraintSolver, btCollisionConfiguration* collisionConfiguration); + int getNumSubsteps() const { return _numSubsteps; } int stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps = 1, btScalar fixedTimeStep = btScalar(1.)/btScalar(60.), SubStepCallback onSubStep = []() { }); @@ -61,6 +62,7 @@ private: VectorOfMotionStates _deactivatedStates; SetOfMotionStates _activeStates; SetOfMotionStates _lastActiveStates; + int _numSubsteps { 0 }; }; #endif // hifi_ThreadSafeDynamicsWorld_h diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 3643e608ed..b3a93ab1de 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -181,7 +181,7 @@ class DebugAmbientOcclusionConfig : public render::Job::Config { Q_PROPERTY(bool showCursorPixel MEMBER showCursorPixel NOTIFY dirty) Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty) public: - DebugAmbientOcclusionConfig() : render::Job::Config(true) {} + DebugAmbientOcclusionConfig() : render::Job::Config(false) {} bool showCursorPixel{ false }; glm::vec2 debugCursorTexcoord{ 0.5f, 0.5f }; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 1596f7ba83..79e0637af4 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -215,10 +215,7 @@ void CauterizedModel::updateRenderItems() { modelTransform.setRotation(self->getRotation()); bool isWireframe = self->isWireframe(); - bool isVisible = self->isVisible(); - bool canCastShadow = self->canCastShadow(); - bool isLayeredInFront = self->isLayeredInFront(); - bool isLayeredInHUD = self->isLayeredInHUD(); + auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags(); bool enableCauterization = self->getEnableCauterization(); render::Transaction transaction; @@ -234,7 +231,7 @@ void CauterizedModel::updateRenderItems() { bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey, - isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, canCastShadow, enableCauterization](CauterizedMeshPartPayload& data) { + isWireframe, renderItemKeyGlobalFlags, enableCauterization](CauterizedMeshPartPayload& data) { if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions, cauterizedMeshState.clusterDualQuaternions); @@ -276,8 +273,7 @@ void CauterizedModel::updateRenderItems() { data.updateTransformForCauterizedMesh(renderTransform); data.setEnableCauterization(enableCauterization); - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, render::ItemKey::TAG_BITS_ALL); - data.setLayer(isLayeredInFront, isLayeredInHUD); + data.updateKey(renderItemKeyGlobalFlags); data.setShapeKey(invalidatePayloadShapeKey, isWireframe, useDualQuaternionSkinning); }); } diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 0d8561ad21..f8f49921a3 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -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 INSTANCED_SOLID_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_NORMALS_OFFSET = offsetof(GeometryCache::ShapeVertex, normal); @@ -690,6 +693,38 @@ gpu::Stream::FormatPointer& getInstancedSolidFadeStreamFormat() { return INSTANCED_SOLID_FADE_STREAM_FORMAT; } +gpu::Stream::FormatPointer& getWireStreamFormat() { + if (!WIRE_STREAM_FORMAT) { + WIRE_STREAM_FORMAT = std::make_shared(); // 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(); // 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(); // 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 GeometryCache::_simplePrograms; gpu::ShaderPointer GeometryCache::_simpleShader; @@ -827,7 +862,7 @@ void GeometryCache::renderShape(gpu::Batch& batch, Shape shape) { } void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape) { - batch.setInputFormat(getSolidStreamFormat()); + batch.setInputFormat(getWireStreamFormat()); _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) { - batch.setInputFormat(getSolidStreamFormat()); + batch.setInputFormat(getWireStreamFormat()); // Color must be set after input format batch._glColor4f(color.r, color.g, color.b, color.a); _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) { - batch.setInputFormat(getInstancedSolidStreamFormat()); + batch.setInputFormat(getInstancedWireStreamFormat()); setupBatchInstance(batch, colorBuffer); _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, gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3) { - batch.setInputFormat(getInstancedSolidFadeStreamFormat()); + batch.setInputFormat(getInstancedWireFadeStreamFormat()); setupBatchFadeInstance(batch, colorBuffer, fadeBuffer1, fadeBuffer2, fadeBuffer3); _shapes[shape].drawWireInstances(batch, count); } diff --git a/libraries/render-utils/src/LightClusters.h b/libraries/render-utils/src/LightClusters.h index f495dabebb..fa054c304a 100644 --- a/libraries/render-utils/src/LightClusters.h +++ b/libraries/render-utils/src/LightClusters.h @@ -195,7 +195,7 @@ class DebugLightClustersConfig : public render::Job::Config { Q_PROPERTY(bool doDrawClusterFromDepth MEMBER doDrawClusterFromDepth NOTIFY dirty) Q_PROPERTY(bool doDrawContent MEMBER doDrawContent NOTIFY dirty) public: - DebugLightClustersConfig() : render::Job::Config(true){} + DebugLightClustersConfig() : render::Job::Config(false){} bool doDrawGrid{ false }; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 72a4b7e0a4..7cf8bc8297 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -79,28 +79,10 @@ void MeshPartPayload::removeMaterial(graphics::MaterialPointer material) { _drawMaterials.remove(material); } -void MeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled) { - ItemKey::Builder builder; +void MeshPartPayload::updateKey(const render::ItemKey& key) { + ItemKey::Builder builder(key); builder.withTypeShape(); - if (!isVisible) { - builder.withInvisible(); - } - - builder.withTagBits(tagBits); - - if (isLayered) { - builder.withLayered(); - } - - if (canCastShadow) { - builder.withShadowCaster(); - } - - if (isGroupCulled) { - builder.withSubMetaCulled(); - } - if (topMaterialExists()) { auto matKey = _drawMaterials.top().material->getKey(); if (matKey.isTranslucent()) { @@ -200,12 +182,6 @@ template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointe } return Item::Bound(); } -template <> int payloadGetLayer(const ModelMeshPartPayload::Pointer& payload) { - if (payload) { - return payload->getLayer(); - } - return 0; -} template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload) { if (payload) { @@ -332,28 +308,10 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& render } // Note that this method is called for models but not for shapes -void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled) { - ItemKey::Builder builder; +void ModelMeshPartPayload::updateKey(const render::ItemKey& key) { + ItemKey::Builder builder(key); builder.withTypeShape(); - if (!isVisible) { - builder.withInvisible(); - } - - builder.withTagBits(tagBits); - - if (isLayered) { - builder.withLayered(); - } - - if (canCastShadow) { - builder.withShadowCaster(); - } - - if (isGroupCulled) { - builder.withSubMetaCulled(); - } - if (_isBlendShaped || _isSkinned) { builder.withDeformed(); } @@ -368,20 +326,6 @@ void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCas _itemKey = builder.build(); } -void ModelMeshPartPayload::setLayer(bool isLayeredInFront, bool isLayeredInHUD) { - if (isLayeredInFront) { - _layer = Item::LAYER_3D_FRONT; - } else if (isLayeredInHUD) { - _layer = Item::LAYER_3D_HUD; - } else { - _layer = Item::LAYER_3D; - } -} - -int ModelMeshPartPayload::getLayer() const { - return _layer; -} - void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe, bool useDualQuaternionSkinning) { if (invalidateShapeKey) { _shapeKey = ShapeKey::Builder::invalid(); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 08ad7a8311..5c7177e890 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -32,7 +32,7 @@ public: typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - virtual void updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled = false); + virtual void updateKey(const render::ItemKey& key); virtual void updateMeshPart(const std::shared_ptr& drawMesh, int partIndex); @@ -95,7 +95,7 @@ public: void notifyLocationChanged() override; - void updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled = false) override; + void updateKey(const render::ItemKey& key) override; // matrix palette skinning void updateClusterBuffer(const std::vector& clusterMatrices); @@ -105,11 +105,9 @@ public: void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform); // Render Item interface - int getLayer() const; render::ShapeKey getShapeKey() const override; // shape interface void render(RenderArgs* args) override; - void setLayer(bool isLayeredInFront, bool isLayeredInHUD); void setShapeKey(bool invalidateShapeKey, bool isWireframe, bool useDualQuaternionSkinning); // ModelMeshPartPayload functions to perform render @@ -139,13 +137,11 @@ private: gpu::BufferPointer _blendedVertexBuffer; render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() }; - int _layer { render::Item::LAYER_3D }; }; namespace render { template <> const ItemKey payloadGetKey(const ModelMeshPartPayload::Pointer& payload); template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload); - template <> int payloadGetLayer(const ModelMeshPartPayload::Pointer& payload); template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload); template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args); } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 65b12ac0d4..7cfb1f6bc8 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -103,11 +103,10 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : _snapModelToRegistrationPoint(false), _snappedToRegistrationPoint(false), _url(HTTP_INVALID_COM), - _isVisible(true), - _canCastShadow(false), _blendNumber(0), _appliedBlendNumber(0), - _isWireframe(false) + _isWireframe(false), + _renderItemKeyGlobalFlags(render::ItemKey::Builder().withVisible().withTagBits(render::hifi::TAG_ALL_VIEWS).build()) { // we may have been created in the network thread, but we live in the main thread if (_viewState) { @@ -268,12 +267,7 @@ void Model::updateRenderItems() { modelTransform.setScale(glm::vec3(1.0f)); bool isWireframe = self->isWireframe(); - bool isVisible = self->isVisible(); - bool canCastShadow = self->canCastShadow(); - uint8_t viewTagBits = self->getViewTagBits(); - bool isLayeredInFront = self->isLayeredInFront(); - bool isLayeredInHUD = self->isLayeredInHUD(); - bool isGroupCulled = self->isGroupCulled(); + auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags(); render::Transaction transaction; for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) { @@ -287,9 +281,7 @@ void Model::updateRenderItems() { bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, - invalidatePayloadShapeKey, isWireframe, isVisible, - canCastShadow, viewTagBits, isLayeredInFront, - isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { + invalidatePayloadShapeKey, isWireframe, renderItemKeyGlobalFlags](ModelMeshPartPayload& data) { if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions); } else { @@ -313,8 +305,7 @@ void Model::updateRenderItems() { } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); - data.setLayer(isLayeredInFront, isLayeredInHUD); + data.updateKey(renderItemKeyGlobalFlags); data.setShapeKey(invalidatePayloadShapeKey, isWireframe, useDualQuaternionSkinning); }); } @@ -322,8 +313,9 @@ void Model::updateRenderItems() { Transform collisionMeshOffset; collisionMeshOffset.setIdentity(); foreach(auto itemID, self->_collisionRenderItemsMap.keys()) { - transaction.updateItem(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) { + transaction.updateItem(itemID, [renderItemKeyGlobalFlags, modelTransform, collisionMeshOffset](MeshPartPayload& data) { // update the model transform for this render item. + data.updateKey(renderItemKeyGlobalFlags); data.updateTransform(modelTransform, collisionMeshOffset); }); } @@ -773,110 +765,100 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) { } } -void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled) { - if (_isVisible != isVisible || _viewTagBits != viewTagBits || _isGroupCulled != isGroupCulled) { - _isVisible = isVisible; - _viewTagBits = viewTagBits; - _isGroupCulled = isGroupCulled; +void Model::updateRenderItemsKey(const render::ScenePointer& scene) { + if (!scene) { + _needsFixupInScene = true; + return; + } + auto renderItemsKey = _renderItemKeyGlobalFlags; + render::Transaction transaction; + foreach(auto item, _modelMeshRenderItemsMap.keys()) { + transaction.updateItem(item, [renderItemsKey](ModelMeshPartPayload& data) { + data.updateKey(renderItemsKey); + }); + } + foreach(auto item, _collisionRenderItemsMap.keys()) { + transaction.updateItem(item, [renderItemsKey](ModelMeshPartPayload& data) { + data.updateKey(renderItemsKey); + }); + } + scene->enqueueTransaction(transaction); +} - bool isLayeredInFront = _isLayeredInFront; - bool isLayeredInHUD = _isLayeredInHUD; - bool canCastShadow = _canCastShadow; - render::Transaction transaction; - foreach (auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, - isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); - }); - } - foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, - isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); - }); - } - scene->enqueueTransaction(transaction); +void Model::setVisibleInScene(bool visible, const render::ScenePointer& scene) { + if (Model::isVisible() != visible) { + auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags); + _renderItemKeyGlobalFlags = (visible ? keyBuilder.withVisible() : keyBuilder.withInvisible()); + updateRenderItemsKey(scene); } } -void Model::setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled) { - if (_canCastShadow != canCastShadow) { - _canCastShadow = canCastShadow; +bool Model::isVisible() const { + return _renderItemKeyGlobalFlags.isVisible(); +} - bool isVisible = _isVisible; - bool isLayeredInFront = _isLayeredInFront; - bool isLayeredInHUD = _isLayeredInHUD; - - render::Transaction transaction; - foreach (auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, - [isVisible, viewTagBits, canCastShadow, isLayeredInFront, isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, viewTagBits, canCastShadow, isLayeredInFront || isLayeredInHUD, isGroupCulled); - }); - } - - scene->enqueueTransaction(transaction); +void Model::setCanCastShadow(bool castShadow, const render::ScenePointer& scene) { + if (Model::canCastShadow() != castShadow) { + auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags); + _renderItemKeyGlobalFlags = (castShadow ? keyBuilder.withShadowCaster() : keyBuilder.withoutShadowCaster()); + updateRenderItemsKey(scene); } } -void Model::setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene) { - if (_isLayeredInFront != isLayeredInFront) { - _isLayeredInFront = isLayeredInFront; +bool Model::canCastShadow() const { + return _renderItemKeyGlobalFlags.isShadowCaster(); +} - bool isVisible = _isVisible; - bool canCastShadow = _canCastShadow; - uint8_t viewTagBits = _viewTagBits; - bool isLayeredInHUD = _isLayeredInHUD; - bool isGroupCulled = _isGroupCulled; - - render::Transaction transaction; - foreach(auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, - isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); - data.setLayer(isLayeredInFront, isLayeredInHUD); - }); - } - foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, - isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); - data.setLayer(isLayeredInFront, isLayeredInHUD); - }); - } - scene->enqueueTransaction(transaction); +void Model::setLayeredInFront(bool layeredInFront, const render::ScenePointer& scene) { + if (Model::isLayeredInFront() != layeredInFront) { + auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags); + _renderItemKeyGlobalFlags = (layeredInFront ? keyBuilder.withLayer(render::hifi::LAYER_3D_FRONT) : keyBuilder.withoutLayer()); + updateRenderItemsKey(scene); } } -void Model::setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene) { - if (_isLayeredInHUD != isLayeredInHUD) { - _isLayeredInHUD = isLayeredInHUD; +bool Model::isLayeredInFront() const { + return _renderItemKeyGlobalFlags.isLayer(render::hifi::LAYER_3D_FRONT); +} - bool isVisible = _isVisible; - bool canCastShadow = _canCastShadow; - uint8_t viewTagBits = _viewTagBits; - bool isLayeredInFront = _isLayeredInFront; - bool isGroupCulled = _isGroupCulled; - - render::Transaction transaction; - foreach(auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, - isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); - data.setLayer(isLayeredInFront, isLayeredInHUD); - }); - } - foreach(auto item, _collisionRenderItemsMap.keys()) { - transaction.updateItem(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow, - isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) { - data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled); - data.setLayer(isLayeredInFront, isLayeredInHUD); - }); - } - scene->enqueueTransaction(transaction); +void Model::setLayeredInHUD(bool layeredInHUD, const render::ScenePointer& scene) { + if (Model::isLayeredInHUD() != layeredInHUD) { + auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags); + _renderItemKeyGlobalFlags = (layeredInHUD ? keyBuilder.withLayer(render::hifi::LAYER_3D_HUD) : keyBuilder.withoutLayer()); + updateRenderItemsKey(scene); } } +bool Model::isLayeredInHUD() const { + return _renderItemKeyGlobalFlags.isLayer(render::hifi::LAYER_3D_HUD); +} + +void Model::setTagMask(uint8_t mask, const render::ScenePointer& scene) { + if (Model::getTagMask() != mask) { + auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags); + _renderItemKeyGlobalFlags = keyBuilder.withTagBits(mask); + updateRenderItemsKey(scene); + } +} +render::hifi::Tag Model::getTagMask() const { + return (render::hifi::Tag) _renderItemKeyGlobalFlags.getTagBits(); +} + +void Model::setGroupCulled(bool groupCulled, const render::ScenePointer& scene) { + if (Model::isGroupCulled() != groupCulled) { + auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags); + _renderItemKeyGlobalFlags = (groupCulled ? keyBuilder.withSubMetaCulled() : keyBuilder.withoutSubMetaCulled()); + updateRenderItemsKey(scene); + } +} +bool Model::isGroupCulled() const { + return _renderItemKeyGlobalFlags.isSubMetaCulled(); +} + +const render::ItemKey Model::getRenderItemKeyGlobalFlags() const { + return _renderItemKeyGlobalFlags; +} + bool Model::addToScene(const render::ScenePointer& scene, render::Transaction& transaction, render::Item::Status::Getters& statusGetters) { @@ -1676,20 +1658,16 @@ void Model::addMaterial(graphics::MaterialLayer material, const std::string& par for (auto shapeID : shapeIDs) { if (shapeID < _modelMeshRenderItemIDs.size()) { auto itemID = _modelMeshRenderItemIDs[shapeID]; - bool visible = isVisible(); - uint8_t viewTagBits = getViewTagBits(); - bool layeredInFront = isLayeredInFront(); - bool layeredInHUD = isLayeredInHUD(); - bool canCastShadow = _canCastShadow; + auto renderItemsKey = _renderItemKeyGlobalFlags; bool wireframe = isWireframe(); auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex; bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex); bool useDualQuaternionSkinning = _useDualQuaternionSkinning; - transaction.updateItem(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits, canCastShadow, + transaction.updateItem(itemID, [material, renderItemsKey, invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning](ModelMeshPartPayload& data) { data.addMaterial(material); // if the material changed, we might need to update our item key or shape key - data.updateKey(visible, layeredInFront || layeredInHUD, canCastShadow, viewTagBits); + data.updateKey(renderItemsKey); data.setShapeKey(invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning); }); } @@ -1704,19 +1682,16 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string if (shapeID < _modelMeshRenderItemIDs.size()) { auto itemID = _modelMeshRenderItemIDs[shapeID]; bool visible = isVisible(); - uint8_t viewTagBits = getViewTagBits(); - bool layeredInFront = isLayeredInFront(); - bool layeredInHUD = isLayeredInHUD(); - bool canCastShadow = _canCastShadow; + auto renderItemsKey = _renderItemKeyGlobalFlags; bool wireframe = isWireframe(); auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex; bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex); bool useDualQuaternionSkinning = _useDualQuaternionSkinning; - transaction.updateItem(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits, canCastShadow, + transaction.updateItem(itemID, [material, visible, renderItemsKey, invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning](ModelMeshPartPayload& data) { data.removeMaterial(material); // if the material changed, we might need to update our item key or shape key - data.updateKey(visible, layeredInFront || layeredInHUD, canCastShadow, viewTagBits); + data.updateKey(renderItemsKey); data.setShapeKey(invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning); }); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 2b14a7c055..4180288106 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -33,6 +33,7 @@ #include #include +#include "RenderHifi.h" #include "GeometryCache.h" #include "TextureCache.h" #include "Rig.h" @@ -87,13 +88,27 @@ public: const QUrl& getURL() const { return _url; } // new Scene/Engine rendering support - void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled); + void setVisibleInScene(bool isVisible, const render::ScenePointer& scene = nullptr); + bool isVisible() const; - bool canCastShadow() const { return _canCastShadow; } - void setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled); + render::hifi::Tag getTagMask() const; + void setTagMask(uint8_t mask, const render::ScenePointer& scene = nullptr); + + bool isGroupCulled() const; + void setGroupCulled(bool isGroupCulled, const render::ScenePointer& scene = nullptr); + + bool canCastShadow() const; + void setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene = nullptr); + + void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene = nullptr); + void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene = nullptr); + + bool isLayeredInFront() const; + bool isLayeredInHUD() const; + + // Access the current RenderItemKey Global Flags used by the model and applied to the render items representing the parts of the model. + const render::ItemKey getRenderItemKeyGlobalFlags() const; - void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene); - void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene); bool needsFixupInScene() const; bool needsReload() const { return _needsReload; } @@ -108,13 +123,7 @@ public: void removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction); bool isRenderable() const; - bool isVisible() const { return _isVisible; } - uint8_t getViewTagBits() const { return _viewTagBits; } - - bool isLayeredInFront() const { return _isLayeredInFront; } - bool isLayeredInHUD() const { return _isLayeredInHUD; } - - bool isGroupCulled() const { return _isGroupCulled; } + void updateRenderItemsKey(const render::ScenePointer& scene); virtual void updateRenderItems(); void setRenderItemsNeedUpdate(); @@ -404,10 +413,6 @@ protected: QVector _blendshapeCoefficients; QUrl _url; - bool _isVisible; - uint8_t _viewTagBits{ render::ItemKey::TAG_BITS_ALL }; - - bool _canCastShadow; gpu::Buffers _blendedVertexBuffers; @@ -471,10 +476,16 @@ protected: int _renderInfoDrawCalls { 0 }; int _renderInfoHasTransparent { false }; - bool _isLayeredInFront { false }; - bool _isLayeredInHUD { false }; - - bool _isGroupCulled{ false }; + // This Render ItemKey Global Flags capture the Model wide global set of flags that should be communicated to all the render items representing the Model. + // The flags concerned are: + // - isVisible: if true the Model is visible globally in the scene, regardless of the other flags in the item keys (tags or layer or shadow caster). + // - TagBits: the view mask defined through the TagBits telling in which view the Model is rendered if visible. + // - Layer: In which Layer this Model lives. + // - CastShadow: if true and visible and rendered in the view, the Model cast shadows if in a Light volume casting shadows. + // - CullGroup: if true, the render items representing the parts of the Model are culled by a single Meta render item that knows about them, they are not culled individually. + // For this to work, a Meta RI must exists and knows about the RIs of this Model. + // + render::ItemKey _renderItemKeyGlobalFlags; bool shouldInvalidatePayloadShapeKey(int meshIndex); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 3ea56f8542..47cab54d09 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -27,6 +27,7 @@ #include #include +#include "RenderHifi.h" #include "RenderCommonTask.h" #include "LightingModel.h" #include "StencilMaskPass.h" @@ -200,8 +201,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto overlaysInFrontRangeTimer = task.addJob("BeginOverlaysInFrontRangeTimer", "BeginOverlaysInFrontRangeTimer"); // Layered Overlays - const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT); - const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT); + const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, render::hifi::LAYER_3D_FRONT); + const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, render::hifi::LAYER_3D_FRONT); const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN(0); const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN(0); diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 4388a39cf3..09a2afb711 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -23,6 +23,7 @@ #include +#include "RenderHifi.h" #include "StencilMaskPass.h" #include "ZoneRenderer.h" #include "FadeEffect.h" @@ -79,8 +80,8 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend task.addJob("PrepareStencil", framebuffer); // Layered Overlays - const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT); - const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT); + const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, render::hifi::LAYER_3D_FRONT); + const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, render::hifi::LAYER_3D_FRONT); const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN(0); const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN(0); diff --git a/libraries/render-utils/src/RenderHifi.h b/libraries/render-utils/src/RenderHifi.h new file mode 100644 index 0000000000..c489a0ad1d --- /dev/null +++ b/libraries/render-utils/src/RenderHifi.h @@ -0,0 +1,43 @@ +// +// RenderHifi.h +// libraries/render-utils/src +// +// Created by Sam Gateau on 5/30/2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_RenderHifi_h +#define hifi_RenderHifi_h + +#include + + + +// In the library render-utils we are specializing the generic components of the render library to create the custom hifi render engine +// Objects and types serving this goal are define in the namespace render.hifi +// TODO: extend the namespace to all the classes where it make sense in render-utils +namespace render { + namespace hifi { + + // Tag is the alias names of render::ItemKey::Tag combinations used in the Hifi Render Engine + enum Tag : uint8_t { + TAG_NONE = render::ItemKey::TAG_BITS_NONE, // No Tags at all + TAG_MAIN_VIEW = render::ItemKey::TAG_BITS_0, // Main view + TAG_SECONDARY_VIEW = render::ItemKey::TAG_BITS_1, // Secondary View + TAG_ALL_VIEWS = TAG_MAIN_VIEW | TAG_SECONDARY_VIEW, // All views + }; + + // Layer is the alias names of the render::ItemKey::Layer used in the Hifi Render Engine + enum Layer : uint8_t { + LAYER_3D = render::ItemKey::LAYER_DEFAULT, + LAYER_3D_FRONT = render::ItemKey::LAYER_1, + LAYER_3D_HUD = render::ItemKey::LAYER_2, + LAYER_2D = render::ItemKey::LAYER_3, + LAYER_BACKGROUND = render::ItemKey::LAYER_BACKGROUND, + }; + } +} + +#endif // hifi_RenderHifi_h diff --git a/libraries/render-utils/src/SubsurfaceScattering.h b/libraries/render-utils/src/SubsurfaceScattering.h index 30021fae40..780ce34d7f 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.h +++ b/libraries/render-utils/src/SubsurfaceScattering.h @@ -149,7 +149,7 @@ class DebugSubsurfaceScatteringConfig : public render::Job::Config { Q_PROPERTY(bool showCursorPixel MEMBER showCursorPixel NOTIFY dirty) Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty) public: - DebugSubsurfaceScatteringConfig() : render::Job::Config(true) {} + DebugSubsurfaceScatteringConfig() : render::Job::Config(false) {} bool showProfile{ false }; bool showLUT{ false }; diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index b5819f114f..3f55e6dedc 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -368,9 +368,9 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input RenderArgs* args = renderContext->args; const auto& inShapes = inputs.get0(); - const auto& cullFilter = inputs.get1(); - const auto& boundsFilter = inputs.get2(); - const auto& antiFrustum = inputs.get3(); + const auto& cullFilter = inputs.get1(); + const auto& boundsFilter = inputs.get2(); + const auto& antiFrustum = inputs.get3(); auto& outShapes = outputs.edit0(); auto& outBounds = outputs.edit1(); @@ -380,7 +380,7 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input if (!cullFilter.selectsNothing() || !boundsFilter.selectsNothing()) { auto& details = args->_details.edit(_detailType); Test test(_cullFunctor, args, details, antiFrustum); - auto scene = args->_scene; + auto scene = args->_scene; for (auto& inItems : inShapes) { auto key = inItems.first; @@ -395,26 +395,26 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input if (antiFrustum == nullptr) { for (auto& item : inItems.second) { if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound)) { - const auto shapeKey = scene->getItem(item.id).getKey(); - if (cullFilter.test(shapeKey)) { - outItems->second.emplace_back(item); - } - if (boundsFilter.test(shapeKey)) { - outBounds += item.bound; - } + const auto shapeKey = scene->getItem(item.id).getKey(); + if (cullFilter.test(shapeKey)) { + outItems->second.emplace_back(item); + } + if (boundsFilter.test(shapeKey)) { + outBounds += item.bound; + } } } } else { for (auto& item : inItems.second) { if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound) && test.antiFrustumTest(item.bound)) { - const auto shapeKey = scene->getItem(item.id).getKey(); - if (cullFilter.test(shapeKey)) { - outItems->second.emplace_back(item); - } - if (boundsFilter.test(shapeKey)) { - outBounds += item.bound; - } - } + const auto shapeKey = scene->getItem(item.id).getKey(); + if (cullFilter.test(shapeKey)) { + outItems->second.emplace_back(item); + } + if (boundsFilter.test(shapeKey)) { + outBounds += item.bound; + } + } } } details._rendered += (int)outItems->second.size(); @@ -487,6 +487,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext, if (filter.test(item.getKey())) { ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); + } } } diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index b0842d63cd..da8e60d40a 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -36,12 +36,11 @@ public: } }; -Engine::Engine() : Task(EngineTask::JobModel::create("Engine")), - _renderContext(std::make_shared()) +RenderEngine::RenderEngine() : Engine(EngineTask::JobModel::create("Engine"), std::make_shared()) { } -void Engine::load() { +void RenderEngine::load() { auto config = getConfiguration(); const QString configFile= "config/render.json"; diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 0271c71529..130ed6533f 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -25,7 +25,7 @@ namespace render { class RenderContext : public task::JobContext { public: - RenderContext() : task::JobContext(trace_render()) {} + RenderContext() : task::JobContext() {} virtual ~RenderContext() {} RenderArgs* args; @@ -33,7 +33,9 @@ namespace render { }; using RenderContextPointer = std::shared_ptr; - Task_DeclareTypeAliases(RenderContext) + Task_DeclareCategoryTimeProfilerClass(RenderTimeProfiler, trace_render); + + Task_DeclareTypeAliases(RenderContext, RenderTimeProfiler) // Versions of the COnfig integrating a gpu & batch timer class GPUJobConfig : public JobConfig { @@ -57,10 +59,10 @@ namespace render { class GPUTaskConfig : public TaskConfig { Q_OBJECT - Q_PROPERTY(double gpuRunTime READ getGPURunTime) - Q_PROPERTY(double batchRunTime READ getBatchRunTime) + Q_PROPERTY(double gpuRunTime READ getGPURunTime) + Q_PROPERTY(double batchRunTime READ getBatchRunTime) - double _msGPURunTime { 0.0 }; + double _msGPURunTime { 0.0 }; double _msBatchRunTime { 0.0 }; public: @@ -80,32 +82,25 @@ namespace render { // 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 - // the engine should not be known from its jobs. - class Engine : public Task { + class RenderEngine : public Engine { public: - Engine(); - ~Engine() = default; + RenderEngine(); + ~RenderEngine() = default; // Load any persisted settings, and set up the presets // This should be run after adding all jobs, and before building ui void load(); // Register the scene - void registerScene(const ScenePointer& scene) { _renderContext->_scene = scene; } + void registerScene(const ScenePointer& scene) { _context->_scene = scene; } // acces the RenderContext - RenderContextPointer getRenderContext() const { return _renderContext; } - - // Render a frame - // Must have a scene registered and a context set - void run() { assert(_renderContext); Task::run(_renderContext); } + RenderContextPointer getRenderContext() const { return _context; } protected: - RenderContextPointer _renderContext; - - void run(const RenderContextPointer& context) override { assert(_renderContext); Task::run(_renderContext); } }; - using EnginePointer = std::shared_ptr; + using EnginePointer = std::shared_ptr; } diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 9c5efb9fa7..532964777f 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -29,25 +29,9 @@ const float Item::Status::Value::CYAN = 180.0f; const float Item::Status::Value::BLUE = 240.0f; const float Item::Status::Value::MAGENTA = 300.0f; -const int Item::LAYER_2D = 0; -const int Item::LAYER_3D = 1; -const int Item::LAYER_3D_FRONT = 2; -const int Item::LAYER_3D_HUD = 3; - -const uint8_t ItemKey::TAG_BITS_ALL { 0xFF }; -const uint8_t ItemKey::TAG_BITS_NONE { 0x00 }; -const uint8_t ItemKey::TAG_BITS_0 { 0x01 }; -const uint8_t ItemKey::TAG_BITS_1 { 0x02 }; -const uint8_t ItemKey::TAG_BITS_2 { 0x04 }; -const uint8_t ItemKey::TAG_BITS_3 { 0x08 }; -const uint8_t ItemKey::TAG_BITS_4 { 0x10 }; -const uint8_t ItemKey::TAG_BITS_5 { 0x20 }; -const uint8_t ItemKey::TAG_BITS_6 { 0x40 }; -const uint8_t ItemKey::TAG_BITS_7 { 0x80 }; - const uint32_t ItemKey::KEY_TAG_BITS_MASK = ((uint32_t) ItemKey::TAG_BITS_ALL) << FIRST_TAG_BIT; - +const uint32_t ItemKey::KEY_LAYER_BITS_MASK = ((uint32_t)ItemKey::LAYER_BITS_ALL) << FIRST_LAYER_BIT; void Item::Status::Value::setScale(float scale) { _scale = (std::numeric_limits::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f)); diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index e4dcc7ee03..1dfef73686 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -52,23 +52,45 @@ public: TAG_6, TAG_7, - NUM_TAGS + NUM_TAGS, + + // Tag bits are derived from the Tag enum + TAG_BITS_ALL = 0xFF, + TAG_BITS_NONE = 0x00, + TAG_BITS_0 = 0x01, + TAG_BITS_1 = 0x02, + TAG_BITS_2 = 0x04, + TAG_BITS_3 = 0x08, + TAG_BITS_4 = 0x10, + TAG_BITS_5 = 0x20, + TAG_BITS_6 = 0x40, + TAG_BITS_7 = 0x80, + }; + + // Items are organized in layers, an item belongs to one of the 8 Layers available. + // By default an item is in the 'LAYER_DEFAULT' meaning that it is NOT layered. + // THere is NO ordering relationship between layers. + enum Layer : uint8_t { + LAYER_DEFAULT = 0, // layer 0 aka Default is a 'NOT' layer, items are not considered layered, this is the default value + LAYER_1, + LAYER_2, + LAYER_3, + LAYER_4, + LAYER_5, + LAYER_6, + LAYER_BACKGROUND, // Last Layer is the background by convention + + NUM_LAYERS, + + // Layer bits are derived from the Layer enum, the number of bits needed to represent integer 0 to NUM_LAYERS + NUM_LAYER_BITS = 3, + LAYER_BITS_ALL = 0x07, }; - // Tag bits are derived from the Tag enum - const static uint8_t TAG_BITS_ALL; - const static uint8_t TAG_BITS_NONE; - const static uint8_t TAG_BITS_0; - const static uint8_t TAG_BITS_1; - const static uint8_t TAG_BITS_2; - const static uint8_t TAG_BITS_3; - const static uint8_t TAG_BITS_4; - const static uint8_t TAG_BITS_5; - const static uint8_t TAG_BITS_6; - const static uint8_t TAG_BITS_7; enum FlagBit : uint32_t { - TYPE_SHAPE = 0, // Item is a Shape - TYPE_LIGHT, // Item is a Light + TYPE_SHAPE = 0, // Item is a Shape: Implements the Shape Interface that draw a Geometry rendered with a Material + TYPE_LIGHT, // Item is a Light: Implements the Light Interface that + TYPE_CAMERA, // Item is a Camera: Implements the Camera Interface TYPE_META, // Item is a Meta: meanning it s used to represent a higher level object, potentially represented by other render items TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work... @@ -77,13 +99,15 @@ public: DEFORMED, // Deformed within bound, not solid INVISIBLE, // Visible or not in the scene? SHADOW_CASTER, // Item cast shadows - LAYERED, // Item belongs to one of the layers different from the default layer META_CULL_GROUP, // As a meta item, the culling of my sub items is based solely on my bounding box and my visibility in the view SUB_META_CULLED, // As a sub item of a meta render item set as cull group, need to be set to my culling to the meta render it FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against LAST_TAG_BIT = FIRST_TAG_BIT + NUM_TAGS, + FIRST_LAYER_BIT, // 8 Exclusive Layers (encoded in 3 bits) available to organize the items in layers, an item can only belong to ONE layer + LAST_LAYER_BIT = FIRST_LAYER_BIT + NUM_LAYER_BITS, + __SMALLER, // Reserved bit for spatialized item to indicate that it is smaller than expected in the cell in which it belongs (probably because it overlaps over several smaller cells) NUM_FLAGS, // Not a valid flag @@ -96,6 +120,12 @@ public: return (keyBits & ~KEY_TAG_BITS_MASK) | (((uint32_t)tagBits) << FIRST_TAG_BIT); } + // All the bits touching layer bits sets to true + const static uint32_t KEY_LAYER_BITS_MASK; + static uint32_t evalLayerBitsWithKeyBits(uint8_t layer, const uint32_t keyBits) { + return (keyBits & ~KEY_LAYER_BITS_MASK) | (((uint32_t)layer & LAYER_BITS_ALL) << FIRST_LAYER_BIT); + } + // The key is the Flags Flags _flags; @@ -122,20 +152,26 @@ public: Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } Builder& withDeformed() { _flags.set(DEFORMED); 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& withLayered() { _flags.set(LAYERED); return (*this); } + Builder& withoutShadowCaster() { _flags.reset(SHADOW_CASTER); return (*this); } Builder& withMetaCullGroup() { _flags.set(META_CULL_GROUP); return (*this); } + Builder& withoutMetaCullGroup() { _flags.reset(META_CULL_GROUP); return (*this); } Builder& withSubMetaCulled() { _flags.set(SUB_META_CULLED); return (*this); } + Builder& withoutSubMetaCulled() { _flags.reset(SUB_META_CULLED); return (*this); } Builder& withTag(Tag tag) { _flags.set(FIRST_TAG_BIT + tag); return (*this); } // Set ALL the tags in one call using the Tag bits Builder& withTagBits(uint8_t tagBits) { _flags = evalTagBitsWithKeyBits(tagBits, _flags.to_ulong()); return (*this); } + Builder& withLayer(uint8_t layer) { _flags = evalLayerBitsWithKeyBits(layer, _flags.to_ulong()); return (*this); } + Builder& withoutLayer() { return withLayer(LAYER_DEFAULT); } + // Convenient standard keys that we will keep on using all over the place static Builder opaqueShape() { return Builder().withTypeShape(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); } static Builder light() { return Builder().withTypeLight(); } - static Builder background() { return Builder().withViewSpace().withLayered(); } + static Builder background() { return Builder().withViewSpace().withLayer(LAYER_BACKGROUND); } }; ItemKey(const Builder& builder) : ItemKey(builder._flags) {} @@ -160,9 +196,6 @@ public: bool isShadowCaster() const { return _flags[SHADOW_CASTER]; } - bool isLayered() const { return _flags[LAYERED]; } - bool isSpatial() const { return !isLayered(); } - bool isMetaCullGroup() const { return _flags[META_CULL_GROUP]; } void setMetaCullGroup(bool cullGroup) { (cullGroup ? _flags.set(META_CULL_GROUP) : _flags.reset(META_CULL_GROUP)); } @@ -172,6 +205,11 @@ public: bool isTag(Tag tag) const { return _flags[FIRST_TAG_BIT + tag]; } uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); } + uint8_t getLayer() const { return ((_flags.to_ulong() & KEY_LAYER_BITS_MASK) >> FIRST_LAYER_BIT); } + bool isLayer(uint8_t layer) const { return getLayer() == layer; } + bool isLayered() const { return getLayer() != LAYER_DEFAULT; } + bool isSpatial() const { return !isLayered(); } + // Probably not public, flags used by the scene bool isSmall() const { return _flags[__SMALLER]; } void setSmaller(bool smaller) { (smaller ? _flags.set(__SMALLER) : _flags.reset(__SMALLER)); } @@ -229,9 +267,6 @@ public: Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } - Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } - Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } - Builder& withoutMetaCullGroup() { _value.reset(ItemKey::META_CULL_GROUP); _mask.set(ItemKey::META_CULL_GROUP); return (*this); } Builder& withMetaCullGroup() { _value.set(ItemKey::META_CULL_GROUP); _mask.set(ItemKey::META_CULL_GROUP); return (*this); } @@ -243,6 +278,9 @@ public: // Set ALL the tags in one call using the Tag bits and the Tag bits touched Builder& withTagBits(uint8_t tagBits, uint8_t tagMask) { _value = ItemKey::evalTagBitsWithKeyBits(tagBits, _value.to_ulong()); _mask = ItemKey::evalTagBitsWithKeyBits(tagMask, _mask.to_ulong()); return (*this); } + Builder& withoutLayered() { _value = ItemKey::evalLayerBitsWithKeyBits(ItemKey::LAYER_DEFAULT, _value.to_ulong()); _mask |= ItemKey::KEY_LAYER_BITS_MASK; return (*this); } + Builder& withLayer(uint8_t layer) { _value = ItemKey::evalLayerBitsWithKeyBits(layer, _value.to_ulong()); _mask |= ItemKey::KEY_LAYER_BITS_MASK; return (*this); } + Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); } // Convenient standard keys that we will keep on using all over the place @@ -251,9 +289,7 @@ public: static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } static Builder light() { return Builder().withTypeLight(); } static Builder meta() { return Builder().withTypeMeta(); } - static Builder background() { return Builder().withViewSpace().withLayered(); } - static Builder opaqueShapeLayered() { return Builder().withTypeShape().withOpaque().withWorldSpace().withLayered(); } - static Builder transparentShapeLayered() { return Builder().withTypeShape().withTransparent().withWorldSpace().withLayered(); } + static Builder background() { return Builder().withViewSpace().withLayer(ItemKey::LAYER_BACKGROUND); } static Builder nothing() { return Builder().withNothing(); } }; @@ -376,7 +412,6 @@ public: public: virtual const ItemKey getKey() const = 0; virtual const Bound getBound() const = 0; - virtual int getLayer() const = 0; virtual void render(RenderArgs* args) = 0; virtual const ShapeKey getShapeKey() const = 0; @@ -421,13 +456,8 @@ public: // Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace()) const Bound getBound() const { return _payload->getBound(); } - // Get the layer where the item belongs. - int getLayer() const { return _payload->getLayer(); } - - static const int LAYER_2D; - static const int LAYER_3D; - static const int LAYER_3D_FRONT; - static const int LAYER_3D_HUD; + // Get the layer where the item belongs, simply reflecting the key. + int getLayer() const { return _key.getLayer(); } // Render call for the item void render(RenderArgs* args) const { _payload->render(args); } @@ -477,7 +507,6 @@ inline QDebug operator<<(QDebug debug, const Item& item) { // Item shared interface supported by the payload template const ItemKey payloadGetKey(const std::shared_ptr& payloadData) { return ItemKey(); } template const Item::Bound payloadGetBound(const std::shared_ptr& payloadData) { return Item::Bound(); } -template int payloadGetLayer(const std::shared_ptr& payloadData) { return 0; } template void payloadRender(const std::shared_ptr& payloadData, RenderArgs* args) { } // Shape type interface @@ -504,7 +533,6 @@ public: // Payload general interface virtual const ItemKey getKey() const override { return payloadGetKey(_data); } virtual const Item::Bound getBound() const override { return payloadGetBound(_data); } - virtual int getLayer() const override { return payloadGetLayer(_data); } virtual void render(RenderArgs* args) override { payloadRender(_data, args); } diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 2d8bc7f4dd..68acfe8d0f 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -21,7 +21,7 @@ namespace render { -class Engine; +class RenderEngine; class Scene; // Transaction is the mechanism to make any change to the scene. @@ -236,7 +236,7 @@ protected: StageMap _stages; - friend class Engine; + friend class RenderEngine; }; typedef std::shared_ptr ScenePointer; diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 7f7a3a68b0..72d6901fb5 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -121,7 +121,7 @@ public: /**jsdoc * 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} [responseType=text] the desired reponse type (text | arraybuffer | json) * @property {boolean} [decompress=false] whether to attempt gunzip decompression on the fetched data @@ -137,7 +137,7 @@ public: /**jsdoc * 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} [hash] the resolved hash for the fetched asset * @property {string|ArrayBuffer|Object} [response] response data (possibly converted per .responseType value) @@ -159,7 +159,7 @@ public: /**jsdoc * 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 {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 @@ -174,7 +174,7 @@ public: /**jsdoc * 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} [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 diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 1ccdfdbf31..76b7ac45e3 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -43,7 +43,7 @@ * @hifi-server-entity * @hifi-assignment-client * - * @property IDENTITY {Quat} { x: 0, y: 0, z: 0, w: 1 } : The identity rotation, i.e., no rotation. + * @property {Quat} IDENTITY - { x: 0, y: 0, z: 0, w: 1 } : The identity rotation, i.e., no rotation. * Read-only. * @example Print the IDENTITY value. * print(JSON.stringify(Quat.IDENTITY)); // { x: 0, y: 0, z: 0, w: 1 } diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index fdfbc6f6c0..da42cf2df3 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -21,7 +21,7 @@ namespace SceneScripting { /**jsdoc - * @typedef Scene.Stage.Location + * @typedef {object} Scene.Stage.Location * @property {number} longitude * @property {number} latitude * @property {number} altitude @@ -49,7 +49,7 @@ namespace SceneScripting { using LocationPointer = std::unique_ptr; /**jsdoc - * @typedef Scene.Stage.Time + * @typedef {object} Scene.Stage.Time * @property {number} hour * @property {number} day */ @@ -73,7 +73,7 @@ namespace SceneScripting { using TimePointer = std::unique_ptr