mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 04:24:08 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into fixCameraMode
This commit is contained in:
commit
9ed8c04097
113 changed files with 1513 additions and 554 deletions
BUILD_WIN.md
android
assignment-client/src/entities
cmake/externals
domain-server/src
interface/src
libraries
animation/src
audio/src
avatars-renderer/src/avatars-renderer
avatars/src
controllers/src/controllers
entities/src
AnimationPropertyGroup.cppEntityEditPacketSender.cppEntityEditPacketSender.hEntityItem.cppEntityItem.hEntityItemProperties.cppEntityItemProperties.hEntityItemPropertiesDefaults.hEntityPropertyFlags.hEntityScriptingInterface.cppEntityScriptingInterface.hEntitySimulation.cppEntityTree.cppEntityTree.hKeyLightPropertyGroup.h
fbx/src
gpu-gl/src/gpu
input-plugins/src/input-plugins
model-networking/src/model-networking
networking/src
physics/src
CharacterController.cppCharacterController.hCharacterGhostObject.cppCharacterGhostObject.hEntityMotionState.cppEntityMotionState.hObjectMotionState.hPhysicsEngine.cpp
render-utils/src
render/src/render
script-engine/src
shared/src
task/src/task
ui/src
plugins
scripts
developer/utilities
lib/jet
render
system/controllers/controllerModules
14
BUILD_WIN.md
14
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.
|
||||
|
||||
|
|
|
@ -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') {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
|
|
@ -44,6 +44,7 @@ EntityServer::EntityServer(ReceivedMessage& message) :
|
|||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListenerForTypes({ PacketType::EntityAdd,
|
||||
PacketType::EntityClone,
|
||||
PacketType::EntityEdit,
|
||||
PacketType::EntityErase,
|
||||
PacketType::EntityPhysics,
|
||||
|
|
8
cmake/externals/bullet/CMakeLists.txt
vendored
8
cmake/externals/bullet/CMakeLists.txt
vendored
|
@ -17,8 +17,8 @@ include(ExternalProject)
|
|||
if (WIN32)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz
|
||||
URL_MD5 03051bf112dcc78ddd296f9cab38fd68
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz
|
||||
URL_MD5 0a6876607ebe83e227427215f15946fd
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 -DUSE_DX11=0
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
|
@ -28,8 +28,8 @@ if (WIN32)
|
|||
else ()
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.83-ccd-and-cmake-fixes.tgz
|
||||
URL_MD5 03051bf112dcc78ddd296f9cab38fd68
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.88.tgz
|
||||
URL_MD5 0a6876607ebe83e227427215f15946fd
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
|
|
6
cmake/externals/glm/CMakeLists.txt
vendored
6
cmake/externals/glm/CMakeLists.txt
vendored
|
@ -3,8 +3,8 @@ set(EXTERNAL_NAME glm)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.zip
|
||||
URL_MD5 579ac77a3110befa3244d68c0ceb7281
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.5-patched.zip
|
||||
URL_MD5 7d39ecc1cea275427534c3cfd6dd63f0
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTERNAL_ARGS}
|
||||
LOG_DOWNLOAD 1
|
||||
|
@ -18,4 +18,4 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
|||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glm include directories")
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glm include directories")
|
||||
|
|
|
@ -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 ""
|
||||
|
|
|
@ -479,7 +479,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
limitedNodeList->killNodeWithUUID(existingNodeID);
|
||||
}
|
||||
|
||||
// add the connecting node (or re-use the matched one from eachNodeBreakable above)
|
||||
// add the connecting node
|
||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
|
||||
|
||||
// set the edit rights for this user
|
||||
|
@ -508,26 +508,22 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
return newNode;
|
||||
}
|
||||
|
||||
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
QUuid nodeID) {
|
||||
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) {
|
||||
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
|
||||
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
|
||||
|
||||
if (connectedPeer) {
|
||||
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
||||
nodeID = nodeConnection.connectUUID;
|
||||
|
||||
if (connectedPeer->getActiveSocket()) {
|
||||
// set their discovered socket to whatever the activated socket on the network peer object was
|
||||
discoveredSocket = *connectedPeer->getActiveSocket();
|
||||
}
|
||||
} else {
|
||||
// we got a connectUUID we didn't recognize, either use the hinted node ID or randomly generate a new one
|
||||
if (nodeID.isNull()) {
|
||||
nodeID = QUuid::createUuid();
|
||||
}
|
||||
if (connectedPeer && connectedPeer->getActiveSocket()) {
|
||||
// set their discovered socket to whatever the activated socket on the network peer object was
|
||||
discoveredSocket = *connectedPeer->getActiveSocket();
|
||||
}
|
||||
|
||||
// create a new node ID for the verified connecting node
|
||||
auto nodeID = QUuid::createUuid();
|
||||
|
||||
// add a mapping from connection node ID to ICE peer ID
|
||||
// so that we can remove the ICE peer once we see this node connect
|
||||
_nodeToICEPeerIDs.insert(nodeID, nodeConnection.connectUUID);
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
Node::LocalID newLocalID = findOrCreateLocalID(nodeID);
|
||||
|
@ -541,6 +537,15 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node
|
|||
return newNode;
|
||||
}
|
||||
|
||||
void DomainGatekeeper::cleanupICEPeerForNode(const QUuid& nodeID) {
|
||||
// remove this node ID from our node to ICE peer ID map
|
||||
// and the associated ICE peer (if it still exists)
|
||||
auto icePeerID = _nodeToICEPeerIDs.take(nodeID);
|
||||
if (!icePeerID.isNull()) {
|
||||
_icePeers.remove(icePeerID);
|
||||
}
|
||||
}
|
||||
|
||||
bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
||||
const QByteArray& usernameSignature,
|
||||
const HifiSockAddr& senderSockAddr) {
|
||||
|
|
|
@ -39,8 +39,8 @@ public:
|
|||
void addPendingAssignedNode(const QUuid& nodeUUID, const QUuid& assignmentUUID,
|
||||
const QUuid& walletUUID, const QString& nodeVersion);
|
||||
QUuid assignmentUUIDForPendingAssignment(const QUuid& tempUUID);
|
||||
|
||||
void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); }
|
||||
|
||||
void cleanupICEPeerForNode(const QUuid& nodeID);
|
||||
|
||||
Node::LocalID findOrCreateLocalID(const QUuid& uuid);
|
||||
|
||||
|
@ -77,8 +77,7 @@ private:
|
|||
SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
const QString& username,
|
||||
const QByteArray& usernameSignature);
|
||||
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
QUuid nodeID = QUuid());
|
||||
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection);
|
||||
|
||||
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
|
@ -101,6 +100,10 @@ private:
|
|||
std::unordered_map<QUuid, PendingAssignedNodeData> _pendingAssignedNodes;
|
||||
|
||||
QHash<QUuid, SharedNetworkPeer> _icePeers;
|
||||
|
||||
using ConnectingNodeID = QUuid;
|
||||
using ICEPeerID = QUuid;
|
||||
QHash<ConnectingNodeID, ICEPeerID> _nodeToICEPeerIDs;
|
||||
|
||||
QHash<QString, QUuid> _connectionTokenHash;
|
||||
|
||||
|
|
|
@ -1017,15 +1017,22 @@ void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> mess
|
|||
sendingNode->setPublicSocket(nodeRequestData.publicSockAddr);
|
||||
sendingNode->setLocalSocket(nodeRequestData.localSockAddr);
|
||||
|
||||
// update the NodeInterestSet in case there have been any changes
|
||||
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
|
||||
|
||||
if (!nodeData->hasCheckedIn()) {
|
||||
nodeData->setHasCheckedIn(true);
|
||||
|
||||
// on first check in, make sure we've cleaned up any ICE peer for this node
|
||||
_gatekeeper.cleanupICEPeerForNode(sendingNode->getUUID());
|
||||
}
|
||||
|
||||
// guard against patched agents asking to hear about other agents
|
||||
auto safeInterestSet = nodeRequestData.interestList.toSet();
|
||||
if (sendingNode->getType() == NodeType::Agent) {
|
||||
safeInterestSet.remove(NodeType::Agent);
|
||||
}
|
||||
|
||||
// update the NodeInterestSet in case there have been any changes
|
||||
nodeData->setNodeInterestSet(safeInterestSet);
|
||||
|
||||
// update the connecting hostname in case it has changed
|
||||
|
@ -2945,7 +2952,7 @@ void DomainServer::nodeAdded(SharedNodePointer node) {
|
|||
|
||||
void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||
// if this peer connected via ICE then remove them from our ICE peers hash
|
||||
_gatekeeper.removeICEPeer(node->getUUID());
|
||||
_gatekeeper.cleanupICEPeerForNode(node->getUUID());
|
||||
|
||||
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
|
@ -2978,6 +2985,8 @@ void DomainServer::nodeKilled(SharedNodePointer node) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
broadcastNodeDisconnect(node);
|
||||
}
|
||||
|
||||
SharedAssignmentPointer DomainServer::dequeueMatchingAssignment(const QUuid& assignmentUUID, NodeType_t nodeType) {
|
||||
|
@ -3163,18 +3172,23 @@ void DomainServer::handleKillNode(SharedNodePointer nodeToKill) {
|
|||
const QUuid& nodeUUID = nodeToKill->getUUID();
|
||||
|
||||
limitedNodeList->killNodeWithUUID(nodeUUID);
|
||||
}
|
||||
|
||||
static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID);
|
||||
void DomainServer::broadcastNodeDisconnect(const SharedNodePointer& disconnectedNode) {
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID, true);
|
||||
|
||||
removedNodePacket->reset();
|
||||
removedNodePacket->write(nodeUUID.toRfc4122());
|
||||
removedNodePacket->write(disconnectedNode->getUUID().toRfc4122());
|
||||
|
||||
// broadcast out the DomainServerRemovedNode message
|
||||
limitedNodeList->eachMatchingNode([this, &nodeToKill](const SharedNodePointer& otherNode) -> bool {
|
||||
limitedNodeList->eachMatchingNode([this, &disconnectedNode](const SharedNodePointer& otherNode) -> bool {
|
||||
// only send the removed node packet to nodes that care about the type of node this was
|
||||
return isInInterestSet(otherNode, nodeToKill);
|
||||
return isInInterestSet(otherNode, disconnectedNode);
|
||||
}, [&limitedNodeList](const SharedNodePointer& otherNode){
|
||||
limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode);
|
||||
auto removedNodePacketCopy = NLPacket::createCopy(*removedNodePacket);
|
||||
limitedNodeList->sendPacket(std::move(removedNodePacketCopy), *otherNode);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -717,7 +717,7 @@ private:
|
|||
* <tr><td><code>NavigationFocused</code></td><td>number</td><td>number</td><td><em>Not used.</em></td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Controller.Hardware-Application
|
||||
* @typedef {object} Controller.Hardware-Application
|
||||
*/
|
||||
|
||||
static const QString STATE_IN_HMD = "InHMD";
|
||||
|
|
|
@ -651,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 };
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ class MyAvatar : public Avatar {
|
|||
* while flying.
|
||||
* @property {number} hmdRollControlDeadZone=8 - The amount of HMD roll, in degrees, required before your avatar turns if
|
||||
* <code>hmdRollControlEnabled</code> is enabled.
|
||||
* @property hmdRollControlRate {number} If hmdRollControlEnabled is true, this value determines the maximum turn rate of
|
||||
* @property {number} hmdRollControlRate If hmdRollControlEnabled is true, this value determines the maximum turn rate of
|
||||
* your avatar when rolling your HMD in degrees per second.
|
||||
* @property {number} userHeight=1.75 - The height of the user in sensor space.
|
||||
* @property {number} userEyeHeight=1.65 - The estimated height of the user's eyes in sensor space. <em>Read-only.</em>
|
||||
|
@ -1402,7 +1402,7 @@ private:
|
|||
SharedSoundPointer _collisionSound;
|
||||
|
||||
MyCharacterController _characterController;
|
||||
int16_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR };
|
||||
int32_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR };
|
||||
|
||||
AvatarWeakPointer _lookAtTargetAvatar;
|
||||
glm::vec3 _targetAvatarPosition;
|
||||
|
|
|
@ -22,21 +22,22 @@
|
|||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*
|
||||
* @property PICK_NOTHING {number} A filter flag. Don't intersect with anything. <em>Read-only.</em>
|
||||
* @property PICK_ENTITIES {number} A filter flag. Include entities when intersecting. <em>Read-only.</em>
|
||||
* @property PICK_OVERLAYS {number} A filter flag. Include overlays when intersecting. <em>Read-only.</em>
|
||||
* @property PICK_AVATARS {number} A filter flag. Include avatars when intersecting. <em>Read-only.</em>
|
||||
* @property PICK_HUD {number} A filter flag. Include the HUD sphere when intersecting in HMD mode. <em>Read-only.</em>
|
||||
* @property PICK_COARSE {number} A filter flag. Pick against coarse meshes, instead of exact meshes. <em>Read-only.</em>
|
||||
* @property PICK_INCLUDE_INVISIBLE {number} A filter flag. Include invisible objects when intersecting. <em>Read-only.</em>
|
||||
* @property PICK_INCLUDE_NONCOLLIDABLE {number} A filter flag. Include non-collidable objects when intersecting.
|
||||
* @property {number} PICK_NOTHING A filter flag. Don't intersect with anything. <em>Read-only.</em>
|
||||
* @property {number} PICK_ENTITIES A filter flag. Include entities when intersecting. <em>Read-only.</em>
|
||||
* @property {number} PICK_OVERLAYS A filter flag. Include overlays when intersecting. <em>Read-only.</em>
|
||||
* @property {number} PICK_AVATARS A filter flag. Include avatars when intersecting. <em>Read-only.</em>
|
||||
* @property {number} PICK_HUD A filter flag. Include the HUD sphere when intersecting in HMD mode. <em>Read-only.</em>
|
||||
* @property {number} PICK_COARSE A filter flag. Pick against coarse meshes, instead of exact meshes. <em>Read-only.</em>
|
||||
* @property {number} PICK_INCLUDE_INVISIBLE A filter flag. Include invisible objects when intersecting. <em>Read-only.</em>
|
||||
* @property {number} PICK_INCLUDE_NONCOLLIDABLE A filter flag. Include non-collidable objects when intersecting.
|
||||
* <em>Read-only.</em>
|
||||
* @property PICK_ALL_INTERSECTIONS {number} <em>Read-only.</em>
|
||||
* @property INTERSECTED_NONE {number} An intersection type. Intersected nothing with the given filter flags. <em>Read-only.</em>
|
||||
* @property INTERSECTED_ENTITY {number} An intersection type. Intersected an entity. <em>Read-only.</em>
|
||||
* @property INTERSECTED_OVERLAY {number} An intersection type. Intersected an overlay. <em>Read-only.</em>
|
||||
* @property INTERSECTED_AVATAR {number} An intersection type. Intersected an avatar. <em>Read-only.</em>
|
||||
* @property INTERSECTED_HUD {number} An intersection type. Intersected the HUD sphere. <em>Read-only.</em>
|
||||
* @property {number} PICK_ALL_INTERSECTIONS <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags.
|
||||
* <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_ENTITY An intersection type. Intersected an entity. <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an overlay. <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_AVATAR An intersection type. Intersected an avatar. <em>Read-only.</em>
|
||||
* @property {number} INTERSECTED_HUD An intersection type. Intersected the HUD sphere. <em>Read-only.</em>
|
||||
* @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results. <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
|
@ -99,7 +100,7 @@ public:
|
|||
/**jsdoc
|
||||
* An intersection result for a Ray Pick.
|
||||
*
|
||||
* @typedef {Object} RayPickResult
|
||||
* @typedef {object} RayPickResult
|
||||
* @property {number} type The intersection type.
|
||||
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE)
|
||||
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
|
||||
|
@ -113,7 +114,7 @@ public:
|
|||
/**jsdoc
|
||||
* An intersection result for a Stylus Pick.
|
||||
*
|
||||
* @typedef {Object} StylusPickResult
|
||||
* @typedef {object} StylusPickResult
|
||||
* @property {number} type The intersection type.
|
||||
* @property {boolean} intersects If there was a valid intersection (type != INTERSECTED_NONE)
|
||||
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
|
||||
|
|
|
@ -68,14 +68,14 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
|
|||
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is not intersecting something. Same as a {@link Pointers.RayPointerRenderState},
|
||||
* but with an additional distance field.
|
||||
*
|
||||
* @typedef {Object} Pointers.DefaultRayPointerRenderState
|
||||
* @typedef {object} Pointers.DefaultRayPointerRenderState
|
||||
* @augments Pointers.RayPointerRenderState
|
||||
* @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined.
|
||||
*/
|
||||
/**jsdoc
|
||||
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something.
|
||||
*
|
||||
* @typedef {Object} Pointers.RayPointerRenderState
|
||||
* @typedef {object} Pointers.RayPointerRenderState
|
||||
* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
|
||||
* @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* An overlay to represent the beginning of the Ray Pointer, if desired.
|
||||
|
@ -87,7 +87,7 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
|
|||
/**jsdoc
|
||||
* A trigger mechanism for Ray Pointers.
|
||||
*
|
||||
* @typedef {Object} Pointers.Trigger
|
||||
* @typedef {object} Pointers.Trigger
|
||||
* @property {Controller.Standard|Controller.Actions|function} action This can be a built-in Controller action, like Controller.Standard.LTClick, or a function that evaluates to >= 1.0 when you want to trigger <code>button</code>.
|
||||
* @property {string} button Which button to trigger. "Primary", "Secondary", "Tertiary", and "Focus" are currently supported. Only "Primary" will trigger clicks on web surfaces. If "Focus" is triggered,
|
||||
* it will try to set the entity or overlay focus to the object at which the Pointer is aimed. Buttons besides the first three will still trigger events, but event.button will be "None".
|
||||
|
|
|
@ -522,7 +522,7 @@ int WindowScriptingInterface::openMessageBox(QString title, QString text, int bu
|
|||
* <tr> <td><strong>RestoreDefaults</strong></td> <td><code>0x8000000</code></td> <td>"Restore Defaults"</td> </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Window.MessageBoxButton
|
||||
* @typedef {number} Window.MessageBoxButton
|
||||
*/
|
||||
int WindowScriptingInterface::createMessageBox(QString title, QString text, int buttons, int defaultButton) {
|
||||
auto messageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text,
|
||||
|
|
|
@ -470,7 +470,7 @@ public slots:
|
|||
* </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Window.DisplayTexture
|
||||
* @typedef {string} Window.DisplayTexture
|
||||
*/
|
||||
bool setDisplayTexture(const QString& name);
|
||||
|
||||
|
@ -523,16 +523,21 @@ public slots:
|
|||
int openMessageBox(QString title, QString text, int buttons, int defaultButton);
|
||||
|
||||
/**jsdoc
|
||||
* Open the given resource in the Interface window or in a web browser depending on the url scheme
|
||||
* Open a URL in the Interface window or other application, depending on the URL's scheme. If the URL starts with
|
||||
* <code>hifi://</code> then that URL is navigated to in Interface, otherwise the URL is opened in the application the OS
|
||||
* associates with the URL's scheme (e.g., a Web browser for <code>http://</code>).
|
||||
* @function Window.openUrl
|
||||
* @param {string} url - The resource to open
|
||||
* @param {string} url - The URL to open.
|
||||
*/
|
||||
void openUrl(const QUrl& url);
|
||||
|
||||
/**jsdoc
|
||||
* (Android only) Open the requested Activity and optionally back to the scene when the activity is done
|
||||
* Open an Android activity and optionally return back to the scene when the activity is completed. <em>Android only.</em>
|
||||
* @function Window.openAndroidActivity
|
||||
* @param {string} activityName - The name of the activity to open. One of "Home", "Login" or "Privacy Policy"
|
||||
* @param {string} activityName - The name of the activity to open: one of <code>"Home"</code>, <code>"Login"</code>, or
|
||||
* <code>"Privacy Policy"</code>.
|
||||
* @param {boolean} backToScene - If <code>true</code>, the user is automatically returned back to the scene when the
|
||||
* activity is completed.
|
||||
*/
|
||||
void openAndroidActivity(const QString& activityName, const bool backToScene);
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ const OverlayID UNKNOWN_OVERLAY_ID = OverlayID();
|
|||
* @property {number} distance - The distance from the {@link PickRay} origin to the intersection point.
|
||||
* @property {Vec3} surfaceNormal - The normal of the overlay surface at the intersection point.
|
||||
* @property {Vec3} intersection - The position of the intersection point.
|
||||
* @property {Object} extraInfo Additional intersection details, if available.
|
||||
* @property {object} extraInfo Additional intersection details, if available.
|
||||
*/
|
||||
class RayToOverlayIntersectionResult {
|
||||
public:
|
||||
|
@ -482,7 +482,7 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Check if there is an overlay of a given ID.
|
||||
* @function Overlays.isAddedOverly
|
||||
* @function Overlays.isAddedOverlay
|
||||
* @param {Uuid} overlayID - The ID to check.
|
||||
* @returns {boolean} <code>true</code> if an overlay with the given ID exists, <code>false</code> otherwise.
|
||||
*/
|
||||
|
|
|
@ -70,7 +70,7 @@ public:
|
|||
* @function AnimationCache.prefetch
|
||||
* @param {string} url - URL of the resource to prefetch.
|
||||
* @param {object} [extra=null]
|
||||
* @returns {Resource}
|
||||
* @returns {ResourceObject}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
|
@ -79,7 +79,7 @@ public:
|
|||
* @param {string} url - URL of the resource to load.
|
||||
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
|
||||
* @param {} [extra=null]
|
||||
* @returns {Resource}
|
||||
* @returns {object}
|
||||
*/
|
||||
|
||||
|
||||
|
@ -87,7 +87,7 @@ public:
|
|||
* Returns animation resource for particular animation.
|
||||
* @function AnimationCache.getAnimation
|
||||
* @param {string} url - URL to load.
|
||||
* @returns {Resource} animation
|
||||
* @returns {AnimationObject} animation
|
||||
*/
|
||||
Q_INVOKABLE AnimationPointer getAnimation(const QString& url) { return getAnimation(QUrl(url)); }
|
||||
Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url);
|
||||
|
@ -104,6 +104,17 @@ private:
|
|||
|
||||
Q_DECLARE_METATYPE(AnimationPointer)
|
||||
|
||||
/**jsdoc
|
||||
* @class AnimationObject
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {string[]} jointNames
|
||||
* @property {FBXAnimationFrame[]} frames
|
||||
*/
|
||||
/// An animation loaded from the network.
|
||||
class Animation : public Resource {
|
||||
Q_OBJECT
|
||||
|
@ -118,9 +129,16 @@ public:
|
|||
|
||||
virtual bool isLoaded() const override;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function AnimationObject.getJointNames
|
||||
* @returns {string[]}
|
||||
*/
|
||||
Q_INVOKABLE QStringList getJointNames() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function AnimationObject.getFrames
|
||||
* @returns {FBXAnimationFrame[]}
|
||||
*/
|
||||
Q_INVOKABLE QVector<FBXAnimationFrame> getFrames() const;
|
||||
|
||||
const QVector<FBXAnimationFrame>& getFramesReference() const;
|
||||
|
|
|
@ -77,6 +77,17 @@ private:
|
|||
|
||||
typedef QSharedPointer<Sound> SharedSoundPointer;
|
||||
|
||||
/**jsdoc
|
||||
* @class SoundObject
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {boolean} downloaded
|
||||
* @property {number} duration
|
||||
*/
|
||||
class SoundScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -90,6 +101,10 @@ public:
|
|||
bool isReady() const { return _sound->isReady(); }
|
||||
float getDuration() { return _sound->getDuration(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function SoundObject.ready
|
||||
* @returns {Signal}
|
||||
*/
|
||||
signals:
|
||||
void ready();
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -292,7 +292,7 @@ public:
|
|||
*/
|
||||
/**jsdoc
|
||||
* Information about a single joint in an Avatar's skeleton hierarchy.
|
||||
* @typedef MyAvatar.SkeletonJoint
|
||||
* @typedef {object} MyAvatar.SkeletonJoint
|
||||
* @property {string} name - Joint name.
|
||||
* @property {number} index - Joint index.
|
||||
* @property {number} parentIndex - Index of this joint's parent (-1 if no parent).
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -578,8 +578,7 @@ public:
|
|||
* @param {Quat} rotation - The rotation of the joint relative to its parent.
|
||||
* @param {Vec3} translation - The translation of the joint relative to its parent.
|
||||
* @example <caption>Set your avatar to it's default T-pose for a while.<br />
|
||||
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/t-pose.png" />
|
||||
* </caption>
|
||||
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/images/t-pose.png" /></caption>
|
||||
* // Set all joint translations and rotations to defaults.
|
||||
* var i, length, rotation, translation;
|
||||
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
|
||||
|
@ -680,8 +679,7 @@ public:
|
|||
* @param {string} name - The name of the joint.
|
||||
* @param {Quat} rotation - The rotation of the joint relative to its parent.
|
||||
* @example <caption>Set your avatar to its default T-pose then rotate its right arm.<br />
|
||||
* <img alt="Avatar in T-pose with arm rotated"
|
||||
* src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/armpose.png" /></caption>
|
||||
* <img alt="Avatar in T-pose with arm rotated" src="https://docs.highfidelity.com/images/armpose.png" /></caption>
|
||||
* // Set all joint translations and rotations to defaults.
|
||||
* var i, length, rotation, translation;
|
||||
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
|
||||
|
@ -713,8 +711,7 @@ public:
|
|||
* @param {Vec3} translation - The translation of the joint relative to its parent.
|
||||
* @example <caption>Stretch your avatar's neck. Depending on the avatar you are using, you will either see a gap between
|
||||
* the head and body or you will see the neck stretched.<br />
|
||||
* <img alt="Avatar with neck stretched"
|
||||
* src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/stretched-neck.png" /></caption>
|
||||
* <img alt="Avatar with neck stretched" src="https://docs.highfidelity.com/images/stretched-neck.png" /></caption>
|
||||
* // Stretch your avatar's neck.
|
||||
* MyAvatar.setJointTranslation("Neck", { x: 0, y: 25, z: 0 });
|
||||
*
|
||||
|
@ -798,8 +795,7 @@ public:
|
|||
* @param {Quat[]} jointRotations - The rotations for all joints in the avatar. The values are in the same order as the
|
||||
* array returned by {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}.
|
||||
* @example <caption>Set your avatar to its default T-pose then rotate its right arm.<br />
|
||||
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/user/pages/06.api-reference/25.myavatar/armpose.png" />
|
||||
* </caption>
|
||||
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/images/armpose.png" /></caption>
|
||||
* // Set all joint translations and rotations to defaults.
|
||||
* var i, length, rotation, translation;
|
||||
* for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) {
|
||||
|
|
|
@ -307,7 +307,7 @@ namespace controller {
|
|||
* action.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Controller.Actions
|
||||
* @typedef {object} Controller.Actions
|
||||
*/
|
||||
// Device functions
|
||||
Input::NamedVector ActionsDevice::getAvailableInputs() const {
|
||||
|
|
|
@ -79,7 +79,7 @@ enum Hand {
|
|||
* {@link Controller.Hardware-Vive}.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Controller.Hardware
|
||||
* @typedef {object} Controller.Hardware
|
||||
* @example <caption>List all the currently available <code>Controller.Hardware</code> properties.</caption>
|
||||
* function printProperties(string, item) {
|
||||
* print(string);
|
||||
|
|
|
@ -231,7 +231,7 @@ void StandardController::focusOutEvent() {
|
|||
*
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Controller.Standard
|
||||
* @typedef {object} Controller.Standard
|
||||
*/
|
||||
Input::NamedVector StandardController::getAvailableInputs() const {
|
||||
static Input::NamedVector availableInputs {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -154,3 +154,11 @@ void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityI
|
|||
queueOctreeEditMessage(PacketType::EntityErase, bufferOut);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityEditPacketSender::queueCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID) {
|
||||
QByteArray bufferOut(NLPacket::maxPayloadSize(PacketType::EntityClone), 0);
|
||||
|
||||
if (EntityItemProperties::encodeCloneEntityMessage(entityIDToClone, newEntityID, bufferOut)) {
|
||||
queueOctreeEditMessage(PacketType::EntityClone, bufferOut);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
|
||||
|
||||
void queueEraseEntityMessage(const EntityItemID& entityItemID);
|
||||
void queueCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID);
|
||||
|
||||
// My server type is the model server
|
||||
virtual char getMyNodeType() const override { return NodeType::EntityServer; }
|
||||
|
|
|
@ -124,6 +124,13 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
|
||||
requestedProperties += PROP_LAST_EDITED_BY;
|
||||
|
||||
requestedProperties += PROP_CLONEABLE;
|
||||
requestedProperties += PROP_CLONE_LIFETIME;
|
||||
requestedProperties += PROP_CLONE_LIMIT;
|
||||
requestedProperties += PROP_CLONE_DYNAMIC;
|
||||
requestedProperties += PROP_CLONE_AVATAR_ENTITY;
|
||||
requestedProperties += PROP_CLONE_ORIGIN_ID;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
|
@ -288,6 +295,13 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, getQueryAACube());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, getLastEditedBy());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, getCloneable());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, getCloneLifetime());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, getCloneLimit());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, getCloneDynamic());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, getCloneAvatarEntity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, getCloneOriginID());
|
||||
|
||||
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
|
||||
requestedProperties,
|
||||
propertyFlags,
|
||||
|
@ -803,7 +817,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible);
|
||||
READ_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow);
|
||||
READ_ENTITY_PROPERTY(PROP_COLLISIONLESS, bool, setCollisionless);
|
||||
READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint8_t, setCollisionMask);
|
||||
READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint16_t, setCollisionMask);
|
||||
READ_ENTITY_PROPERTY(PROP_DYNAMIC, bool, setDynamic);
|
||||
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked);
|
||||
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
|
||||
|
@ -848,6 +862,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
|
||||
READ_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, QUuid, setLastEditedBy);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_CLONEABLE, bool, setCloneable);
|
||||
READ_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, float, setCloneLifetime);
|
||||
READ_ENTITY_PROPERTY(PROP_CLONE_LIMIT, float, setCloneLimit);
|
||||
READ_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, bool, setCloneDynamic);
|
||||
READ_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity);
|
||||
READ_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID);
|
||||
|
||||
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData, somethingChanged);
|
||||
|
||||
|
@ -1275,6 +1296,13 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
|
|||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy);
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneable, getCloneable);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneLifetime, getCloneLifetime);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneLimit, getCloneLimit);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneDynamic, getCloneDynamic);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneAvatarEntity, getCloneAvatarEntity);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneOriginID, getCloneOriginID);
|
||||
|
||||
properties._defaultSettings = false;
|
||||
|
||||
return properties;
|
||||
|
@ -1382,6 +1410,13 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy);
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneable, setCloneable);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneLifetime, setCloneLifetime);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneLimit, setCloneLimit);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneDynamic, setCloneDynamic);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneAvatarEntity, setCloneAvatarEntity);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneOriginID, setCloneOriginID);
|
||||
|
||||
if (updateQueryAACube()) {
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
@ -1814,7 +1849,7 @@ void EntityItem::setCollisionless(bool value) {
|
|||
});
|
||||
}
|
||||
|
||||
void EntityItem::setCollisionMask(uint8_t value) {
|
||||
void EntityItem::setCollisionMask(uint16_t value) {
|
||||
withWriteLock([&] {
|
||||
if ((_collisionMask & ENTITY_COLLISION_MASK_DEFAULT) != (value & ENTITY_COLLISION_MASK_DEFAULT)) {
|
||||
_collisionMask = (value & ENTITY_COLLISION_MASK_DEFAULT);
|
||||
|
@ -1879,7 +1914,7 @@ void EntityItem::setCreated(quint64 value) {
|
|||
});
|
||||
}
|
||||
|
||||
void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const {
|
||||
void EntityItem::computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask) const {
|
||||
if (_collisionless) {
|
||||
group = BULLET_COLLISION_GROUP_COLLISIONLESS;
|
||||
mask = 0;
|
||||
|
@ -1892,7 +1927,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
|
|||
group = BULLET_COLLISION_GROUP_STATIC;
|
||||
}
|
||||
|
||||
uint8_t userMask = getCollisionMask();
|
||||
uint16_t userMask = getCollisionMask();
|
||||
|
||||
if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) !=
|
||||
(bool)(userMask & USER_COLLISION_GROUP_OTHER_AVATAR)) {
|
||||
|
@ -1906,7 +1941,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
|
|||
if ((bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
|
||||
userMask &= ~USER_COLLISION_GROUP_MY_AVATAR;
|
||||
}
|
||||
mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask);
|
||||
mask = Physics::getDefaultCollisionMask(group) & (int32_t)(userMask);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2760,8 +2795,8 @@ bool EntityItem::getCollisionless() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
uint8_t EntityItem::getCollisionMask() const {
|
||||
uint8_t result;
|
||||
uint16_t EntityItem::getCollisionMask() const {
|
||||
uint16_t result;
|
||||
withReadLock([&] {
|
||||
result = _collisionMask;
|
||||
});
|
||||
|
@ -2975,3 +3010,118 @@ std::unordered_map<std::string, graphics::MultiMaterial> EntityItem::getMaterial
|
|||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
bool EntityItem::getCloneable() const {
|
||||
bool result;
|
||||
withReadLock([&] {
|
||||
result = _cloneable;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::setCloneable(bool value) {
|
||||
withWriteLock([&] {
|
||||
_cloneable = value;
|
||||
});
|
||||
}
|
||||
|
||||
float EntityItem::getCloneLifetime() const {
|
||||
float result;
|
||||
withReadLock([&] {
|
||||
result = _cloneLifetime;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::setCloneLifetime(float value) {
|
||||
withWriteLock([&] {
|
||||
_cloneLifetime = value;
|
||||
});
|
||||
}
|
||||
|
||||
float EntityItem::getCloneLimit() const {
|
||||
float result;
|
||||
withReadLock([&] {
|
||||
result = _cloneLimit;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::setCloneLimit(float value) {
|
||||
withWriteLock([&] {
|
||||
_cloneLimit = value;
|
||||
});
|
||||
}
|
||||
|
||||
bool EntityItem::getCloneDynamic() const {
|
||||
bool result;
|
||||
withReadLock([&] {
|
||||
result = _cloneDynamic;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::setCloneDynamic(bool value) {
|
||||
withWriteLock([&] {
|
||||
_cloneDynamic = value;
|
||||
});
|
||||
}
|
||||
|
||||
bool EntityItem::getCloneAvatarEntity() const {
|
||||
bool result;
|
||||
withReadLock([&] {
|
||||
result = _cloneAvatarEntity;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::setCloneAvatarEntity(bool value) {
|
||||
withWriteLock([&] {
|
||||
_cloneAvatarEntity = value;
|
||||
});
|
||||
}
|
||||
|
||||
const QUuid EntityItem::getCloneOriginID() const {
|
||||
QUuid result;
|
||||
withReadLock([&] {
|
||||
result = _cloneOriginID;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::setCloneOriginID(const QUuid& value) {
|
||||
withWriteLock([&] {
|
||||
_cloneOriginID = value;
|
||||
});
|
||||
}
|
||||
|
||||
void EntityItem::addCloneID(const QUuid& cloneID) {
|
||||
withWriteLock([&] {
|
||||
if (!_cloneIDs.contains(cloneID)) {
|
||||
_cloneIDs.append(cloneID);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EntityItem::removeCloneID(const QUuid& cloneID) {
|
||||
withWriteLock([&] {
|
||||
int index = _cloneIDs.indexOf(cloneID);
|
||||
if (index >= 0) {
|
||||
_cloneIDs.removeAt(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const QVector<QUuid> EntityItem::getCloneIDs() const {
|
||||
QVector<QUuid> result;
|
||||
withReadLock([&] {
|
||||
result = _cloneIDs;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::setCloneIDs(const QVector<QUuid>& cloneIDs) {
|
||||
withWriteLock([&] {
|
||||
_cloneIDs = cloneIDs;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -288,10 +288,10 @@ public:
|
|||
bool getCollisionless() const;
|
||||
void setCollisionless(bool value);
|
||||
|
||||
uint8_t getCollisionMask() const;
|
||||
void setCollisionMask(uint8_t value);
|
||||
uint16_t getCollisionMask() const;
|
||||
void setCollisionMask(uint16_t value);
|
||||
|
||||
void computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const;
|
||||
void computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask) const;
|
||||
|
||||
bool getDynamic() const;
|
||||
void setDynamic(bool value);
|
||||
|
@ -341,6 +341,19 @@ public:
|
|||
quint32 getStaticCertificateVersion() const;
|
||||
void setStaticCertificateVersion(const quint32&);
|
||||
|
||||
bool getCloneable() const;
|
||||
void setCloneable(bool value);
|
||||
float getCloneLifetime() const;
|
||||
void setCloneLifetime(float value);
|
||||
float getCloneLimit() const;
|
||||
void setCloneLimit(float value);
|
||||
bool getCloneDynamic() const;
|
||||
void setCloneDynamic(bool value);
|
||||
bool getCloneAvatarEntity() const;
|
||||
void setCloneAvatarEntity(bool value);
|
||||
const QUuid getCloneOriginID() const;
|
||||
void setCloneOriginID(const QUuid& value);
|
||||
|
||||
// TODO: get rid of users of getRadius()...
|
||||
float getRadius() const;
|
||||
|
||||
|
@ -494,6 +507,11 @@ public:
|
|||
void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; }
|
||||
uint64_t getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; }
|
||||
|
||||
void addCloneID(const QUuid& cloneID);
|
||||
void removeCloneID(const QUuid& cloneID);
|
||||
const QVector<QUuid> getCloneIDs() const;
|
||||
void setCloneIDs(const QVector<QUuid>& cloneIDs);
|
||||
|
||||
signals:
|
||||
void requestRenderUpdate();
|
||||
|
||||
|
@ -562,7 +580,7 @@ protected:
|
|||
bool _visible { ENTITY_ITEM_DEFAULT_VISIBLE };
|
||||
bool _canCastShadow{ ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW };
|
||||
bool _collisionless { ENTITY_ITEM_DEFAULT_COLLISIONLESS };
|
||||
uint8_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT };
|
||||
uint16_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT };
|
||||
bool _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC };
|
||||
bool _locked { ENTITY_ITEM_DEFAULT_LOCKED };
|
||||
QString _userData { ENTITY_ITEM_DEFAULT_USER_DATA };
|
||||
|
@ -648,6 +666,14 @@ protected:
|
|||
|
||||
bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera
|
||||
|
||||
bool _cloneable { ENTITY_ITEM_DEFAULT_CLONEABLE };
|
||||
float _cloneLifetime { ENTITY_ITEM_DEFAULT_CLONE_LIFETIME };
|
||||
float _cloneLimit { ENTITY_ITEM_DEFAULT_CLONE_LIMIT };
|
||||
bool _cloneDynamic { ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC };
|
||||
bool _cloneAvatarEntity { ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY };
|
||||
QUuid _cloneOriginID;
|
||||
QVector<QUuid> _cloneIDs;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
|
||||
std::mutex _materialsLock;
|
||||
|
|
|
@ -130,7 +130,7 @@ void buildStringToMaterialMappingModeLookup() {
|
|||
addMaterialMappingMode(PROJECTED);
|
||||
}
|
||||
|
||||
QString getCollisionGroupAsString(uint8_t group) {
|
||||
QString getCollisionGroupAsString(uint16_t group) {
|
||||
switch (group) {
|
||||
case USER_COLLISION_GROUP_DYNAMIC:
|
||||
return "dynamic";
|
||||
|
@ -146,7 +146,7 @@ QString getCollisionGroupAsString(uint8_t group) {
|
|||
return "";
|
||||
}
|
||||
|
||||
uint8_t getCollisionGroupAsBitMask(const QStringRef& name) {
|
||||
uint16_t getCollisionGroupAsBitMask(const QStringRef& name) {
|
||||
if (0 == name.compare(QString("dynamic"))) {
|
||||
return USER_COLLISION_GROUP_DYNAMIC;
|
||||
} else if (0 == name.compare(QString("static"))) {
|
||||
|
@ -164,7 +164,7 @@ uint8_t getCollisionGroupAsBitMask(const QStringRef& name) {
|
|||
QString EntityItemProperties::getCollisionMaskAsString() const {
|
||||
QString maskString("");
|
||||
for (int i = 0; i < NUM_USER_COLLISION_GROUPS; ++i) {
|
||||
uint8_t group = 0x01 << i;
|
||||
uint16_t group = 0x0001 << i;
|
||||
if (group & _collisionMask) {
|
||||
maskString.append(getCollisionGroupAsString(group));
|
||||
maskString.append(',');
|
||||
|
@ -175,7 +175,7 @@ QString EntityItemProperties::getCollisionMaskAsString() const {
|
|||
|
||||
void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) {
|
||||
QVector<QStringRef> groups = maskString.splitRef(',');
|
||||
uint8_t mask = 0x00;
|
||||
uint16_t mask = 0x0000;
|
||||
for (auto groupName : groups) {
|
||||
mask |= getCollisionGroupAsBitMask(groupName);
|
||||
}
|
||||
|
@ -436,6 +436,13 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_DPI, dpi);
|
||||
CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints);
|
||||
|
||||
CHECK_PROPERTY_CHANGE(PROP_CLONEABLE, cloneable);
|
||||
CHECK_PROPERTY_CHANGE(PROP_CLONE_LIFETIME, cloneLifetime);
|
||||
CHECK_PROPERTY_CHANGE(PROP_CLONE_LIMIT, cloneLimit);
|
||||
CHECK_PROPERTY_CHANGE(PROP_CLONE_DYNAMIC, cloneDynamic);
|
||||
CHECK_PROPERTY_CHANGE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity);
|
||||
CHECK_PROPERTY_CHANGE(PROP_CLONE_ORIGIN_ID, cloneOriginID);
|
||||
|
||||
changedProperties += _animation.getChangedProperties();
|
||||
changedProperties += _keyLight.getChangedProperties();
|
||||
changedProperties += _ambientLight.getChangedProperties();
|
||||
|
@ -479,7 +486,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {boolean} locked=false - Whether or not the entity can be edited or deleted. If <code>true</code> then the
|
||||
* entity's properties other than <code>locked</code> cannot be changed, and the entity cannot be deleted.
|
||||
* @property {boolean} visible=true - Whether or not the entity is rendered. If <code>true</code> then the entity is rendered.
|
||||
* @property {boolean} canCastShadows=true - Whether or not the entity casts shadows. Currently applicable only to
|
||||
* @property {boolean} canCastShadow=true - Whether or not the entity can cast a shadow. Currently applicable only to
|
||||
* {@link Entities.EntityType|Model} and {@link Entities.EntityType|Shape} entities. Shadows are cast if inside a
|
||||
* {@link Entities.EntityType|Zone} entity with <code>castShadows</code> enabled in its
|
||||
* {@link Entities.EntityProperties-Zone|keyLight} property.
|
||||
|
@ -1391,7 +1398,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
|
||||
/**jsdoc
|
||||
* The axis-aligned bounding box of an entity.
|
||||
* @typedef Entities.BoundingBox
|
||||
* @typedef {object} Entities.BoundingBox
|
||||
* @property {Vec3} brn - The bottom right near (minimum axes values) corner of the AA box.
|
||||
* @property {Vec3} tfl - The top far left (maximum axes values) corner of the AA box.
|
||||
* @property {Vec3} center - The center of the AA box.
|
||||
|
@ -1430,6 +1437,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); // Gettable but not settable except at entity creation
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); // Gettable but not settable
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE, cloneable);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_LIFETIME, cloneLifetime);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_LIMIT, cloneLimit);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_DYNAMIC, cloneDynamic);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_ORIGIN_ID, cloneOriginID);
|
||||
|
||||
// Rendering info
|
||||
if (!skipDefaults && !strictSemantics) {
|
||||
QScriptValue renderInfo = engine->newObject();
|
||||
|
@ -1506,7 +1520,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(localRenderAlpha, float, setLocalRenderAlpha);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionless, bool, setCollisionless);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ignoreForCollisions, bool, setCollisionless, getCollisionless); // legacy support
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionMask, uint8_t, setCollisionMask);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionMask, uint16_t, setCollisionMask);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(collidesWith, CollisionMask);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(collisionsWillMove, bool, setDynamic, getDynamic); // legacy support
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(dynamic, bool, setDynamic);
|
||||
|
@ -1642,6 +1656,13 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI);
|
||||
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneable, bool, setCloneable);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneLifetime, float, setCloneLifetime);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneLimit, float, setCloneLimit);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneDynamic, bool, setCloneDynamic);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneAvatarEntity, bool, setCloneAvatarEntity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneOriginID, QUuid, setCloneOriginID);
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
||||
|
@ -1793,6 +1814,13 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
|
||||
COPY_PROPERTY_IF_CHANGED(dpi);
|
||||
|
||||
COPY_PROPERTY_IF_CHANGED(cloneable);
|
||||
COPY_PROPERTY_IF_CHANGED(cloneLifetime);
|
||||
COPY_PROPERTY_IF_CHANGED(cloneLimit);
|
||||
COPY_PROPERTY_IF_CHANGED(cloneDynamic);
|
||||
COPY_PROPERTY_IF_CHANGED(cloneAvatarEntity);
|
||||
COPY_PROPERTY_IF_CHANGED(cloneOriginID);
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
||||
|
@ -2017,6 +2045,13 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
|||
|
||||
ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t);
|
||||
|
||||
ADD_PROPERTY_TO_MAP(PROP_CLONEABLE, Cloneable, cloneable, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid);
|
||||
|
||||
// FIXME - these are not yet handled
|
||||
//ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64);
|
||||
|
||||
|
@ -2331,6 +2366,12 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, properties.getEntityInstanceNumber());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, properties.getStaticCertificateVersion());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, properties.getCloneable());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, properties.getCloneLifetime());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, properties.getCloneLimit());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, properties.getCloneDynamic());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, properties.getCloneAvatarEntity());
|
||||
}
|
||||
|
||||
if (propertyCount > 0) {
|
||||
|
@ -2535,7 +2576,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONLESS, bool, setCollisionless);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_MASK, uint8_t, setCollisionMask);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_MASK, uint16_t, setCollisionMask);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DYNAMIC, bool, setDynamic);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData);
|
||||
|
@ -2701,6 +2742,12 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CERTIFICATE_ID, QString, setCertificateID);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE, bool, setCloneable);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_LIFETIME, float, setCloneLifetime);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_LIMIT, float, setCloneLimit);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_DYNAMIC, bool, setCloneDynamic);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
@ -2780,6 +2827,52 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
|
|||
return true;
|
||||
}
|
||||
|
||||
bool EntityItemProperties::encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer) {
|
||||
char* copyAt = buffer.data();
|
||||
int outputLength = 0;
|
||||
|
||||
if (buffer.size() < (int)(NUM_BYTES_RFC4122_UUID * 2)) {
|
||||
qCDebug(entities) << "ERROR - encodeCloneEntityMessage() called with buffer that is too small!";
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(copyAt, entityIDToClone.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
|
||||
copyAt += NUM_BYTES_RFC4122_UUID;
|
||||
outputLength += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
memcpy(copyAt, newEntityID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
|
||||
copyAt += NUM_BYTES_RFC4122_UUID;
|
||||
outputLength += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
buffer.resize(outputLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EntityItemProperties::decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID) {
|
||||
const unsigned char* packetData = (const unsigned char*)buffer.constData();
|
||||
const unsigned char* dataAt = packetData;
|
||||
size_t packetLength = buffer.size();
|
||||
processedBytes = 0;
|
||||
|
||||
if (NUM_BYTES_RFC4122_UUID * 2 > packetLength) {
|
||||
qCDebug(entities) << "EntityItemProperties::processEraseMessageDetails().... bailing because not enough bytes in buffer";
|
||||
return false; // bail to prevent buffer overflow
|
||||
}
|
||||
|
||||
QByteArray encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID);
|
||||
entityIDToClone = QUuid::fromRfc4122(encodedID);
|
||||
dataAt += encodedID.size();
|
||||
processedBytes += encodedID.size();
|
||||
|
||||
encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID);
|
||||
newEntityID = QUuid::fromRfc4122(encodedID);
|
||||
dataAt += encodedID.size();
|
||||
processedBytes += encodedID.size();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EntityItemProperties::markAllChanged() {
|
||||
_lastEditedByChanged = true;
|
||||
_simulationOwnerChanged = true;
|
||||
|
@ -2941,6 +3034,13 @@ void EntityItemProperties::markAllChanged() {
|
|||
|
||||
_dpiChanged = true;
|
||||
_relayParentJointsChanged = true;
|
||||
|
||||
_cloneableChanged = true;
|
||||
_cloneLifetimeChanged = true;
|
||||
_cloneLimitChanged = true;
|
||||
_cloneDynamicChanged = true;
|
||||
_cloneAvatarEntityChanged = true;
|
||||
_cloneOriginIDChanged = true;
|
||||
}
|
||||
|
||||
// The minimum bounding box for the entity.
|
||||
|
@ -3373,6 +3473,25 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
out += "isUVModeStretch";
|
||||
}
|
||||
|
||||
if (cloneableChanged()) {
|
||||
out += "cloneable";
|
||||
}
|
||||
if (cloneLifetimeChanged()) {
|
||||
out += "cloneLifetime";
|
||||
}
|
||||
if (cloneLimitChanged()) {
|
||||
out += "cloneLimit";
|
||||
}
|
||||
if (cloneDynamicChanged()) {
|
||||
out += "cloneDynamic";
|
||||
}
|
||||
if (cloneAvatarEntityChanged()) {
|
||||
out += "cloneAvatarEntity";
|
||||
}
|
||||
if (cloneOriginIDChanged()) {
|
||||
out += "cloneOriginID";
|
||||
}
|
||||
|
||||
getAnimation().listChangedProperties(out);
|
||||
getKeyLight().listChangedProperties(out);
|
||||
getAmbientLight().listChangedProperties(out);
|
||||
|
@ -3536,3 +3655,18 @@ bool EntityItemProperties::verifyStaticCertificateProperties() {
|
|||
// I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash.
|
||||
return verifySignature(EntityItem::_marketplacePublicKey, getStaticCertificateHash(), QByteArray::fromBase64(getCertificateID().toUtf8()));
|
||||
}
|
||||
|
||||
void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityIDToClone) {
|
||||
setName(getName() + "-clone-" + entityIDToClone.toString());
|
||||
setLocked(false);
|
||||
setLifetime(getCloneLifetime());
|
||||
setDynamic(getCloneDynamic());
|
||||
setClientOnly(getCloneAvatarEntity());
|
||||
setCreated(usecTimestampNow());
|
||||
setLastEdited(usecTimestampNow());
|
||||
setCloneable(ENTITY_ITEM_DEFAULT_CLONEABLE);
|
||||
setCloneLifetime(ENTITY_ITEM_DEFAULT_CLONE_LIFETIME);
|
||||
setCloneLimit(ENTITY_ITEM_DEFAULT_CLONE_LIMIT);
|
||||
setCloneDynamic(ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC);
|
||||
setCloneAvatarEntity(ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY);
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3, ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY);
|
||||
DEFINE_PROPERTY(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING);
|
||||
DEFINE_PROPERTY(PROP_COLLISIONLESS, Collisionless, collisionless, bool, ENTITY_ITEM_DEFAULT_COLLISIONLESS);
|
||||
DEFINE_PROPERTY(PROP_COLLISION_MASK, CollisionMask, collisionMask, uint8_t, ENTITY_COLLISION_MASK_DEFAULT);
|
||||
DEFINE_PROPERTY(PROP_COLLISION_MASK, CollisionMask, collisionMask, uint16_t, ENTITY_COLLISION_MASK_DEFAULT);
|
||||
DEFINE_PROPERTY(PROP_DYNAMIC, Dynamic, dynamic, bool, ENTITY_ITEM_DEFAULT_DYNAMIC);
|
||||
DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool, LightEntityItem::DEFAULT_IS_SPOTLIGHT);
|
||||
DEFINE_PROPERTY(PROP_INTENSITY, Intensity, intensity, float, LightEntityItem::DEFAULT_INTENSITY);
|
||||
|
@ -272,6 +272,13 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS);
|
||||
DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS);
|
||||
|
||||
DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE);
|
||||
DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME);
|
||||
DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT);
|
||||
DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC);
|
||||
DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY);
|
||||
DEFINE_PROPERTY_REF(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID);
|
||||
|
||||
static QString getComponentModeString(uint32_t mode);
|
||||
static QString getComponentModeAsString(uint32_t mode);
|
||||
|
||||
|
@ -294,6 +301,8 @@ public:
|
|||
QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties);
|
||||
|
||||
static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer);
|
||||
static bool encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer);
|
||||
static bool decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID);
|
||||
|
||||
static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes,
|
||||
EntityItemID& entityID, EntityItemProperties& properties);
|
||||
|
@ -369,6 +378,8 @@ public:
|
|||
bool verifyStaticCertificateProperties();
|
||||
static bool verifySignature(const QString& key, const QByteArray& text, const QByteArray& signature);
|
||||
|
||||
void convertToCloneProperties(const EntityItemID& entityIDToClone);
|
||||
|
||||
protected:
|
||||
QString getCollisionMaskAsString() const;
|
||||
void setCollisionMaskFromString(const QString& maskString);
|
||||
|
@ -515,6 +526,13 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
|||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientLightMode, ambientLightMode, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, SkyboxMode, skyboxMode, "");
|
||||
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cloneable, cloneable, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLifetime, cloneLifetime, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLimit, cloneLimit, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneDynamic, cloneDynamic, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneAvatarEntity, cloneAvatarEntity, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneOriginID, cloneOriginID, "");
|
||||
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, "");
|
||||
|
|
|
@ -97,4 +97,11 @@ const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid();
|
|||
|
||||
const bool ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS = false;
|
||||
|
||||
const bool ENTITY_ITEM_DEFAULT_CLONEABLE = false;
|
||||
const float ENTITY_ITEM_DEFAULT_CLONE_LIFETIME = 300.0f;
|
||||
const int ENTITY_ITEM_DEFAULT_CLONE_LIMIT = 0;
|
||||
const bool ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC = false;
|
||||
const bool ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY = false;
|
||||
const QUuid ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID = QUuid();
|
||||
|
||||
#endif // hifi_EntityItemPropertiesDefaults_h
|
||||
|
|
|
@ -204,6 +204,13 @@ enum EntityPropertyList {
|
|||
PROP_CERTIFICATE_ID,
|
||||
PROP_STATIC_CERTIFICATE_VERSION,
|
||||
|
||||
PROP_CLONEABLE,
|
||||
PROP_CLONE_LIFETIME,
|
||||
PROP_CLONE_LIMIT,
|
||||
PROP_CLONE_DYNAMIC,
|
||||
PROP_CLONE_AVATAR_ENTITY,
|
||||
PROP_CLONE_ORIGIN_ID,
|
||||
|
||||
PROP_HAZE_MODE,
|
||||
|
||||
PROP_KEYLIGHT_COLOR,
|
||||
|
|
|
@ -258,33 +258,9 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent);
|
||||
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
|
||||
|
||||
EntityItemID id = EntityItemID(QUuid::createUuid());
|
||||
|
||||
EntityItemID id;
|
||||
// If we have a local entity tree set, then also update it.
|
||||
bool success = true;
|
||||
if (_entityTree) {
|
||||
_entityTree->withWriteLock([&] {
|
||||
EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);
|
||||
if (entity) {
|
||||
if (propertiesWithSimID.queryAACubeRelatedPropertyChanged()) {
|
||||
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
|
||||
bool success;
|
||||
AACube queryAACube = entity->getQueryAACube(success);
|
||||
if (success) {
|
||||
propertiesWithSimID.setQueryAACube(queryAACube);
|
||||
}
|
||||
}
|
||||
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
// since we're creating this object we will immediately volunteer to own its simulation
|
||||
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
propertiesWithSimID.setLastEdited(entity->getLastEdited());
|
||||
} else {
|
||||
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
||||
success = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
bool success = addLocalEntityCopy(propertiesWithSimID, id);
|
||||
|
||||
// queue the packet
|
||||
if (success) {
|
||||
|
@ -295,6 +271,37 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
}
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properties, EntityItemID& id, bool isClone) {
|
||||
bool success = true;
|
||||
id = EntityItemID(QUuid::createUuid());
|
||||
|
||||
if (_entityTree) {
|
||||
_entityTree->withWriteLock([&] {
|
||||
EntityItemPointer entity = _entityTree->addEntity(id, properties, isClone);
|
||||
if (entity) {
|
||||
if (properties.queryAACubeRelatedPropertyChanged()) {
|
||||
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
|
||||
bool success;
|
||||
AACube queryAACube = entity->getQueryAACube(success);
|
||||
if (success) {
|
||||
properties.setQueryAACube(queryAACube);
|
||||
}
|
||||
}
|
||||
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
// since we're creating this object we will immediately volunteer to own its simulation
|
||||
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
properties.setLastEdited(entity->getLastEdited());
|
||||
} else {
|
||||
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
||||
success = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& textures,
|
||||
const QString& shapeType, bool dynamic, bool collisionless,
|
||||
const glm::vec3& position, const glm::vec3& gravity) {
|
||||
|
@ -320,6 +327,28 @@ QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QStrin
|
|||
return addEntity(properties);
|
||||
}
|
||||
|
||||
QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) {
|
||||
EntityItemID newEntityID;
|
||||
EntityItemProperties properties = getEntityProperties(entityIDToClone);
|
||||
bool cloneAvatarEntity = properties.getCloneAvatarEntity();
|
||||
properties.convertToCloneProperties(entityIDToClone);
|
||||
|
||||
if (cloneAvatarEntity) {
|
||||
return addEntity(properties, true);
|
||||
} else {
|
||||
// setLastEdited timestamp to 0 to ensure this entity gets updated with the properties
|
||||
// from the server-created entity, don't change this unless you know what you are doing
|
||||
properties.setLastEdited(0);
|
||||
bool success = addLocalEntityCopy(properties, newEntityID, true);
|
||||
if (success) {
|
||||
getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID);
|
||||
return newEntityID;
|
||||
} else {
|
||||
return QUuid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) {
|
||||
EntityPropertyFlags noSpecificProperties;
|
||||
return getEntityProperties(identity, noSpecificProperties);
|
||||
|
|
|
@ -224,6 +224,16 @@ public slots:
|
|||
Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic,
|
||||
bool collisionless, const glm::vec3& position, const glm::vec3& gravity);
|
||||
|
||||
/**jsdoc
|
||||
* Request a clone of an entity. Only entities that have been marked as 'cloneable' will be able to be cloned using this method.
|
||||
* A cloned entity has most of the properties of the orignal entity, and can be requested from clients that do not have rez permissions.
|
||||
* The client requests a clone from the entity server, which returns back the entityID of a valid clone if the operation was allowed.
|
||||
* @function Entities.cloneEntity
|
||||
* @param {Uuid} entityIDToClone - the ID of the entity to clone
|
||||
* @returns {Entities.EntityID} The ID of the newly created clone
|
||||
*/
|
||||
Q_INVOKABLE QUuid cloneEntity(QUuid entityIDToClone);
|
||||
|
||||
/**jsdoc
|
||||
* Get the properties of an entity.
|
||||
* @function Entities.getEntityProperties
|
||||
|
@ -1875,6 +1885,7 @@ private:
|
|||
bool polyVoxWorker(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
|
||||
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
|
||||
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||
bool addLocalEntityCopy(EntityItemProperties& propertiesWithSimID, EntityItemID& id, bool isClone = false);
|
||||
|
||||
EntityItemPointer checkForTreeEntityAndTypeMatch(const QUuid& entityID,
|
||||
EntityTypes::EntityType entityType = EntityTypes::Unknown);
|
||||
|
|
|
@ -65,6 +65,7 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
|
|||
removeEntityInternal(entity);
|
||||
if (entity->getElement()) {
|
||||
_deadEntities.insert(entity);
|
||||
_entityTree->cleanupCloneIDs(entity->getEntityItemID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -228,6 +228,7 @@ bool EntityTree::handlesEditPacketType(PacketType packetType) const {
|
|||
// we handle these types of "edit" packets
|
||||
switch (packetType) {
|
||||
case PacketType::EntityAdd:
|
||||
case PacketType::EntityClone:
|
||||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityErase:
|
||||
case PacketType::EntityPhysics:
|
||||
|
@ -492,7 +493,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
|||
return true;
|
||||
}
|
||||
|
||||
EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone) {
|
||||
EntityItemPointer result = NULL;
|
||||
EntityItemProperties props = properties;
|
||||
|
||||
|
@ -504,7 +505,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
|
|||
|
||||
if (!properties.getClientOnly() && getIsClient() &&
|
||||
!nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() &&
|
||||
!nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain) {
|
||||
!nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain && !isClone) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -592,6 +593,7 @@ void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ign
|
|||
return;
|
||||
}
|
||||
|
||||
cleanupCloneIDs(entityID);
|
||||
unhookChildAvatar(entityID);
|
||||
emit deletingEntity(entityID);
|
||||
emit deletingEntityPointer(existingEntity.get());
|
||||
|
@ -625,6 +627,28 @@ void EntityTree::unhookChildAvatar(const EntityItemID entityID) {
|
|||
});
|
||||
}
|
||||
|
||||
void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) {
|
||||
EntityItemPointer entity = findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
// remove clone ID from it's clone origin's clone ID list if clone origin exists
|
||||
const QUuid& cloneOriginID = entity->getCloneOriginID();
|
||||
if (!cloneOriginID.isNull()) {
|
||||
EntityItemPointer cloneOrigin = findEntityByID(cloneOriginID);
|
||||
if (cloneOrigin) {
|
||||
cloneOrigin->removeCloneID(entityID);
|
||||
}
|
||||
}
|
||||
// clear the clone origin ID on any clones that this entity had
|
||||
const QVector<QUuid>& cloneIDs = entity->getCloneIDs();
|
||||
foreach(const QUuid& cloneChildID, cloneIDs) {
|
||||
EntityItemPointer cloneChild = findEntityByEntityItemID(cloneChildID);
|
||||
if (cloneChild) {
|
||||
cloneChild->setCloneOriginID(QUuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool ignoreWarnings) {
|
||||
// NOTE: callers must lock the tree before using this method
|
||||
DeleteEntityOperator theOperator(getThisPointer());
|
||||
|
@ -653,6 +677,7 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool i
|
|||
}
|
||||
|
||||
// tell our delete operator about this entityID
|
||||
cleanupCloneIDs(entityID);
|
||||
unhookChildAvatar(entityID);
|
||||
theOperator.addEntityIDToDeleteList(entityID);
|
||||
emit deletingEntity(entityID);
|
||||
|
@ -1392,6 +1417,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
|
||||
int processedBytes = 0;
|
||||
bool isAdd = false;
|
||||
bool isClone = false;
|
||||
// we handle these types of "edit" packets
|
||||
switch (message.getType()) {
|
||||
case PacketType::EntityErase: {
|
||||
|
@ -1400,6 +1426,9 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
break;
|
||||
}
|
||||
|
||||
case PacketType::EntityClone:
|
||||
isClone = true; // fall through to next case
|
||||
// FALLTHRU
|
||||
case PacketType::EntityAdd:
|
||||
isAdd = true; // fall through to next case
|
||||
// FALLTHRU
|
||||
|
@ -1422,8 +1451,22 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
EntityItemProperties properties;
|
||||
startDecode = usecTimestampNow();
|
||||
|
||||
bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes,
|
||||
entityItemID, properties);
|
||||
bool validEditPacket = false;
|
||||
EntityItemID entityIDToClone;
|
||||
EntityItemPointer entityToClone;
|
||||
if (isClone) {
|
||||
QByteArray buffer = QByteArray::fromRawData(reinterpret_cast<const char*>(editData), maxLength);
|
||||
validEditPacket = EntityItemProperties::decodeCloneEntityMessage(buffer, processedBytes, entityIDToClone, entityItemID);
|
||||
if (validEditPacket) {
|
||||
entityToClone = findEntityByEntityItemID(entityIDToClone);
|
||||
if (entityToClone) {
|
||||
properties = entityToClone->getProperties();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes, entityItemID, properties);
|
||||
}
|
||||
|
||||
endDecode = usecTimestampNow();
|
||||
|
||||
EntityItemPointer existingEntity;
|
||||
|
@ -1491,24 +1534,26 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
|
||||
}
|
||||
|
||||
if ((isAdd || properties.lifetimeChanged()) &&
|
||||
((!senderNode->getCanRez() && senderNode->getCanRezTmp()) ||
|
||||
(!senderNode->getCanRezCertified() && senderNode->getCanRezTmpCertified()))) {
|
||||
// this node is only allowed to rez temporary entities. if need be, cap the lifetime.
|
||||
if (properties.getLifetime() == ENTITY_ITEM_IMMORTAL_LIFETIME ||
|
||||
properties.getLifetime() > _maxTmpEntityLifetime) {
|
||||
properties.setLifetime(_maxTmpEntityLifetime);
|
||||
if (!isClone) {
|
||||
if ((isAdd || properties.lifetimeChanged()) &&
|
||||
((!senderNode->getCanRez() && senderNode->getCanRezTmp()) ||
|
||||
(!senderNode->getCanRezCertified() && senderNode->getCanRezTmpCertified()))) {
|
||||
// this node is only allowed to rez temporary entities. if need be, cap the lifetime.
|
||||
if (properties.getLifetime() == ENTITY_ITEM_IMMORTAL_LIFETIME ||
|
||||
properties.getLifetime() > _maxTmpEntityLifetime) {
|
||||
properties.setLifetime(_maxTmpEntityLifetime);
|
||||
bumpTimestamp(properties);
|
||||
}
|
||||
}
|
||||
|
||||
if (isAdd && properties.getLocked() && !senderNode->isAllowedEditor()) {
|
||||
// if a node can't change locks, don't allow it to create an already-locked entity -- automatically
|
||||
// clear the locked property and allow the unlocked entity to be created.
|
||||
properties.setLocked(false);
|
||||
bumpTimestamp(properties);
|
||||
}
|
||||
}
|
||||
|
||||
if (isAdd && properties.getLocked() && !senderNode->isAllowedEditor()) {
|
||||
// if a node can't change locks, don't allow it to create an already-locked entity -- automatically
|
||||
// clear the locked property and allow the unlocked entity to be created.
|
||||
properties.setLocked(false);
|
||||
bumpTimestamp(properties);
|
||||
}
|
||||
|
||||
// If we got a valid edit packet, then it could be a new entity or it could be an update to
|
||||
// an existing entity... handle appropriately
|
||||
if (validEditPacket) {
|
||||
|
@ -1566,17 +1611,32 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
} else if (isAdd) {
|
||||
bool failedAdd = !allowed;
|
||||
bool isCertified = !properties.getCertificateID().isEmpty();
|
||||
bool isCloneable = properties.getCloneable();
|
||||
int cloneLimit = properties.getCloneLimit();
|
||||
if (!allowed) {
|
||||
qCDebug(entities) << "Filtered entity add. ID:" << entityItemID;
|
||||
} else if (!isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
|
||||
} else if (!isClone && !isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
|
||||
failedAdd = true;
|
||||
qCDebug(entities) << "User without 'uncertified rez rights' [" << senderNode->getUUID()
|
||||
<< "] attempted to add an uncertified entity with ID:" << entityItemID;
|
||||
} else if (isCertified && !senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) {
|
||||
} else if (!isClone && isCertified && !senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) {
|
||||
failedAdd = true;
|
||||
qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID()
|
||||
<< "] attempted to add a certified entity with ID:" << entityItemID;
|
||||
} else if (isClone && isCertified) {
|
||||
failedAdd = true;
|
||||
qCDebug(entities) << "User attempted to clone certified entity from entity ID:" << entityIDToClone;
|
||||
} else if (isClone && !isCloneable) {
|
||||
failedAdd = true;
|
||||
qCDebug(entities) << "User attempted to clone non-cloneable entity from entity ID:" << entityIDToClone;
|
||||
} else if (isClone && entityToClone && entityToClone->getCloneIDs().size() >= cloneLimit && cloneLimit != 0) {
|
||||
failedAdd = true;
|
||||
qCDebug(entities) << "User attempted to clone entity ID:" << entityIDToClone << " which reached it's cloneable limit.";
|
||||
} else {
|
||||
if (isClone) {
|
||||
properties.convertToCloneProperties(entityIDToClone);
|
||||
}
|
||||
|
||||
// this is a new entity... assign a new entityID
|
||||
properties.setCreated(properties.getLastEdited());
|
||||
properties.setLastEditedBy(senderNode->getUUID());
|
||||
|
@ -1597,10 +1657,15 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
}
|
||||
}
|
||||
|
||||
if (newEntity && isClone) {
|
||||
entityToClone->addCloneID(newEntity->getEntityItemID());
|
||||
newEntity->setCloneOriginID(entityIDToClone);
|
||||
}
|
||||
|
||||
if (newEntity) {
|
||||
newEntity->markAsChangedOnServer();
|
||||
notifyNewlyCreatedEntity(*newEntity, senderNode);
|
||||
|
||||
|
||||
startLogging = usecTimestampNow();
|
||||
if (wantEditLogging()) {
|
||||
qCDebug(entities) << "User [" << senderNode->getUUID() << "] added entity. ID:"
|
||||
|
@ -1927,6 +1992,7 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo
|
|||
|
||||
if (shouldEraseEntity(entityID, sourceNode)) {
|
||||
entityItemIDsToDelete << entityItemID;
|
||||
cleanupCloneIDs(entityItemID);
|
||||
}
|
||||
}
|
||||
deleteEntities(entityItemIDsToDelete, true, true);
|
||||
|
@ -1976,6 +2042,7 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons
|
|||
|
||||
if (shouldEraseEntity(entityID, sourceNode)) {
|
||||
entityItemIDsToDelete << entityItemID;
|
||||
cleanupCloneIDs(entityItemID);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2322,6 +2389,8 @@ bool EntityTree::readFromMap(QVariantMap& map) {
|
|||
return false;
|
||||
}
|
||||
|
||||
QMap<QUuid, QVector<QUuid>> cloneIDs;
|
||||
|
||||
bool success = true;
|
||||
foreach (QVariant entityVariant, entitiesQList) {
|
||||
// QVariantMap --> QScriptValue --> EntityItemProperties --> Entity
|
||||
|
@ -2409,11 +2478,43 @@ bool EntityTree::readFromMap(QVariantMap& map) {
|
|||
}
|
||||
}
|
||||
|
||||
// Convert old cloneable entities so they use cloneableData instead of userData
|
||||
if (contentVersion < (int)EntityVersion::CloneableData) {
|
||||
QJsonObject userData = QJsonDocument::fromJson(properties.getUserData().toUtf8()).object();
|
||||
QJsonObject grabbableKey = userData["grabbableKey"].toObject();
|
||||
QJsonValue cloneable = grabbableKey["cloneable"];
|
||||
if (cloneable.isBool() && cloneable.toBool()) {
|
||||
QJsonValue cloneLifetime = grabbableKey["cloneLifetime"];
|
||||
QJsonValue cloneLimit = grabbableKey["cloneLimit"];
|
||||
QJsonValue cloneDynamic = grabbableKey["cloneDynamic"];
|
||||
QJsonValue cloneAvatarEntity = grabbableKey["cloneAvatarEntity"];
|
||||
|
||||
// This is cloneable, we need to convert the properties
|
||||
properties.setCloneable(true);
|
||||
properties.setCloneLifetime(cloneLifetime.toInt());
|
||||
properties.setCloneLimit(cloneLimit.toInt());
|
||||
properties.setCloneDynamic(cloneDynamic.toBool());
|
||||
properties.setCloneAvatarEntity(cloneAvatarEntity.toBool());
|
||||
}
|
||||
}
|
||||
|
||||
EntityItemPointer entity = addEntity(entityItemID, properties);
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType();
|
||||
success = false;
|
||||
}
|
||||
|
||||
const QUuid& cloneOriginID = entity->getCloneOriginID();
|
||||
if (!cloneOriginID.isNull()) {
|
||||
cloneIDs[cloneOriginID].push_back(entity->getEntityItemID());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& entityID : cloneIDs.keys()) {
|
||||
auto entity = findEntityByID(entityID);
|
||||
if (entity) {
|
||||
entity->setCloneIDs(cloneIDs.value(entityID));
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
|
|
|
@ -110,13 +110,14 @@ public:
|
|||
// The newer API...
|
||||
void postAddEntity(EntityItemPointer entityItem);
|
||||
|
||||
EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone = false);
|
||||
|
||||
// use this method if you only know the entityID
|
||||
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||
|
||||
// check if the avatar is a child of this entity, If so set the avatar parentID to null
|
||||
void unhookChildAvatar(const EntityItemID entityID);
|
||||
void cleanupCloneIDs(const EntityItemID& entityID);
|
||||
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true);
|
||||
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = true);
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ class ReadBitstreamToTreeParams;
|
|||
* @property {Vec3} direction=0,-1,0 - The direction the light is shining.
|
||||
* @property {boolean} castShadows=false - If <code>true</code> then shadows are cast. Shadows are cast by avatars, plus
|
||||
* {@link Entities.EntityType|Model} and {@link Entities.EntityType|Shape} entities that have their
|
||||
* <code>{@link Entities.EntityProperties|canCastShadows}</code> property set to <code>true</code>.
|
||||
* <code>{@link Entities.EntityProperties|canCastShadow}</code> property set to <code>true</code>.
|
||||
*/
|
||||
class KeyLightPropertyGroup : public PropertyGroup {
|
||||
public:
|
||||
|
|
|
@ -258,6 +258,11 @@ public:
|
|||
QHash<QString, size_t> texcoordSetMap;
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* @typedef {object} FBXAnimationFrame
|
||||
* @property {Quat[]} rotations
|
||||
* @property {Vec3[]} translations
|
||||
*/
|
||||
/// A single animation frame extracted from an FBX document.
|
||||
class FBXAnimationFrame {
|
||||
public:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -279,7 +279,7 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic
|
|||
* moved down. The data value is how far the average position of all touch points moved.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Controller.Hardware-Keyboard
|
||||
* @typedef {object} Controller.Hardware-Keyboard
|
||||
* @todo <em>Currently, the mouse wheel in an ordinary mouse generates left/right wheel events instead of up/down.</em>
|
||||
*/
|
||||
controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInputs() const {
|
||||
|
|
|
@ -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}
|
||||
*/
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -138,7 +138,7 @@ public:
|
|||
* </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef location.LookupTrigger
|
||||
* @typedef {number} location.LookupTrigger
|
||||
*/
|
||||
enum LookupTrigger {
|
||||
UserInput,
|
||||
|
@ -184,7 +184,7 @@ public slots:
|
|||
/**jsdoc
|
||||
* Go to a specified metaverse address.
|
||||
* @function location.handleLookupString
|
||||
* @param {string} address - The address to go to: a <code>"hifi:/"<code> address, an IP address (e.g.,
|
||||
* @param {string} address - The address to go to: a <code>"hifi://"<code> address, an IP address (e.g.,
|
||||
* <code>"127.0.0.1"</code> or <code>"localhost"</code>), a domain name, a named path on a domain (starts with
|
||||
* <code>"/"</code>), a position or position and orientation, or a user (starts with <code>"@"</code>).
|
||||
* @param {boolean} fromSuggestions=false - Set to <code>true</code> if the address is obtained from the "Goto" dialog.
|
||||
|
|
|
@ -137,7 +137,7 @@ public:
|
|||
* </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Window.ConnectionRefusedReason
|
||||
* @typedef {number} Window.ConnectionRefusedReason
|
||||
*/
|
||||
enum class ConnectionRefusedReason : uint8_t {
|
||||
Unknown,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -29,10 +29,11 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::DomainList:
|
||||
return static_cast<PacketVersion>(DomainListVersion::GetMachineFingerprintFromUUIDSupport);
|
||||
case PacketType::EntityAdd:
|
||||
case PacketType::EntityClone:
|
||||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
case PacketType::EntityPhysics:
|
||||
return static_cast<PacketVersion>(EntityVersion::MaterialData);
|
||||
return static_cast<PacketVersion>(EntityVersion::CollisionMask16Bytes);
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
||||
case PacketType::AvatarIdentity:
|
||||
|
|
|
@ -131,6 +131,8 @@ public:
|
|||
OctreeDataFileReply,
|
||||
OctreeDataPersist,
|
||||
|
||||
EntityClone,
|
||||
|
||||
NUM_PACKET_TYPE
|
||||
};
|
||||
|
||||
|
@ -232,7 +234,9 @@ enum class EntityVersion : PacketVersion {
|
|||
SoftEntities,
|
||||
MaterialEntities,
|
||||
ShadowControl,
|
||||
MaterialData
|
||||
MaterialData,
|
||||
CloneableData,
|
||||
CollisionMask16Bytes
|
||||
};
|
||||
|
||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -779,7 +779,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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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<gpu::Stream::Format>(); // 1 for everyone
|
||||
WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT);
|
||||
WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT);
|
||||
}
|
||||
return WIRE_STREAM_FORMAT;
|
||||
}
|
||||
|
||||
gpu::Stream::FormatPointer& getInstancedWireStreamFormat() {
|
||||
if (!INSTANCED_WIRE_STREAM_FORMAT) {
|
||||
INSTANCED_WIRE_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
|
||||
INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT);
|
||||
INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT);
|
||||
INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
|
||||
}
|
||||
return INSTANCED_WIRE_STREAM_FORMAT;
|
||||
}
|
||||
|
||||
gpu::Stream::FormatPointer& getInstancedWireFadeStreamFormat() {
|
||||
if (!INSTANCED_WIRE_FADE_STREAM_FORMAT) {
|
||||
INSTANCED_WIRE_FADE_STREAM_FORMAT = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
|
||||
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT);
|
||||
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT);
|
||||
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
|
||||
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD2, gpu::Stream::TEXCOORD2, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
|
||||
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD3, gpu::Stream::TEXCOORD3, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
|
||||
INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD4, gpu::Stream::TEXCOORD4, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE);
|
||||
}
|
||||
return INSTANCED_WIRE_FADE_STREAM_FORMAT;
|
||||
}
|
||||
|
||||
QHash<SimpleProgramKey, gpu::PipelinePointer> GeometryCache::_simplePrograms;
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -36,12 +36,11 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
Engine::Engine() : Task(EngineTask::JobModel::create("Engine")),
|
||||
_renderContext(std::make_shared<RenderContext>())
|
||||
RenderEngine::RenderEngine() : Engine(EngineTask::JobModel::create("Engine"), std::make_shared<RenderContext>())
|
||||
{
|
||||
}
|
||||
|
||||
void Engine::load() {
|
||||
void RenderEngine::load() {
|
||||
auto config = getConfiguration();
|
||||
const QString configFile= "config/render.json";
|
||||
|
||||
|
|
|
@ -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<RenderContext>;
|
||||
|
||||
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<Engine>;
|
||||
using EnginePointer = std::shared_ptr<RenderEngine>;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -122,6 +122,7 @@ 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& withMetaCullGroup() { _flags.set(META_CULL_GROUP); return (*this); }
|
||||
|
|
|
@ -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<Scene> ScenePointer;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property IDENTITY {Quat} <code>{ x: 0, y: 0, z: 0, w: 1 }</code> : The identity rotation, i.e., no rotation.
|
||||
* @property {Quat} IDENTITY - <code>{ x: 0, y: 0, z: 0, w: 1 }</code> : The identity rotation, i.e., no rotation.
|
||||
* <em>Read-only.</em>
|
||||
* @example <caption>Print the <code>IDENTITY</code> value.</caption>
|
||||
* print(JSON.stringify(Quat.IDENTITY)); // { x: 0, y: 0, z: 0, w: 1 }
|
||||
|
|
|
@ -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<Location>;
|
||||
|
||||
/**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<Time>;
|
||||
|
||||
/**jsdoc
|
||||
* @typedef Scene.Stage.KeyLight
|
||||
* @typedef {object} Scene.Stage.KeyLight
|
||||
* @property {Vec3} color
|
||||
* @property {number} intensity
|
||||
* @property {number} ambientIntensity
|
||||
|
|
|
@ -558,6 +558,16 @@ static void scriptableResourceFromScriptValue(const QScriptValue& value, Scripta
|
|||
resource = static_cast<ScriptableResourceRawPtr>(value.toQObject());
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Resource
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {Resource.State} State
|
||||
*/
|
||||
static QScriptValue createScriptableResourcePrototype(ScriptEnginePointer engine) {
|
||||
auto prototype = engine->newObject();
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property NULL {Uuid} The null UUID, <code>{00000000-0000-0000-0000-000000000000}</code>.
|
||||
* @property {Uuid} NULL - The null UUID, <code>{00000000-0000-0000-0000-000000000000}</code>.
|
||||
*/
|
||||
|
||||
/// Scriptable interface for a UUID helper class object. Used exclusively in the JavaScript API
|
||||
|
|
|
@ -42,8 +42,7 @@
|
|||
/**jsdoc
|
||||
* The Vec3 API facilities for generating and manipulating 3-dimensional vectors. High Fidelity uses a right-handed
|
||||
* Cartesian coordinate system where the y-axis is the "up" and the negative z-axis is the "front" direction.
|
||||
* <img alt="High Fidelity coordinate system"
|
||||
* src="https://docs.highfidelity.com/user/pages/06.api-reference/43.vec3/opengl-coord-system.jpg" />
|
||||
* <img alt="High Fidelity coordinate system" src="https://docs.highfidelity.com/images/opengl-coord-system.jpg" />
|
||||
*
|
||||
* @namespace Vec3
|
||||
* @variation 0
|
||||
|
|
|
@ -34,13 +34,13 @@ enum CollisionFilterGroups {
|
|||
*
|
||||
*/
|
||||
|
||||
const int16_t BULLET_COLLISION_GROUP_STATIC = 1 << 0;
|
||||
const int16_t BULLET_COLLISION_GROUP_DYNAMIC = 1 << 1;
|
||||
const int16_t BULLET_COLLISION_GROUP_KINEMATIC = 1 << 2;
|
||||
const int16_t BULLET_COLLISION_GROUP_MY_AVATAR = 1 << 3;
|
||||
const int16_t BULLET_COLLISION_GROUP_OTHER_AVATAR = 1 << 4;
|
||||
const int32_t BULLET_COLLISION_GROUP_STATIC = 1 << 0;
|
||||
const int32_t BULLET_COLLISION_GROUP_DYNAMIC = 1 << 1;
|
||||
const int32_t BULLET_COLLISION_GROUP_KINEMATIC = 1 << 2;
|
||||
const int32_t BULLET_COLLISION_GROUP_MY_AVATAR = 1 << 3;
|
||||
const int32_t BULLET_COLLISION_GROUP_OTHER_AVATAR = 1 << 4;
|
||||
// ...
|
||||
const int16_t BULLET_COLLISION_GROUP_COLLISIONLESS = 1 << 14;
|
||||
const int32_t BULLET_COLLISION_GROUP_COLLISIONLESS = 1 << 31;
|
||||
|
||||
|
||||
/* Note: In order for objectA to collide with objectB at the filter stage
|
||||
|
@ -48,21 +48,21 @@ const int16_t BULLET_COLLISION_GROUP_COLLISIONLESS = 1 << 14;
|
|||
*/
|
||||
|
||||
// the default collision mask is: collides with everything except collisionless
|
||||
const int16_t BULLET_COLLISION_MASK_DEFAULT = ~ BULLET_COLLISION_GROUP_COLLISIONLESS;
|
||||
const int32_t BULLET_COLLISION_MASK_DEFAULT = ~ BULLET_COLLISION_GROUP_COLLISIONLESS;
|
||||
|
||||
// STATIC does not collide with itself (as optimization of physics simulation)
|
||||
const int16_t BULLET_COLLISION_MASK_STATIC = ~ (BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_KINEMATIC | BULLET_COLLISION_GROUP_STATIC);
|
||||
const int32_t BULLET_COLLISION_MASK_STATIC = ~ (BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_KINEMATIC | BULLET_COLLISION_GROUP_STATIC);
|
||||
|
||||
const int16_t BULLET_COLLISION_MASK_DYNAMIC = BULLET_COLLISION_MASK_DEFAULT;
|
||||
const int16_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_STATIC;
|
||||
const int32_t BULLET_COLLISION_MASK_DYNAMIC = BULLET_COLLISION_MASK_DEFAULT;
|
||||
const int32_t BULLET_COLLISION_MASK_KINEMATIC = BULLET_COLLISION_MASK_STATIC;
|
||||
|
||||
// MY_AVATAR does not collide with itself
|
||||
const int16_t BULLET_COLLISION_MASK_MY_AVATAR = ~(BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_MY_AVATAR);
|
||||
const int32_t BULLET_COLLISION_MASK_MY_AVATAR = ~(BULLET_COLLISION_GROUP_COLLISIONLESS | BULLET_COLLISION_GROUP_MY_AVATAR);
|
||||
|
||||
const int16_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_MASK_DEFAULT;
|
||||
const int32_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_MASK_DEFAULT;
|
||||
|
||||
// COLLISIONLESS gets an empty mask.
|
||||
const int16_t BULLET_COLLISION_MASK_COLLISIONLESS = 0;
|
||||
const int32_t BULLET_COLLISION_MASK_COLLISIONLESS = 0;
|
||||
|
||||
/**jsdoc
|
||||
* <p>An entity may collide with the following types of items:</p>
|
||||
|
@ -72,35 +72,35 @@ const int16_t BULLET_COLLISION_MASK_COLLISIONLESS = 0;
|
|||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>1</code></td><td>Static entities — non-dynamic entities with no velocity.</td></tr>
|
||||
* <tr><td><code>2</code></td><td>Dynamic entities — entities that have their <code>dynamic</code> property set to
|
||||
* <tr><td><code>2</code></td><td>Dynamic entities — entities that have their <code>dynamic</code> property set to
|
||||
* <code>true</code>.</td></tr>
|
||||
* <tr><td><code>4</code></td><td>Kinematic entities — non-dynamic entities with velocity.</td></tr>
|
||||
* <tr><td><code>8</code></td><td>My avatar.</td></tr>
|
||||
* <tr><td><code>16</code></td><td>Other avatars.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* <p>The values for the collision types that are enabled are added together to give the CollisionMask value. For example, a
|
||||
* <p>The values for the collision types that are enabled are added together to give the CollisionMask value. For example, a
|
||||
* value of <code>31</code> means that an entity will collide with all item types.</p>
|
||||
* @typedef {number} Entities.CollisionMask
|
||||
*/
|
||||
|
||||
// The USER collision groups are exposed to script and can be used to generate per-object collision masks.
|
||||
// They are not necessarily the same as the BULLET_COLLISION_GROUPS, but we start them off with matching numbers.
|
||||
const uint8_t USER_COLLISION_GROUP_STATIC = 1 << 0;
|
||||
const uint8_t USER_COLLISION_GROUP_DYNAMIC = 1 << 1;
|
||||
const uint8_t USER_COLLISION_GROUP_KINEMATIC = 1 << 2;
|
||||
const uint8_t USER_COLLISION_GROUP_MY_AVATAR = 1 << 3;
|
||||
const uint8_t USER_COLLISION_GROUP_OTHER_AVATAR = 1 << 4;
|
||||
const uint16_t USER_COLLISION_GROUP_STATIC = 1 << 0;
|
||||
const uint16_t USER_COLLISION_GROUP_DYNAMIC = 1 << 1;
|
||||
const uint16_t USER_COLLISION_GROUP_KINEMATIC = 1 << 2;
|
||||
const uint16_t USER_COLLISION_GROUP_MY_AVATAR = 1 << 3;
|
||||
const uint16_t USER_COLLISION_GROUP_OTHER_AVATAR = 1 << 4;
|
||||
|
||||
const uint8_t ENTITY_COLLISION_MASK_DEFAULT =
|
||||
const uint16_t ENTITY_COLLISION_MASK_DEFAULT =
|
||||
USER_COLLISION_GROUP_STATIC |
|
||||
USER_COLLISION_GROUP_DYNAMIC |
|
||||
USER_COLLISION_GROUP_KINEMATIC |
|
||||
USER_COLLISION_GROUP_MY_AVATAR |
|
||||
USER_COLLISION_GROUP_OTHER_AVATAR;
|
||||
|
||||
const uint8_t USER_COLLISION_MASK_AVATARS = USER_COLLISION_GROUP_MY_AVATAR | USER_COLLISION_GROUP_OTHER_AVATAR;
|
||||
const uint16_t USER_COLLISION_MASK_AVATARS = USER_COLLISION_GROUP_MY_AVATAR | USER_COLLISION_GROUP_OTHER_AVATAR;
|
||||
|
||||
const int NUM_USER_COLLISION_GROUPS = 5;
|
||||
const int32_t NUM_USER_COLLISION_GROUPS = 5;
|
||||
|
||||
#endif // hifi_PhysicsCollisionGroups_h
|
||||
|
|
|
@ -61,7 +61,7 @@ glm::quat computeBulletRotationStep(const glm::vec3& angularVelocity, float time
|
|||
}
|
||||
/* end Bullet code derivation*/
|
||||
|
||||
int16_t Physics::getDefaultCollisionMask(int16_t group) {
|
||||
int32_t Physics::getDefaultCollisionMask(int32_t group) {
|
||||
switch(group) {
|
||||
case BULLET_COLLISION_GROUP_STATIC:
|
||||
return BULLET_COLLISION_MASK_STATIC;
|
||||
|
|
|
@ -31,7 +31,7 @@ const float KINEMATIC_ANGULAR_SPEED_THRESHOLD = 0.008f; // ~0.5 deg/sec
|
|||
glm::quat computeBulletRotationStep(const glm::vec3& angularVelocity, float timeStep);
|
||||
|
||||
namespace Physics {
|
||||
int16_t getDefaultCollisionMask(int16_t group);
|
||||
int32_t getDefaultCollisionMask(int32_t group);
|
||||
|
||||
void setSessionUUID(const QUuid& sessionID);
|
||||
const QUuid& getSessionUUID();
|
||||
|
|
|
@ -44,13 +44,14 @@ class Camera : public QObject {
|
|||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*
|
||||
* @property position {Vec3} The position of the camera. You can set this value only when the camera is in independent mode.
|
||||
* @property orientation {Quat} The orientation of the camera. You can set this value only when the camera is in independent
|
||||
* @property {Vec3} position - The position of the camera. You can set this value only when the camera is in independent
|
||||
* mode.
|
||||
* @property mode {Camera.Mode} The camera mode.
|
||||
* @property frustum {ViewFrustum} The camera frustum.
|
||||
* @property cameraEntity {Uuid} The ID of the entity that is used for the camera position and orientation when the camera
|
||||
* is in entity mode.
|
||||
* @property {Quat} orientation - The orientation of the camera. You can set this value only when the camera is in
|
||||
* independent mode.
|
||||
* @property {Camera.Mode} mode - The camera mode.
|
||||
* @property {ViewFrustum} frustum - The camera frustum.
|
||||
* @property {Uuid} cameraEntity - The ID of the entity that is used for the camera position and orientation when the
|
||||
* camera is in entity mode.
|
||||
*/
|
||||
// FIXME: The cameraEntity property definition is copied from FancyCamera.h.
|
||||
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#ifndef hifi_task_Config_h
|
||||
#define hifi_task_Config_h
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qjsondocument.h>
|
||||
#include <QtCore/qjsonobject.h>
|
||||
|
@ -117,11 +119,19 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); emit loaded(); }
|
||||
|
||||
Q_INVOKABLE QObject* getConfig(const QString& name) { return nullptr; }
|
||||
|
||||
// Running Time measurement
|
||||
// The new stats signal is emitted once per run time of a job when stats (cpu runtime) are updated
|
||||
void setCPURunTime(double mstime) { _msCPURunTime = mstime; emit newStats(); }
|
||||
void setCPURunTime(const std::chrono::nanoseconds& runtime) { _msCPURunTime = std::chrono::duration<double, std::milli>(runtime).count(); emit newStats(); }
|
||||
double getCPURunTime() const { return _msCPURunTime; }
|
||||
|
||||
// Describe the node graph data connections of the associated Job/Task
|
||||
Q_INVOKABLE virtual bool isTask() const { return false; }
|
||||
Q_INVOKABLE virtual QObjectList getSubConfigs() const { return QObjectList(); }
|
||||
Q_INVOKABLE virtual int getNumSubs() const { return 0; }
|
||||
Q_INVOKABLE virtual QObject* getSubConfig(int i) const { return nullptr; }
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -151,6 +161,8 @@ signals:
|
|||
void dirtyEnabled();
|
||||
};
|
||||
|
||||
using QConfigPointer = std::shared_ptr<JobConfig>;
|
||||
|
||||
class TConfigProxy {
|
||||
public:
|
||||
using Config = JobConfig;
|
||||
|
@ -173,9 +185,10 @@ public:
|
|||
|
||||
using Persistent = PersistentConfig<TaskConfig>;
|
||||
|
||||
TaskConfig() = default ;
|
||||
TaskConfig() = default;
|
||||
TaskConfig(bool enabled) : JobConfig(enabled) {}
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function Render.getConfig
|
||||
* @param {string} name
|
||||
|
@ -212,6 +225,21 @@ public:
|
|||
return root->findChild<typename T::Config*>(tokens.front());
|
||||
}
|
||||
|
||||
Q_INVOKABLE bool isTask() const override { return true; }
|
||||
Q_INVOKABLE QObjectList getSubConfigs() const override {
|
||||
auto list = findChildren<JobConfig*>(QRegExp(".*"), Qt::FindDirectChildrenOnly);
|
||||
QObjectList returned;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
returned.push_back(list[i]);
|
||||
}
|
||||
return returned;
|
||||
}
|
||||
Q_INVOKABLE int getNumSubs() const override { return getSubConfigs().size(); }
|
||||
Q_INVOKABLE QObject* getSubConfig(int i) const override {
|
||||
auto subs = getSubConfigs();
|
||||
return ((i < 0 || i >= subs.size()) ? nullptr : subs[i]);
|
||||
}
|
||||
|
||||
void connectChildConfig(QConfigPointer childConfig, const std::string& name);
|
||||
void transferChildrenConfigs(QConfigPointer source);
|
||||
|
||||
|
@ -225,8 +253,6 @@ public slots:
|
|||
void refresh();
|
||||
};
|
||||
|
||||
using QConfigPointer = std::shared_ptr<QObject>;
|
||||
|
||||
}
|
||||
|
||||
#endif // hifi_task_Config_h
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
|
||||
using namespace task;
|
||||
|
||||
JobContext::JobContext(const QLoggingCategory& category) :
|
||||
profileCategory(category) {
|
||||
assert(&category);
|
||||
JobContext::JobContext() {
|
||||
}
|
||||
|
||||
JobContext::~JobContext() {
|
||||
|
|
|
@ -15,20 +15,15 @@
|
|||
#include "Config.h"
|
||||
#include "Varying.h"
|
||||
|
||||
#include "SettingHandle.h"
|
||||
|
||||
#include <Profile.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
namespace task {
|
||||
|
||||
class JobConcept;
|
||||
template <class JC> class JobT;
|
||||
template <class JC> class TaskT;
|
||||
template <class JC, class TP> class JobT;
|
||||
template <class JC, class TP> class TaskT;
|
||||
class JobNoIO {};
|
||||
|
||||
// Task Flow control class is a simple per value object used to communicate flow control commands trhough the graph of tasks.
|
||||
// From within the Job::Run function, you can access it from the JobCOntext and issue commands which will be picked up by the Task calling for the Job run.
|
||||
// From within the Job::Run function, you can access it from the JobContext and issue commands which will be picked up by the Task calling for the Job run.
|
||||
// This is first introduced to provide a way to abort all the work from within a task job. see the "abortTask" call
|
||||
class TaskFlow {
|
||||
public:
|
||||
|
@ -55,11 +50,10 @@ protected:
|
|||
// The JobContext can be derived to add more global state to it that Jobs can access
|
||||
class JobContext {
|
||||
public:
|
||||
JobContext(const QLoggingCategory& category);
|
||||
JobContext();
|
||||
virtual ~JobContext();
|
||||
|
||||
std::shared_ptr<JobConfig> jobConfig { nullptr };
|
||||
const QLoggingCategory& profileCategory;
|
||||
|
||||
// Task flow control
|
||||
TaskFlow taskFlow{};
|
||||
|
@ -80,10 +74,11 @@ public:
|
|||
|
||||
virtual const Varying getInput() const { return Varying(); }
|
||||
virtual const Varying getOutput() const { return Varying(); }
|
||||
virtual Varying& editInput() = 0;
|
||||
|
||||
virtual QConfigPointer& getConfiguration() { return _config; }
|
||||
virtual void applyConfiguration() = 0;
|
||||
void setCPURunTime(double mstime) { std::static_pointer_cast<Config>(_config)->setCPURunTime(mstime); }
|
||||
void setCPURunTime(const std::chrono::nanoseconds& runtime) { std::static_pointer_cast<Config>(_config)->setCPURunTime(runtime); }
|
||||
|
||||
QConfigPointer _config;
|
||||
protected:
|
||||
|
@ -114,10 +109,11 @@ template <class T, class JC, class I, class O> void jobRun(T& data, const JC& jo
|
|||
data.run(jobContext, input, output);
|
||||
}
|
||||
|
||||
template <class JC>
|
||||
template <class JC, class TP>
|
||||
class Job {
|
||||
public:
|
||||
using Context = JC;
|
||||
using TimeProfiler = TP;
|
||||
using ContextPointer = std::shared_ptr<Context>;
|
||||
using Config = JobConfig;
|
||||
using None = JobNoIO;
|
||||
|
@ -143,6 +139,7 @@ public:
|
|||
|
||||
const Varying getInput() const override { return _input; }
|
||||
const Varying getOutput() const override { return _output; }
|
||||
Varying& editInput() override { return _input; }
|
||||
|
||||
template <class... A>
|
||||
Model(const std::string& name, const Varying& input, QConfigPointer config, A&&... args) :
|
||||
|
@ -160,7 +157,7 @@ public:
|
|||
|
||||
|
||||
void applyConfiguration() override {
|
||||
Duration profileRange(trace_render(), ("configure::" + JobConcept::getName()).c_str());
|
||||
TimeProfiler probe(("configure::" + JobConcept::getName()));
|
||||
|
||||
jobConfigure(_data, *std::static_pointer_cast<C>(Concept::_config));
|
||||
}
|
||||
|
@ -185,6 +182,9 @@ public:
|
|||
QConfigPointer& getConfiguration() const { return _concept->getConfiguration(); }
|
||||
void applyConfiguration() { return _concept->applyConfiguration(); }
|
||||
|
||||
template <class I> void feedInput(const I& in) { _concept->editInput().template edit<I>() = in; }
|
||||
template <class I, class S> void feedInput(int index, const S& inS) { (_concept->editInput().template editN<I>(index)).template edit<S>() = inS; }
|
||||
|
||||
template <class T> T& edit() {
|
||||
auto concept = std::static_pointer_cast<typename T::JobModel>(_concept);
|
||||
assert(concept);
|
||||
|
@ -198,14 +198,10 @@ public:
|
|||
}
|
||||
|
||||
virtual void run(const ContextPointer& jobContext) {
|
||||
PerformanceTimer perfTimer(getName().c_str());
|
||||
// NOTE: rather than use the PROFILE_RANGE macro, we create a Duration manually
|
||||
Duration profileRange(jobContext->profileCategory, ("run::" + getName()).c_str());
|
||||
auto start = usecTimestampNow();
|
||||
|
||||
TimeProfiler probe(getName());
|
||||
auto startTime = std::chrono::high_resolution_clock::now();
|
||||
_concept->run(jobContext);
|
||||
|
||||
_concept->setCPURunTime((double)(usecTimestampNow() - start) / 1000.0);
|
||||
_concept->setCPURunTime((std::chrono::high_resolution_clock::now() - startTime));
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -220,13 +216,14 @@ protected:
|
|||
// The build method is where child Jobs can be added internally to the task
|
||||
// where the input of the task can be setup to feed the child jobs
|
||||
// and where the output of the task is defined
|
||||
template <class JC>
|
||||
class Task : public Job<JC> {
|
||||
template <class JC, class TP>
|
||||
class Task : public Job<JC, TP> {
|
||||
public:
|
||||
using Context = JC;
|
||||
using TimeProfiler = TP;
|
||||
using ContextPointer = std::shared_ptr<Context>;
|
||||
using Config = TaskConfig;
|
||||
using JobType = Job<JC>;
|
||||
using JobType = Job<JC, TP>;
|
||||
using None = typename JobType::None;
|
||||
using Concept = typename JobType::Concept;
|
||||
using ConceptPointer = typename JobType::ConceptPointer;
|
||||
|
@ -242,6 +239,8 @@ public:
|
|||
|
||||
const Varying getInput() const override { return _input; }
|
||||
const Varying getOutput() const override { return _output; }
|
||||
Varying& editInput() override { return _input; }
|
||||
|
||||
typename Jobs::iterator editJob(std::string name) {
|
||||
typename Jobs::iterator jobIt;
|
||||
for (jobIt = _jobs.begin(); jobIt != _jobs.end(); ++jobIt) {
|
||||
|
@ -295,7 +294,7 @@ public:
|
|||
auto model = std::make_shared<TaskModel>(name, input, std::make_shared<C>());
|
||||
|
||||
{
|
||||
Duration profileRange(trace_render(), ("build::" + model->getName()).c_str());
|
||||
TimeProfiler probe("build::" + model->getName());
|
||||
model->_data.build(*(model), model->_input, model->_output, std::forward<A>(args)...);
|
||||
}
|
||||
// Recreate the Config to use the templated type
|
||||
|
@ -330,7 +329,7 @@ public:
|
|||
}
|
||||
|
||||
void applyConfiguration() override {
|
||||
Duration profileRange(trace_render(), ("configure::" + JobConcept::getName()).c_str());
|
||||
TimeProfiler probe("configure::" + JobConcept::getName());
|
||||
jobConfigure(_data, *std::static_pointer_cast<C>(Concept::_config));
|
||||
for (auto& job : TaskConcept::_jobs) {
|
||||
job.applyConfiguration();
|
||||
|
@ -370,15 +369,44 @@ public:
|
|||
|
||||
protected:
|
||||
};
|
||||
|
||||
template <class JC, class TP>
|
||||
class Engine : public Task<JC, TP> {
|
||||
public:
|
||||
using Context = JC;
|
||||
using ContextPointer = std::shared_ptr<Context>;
|
||||
using Config = TaskConfig;
|
||||
using TaskType = Task<JC, TP>;
|
||||
using ConceptPointer = typename TaskType::ConceptPointer;
|
||||
|
||||
Engine(const ConceptPointer& concept, const ContextPointer& context) : TaskType(concept), _context(context) {}
|
||||
~Engine() = default;
|
||||
|
||||
void reset(const ContextPointer& context) { _context = context; }
|
||||
|
||||
void run() {
|
||||
if (_context) {
|
||||
run(_context);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void run(const ContextPointer& jobContext) override {
|
||||
TaskType::run(_context);
|
||||
}
|
||||
|
||||
ContextPointer _context;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#define Task_DeclareTypeAliases(ContextType) \
|
||||
#define Task_DeclareTypeAliases(ContextType, TimeProfiler) \
|
||||
using JobConfig = task::JobConfig; \
|
||||
using TaskConfig = task::TaskConfig; \
|
||||
template <class T> using PersistentConfig = task::PersistentConfig<T>; \
|
||||
using Job = task::Job<ContextType>; \
|
||||
using Task = task::Task<ContextType>; \
|
||||
using Job = task::Job<ContextType, TimeProfiler>; \
|
||||
using Task = task::Task<ContextType, TimeProfiler>; \
|
||||
using Engine = task::Engine<ContextType, TimeProfiler>; \
|
||||
using Varying = task::Varying; \
|
||||
template < typename T0, typename T1 > using VaryingSet2 = task::VaryingSet2<T0, T1>; \
|
||||
template < typename T0, typename T1, typename T2 > using VaryingSet3 = task::VaryingSet3<T0, T1, T2>; \
|
||||
|
@ -389,4 +417,16 @@ protected:
|
|||
template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7 > using VaryingSet8 = task::VaryingSet8<T0, T1, T2, T3, T4, T5, T6, T7>; \
|
||||
template < class T, int NUM > using VaryingArray = task::VaryingArray<T, NUM>;
|
||||
|
||||
|
||||
|
||||
#include <Profile.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#define Task_DeclareCategoryTimeProfilerClass(className, category) \
|
||||
class className : public PerformanceTimer { \
|
||||
public: \
|
||||
className(const std::string& label) : PerformanceTimer(label.c_str()), profileRange(category(), label.c_str()) {} \
|
||||
Duration profileRange; \
|
||||
};
|
||||
|
||||
#endif // hifi_task_Task_h
|
||||
|
|
|
@ -20,7 +20,7 @@ public:
|
|||
/**jsdoc
|
||||
* Creates a new button, adds it to this and returns it.
|
||||
* @function QmlFragmentClass#addButton
|
||||
* @param properties {Object} button properties
|
||||
* @param properties {object} button properties
|
||||
* @returns {TabletButtonProxy}
|
||||
*/
|
||||
Q_INVOKABLE QObject* addButton(const QVariant& properties);
|
||||
|
|
|
@ -493,7 +493,7 @@ protected:
|
|||
int _stableOrder;
|
||||
|
||||
/**jsdoc
|
||||
* @typedef TabletButtonProxy.ButtonProperties
|
||||
* @typedef {object} TabletButtonProxy.ButtonProperties
|
||||
* @property {string} icon - URL to button icon. (50 x 50)
|
||||
* @property {string} hoverIcon - URL to button icon, displayed during mouse hover. (50 x 50)
|
||||
* @property {string} activeHoverIcon - URL to button icon used when button is active, and during mouse hover. (50 x 50)
|
||||
|
|
|
@ -442,7 +442,7 @@ void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) {
|
|||
* <tr><td><code>RightHand</code></td><td>number</td><td>{@link Pose}</td><td>right hand pose.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Controller.Hardware-OculusTouch
|
||||
* @typedef {object} Controller.Hardware-OculusTouch
|
||||
*/
|
||||
controller::Input::NamedVector OculusControllerManager::TouchDevice::getAvailableInputs() const {
|
||||
using namespace controller;
|
||||
|
|
|
@ -1297,7 +1297,7 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu
|
|||
* <tr><td><code>TrackedObject15</code></td><td>number</td><td>{@link Pose}</td><td>Tracker 15 pose.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef Controller.Hardware-Vive
|
||||
* @typedef {object} Controller.Hardware-Vive
|
||||
*/
|
||||
controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const {
|
||||
using namespace controller;
|
||||
|
|
82
scripts/developer/utilities/lib/jet/jet.js
Normal file
82
scripts/developer/utilities/lib/jet/jet.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// Job Engine & Task...
|
||||
// jet.js
|
||||
//
|
||||
// Created by Sam Gateau, 2018/03/28
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
"use strict";
|
||||
|
||||
// traverse task tree
|
||||
function task_traverse(root, functor, depth) {
|
||||
if (root.isTask()) {
|
||||
depth++;
|
||||
for (var i = 0; i <root.getNumSubs(); i++) {
|
||||
var sub = root.getSubConfig(i);
|
||||
if (functor(sub, depth, i)) {
|
||||
task_traverse(sub, functor, depth, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function task_traverseTree(root, functor) {
|
||||
if (functor(root, 0, 0)) {
|
||||
task_traverse(root, functor, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Access job properties
|
||||
// return all the properties of a job
|
||||
function job_propKeys(job) {
|
||||
var keys = Object.keys(job)
|
||||
var propKeys = [];
|
||||
for (var k=0; k < keys.length;k++) {
|
||||
// Filter for relevant property
|
||||
var key = keys[k]
|
||||
if ((typeof job[key]) !== "function") {
|
||||
if ((key !== "objectName") && (key !== "cpuRunTime") && (key !== "enabled")) {
|
||||
propKeys.push(keys[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return propKeys;
|
||||
}
|
||||
|
||||
// Use this function to create a functor that will fill the specifed array with one entry name per task and job and it s rank
|
||||
function job_list_functor(jobList, maxDepth) {
|
||||
if (maxDepth === undefined) maxDepth = 100
|
||||
return function (job, depth, index) {
|
||||
jobList.push(job.objectName);
|
||||
return depth < maxDepth;
|
||||
}
|
||||
}
|
||||
|
||||
// Use this function to create a functor that will print the content of the Job visited calling the specified 'printout' function
|
||||
function job_print_functor(printout, showProps, maxDepth) {
|
||||
if (maxDepth === undefined) maxDepth = 100
|
||||
return function (job, depth, index) {
|
||||
var tab = " "
|
||||
var depthTab = "";
|
||||
for (var d = 0; d < depth; d++) { depthTab += tab }
|
||||
printout(depthTab + index + " " + job.objectName + " " + (job.enabled ? "on " : "off ") + job.cpuRunTime + "ms")
|
||||
if (showProps) {
|
||||
var keys = job_propKeys(job);
|
||||
for (var p=0; p < keys.length;p++) {
|
||||
var prop = job[keys[p]]
|
||||
printout(depthTab + tab + tab + typeof prop + " " + keys[p] + " " + prop);
|
||||
}
|
||||
}
|
||||
return depth < maxDepth;
|
||||
}
|
||||
}
|
||||
|
||||
// Expose functions for regular js including this files through the 'Jet' object
|
||||
/*Jet = {}
|
||||
Jet.task_traverse = task_traverse
|
||||
Jet.task_traverseTree = task_traverseTree
|
||||
Jet.job_propKeys = job_propKeys
|
||||
Jet.job_print_functor = job_print_functor
|
||||
*/
|
45
scripts/developer/utilities/lib/jet/qml/TaskList.qml
Normal file
45
scripts/developer/utilities/lib/jet/qml/TaskList.qml
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// jet/TaskList.qml
|
||||
//
|
||||
// Created by Sam Gateau, 2018/03/28
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4 as Original
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import "qrc:///qml/styles-uit"
|
||||
import "qrc:///qml/controls-uit" as HifiControls
|
||||
|
||||
import "../jet.js" as Jet
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi;}
|
||||
color: hifi.colors.baseGray;
|
||||
id: root
|
||||
// width: parent ? parent.width : 200
|
||||
// height: parent ? parent.height : 400
|
||||
property var rootConfig : Workload
|
||||
|
||||
Original.TextArea {
|
||||
id: textArea
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
text: ""
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
var message = ""
|
||||
var functor = Jet.job_print_functor(function (line) { message += line + "\n"; }, false);
|
||||
Jet.task_traverseTree(rootConfig, functor);
|
||||
textArea.append(message);
|
||||
}
|
||||
|
||||
function clearWindow() {
|
||||
textArea.remove(0,textArea.length);
|
||||
}
|
||||
}
|
119
scripts/developer/utilities/lib/jet/qml/TaskListView.qml
Normal file
119
scripts/developer/utilities/lib/jet/qml/TaskListView.qml
Normal file
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// jet/TaskListView.qml
|
||||
//
|
||||
// Created by Sam Gateau, 2018/05/09
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4 as Original
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import "qrc:///qml/styles-uit"
|
||||
import "qrc:///qml/controls-uit" as HifiControls
|
||||
|
||||
import "../jet.js" as Jet
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi;}
|
||||
color: hifi.colors.baseGray;
|
||||
id: root;
|
||||
|
||||
property var rootConfig : Workload
|
||||
property var myArray : []
|
||||
|
||||
Component.onCompleted: {
|
||||
var message = ""
|
||||
var maxDepth = 3;
|
||||
|
||||
var jobTreePath = []
|
||||
var jobsRoot;
|
||||
|
||||
var functor = function (job, depth, index) {
|
||||
var newItem = {"name": job.objectName, "level": depth, "index": index, "subNode": [], "init": depth < maxDepth, "path": ""}
|
||||
if (depth == 0) {
|
||||
jobsModel.append(newItem)
|
||||
jobsRoot = jobsModel.get(0).subNode;
|
||||
} else {
|
||||
if (jobTreePath.length < depth) {
|
||||
var node = jobsRoot;
|
||||
var path;
|
||||
for (var n = 0; n < jobTreePath.length; n++) {
|
||||
newItem.path += (n > 0 ? "." : "") + node.get(jobTreePath[n]).name
|
||||
node = node.get(jobTreePath[n]).subNode
|
||||
}
|
||||
node.append(newItem)
|
||||
jobTreePath.push(0);
|
||||
} else if (jobTreePath.length >= depth) {
|
||||
var node = jobsRoot;
|
||||
for (var n = 0; n < (depth - 1); n++) {
|
||||
newItem.path += (n > 0 ? "." : "") + node.get(jobTreePath[n]).name
|
||||
node = node.get(jobTreePath[n]).subNode
|
||||
}
|
||||
node.append(newItem)
|
||||
jobTreePath[depth-1] = index;
|
||||
while (jobTreePath.length > depth) {
|
||||
jobTreePath.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Jet.task_traverseTree(rootConfig, functor);
|
||||
}
|
||||
|
||||
|
||||
ListModel {
|
||||
id: jobsModel
|
||||
}
|
||||
|
||||
Component {
|
||||
id: objRecursiveDelegate
|
||||
Column {
|
||||
id: objRecursiveColumn
|
||||
clip: true
|
||||
visible: model.init
|
||||
|
||||
MouseArea {
|
||||
width: objRow.implicitWidth
|
||||
height: objRow.implicitHeight
|
||||
onDoubleClicked: {
|
||||
for(var i = 1; i < parent.children.length - 1; ++i) {
|
||||
parent.children[i].visible = !parent.children[i].visible
|
||||
}
|
||||
}
|
||||
Row {
|
||||
id: objRow
|
||||
Item {
|
||||
height: 1
|
||||
width: model.level * 15
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
property var config: root.rootConfig.getConfig(model.path + "." + model.name);
|
||||
text: (objRecursiveColumn.children.length > 2 ?
|
||||
objRecursiveColumn.children[1].visible ?
|
||||
qsTr("- ") : qsTr("+ ") : qsTr(" ")) + model.name + " ms=" + config.cpuRunTime.toFixed(2)
|
||||
checked: config.enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
model: subNode
|
||||
delegate: objRecursiveDelegate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Original.ScrollView {
|
||||
anchors.fill: parent
|
||||
ListView {
|
||||
id: theView
|
||||
model: jobsModel
|
||||
delegate: objRecursiveDelegate
|
||||
}
|
||||
}
|
||||
}
|
2
scripts/developer/utilities/lib/jet/qml/qmldir
Normal file
2
scripts/developer/utilities/lib/jet/qml/qmldir
Normal file
|
@ -0,0 +1,2 @@
|
|||
TaskList 1.0 TaskList.qml
|
||||
TaskViewList 1.0 TaskViewList.qml
|
|
@ -23,13 +23,22 @@ Item {
|
|||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 24
|
||||
|
||||
property var labelAreaWidthScale: 0.5
|
||||
|
||||
property bool integral: false
|
||||
property var config
|
||||
property string property
|
||||
property alias label: labelControl.text
|
||||
property alias min: sliderControl.minimumValue
|
||||
property alias max: sliderControl.maximumValue
|
||||
|
||||
property alias label: labelControl.text
|
||||
property bool showLabel: true
|
||||
|
||||
property bool showValue: true
|
||||
|
||||
|
||||
|
||||
signal valueChanged(real value)
|
||||
|
||||
Component.onCompleted: {
|
||||
|
@ -41,20 +50,12 @@ Item {
|
|||
HifiControls.Label {
|
||||
id: labelControl
|
||||
text: root.label
|
||||
enabled: true
|
||||
enabled: root.showLabel
|
||||
anchors.left: root.left
|
||||
anchors.right: root.horizontalCenter
|
||||
width: root.width * root.labelAreaWidthScale
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
id: labelValue
|
||||
text: sliderControl.value.toFixed(root.integral ? 0 : 2)
|
||||
anchors.right: root.right
|
||||
anchors.bottom: root.bottom
|
||||
anchors.bottomMargin: 0
|
||||
}
|
||||
|
||||
|
||||
Binding {
|
||||
id: bindingControl
|
||||
target: root.config
|
||||
|
@ -66,7 +67,7 @@ Item {
|
|||
HifiControls.Slider {
|
||||
id: sliderControl
|
||||
stepSize: root.integral ? 1.0 : 0.0
|
||||
anchors.left: root.horizontalCenter
|
||||
anchors.left: labelControl.right
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.top: root.top
|
||||
|
@ -74,4 +75,17 @@ Item {
|
|||
|
||||
onValueChanged: { root.valueChanged(value) }
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
id: labelValue
|
||||
enabled: root.showValue
|
||||
text: sliderControl.value.toFixed(root.integral ? 0 : 2)
|
||||
anchors.right: labelControl.right
|
||||
anchors.rightMargin: 5
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -36,9 +36,9 @@ Rectangle {
|
|||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 20
|
||||
spacing: 5
|
||||
Column {
|
||||
spacing: 10
|
||||
spacing: 5
|
||||
// padding: 10
|
||||
Repeater {
|
||||
model: [
|
||||
|
@ -61,7 +61,7 @@ Rectangle {
|
|||
|
||||
|
||||
Column {
|
||||
spacing: 10
|
||||
spacing: 5
|
||||
Repeater {
|
||||
model: [
|
||||
"Obscurance:LightingModel:enableObscurance",
|
||||
|
@ -81,7 +81,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
Column {
|
||||
spacing: 10
|
||||
spacing: 5
|
||||
Repeater {
|
||||
model: [
|
||||
"Ambient:LightingModel:enableAmbientLight",
|
||||
|
@ -105,7 +105,7 @@ Rectangle {
|
|||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 10
|
||||
spacing: 5
|
||||
Repeater {
|
||||
model: [ "Tone Mapping Exposure:ToneMapping:exposure:5.0:-5.0"
|
||||
]
|
||||
|
@ -211,9 +211,9 @@ Rectangle {
|
|||
|
||||
Separator {}
|
||||
Row {
|
||||
spacing: 10
|
||||
spacing: 5
|
||||
Column {
|
||||
spacing: 10
|
||||
spacing: 5
|
||||
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
|
@ -254,7 +254,7 @@ Rectangle {
|
|||
|
||||
}
|
||||
Column {
|
||||
spacing: 10
|
||||
spacing: 5
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Metas"
|
||||
|
@ -275,6 +275,13 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
Separator {}
|
||||
HifiControls.Button {
|
||||
text: "Engine"
|
||||
// activeFocusOnPress: false
|
||||
onClicked: {
|
||||
sendToScript({method: "openEngineView"});
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
|
13
scripts/developer/utilities/render/engineInspector.js
Normal file
13
scripts/developer/utilities/render/engineInspector.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
function openEngineTaskView() {
|
||||
// Set up the qml ui
|
||||
var qml = Script.resolvePath('engineInspector.qml');
|
||||
var window = new OverlayWindow({
|
||||
title: 'Render Engine',
|
||||
source: qml,
|
||||
width: 300,
|
||||
height: 400
|
||||
});
|
||||
window.setPosition(200, 50);
|
||||
//window.closed.connect(function() { Script.stop(); });
|
||||
}
|
||||
openEngineTaskView();
|
30
scripts/developer/utilities/render/engineInspector.qml
Normal file
30
scripts/developer/utilities/render/engineInspector.qml
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// deferredLighting.qml
|
||||
//
|
||||
// Created by Sam Gateau on 6/6/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import "qrc:///qml/styles-uit"
|
||||
import "qrc:///qml/controls-uit" as HifiControls
|
||||
|
||||
import "../lib/jet/qml" as Jet
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi;}
|
||||
id: render;
|
||||
anchors.fill: parent
|
||||
|
||||
property var mainViewTask: Render.getConfig("RenderMainView")
|
||||
|
||||
Jet.TaskListView {
|
||||
rootConfig: Render
|
||||
anchors.fill: render
|
||||
}
|
||||
}
|
|
@ -64,9 +64,6 @@
|
|||
button.editProperties({isActive: onLuciScreen});
|
||||
wireEventBridge(onLuciScreen);
|
||||
}
|
||||
|
||||
function fromQml(message) {
|
||||
}
|
||||
|
||||
button.clicked.connect(onClicked);
|
||||
tablet.screenChanged.connect(onScreenChanged);
|
||||
|
@ -82,14 +79,6 @@
|
|||
Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); });
|
||||
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
if (onLuciScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
button.clicked.disconnect(onClicked);
|
||||
tablet.screenChanged.disconnect(onScreenChanged);
|
||||
tablet.removeButton(button);
|
||||
});
|
||||
|
||||
function setDebugCursor(x, y) {
|
||||
nx = (x / Window.innerWidth);
|
||||
|
@ -98,4 +87,46 @@
|
|||
Render.getConfig("RenderMainView").getConfig("Antialiasing").debugCursorTexcoord = { x: nx, y: ny };
|
||||
}
|
||||
|
||||
|
||||
function fromQml(message) {
|
||||
switch (message.method) {
|
||||
case "openEngineView":
|
||||
openEngineTaskView();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var engineInspectorView = null
|
||||
function openEngineTaskView() {
|
||||
if (engineInspectorView == null) {
|
||||
var qml = Script.resolvePath('engineInspector.qml');
|
||||
var window = new OverlayWindow({
|
||||
title: 'Render Engine',
|
||||
source: qml,
|
||||
width: 300,
|
||||
height: 400
|
||||
});
|
||||
window.setPosition(200, 50);
|
||||
engineInspectorView = window
|
||||
window.closed.connect(function() { engineInspectorView = null; });
|
||||
} else {
|
||||
engineInspectorView.setPosition(200, 50);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
if (onLuciScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
button.clicked.disconnect(onClicked);
|
||||
tablet.screenChanged.disconnect(onScreenChanged);
|
||||
tablet.removeButton(button);
|
||||
|
||||
if (engineInspectorView !== null) {
|
||||
engineInspectorView.close()
|
||||
}
|
||||
});
|
||||
}());
|
|
@ -413,8 +413,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
|
||||
this.cloneHotspot = function(props, controllerData) {
|
||||
if (entityIsCloneable(props)) {
|
||||
var worldEntityProps = controllerData.nearbyEntityProperties[this.hand];
|
||||
var cloneID = cloneEntity(props, worldEntityProps);
|
||||
var cloneID = cloneEntity(props);
|
||||
return cloneID;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue