Merge branch 'master' into M21222

# Conflicts:
#	libraries/avatars/src/AvatarData.h
This commit is contained in:
David Rowe 2019-03-13 16:14:14 +13:00
commit 7445d73857
118 changed files with 3674 additions and 775 deletions

BIN
.vs/slnx.sqlite Normal file

Binary file not shown.

View file

@ -37,8 +37,14 @@ sudo apt-get -y install libpulse0 libnss3 libnspr4 libfontconfig1 libxcursor1 li
Install build tools:
```bash
# For Ubuntu 18.04
sudo apt-get install cmake
```
```bash
# For Ubuntu 16.04
wget https://cmake.org/files/v3.9/cmake-3.9.5-Linux-x86_64.sh
sudo sh cmake-3.9.5-Linux-x86_64.sh --prefix=/usr/local --exclude-subdir
```
Install Python 3:
```bash
@ -61,7 +67,7 @@ git tags
Then checkout last tag with:
```bash
git checkout tags/v0.71.0
git checkout tags/v0.79.0
```
### Compiling

View file

@ -117,7 +117,8 @@ void RenderThread::setup() {
{ std::unique_lock<std::mutex> lock(_frameLock); }
ovr::VrHandler::initVr();
ovr::VrHandler::setHandler(this);
// Enable KHR_no_error for this context
ovr::VrHandler::setHandler(this, true);
makeCurrent();

View file

@ -35,6 +35,8 @@
#include <TryLocker.h>
#include "../AssignmentDynamicFactory.h"
#include "../entities/AssignmentParentFinder.h"
#include <model-networking/ModelCache.h>
#include <hfm/ModelFormatRegistry.h>
const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
@ -60,7 +62,10 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
{
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
DependencyManager::set<AssignmentDynamicFactory>();
DependencyManager::set<ModelFormatRegistry>();
DependencyManager::set<ModelCache>();
DependencyManager::set<ResourceCacheSharedItems>();
DependencyManager::set<ResourceManager>();
// make sure we hear about node kills so we can tell the other nodes
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::handleAvatarKilled);
@ -1060,6 +1065,10 @@ void AvatarMixer::handleOctreePacket(QSharedPointer<ReceivedMessage> message, Sh
}
void AvatarMixer::aboutToFinish() {
DependencyManager::destroy<ResourceManager>();
DependencyManager::destroy<ResourceCacheSharedItems>();
DependencyManager::destroy<ModelCache>();
DependencyManager::destroy<ModelFormatRegistry>();
DependencyManager::destroy<AssignmentDynamicFactory>();
DependencyManager::destroy<AssignmentParentFinder>();

View file

@ -130,12 +130,16 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared
}
_lastReceivedSequenceNumber = sequenceNumber;
glm::vec3 oldPosition = getPosition();
bool oldHasPriority = _avatar->getHasPriority();
// compute the offset to the data payload
if (!_avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead()))) {
return false;
}
// Regardless of what the client says, restore the priority as we know it without triggering any update.
_avatar->setHasPriorityWithoutTimestampReset(oldHasPriority);
auto newPosition = getPosition();
if (newPosition != oldPosition) {
//#define AVATAR_HERO_TEST_HACK

View file

@ -19,11 +19,8 @@
class MixerAvatar : public AvatarData {
public:
bool getHasPriority() const { return _hasPriority; }
void setHasPriority(bool hasPriority) { _hasPriority = hasPriority; }
private:
bool _hasPriority { false };
};
using MixerAvatarSharedPointer = std::shared_ptr<MixerAvatar>;

View file

@ -1,6 +1,6 @@
macro(target_oculus_mobile)
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/oculus/VrApi)
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/oculus_1.22/VrApi)
# Mobile SDK
set(OVR_MOBILE_INCLUDE_DIRS ${INSTALL_DIR}/Include)

View file

@ -45,10 +45,10 @@ ANDROID_PACKAGES = {
'sharedLibFolder': 'lib',
'includeLibs': ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so']
},
'oculus': {
'file': 'ovr_sdk_mobile_1.19.0.zip',
'versionId': 's_RN1vlEvUi3pnT7WPxUC4pQ0RJBs27y',
'checksum': '98f0afb62861f1f02dd8110b31ed30eb',
'oculus_1.22': {
'file': 'ovr_sdk_mobile_1.22.zip',
'versionId': 'InhomR5gwkzyiLAelH3X9k4nvV3iIpA_',
'checksum': '1ac3c5b0521e5406f287f351015daff8',
'sharedLibFolder': 'VrApi/Libs/Android/arm64-v8a/Release',
'includeLibs': ['libvrapi.so']
},

View file

@ -5,6 +5,7 @@
{ "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.E", "when": "!Keyboard.Control", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.Q", "when": "!Keyboard.Control", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" },
{ "comment" : "Mouse turn need to be small continuous increments",
"from": { "makeAxis" : [

View file

@ -14,7 +14,6 @@
<glyph glyph-name="headphones-mic" unicode="&#102;" d="M419 348l-22 0c-3 48-42 83-89 83l-105 0c-47 0-86-35-89-83l-20 0c-25 0-45-19-45-44l0-71c0-25 20-45 45-45l19 0c1-27 14-50 33-66-3-17 5-35 20-45l41-25c7-4 15-7 23-7 3 0 6 1 10 2 11 2 21 9 27 19 13 21 6 48-14 60l-41 26c-10 6-21 8-33 5-8-2-15-6-20-11-12 11-20 27-20 45l0 152c0 34 29 62 64 62l105 0c35 0 64-28 64-62l0-152c0-1-1-3-1-3l48 0c25 0 46 20 46 45l0 71c0 25-21 44-46 44z m-306-134l-19 0c-10 0-19 9-19 19l0 71c0 10 9 19 19 19l19 0z m61-90c3 4 7 6 11 7 2 1 3 1 4 1 4 0 7-1 9-3l41-25c8-5 11-16 6-24-3-4-7-7-11-8-5-1-10 0-14 2l-40 25c-8 6-11 16-6 25z m264 109c0-10-8-19-19-19l-22 0 0 109 22 0c11 0 19-9 19-19z"/>
<glyph glyph-name="gamepad" unicode="&#103;" d="M107 136c-10 0-20 3-29 10-46 37-8 131-4 141l1 1c1 4 3 7 5 11 16 30 37 73 81 73l182 0c51 0 71-47 87-85 5-10 44-101 4-138-28-26-67-1-102 22-21 13-42 26-56 26l-39 0c-13 0-33-13-53-27-25-16-52-34-77-34z m-10 141c-10-24-30-90-3-112 17-13 47 7 76 26 24 16 47 31 67 31l40 0c20 0 44-15 68-30 28-18 59-37 72-25 23 22 0 89-10 110-17 42-31 70-64 70l-182 0c-29 0-45-33-59-60-2-3-3-7-5-10z m247-36l-3 0c-7 0-13 6-13 13 0 7 6 13 13 13l3 0c7 0 13-6 13-13 0-7-6-13-13-13z m0 55l-3 0c-7 0-13 6-13 13 0 7 6 13 13 13l3 0c7 0 13-6 13-13 0-7-6-13-13-13z m-29-29l-2 0c-8 0-13 6-13 13 0 8 5 13 13 13l2 0c8 0 13-5 13-13 0-7-5-13-13-13z m57 0l-3 0c-7 0-13 6-13 13 0 8 6 13 13 13l3 0c7 0 13-5 13-13 0-7-6-13-13-13z m-172 26l-12 0 0 13c0 7-6 13-13 13-7 0-13-6-13-13l0-13-13 0c-7 0-13-6-13-13 0-7 6-13 13-13l13 0 0-12c0-8 6-14 13-14 7 0 13 6 13 14l0 12 12 0c8 0 13 6 13 13 0 7-5 13-13 13z"/>
<glyph glyph-name="headphones" unicode="&#104;" d="M141 168l0 152c0 35 28 65 63 65l106 0c34 0 62-30 62-65l0-151c0-1 1-3 0-4l48 0c25 0 47 21 47 46l0 70c0 25-22 45-47 45l-21 0c-4 47-42 83-89 83l-106 0c-47 0-86-36-89-83l-19 0c-25 0-45-20-45-45l0-70c0-25 20-46 45-46l45 0z m-63 43l0 70c0 11 7 19 18 19l19 0 0-108-19 0c-11 0-18 8-18 19z m362 0c0-11-9-19-20-19l-20 0 0 108 20 0c11 0 20-8 20-19z"/>
<glyph glyph-name="mic" unicode="&#105;" d="M318 370c0 33-26 59-59 59l-6 0c-33 0-59-26-59-59l0-105c0-33 26-59 59-59l6 0c33 0 59 26 59 59z m-25-103c0-19-15-34-34-34l-7 0c-19 0-34 15-34 34l0 104c0 19 15 34 34 34l7 0c19 0 34-15 34-34z m82 8c0 7-6 13-12 13-7 0-13-6-13-13 0-51-42-93-93-93-52 0-93 42-93 93 0 7-6 13-13 13-7 0-12-6-12-13 0-60 46-110 104-117l0-34-80 0c-8 0-14-6-14-14 0-7 6-13 14-13l186 0c7 0 13 6 13 13 0 8-6 14-13 14l-80 0 0 34c60 6 106 56 106 117z"/>
<glyph glyph-name="upload" unicode="&#106;" d="M330 193l-83 86-84-86 52 0 0-141 61 23 0 118z m-12 247c-39 0-76-15-105-41-23-21-40-49-47-80-9 3-19 4-29 4-53 0-97-43-97-97 0-54 44-98 97-98 1 0 19 0 45 0l0 29c-26 0-44 0-45 0-37 0-68 31-68 69 0 37 31 68 68 68 12 0 23-3 34-9l19-11 2 23c3 31 18 59 41 81 23 21 54 33 85 33 70 0 127-57 127-127 0-70-57-127-127-127 0 0-5 0-10 0l0-29c5 0 10 0 10 0 86 0 156 70 156 156 0 86-70 156-156 156z"/>
<glyph glyph-name="script" unicode="&#107;" d="M283 80l-150 0c-30 0-56 15-73 44-13 21-17 42-17 42l-3 15 91 0 0 252 315 0 0-238c1-7 5-58-21-87-13-14-29-21-50-21-42 0-63 23-73 41-6 11-9 21-10 29l-220 0c2-6 5-13 9-20 13-21 31-32 52-32l150 0c7 0 13-6 13-13 0-6-6-12-13-12z m-127 101l158 0 1-12c0 0 1-15 9-30 10-18 27-28 51-28 13 0 23 5 31 13 10 11 14 29 15 42 1 15 0 27 0 27l0 1 0 214-265 0z m225 168l-185 0c-8 0-14 6-14 13 0 8 6 14 14 14l185 0c8 0 14-6 14-14 0-7-6-13-14-13z m0-61l-185 0c-8 0-14 7-14 14 0 8 6 14 14 14l185 0c8 0 14-6 14-14 0-7-6-14-14-14z m0-60l-185 0c-8 0-14 6-14 14 0 7 6 13 14 13l185 0c8 0 14-6 14-13 0-8-6-14-14-14z"/>
<glyph glyph-name="text" unicode="&#108;" d="M220 134l-81 232c-1 2-3 4-6 4l-10 0c-3 0-5-2-6-4l-83-233c-1-2 0-5 1-7 1-1 3-3 6-3l16 0c3 0 5 3 6 5l27 79 74 0 27-79c1-2 4-5 7-5l16 0c2 0 4 2 5 4 2 1 2 4 1 7z m-120 102l26 73c1 2 1 3 2 5 0-2 1-3 2-4l24-74z m252 60c-10 12-25 18-44 18-17 0-35-5-53-14-3-2-4-6-3-9l5-14c1-2 2-3 4-4 2-1 4 0 6 1 14 8 28 12 41 12 11 0 19-3 23-10 5-7 8-18 8-33l0-4-23-1c-25 0-44-6-58-16-14-11-21-26-21-45 0-17 4-31 14-40 10-10 23-16 40-16 12 0 23 3 32 8 6 4 11 8 17 14l1-13c1-4 4-7 7-7l10 0c4 0 8 4 8 8l0 115c0 23-5 39-14 50z m-87-119c0 11 4 19 11 24 8 5 22 9 42 10l19 1 0-10c0-17-4-30-12-39-8-9-19-13-33-13-9 0-16 2-20 7-5 4-7 11-7 20z m186-105c-10 0-15 8-15 18 0 22 0 298 0 320 0 10 5 19 15 19 0 0 0 0 0 0 10 0 15-8 15-18 1-22 1-299 1-321 0-10-6-18-16-18 0 0 0 0 0 0z"/>
@ -97,7 +96,6 @@
<glyph glyph-name="acceleration" unicode="&#57347;" d="M207 350c3 2 8 3 14 3 3 0 5 0 8-1 2 0 4-1 5-2 2-1 3-2 4-4 1-1 2-3 2-4l0 0c1 1 2 3 3 4 2 1 3 3 5 4 2 1 4 2 7 2 2 1 5 1 8 1 5 0 8 0 11-1 3-2 5-3 7-5 1-2 2-5 3-7 1-3 1-6 1-8l0-35-15 0 0 35c0 1 0 3 0 4 0 2-1 4-2 5-1 1-2 2-3 3-2 1-4 1-7 1-2 0-4 0-6-1-2-1-4-2-6-4-1-1-2-3-3-5-1-2-1-4-1-7l0-31-14 0 0 35c0 1-1 3-1 4 0 2-1 3-1 5-1 1-2 2-4 3-2 1-4 1-7 1-2 0-4 0-6-1-2-1-4-2-5-4-2-1-3-3-3-5-1-2-2-4-2-7l0-32-14 0 0 41c0 3 0 5 0 8 0 2 0 4-1 6l14 0c0-1 1-3 1-5 0-2 0-3 0-4l0 0c2 3 4 6 8 8z m112-92c0-2 0-4-1-5 0-1-1-3-2-4 0-2-1-3-2-4-1-2-2-3-3-5l-11-15 18 0 0-6-25 0 0 6 14 20c2 2 3 4 4 6 1 2 1 4 1 6 0 3 0 5-1 6-1 2-3 3-5 3-2 0-4-1-5-2-1-2-2-3-2-6l-7 1c1 4 3 7 5 9 2 2 5 3 9 3 2 0 4 0 6-1 1 0 3-1 4-3 1-1 2-2 2-4 1-2 1-3 1-5z m-112-18c-1 1-2 2-3 3-1 1-3 1-5 1-1 0-3 0-4-2-1-1-2-3-2-5 0-2 1-3 2-4 1-1 3-2 6-3 1-1 2-1 3-2 2-1 3-2 4-3 1-1 2-2 2-4 1-1 1-3 1-5 0-2 0-4-1-6-1-2-2-3-3-5-1-1-3-2-4-2-2-1-4-1-6-1-3 0-5 0-7 1-3 1-4 3-6 5l5 5c1-2 2-3 3-4 2 0 3-1 5-1 2 0 4 1 5 2 1 2 2 3 2 6 0 1 0 2-1 3 0 1-1 2-2 2 0 1-1 1-2 2-1 0-2 1-3 1-2 1-3 1-4 2-1 0-2 1-3 2-1 1-1 2-2 4 0 1-1 3-1 5 0 2 1 4 1 5 1 2 2 3 3 5 1 1 3 2 4 2 2 1 4 1 6 1 2 0 5 0 7-1 2-1 3-2 5-4z m18-16c0-2 0-4 0-6 1-2 1-4 2-5 1-1 2-3 3-3 1-1 3-2 4-2 2 0 4 1 5 2 1 2 2 3 3 5l6-3c-2-3-3-6-6-8-2-1-5-2-8-2-5 0-10 2-13 6-3 4-4 10-4 18 0 4 0 7 1 10 1 3 2 6 4 8 1 2 3 3 5 5 2 1 4 1 7 1 2 0 5 0 7-1 2-2 3-3 5-5 1-2 2-5 2-7 1-3 1-6 1-9l0-4-24 0z m16 6c0 4 0 7-2 10-1 2-3 4-6 4-1 0-3-1-4-2-1-1-2-2-2-3-1-2-1-3-2-5 0-1 0-2 0-4l16 0z m31-28c-3 0-5 0-7 1-2 2-4 3-5 5-2 3-3 5-3 8-1 3-1 6-1 10 0 4 0 7 1 10 0 3 1 5 3 8 1 2 3 3 5 5 2 1 5 1 7 1 3 0 5 0 6-1 2-1 3-1 4-2l-4-6c0 1-1 1-2 2-1 0-2 1-4 1-1 0-2-1-3-2-1-1-2-2-3-3-1-2-1-4-2-6 0-2 0-4 0-7 0-2 0-5 0-7 1-2 1-4 2-5 1-2 2-3 3-4 1-1 2-1 4-1 1 0 3 0 4 1 1 0 1 1 2 2l4-6c-3-3-6-4-11-4z m17 74l-107 0c-2 0-4 2-4 4 0 2 2 4 4 4l107 0c2 0 4-2 4-4 0-2-2-4-4-4z"/>
<glyph glyph-name="particles" unicode="&#57348;" d="M332 229c0 12 10 23 23 23 13 0 23-11 23-23 0-13-10-24-23-24-13 0-23 11-23 24z m-54-68c0 13 10 23 23 23 13 0 23-10 23-23 0-13-10-23-23-23-13 0-23 10-23 23z m-62-18c0 13 11 23 24 23 12 0 23-10 23-23 0-13-11-23-23-23-13 0-24 10-24 23z m-46 60c0 13 10 23 23 23 13 0 23-10 23-23 0-13-10-23-23-23-13 0-23 10-23 23z m81-138c-5 3-8 9-9 15-1 6 1 12 4 17 4 5 9 8 15 9 6 1 13 0 18-4 5-4 8-9 9-15 1-6-1-13-4-18-4-4-9-8-16-9-6-1-12 1-17 5z m-183 201c0 13 10 23 23 23 13 0 23-10 23-23 0-13-10-23-23-23-13 0-23 10-23 23z m111 46c0 13 10 24 23 24 13 0 23-11 23-24 0-12-10-23-23-23-13 0-23 11-23 23z m71-94c0 13 11 23 24 23 12 0 23-10 23-23 0-13-11-23-23-23-13 0-24 10-24 23z m163 52c0 13 10 23 23 23 13 0 23-10 23-23 0-13-10-23-23-23-12 0-23 10-23 23z m-111 38c0 13 11 23 23 23 13 0 24-10 24-23 0-13-11-23-24-23-12 0-23 10-23 23z m-170 90c0 13 11 24 24 24 12 0 23-11 23-24 0-12-11-23-23-23-13 0-24 11-24 23z m235-23c0 13 10 23 23 23 13 0 23-10 23-23 0-13-10-23-23-23-13 0-23 10-23 23z"/>
<glyph glyph-name="voxels" unicode="&#57349;" d="M434 379l-85 49c-4 2-10 2-14 0l-77-45-79 46c-4 2-10 2-14 0l-85-49c-4-3-7-7-7-12l0-98c0-5 3-10 7-13l78-45 0-89c0-5 3-10 7-12l85-49c2-2 5-2 7-2 2 0 5 0 7 2l85 49c4 2 7 7 7 12l0 88 78 45c5 3 7 7 7 12l0 99c0 5-2 9-7 12z m-21-88l-59 34 0 68 59-35z m-69-55l-73 42 0 80 59 35 0-68-29-17c-6-3-8-11-4-16 2-4 6-6 10-6 2 0 4 0 6 1l29 17 60-34z m-75-57l0 66 59-34 0-66z m-26 113l-59 34 0 68 59-34z m-142 68l59 34 0-68-31-18c-6-3-8-11-5-17 2-3 7-6 11-6 2 0 4 1 6 2l31 18 59-34-60-34-70 41z m156-270l-71 41 0 81 59 34 0-67-28-16c-6-3-8-11-5-17 3-3 7-6 11-6 2 0 4 1 6 2l28 16 59-34z"/>
<glyph glyph-name="lock" unicode="&#57350;" d="M389 233l0 62c0 68-55 124-123 124-69 0-124-56-124-124l0-62c-24-4-44-26-44-52l0-74c0-29 24-52 52-52l230 0c29 0 53 23 53 52l0 74c0 26-18 48-44 52z m-123 129c37 0 67-30 67-67l0-61-135 0 0 61c0 37 31 67 68 67z"/>
<glyph glyph-name="visible" unicode="&#57351;" d="M258 116c-55 0-106 17-147 51-31 25-47 51-47 52-4 7-4 16 1 23 2 4 66 98 195 96 133-3 192-93 195-97 4-6 4-15 0-22 0-1-15-27-46-53-29-23-79-50-151-50 0 0 0 0 0 0z m-148 113c7-7 17-18 30-29 34-27 73-40 118-40 0 0 0 0 0 0 47 0 88 13 122 40 13 10 23 21 29 29-7 7-16 16-30 26-34 25-74 38-119 38-81 2-130-42-150-64z m-27 1z m227-4c0-25-21-46-47-46-26 0-47 21-47 46 0 26 21 47 47 47 26 0 47-21 47-47z"/>
<glyph glyph-name="model" unicode="&#57352;" d="M494 395c-2 5-8 8-13 7l-90-16 45 72c3 5 2 11-1 15-4 4-10 5-15 3l-213-98c-15 5-72 27-111 43 0 0-1 0-1 0 0 0 0 0 0 0 0 0-1 0-1 1 0 0-1 0-1 0 0 0-1 0-1 0 0 0 0 0 0 0-1 0-1 0-2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1 0-1 0 0 0 0 0 0-1-1 0-1 0-2 0 0 0 0 0 0 0 0 0-1 0-1 0 0 0 0-1 0-1-1 0-2 0-3-1 0 0 0 0 0 0 0-1-1-1-1-1 0 0 0 0 0 0 0 0 0-1 0-1-1 0-1 0-1 0 0 0 0 0 0-1 0 0 0 0-1-1l-27-52-33-40c-3-4-3-10 0-15 2-3 6-5 10-5 1 0 2 0 4 1l50 17 40 2-26-51c-3-4-2-9 1-13 1-1 26-30 52-58 15-17 28-30 38-40 6-6 11-11 15-14l-16-61-46-18c-6-3-9-10-6-16 2-5 6-8 11-8 1 0 3 1 4 1l45 18 17-15c2-3 5-4 8-4 4 0 7 2 9 5 5 5 4 12-1 17l-17 15 16 61 76-90c2-2 6-4 9-4 1 0 2 0 3 0l85 23c5 2 8 6 9 11 0 5-2 9-7 12l-136 72 45 91 178 123c5 3 6 9 4 15z m-200-117l-122 55 41 21 181 83z m-148 73l-24 33c16-6 39-15 54-21z m-59-6l-9 13 15 29 27-38 2-2z m36-77l23 44c18-45 35-91 47-121-19 20-45 49-70 77z m194-194l-57 68 105-55z m-94 101c-5 14-42 104-30 77 0-2-21 49-28 66l121-59-48-95z m108 120l43 63 70 16z"/>
<glyph glyph-name="avatar-2" unicode="&#57353;" d="M256 88c-93 0-169 75-169 168 0 93 76 169 169 169 93 0 169-76 169-169 0-93-76-168-169-168z m0 316c-81 0-148-66-148-148 0-81 67-147 148-147 81 0 148 66 148 147 0 82-67 148-148 148z m97-90l-1 1c-3 3-7 4-10 4-1 0-61-9-86-9-1 0-1 0-2 0-25 0-87 10-87 10-5 0-10-2-13-6l-1-2c-2-3-2-7-1-10 1-4 3-6 6-8 12-5 49-20 60-22 2 0 5 0 6-7 1-8-3-46-7-65-5-17-13-40-13-41-2-6 1-13 7-15l8-3c3-1 6-1 9 1 3 1 5 4 6 7l21 65 20-67c1-3 3-6 6-7 2-1 4-1 5-1 2 0 3 0 5 0l7 3c5 2 8 8 7 14 0 0-6 24-11 44-3 12-4 30-5 45 0 9-1 16-2 22 0 1 0 4 5 5 0 0 1 0 2 0l55 22c4 2 6 5 7 9 1 4 0 8-3 11z m-68 37c0-16-13-29-29-29-16 0-29 13-29 29 0 16 13 29 29 29 16 0 29-13 29-29z"/>
@ -137,7 +135,6 @@
<glyph glyph-name="wallet" unicode="&#57383;" d="M400 400c-3 10-8 19-15 24-7 5-15 8-24 7l-227 0c-19 0-35-15-35-34l0-58c-19-2-34-18-34-37l0-95c0-19 15-35 34-37l0-53c0-19 16-34 35-34l228 0c20 0 30 16 38 31l0 1c1 2 21 53 21 139 0 83-19 140-21 146z m-309-193l0 95c0 6 5 11 11 11l86 0c6 0 11-5 11-11l0-95c0-6-5-11-11-11l-86 0c-6 0-11 5-11 11z m285-82c-8-16-11-16-14-16l-228 0c-5 0-9 4-9 8l0 53 63 0c21 0 37 17 37 37l0 95c0 20-16 37-37 37l-63 0 0 58c0 4 4 8 9 8l228 0c3 0 10 1 13-13l0 0 0-1c1 0 20-55 20-137 0-77-17-124-19-129z"/>
<glyph glyph-name="send" unicode="&#57384;" d="M391 376c4-4 4-10 2-16-7-21-14-42-22-63-21-63-43-125-65-188-1-4-4-7-6-10-8-6-17-4-22 5-15 28-30 56-44 85-1 1 0 3 0 4 18 21 35 43 53 64 5 6 10 12 15 18 4 6 5 10 1 14-4 4-8 3-14-1-18-15-36-30-54-44-9-8-19-16-28-24-1-1-3-1-4 0-29 14-57 29-85 44-6 3-8 8-8 14 1 7 5 11 11 13 36 13 72 25 107 37 49 17 97 34 145 51 7 2 13 2 18-3z"/>
<glyph glyph-name="password" unicode="&#57385;" d="M104 267l0 41 22 0 0-41 35 20 11-19-35-20 35-20-11-20-35 20 0-40-22 0 0 40-35-20-11 20 35 20-35 20 11 19z m136 0l0 41 23 0 0-41 35 20 11-19-35-20 35-20-11-20-35 20 0-40-23 0 0 40-35-20-11 20 35 20-35 20 11 19z m137 0l0 41 23 0 0-41 34 20 12-19-35-20 35-20-12-20-35 20 0-40-22 0 0 40-35-20-11 20 35 20-35 20 11 19z"/>
<glyph glyph-name="rez" unicode="&#57381;" d="M373 321c-2 5-6 8-11 8l-49 8 55 61c4 4 5 9 3 14-2 5-7 8-12 8 0 0 0 0 0 0l-114-1c-5-1-10-4-12-9l-54-136c-1-4-1-8 1-11 2-4 6-6 9-7l38-5-54-136c-2-6 0-13 6-16 2-1 4-2 7-2 3 0 7 2 10 5l175 206c3 4 3 9 2 13z"/>
<glyph glyph-name="keyboard-collapse" unicode="&#57387;" d="M373 249l-26 0 0 25 26 0z m-35 0l-27 0 0 25 27 0z m-36 0l-27 0 0 25 27 0z m-36 0l-26 0 0 25 26 0z m-35 0l-27 0 0 25 27 0z m-36 0l-27 0 0 25 27 0z m-36 0l-26 0 0 25 26 0z m224-1l18 0c7 0 13 6 13 13 0 7-6 13-13 13l-18 0m-262 0l-17 0c-7 0-13-6-13-13 0-7 6-13 13-13l17 0m252 39l-31 0 0 25 31 0z m-42 0l-31 0 0 25 31 0z m-41 0l-32 0 0 25 32 0z m-42 0l-32 0 0 25 32 0z m-42 0l-32 0 0 25 32 0z m-41 0l-32 0 0 25 32 0z m218-1l18 0c7 0 13 6 13 13 0 8-6 14-13 14l-18 0m-262 0l-17 0c-7 0-13-6-13-14 0-7 6-13 13-13l17 0m288-124l-315 0c-33 0-59 28-59 61l0 76c0 34 26 61 59 61l315 0c33 0 59-27 59-61l0-76c1-33-26-61-59-61z m-315 172c-18 0-33-16-33-34l0-77c0-19 15-34 33-34l315 0c18 0 33 15 33 34l0 77c0 19-15 34-33 34z m248-99l32 0 0-25-32 0z m-41 0l31 0 0-25-31 0z m-42 0l31 0 0-25-31 0z m-43 0l32 0 0-25-32 0z m-42 0l32 0 0-25-32 0z m-41 0l31 0 0-25-31 0z m250-26l18 0c7 0 13 6 13 14 0 7-6 13-13 13l-18 0m-262 0l-17 0c-7 0-13-6-13-13 0-8 6-13 13-13l17 0m81-82l50-50 53 54-107 0"/>
<glyph glyph-name="image" unicode="&#57386;" d="M257 428c52 0 104 0 156 0 24 0 37-13 37-37 1-90 1-179 0-269 0-25-13-38-39-38-103 0-207 0-311 0-26 0-39 13-39 40 0 88 0 176 0 263 0 28 13 41 41 41 51 0 103 0 155 0z m167-263c0 7 0 10 0 14 0 69 0 138 0 206 0 17 0 17-17 17-101 0-202 0-303 0-16 0-17-1-17-17 0-58 0-115 0-173 0-3 0-7 0-12 8 3 14 6 19 9 17 8 30 7 44-6 5-5 10-10 15-15 5-7 11-8 19-4 40 21 81 41 121 61 19 10 31 8 46-7 9-9 18-18 27-27 15-15 29-29 46-46z m-328-54c7 0 11-1 15-1 98 0 197 0 296 0 6 0 14 2 16 6 5 7 0 14-6 20-27 26-54 53-80 80-8 9-15 9-26 4-67-35-135-68-203-102-3-2-6-4-12-7z m-8 26c21 10 40 20 63 31-8 7-14 12-20 17-2 2-7 3-10 3-30-9-36-17-34-48 0 0 0-1 1-3z m134 169c1-25-21-46-46-46-25-1-46 20-47 46 0 25 21 46 47 47 25 0 46-21 46-47z m-46 22c-12 0-22-9-22-21 0-13 9-22 21-23 13 0 23 9 23 22 0 13-10 22-22 22z"/>
<glyph glyph-name="environments" unicode="&#57388;" d="M256 454c-110 0-199-89-199-199 0-110 89-199 199-199 0 0 0 0 0 0l2-1 0 1c109 1 197 90 197 199 0 110-89 199-199 199z m114-204l0 0 0 5c0 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 2l0 4 0 0c-1 22-4 43-10 63 21 5 36 11 46 15 15-26 24-56 24-87 0-30-8-59-23-85-8 4-23 11-47 16 6 21 9 42 10 64z m-19 102c-13 30-30 53-46 69 34-10 64-30 86-57-9-4-22-8-40-12z m-6-95c0-23-4-45-11-66-20 4-42 6-65 6l0 120c23 1 45 3 66 6 6-21 10-43 10-66z m-46-170c17 17 37 41 52 75 20-5 34-10 42-13-24-31-57-53-94-62z m-30 256l0 77c17-14 40-37 56-73-17-2-36-4-56-4z m0-248l0 77c20-1 39-3 56-5-16-35-40-59-56-72z m-83 252c17 35 40 59 56 73l0-77c-19 0-38 1-56 4z m-19-90c0 23 4 45 10 65 20-3 42-5 65-5l0-120c-23 0-45-1-65-4-6 20-10 42-10 64z m-47 106c22 28 52 48 86 58-15-16-33-39-46-70-18 4-31 8-40 12z m40-199c15-36 37-61 53-77-38 10-72 32-96 64 9 4 24 9 43 13z m82 8l0-77c-16 13-39 37-56 73 18 2 37 3 56 4z m-100 92l-1 0 0-4c0-1 0-1 0-2 0 0 0 0 0-1 0 0 0 0 0-1 0 0 0-1 0-1l0-5 1 0c0-21 4-42 10-62-23-5-39-10-49-15-14 25-21 53-21 82 0 30 8 60 23 86 10-4 25-10 47-14-6-20-10-41-10-63z"/>
@ -156,4 +153,10 @@
<glyph glyph-name="avatar-1" unicode="&#84;" d="M396 344l-2 2c-4 4-9 5-15 5-1 0-88-13-124-14-1 0-2 0-3 0-37 0-126 15-127 15-7 1-14-2-18-8l-2-4c-3-4-3-9-2-14 2-5 5-9 10-11 16-7 69-22 85-29 3-1 10-4 10-14 1-11-4-67-10-93-7-26-18-60-19-60-3-9 2-19 11-22l11-4c4-2 9-1 13 1 5 2 8 6 9 10l31 94 28-96c2-5 5-9 9-11 3-1 5-2 8-2 2 0 4 0 7 1l10 4c8 3 12 12 10 20 0 1-8 36-16 65-4 17-6 43-7 64-1 13-1 21-3 30 0 1 2 11 10 14 10 4 81 29 80 28 6 2 10 7 11 13 1 6-1 12-5 16z m-98 54c0-24-19-43-43-43-24 0-43 19-43 43 0 23 19 42 43 42 24 0 43-19 43-42z"/>
<glyph glyph-name="steam-square" unicode="&#57397;" d="M391 327c0 15-5 28-16 39-11 11-24 16-39 16-15 0-28-5-39-16-11-11-16-24-16-39 0-15 5-28 16-39 11-11 24-16 39-16 15 0 28 5 39 16 11 11 16 24 16 39z m-174-168c0-16-5-29-16-40-11-11-25-16-40-16-11 0-21 2-30 8-9 5-16 13-20 22 9-4 19-8 28-12 11-4 22-4 34 1 11 5 19 13 24 25 5 11 5 22 0 34-5 11-13 19-25 24l-23 9c4 1 8 2 12 2 15 0 29-6 40-17 11-11 16-24 16-40z m258 234l0-274c0-23-8-42-24-58-16-16-35-24-58-24l-274 0c-23 0-42 8-58 24-16 16-24 35-24 58l0 44 49-20c4-18 12-32 26-44 14-11 30-17 49-17 19 0 37 7 51 20 15 14 23 30 25 50l99 72c28 0 53 10 73 30 20 20 30 44 30 73 0 28-10 52-30 73-20 20-45 30-73 30-28 0-53-10-73-30-20-20-30-44-30-72l-64-92c-2 0-5 0-8 0-15 0-28-4-40-11l-84 34 0 134c0 23 8 42 24 58 16 16 35 24 58 24l274 0c23 0 42-8 58-24 16-16 24-35 24-58z m-70-66c0-19-7-36-20-49-14-14-30-20-49-20-19 0-36 6-49 20-13 13-20 30-20 49 0 19 7 35 20 48 13 14 30 21 49 21 19 0 35-7 49-20 13-14 20-30 20-49z"/>
<glyph glyph-name="oculus" unicode="&#57398;" d="M431 363c-16 13-34 22-54 26-11 3-23 4-34 5-9 0-18 0-26 0-41 0-82 0-123 0-9 0-18 0-26 0-12-1-24-2-35-5-20-4-38-13-54-26-32-26-51-65-51-106 0-41 19-80 51-106 16-13 34-22 54-27 12-2 23-3 35-4 8 0 17 0 26 0 41 0 82 0 123 0 8 0 17 0 26 0 11 0 23 2 34 4 20 5 38 14 54 27 32 26 51 65 51 106 0 41-19 80-51 106z m-60-143c-6-4-13-7-20-8-7-1-14-1-22-1-49 0-99 0-148 0-8 0-15 0-22 1-7 1-14 4-20 8-12 8-19 22-19 37 0 15 7 29 19 37 6 4 13 7 20 8 7 1 14 1 22 1 49 0 99 0 148 0 8 0 15 0 22-1 7-1 14-4 20-8 12-8 19-22 19-37 0-15-7-29-19-37z"/>
<glyph glyph-name="check-circled" unicode="&#57399;" d="M258 74c-48 0-94 19-128 53-34 34-53 80-53 130 0 47 19 92 53 126 35 34 80 53 128 53 0 0 0 0 0 0 49 0 95-19 129-54 34-34 52-80 52-129 0-47-19-92-53-126-35-34-80-53-128-53z m0 324c-38 0-74-15-101-42-27-27-42-62-42-100 0-39 15-75 42-102 27-27 63-42 101-42 38 0 74 15 101 42 27 27 42 62 42 99 0 39-15 75-42 102-27 28-62 43-101 43 0 0 0 0 0 0z m-25-164c-2 3-3 5-4 6-13 13-26 26-39 39-10 11-26 12-36 2-10-10-10-25 1-36 20-21 40-41 60-61 11-10 26-10 36 0 36 35 71 71 106 107 4 3 7 9 8 14 2 10-3 21-13 26-10 4-21 3-29-5-28-29-57-57-85-86-2-1-3-3-5-6z"/>
<glyph glyph-name="rez-01" unicode="&#57381;" d="M373 321c-2 5-6 8-11 8l-49 8 55 61c4 4 5 9 3 14-2 5-7 8-12 8 0 0 0 0 0 0l-114-1c-5-1-10-4-12-9l-54-136c-1-4-1-8 1-11 2-4 6-6 9-7l38-5-54-136c-2-6 0-13 6-16 2-1 4-2 7-2 3 0 7 2 10 5l175 206c3 4 3 9 2 13z"/>
<glyph glyph-name="locked" unicode="&#57350;" d="M367 271c-7 7-15 12-23 17l0 64c0 27-9 50-28 69-19 19-42 28-69 28l-6 0c-27 0-50-9-70-28-19-19-28-42-28-69l0-65c-8-4-16-10-23-16-19-19-29-42-29-69l0-43c0-27 10-50 29-68 20-19 43-28 70-28l108 0c27 0 50 9 69 28 19 19 28 42 28 68l0 43c0 27-9 50-28 69z m-187 81c0 17 6 31 18 42 12 12 26 18 43 18l6 0c17 0 31-6 43-18 12-11 18-25 18-42l0-54c-3 1-7 1-10 1l-108 0c-4 0-7 0-10-1z m179-193c0-17-6-30-18-42-12-12-26-18-43-18l-108 0c-17 0-31 6-43 18-12 12-18 25-18 42l0 43c0 17 6 30 18 42 12 12 26 18 43 18l108 0c17 0 31-6 43-18 12-12 18-25 18-42z m-116 55c-12 0-22-10-22-22l0-55c0-12 10-22 22-22 13 0 23 10 23 22l0 55c0 12-10 22-23 22z"/>
<glyph glyph-name="unlocked" unicode="&#57401;" d="M243 214c-12 0-22-10-22-22l0-55c0-12 10-22 22-22 13 0 23 10 23 22l0 55c0 12-10 22-23 22z m237 207c-19 19-42 28-69 28l-6 0c-27 0-50-9-69-28-20-19-29-42-29-69l0-53c-3 0-6 0-9 0l-108 0c-27 0-50-9-70-28-19-19-29-42-29-69l0-43c0-27 10-50 29-68 19-19 43-28 69-28l109 0c27 0 50 9 69 28 18 19 28 42 28 68l0 43c0 27-9 50-28 69-7 7-15 12-23 17l0 64c0 17 6 31 18 42 12 12 26 18 43 18l6 0c17 0 31-6 43-18 12-11 18-25 18-42l0-81 36 0 0 81c0 27-9 50-28 69z m-121-219l0-43c0-17-6-30-18-42-12-12-26-18-43-18l-108 0c-17 0-31 6-43 18-12 12-18 25-18 42l0 43c0 17 6 30 18 42 12 12 26 18 43 18l108 0c17 0 31-6 43-18 12-12 18-26 18-42z"/>
<glyph glyph-name="mic" unicode="&#105;" d="M299 347l0 47c0 21-17 38-40 38-21 0-39-17-39-38l0-47z m-79-41l0-46c0-21 18-38 39-38 22 0 40 17 40 38l0 46z m159 3l0-56c0-55-43-98-98-108l0-36 46 0c11 0 20-9 20-19 0-11-9-20-20-20l-131 0c-10 0-19 9-19 20 0 10 9 19 19 19l46 0 0 36c-55 9-98 53-98 108l0 58c0 11 10 19 20 18 10 0 17-9 17-20l0-56c0-40 37-72 80-72 44 0 80 32 80 72l0 55c0 11 8 19 18 19 10 0 20-7 20-18z"/>
<glyph glyph-name="92-lock-01" unicode="&#57400;" d="M389 233l0 62c0 68-55 124-123 124-69 0-124-56-124-124l0-62c-24-4-44-26-44-52l0-74c0-29 24-52 52-52l230 0c29 0 53 23 53 52l0 74c0 26-18 48-44 52z m-123 129c37 0 67-30 67-67l0-61-135 0 0 61c0 37 31 67 68 67z"/>
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View file

@ -12,7 +12,7 @@
<body>
<div class="container">
<h1>HiFi Glyphs</h1>
<p class="small">This font was created for use in <a href="http://highfidelity.io/">High Fidelity</a></p>
<p class="small">This font was created with<a href="http://fontastic.me/">Fontastic</a></p>
<h2>CSS mapping</h2>
<ul class="glyphs css-mapping">
<li>
@ -43,10 +43,6 @@
<div class="icon icon-headphones"></div>
<input type="text" readonly="readonly" value="headphones">
</li>
<li>
<div class="icon icon-mic"></div>
<input type="text" readonly="readonly" value="mic">
</li>
<li>
<div class="icon icon-upload"></div>
<input type="text" readonly="readonly" value="upload">
@ -375,10 +371,6 @@
<div class="icon icon-voxels"></div>
<input type="text" readonly="readonly" value="voxels">
</li>
<li>
<div class="icon icon-lock"></div>
<input type="text" readonly="readonly" value="lock">
</li>
<li>
<div class="icon icon-visible"></div>
<input type="text" readonly="readonly" value="visible">
@ -535,10 +527,6 @@
<div class="icon icon-password"></div>
<input type="text" readonly="readonly" value="password">
</li>
<li>
<div class="icon icon-rez"></div>
<input type="text" readonly="readonly" value="rez">
</li>
<li>
<div class="icon icon-keyboard-collapse"></div>
<input type="text" readonly="readonly" value="keyboard-collapse">
@ -611,6 +599,30 @@
<div class="icon icon-oculus"></div>
<input type="text" readonly="readonly" value="oculus">
</li>
<li>
<div class="icon icon-check-circled"></div>
<input type="text" readonly="readonly" value="check-circled">
</li>
<li>
<div class="icon icon-rez-01"></div>
<input type="text" readonly="readonly" value="rez-01">
</li>
<li>
<div class="icon icon-locked"></div>
<input type="text" readonly="readonly" value="locked">
</li>
<li>
<div class="icon icon-unlocked"></div>
<input type="text" readonly="readonly" value="unlocked">
</li>
<li>
<div class="icon icon-mic"></div>
<input type="text" readonly="readonly" value="mic">
</li>
<li>
<div class="icon icon-92-lock-01"></div>
<input type="text" readonly="readonly" value="92-lock-01">
</li>
</ul>
<h2>Character mapping</h2>
<ul class="glyphs character-mapping">
@ -642,10 +654,6 @@
<div data-icon="h" class="icon"></div>
<input type="text" readonly="readonly" value="h">
</li>
<li>
<div data-icon="i" class="icon"></div>
<input type="text" readonly="readonly" value="i">
</li>
<li>
<div data-icon="j" class="icon"></div>
<input type="text" readonly="readonly" value="j">
@ -974,10 +982,6 @@
<div data-icon="&#xe005;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe005;">
</li>
<li>
<div data-icon="&#xe006;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe006;">
</li>
<li>
<div data-icon="&#xe007;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe007;">
@ -1134,10 +1138,6 @@
<div data-icon="&#xe029;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe029;">
</li>
<li>
<div data-icon="&#xe025;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe025;">
</li>
<li>
<div data-icon="&#xe02b;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe02b;">
@ -1210,6 +1210,30 @@
<div data-icon="&#xe036;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe036;">
</li>
<li>
<div data-icon="&#xe037;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe037;">
</li>
<li>
<div data-icon="&#xe025;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe025;">
</li>
<li>
<div data-icon="&#xe006;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe006;">
</li>
<li>
<div data-icon="&#xe039;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe039;">
</li>
<li>
<div data-icon="i" class="icon"></div>
<input type="text" readonly="readonly" value="i">
</li>
<li>
<div data-icon="&#xe038;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe038;">
</li>
</ul>
</div>
<script>(function() {
@ -1229,4 +1253,4 @@
</script>
</body>
</html>
</html>

View file

@ -59,9 +59,6 @@
.icon-headphones:before {
content: "\68";
}
.icon-mic:before {
content: "\69";
}
.icon-upload:before {
content: "\6a";
}
@ -308,9 +305,6 @@
.icon-voxels:before {
content: "\e005";
}
.icon-lock:before {
content: "\e006";
}
.icon-visible:before {
content: "\e007";
}
@ -428,9 +422,6 @@
.icon-password:before {
content: "\e029";
}
.icon-rez:before {
content: "\e025";
}
.icon-keyboard-collapse:before {
content: "\e02b";
}
@ -485,3 +476,21 @@
.icon-oculus:before {
content: "\e036";
}
.icon-check-circled:before {
content: "\e037";
}
.icon-rez-01:before {
content: "\e025";
}
.icon-locked:before {
content: "\e006";
}
.icon-unlocked:before {
content: "\e039";
}
.icon-mic:before {
content: "\69";
}
.icon-92-lock-01:before {
content: "\e038";
}

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_4" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 66.7 66" style="enable-background:new 0 0 66.7 66;" xml:space="preserve">
<style type="text/css">
.st0{fill:#00B4EF;}
</style>
<path class="st0" d="M66.7,29.5l-7-5.9l1.3-9l-8.9-2l-3.1-8.5l-8.9,2.3l-6.7-6.1l-6.7,6.1l-8.9-2.3l-3.1,8.5l-8.9,2l1.3,9l-7,5.9
l5.3,7.4l-3.4,8.4l8.2,4.1l0.9,9l9.2-0.2l5.1,7.5l8-4.4l8,4.4l5.1-7.5l9.2,0.2l0.9-9l8.2-4.1l-3.4-8.4L66.7,29.5z M33.3,58.4
C19.3,58.4,8,47,8,33S19.3,7.6,33.3,7.6c14,0,25.4,11.4,25.4,25.4S47.3,58.4,33.3,58.4z M45.5,22.9H21.2c-3,0-5.5,2.3-5.8,5.2h-1.5
c-1.1,0-2.1,0.9-2.1,2c0,0,0,0,0,0v5.7c0,1.1,0.9,2.1,2,2.1c0,0,0,0,0,0h1.5c0.1,0.5,0.2,0.9,0.3,1.3l0.1,0.2
c0.6,1.5,1.8,2.7,3.3,3.3h0.1c0.3,0.1,0.6,0.2,1,0.2c0.1,0,0.2,0.1,0.3,0.1H27c1.1,0,2.2-0.4,3-1.2c0.9-0.9,2.1-1.4,3.3-1.3
c1.2,0,2.4,0.4,3.3,1.3c0.8,0.8,1.9,1.2,3,1.2h5.9c2.9,0,5.4-2.2,5.8-5.1h1.5c1.1,0,2.1-0.9,2.1-2c0,0,0,0,0,0v-5.7
c-0.1-1.1-1-1.9-2.1-1.9h-1.4C51,25.2,48.5,22.9,45.5,22.9z M15.3,36.7h-1.4c-0.4,0-0.7-0.3-0.7-0.7c0,0,0,0,0,0v-5.7
c0-0.4,0.3-0.7,0.7-0.7h1.4V36.7z M33.3,38.1c-1,0-1.8-0.8-1.8-1.8s0.8-1.8,1.8-1.8s1.8,0.8,1.8,1.8S34.3,38.1,33.3,38.1z
M37.2,34.8C37,34.9,36.9,35,36.7,35c-0.3,0-0.6-0.1-0.7-0.4c-0.6-0.9-1.6-1.5-2.7-1.5c-1.1,0-2.1,0.5-2.7,1.5
c-0.3,0.4-0.8,0.5-1.2,0.3c-0.4-0.3-0.5-0.8-0.3-1.2c0.9-1.4,2.5-2.2,4.1-2.2c1.7,0,3.2,0.8,4.1,2.3C37.7,34,37.6,34.6,37.2,34.8z
M39.8,33.1c-0.1,0.1-0.3,0.1-0.5,0.1c-0.3,0-0.6-0.1-0.7-0.4C37.4,31,35.5,30,33.3,30c-2.1,0-4.1,1.1-5.3,2.9
c-0.3,0.4-0.8,0.5-1.2,0.3c-0.4-0.3-0.5-0.8-0.3-1.2c1.5-2.3,4-3.7,6.8-3.7c2.7,0,5.3,1.4,6.8,3.7C40.3,32.3,40.2,32.8,39.8,33.1z
M42.5,31.5c-0.1,0.1-0.3,0.1-0.5,0.1c-0.3,0-0.6-0.1-0.7-0.4c-1.8-2.8-4.7-4.4-8-4.4c-3.2,0-6.2,1.6-8,4.4
c-0.3,0.4-0.8,0.5-1.2,0.3c-0.4-0.3-0.5-0.8-0.3-1.2c2.1-3.3,5.6-5.2,9.5-5.2c3.9,0,7.4,2,9.5,5.2C43,30.7,42.9,31.3,42.5,31.5z
M51.3,29.6h1.4c0.4,0,0.7,0.3,0.7,0.7v5.7c0,0.4-0.3,0.7-0.7,0.7h-1.4V29.6z M30.9,16.9l2.4-1.8l2.4,1.8l-0.9-2.8l2.4-1.8h-3
l-0.9-2.8l-0.9,2.8h-3l2.4,1.8L30.9,16.9z M35.8,49.1l-2.4,1.8l-2.4-1.8l0.9,2.8l-2.4,1.8h3l0.9,2.8l0.9-2.8h3l-2.4-1.8L35.8,49.1z
M42,17.4l1.3,2.7l0.6-2.9l3-0.4l-2.6-1.5l0.6-2.9l-2.2,2L40,13l1.3,2.7l-2.2,2L42,17.4z M24.7,48.6l-1.3-2.7l-0.6,2.9l-3,0.4
l2.6,1.5l-0.6,2.9l2.2-2l2.6,1.5l-1.3-2.7l2.2-2L24.7,48.6z M43.8,48.8l-0.6-2.9L42,48.6l-3-0.4l2.2,2L40,53l2.6-1.5l2.2,2l-0.6-2.9
l2.6-1.5L43.8,48.8z M22.8,17.2l0.6,2.9l1.3-2.7l3,0.4l-2.2-2l1.3-2.7l-2.6,1.5l-2.2-2l0.6,2.9l-2.6,1.5L22.8,17.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1 @@
<svg id="Art" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><defs><style>.cls-1{fill-rule:evenodd;}</style></defs><title>mic-ptt-a</title><path class="cls-1" d="M34.52,10.09l-1.85,1.85a13.19,13.19,0,0,0-9.82-4.46,13.35,13.35,0,0,0-9.58,4.2L11.42,9.84a15.66,15.66,0,0,1,23.1.25Zm-6.13,6.13,1.85-1.85a9.62,9.62,0,0,0-14.53-.25L17.55,16a7.34,7.34,0,0,1,5.3-2.44A6.85,6.85,0,0,1,28.39,16.22ZM23,16a3.58,3.58,0,0,1,1.51.3,3.68,3.68,0,0,1,1.26.88,3.88,3.88,0,0,1,.84,1.3A3.94,3.94,0,0,1,26.9,20v5.07a1.91,1.91,0,0,1,.48-.05A3.93,3.93,0,0,1,30,26.07a3.38,3.38,0,0,1,1.5-.32,3.27,3.27,0,0,1,2.77,1.36,2.75,2.75,0,0,1,.85-.1,3.35,3.35,0,0,1,1.33.25,3.18,3.18,0,0,1,1.12.76,3.23,3.23,0,0,1,.73,1.13,3.32,3.32,0,0,1,.24,1.32v3.31a12.27,12.27,0,0,1-.43,3.41l-1.36,5.65a2.67,2.67,0,0,1-1,1.55A2.89,2.89,0,0,1,34,45H23a4.47,4.47,0,0,1-1.76-.43,3.88,3.88,0,0,1-1.36-1.12L14.1,35.7a3.72,3.72,0,0,1-.8-2.35,3.64,3.64,0,0,1,.28-1.5,3.75,3.75,0,0,1,.84-1.27,3.9,3.9,0,0,1,2.77-1.18,4.5,4.5,0,0,1,2,.54V19.88a4.06,4.06,0,0,1,1.13-2.78,3.74,3.74,0,0,1,1.25-.83A3.85,3.85,0,0,1,23,16Zm0,2a1.89,1.89,0,0,0-.74.12,2,2,0,0,0-1.06,1,1.92,1.92,0,0,0-.15.74V35.21l-2.32-3.06a2,2,0,0,0-.7-.59,1.88,1.88,0,0,0-.9-.2,1.85,1.85,0,0,0-.74.15,2,2,0,0,0-.63.43,2,2,0,0,0-.4.63,1.9,1.9,0,0,0-.13.74,2,2,0,0,0,.38,1.17l5.86,7.79a1.79,1.79,0,0,0,.68.57A1.74,1.74,0,0,0,23,43H34a1.23,1.23,0,0,0,.59-.15.88.88,0,0,0,.24-.23.71.71,0,0,0,.13-.31l1.37-5.6a12,12,0,0,0,.37-3V30.43a1.7,1.7,0,0,0-.43-1.07,1.31,1.31,0,0,0-.47-.37,1.35,1.35,0,0,0-.59-.11,1.46,1.46,0,0,0-.55.11,1.23,1.23,0,0,0-.46.32,1.64,1.64,0,0,0-.43,1.07h-.48v-1a1.52,1.52,0,0,0-.12-.66,1.61,1.61,0,0,0-.37-.56,1.63,1.63,0,0,0-1.22-.54,2,2,0,0,0-1.23.54,1.77,1.77,0,0,0-.36.53,1.57,1.57,0,0,0-.11.64v1h-.49V29a2.22,2.22,0,0,0-.58-1.44,1.71,1.71,0,0,0-.62-.44A1.88,1.88,0,0,0,27.4,27a2,2,0,0,0-.74.13,1.85,1.85,0,0,0-.63.41,2,2,0,0,0-.53,1.36v1.5h-.57V20a2,2,0,0,0-.54-1.44,1.75,1.75,0,0,0-.63-.44A1.73,1.73,0,0,0,23,18Z"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Art" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M16.7,13.2c-0.3,0.3-0.7,0.6-1,0.9l1.8,1.9c1.4-1.5,3.3-2.4,5.3-2.4c2.2,0,4.2,0.9,5.5,2.7l1.8-1.8
C26.8,10.3,20.8,9.8,16.7,13.2z M32.7,11.9l1.9-1.9c-0.3-0.3-0.6-0.7-1-1c-6.3-5.9-16.2-5.6-22.1,0.7l1.9,1.8
c2.5-2.6,5.9-4.2,9.6-4.2C26.6,7.5,30.2,9.1,32.7,11.9z M38.3,29.1c-0.2-0.4-0.4-0.8-0.7-1.1c-0.3-0.3-0.7-0.6-1.1-0.8
C36,27.1,35.6,27,35.1,27c-0.3,0-0.6,0-0.8,0.1c-0.6-0.9-1.7-1.4-2.8-1.4c-0.5,0-1,0.1-1.5,0.3c-0.7-0.7-1.6-1-2.6-1.1
c-0.2,0-0.3,0-0.5,0.1V20c0-0.5-0.1-1-0.3-1.5c-0.2-0.5-0.5-0.9-0.8-1.3c-0.4-0.4-0.8-0.7-1.3-0.9C24,16.1,23.5,16,23,16
c-0.5,0-1,0.1-1.4,0.3c-0.5,0.2-0.9,0.5-1.3,0.8c-0.7,0.7-1.1,1.7-1.1,2.8v10.1c-0.6-0.3-1.3-0.5-2-0.5c-1,0-2,0.4-2.8,1.2
c-0.4,0.4-0.6,0.8-0.8,1.3c-0.2,0.5-0.3,1-0.3,1.5c0,0.9,0.3,1.7,0.8,2.4l5.8,7.8c0.4,0.5,0.8,0.9,1.4,1.1c0.6,0.3,1.2,0.4,1.8,0.4
h11c0.6,0,1.2-0.2,1.8-0.6c0.5-0.4,0.9-0.9,1-1.6l1.4-5.6c0.3-1.1,0.4-2.3,0.4-3.4v-3.3C38.6,30,38.5,29.6,38.3,29.1z M36.7,33.7
c0,1-0.1,2-0.4,3L35,42.3c0,0.1-0.1,0.2-0.1,0.3c-0.1,0.1-0.1,0.2-0.2,0.2C34.4,42.9,34.2,43,34,43H23c-0.3,0-0.6,0-0.8-0.2
c-0.3-0.1-0.5-0.3-0.7-0.6l-5.9-7.8c-0.2-0.3-0.4-0.7-0.4-1.2c0-0.3,0-0.5,0.1-0.7c0.1-0.2,0.2-0.4,0.4-0.6c0.2-0.2,0.4-0.3,0.6-0.4
c0.2-0.1,0.5-0.2,0.7-0.2c0.3,0,0.6,0.1,0.9,0.2c0.3,0.1,0.5,0.3,0.7,0.6l2.3,3.1V19.9c0-0.3,0.1-0.5,0.2-0.7c0.2-0.5,0.6-0.8,1.1-1
C22.5,18,22.7,18,23,18c0.3,0,0.5,0,0.8,0.1c0.2,0.1,0.5,0.2,0.6,0.4c0.4,0.4,0.6,0.9,0.5,1.4v10.4h0.6v-1.5c0-0.5,0.2-1,0.5-1.4
c0.2-0.2,0.4-0.3,0.6-0.4c0.2-0.1,0.5-0.1,0.7-0.1c0.3,0,0.5,0,0.8,0.1c0.2,0.1,0.4,0.2,0.6,0.4c0.4,0.4,0.6,0.9,0.6,1.4v1.3h0.5v-1
c0-0.2,0-0.4,0.1-0.6c0.1-0.2,0.2-0.4,0.4-0.5c0.3-0.3,0.8-0.5,1.2-0.5c0.5,0,0.9,0.2,1.2,0.5c0.2,0.2,0.3,0.3,0.4,0.6
c0.1,0.2,0.1,0.4,0.1,0.7v1h0.5c0-0.4,0.2-0.8,0.4-1.1c0.1-0.1,0.3-0.3,0.5-0.3c0.2-0.1,0.4-0.1,0.6-0.1c0.2,0,0.4,0,0.6,0.1
c0.2,0.1,0.3,0.2,0.5,0.4c0.3,0.3,0.4,0.7,0.4,1.1V33.7z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -113,6 +113,10 @@ Item {
visible: root.expanded
text: "Avatars Updated: " + root.updatedAvatarCount
}
StatText {
visible: root.expanded
text: "Heroes Count/Updated: " + root.heroAvatarCount + "/" + root.updatedHeroAvatarCount
}
StatText {
visible: root.expanded
text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount

View file

@ -115,6 +115,10 @@ Item {
visible: root.expanded
text: "Avatars Updated: " + root.updatedAvatarCount
}
StatText {
visible: root.expanded
text: "Heroes Count/Updated: " + root.heroAvatarCount + "/" + root.updatedHeroAvatarCount
}
StatText {
visible: root.expanded
text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount

View file

@ -27,6 +27,7 @@ Item {
property string labelGlyphOnText: "";
property int labelGlyphOnSize: 32;
property alias checked: originalSwitch.checked;
property string backgroundOnColor: "#252525";
signal onCheckedChanged;
signal clicked;
@ -40,10 +41,10 @@ Item {
onClicked: rootSwitch.clicked();
hoverEnabled: true
topPadding: 3;
topPadding: 1;
leftPadding: 3;
rightPadding: 3;
bottomPadding: 3;
bottomPadding: 1;
onHoveredChanged: {
if (hovered) {
@ -54,7 +55,7 @@ Item {
}
background: Rectangle {
color: "#252525";
color: checked ? backgroundOnColor : "#252525";
implicitWidth: rootSwitch.switchWidth;
implicitHeight: rootSwitch.height;
radius: rootSwitch.switchRadius;

View file

@ -30,6 +30,7 @@ Item {
property string imageUrl: "";
property var goFunction: null;
property string storyId: "";
property bool standaloneOptimized: false;
property bool drillDownToPlace: false;
property bool showPlace: isConcurrency;
@ -40,6 +41,7 @@ Item {
property bool isAnnouncement: action === 'announcement';
property bool isStacked: !isConcurrency && drillDownToPlace;
property int textPadding: 10;
property int smallMargin: 4;
property int messageHeight: 40;
@ -267,9 +269,33 @@ Item {
hoverEnabled: false
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
goFunction("hifi://" + hifiUrl);
goFunction("hifi://" + hifiUrl, standaloneOptimized);
}
}
Image {
id: standaloneOptomizedBadge
anchors {
right: actionIcon.left
top: actionIcon.top
topMargin: 2
rightMargin: 3
}
height: root.standaloneOptimized ? 25 : 0
width: 25
visible: root.standaloneOptimized && isConcurrency
fillMode: Image.PreserveAspectFit
source: "../../icons/standalone-optimized.svg"
}
ColorOverlay {
anchors.fill: standaloneOptomizedBadge
source: standaloneOptomizedBadge
color: hifi.colors.blueHighlight
visible: root.standaloneOptimized && isConcurrency
}
StateImage {
id: actionIcon;
visible: !isAnnouncement;
@ -281,7 +307,8 @@ Item {
right: parent.right;
margins: smallMargin;
}
}
}
function go() {
Tablet.playSound(TabletEnums.ButtonClick);
goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId));

View file

@ -82,6 +82,7 @@ Column {
action: data.action || "",
thumbnail_url: resolveUrl(thumbnail_url),
image_url: resolveUrl(data.details && data.details.image_url),
standalone_optimized: data.standalone_optimized,
metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
@ -127,6 +128,7 @@ Column {
hifiUrl: model.place_name + model.path;
thumbnail: model.thumbnail_url;
imageUrl: model.image_url;
standaloneOptimized: model.standalone_optimized;
action: model.action;
timestamp: model.created_at;
onlineUsers: model.online_users;
@ -187,4 +189,14 @@ Column {
}
}
}
function isStandalone(address) {
var lowerAddress = address.toLowerCase();
for (var i=0; i < suggestions.count; i++) {
if (suggestions.get(i).place_name.toLowerCase() === lowerAddress) {
return suggestions.get(i).standalone_optimized;
}
}
return false;
}
}

View file

@ -376,7 +376,7 @@ Item {
}
FiraSansRegular {
id: nameCardConnectionInfoText
visible: selected && !isMyCard && pal.activeTab == "connectionsTab" && !isMyCard
visible: selected && !isMyCard && pal.activeTab == "connectionsTab"
width: parent.width
height: displayNameTextPixelSize
size: displayNameTextPixelSize - 4
@ -412,7 +412,7 @@ Item {
}
FiraSansRegular {
id: nameCardRemoveConnectionText
visible: selected && !isMyCard && pal.activeTab == "connectionsTab" && !isMyCard
visible: selected && !isMyCard && pal.activeTab == "connectionsTab"
width: parent.width
height: displayNameTextPixelSize
size: displayNameTextPixelSize - 4
@ -425,7 +425,7 @@ Item {
}
HifiControls.Button {
id: visitConnectionButton
visible: selected && !isMyCard && pal.activeTab == "connectionsTab" && !isMyCard
visible: selected && !isMyCard && pal.activeTab == "connectionsTab"
text: "Visit"
enabled: thisNameCard.placeName !== ""
anchors.verticalCenter: nameCardRemoveConnectionImage.verticalCenter
@ -450,7 +450,7 @@ Item {
// Style
radius: 4
color: "#c5c5c5"
visible: (isMyCard || (selected && pal.activeTab == "nearbyTab")) && isPresent
visible: (!isMyCard && (selected && pal.activeTab == "nearbyTab")) && isPresent
// Rectangle for the zero-gain point on the VU meter
Rectangle {
id: vuMeterZeroGain
@ -481,7 +481,7 @@ Item {
id: vuMeterBase
// Anchors
anchors.fill: parent
visible: isMyCard || selected
visible: !isMyCard && selected
// Style
color: parent.color
radius: parent.radius
@ -489,7 +489,7 @@ Item {
// Rectangle for the VU meter audio level
Rectangle {
id: vuMeterLevel
visible: isMyCard || selected
visible: !isMyCard && selected
// Size
width: (thisNameCard.audioLevel) * parent.width
// Style
@ -525,7 +525,7 @@ Item {
anchors.verticalCenter: nameCardVUMeter.verticalCenter;
anchors.left: nameCardVUMeter.left;
// Properties
visible: (isMyCard || (selected && pal.activeTab == "nearbyTab")) && isPresent;
visible: (!isMyCard && (selected && pal.activeTab == "nearbyTab")) && isPresent;
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
@ -572,19 +572,7 @@ Item {
implicitHeight: 16
}
}
RalewayRegular {
// The slider for my card is special, it controls the master gain
id: gainSliderText;
visible: isMyCard;
text: "master volume";
size: hifi.fontSizes.tabularData;
anchors.left: parent.right;
anchors.leftMargin: 8;
color: hifi.colors.baseGrayHighlight;
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignTop;
}
}
}
function updateGainFromQML(avatarUuid, sliderValue, isReleased) {
if (Users.getAvatarGain(avatarUuid) != sliderValue) {

View file

@ -11,12 +11,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import stylesUit 1.0
import controlsUit 1.0 as HifiControls
import controlsUit 1.0 as HifiControlsUit
import "../../windows"
import "./" as AudioControls
@ -26,7 +26,11 @@ Rectangle {
HifiConstants { id: hifi; }
property var eventBridge;
// leave as blank, this is user's volume for the avatar mixer
property var myAvatarUuid: ""
property string title: "Audio Settings"
property int switchHeight: 16
property int switchWidth: 40
signal sendToScript(var message);
color: hifi.colors.baseGray;
@ -38,7 +42,7 @@ Rectangle {
property bool isVR: AudioScriptingInterface.context === "VR"
property real rightMostInputLevelPos: 0
property real rightMostInputLevelPos: 450
//placeholder for control sizes and paddings
//recalculates dynamically in case of UI size is changed
QtObject {
@ -80,16 +84,16 @@ Rectangle {
});
}
function disablePeakValues() {
root.showPeaks = false;
AudioScriptingInterface.devices.input.peakValuesEnabled = false;
function updateMyAvatarGainFromQML(sliderValue, isReleased) {
if (Users.getAvatarGain(myAvatarUuid) != sliderValue) {
Users.setAvatarGain(myAvatarUuid, sliderValue);
}
}
Component.onCompleted: enablePeakValues();
Component.onDestruction: disablePeakValues();
onVisibleChanged: visible ? enablePeakValues() : disablePeakValues();
Column {
id: column
spacing: 12;
anchors.top: bar.bottom
anchors.bottom: parent.bottom
@ -98,70 +102,134 @@ Rectangle {
Separator { }
RalewayRegular {
x: margins.paddings + muteMic.boxSize + muteMic.spacing;
size: 16;
color: "white";
text: qsTr("Input Device Settings")
}
ColumnLayout {
x: margins.paddings;
spacing: 16;
RowLayout {
x: 2 * margins.paddings;
width: parent.width;
// mute is in its own row
RowLayout {
spacing: (margins.sizeCheckBox - 10.5) * 3;
AudioControls.CheckBox {
id: muteMic
text: qsTr("Mute microphone");
spacing: margins.sizeCheckBox - boxSize
isRedCheck: true;
ColumnLayout {
id: columnOne
spacing: 24;
x: margins.paddings
HifiControlsUit.Switch {
id: muteMic;
height: root.switchHeight;
switchWidth: root.switchWidth;
labelTextOn: "Mute microphone";
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.muted;
onClicked: {
if (AudioScriptingInterface.pushToTalk && !checked) {
// disable push to talk if unmuting
AudioScriptingInterface.pushToTalk = false;
}
AudioScriptingInterface.muted = checked;
checked = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding
}
}
AudioControls.CheckBox {
id: stereoMic
spacing: muteMic.spacing;
text: qsTr("Enable stereo input");
checked: AudioScriptingInterface.isStereoInput;
onClicked: {
AudioScriptingInterface.isStereoInput = checked;
checked = Qt.binding(function() { return AudioScriptingInterface.isStereoInput; }); // restore binding
}
}
}
RowLayout {
spacing: muteMic.spacing*2; //make it visually distinguish
AudioControls.CheckBox {
spacing: muteMic.spacing
text: qsTr("Enable noise reduction");
HifiControlsUit.Switch {
height: root.switchHeight;
switchWidth: root.switchWidth;
labelTextOn: "Noise Reduction";
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.noiseReduction;
onClicked: {
onCheckedChanged: {
AudioScriptingInterface.noiseReduction = checked;
checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding
}
}
AudioControls.CheckBox {
spacing: muteMic.spacing
text: qsTr("Show audio level meter");
checked: AvatarInputs.showAudioTools;
HifiControlsUit.Switch {
id: pttSwitch
height: root.switchHeight;
switchWidth: root.switchWidth;
labelTextOn: qsTr("Push To Talk (T)");
backgroundOnColor: "#E3E3E3";
checked: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD;
onCheckedChanged: {
if (bar.currentIndex === 0) {
AudioScriptingInterface.pushToTalkDesktop = checked;
} else {
AudioScriptingInterface.pushToTalkHMD = checked;
}
checked = Qt.binding(function() {
if (bar.currentIndex === 0) {
return AudioScriptingInterface.pushToTalkDesktop;
} else {
return AudioScriptingInterface.pushToTalkHMD;
}
}); // restore binding
}
}
}
ColumnLayout {
spacing: 24;
HifiControlsUit.Switch {
id: warnMutedSwitch
height: root.switchHeight;
switchWidth: root.switchWidth;
labelTextOn: qsTr("Warn when muted");
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.warnWhenMuted;
onClicked: {
AudioScriptingInterface.warnWhenMuted = checked;
checked = Qt.binding(function() { return AudioScriptingInterface.warnWhenMuted; }); // restore binding
}
}
HifiControlsUit.Switch {
id: audioLevelSwitch
height: root.switchHeight;
switchWidth: root.switchWidth;
labelTextOn: qsTr("Audio Level Meter");
backgroundOnColor: "#E3E3E3";
checked: AvatarInputs.showAudioTools;
onCheckedChanged: {
AvatarInputs.showAudioTools = checked;
checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding
}
onXChanged: rightMostInputLevelPos = x + width
}
HifiControlsUit.Switch {
id: stereoInput;
height: root.switchHeight;
switchWidth: root.switchWidth;
labelTextOn: qsTr("Stereo input");
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.isStereoInput;
onCheckedChanged: {
AudioScriptingInterface.isStereoInput = checked;
checked = Qt.binding(function() { return AudioScriptingInterface.isStereoInput; }); // restore binding
}
}
}
}
Separator {}
Item {
anchors.left: parent.left
width: rightMostInputLevelPos;
height: pttText.height;
RalewayRegular {
id: pttText
x: margins.paddings;
color: hifi.colors.white;
width: rightMostInputLevelPos;
height: paintedHeight;
wrapMode: Text.WordWrap;
font.italic: true
size: 16;
text: (bar.currentIndex === 0) ? qsTr("Press and hold the button \"T\" to talk.") :
qsTr("Press and hold grip triggers on both of your controllers to talk.");
}
}
Separator { }
Item {
x: margins.paddings;
@ -171,7 +239,7 @@ Rectangle {
HiFiGlyphs {
width: margins.sizeCheckBox
text: hifi.glyphs.mic;
color: hifi.colors.primaryHighlight;
color: hifi.colors.white;
anchors.left: parent.left
anchors.leftMargin: -size/4 //the glyph has empty space at left about 25%
anchors.verticalCenter: parent.verticalCenter;
@ -183,8 +251,8 @@ Rectangle {
anchors.left: parent.left
anchors.leftMargin: margins.sizeCheckBox
size: 16;
color: hifi.colors.lightGrayText;
text: qsTr("CHOOSE INPUT DEVICE");
color: hifi.colors.white;
text: qsTr("Choose input device");
}
}
@ -210,19 +278,19 @@ Rectangle {
width: parent.width - inputLevel.width
clip: true
checkable: !checked
checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD;
checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD;
boxSize: margins.sizeCheckBox / 2
isRound: true
text: devicename
onPressed: {
if (!checked) {
stereoMic.checked = false;
stereoInput.checked = false;
AudioScriptingInterface.setStereoInput(false); // the next selected audio device might not support stereo
AudioScriptingInterface.setInputDevice(info, bar.currentIndex === 1);
}
}
}
InputPeak {
AudioControls.InputPeak {
id: inputLevel
anchors.right: parent.right
peak: model.peak;
@ -233,6 +301,13 @@ Rectangle {
}
}
}
AudioControls.LoopbackAudio {
x: margins.paddings
visible: (bar.currentIndex === 1 && isVR) ||
(bar.currentIndex === 0 && !isVR);
anchors { left: parent.left; leftMargin: margins.paddings }
}
Separator {}
@ -247,7 +322,7 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter;
width: margins.sizeCheckBox
text: hifi.glyphs.unmuted;
color: hifi.colors.primaryHighlight;
color: hifi.colors.white;
size: 36;
}
@ -257,8 +332,8 @@ Rectangle {
anchors.leftMargin: margins.sizeCheckBox
anchors.verticalCenter: parent.verticalCenter;
size: 16;
color: hifi.colors.lightGrayText;
text: qsTr("CHOOSE OUTPUT DEVICE");
color: hifi.colors.white;
text: qsTr("Choose output device");
}
}
@ -293,7 +368,68 @@ Rectangle {
}
}
}
PlaySampleSound {
Item {
id: gainContainer
x: margins.paddings;
width: parent.width - margins.paddings*2
height: gainSliderTextMetrics.height
HifiControlsUit.Slider {
id: gainSlider
anchors.right: parent.right
height: parent.height
width: 200
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
value: Users.getAvatarGain(myAvatarUuid)
onValueChanged: {
updateMyAvatarGainFromQML(value, false);
}
onPressedChanged: {
if (!pressed) {
updateMyAvatarGainFromQML(value, false);
}
}
MouseArea {
anchors.fill: parent
onWheel: {
// Do nothing.
}
onDoubleClicked: {
gainSlider.value = 0.0
}
onPressed: {
// Pass through to Slider
mouse.accepted = false
}
onReleased: {
// the above mouse.accepted seems to make this
// never get called, nonetheless...
mouse.accepted = false
}
}
}
TextMetrics {
id: gainSliderTextMetrics
text: gainSliderText.text
font: gainSliderText.font
}
RalewayRegular {
// The slider for my card is special, it controls the master gain
id: gainSliderText;
text: "Avatar volume";
size: 16;
anchors.left: parent.left;
color: hifi.colors.white;
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignTop;
}
}
AudioControls.PlaySampleSound {
x: margins.paddings
visible: (bar.currentIndex === 1 && isVR) ||

View file

@ -0,0 +1,67 @@
//
// LoopbackAudio.qml
// qml/hifi/audio
//
// Created by Seth Alves on 2019-2-18
// Copyright 2019 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 2.2
import QtQuick.Layouts 1.3
import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit
RowLayout {
property bool audioLoopedBack: AudioScriptingInterface.getServerEcho();
function startAudioLoopback() {
if (!audioLoopedBack) {
audioLoopedBack = true;
AudioScriptingInterface.setServerEcho(true);
}
}
function stopAudioLoopback() {
if (audioLoopedBack) {
audioLoopedBack = false;
AudioScriptingInterface.setServerEcho(false);
}
}
HifiConstants { id: hifi; }
Timer {
id: loopbackTimer
interval: 8000;
running: false;
repeat: false;
onTriggered: {
stopAudioLoopback();
}
}
HifiControlsUit.Button {
text: audioLoopedBack ? qsTr("STOP TESTING YOUR VOICE") : qsTr("TEST YOUR VOICE");
color: audioLoopedBack ? hifi.buttons.red : hifi.buttons.blue;
onClicked: {
if (audioLoopedBack) {
loopbackTimer.stop();
stopAudioLoopback();
} else {
loopbackTimer.restart();
startAudioLoopback();
}
}
}
RalewayRegular {
Layout.leftMargin: 2;
size: 14;
color: "white";
font.italic: true
text: audioLoopedBack ? qsTr("Speak in your input") : "";
}
}

View file

@ -11,18 +11,21 @@
import QtQuick 2.5
import QtGraphicalEffects 1.0
import stylesUit 1.0
import TabletScriptingInterface 1.0
Rectangle {
HifiConstants { id: hifi; }
readonly property var level: AudioScriptingInterface.inputLevel;
property bool gated: false;
Component.onCompleted: {
AudioScriptingInterface.noiseGateOpened.connect(function() { gated = false; });
AudioScriptingInterface.noiseGateClosed.connect(function() { gated = true; });
}
property bool standalone: false;
property var dragTarget: null;
@ -64,6 +67,9 @@ Rectangle {
hoverEnabled: true;
scrollGestureEnabled: false;
onClicked: {
if (AudioScriptingInterface.pushToTalk) {
return;
}
AudioScriptingInterface.muted = !AudioScriptingInterface.muted;
Tablet.playSound(TabletEnums.ButtonClick);
}
@ -106,9 +112,10 @@ Rectangle {
Image {
readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg";
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
readonly property string pushToTalkIcon: "../../../icons/tablet-icons/mic-ptt-i.svg";
id: image;
source: AudioScriptingInterface.muted ? mutedIcon : unmutedIcon;
source: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? pushToTalkIcon : AudioScriptingInterface.muted ? mutedIcon : unmutedIcon;
width: 30;
height: 30;
@ -133,7 +140,7 @@ Rectangle {
readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted;
visible: AudioScriptingInterface.muted;
visible: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) || AudioScriptingInterface.muted;
anchors {
left: parent.left;
@ -152,7 +159,7 @@ Rectangle {
color: parent.color;
text: AudioScriptingInterface.muted ? "MUTED" : "MUTE";
text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? (HMD.active ? "MUTED PTT" : "MUTED PTT-(T)") : (AudioScriptingInterface.muted ? "MUTED" : "MUTE");
font.pointSize: 12;
}
@ -162,7 +169,7 @@ Rectangle {
verticalCenter: parent.verticalCenter;
}
width: 50;
width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? (HMD.active ? 27 : 25) : 50;
height: 4;
color: parent.color;
}
@ -173,7 +180,7 @@ Rectangle {
verticalCenter: parent.verticalCenter;
}
width: 50;
width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? (HMD.active ? 27 : 25) : 50;
height: 4;
color: parent.color;
}
@ -228,12 +235,12 @@ Rectangle {
}
}
}
Rectangle {
id: gatedIndicator;
visible: gated && !AudioScriptingInterface.clipping
radius: 4;
radius: 4;
width: 2 * radius;
height: 2 * radius;
color: "#0080FF";
@ -242,12 +249,12 @@ Rectangle {
verticalCenter: parent.verticalCenter;
}
}
Rectangle {
id: clippingIndicator;
visible: AudioScriptingInterface.clipping
radius: 4;
radius: 4;
width: 2 * radius;
height: 2 * radius;
color: colors.red;

View file

@ -14,7 +14,7 @@ import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import stylesUit 1.0
import controlsUit 1.0 as HifiControls
import controlsUit 1.0 as HifiControlsUit
RowLayout {
property var sound: null;
@ -55,32 +55,9 @@ RowLayout {
HifiConstants { id: hifi; }
Button {
id: control
background: Rectangle {
implicitWidth: 20;
implicitHeight: 20;
radius: hifi.buttons.radius;
gradient: Gradient {
GradientStop {
position: 0.2;
color: isPlaying ? hifi.buttons.colorStart[hifi.buttons.blue] : hifi.buttons.colorStart[hifi.buttons.black];
}
GradientStop {
position: 1.0;
color: isPlaying ? hifi.buttons.colorFinish[hifi.buttons.blue] : hifi.buttons.colorFinish[hifi.buttons.black];
}
}
}
contentItem: HiFiGlyphs {
// absolutely position due to asymmetry in glyph
// x: isPlaying ? 0 : 1;
// y: 1;
size: 14;
color: (control.pressed || control.hovered) ? (isPlaying ? "black" : hifi.colors.primaryHighlight) : "white";
text: isPlaying ? hifi.glyphs.stop_square : hifi.glyphs.playback_play;
}
HifiControlsUit.Button {
text: isPlaying ? qsTr("STOP TESTING YOUR SOUND") : qsTr("TEST YOUR SOUND");
color: isPlaying ? hifi.buttons.red : hifi.buttons.blue;
onClicked: isPlaying ? stopSound() : playSound();
}
@ -88,7 +65,7 @@ RowLayout {
Layout.leftMargin: 2;
size: 14;
color: "white";
text: isPlaying ? qsTr("Stop sample sound") : qsTr("Play sample sound");
font.italic: true
text: isPlaying ? qsTr("Listen to your output") : "";
}
}

View file

@ -30,6 +30,8 @@ Rectangle {
property string dateAcquired: "--";
property string itemCost: "--";
property string marketplace_item_id: "";
property bool standaloneOptimized: false;
property bool standaloneIncompatible: false;
property string certTitleTextColor: hifi.colors.darkGray;
property string certTextColor: hifi.colors.white;
property string infoTextColor: hifi.colors.blueAccent;
@ -71,6 +73,8 @@ Rectangle {
} else {
root.marketplace_item_id = result.data.marketplace_item_id;
root.isMyCert = result.isMyCert ? result.isMyCert : false;
root.standaloneOptimized = result.data.standalone_optimized;
root.standaloneIncompatible = result.data.standalone_incompatible;
if (root.certInfoReplaceMode > 3) {
root.itemName = result.data.marketplace_item_name;
@ -421,6 +425,24 @@ Rectangle {
anchors.rightMargin: 24;
height: root.useGoldCert ? 220 : 372;
HiFiGlyphs {
id: standaloneOptomizedBadge
anchors {
right: parent.right
top: ownedByHeader.top
rightMargin: 15
topMargin: 28
}
visible: root.standaloneOptimized
text: hifi.glyphs.hmd
size: 34
horizontalAlignment: Text.AlignHCenter
color: hifi.colors.blueHighlight
}
RalewayRegular {
id: errorText;
visible: !root.useGoldCert;
@ -467,6 +489,7 @@ Rectangle {
color: root.infoTextColor;
elide: Text.ElideRight;
}
AnonymousProRegular {
id: isMyCertText;
visible: root.isMyCert && ownedBy.text !== "--" && ownedBy.text !== "";
@ -485,14 +508,46 @@ Rectangle {
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: standaloneHeader;
text: root.standaloneOptimized ? "STAND-ALONE OPTIMIZED" : "STAND-ALONE INCOMPATIBLE";
// Text size
size: 16;
// Anchors
anchors.top: ownedBy.bottom;
anchors.topMargin: 15;
anchors.left: parent.left;
anchors.right: parent.right;
visible: root.standaloneOptimized || root.standaloneIncompatible;
height: visible ? paintedHeight : 0;
// Style
color: hifi.colors.darkGray;
}
RalewayRegular {
id: standaloneText;
text: root.standaloneOptimized ? "This item is stand-alone optimized" : "This item is incompatible with stand-alone devices";
// Text size
size: 18;
// Anchors
anchors.top: standaloneHeader.bottom;
anchors.topMargin: 8;
anchors.left: standaloneHeader.left;
visible: root.standaloneOptimized || root.standaloneIncompatible;
height: visible ? paintedHeight : 0;
// Style
color: root.infoTextColor;
elide: Text.ElideRight;
}
RalewayRegular {
id: dateAcquiredHeader;
text: "ACQUISITION DATE";
// Text size
size: 16;
// Anchors
anchors.top: ownedBy.bottom;
anchors.topMargin: 28;
anchors.top: standaloneText.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.right: parent.horizontalCenter;
anchors.rightMargin: 8;
@ -521,8 +576,8 @@ Rectangle {
// Text size
size: 16;
// Anchors
anchors.top: ownedBy.bottom;
anchors.topMargin: 28;
anchors.top: standaloneText.bottom;
anchors.topMargin: 20;
anchors.left: parent.horizontalCenter;
anchors.right: parent.right;
height: paintedHeight;

View file

@ -91,6 +91,14 @@ Rectangle {
id: -1,
name: "Everything"
});
categoriesModel.append({
id: -1,
name: "Stand-alone Optimized"
});
categoriesModel.append({
id: -1,
name: "Stand-alone Compatible"
});
result.data.items.forEach(function(category) {
categoriesModel.append({
id: category.id,
@ -127,6 +135,8 @@ Rectangle {
marketplaceItem.availability = result.data.availability;
marketplaceItem.updated_item_id = result.data.updated_item_id || "";
marketplaceItem.created_at = result.data.created_at;
marketplaceItem.standaloneOptimized = result.data.standalone_optimized;
marketplaceItem.standaloneVisible = result.data.standalone_optimized || result.data.standalone_incompatible;
marketplaceItemScrollView.contentHeight = marketplaceItemContent.height;
itemsList.visible = false;
marketplaceItemView.visible = true;
@ -191,16 +201,16 @@ Rectangle {
visible: true
Image {
id: marketplaceHeaderImage;
source: "../common/images/marketplaceHeaderImage.png";
anchors.top: parent.top;
anchors.topMargin: 2;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 0;
anchors.left: parent.left;
anchors.leftMargin: 8;
width: 140;
fillMode: Image.PreserveAspectFit;
id: marketplaceHeaderImage
source: "../common/images/marketplaceHeaderImage.png"
anchors.top: parent.top
anchors.topMargin: 2
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.left: parent.left
anchors.leftMargin: 8
width: 140
fillMode: Image.PreserveAspectFit
MouseArea {
anchors.fill: parent;
@ -372,6 +382,7 @@ Rectangle {
id: categoriesDropdown
anchors.fill: parent;
anchors.topMargin: 2
visible: false
z: 10
@ -410,6 +421,7 @@ Rectangle {
model: categoriesModel
delegate: ItemDelegate {
id: categoriesItemDelegate
height: 34
width: parent.width
@ -421,6 +433,8 @@ Rectangle {
color: hifi.colors.white
visible: true
border.color: hifi.colors.blueHighlight
border.width: 0
RalewayRegular {
id: categoriesItemText
@ -429,7 +443,7 @@ Rectangle {
anchors.fill:parent
text: model.name
color: ListView.isCurrentItem ? hifi.colors.lightBlueHighlight : hifi.colors.baseGray
color: categoriesItemDelegate.ListView.isCurrentItem ? hifi.colors.blueHighlight : hifi.colors.baseGray
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
size: 14
@ -439,16 +453,22 @@ Rectangle {
MouseArea {
anchors.fill: parent
z: 10
hoverEnabled: true
propagateComposedEvents: false
onEntered: {
categoriesItem.color = ListView.isCurrentItem ? hifi.colors.white : hifi.colors.lightBlueHighlight;
onPositionChanged: {
// Must use onPositionChanged and not onEntered
// due to a QML bug where a mouseenter event was
// being fired on open of the categories list even
// though the mouse was outside the borders
categoriesItem.border.width = 2;
}
onExited: {
categoriesItem.border.width = 0;
}
onExited: {
categoriesItem.color = ListView.isCurrentItem ? hifi.colors.lightBlueHighlight : hifi.colors.white;
onCanceled: {
categoriesItem.border.width = 0;
}
onClicked: {
@ -546,7 +566,8 @@ Rectangle {
price: model.cost
availability: model.availability
isLoggedIn: root.isLoggedIn;
standaloneOptimized: model.standalone_optimized
onShowItem: {
MarketplaceScriptingInterface.getMarketplaceItem(item_id);
}

View file

@ -15,6 +15,7 @@ import Hifi 1.0 as Hifi
import QtQuick 2.9
import QtQuick.Controls 2.2
import stylesUit 1.0
import QtGraphicalEffects 1.0
import controlsUit 1.0 as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
@ -40,9 +41,11 @@ Rectangle {
property string license: ""
property string posted: ""
property string created_at: ""
property bool isLoggedIn: false;
property int edition: -1;
property bool supports3DHTML: false;
property bool isLoggedIn: false
property int edition: -1
property bool supports3DHTML: false
property bool standaloneVisible: false
property bool standaloneOptimized: false
onCategoriesChanged: {
categoriesListModel.clear();
@ -240,10 +243,43 @@ Rectangle {
right: parent.right;
top: itemImage.bottom;
}
height: categoriesList.y - buyButton.y + categoriesList.height
height: categoriesList.y - badges.y + categoriesList.height
function evalHeight() {
height = categoriesList.y - buyButton.y + categoriesList.height;
height = categoriesList.y - badges.y + categoriesList.height;
}
Item {
id: badges
anchors {
right: buyButton.left
top: parent.top
rightMargin: 10
}
height: childrenRect.height
Image {
id: standaloneOptomizedBadge
anchors {
topMargin: 15
right: parent.right
top: parent.top
}
height: root.standaloneOptimized ? 50 : 0
width: 50
visible: root.standaloneOptimized
fillMode: Image.PreserveAspectFit
source: "../../../../icons/standalone-optimized.svg"
}
ColorOverlay {
anchors.fill: standaloneOptomizedBadge
source: standaloneOptomizedBadge
color: hifi.colors.blueHighlight
visible: root.standaloneOptimized
}
}
HifiControlsUit.Button {
@ -252,10 +288,10 @@ Rectangle {
anchors {
right: parent.right
top: parent.top
left: parent.left
topMargin: 15
}
height: 50
height: 50
width: 180
property bool isUpdate: root.edition >= 0 // Special case of updating from a specific older item
property bool isStocking: (creator === Account.username) && (availability === "not for sale") && !updated_item_id // Note: server will say "sold out" or "invalidated" before it says NFS
@ -275,11 +311,11 @@ Rectangle {
id: creatorItem
anchors {
top: buyButton.bottom
top: parent.top
leftMargin: 15
topMargin: 15
}
width: parent.width
width: paintedWidth
height: childrenRect.height
RalewaySemiBold {
@ -528,13 +564,55 @@ Rectangle {
}
}
}
Item {
id: standaloneItem
anchors {
top: licenseItem.bottom
topMargin: 15
left: parent.left
right: parent.right
}
height: root.standaloneVisible ? childrenRect.height : 0
visible: root.standaloneVisible
RalewaySemiBold {
id: standaloneLabel
anchors.top: parent.top
anchors.left: parent.left
width: paintedWidth
height: 20
text: root.standaloneOptimized ? "STAND-ALONE OPTIMIZED:" : "STAND-ALONE INCOMPATIBLE:"
size: 14
color: hifi.colors.lightGrayText
verticalAlignment: Text.AlignVCenter
}
RalewaySemiBold {
id: standaloneOptimizedText
anchors.top: standaloneLabel.bottom
anchors.left: parent.left
anchors.topMargin: 5
width: paintedWidth
text: root.standaloneOptimized ? "This item is stand-alone optimized" : "This item is incompatible with stand-alone devices"
size: 14
color: hifi.colors.lightGray
verticalAlignment: Text.AlignVCenter
}
}
Item {
id: descriptionItem
property string text: ""
anchors {
top: licenseItem.bottom
top: standaloneItem.bottom
topMargin: 15
left: parent.left
right: parent.right

View file

@ -35,8 +35,9 @@ Rectangle {
property string category: ""
property int price: 0
property string availability: "unknown"
property bool isLoggedIn: false;
property bool isLoggedIn: false
property bool standaloneOptimized: false
signal buy()
signal showItem()
signal categoryClicked(string category)
@ -240,16 +241,18 @@ Rectangle {
id: creatorText
anchors {
top: creatorLabel.top;
left: creatorLabel.right;
leftMargin: 10;
top: creatorLabel.top
left: creatorLabel.right
leftMargin: 15
right: badges.left
}
width: paintedWidth;
width: paintedWidth
text: root.creator;
size: 14;
color: hifi.colors.lightGray;
verticalAlignment: Text.AlignVCenter;
text: root.creator
size: 14
elide: Text.ElideRight
color: hifi.colors.lightGray
verticalAlignment: Text.AlignVCenter
}
RalewaySemiBold {
@ -260,12 +263,12 @@ Rectangle {
left: parent.left
leftMargin: 15
}
width: paintedWidth;
width: paintedWidth
text: "IN:";
size: 14;
color: hifi.colors.lightGrayText;
verticalAlignment: Text.AlignVCenter;
text: "IN:"
size: 14
color: hifi.colors.lightGrayText
verticalAlignment: Text.AlignVCenter
}
RalewaySemiBold {
@ -274,23 +277,57 @@ Rectangle {
anchors {
top: categoryLabel.top
left: categoryLabel.right
leftMargin: 10
leftMargin: 15
right: badges.left
}
width: paintedWidth
text: root.category
size: 14
color: hifi.colors.blueHighlight;
verticalAlignment: Text.AlignVCenter;
color: hifi.colors.blueHighlight
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
MouseArea {
anchors.fill: parent
onClicked: root.categoryClicked(root.category);
}
}
Item {
id: badges
anchors {
right: buyButton.left
top: parent.top
topMargin: 10
rightMargin: 10
}
height: 50
Image {
id: standaloneOptomizedBadge
anchors {
right: parent.right
top: parent.top
}
height: root.standaloneOptimized ? 40 : 0
width: 40
visible: root.standaloneOptimized
fillMode: Image.PreserveAspectFit
source: "../../../../icons/standalone-optimized.svg"
}
ColorOverlay {
anchors.fill: standaloneOptomizedBadge
source: standaloneOptomizedBadge
color: hifi.colors.blueHighlight
visible: root.standaloneOptimized
}
}
HifiControlsUit.Button {
id: buyButton
anchors {
right: parent.right
top: parent.top

View file

@ -13,6 +13,7 @@
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import stylesUit 1.0
@ -50,6 +51,8 @@ Item {
property string upgradeTitle;
property bool updateAvailable: root.updateItemId && root.updateItemId !== "";
property bool valid;
property bool standaloneOptimized;
property bool standaloneIncompatible;
property string originalStatusText;
property string originalStatusColor;
@ -403,7 +406,9 @@ Item {
id: permissionExplanationText;
anchors.fill: parent;
text: {
if (root.itemType === "contentSet") {
if (root.standaloneIncompatible) {
"This item is incompatible with stand-alone devices. <a href='#standaloneIncompatible'>Learn more</a>";
} else if (root.itemType === "contentSet") {
"You do not have 'Replace Content' permissions in this domain. <a href='#replaceContentPermission'>Learn more</a>";
} else if (root.itemType === "entity") {
"You do not have 'Rez Certified' permissions in this domain. <a href='#rezCertifiedPermission'>Learn more</a>";
@ -417,7 +422,11 @@ Item {
verticalAlignment: Text.AlignVCenter;
onLinkActivated: {
sendToPurchases({method: 'showPermissionsExplanation', itemType: root.itemType});
if (link === "#standaloneIncompatible") {
sendToPurchases({method: 'showStandaloneIncompatibleExplanation'});
} else {
sendToPurchases({method: 'showPermissionsExplanation', itemType: root.itemType});
}
}
}
}
@ -699,7 +708,8 @@ Item {
anchors.bottomMargin: 8;
width: 160;
height: 40;
enabled: root.hasPermissionToRezThis &&
enabled: !root.standaloneIncompatible &&
root.hasPermissionToRezThis &&
MyAvatar.skeletonModelURL !== root.itemHref &&
!root.wornEntityID &&
root.valid;
@ -838,6 +848,28 @@ Item {
root.sendToPurchases({ method: 'flipCard' });
}
}
}
Image {
id: standaloneOptomizedBadge
anchors {
right: parent.right
bottom: parent.bottom
rightMargin: 15
bottomMargin:12
}
height: root.standaloneOptimized ? 36 : 0
width: 36
visible: root.standaloneOptimized
fillMode: Image.PreserveAspectFit
source: "../../../../icons/standalone-optimized.svg"
}
ColorOverlay {
anchors.fill: standaloneOptomizedBadge
source: standaloneOptomizedBadge
color: hifi.colors.blueHighlight
visible: root.standaloneOptimized
}
}

View file

@ -12,7 +12,7 @@
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick 2.9
import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit
import "../../../controls" as HifiControls
@ -33,6 +33,7 @@ Rectangle {
property bool purchasesReceived: false;
property bool punctuationMode: false;
property bool isDebuggingFirstUseTutorial: false;
property bool isStandalone: false;
property string installedApps;
property bool keyboardRaised: false;
property int numUpdatesAvailable: 0;
@ -44,6 +45,7 @@ Rectangle {
purchasesModel.getFirstPage();
Commerce.getAvailableUpdates();
}
Connections {
target: Commerce;
@ -110,6 +112,10 @@ Rectangle {
}
}
Component.onCompleted: {
isStandalone = PlatformInfo.isStandalone();
}
HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup;
z: 999;
@ -527,6 +533,7 @@ Rectangle {
ListView {
id: purchasesContentsList;
visible: purchasesModel.count !== 0;
interactive: !lightboxPopup.visible;
clip: true;
model: purchasesModel;
snapMode: ListView.NoSnap;
@ -553,6 +560,8 @@ Rectangle {
upgradeTitle: model.upgrade_title;
itemType: model.item_type;
valid: model.valid;
standaloneOptimized: model.standalone_optimized
standaloneIncompatible: root.isStandalone && model.standalone_incompatible
anchors.topMargin: 10;
anchors.bottomMargin: 10;
@ -673,6 +682,14 @@ Rectangle {
lightboxPopup.visible = false;
}
lightboxPopup.visible = true;
} else if (msg.method === "showStandaloneIncompatibleExplanation") {
lightboxPopup.titleText = "Stand-alone Incompatible";
lightboxPopup.bodyText = "The item is incompatible with stand-alone devices.";
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = function() {
lightboxPopup.visible = false;
}
lightboxPopup.visible = true;
} else if (msg.method === "setFilterText") {
filterBar.text = msg.filterText;
} else if (msg.method === "flipCard") {

View file

@ -97,10 +97,11 @@ Rectangle {
textFormat: Text.StyledText
linkColor: "#00B4EF"
color: "white"
text: "Blockchain technology from <a href=\"https://elementsproject.org/elements/\">Elements</a>."
property string link: "https://eos.io/"
text: "Blockchain technology from <a href=\"" + link + "\">EOS</a>."
size: 14
onLinkActivated: {
HiFiAbout.openUrl("https://elementsproject.org/elements/");
HiFiAbout.openUrl(link);
}
}
RalewayRegular {

View file

@ -0,0 +1,59 @@
//
// Created by Dante Ruiz on 3/4/19.
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import stylesUit 1.0
import controlsUit 1.0 as HifiControls
Rectangle {
id: root
anchors.fill: parent
property string pluginName: ""
property var displayInformation: null
HifiConstants { id: hifi }
color: hifi.colors.baseGray
HifiControls.CheckBox {
id: box
width: 15
height: 15
anchors {
left: root.left
leftMargin: 75
}
onClicked: {
sendConfigurationSettings( { trackControllersInOculusHome: checked });
}
}
RalewaySemiBold {
id: head
text: "Track hand controllers in Oculus Home"
size: 12
color: "white"
anchors.left: box.right
anchors.leftMargin: 5
}
function displayConfiguration() {
var configurationSettings = InputConfiguration.configurationSettings(root.pluginName);
box.checked = configurationSettings.trackControllersInOculusHome;
}
function sendConfigurationSettings(settings) {
InputConfiguration.setConfigurationSettings(settings, root.pluginName);
}
}

View file

@ -0,0 +1,144 @@
//
// TADLightbox.qml
// qml/hifi/tablet
//
// TADLightbox
//
// Created by Roxanne Skelly on 2019-03-07
// Copyright 2019 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 Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit
import "qrc:////qml//controls" as HifiControls
// references XXX from root context
Rectangle {
property string titleText;
property string bodyImageSource;
property string bodyText;
property string button1color: hifi.buttons.noneBorderlessGray;
property string button1text;
property var button1method;
property string button2color: hifi.buttons.blue;
property string button2text;
property var button2method;
property string buttonLayout: "leftright";
id: root;
visible: false;
anchors.fill: parent;
color: Qt.rgba(0, 0, 0, 0.5);
z: 999;
HifiConstants { id: hifi; }
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
// able to click on a button/mouseArea underneath the popup.
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
hoverEnabled: true;
}
Rectangle {
anchors.centerIn: parent;
width: 376;
height: childrenRect.height + 30;
color: "white";
RalewaySemiBold {
id: titleText;
text: root.titleText;
anchors.top: parent.top;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: paintedHeight;
color: hifi.colors.black;
size: 24;
verticalAlignment: Text.AlignTop;
wrapMode: Text.WordWrap;
}
RalewayRegular {
id: bodyText;
text: root.bodyText;
anchors.top: root.bodyImageSource ? bodyImage.bottom : (root.titleText ? titleText.bottom : parent.top);
anchors.topMargin: root.bodyImageSource ? 20 : (root.titleText ? 20 : 30);
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: paintedHeight;
color: hifi.colors.black;
size: 20;
verticalAlignment: Text.AlignTop;
wrapMode: Text.WordWrap;
}
Item {
id: buttons;
anchors.top: bodyText.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
height: root.buttonLayout === "leftright" ? 70 : 150;
// Button 1
HifiControlsUit.Button {
id: button1;
color: root.button1color;
colorScheme: hifi.colorSchemes.light;
anchors.top: root.buttonLayout === "leftright" ? parent.top : parent.top;
anchors.left: parent.left;
anchors.leftMargin: root.buttonLayout === "leftright" ? 30 : 10;
anchors.right: root.buttonLayout === "leftright" ? undefined : parent.right;
anchors.rightMargin: root.buttonLayout === "leftright" ? undefined : 10;
width: root.buttonLayout === "leftright" ? (root.button2text ? parent.width/2 - anchors.leftMargin*2 : parent.width - anchors.leftMargin * 2) :
(undefined);
height: 40;
text: root.button1text;
onClicked: {
button1method();
}
visible: (root.button1text !== "");
}
// Button 2
HifiControlsUit.Button {
id: button2;
visible: root.button2text;
color: root.button2color;
colorScheme: hifi.colorSchemes.light;
anchors.top: root.buttonLayout === "leftright" ? parent.top : button1.bottom;
anchors.topMargin: root.buttonLayout === "leftright" ? undefined : 20;
anchors.left: root.buttonLayout === "leftright" ? undefined : parent.left;
anchors.leftMargin: root.buttonLayout === "leftright" ? undefined : 10;
anchors.right: parent.right;
anchors.rightMargin: root.buttonLayout === "leftright" ? 30 : 10;
width: root.buttonLayout === "leftright" ? parent.width/2 - anchors.rightMargin*2 : undefined;
height: 40;
text: root.button2text;
onClicked: {
button2method();
}
}
}
}
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -73,7 +73,7 @@ StackView {
function resetAfterTeleport() {
//storyCardFrame.shown = root.shown = false;
}
function goCard(targetString) {
function goCard(targetString, standaloneOptimized) {
if (0 !== targetString.indexOf('hifi://')) {
var card = tabletWebView.createObject();
card.url = addressBarDialog.metaverseServerUrl + targetString;
@ -82,7 +82,7 @@ StackView {
return;
}
location.text = targetString;
toggleOrGo(targetString, true);
toggleOrGo(targetString, true, standaloneOptimized);
clearAddressLineTimer.start();
}
@ -230,7 +230,7 @@ StackView {
updateLocationText(text.length > 0);
}
onAccepted: {
toggleOrGo();
toggleOrGo(addressLine.text, false, places.isStandalone(addressLine.text));
}
// unfortunately TextField from Quick Controls 2 disallow customization of placeHolderText color without creation of new style
@ -392,7 +392,18 @@ StackView {
right: parent.right
}
}
}
TADLightbox {
id: unoptimizedDomain
titleText: "Unoptimized Domain"
bodyText: "You're trying to access a place that hasn't been optimized for your device. Are you sure you want to continue."
button1text: "CANCEL"
button2text: "YES CONTINUE"
visible: false
button1method: function() {
visible = false;
}
}
function updateLocationText(enteringAddress) {
@ -407,14 +418,30 @@ StackView {
}
}
function toggleOrGo(address, fromSuggestions) {
if (address !== undefined && address !== "") {
addressBarDialog.loadAddress(address, fromSuggestions);
clearAddressLineTimer.start();
} else if (addressLine.text !== "") {
addressBarDialog.loadAddress(addressLine.text, fromSuggestions);
clearAddressLineTimer.start();
function toggleOrGo(address, fromSuggestions, standaloneOptimized) {
var goTarget = function () {
if (address !== undefined && address !== "") {
addressBarDialog.loadAddress(address, fromSuggestions);
clearAddressLineTimer.start();
} else if (addressLine.text !== "") {
addressBarDialog.loadAddress(addressLine.text, fromSuggestions);
clearAddressLineTimer.start();
}
DialogsManager.hideAddressBar();
}
unoptimizedDomain.button2method = function() {
Settings.setValue("ShowUnoptimizedDomainWarning", false);
goTarget();
}
var showPopup = PlatformInfo.isStandalone() && !standaloneOptimized && Settings.getValue("ShowUnoptimizedDomainWarning", true);
if(!showPopup) {
goTarget();
} else {
unoptimizedDomain.visible = true;
}
DialogsManager.hideAddressBar();
}
}

View file

@ -246,6 +246,8 @@
#include "AboutUtil.h"
#include <DisableDeferred.h>
#if defined(Q_OS_WIN)
#include <VersionHelpers.h>
@ -289,13 +291,6 @@ static const QString DISABLE_WATCHDOG_FLAG{ "HIFI_DISABLE_WATCHDOG" };
static bool DISABLE_WATCHDOG = nsightActive() || QProcessEnvironment::systemEnvironment().contains(DISABLE_WATCHDOG_FLAG);
#endif
#if defined(USE_GLES)
static bool DISABLE_DEFERRED = true;
#else
static const QString RENDER_FORWARD{ "HIFI_RENDER_FORWARD" };
static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
#endif
#if !defined(Q_OS_ANDROID)
static const uint32_t MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16;
#else
@ -755,6 +750,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
bool isStore = cmdOptionExists(argc, const_cast<const char**>(argv), OCULUS_STORE_ARG);
qApp->setProperty(hifi::properties::OCULUS_STORE, isStore);
// emulate standalone device
static const auto STANDALONE_ARG = "--standalone";
bool isStandalone = cmdOptionExists(argc, const_cast<const char**>(argv), STANDALONE_ARG);
qApp->setProperty(hifi::properties::STANDALONE, isStandalone);
// Ignore any previous crashes if running from command line with a test script.
bool inTestMode { false };
for (int i = 0; i < argc; ++i) {
@ -1599,12 +1599,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
using namespace controller;
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto audioScriptingInterface = reinterpret_cast<scripting::Audio*>(DependencyManager::get<AudioScriptingInterface>().data());
{
auto actionEnum = static_cast<Action>(action);
int key = Qt::Key_unknown;
static int lastKey = Qt::Key_unknown;
bool navAxis = false;
switch (actionEnum) {
case Action::TOGGLE_PUSHTOTALK:
if (state > 0.0f) {
audioScriptingInterface->setPushingToTalk(true);
} else if (state <= 0.0f) {
audioScriptingInterface->setPushingToTalk(false);
}
break;
case Action::UI_NAV_VERTICAL:
navAxis = true;
if (state > 0.0f) {
@ -3030,6 +3039,10 @@ void Application::initializeUi() {
};
OffscreenQmlSurface::addWhitelistContextHandler({
QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
QUrl{ "hifi/commerce/purchases/Purchases.qml" },
QUrl{ "hifi/commerce/wallet/Wallet.qml" },
QUrl{ "hifi/commerce/wallet/WalletHome.qml" },
QUrl{ "hifi/tablet/TabletAddressDialog.qml" },
}, platformInfoCallback);
QmlContextCallback ttsCallback = [](QQmlContext* context) {
@ -4306,6 +4319,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
if (_keyboardMouseDevice->isActive()) {
_keyboardMouseDevice->keyReleaseEvent(event);
}
}
void Application::focusOutEvent(QFocusEvent* event) {
@ -5237,6 +5251,9 @@ void Application::loadSettings() {
}
}
auto audioScriptingInterface = reinterpret_cast<scripting::Audio*>(DependencyManager::get<AudioScriptingInterface>().data());
audioScriptingInterface->loadData();
getMyAvatar()->loadData();
_settingsLoaded = true;
}
@ -5246,6 +5263,9 @@ void Application::saveSettings() const {
DependencyManager::get<AudioClient>()->saveSettings();
DependencyManager::get<LODManager>()->saveSettings();
auto audioScriptingInterface = reinterpret_cast<scripting::Audio*>(DependencyManager::get<AudioScriptingInterface>().data());
audioScriptingInterface->saveData();
Menu::getInstance()->saveSettings();
getMyAvatar()->saveData();
PluginManager::getInstance()->saveSettings();

View file

@ -12,10 +12,52 @@
#include "AvatarDoctor.h"
#include <model-networking/ModelCache.h>
#include <AvatarConstants.h>
#include <Rig.h>
#include <ResourceManager.h>
#include <QDir>
#include <FSTReader.h>
const int NETWORKED_JOINTS_LIMIT = 256;
const float HIPS_GROUND_MIN_Y = 0.01f;
const float HIPS_SPINE_CHEST_MIN_SEPARATION = 0.001f;
const QString LEFT_JOINT_PREFIX = "Left";
const QString RIGHT_JOINT_PREFIX = "Right";
AvatarDoctor::AvatarDoctor(QUrl avatarFSTFileUrl) :
const QStringList LEG_MAPPING_SUFFIXES = {
"UpLeg"
"Leg",
"Foot",
"ToeBase",
};
static QStringList ARM_MAPPING_SUFFIXES = {
"Shoulder",
"Arm",
"ForeArm",
"Hand",
};
static QStringList HAND_MAPPING_SUFFIXES = {
"HandIndex3",
"HandIndex2",
"HandIndex1",
"HandPinky3",
"HandPinky2",
"HandPinky1",
"HandMiddle3",
"HandMiddle2",
"HandMiddle1",
"HandRing3",
"HandRing2",
"HandRing1",
"HandThumb3",
"HandThumb2",
"HandThumb1",
};
const QUrl DEFAULT_DOCS_URL = QUrl("https://docs.highfidelity.com/create/avatars/create-avatars.html#create-your-own-avatar");
AvatarDoctor::AvatarDoctor(const QUrl& avatarFSTFileUrl) :
_avatarFSTFileUrl(avatarFSTFileUrl) {
connect(this, &AvatarDoctor::complete, this, [this](QVariantList errors) {
@ -39,136 +81,215 @@ void AvatarDoctor::startDiagnosing() {
const auto resource = DependencyManager::get<ModelCache>()->getGeometryResource(_avatarFSTFileUrl);
resource->refresh();
const QUrl DEFAULT_URL = QUrl("https://docs.highfidelity.com/create/avatars/create-avatars.html#create-your-own-avatar");
const auto resourceLoaded = [this, resource, DEFAULT_URL](bool success) {
const auto resourceLoaded = [this, resource](bool success) {
// MODEL
if (!success) {
_errors.push_back({ "Model file cannot be opened", DEFAULT_URL });
_errors.push_back({ "Model file cannot be opened.", DEFAULT_DOCS_URL });
emit complete(getErrors());
return;
}
_model = resource;
const auto model = resource.data();
const auto avatarModel = resource.data()->getHFMModel();
if (!avatarModel.originalURL.endsWith(".fbx")) {
_errors.push_back({ "Unsupported avatar model format", DEFAULT_URL });
_errors.push_back({ "Unsupported avatar model format.", DEFAULT_DOCS_URL });
emit complete(getErrors());
return;
}
// RIG
if (avatarModel.joints.isEmpty()) {
_errors.push_back({ "Avatar has no rig", DEFAULT_URL });
_errors.push_back({ "Avatar has no rig.", DEFAULT_DOCS_URL });
} else {
if (avatarModel.joints.length() > 256) {
_errors.push_back({ "Avatar has over 256 bones", DEFAULT_URL });
auto jointNames = avatarModel.getJointNames();
if (avatarModel.joints.length() > NETWORKED_JOINTS_LIMIT) {
_errors.push_back({tr( "Avatar has over %n bones.", "", NETWORKED_JOINTS_LIMIT), DEFAULT_DOCS_URL });
}
// Avatar does not have Hips bone mapped
if (!avatarModel.getJointNames().contains("Hips")) {
_errors.push_back({ "Hips are not mapped", DEFAULT_URL });
if (!jointNames.contains("Hips")) {
_errors.push_back({ "Hips are not mapped.", DEFAULT_DOCS_URL });
}
if (!avatarModel.getJointNames().contains("Spine")) {
_errors.push_back({ "Spine is not mapped", DEFAULT_URL });
if (!jointNames.contains("Spine")) {
_errors.push_back({ "Spine is not mapped.", DEFAULT_DOCS_URL });
}
if (!avatarModel.getJointNames().contains("Head")) {
_errors.push_back({ "Head is not mapped", DEFAULT_URL });
if (!jointNames.contains("Spine1")) {
_errors.push_back({ "Chest (Spine1) is not mapped.", DEFAULT_DOCS_URL });
}
if (!jointNames.contains("Neck")) {
_errors.push_back({ "Neck is not mapped.", DEFAULT_DOCS_URL });
}
if (!jointNames.contains("Head")) {
_errors.push_back({ "Head is not mapped.", DEFAULT_DOCS_URL });
}
if (!jointNames.contains("LeftEye")) {
if (jointNames.contains("RightEye")) {
_errors.push_back({ "LeftEye is not mapped.", DEFAULT_DOCS_URL });
} else {
_errors.push_back({ "Eyes are not mapped.", DEFAULT_DOCS_URL });
}
} else if (!jointNames.contains("RightEye")) {
_errors.push_back({ "RightEye is not mapped.", DEFAULT_DOCS_URL });
}
const auto checkJointAsymmetry = [jointNames] (const QStringList& jointMappingSuffixes) {
for (const QString& jointSuffix : jointMappingSuffixes) {
if (jointNames.contains(LEFT_JOINT_PREFIX + jointSuffix) !=
jointNames.contains(RIGHT_JOINT_PREFIX + jointSuffix)) {
return true;
}
}
return false;
};
const auto isDescendantOfJointWhenJointsExist = [avatarModel, jointNames] (const QString& jointName, const QString& ancestorName) {
if (!jointNames.contains(jointName) || !jointNames.contains(ancestorName)) {
return true;
}
auto currentJoint = avatarModel.joints[avatarModel.jointIndices[jointName] - 1];
while (currentJoint.parentIndex != -1) {
currentJoint = avatarModel.joints[currentJoint.parentIndex];
if (currentJoint.name == ancestorName) {
return true;
}
}
return false;
};
if (checkJointAsymmetry(ARM_MAPPING_SUFFIXES)) {
_errors.push_back({ "Asymmetrical arm bones.", DEFAULT_DOCS_URL });
}
if (checkJointAsymmetry(HAND_MAPPING_SUFFIXES)) {
_errors.push_back({ "Asymmetrical hand bones.", DEFAULT_DOCS_URL });
}
if (checkJointAsymmetry(LEG_MAPPING_SUFFIXES)) {
_errors.push_back({ "Asymmetrical leg bones.", DEFAULT_DOCS_URL });
}
// Multiple skeleton root joints checkup
int skeletonRootJoints = 0;
for (const HFMJoint& joint: avatarModel.joints) {
if (joint.parentIndex == -1 && joint.isSkeletonJoint) {
skeletonRootJoints++;
}
}
if (skeletonRootJoints > 1) {
_errors.push_back({ "Multiple top-level joints found.", DEFAULT_DOCS_URL });
}
Rig rig;
rig.reset(avatarModel);
const float eyeHeight = rig.getUnscaledEyeHeight();
const float ratio = eyeHeight / DEFAULT_AVATAR_HEIGHT;
const float avatarHeight = eyeHeight + ratio * DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD;
// SCALE
const float RECOMMENDED_MIN_HEIGHT = DEFAULT_AVATAR_HEIGHT * 0.25f;
const float RECOMMENDED_MAX_HEIGHT = DEFAULT_AVATAR_HEIGHT * 1.5f;
if (avatarHeight < RECOMMENDED_MIN_HEIGHT) {
_errors.push_back({ "Avatar is possibly too short.", DEFAULT_DOCS_URL });
} else if (avatarHeight > RECOMMENDED_MAX_HEIGHT) {
_errors.push_back({ "Avatar is possibly too tall.", DEFAULT_DOCS_URL });
}
// HipsNotOnGround
auto hipsIndex = rig.indexOfJoint("Hips");
if (hipsIndex >= 0) {
glm::vec3 hipsPosition;
if (rig.getJointPosition(hipsIndex, hipsPosition)) {
const auto hipJoint = avatarModel.joints.at(avatarModel.getJointIndex("Hips"));
if (hipsPosition.y < HIPS_GROUND_MIN_Y) {
_errors.push_back({ "Hips are on ground.", DEFAULT_DOCS_URL });
}
}
}
// HipsSpineChestNotCoincident
auto spineIndex = rig.indexOfJoint("Spine");
auto chestIndex = rig.indexOfJoint("Spine1");
if (hipsIndex >= 0 && spineIndex >= 0 && chestIndex >= 0) {
glm::vec3 hipsPosition;
glm::vec3 spinePosition;
glm::vec3 chestPosition;
if (rig.getJointPosition(hipsIndex, hipsPosition) &&
rig.getJointPosition(spineIndex, spinePosition) &&
rig.getJointPosition(chestIndex, chestPosition)) {
const auto hipsToSpine = glm::length(hipsPosition - spinePosition);
const auto spineToChest = glm::length(spinePosition - chestPosition);
if (hipsToSpine < HIPS_SPINE_CHEST_MIN_SEPARATION && spineToChest < HIPS_SPINE_CHEST_MIN_SEPARATION) {
_errors.push_back({ "Hips/Spine/Chest overlap.", DEFAULT_DOCS_URL });
}
}
}
auto mapping = resource->getMapping();
if (mapping.contains(JOINT_NAME_MAPPING_FIELD) && mapping[JOINT_NAME_MAPPING_FIELD].type() == QVariant::Hash) {
const auto& jointNameMappings = mapping[JOINT_NAME_MAPPING_FIELD].toHash();
QStringList jointValues;
for (const auto& jointVariant: jointNameMappings.values()) {
jointValues << jointVariant.toString();
}
const auto& uniqueJointValues = jointValues.toSet();
for (const auto& jointName: uniqueJointValues) {
if (jointValues.count(jointName) > 1) {
_errors.push_back({ tr("%1 is mapped multiple times.").arg(jointName), DEFAULT_DOCS_URL });
}
}
}
if (!isDescendantOfJointWhenJointsExist("Spine", "Hips")) {
_errors.push_back({ "Spine is not a child of Hips.", DEFAULT_DOCS_URL });
}
if (!isDescendantOfJointWhenJointsExist("Spine1", "Spine")) {
_errors.push_back({ "Spine1 is not a child of Spine.", DEFAULT_DOCS_URL });
}
if (!isDescendantOfJointWhenJointsExist("Head", "Spine1")) {
_errors.push_back({ "Head is not a child of Spine1.", DEFAULT_DOCS_URL });
}
}
// SCALE
const float RECOMMENDED_MIN_HEIGHT = DEFAULT_AVATAR_HEIGHT * 0.25f;
const float RECOMMENDED_MAX_HEIGHT = DEFAULT_AVATAR_HEIGHT * 1.5f;
const float avatarHeight = avatarModel.bindExtents.largestDimension();
if (avatarHeight < RECOMMENDED_MIN_HEIGHT) {
_errors.push_back({ "Avatar is possibly too small.", DEFAULT_URL });
} else if (avatarHeight > RECOMMENDED_MAX_HEIGHT) {
_errors.push_back({ "Avatar is possibly too large.", DEFAULT_URL });
}
// TEXTURES
QStringList externalTextures{};
QSet<QString> textureNames{};
auto addTextureToList = [&externalTextures](hfm::Texture texture) mutable {
if (!texture.filename.isEmpty() && texture.content.isEmpty() && !externalTextures.contains(texture.name)) {
externalTextures << texture.name;
auto materialMappingHandled = [this]() mutable {
_materialMappingLoadedCount++;
// Continue diagnosing the textures as soon as the material mappings have tried to load.
if (_materialMappingLoadedCount == _materialMappingCount) {
// TEXTURES
diagnoseTextures();
}
};
foreach(const HFMMaterial material, avatarModel.materials) {
addTextureToList(material.normalTexture);
addTextureToList(material.albedoTexture);
addTextureToList(material.opacityTexture);
addTextureToList(material.glossTexture);
addTextureToList(material.roughnessTexture);
addTextureToList(material.specularTexture);
addTextureToList(material.metallicTexture);
addTextureToList(material.emissiveTexture);
addTextureToList(material.occlusionTexture);
addTextureToList(material.scatteringTexture);
addTextureToList(material.lightmapTexture);
}
if (!externalTextures.empty()) {
// Check External Textures:
auto modelTexturesURLs = model->getTextures();
_externalTextureCount = externalTextures.length();
foreach(const QString textureKey, externalTextures) {
if (!modelTexturesURLs.contains(textureKey)) {
_missingTextureCount++;
_checkedTextureCount++;
continue;
}
const QUrl textureURL = modelTexturesURLs[textureKey].toUrl();
auto textureResource = DependencyManager::get<TextureCache>()->getTexture(textureURL);
auto checkTextureLoadingComplete = [this, DEFAULT_URL] () mutable {
qDebug() << "checkTextureLoadingComplete" << _checkedTextureCount << "/" << _externalTextureCount;
if (_checkedTextureCount == _externalTextureCount) {
if (_missingTextureCount > 0) {
_errors.push_back({ tr("Missing %n texture(s).","", _missingTextureCount), DEFAULT_URL });
}
if (_unsupportedTextureCount > 0) {
_errors.push_back({ tr("%n unsupported texture(s) found.", "", _unsupportedTextureCount), DEFAULT_URL });
}
emit complete(getErrors());
}
};
auto textureLoaded = [this, textureResource, checkTextureLoadingComplete] (bool success) mutable {
if (!success) {
auto normalizedURL = DependencyManager::get<ResourceManager>()->normalizeURL(textureResource->getURL());
if (normalizedURL.isLocalFile()) {
QFile textureFile(normalizedURL.toLocalFile());
if (textureFile.exists()) {
_unsupportedTextureCount++;
} else {
_missingTextureCount++;
}
} else {
_missingTextureCount++;
}
}
_checkedTextureCount++;
checkTextureLoadingComplete();
};
if (textureResource) {
textureResource->refresh();
if (textureResource->isLoaded()) {
textureLoaded(!textureResource->isFailed());
} else {
connect(textureResource.data(), &NetworkTexture::finished, this, textureLoaded);
}
_materialMappingCount = (int)model->getMaterialMapping().size();
_materialMappingLoadedCount = 0;
for (const auto& materialMapping : model->getMaterialMapping()) {
// refresh the texture mappings
auto materialMappingResource = materialMapping.second;
if (materialMappingResource) {
materialMappingResource->refresh();
if (materialMappingResource->isLoaded()) {
materialMappingHandled();
} else {
_missingTextureCount++;
_checkedTextureCount++;
checkTextureLoadingComplete();
connect(materialMappingResource.data(), &NetworkTexture::finished, this,
[materialMappingHandled](bool success) mutable {
materialMappingHandled();
});
}
} else {
materialMappingHandled();
}
} else {
emit complete(getErrors());
}
if (_materialMappingCount == 0) {
// TEXTURES
diagnoseTextures();
}
};
@ -179,11 +300,117 @@ void AvatarDoctor::startDiagnosing() {
connect(resource.data(), &GeometryResource::finished, this, resourceLoaded);
}
} else {
_errors.push_back({ "Model file cannot be opened", DEFAULT_URL });
_errors.push_back({ "Model file cannot be opened", DEFAULT_DOCS_URL });
emit complete(getErrors());
}
}
void AvatarDoctor::diagnoseTextures() {
const auto model = _model.data();
const auto avatarModel = _model.data()->getHFMModel();
QVector<QString> externalTextures{};
QVector<QString> textureNames{};
int texturesFound = 0;
auto addTextureToList = [&externalTextures, &texturesFound](hfm::Texture texture) mutable {
if (texture.filename.isEmpty()) {
return;
}
if (texture.content.isEmpty() && !externalTextures.contains(texture.name)) {
externalTextures << texture.name;
}
texturesFound++;
};
for (const auto& material : avatarModel.materials) {
addTextureToList(material.normalTexture);
addTextureToList(material.albedoTexture);
addTextureToList(material.opacityTexture);
addTextureToList(material.glossTexture);
addTextureToList(material.roughnessTexture);
addTextureToList(material.specularTexture);
addTextureToList(material.metallicTexture);
addTextureToList(material.emissiveTexture);
addTextureToList(material.occlusionTexture);
addTextureToList(material.scatteringTexture);
addTextureToList(material.lightmapTexture);
}
for (const auto& materialMapping : model->getMaterialMapping()) {
for (const auto& networkMaterial : materialMapping.second.data()->parsedMaterials.networkMaterials) {
texturesFound += (int)networkMaterial.second->getTextureMaps().size();
}
}
auto normalizedURL = DependencyManager::get<ResourceManager>()->normalizeURL(
QUrl(avatarModel.originalURL)).resolved(QUrl("textures"));
if (texturesFound == 0) {
_errors.push_back({ tr("No textures assigned."), DEFAULT_DOCS_URL });
}
if (!externalTextures.empty()) {
// Check External Textures:
auto modelTexturesURLs = model->getTextures();
_externalTextureCount = externalTextures.length();
auto checkTextureLoadingComplete = [this]() mutable {
if (_checkedTextureCount == _externalTextureCount) {
if (_missingTextureCount > 0) {
_errors.push_back({ tr("Missing %n texture(s).","", _missingTextureCount), DEFAULT_DOCS_URL });
}
if (_unsupportedTextureCount > 0) {
_errors.push_back({ tr("%n unsupported texture(s) found.", "", _unsupportedTextureCount),
DEFAULT_DOCS_URL });
}
emit complete(getErrors());
}
};
for (const QString& textureKey : externalTextures) {
if (!modelTexturesURLs.contains(textureKey)) {
_missingTextureCount++;
_checkedTextureCount++;
continue;
}
const QUrl textureURL = modelTexturesURLs[textureKey].toUrl();
auto textureResource = DependencyManager::get<TextureCache>()->getTexture(textureURL);
auto textureLoaded = [this, textureResource, checkTextureLoadingComplete](bool success) mutable {
if (!success) {
auto normalizedURL = DependencyManager::get<ResourceManager>()->normalizeURL(textureResource->getURL());
if (normalizedURL.isLocalFile()) {
QFile textureFile(normalizedURL.toLocalFile());
if (textureFile.exists()) {
_unsupportedTextureCount++;
} else {
_missingTextureCount++;
}
} else {
_missingTextureCount++;
}
}
_checkedTextureCount++;
checkTextureLoadingComplete();
};
if (textureResource) {
textureResource->refresh();
if (textureResource->isLoaded()) {
textureLoaded(!textureResource->isFailed());
} else {
connect(textureResource.data(), &NetworkTexture::finished, this, textureLoaded);
}
} else {
_missingTextureCount++;
_checkedTextureCount++;
}
}
checkTextureLoadingComplete();
} else {
emit complete(getErrors());
}
}
QVariantList AvatarDoctor::getErrors() const {
QVariantList result;
for (const auto& error : _errors) {

View file

@ -16,6 +16,7 @@
#include <QUrl>
#include <QVector>
#include <QVariantMap>
#include "GeometryCache.h"
struct AvatarDiagnosticResult {
QString message;
@ -27,7 +28,7 @@ Q_DECLARE_METATYPE(QVector<AvatarDiagnosticResult>)
class AvatarDoctor : public QObject {
Q_OBJECT
public:
AvatarDoctor(QUrl avatarFSTFileUrl);
AvatarDoctor(const QUrl& avatarFSTFileUrl);
Q_INVOKABLE void startDiagnosing();
@ -37,6 +38,8 @@ signals:
void complete(QVariantList errors);
private:
void diagnoseTextures();
QUrl _avatarFSTFileUrl;
QVector<AvatarDiagnosticResult> _errors;
@ -45,6 +48,11 @@ private:
int _missingTextureCount = 0;
int _unsupportedTextureCount = 0;
int _materialMappingCount = 0;
int _materialMappingLoadedCount = 0;
GeometryResource::Pointer _model;
bool _isDiagnosing = false;
};

View file

@ -232,96 +232,142 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
auto avatarMap = getHashCopy();
const auto& views = qApp->getConicalViews();
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(views,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
sortedAvatars.reserve(avatarMap.size() - 1); // don't include MyAvatar
// Prepare 2 queues for heros and for crowd avatars
using AvatarPriorityQueue = PrioritySortUtil::PriorityQueue<SortableAvatar>;
// Keep two independent queues, one for heroes and one for the riff-raff.
enum PriorityVariants
{
kHero = 0,
kNonHero,
NumVariants
};
AvatarPriorityQueue avatarPriorityQueues[NumVariants] = {
{ views,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge },
{ views,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge } };
// Reserve space
//avatarPriorityQueues[kHero].reserve(10); // just few
avatarPriorityQueues[kNonHero].reserve(avatarMap.size() - 1); // don't include MyAvatar
// Build vector and compute priorities
auto nodeList = DependencyManager::get<NodeList>();
AvatarHash::iterator itr = avatarMap.begin();
while (itr != avatarMap.end()) {
const auto& avatar = std::static_pointer_cast<Avatar>(*itr);
auto avatar = std::static_pointer_cast<Avatar>(*itr);
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
// DO NOT update or fade out uninitialized Avatars
if (avatar != _myAvatar && avatar->isInitialized() && !nodeList->isPersonalMutingNode(avatar->getID())) {
sortedAvatars.push(SortableAvatar(avatar));
if (avatar->getHasPriority()) {
avatarPriorityQueues[kHero].push(SortableAvatar(avatar));
} else {
avatarPriorityQueues[kNonHero].push(SortableAvatar(avatar));
}
}
++itr;
}
// Sort
const auto& sortedAvatarVector = sortedAvatars.getSortedVector();
_numHeroAvatars = (int)avatarPriorityQueues[kHero].size();
// process in sorted order
uint64_t startTime = usecTimestampNow();
uint64_t updateExpiry = startTime + MAX_UPDATE_AVATARS_TIME_BUDGET;
const uint64_t MAX_UPDATE_HEROS_TIME_BUDGET = uint64_t(0.8 * MAX_UPDATE_AVATARS_TIME_BUDGET);
uint64_t updatePriorityExpiries[NumVariants] = { startTime + MAX_UPDATE_HEROS_TIME_BUDGET, startTime + MAX_UPDATE_AVATARS_TIME_BUDGET };
int numHerosUpdated = 0;
int numAvatarsUpdated = 0;
int numAVatarsNotUpdated = 0;
int numAvatarsNotUpdated = 0;
render::Transaction renderTransaction;
workload::Transaction workloadTransaction;
for (auto it = sortedAvatarVector.begin(); it != sortedAvatarVector.end(); ++it) {
const SortableAvatar& sortData = *it;
const auto avatar = std::static_pointer_cast<OtherAvatar>(sortData.getAvatar());
if (!avatar->_isClientAvatar) {
avatar->setIsClientAvatar(true);
}
// TODO: to help us scale to more avatars it would be nice to not have to poll this stuff every update
if (avatar->getSkeletonModel()->isLoaded()) {
// remove the orb if it is there
avatar->removeOrb();
if (avatar->needsPhysicsUpdate()) {
_avatarsToChangeInPhysics.insert(avatar);
}
} else {
avatar->updateOrbPosition();
}
for (int p = kHero; p < NumVariants; p++) {
auto& priorityQueue = avatarPriorityQueues[p];
// Sorting the current queue HERE as part of the measured timing.
const auto& sortedAvatarVector = priorityQueue.getSortedVector();
// for ALL avatars...
if (_shouldRender) {
avatar->ensureInScene(avatar, qApp->getMain3DScene());
}
avatar->animateScaleChanges(deltaTime);
auto passExpiry = updatePriorityExpiries[p];
uint64_t now = usecTimestampNow();
if (now < updateExpiry) {
// we're within budget
bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
if (inView && avatar->hasNewJointData()) {
numAvatarsUpdated++;
for (auto it = sortedAvatarVector.begin(); it != sortedAvatarVector.end(); ++it) {
const SortableAvatar& sortData = *it;
const auto avatar = std::static_pointer_cast<OtherAvatar>(sortData.getAvatar());
if (!avatar->_isClientAvatar) {
avatar->setIsClientAvatar(true);
}
auto transitStatus = avatar->_transit.update(deltaTime, avatar->_serverPosition, _transitConfig);
if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT || transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) {
avatar->_transit.reset();
avatar->setIsNewAvatar(false);
}
avatar->simulate(deltaTime, inView);
if (avatar->getSkeletonModel()->isLoaded() && avatar->getWorkloadRegion() == workload::Region::R1) {
_myAvatar->addAvatarHandsToFlow(avatar);
}
avatar->updateRenderItem(renderTransaction);
avatar->updateSpaceProxy(workloadTransaction);
avatar->setLastRenderUpdateTime(startTime);
} else {
// we've spent our full time budget --> bail on the rest of the avatar updates
// --> more avatars may freeze until their priority trickles up
// --> some scale animations may glitch
// --> some avatar velocity measurements may be a little off
// no time to simulate, but we take the time to count how many were tragically missed
while (it != sortedAvatarVector.end()) {
const SortableAvatar& newSortData = *it;
const auto& newAvatar = newSortData.getAvatar();
bool inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
// Once we reach an avatar that's not in view, all avatars after it will also be out of view
if (!inView) {
break;
// TODO: to help us scale to more avatars it would be nice to not have to poll this stuff every update
if (avatar->getSkeletonModel()->isLoaded()) {
// remove the orb if it is there
avatar->removeOrb();
if (avatar->needsPhysicsUpdate()) {
_avatarsToChangeInPhysics.insert(avatar);
}
numAVatarsNotUpdated += (int)(newAvatar->hasNewJointData());
++it;
} else {
avatar->updateOrbPosition();
}
break;
// for ALL avatars...
if (_shouldRender) {
avatar->ensureInScene(avatar, qApp->getMain3DScene());
}
avatar->animateScaleChanges(deltaTime);
uint64_t now = usecTimestampNow();
if (now < passExpiry) {
// we're within budget
bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
if (inView && avatar->hasNewJointData()) {
numAvatarsUpdated++;
}
auto transitStatus = avatar->_transit.update(deltaTime, avatar->_serverPosition, _transitConfig);
if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT ||
transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) {
avatar->_transit.reset();
avatar->setIsNewAvatar(false);
}
avatar->simulate(deltaTime, inView);
if (avatar->getSkeletonModel()->isLoaded() && avatar->getWorkloadRegion() == workload::Region::R1) {
_myAvatar->addAvatarHandsToFlow(avatar);
}
avatar->updateRenderItem(renderTransaction);
avatar->updateSpaceProxy(workloadTransaction);
avatar->setLastRenderUpdateTime(startTime);
} else {
// we've spent our time budget for this priority bucket
// let's deal with the reminding avatars if this pass and BREAK from the for loop
if (p == kHero) {
// Hero,
// --> put them back in the non hero queue
auto& crowdQueue = avatarPriorityQueues[kNonHero];
while (it != sortedAvatarVector.end()) {
crowdQueue.push(SortableAvatar((*it).getAvatar()));
++it;
}
} else {
// Non Hero
// --> bail on the rest of the avatar updates
// --> more avatars may freeze until their priority trickles up
// --> some scale animations may glitch
// --> some avatar velocity measurements may be a little off
// no time to simulate, but we take the time to count how many were tragically missed
numAvatarsNotUpdated = sortedAvatarVector.end() - it;
}
// We had to cut short this pass, we must break out of the for loop here
break;
}
}
if (p == kHero) {
numHerosUpdated = numAvatarsUpdated;
}
}
@ -337,7 +383,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
_space->enqueueTransaction(workloadTransaction);
_numAvatarsUpdated = numAvatarsUpdated;
_numAvatarsNotUpdated = numAVatarsNotUpdated;
_numAvatarsNotUpdated = numAvatarsNotUpdated;
_numHeroAvatarsUpdated = numHerosUpdated;
simulateAvatarFades(deltaTime);

View file

@ -90,6 +90,8 @@ public:
int getNumAvatarsUpdated() const { return _numAvatarsUpdated; }
int getNumAvatarsNotUpdated() const { return _numAvatarsNotUpdated; }
int getNumHeroAvatars() const { return _numHeroAvatars; }
int getNumHeroAvatarsUpdated() const { return _numHeroAvatarsUpdated; }
float getAvatarSimulationTime() const { return _avatarSimulationTime; }
void updateMyAvatar(float deltaTime);
@ -242,6 +244,8 @@ private:
RateCounter<> _myAvatarSendRate;
int _numAvatarsUpdated { 0 };
int _numAvatarsNotUpdated { 0 };
int _numHeroAvatars{ 0 };
int _numHeroAvatarsUpdated{ 0 };
float _avatarSimulationTime { 0.0f };
bool _shouldRender { true };
bool _myAvatarDataPacketsPaused { false };

View file

@ -5377,10 +5377,22 @@ void MyAvatar::addAvatarHandsToFlow(const std::shared_ptr<Avatar>& otherAvatar)
* @property {number} [offset=Vec3.ZERO] - Offset of the collision sphere from the joint.
*/
void MyAvatar::useFlow(bool isActive, bool isCollidable, const QVariantMap& physicsConfig, const QVariantMap& collisionsConfig) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "useFlow",
Q_ARG(bool, isActive),
Q_ARG(bool, isCollidable),
Q_ARG(const QVariantMap&, physicsConfig),
Q_ARG(const QVariantMap&, collisionsConfig));
return;
}
if (_skeletonModel->isLoaded()) {
_skeletonModel->getRig().initFlow(isActive);
auto &flow = _skeletonModel->getRig().getFlow();
auto &collisionSystem = flow.getCollisionSystem();
if (!flow.isInitialized() && isActive) {
_skeletonModel->getRig().initFlow(true);
} else {
flow.setActive(isActive);
}
collisionSystem.setActive(isCollidable);
auto physicsGroups = physicsConfig.keys();
if (physicsGroups.size() > 0) {
@ -5431,6 +5443,93 @@ void MyAvatar::useFlow(bool isActive, bool isCollidable, const QVariantMap& phys
}
}
QVariantMap MyAvatar::getFlowData() {
QVariantMap result;
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "getFlowData",
Q_RETURN_ARG(QVariantMap, result));
return result;
}
if (_skeletonModel->isLoaded()) {
auto jointNames = getJointNames();
auto &flow = _skeletonModel->getRig().getFlow();
auto &collisionSystem = flow.getCollisionSystem();
bool initialized = flow.isInitialized();
result.insert("initialized", initialized);
result.insert("active", flow.getActive());
result.insert("colliding", collisionSystem.getActive());
QVariantMap physicsData;
QVariantMap collisionsData;
QVariantMap threadData;
std::map<QString, QVariantList> groupJointsMap;
QVariantList jointCollisionData;
auto &groups = flow.getGroupSettings();
for (auto &joint : flow.getJoints()) {
auto &groupName = joint.second.getGroup();
if (groups.find(groupName) != groups.end()) {
if (groupJointsMap.find(groupName) == groupJointsMap.end()) {
groupJointsMap.insert(std::pair<QString, QVariantList>(groupName, QVariantList()));
}
groupJointsMap[groupName].push_back(joint.second.getIndex());
}
}
for (auto &group : groups) {
QVariantMap settingsObject;
QString groupName = group.first;
FlowPhysicsSettings groupSettings = group.second;
settingsObject.insert("active", groupSettings._active);
settingsObject.insert("damping", groupSettings._damping);
settingsObject.insert("delta", groupSettings._delta);
settingsObject.insert("gravity", groupSettings._gravity);
settingsObject.insert("inertia", groupSettings._inertia);
settingsObject.insert("radius", groupSettings._radius);
settingsObject.insert("stiffness", groupSettings._stiffness);
settingsObject.insert("jointIndices", groupJointsMap[groupName]);
physicsData.insert(groupName, settingsObject);
}
auto &collisions = collisionSystem.getCollisions();
for (auto &collision : collisions) {
QVariantMap collisionObject;
collisionObject.insert("offset", vec3toVariant(collision._offset));
collisionObject.insert("radius", collision._radius);
collisionObject.insert("jointIndex", collision._jointIndex);
QString jointName = jointNames.size() > collision._jointIndex ? jointNames[collision._jointIndex] : "unknown";
collisionsData.insert(jointName, collisionObject);
}
for (auto &thread : flow.getThreads()) {
QVariantList indices;
for (int index : thread._joints) {
indices.append(index);
}
threadData.insert(thread._jointsPointer->at(thread._joints[0]).getName(), indices);
}
result.insert("physics", physicsData);
result.insert("collisions", collisionsData);
result.insert("threads", threadData);
}
return result;
}
QVariantList MyAvatar::getCollidingFlowJoints() {
QVariantList result;
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "getCollidingFlowJoints",
Q_RETURN_ARG(QVariantList, result));
return result;
}
if (_skeletonModel->isLoaded()) {
auto& flow = _skeletonModel->getRig().getFlow();
for (auto &joint : flow.getJoints()) {
if (joint.second.isColliding()) {
result.append(joint.second.getIndex());
}
}
}
return result;
}
void MyAvatar::initFlowFromFST() {
if (_skeletonModel->isLoaded()) {
auto &flowData = _skeletonModel->getHFMModel().flowData;

View file

@ -1562,6 +1562,19 @@ public:
*/
Q_INVOKABLE void useFlow(bool isActive, bool isCollidable, const QVariantMap& physicsConfig = QVariantMap(), const QVariantMap& collisionsConfig = QVariantMap());
/**jsdoc
* @function MyAvatar.getFlowData
* @returns {object}
*/
Q_INVOKABLE QVariantMap getFlowData();
/**jsdoc
* returns the indices of every colliding flow joint
* @function MyAvatar.getCollidingFlowJoints
* @returns {int[]}
*/
Q_INVOKABLE QVariantList getCollidingFlowJoints();
public slots:
/**jsdoc

View file

@ -200,17 +200,6 @@ void OtherAvatar::resetDetailedMotionStates() {
void OtherAvatar::setWorkloadRegion(uint8_t region) {
_workloadRegion = region;
QString printRegion = "";
if (region == workload::Region::R1) {
printRegion = "R1";
} else if (region == workload::Region::R2) {
printRegion = "R2";
} else if (region == workload::Region::R3) {
printRegion = "R3";
} else {
printRegion = "invalid";
}
qCDebug(avatars) << "Setting workload region to " << printRegion;
computeShapeLOD();
}
@ -235,7 +224,6 @@ void OtherAvatar::computeShapeLOD() {
if (newLOD != _bodyLOD) {
_bodyLOD = newLOD;
if (isInPhysicsSimulation()) {
qCDebug(avatars) << "Changing to body LOD " << newLOD;
_needsReinsertion = true;
}
}

View file

@ -25,6 +25,8 @@ QString Audio::DESKTOP { "Desktop" };
QString Audio::HMD { "VR" };
Setting::Handle<bool> enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true };
Setting::Handle<bool> enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true };
float Audio::loudnessToLevel(float loudness) {
float level = loudness * (1/32768.0f); // level in [0, 1]
@ -37,10 +39,13 @@ Audio::Audio() : _devices(_contextIsHMD) {
auto client = DependencyManager::get<AudioClient>().data();
connect(client, &AudioClient::muteToggled, this, &Audio::setMuted);
connect(client, &AudioClient::noiseReductionChanged, this, &Audio::enableNoiseReduction);
connect(client, &AudioClient::warnWhenMutedChanged, this, &Audio::enableWarnWhenMuted);
connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged);
connect(client, &AudioClient::inputVolumeChanged, this, &Audio::setInputVolume);
connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged);
connect(this, &Audio::pushingToTalkChanged, this, &Audio::handlePushedToTalk);
enableNoiseReduction(enableNoiseReductionSetting.get());
enableWarnWhenMuted(enableWarnWhenMutedSetting.get());
onContextChanged();
}
@ -63,26 +68,174 @@ void Audio::stopRecording() {
}
bool Audio::isMuted() const {
return resultWithReadLock<bool>([&] {
return _isMuted;
});
bool isHMD = qApp->isHMDMode();
if (isHMD) {
return getMutedHMD();
} else {
return getMutedDesktop();
}
}
void Audio::setMuted(bool isMuted) {
bool isHMD = qApp->isHMDMode();
if (isHMD) {
setMutedHMD(isMuted);
} else {
setMutedDesktop(isMuted);
}
}
void Audio::setMutedDesktop(bool isMuted) {
bool changed = false;
withWriteLock([&] {
if (_isMuted != isMuted) {
_isMuted = isMuted;
if (_desktopMuted != isMuted) {
changed = true;
_desktopMuted = isMuted;
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false));
changed = true;
}
});
if (changed) {
emit mutedChanged(isMuted);
emit desktopMutedChanged(isMuted);
}
}
bool Audio::getMutedDesktop() const {
return resultWithReadLock<bool>([&] {
return _desktopMuted;
});
}
void Audio::setMutedHMD(bool isMuted) {
bool changed = false;
withWriteLock([&] {
if (_hmdMuted != isMuted) {
changed = true;
_hmdMuted = isMuted;
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false));
}
});
if (changed) {
emit mutedChanged(isMuted);
emit hmdMutedChanged(isMuted);
}
}
bool Audio::getMutedHMD() const {
return resultWithReadLock<bool>([&] {
return _hmdMuted;
});
}
bool Audio::getPTT() {
bool isHMD = qApp->isHMDMode();
if (isHMD) {
return getPTTHMD();
} else {
return getPTTDesktop();
}
}
void scripting::Audio::setPushingToTalk(bool pushingToTalk) {
bool changed = false;
withWriteLock([&] {
if (_pushingToTalk != pushingToTalk) {
changed = true;
_pushingToTalk = pushingToTalk;
}
});
if (changed) {
emit pushingToTalkChanged(pushingToTalk);
}
}
bool Audio::getPushingToTalk() const {
return resultWithReadLock<bool>([&] {
return _pushingToTalk;
});
}
void Audio::setPTT(bool enabled) {
bool isHMD = qApp->isHMDMode();
if (isHMD) {
setPTTHMD(enabled);
} else {
setPTTDesktop(enabled);
}
}
void Audio::setPTTDesktop(bool enabled) {
bool changed = false;
withWriteLock([&] {
if (_pttDesktop != enabled) {
changed = true;
_pttDesktop = enabled;
}
});
if (!enabled) {
// Set to default behavior (unmuted for Desktop) on Push-To-Talk disable.
setMutedDesktop(true);
} else {
// Should be muted when not pushing to talk while PTT is enabled.
setMutedDesktop(true);
}
if (changed) {
emit pushToTalkChanged(enabled);
emit pushToTalkDesktopChanged(enabled);
}
}
bool Audio::getPTTDesktop() const {
return resultWithReadLock<bool>([&] {
return _pttDesktop;
});
}
void Audio::setPTTHMD(bool enabled) {
bool changed = false;
withWriteLock([&] {
if (_pttHMD != enabled) {
changed = true;
_pttHMD = enabled;
}
});
if (!enabled) {
// Set to default behavior (unmuted for HMD) on Push-To-Talk disable.
setMutedHMD(false);
} else {
// Should be muted when not pushing to talk while PTT is enabled.
setMutedHMD(true);
}
if (changed) {
emit pushToTalkChanged(enabled);
emit pushToTalkHMDChanged(enabled);
}
}
void Audio::saveData() {
_desktopMutedSetting.set(getMutedDesktop());
_hmdMutedSetting.set(getMutedHMD());
_pttDesktopSetting.set(getPTTDesktop());
_pttHMDSetting.set(getPTTHMD());
}
void Audio::loadData() {
_desktopMuted = _desktopMutedSetting.get();
_hmdMuted = _hmdMutedSetting.get();
_pttDesktop = _pttDesktopSetting.get();
_pttHMD = _pttHMDSetting.get();
}
bool Audio::getPTTHMD() const {
return resultWithReadLock<bool>([&] {
return _pttHMD;
});
}
bool Audio::noiseReductionEnabled() const {
return resultWithReadLock<bool>([&] {
return _enableNoiseReduction;
@ -105,6 +258,28 @@ void Audio::enableNoiseReduction(bool enable) {
}
}
bool Audio::warnWhenMutedEnabled() const {
return resultWithReadLock<bool>([&] {
return _enableWarnWhenMuted;
});
}
void Audio::enableWarnWhenMuted(bool enable) {
bool changed = false;
withWriteLock([&] {
if (_enableWarnWhenMuted != enable) {
_enableWarnWhenMuted = enable;
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setWarnWhenMuted", Q_ARG(bool, enable), Q_ARG(bool, false));
enableWarnWhenMutedSetting.set(enable);
changed = true;
}
});
if (changed) {
emit warnWhenMutedChanged(enable);
}
}
float Audio::getInputVolume() const {
return resultWithReadLock<bool>([&] {
return _inputVolume;
@ -179,11 +354,26 @@ void Audio::onContextChanged() {
changed = true;
}
});
if (isHMD) {
setMuted(getMutedHMD());
} else {
setMuted(getMutedDesktop());
}
if (changed) {
emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP);
}
}
void Audio::handlePushedToTalk(bool enabled) {
if (getPTT()) {
if (enabled) {
setMuted(false);
} else {
setMuted(true);
}
}
}
void Audio::setReverb(bool enable) {
withWriteLock([&] {
DependencyManager::get<AudioClient>()->setReverb(enable);

View file

@ -12,6 +12,7 @@
#ifndef hifi_scripting_Audio_h
#define hifi_scripting_Audio_h
#include <functional>
#include "AudioScriptingInterface.h"
#include "AudioDevices.h"
#include "AudioEffectOptions.h"
@ -19,6 +20,9 @@
#include "AudioFileWav.h"
#include <shared/ReadWriteLockable.h>
using MutedGetter = std::function<bool()>;
using MutedSetter = std::function<void(bool)>;
namespace scripting {
class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
@ -37,16 +41,16 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
* @hifi-assignment-client
*
* @property {boolean} muted - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
* @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
* @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
* above the noise floor.
* @property {number} inputLevel - The loudness of the audio input, range <code>0.0</code> (no sound) &ndash;
* @property {number} inputLevel - The loudness of the audio input, range <code>0.0</code> (no sound) &ndash;
* <code>1.0</code> (the onset of clipping). <em>Read-only.</em>
* @property {boolean} clipping - <code>true</code> if the audio input is clipping, otherwise <code>false</code>.
* @property {number} inputVolume - Adjusts the volume of the input audio; range <code>0.0</code> &ndash; <code>1.0</code>.
* If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some
* @property {number} inputVolume - Adjusts the volume of the input audio; range <code>0.0</code> &ndash; <code>1.0</code>.
* If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some
* devices, and others might only support values of <code>0.0</code> and <code>1.0</code>.
* @property {boolean} isStereoInput - <code>true</code> if the input audio is being used in stereo, otherwise
* @property {boolean} isStereoInput - <code>true</code> if the input audio is being used in stereo, otherwise
* <code>false</code>. Some devices do not support stereo, in which case the value is always <code>false</code>.
* @property {string} context - The current context of the audio: either <code>"Desktop"</code> or <code>"HMD"</code>.
* <em>Read-only.</em>
@ -58,11 +62,18 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
Q_PROPERTY(bool warnWhenMuted READ warnWhenMutedEnabled WRITE enableWarnWhenMuted NOTIFY warnWhenMutedChanged)
Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged)
Q_PROPERTY(float inputLevel READ getInputLevel NOTIFY inputLevelChanged)
Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged)
Q_PROPERTY(QString context READ getContext NOTIFY contextChanged)
Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop)
Q_PROPERTY(bool desktopMuted READ getMutedDesktop WRITE setMutedDesktop NOTIFY desktopMutedChanged)
Q_PROPERTY(bool hmdMuted READ getMutedHMD WRITE setMutedHMD NOTIFY hmdMutedChanged)
Q_PROPERTY(bool pushToTalk READ getPTT WRITE setPTT NOTIFY pushToTalkChanged);
Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged)
Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged)
Q_PROPERTY(bool pushingToTalk READ getPushingToTalk WRITE setPushingToTalk NOTIFY pushingToTalkChanged)
public:
static QString AUDIO;
@ -75,6 +86,7 @@ public:
bool isMuted() const;
bool noiseReductionEnabled() const;
bool warnWhenMutedEnabled() const;
float getInputVolume() const;
float getInputLevel() const;
bool isClipping() const;
@ -82,10 +94,30 @@ public:
void showMicMeter(bool show);
// Mute setting setters and getters
void setMutedDesktop(bool isMuted);
bool getMutedDesktop() const;
void setMutedHMD(bool isMuted);
bool getMutedHMD() const;
void setPTT(bool enabled);
bool getPTT();
void setPushingToTalk(bool pushingToTalk);
bool getPushingToTalk() const;
// Push-To-Talk setters and getters
void setPTTDesktop(bool enabled);
bool getPTTDesktop() const;
void setPTTHMD(bool enabled);
bool getPTTHMD() const;
// Settings handlers
void saveData();
void loadData();
/**jsdoc
* @function Audio.setInputDevice
* @param {object} device
* @param {boolean} isHMD
* @param {boolean} isHMD
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD);
@ -99,8 +131,8 @@ public:
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
/**jsdoc
* Enable or disable reverberation. Reverberation is done by the client, on the post-mix audio. The reverberation options
* come from either the domain's audio zone if used &mdash; configured on the server &mdash; or as scripted by
* Enable or disable reverberation. Reverberation is done by the client, on the post-mix audio. The reverberation options
* come from either the domain's audio zone if used &mdash; configured on the server &mdash; or as scripted by
* {@link Audio.setReverbOptions|setReverbOptions}.
* @function Audio.setReverb
* @param {boolean} enable - <code>true</code> to enable reverberation, <code>false</code> to disable.
@ -110,13 +142,13 @@ public:
* var injectorOptions = {
* position: MyAvatar.position
* };
*
*
* Script.setTimeout(function () {
* print("Reverb OFF");
* Audio.setReverb(false);
* injector = Audio.playSound(sound, injectorOptions);
* }, 1000);
*
*
* Script.setTimeout(function () {
* var reverbOptions = new AudioEffectOptions();
* reverbOptions.roomSize = 100;
@ -124,26 +156,26 @@ public:
* print("Reverb ON");
* Audio.setReverb(true);
* }, 4000);
*
*
* Script.setTimeout(function () {
* print("Reverb OFF");
* Audio.setReverb(false);
* }, 8000); */
Q_INVOKABLE void setReverb(bool enable);
/**jsdoc
* Configure reverberation options. Use {@link Audio.setReverb|setReverb} to enable or disable reverberation.
* @function Audio.setReverbOptions
* @param {AudioEffectOptions} options - The reverberation options.
*/
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
/**jsdoc
* Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format.
* @function Audio.startRecording
* @param {string} filename - The path and name of the file to make the recording in. Should have a <code>.wav</code>
* @param {string} filename - The path and name of the file to make the recording in. Should have a <code>.wav</code>
* extension. The file is overwritten if it already exists.
* @returns {boolean} <code>true</code> if the specified file could be opened and audio recording has started, otherwise
* @returns {boolean} <code>true</code> if the specified file could be opened and audio recording has started, otherwise
* <code>false</code>.
* @example <caption>Make a 10 second audio recording.</caption>
* var filename = File.getTempDir() + "/audio.wav";
@ -152,13 +184,13 @@ public:
* Audio.stopRecording();
* print("Audio recording made in: " + filename);
* }, 10000);
*
*
* } else {
* print("Could not make an audio recording in: " + filename);
* }
*/
Q_INVOKABLE bool startRecording(const QString& filename);
/**jsdoc
* Finish making an audio recording started with {@link Audio.startRecording|startRecording}.
* @function Audio.stopRecording
@ -192,7 +224,47 @@ signals:
* });
*/
void mutedChanged(bool isMuted);
/**jsdoc
* Triggered when desktop audio input is muted or unmuted.
* @function Audio.desktopMutedChanged
* @param {boolean} isMuted - <code>true</code> if the audio input is muted for desktop mode, otherwise <code>false</code>.
* @returns {Signal}
*/
void desktopMutedChanged(bool isMuted);
/**jsdoc
* Triggered when HMD audio input is muted or unmuted.
* @function Audio.hmdMutedChanged
* @param {boolean} isMuted - <code>true</code> if the audio input is muted for HMD mode, otherwise <code>false</code>.
* @returns {Signal}
*/
void hmdMutedChanged(bool isMuted);
/**
* Triggered when Push-to-Talk has been enabled or disabled.
* @function Audio.pushToTalkChanged
* @param {boolean} enabled - <code>true</code> if Push-to-Talk is enabled, otherwise <code>false</code>.
* @returns {Signal}
*/
void pushToTalkChanged(bool enabled);
/**
* Triggered when Push-to-Talk has been enabled or disabled for desktop mode.
* @function Audio.pushToTalkDesktopChanged
* @param {boolean} enabled - <code>true</code> if Push-to-Talk is emabled for Desktop mode, otherwise <code>false</code>.
* @returns {Signal}
*/
void pushToTalkDesktopChanged(bool enabled);
/**
* Triggered when Push-to-Talk has been enabled or disabled for HMD mode.
* @function Audio.pushToTalkHMDChanged
* @param {boolean} enabled - <code>true</code> if Push-to-Talk is emabled for HMD mode, otherwise <code>false</code>.
* @returns {Signal}
*/
void pushToTalkHMDChanged(bool enabled);
/**jsdoc
* Triggered when the audio input noise reduction is enabled or disabled.
* @function Audio.noiseReductionChanged
@ -201,12 +273,20 @@ signals:
*/
void noiseReductionChanged(bool isEnabled);
/**jsdoc
* Triggered when "warn when muted" is enabled or disabled.
* @function Audio.warnWhenMutedChanged
* @param {boolean} isEnabled - <code>true</code> if "warn when muted" is enabled, otherwise <code>false</code>.
* @returns {Signal}
*/
void warnWhenMutedChanged(bool isEnabled);
/**jsdoc
* Triggered when the input audio volume changes.
* @function Audio.inputVolumeChanged
* @param {number} volume - The requested volume to be applied to the audio input, range <code>0.0</code> &ndash;
* <code>1.0</code>. The resulting value of <code>Audio.inputVolume</code> depends on the capabilities of the device:
* for example, the volume can't be changed on some devices, and others might only support values of <code>0.0</code>
* @param {number} volume - The requested volume to be applied to the audio input, range <code>0.0</code> &ndash;
* <code>1.0</code>. The resulting value of <code>Audio.inputVolume</code> depends on the capabilities of the device:
* for example, the volume can't be changed on some devices, and others might only support values of <code>0.0</code>
* and <code>1.0</code>.
* @returns {Signal}
*/
@ -215,7 +295,7 @@ signals:
/**jsdoc
* Triggered when the input audio level changes.
* @function Audio.inputLevelChanged
* @param {number} level - The loudness of the input audio, range <code>0.0</code> (no sound) &ndash; <code>1.0</code> (the
* @param {number} level - The loudness of the input audio, range <code>0.0</code> (no sound) &ndash; <code>1.0</code> (the
* onset of clipping).
* @returns {Signal}
*/
@ -237,6 +317,14 @@ signals:
*/
void contextChanged(const QString& context);
/**jsdoc
* Triggered when pushing to talk.
* @function Audio.pushingToTalkChanged
* @param {boolean} talking - <code>true</code> if broadcasting with PTT, <code>false</code> otherwise.
* @returns {Signal}
*/
void pushingToTalkChanged(bool talking);
public slots:
/**jsdoc
@ -245,9 +333,12 @@ public slots:
*/
void onContextChanged();
void handlePushedToTalk(bool enabled);
private slots:
void setMuted(bool muted);
void enableNoiseReduction(bool enable);
void enableWarnWhenMuted(bool enable);
void setInputVolume(float volume);
void onInputLoudnessChanged(float loudness, bool isClipping);
@ -260,11 +351,20 @@ private:
float _inputVolume { 1.0f };
float _inputLevel { 0.0f };
bool _isClipping { false };
bool _isMuted { false };
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
bool _enableWarnWhenMuted { true };
bool _contextIsHMD { false };
AudioDevices* getDevices() { return &_devices; }
AudioDevices _devices;
Setting::Handle<bool> _desktopMutedSetting{ QStringList { Audio::AUDIO, "desktopMuted" }, true };
Setting::Handle<bool> _hmdMutedSetting{ QStringList { Audio::AUDIO, "hmdMuted" }, true };
Setting::Handle<bool> _pttDesktopSetting{ QStringList { Audio::AUDIO, "pushToTalkDesktop" }, false };
Setting::Handle<bool> _pttHMDSetting{ QStringList { Audio::AUDIO, "pushToTalkHMD" }, false };
bool _desktopMuted{ true };
bool _hmdMuted{ false };
bool _pttDesktop{ false };
bool _pttHMD{ false };
bool _pushingToTalk{ false };
};
};

View file

@ -7,7 +7,7 @@
//
#include "PlatformInfoScriptingInterface.h"
#include "Application.h"
#include <shared/GlobalAppProperties.h>
#include <thread>
#ifdef Q_OS_WIN
@ -138,6 +138,14 @@ bool PlatformInfoScriptingInterface::has3DHTML() {
#if defined(Q_OS_ANDROID)
return false;
#else
return true;
return !qApp->property(hifi::properties::STANDALONE).toBool();
#endif
}
bool PlatformInfoScriptingInterface::isStandalone() {
#if defined(Q_OS_ANDROID)
return false;
#else
return qApp->property(hifi::properties::STANDALONE).toBool();
#endif
}

View file

@ -68,9 +68,15 @@ public slots:
/**jsdoc
* Returns true if device supports 3d HTML
* @function Window.hasRift
* @function Window.has3DHTML
* @returns {boolean} <code>true</code> if device supports 3d HTML, otherwise <code>false</code>.*/
bool has3DHTML();
/**jsdoc
* Returns true if device is standalone
* @function Window.hasRift
* @returns {boolean} <code>true</code> if device is a standalone device, otherwise <code>false</code>.*/
bool isStandalone();
};
#endif // hifi_PlatformInfoScriptingInterface_h

View file

@ -15,6 +15,7 @@
#include <OffscreenUi.h>
#include <Preferences.h>
#include <RenderShadowTask.h>
#include <plugins/PluginUtils.h>
#include <display-plugins/CompositorHelper.h>
#include "Application.h"

View file

@ -125,8 +125,10 @@ void Stats::updateStats(bool force) {
auto avatarManager = DependencyManager::get<AvatarManager>();
// we need to take one avatar out so we don't include ourselves
STAT_UPDATE(avatarCount, avatarManager->size() - 1);
STAT_UPDATE(heroAvatarCount, avatarManager->getNumHeroAvatars());
STAT_UPDATE(physicsObjectCount, qApp->getNumCollisionObjects());
STAT_UPDATE(updatedAvatarCount, avatarManager->getNumAvatarsUpdated());
STAT_UPDATE(updatedHeroAvatarCount, avatarManager->getNumHeroAvatarsUpdated());
STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated());
STAT_UPDATE(serverCount, (int)nodeList->size());
STAT_UPDATE_FLOAT(renderrate, qApp->getRenderLoopRate(), 0.1f);

View file

@ -49,8 +49,10 @@ private: \
* @property {number} presentdroprate - <em>Read-only.</em>
* @property {number} gameLoopRate - <em>Read-only.</em>
* @property {number} avatarCount - <em>Read-only.</em>
* @property {number} heroAvatarCount - <em>Read-only.</em>
* @property {number} physicsObjectCount - <em>Read-only.</em>
* @property {number} updatedAvatarCount - <em>Read-only.</em>
* @property {number} updatedHeroAvatarCount - <em>Read-only.</em>
* @property {number} notUpdatedAvatarCount - <em>Read-only.</em>
* @property {number} packetInCount - <em>Read-only.</em>
* @property {number} packetOutCount - <em>Read-only.</em>
@ -203,8 +205,10 @@ class Stats : public QQuickItem {
STATS_PROPERTY(float, presentdroprate, 0)
STATS_PROPERTY(int, gameLoopRate, 0)
STATS_PROPERTY(int, avatarCount, 0)
STATS_PROPERTY(int, heroAvatarCount, 0)
STATS_PROPERTY(int, physicsObjectCount, 0)
STATS_PROPERTY(int, updatedAvatarCount, 0)
STATS_PROPERTY(int, updatedHeroAvatarCount, 0)
STATS_PROPERTY(int, notUpdatedAvatarCount, 0)
STATS_PROPERTY(int, packetInCount, 0)
STATS_PROPERTY(int, packetOutCount, 0)
@ -436,6 +440,13 @@ signals:
*/
void avatarCountChanged();
/**jsdoc
* Triggered when the value of the <code>heroAvatarCount</code> property changes.
* @function Stats.heroAvatarCountChanged
* @returns {Signal}
*/
void heroAvatarCountChanged();
/**jsdoc
* Triggered when the value of the <code>updatedAvatarCount</code> property changes.
* @function Stats.updatedAvatarCountChanged
@ -443,6 +454,13 @@ signals:
*/
void updatedAvatarCountChanged();
/**jsdoc
* Triggered when the value of the <code>updatedHeroAvatarCount</code> property changes.
* @function Stats.updatedHeroAvatarCountChanged
* @returns {Signal}
*/
void updatedHeroAvatarCountChanged();
/**jsdoc
* Triggered when the value of the <code>notUpdatedAvatarCount</code> property changes.
* @function Stats.notUpdatedAvatarCountChanged

View file

@ -463,6 +463,7 @@ void Flow::calculateConstraints(const std::shared_ptr<AnimSkeleton>& skeleton,
auto flowPrefix = FLOW_JOINT_PREFIX.toUpper();
auto simPrefix = SIM_JOINT_PREFIX.toUpper();
std::vector<int> handsIndices;
_groupSettings.clear();
for (int i = 0; i < skeleton->getNumJoints(); i++) {
auto name = skeleton->getJointName(i);
@ -509,6 +510,7 @@ void Flow::calculateConstraints(const std::shared_ptr<AnimSkeleton>& skeleton,
auto flowJoint = FlowJoint(i, parentIndex, -1, name, group, jointSettings);
_flowJointData.insert(std::pair<int, FlowJoint>(i, flowJoint));
}
updateGroupSettings(group, jointSettings);
}
} else {
if (PRESET_COLLISION_DATA.find(name) != PRESET_COLLISION_DATA.end()) {
@ -727,6 +729,7 @@ void Flow::setPhysicsSettingsForGroup(const QString& group, const FlowPhysicsSet
joint.second.setSettings(settings);
}
}
updateGroupSettings(group, settings);
}
bool Flow::getJointPositionInWorldFrame(const AnimPoseVec& absolutePoses, int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const {
@ -780,4 +783,12 @@ Flow& Flow::operator=(const Flow& otherFlow) {
}
}
return *this;
}
void Flow::updateGroupSettings(const QString& group, const FlowPhysicsSettings& settings) {
if (_groupSettings.find(group) == _groupSettings.end()) {
_groupSettings.insert(std::pair<QString, FlowPhysicsSettings>(group, settings));
} else {
_groupSettings[group] = settings;
}
}

View file

@ -149,6 +149,7 @@ public:
void setCollisionSettingsByJoint(int jointIndex, const FlowCollisionSettings& settings);
void setActive(bool active) { _active = active; }
bool getActive() const { return _active; }
const std::vector<FlowCollisionSphere>& getCollisions() const { return _selfCollisions; }
protected:
std::vector<FlowCollisionSphere> _selfCollisions;
std::vector<FlowCollisionSphere> _othersCollisions;
@ -221,6 +222,7 @@ public:
const glm::quat& getCurrentRotation() const { return _currentRotation; }
const glm::vec3& getCurrentTranslation() const { return _initialTranslation; }
const glm::vec3& getInitialPosition() const { return _initialPosition; }
bool isColliding() const { return _colliding; }
protected:
@ -293,6 +295,7 @@ public:
void setOthersCollision(const QUuid& otherId, int jointIndex, const glm::vec3& position);
FlowCollisionSystem& getCollisionSystem() { return _collisionSystem; }
void setPhysicsSettingsForGroup(const QString& group, const FlowPhysicsSettings& settings);
const std::map<QString, FlowPhysicsSettings>& getGroupSettings() const { return _groupSettings; }
void cleanUp();
signals:
@ -309,6 +312,7 @@ private:
void setJoints(AnimPoseVec& relativePoses, const std::vector<bool>& overrideFlags);
void updateJoints(AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses);
bool updateRootFramePositions(const AnimPoseVec& absolutePoses, size_t threadIndex);
void updateGroupSettings(const QString& group, const FlowPhysicsSettings& settings);
void setScale(float scale);
float _scale { 1.0f };
@ -316,6 +320,7 @@ private:
glm::vec3 _entityPosition;
glm::quat _entityRotation;
std::map<int, FlowJoint> _flowJointData;
std::map<QString, FlowPhysicsSettings> _groupSettings;
std::vector<FlowThread> _jointThreads;
std::vector<QString> _flowJointKeywords;
FlowCollisionSystem _collisionSystem;

View file

@ -2397,3 +2397,52 @@ void Rig::initFlow(bool isActive) {
_networkFlow.cleanUp();
}
}
float Rig::getUnscaledEyeHeight() const {
// Normally the model offset transform will contain the avatar scale factor, we explicitly remove it here.
AnimPose modelOffsetWithoutAvatarScale(glm::vec3(1.0f), getModelOffsetPose().rot(), getModelOffsetPose().trans());
AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * getGeometryOffsetPose();
// This factor can be used to scale distances in the geometry frame into the unscaled rig frame.
// Typically it will be the unit conversion from cm to m.
float scaleFactor = geomToRigWithoutAvatarScale.scale().x; // in practice this always a uniform scale factor.
int headTopJoint = indexOfJoint("HeadTop_End");
int headJoint = indexOfJoint("Head");
int eyeJoint = indexOfJoint("LeftEye") != -1 ? indexOfJoint("LeftEye") : indexOfJoint("RightEye");
int toeJoint = indexOfJoint("LeftToeBase") != -1 ? indexOfJoint("LeftToeBase") : indexOfJoint("RightToeBase");
// Makes assumption that the y = 0 plane in geometry is the ground plane.
// We also make that assumption in Rig::computeAvatarBoundingCapsule()
const float GROUND_Y = 0.0f;
// Values from the skeleton are in the geometry coordinate frame.
auto skeleton = getAnimSkeleton();
if (eyeJoint >= 0 && toeJoint >= 0) {
// Measure from eyes to toes.
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y;
return scaleFactor * eyeHeight;
} else if (eyeJoint >= 0) {
// Measure Eye joint to y = 0 plane.
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - GROUND_Y;
return scaleFactor * eyeHeight;
} else if (headTopJoint >= 0 && toeJoint >= 0) {
// Measure from ToeBase joint to HeadTop_End joint, then remove forehead distance.
const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT;
float height = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y;
return scaleFactor * (height - height * ratio);
} else if (headTopJoint >= 0) {
// Measure from HeadTop_End joint to the ground, then remove forehead distance.
const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT;
float headHeight = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - GROUND_Y;
return scaleFactor * (headHeight - headHeight * ratio);
} else if (headJoint >= 0) {
// Measure Head joint to the ground, then add in distance from neck to eye.
const float DEFAULT_AVATAR_NECK_TO_EYE = DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD - DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD;
const float ratio = DEFAULT_AVATAR_NECK_TO_EYE / DEFAULT_AVATAR_NECK_HEIGHT;
float neckHeight = skeleton->getAbsoluteDefaultPose(headJoint).trans().y - GROUND_Y;
return scaleFactor * (neckHeight + neckHeight * ratio);
} else {
return DEFAULT_AVATAR_EYE_HEIGHT;
}
}

View file

@ -26,6 +26,7 @@
#include "SimpleMovingAverage.h"
#include "AnimUtil.h"
#include "Flow.h"
#include "AvatarConstants.h"
class Rig;
class AnimInverseKinematics;
@ -237,6 +238,8 @@ public:
void initFlow(bool isActive);
Flow& getFlow() { return _internalFlow; }
float getUnscaledEyeHeight() const;
signals:
void onLoadComplete();

View file

@ -1531,6 +1531,14 @@ void AudioClient::setNoiseReduction(bool enable, bool emitSignal) {
}
}
void AudioClient::setWarnWhenMuted(bool enable, bool emitSignal) {
if (_warnWhenMuted != enable) {
_warnWhenMuted = enable;
if (emitSignal) {
emit warnWhenMutedChanged(_warnWhenMuted);
}
}
}
bool AudioClient::setIsStereoInput(bool isStereoInput) {
bool stereoInputChanged = false;

View file

@ -210,13 +210,16 @@ public slots:
void setNoiseReduction(bool isNoiseGateEnabled, bool emitSignal = true);
bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; }
bool getLocalEcho() { return _shouldEchoLocally; }
void setLocalEcho(bool localEcho) { _shouldEchoLocally = localEcho; }
void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; }
void setWarnWhenMuted(bool isNoiseGateEnabled, bool emitSignal = true);
bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; }
bool getServerEcho() { return _shouldEchoToServer; }
void setServerEcho(bool serverEcho) { _shouldEchoToServer = serverEcho; }
void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; }
virtual bool getLocalEcho() override { return _shouldEchoLocally; }
virtual void setLocalEcho(bool localEcho) override { _shouldEchoLocally = localEcho; }
virtual void toggleLocalEcho() override { _shouldEchoLocally = !_shouldEchoLocally; }
virtual bool getServerEcho() override { return _shouldEchoToServer; }
virtual void setServerEcho(bool serverEcho) override { _shouldEchoToServer = serverEcho; }
virtual void toggleServerEcho() override { _shouldEchoToServer = !_shouldEchoToServer; }
void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer);
void sendMuteEnvironmentPacket();
@ -246,6 +249,7 @@ signals:
void inputVolumeChanged(float volume);
void muteToggled(bool muted);
void noiseReductionChanged(bool noiseReductionEnabled);
void warnWhenMutedChanged(bool warnWhenMutedEnabled);
void mutedByMixer();
void inputReceived(const QByteArray& inputSamples);
void inputLoudnessChanged(float loudness, bool isClipping);
@ -365,6 +369,7 @@ private:
bool _shouldEchoLocally;
bool _shouldEchoToServer;
bool _isNoiseGateEnabled;
bool _warnWhenMuted;
bool _reverb;
AudioEffectOptions _scriptReverbOptions;

View file

@ -45,9 +45,16 @@ public slots:
virtual bool shouldLoopbackInjectors() { return false; }
virtual bool setIsStereoInput(bool stereo) = 0;
virtual bool isStereoInput() = 0;
virtual bool getLocalEcho() = 0;
virtual void setLocalEcho(bool localEcho) = 0;
virtual void toggleLocalEcho() = 0;
virtual bool getServerEcho() = 0;
virtual void setServerEcho(bool serverEcho) = 0;
virtual void toggleServerEcho() = 0;
signals:
void isStereoInputChanged(bool isStereo);
};

View file

@ -2040,54 +2040,7 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const {
// TODO: if performance becomes a concern we can cache this value rather then computing it everytime.
if (_skeletonModel) {
auto& rig = _skeletonModel->getRig();
// Normally the model offset transform will contain the avatar scale factor, we explicitly remove it here.
AnimPose modelOffsetWithoutAvatarScale(glm::vec3(1.0f), rig.getModelOffsetPose().rot(), rig.getModelOffsetPose().trans());
AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * rig.getGeometryOffsetPose();
// This factor can be used to scale distances in the geometry frame into the unscaled rig frame.
// Typically it will be the unit conversion from cm to m.
float scaleFactor = geomToRigWithoutAvatarScale.scale().x; // in practice this always a uniform scale factor.
int headTopJoint = rig.indexOfJoint("HeadTop_End");
int headJoint = rig.indexOfJoint("Head");
int eyeJoint = rig.indexOfJoint("LeftEye") != -1 ? rig.indexOfJoint("LeftEye") : rig.indexOfJoint("RightEye");
int toeJoint = rig.indexOfJoint("LeftToeBase") != -1 ? rig.indexOfJoint("LeftToeBase") : rig.indexOfJoint("RightToeBase");
// Makes assumption that the y = 0 plane in geometry is the ground plane.
// We also make that assumption in Rig::computeAvatarBoundingCapsule()
const float GROUND_Y = 0.0f;
// Values from the skeleton are in the geometry coordinate frame.
auto skeleton = rig.getAnimSkeleton();
if (eyeJoint >= 0 && toeJoint >= 0) {
// Measure from eyes to toes.
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y;
return scaleFactor * eyeHeight;
} else if (eyeJoint >= 0) {
// Measure Eye joint to y = 0 plane.
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - GROUND_Y;
return scaleFactor * eyeHeight;
} else if (headTopJoint >= 0 && toeJoint >= 0) {
// Measure from ToeBase joint to HeadTop_End joint, then remove forehead distance.
const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT;
float height = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y;
return scaleFactor * (height - height * ratio);
} else if (headTopJoint >= 0) {
// Measure from HeadTop_End joint to the ground, then remove forehead distance.
const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT;
float headHeight = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - GROUND_Y;
return scaleFactor * (headHeight - headHeight * ratio);
} else if (headJoint >= 0) {
// Measure Head joint to the ground, then add in distance from neck to eye.
const float DEFAULT_AVATAR_NECK_TO_EYE = DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD - DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD;
const float ratio = DEFAULT_AVATAR_NECK_TO_EYE / DEFAULT_AVATAR_NECK_HEIGHT;
float neckHeight = skeleton->getAbsoluteDefaultPose(headJoint).trans().y - GROUND_Y;
return scaleFactor * (neckHeight + neckHeight * ratio);
} else {
return DEFAULT_AVATAR_EYE_HEIGHT;
}
return _skeletonModel->getRig().getUnscaledEyeHeight();
} else {
return DEFAULT_AVATAR_EYE_HEIGHT;
}

View file

@ -564,6 +564,11 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
setAtBit16(flags, COLLIDE_WITH_OTHER_AVATARS);
}
// Avatar has hero priority
if (getHasPriority()) {
setAtBit16(flags, HAS_HERO_PRIORITY);
}
data->flags = flags;
destinationBuffer += sizeof(AvatarDataPacket::AdditionalFlags);
@ -1152,7 +1157,8 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
auto newHasProceduralEyeFaceMovement = oneAtBit16(bitItems, PROCEDURAL_EYE_FACE_MOVEMENT);
auto newHasProceduralBlinkFaceMovement = oneAtBit16(bitItems, PROCEDURAL_BLINK_FACE_MOVEMENT);
auto newCollideWithOtherAvatars = oneAtBit16(bitItems, COLLIDE_WITH_OTHER_AVATARS);
auto newHasPriority = oneAtBit16(bitItems, HAS_HERO_PRIORITY);
bool keyStateChanged = (_keyState != newKeyState);
bool handStateChanged = (_handState != newHandState);
bool faceStateChanged = (_headData->_isFaceTrackerConnected != newFaceTrackerConnected);
@ -1161,8 +1167,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
bool proceduralEyeFaceMovementChanged = (_headData->getHasProceduralEyeFaceMovement() != newHasProceduralEyeFaceMovement);
bool proceduralBlinkFaceMovementChanged = (_headData->getHasProceduralBlinkFaceMovement() != newHasProceduralBlinkFaceMovement);
bool collideWithOtherAvatarsChanged = (_collideWithOtherAvatars != newCollideWithOtherAvatars);
bool hasPriorityChanged = (getHasPriority() != newHasPriority);
bool somethingChanged = keyStateChanged || handStateChanged || faceStateChanged || eyeStateChanged || audioEnableFaceMovementChanged ||
proceduralEyeFaceMovementChanged || proceduralBlinkFaceMovementChanged || collideWithOtherAvatarsChanged;
proceduralEyeFaceMovementChanged ||
proceduralBlinkFaceMovementChanged || collideWithOtherAvatarsChanged || hasPriorityChanged;
_keyState = newKeyState;
_handState = newHandState;
@ -1172,6 +1180,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
_headData->setHasProceduralEyeFaceMovement(newHasProceduralEyeFaceMovement);
_headData->setHasProceduralBlinkFaceMovement(newHasProceduralBlinkFaceMovement);
_collideWithOtherAvatars = newCollideWithOtherAvatars;
setHasPriorityWithoutTimestampReset(newHasPriority);
sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags);

View file

@ -104,6 +104,9 @@ const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
// Procedural audio to mouth movement is enabled 8th bit
// Procedural Blink is enabled 9th bit
// Procedural Eyelid is enabled 10th bit
// Procedural PROCEDURAL_BLINK_FACE_MOVEMENT is enabled 11th bit
// Procedural Collide with other avatars is enabled 12th bit
// Procedural Has Hero Priority is enabled 13th bit
const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits
const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits
@ -115,6 +118,7 @@ const int AUDIO_ENABLED_FACE_MOVEMENT = 8; // 9th bit
const int PROCEDURAL_EYE_FACE_MOVEMENT = 9; // 10th bit
const int PROCEDURAL_BLINK_FACE_MOVEMENT = 10; // 11th bit
const int COLLIDE_WITH_OTHER_AVATARS = 11; // 12th bit
const int HAS_HERO_PRIORITY = 12; // 13th bit (be scared)
/**jsdoc
* <p>The pointing state of the hands is specified by the following values:
@ -1270,6 +1274,18 @@ public:
int getAverageBytesReceivedPerSecond() const;
int getReceiveRate() const;
// An Avatar can be set Priority from the AvatarMixer side.
bool getHasPriority() const { return _hasPriority; }
// regular setHasPriority does a check of state changed and if true reset 'additionalFlagsChanged' timestamp
void setHasPriority(bool hasPriority) {
if (_hasPriority != hasPriority) {
_additionalFlagsChanged = usecTimestampNow();
_hasPriority = hasPriority;
}
}
// In some cases, we want to assign the hasPRiority flag without reseting timestamp
void setHasPriorityWithoutTimestampReset(bool hasPriority) { _hasPriority = hasPriority; }
const glm::vec3& getTargetVelocity() const { return _targetVelocity; }
void clearRecordingBasis();
@ -1680,6 +1696,7 @@ protected:
bool _isNewAvatar { true };
bool _isClientAvatar { false };
bool _collideWithOtherAvatars { true };
bool _hasPriority{ false };
// null unless MyAvatar or ScriptableAvatar sending traits data to mixer
std::unique_ptr<ClientTraitsHandler, LaterDeleter> _clientTraitsHandler;

View file

@ -180,6 +180,7 @@ namespace controller {
* third person, to full screen mirror, then back to first person and repeat.</td></tr>
* <tr><td><code>ContextMenu</code></td><td>number</td><td>number</td><td>Show / hide the tablet.</td></tr>
* <tr><td><code>ToggleMute</code></td><td>number</td><td>number</td><td>Toggle the microphone mute.</td></tr>
* <tr><td><code>TogglePushToTalk</code></td><td>number</td><td>number</td><td>Toggle push to talk.</td></tr>
* <tr><td><code>ToggleOverlay</code></td><td>number</td><td>number</td><td>Toggle the display of overlays.</td></tr>
* <tr><td><code>Sprint</code></td><td>number</td><td>number</td><td>Set avatar sprint mode.</td></tr>
* <tr><td><code>ReticleClick</code></td><td>number</td><td>number</td><td>Set mouse-pressed.</td></tr>
@ -245,6 +246,8 @@ namespace controller {
* <code>ContextMenu</code> instead.</td></tr>
* <tr><td><code>TOGGLE_MUTE</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
* <code>ToggleMute</code> instead.</td></tr>
* <tr><td><code>TOGGLE_PUSHTOTALK</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
* <code>TogglePushToTalk</code> instead.</td></tr>
* <tr><td><code>SPRINT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
* <code>Sprint</code> instead.</td></tr>
* <tr><td><code>LONGITUDINAL_BACKWARD</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
@ -411,6 +414,7 @@ namespace controller {
makeButtonPair(Action::ACTION2, "SecondaryAction"),
makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"),
makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"),
makeButtonPair(Action::TOGGLE_PUSHTOTALK, "TogglePushToTalk"),
makeButtonPair(Action::CYCLE_CAMERA, "CycleCamera"),
makeButtonPair(Action::TOGGLE_OVERLAY, "ToggleOverlay"),
makeButtonPair(Action::SPRINT, "Sprint"),

View file

@ -60,6 +60,7 @@ enum class Action {
CONTEXT_MENU,
TOGGLE_MUTE,
TOGGLE_PUSHTOTALK,
CYCLE_CAMERA,
TOGGLE_OVERLAY,

View file

@ -181,9 +181,11 @@ void RenderableModelEntityItem::updateModelBounds() {
updateRenderItems = true;
}
if (model->getScaleToFitDimensions() != getScaledDimensions() ||
model->getRegistrationPoint() != getRegistrationPoint() ||
!model->getIsScaledToFit()) {
bool overridingModelTransform = model->isOverridingModelTransformAndOffset();
if (!overridingModelTransform &&
(model->getScaleToFitDimensions() != getScaledDimensions() ||
model->getRegistrationPoint() != getRegistrationPoint() ||
!model->getIsScaledToFit())) {
// The machinery for updateModelBounds will give existing models the opportunity to fix their
// translation/rotation/scale/registration. The first two are straightforward, but the latter two
// have guards to make sure they don't happen after they've already been set. Here we reset those guards.

View file

@ -19,6 +19,8 @@
#include <PerfStat.h>
#include <shaders/Shaders.h>
#include <DisableDeferred.h>
#include "paintStroke_Shared.slh"
using namespace render;
@ -29,13 +31,6 @@ gpu::PipelinePointer PolyLineEntityRenderer::_glowPipeline = nullptr;
static const QUrl DEFAULT_POLYLINE_TEXTURE = PathUtils::resourcesUrl("images/paintStroke.png");
#if defined(USE_GLES)
static bool DISABLE_DEFERRED = true;
#else
static const QString RENDER_FORWARD{ "HIFI_RENDER_FORWARD" };
static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
#endif
PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {
_texture = DependencyManager::get<TextureCache>()->getTexture(DEFAULT_POLYLINE_TEXTURE);

View file

@ -19,6 +19,8 @@
#include "RenderPipelines.h"
#include <DisableDeferred.h>
//#define SHAPE_ENTITY_USE_FADE_EFFECT
#ifdef SHAPE_ENTITY_USE_FADE_EFFECT
#include <FadeEffect.h>
@ -30,13 +32,6 @@ using namespace render::entities;
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
static const float SPHERE_ENTITY_SCALE = 0.5f;
#if defined(USE_GLES)
static bool DISABLE_DEFERRED = true;
#else
static const QString RENDER_FORWARD{ "HIFI_RENDER_FORWARD" };
static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
#endif
static_assert(shader::render_utils::program::simple != 0, "Validate simple program exists");
static_assert(shader::render_utils::program::simple_transparent != 0, "Validate simple transparent program exists");

View file

@ -19,6 +19,8 @@
#include "GLMHelpers.h"
#include <DisableDeferred.h>
using namespace render;
using namespace render::entities;
@ -160,6 +162,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
glm::vec4 backgroundColor;
Transform modelTransform;
glm::vec3 dimensions;
bool forwardRendered;
withReadLock([&] {
modelTransform = _renderTransform;
dimensions = _dimensions;
@ -169,6 +172,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
textColor = EntityRenderer::calculatePulseColor(textColor, _pulseProperties, _created);
backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha);
backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created);
forwardRendered = _renderLayer != RenderLayer::WORLD || DISABLE_DEFERRED;
});
// Render background
@ -188,7 +192,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
if (backgroundColor.a > 0.0f) {
batch.setModelTransform(transformToTopLeft);
auto geometryCache = DependencyManager::get<GeometryCache>();
geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false);
geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false, true, forwardRendered);
geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID);
}
@ -199,7 +203,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
batch.setModelTransform(transformToTopLeft);
glm::vec2 bounds = glm::vec2(dimensions.x - (_leftMargin + _rightMargin), dimensions.y - (_topMargin + _bottomMargin));
_textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale);
_textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale, forwardRendered);
}
}

View file

@ -147,6 +147,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_EDITION_NUMBER;
requestedProperties += PROP_ENTITY_INSTANCE_NUMBER;
requestedProperties += PROP_CERTIFICATE_ID;
requestedProperties += PROP_CERTIFICATE_TYPE;
requestedProperties += PROP_STATIC_CERTIFICATE_VERSION;
return requestedProperties;
@ -337,6 +338,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_EDITION_NUMBER, getEditionNumber());
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, getEntityInstanceNumber());
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, getCertificateID());
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_TYPE, getCertificateType());
APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, getStaticCertificateVersion());
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
@ -942,6 +944,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_EDITION_NUMBER, quint32, setEditionNumber);
READ_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, quint32, setEntityInstanceNumber);
READ_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, QString, setCertificateID);
READ_ENTITY_PROPERTY(PROP_CERTIFICATE_TYPE, QString, setCertificateType);
READ_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion);
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
@ -1381,6 +1384,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire
COPY_ENTITY_PROPERTY_TO_PROPERTIES(editionNumber, getEditionNumber);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(entityInstanceNumber, getEntityInstanceNumber);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(certificateID, getCertificateID);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(certificateType, getCertificateType);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(staticCertificateVersion, getStaticCertificateVersion);
// Script local data
@ -1529,6 +1533,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(editionNumber, setEditionNumber);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(entityInstanceNumber, setEntityInstanceNumber);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(certificateID, setCertificateID);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(certificateType, setCertificateType);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(staticCertificateVersion, setStaticCertificateVersion);
if (updateQueryAACube()) {
@ -1918,25 +1923,19 @@ void EntityItem::setRotation(glm::quat rotation) {
void EntityItem::setVelocity(const glm::vec3& value) {
glm::vec3 velocity = getLocalVelocity();
if (velocity != value) {
if (getShapeType() == SHAPE_TYPE_STATIC_MESH) {
if (velocity != Vectors::ZERO) {
setLocalVelocity(Vectors::ZERO);
}
} else {
float speed = glm::length(value);
if (!glm::isnan(speed)) {
const float MIN_LINEAR_SPEED = 0.001f;
const float MAX_LINEAR_SPEED = 270.0f; // 3m per step at 90Hz
if (speed < MIN_LINEAR_SPEED) {
velocity = ENTITY_ITEM_ZERO_VEC3;
} else if (speed > MAX_LINEAR_SPEED) {
velocity = (MAX_LINEAR_SPEED / speed) * value;
} else {
velocity = value;
}
setLocalVelocity(velocity);
_flags |= Simulation::DIRTY_LINEAR_VELOCITY;
float speed = glm::length(value);
if (!glm::isnan(speed)) {
const float MIN_LINEAR_SPEED = 0.001f;
const float MAX_LINEAR_SPEED = 270.0f; // 3m per step at 90Hz
if (speed < MIN_LINEAR_SPEED) {
velocity = ENTITY_ITEM_ZERO_VEC3;
} else if (speed > MAX_LINEAR_SPEED) {
velocity = (MAX_LINEAR_SPEED / speed) * value;
} else {
velocity = value;
}
setLocalVelocity(velocity);
_flags |= Simulation::DIRTY_LINEAR_VELOCITY;
}
}
}
@ -1954,19 +1953,15 @@ void EntityItem::setDamping(float value) {
void EntityItem::setGravity(const glm::vec3& value) {
withWriteLock([&] {
if (_gravity != value) {
if (getShapeType() == SHAPE_TYPE_STATIC_MESH) {
_gravity = Vectors::ZERO;
} else {
float magnitude = glm::length(value);
if (!glm::isnan(magnitude)) {
const float MAX_ACCELERATION_OF_GRAVITY = 10.0f * 9.8f; // 10g
if (magnitude > MAX_ACCELERATION_OF_GRAVITY) {
_gravity = (MAX_ACCELERATION_OF_GRAVITY / magnitude) * value;
} else {
_gravity = value;
}
_flags |= Simulation::DIRTY_LINEAR_VELOCITY;
float magnitude = glm::length(value);
if (!glm::isnan(magnitude)) {
const float MAX_ACCELERATION_OF_GRAVITY = 10.0f * 9.8f; // 10g
if (magnitude > MAX_ACCELERATION_OF_GRAVITY) {
_gravity = (MAX_ACCELERATION_OF_GRAVITY / magnitude) * value;
} else {
_gravity = value;
}
_flags |= Simulation::DIRTY_LINEAR_VELOCITY;
}
}
});
@ -1975,23 +1970,19 @@ void EntityItem::setGravity(const glm::vec3& value) {
void EntityItem::setAngularVelocity(const glm::vec3& value) {
glm::vec3 angularVelocity = getLocalAngularVelocity();
if (angularVelocity != value) {
if (getShapeType() == SHAPE_TYPE_STATIC_MESH) {
setLocalAngularVelocity(Vectors::ZERO);
} else {
float speed = glm::length(value);
if (!glm::isnan(speed)) {
const float MIN_ANGULAR_SPEED = 0.0002f;
const float MAX_ANGULAR_SPEED = 9.0f * TWO_PI; // 1/10 rotation per step at 90Hz
if (speed < MIN_ANGULAR_SPEED) {
angularVelocity = ENTITY_ITEM_ZERO_VEC3;
} else if (speed > MAX_ANGULAR_SPEED) {
angularVelocity = (MAX_ANGULAR_SPEED / speed) * value;
} else {
angularVelocity = value;
}
setLocalAngularVelocity(angularVelocity);
_flags |= Simulation::DIRTY_ANGULAR_VELOCITY;
float speed = glm::length(value);
if (!glm::isnan(speed)) {
const float MIN_ANGULAR_SPEED = 0.0002f;
const float MAX_ANGULAR_SPEED = 9.0f * TWO_PI; // 1/10 rotation per step at 90Hz
if (speed < MIN_ANGULAR_SPEED) {
angularVelocity = ENTITY_ITEM_ZERO_VEC3;
} else if (speed > MAX_ANGULAR_SPEED) {
angularVelocity = (MAX_ANGULAR_SPEED / speed) * value;
} else {
angularVelocity = value;
}
setLocalAngularVelocity(angularVelocity);
_flags |= Simulation::DIRTY_ANGULAR_VELOCITY;
}
}
}
@ -3150,6 +3141,7 @@ DEFINE_PROPERTY_ACCESSOR(QString, MarketplaceID, marketplaceID)
DEFINE_PROPERTY_ACCESSOR(quint32, EditionNumber, editionNumber)
DEFINE_PROPERTY_ACCESSOR(quint32, EntityInstanceNumber, entityInstanceNumber)
DEFINE_PROPERTY_ACCESSOR(QString, CertificateID, certificateID)
DEFINE_PROPERTY_ACCESSOR(QString, CertificateType, certificateType)
DEFINE_PROPERTY_ACCESSOR(quint32, StaticCertificateVersion, staticCertificateVersion)
uint32_t EntityItem::getDirtyFlags() const {

View file

@ -369,6 +369,8 @@ public:
void setEntityInstanceNumber(const quint32&);
QString getCertificateID() const;
void setCertificateID(const QString& value);
QString getCertificateType() const;
void setCertificateType(const QString& value);
quint32 getStaticCertificateVersion() const;
void setStaticCertificateVersion(const quint32&);
@ -653,6 +655,7 @@ protected:
QString _itemLicense { ENTITY_ITEM_DEFAULT_ITEM_LICENSE };
quint32 _limitedRun { ENTITY_ITEM_DEFAULT_LIMITED_RUN };
QString _certificateID { ENTITY_ITEM_DEFAULT_CERTIFICATE_ID };
QString _certificateType { ENTITY_ITEM_DEFAULT_CERTIFICATE_TYPE };
quint32 _editionNumber { ENTITY_ITEM_DEFAULT_EDITION_NUMBER };
quint32 _entityInstanceNumber { ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER };
QString _marketplaceID { ENTITY_ITEM_DEFAULT_MARKETPLACE_ID };

View file

@ -545,6 +545,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_EDITION_NUMBER, editionNumber);
CHECK_PROPERTY_CHANGE(PROP_ENTITY_INSTANCE_NUMBER, entityInstanceNumber);
CHECK_PROPERTY_CHANGE(PROP_CERTIFICATE_ID, certificateID);
CHECK_PROPERTY_CHANGE(PROP_CERTIFICATE_TYPE, certificateType);
CHECK_PROPERTY_CHANGE(PROP_STATIC_CERTIFICATE_VERSION, staticCertificateVersion);
// Location data for scripts
@ -1644,6 +1645,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EDITION_NUMBER, editionNumber);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ENTITY_INSTANCE_NUMBER, entityInstanceNumber);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CERTIFICATE_ID, certificateID);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CERTIFICATE_TYPE, certificateType);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STATIC_CERTIFICATE_VERSION, staticCertificateVersion);
// Local props for scripts
@ -2054,6 +2056,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(editionNumber, quint32, setEditionNumber);
COPY_PROPERTY_FROM_QSCRIPTVALUE(entityInstanceNumber, quint32, setEntityInstanceNumber);
COPY_PROPERTY_FROM_QSCRIPTVALUE(certificateID, QString, setCertificateID);
COPY_PROPERTY_FROM_QSCRIPTVALUE(certificateType, QString, setCertificateType);
COPY_PROPERTY_FROM_QSCRIPTVALUE(staticCertificateVersion, quint32, setStaticCertificateVersion);
// Script location data
@ -2335,6 +2338,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(editionNumber);
COPY_PROPERTY_IF_CHANGED(entityInstanceNumber);
COPY_PROPERTY_IF_CHANGED(certificateID);
COPY_PROPERTY_IF_CHANGED(certificateType);
COPY_PROPERTY_IF_CHANGED(staticCertificateVersion);
// Local props for scripts
@ -2649,6 +2653,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
ADD_PROPERTY_TO_MAP(PROP_EDITION_NUMBER, EditionNumber, editionNumber, quint32);
ADD_PROPERTY_TO_MAP(PROP_ENTITY_INSTANCE_NUMBER, EntityInstanceNumber, entityInstanceNumber, quint32);
ADD_PROPERTY_TO_MAP(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString);
ADD_PROPERTY_TO_MAP(PROP_CERTIFICATE_TYPE, CertificateType, certificateType, QString);
ADD_PROPERTY_TO_MAP(PROP_STATIC_CERTIFICATE_VERSION, StaticCertificateVersion, staticCertificateVersion, quint32);
// Local script props
@ -3094,6 +3099,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_EDITION_NUMBER, properties.getEditionNumber());
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, properties.getEntityInstanceNumber());
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID());
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_TYPE, properties.getCertificateType());
APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, properties.getStaticCertificateVersion());
if (properties.getType() == EntityTypes::ParticleEffect) {
@ -3573,6 +3579,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EDITION_NUMBER, quint32, setEditionNumber);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ENTITY_INSTANCE_NUMBER, quint32, setEntityInstanceNumber);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CERTIFICATE_ID, QString, setCertificateID);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CERTIFICATE_TYPE, QString, setCertificateType);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion);
if (properties.getType() == EntityTypes::ParticleEffect) {
@ -3982,6 +3989,7 @@ void EntityItemProperties::markAllChanged() {
_editionNumberChanged = true;
_entityInstanceNumberChanged = true;
_certificateIDChanged = true;
_certificateTypeChanged = true;
_staticCertificateVersionChanged = true;
// Common
@ -4443,6 +4451,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (certificateIDChanged()) {
out += "certificateID";
}
if (certificateTypeChanged()) {
out += "certificateType";
}
if (staticCertificateVersionChanged()) {
out += "staticCertificateVersion";
}
@ -4879,6 +4890,9 @@ QByteArray EntityItemProperties::getStaticCertificateJSON() const {
if (!getAnimation().getURL().isEmpty()) {
json["animationURL"] = getAnimation().getURL();
}
if (staticCertificateVersion >= 3) {
ADD_STRING_PROPERTY(certificateType, CertificateType);
}
ADD_STRING_PROPERTY(collisionSoundURL, CollisionSoundURL);
ADD_STRING_PROPERTY(compoundShapeURL, CompoundShapeURL);
ADD_INT_PROPERTY(editionNumber, EditionNumber);

View file

@ -226,6 +226,7 @@ public:
DEFINE_PROPERTY_REF(PROP_EDITION_NUMBER, EditionNumber, editionNumber, quint32, ENTITY_ITEM_DEFAULT_EDITION_NUMBER);
DEFINE_PROPERTY_REF(PROP_ENTITY_INSTANCE_NUMBER, EntityInstanceNumber, entityInstanceNumber, quint32, ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER);
DEFINE_PROPERTY_REF(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString, ENTITY_ITEM_DEFAULT_CERTIFICATE_ID);
DEFINE_PROPERTY_REF(PROP_CERTIFICATE_TYPE, CertificateType, certificateType, QString, ENTITY_ITEM_DEFAULT_CERTIFICATE_TYPE);
DEFINE_PROPERTY_REF(PROP_STATIC_CERTIFICATE_VERSION, StaticCertificateVersion, staticCertificateVersion, quint32, ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION);
// these are used when bouncing location data into and out of scripts
@ -630,6 +631,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EditionNumber, editionNumber, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityInstanceNumber, entityInstanceNumber, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CertificateID, certificateID, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CertificateType, certificateType, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, StaticCertificateVersion, staticCertificateVersion, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalPosition, localPosition, "");

View file

@ -41,6 +41,7 @@ const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString("");
const quint32 ENTITY_ITEM_DEFAULT_EDITION_NUMBER = 0;
const quint32 ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER = 0;
const QString ENTITY_ITEM_DEFAULT_CERTIFICATE_ID = QString("");
const QString ENTITY_ITEM_DEFAULT_CERTIFICATE_TYPE = QString("");
const quint32 ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION = 0;
const glm::u8vec3 ENTITY_ITEM_DEFAULT_COLOR = { 255, 255, 255 };

View file

@ -98,6 +98,7 @@ enum EntityPropertyList {
PROP_EDITION_NUMBER,
PROP_ENTITY_INSTANCE_NUMBER,
PROP_CERTIFICATE_ID,
PROP_CERTIFICATE_TYPE,
PROP_STATIC_CERTIFICATE_VERSION,
// Used to convert values to and from scripts

View file

@ -42,6 +42,7 @@
static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50;
const float EntityTree::DEFAULT_MAX_TMP_ENTITY_LIFETIME = 60 * 60; // 1 hour
static const QString DOMAIN_UNLIMITED = "domainUnlimited";
EntityTree::EntityTree(bool shouldReaverage) :
Octree(shouldReaverage)
@ -300,7 +301,7 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
// Delete an already-existing entity from the tree if it has the same
// CertificateID as the entity we're trying to add.
if (!existingEntityItemID.isNull()) {
if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) {
qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID"
<< existingEntityItemID << ". Deleting existing entity.";
deleteEntity(existingEntityItemID, true);
@ -1870,7 +1871,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
failedAdd = true;
qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID()
<< "] attempted to add a certified entity with ID:" << entityItemID;
} else if (isClone && isCertified) {
} else if (isClone && isCertified && !properties.getCertificateType().contains(DOMAIN_UNLIMITED)) {
failedAdd = true;
qCDebug(entities) << "User attempted to clone certified entity from entity ID:" << entityIDToClone;
} else if (isClone && !isCloneable) {

View file

@ -1043,7 +1043,11 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
cluster.transformLink = createMat4(values);
}
}
clusters.insert(getID(object.properties), cluster);
// skip empty clusters
if (cluster.indices.size() > 0 && cluster.weights.size() > 0) {
clusters.insert(getID(object.properties), cluster);
}
} else if (object.properties.last() == "BlendShapeChannel") {
QByteArray name = object.properties.at(1).toByteArray();
@ -1482,8 +1486,8 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
}
}
// if we don't have a skinned joint, parent to the model itself
if (extracted.mesh.clusters.isEmpty()) {
// the last cluster is the root cluster
{
HFMCluster cluster;
cluster.jointIndex = modelIDs.indexOf(modelID);
if (cluster.jointIndex == -1) {
@ -1494,13 +1498,11 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
}
// whether we're skinned depends on how many clusters are attached
const HFMCluster& firstHFMCluster = extracted.mesh.clusters.at(0);
glm::mat4 inverseModelTransform = glm::inverse(modelTransform);
if (clusterIDs.size() > 1) {
// this is a multi-mesh joint
const int WEIGHTS_PER_VERTEX = 4;
int numClusterIndices = extracted.mesh.vertices.size() * WEIGHTS_PER_VERTEX;
extracted.mesh.clusterIndices.fill(0, numClusterIndices);
extracted.mesh.clusterIndices.fill(extracted.mesh.clusters.size() - 1, numClusterIndices);
QVector<float> weightAccumulators;
weightAccumulators.fill(0.0f, numClusterIndices);
@ -1510,19 +1512,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
const HFMCluster& hfmCluster = extracted.mesh.clusters.at(i);
int jointIndex = hfmCluster.jointIndex;
HFMJoint& joint = hfmModel.joints[jointIndex];
glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform;
glm::vec3 boneEnd = extractTranslation(transformJointToMesh);
glm::vec3 boneBegin = boneEnd;
glm::vec3 boneDirection;
float boneLength = 0.0f;
if (joint.parentIndex != -1) {
boneBegin = extractTranslation(inverseModelTransform * hfmModel.joints[joint.parentIndex].bindTransform);
boneDirection = boneEnd - boneBegin;
boneLength = glm::length(boneDirection);
if (boneLength > EPSILON) {
boneDirection /= boneLength;
}
}
glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform;
ShapeVertices& points = shapeVertices.at(jointIndex);
@ -1535,6 +1524,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
int newIndex = it.value();
// remember vertices with at least 1/4 weight
// FIXME: vertices with no weightpainting won't get recorded here
const float EXPANSION_WEIGHT_THRESHOLD = 0.25f;
if (weight >= EXPANSION_WEIGHT_THRESHOLD) {
// transform to joint-frame and save for later
@ -1575,20 +1565,24 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
int j = i * WEIGHTS_PER_VERTEX;
// normalize weights into uint16_t
float totalWeight = weightAccumulators[j];
for (int k = j + 1; k < j + WEIGHTS_PER_VERTEX; ++k) {
float totalWeight = 0.0f;
for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
totalWeight += weightAccumulators[k];
}
const float ALMOST_HALF = 0.499f;
if (totalWeight > 0.0f) {
const float ALMOST_HALF = 0.499f;
float weightScalingFactor = (float)(UINT16_MAX) / totalWeight;
for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
extracted.mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF);
}
} else {
extracted.mesh.clusterWeights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF);
}
}
} else {
// this is a single-mesh joint
// this is a single-joint mesh
const HFMCluster& firstHFMCluster = extracted.mesh.clusters.at(0);
int jointIndex = firstHFMCluster.jointIndex;
HFMJoint& joint = hfmModel.joints[jointIndex];

View file

@ -25,6 +25,8 @@
#include <StatTracker.h>
#include <GLMHelpers.h>
#include "TGAReader.h"
#include "ImageLogging.h"
using namespace gpu;
@ -203,6 +205,16 @@ QImage processRawImageData(QIODevice& content, const std::string& filename) {
// Help the QImage loader by extracting the image file format from the url filename ext.
// Some tga are not created properly without it.
auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);
content.open(QIODevice::ReadOnly);
if (filenameExtension == "tga") {
QImage image = image::readTGA(content);
if (!image.isNull()) {
return image;
}
content.reset();
}
QImageReader imageReader(&content, filenameExtension.c_str());
if (imageReader.canRead()) {

View file

@ -0,0 +1,202 @@
//
// TGAReader.cpp
// image/src/image
//
// Created by Ryan Huffman
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "TGAReader.h"
#include "ImageLogging.h"
#include <QIODevice>
#include <QDebug>
QImage image::readTGA(QIODevice& content) {
enum class TGAImageType : uint8_t {
NoImageData = 0,
UncompressedColorMapped = 1,
UncompressedTrueColor = 2,
UncompressedBlackWhite = 3,
RunLengthEncodedColorMapped = 9,
RunLengthEncodedTrueColor = 10,
RunLengthEncodedBlackWhite = 11,
};
struct TGAHeader {
uint8_t idLength;
uint8_t colorMapType;
TGAImageType imageType;
struct {
uint64_t firstEntryIndex : 16;
uint64_t length : 16;
uint64_t entrySize : 8;
} colorMap;
uint16_t xOrigin;
uint16_t yOrigin;
uint16_t width;
uint16_t height;
uint8_t pixelDepth;
struct {
uint8_t attributeBitsPerPixel : 4;
uint8_t reserved : 1;
uint8_t orientation : 1;
uint8_t padding : 2;
} imageDescriptor;
};
constexpr bool WANT_DEBUG { false };
constexpr qint64 TGA_HEADER_SIZE_BYTES { 18 };
// BottomLeft: 0, TopLeft: 1
constexpr uint8_t ORIENTATION_BOTTOM_LEFT { 0 };
TGAHeader header;
if (content.isSequential()) {
qWarning(imagelogging) << "TGA - Sequential devices are not supported for reading";
return QImage();
}
if (content.bytesAvailable() < TGA_HEADER_SIZE_BYTES) {
qWarning(imagelogging) << "TGA - Unexpectedly reached end of file";
return QImage();
}
content.read((char*)&header.idLength, 1);
content.read((char*)&header.colorMapType, 1);
content.read((char*)&header.imageType, 1);
content.read((char*)&header.colorMap, 5);
content.read((char*)&header.xOrigin, 2);
content.read((char*)&header.yOrigin, 2);
content.read((char*)&header.width, 2);
content.read((char*)&header.height, 2);
content.read((char*)&header.pixelDepth, 1);
content.read((char*)&header.imageDescriptor, 1);
if (WANT_DEBUG) {
qDebug(imagelogging) << "Id Length: " << (int)header.idLength;
qDebug(imagelogging) << "Color map: " << (int)header.colorMap.firstEntryIndex << header.colorMap.length << header.colorMap.entrySize;
qDebug(imagelogging) << "Color map type: " << (int)header.colorMapType;
qDebug(imagelogging) << "Image type: " << (int)header.imageType;
qDebug(imagelogging) << "Origin: " << header.xOrigin << header.yOrigin;
qDebug(imagelogging) << "Size: " << header.width << header.height;
qDebug(imagelogging) << "Depth: " << header.pixelDepth;
qDebug(imagelogging) << "Image desc: " << header.imageDescriptor.attributeBitsPerPixel << (int)header.imageDescriptor.orientation;
}
if (header.xOrigin != 0 || header.yOrigin != 0) {
qWarning(imagelogging) << "TGA - origin not supporter";
return QImage();
}
if (!(header.pixelDepth == 24 && header.imageDescriptor.attributeBitsPerPixel == 0) && header.pixelDepth != 32) {
qWarning(imagelogging) << "TGA - Only pixel depths of 24 (with no alpha) and 32 bits are supported";
return QImage();
}
if (header.imageDescriptor.attributeBitsPerPixel != 0 && header.imageDescriptor.attributeBitsPerPixel != 8) {
qWarning(imagelogging) << "TGA - Only 0 or 8 bits for the alpha channel is supported";
return QImage();
}
char alphaMask = header.imageDescriptor.attributeBitsPerPixel == 8 ? 0x00 : 0xFF;
int bytesPerPixel = header.pixelDepth / 8;
content.skip(header.idLength);
if (header.imageType == TGAImageType::UncompressedTrueColor) {
qint64 minimumSize = header.width * header.height * bytesPerPixel;
if (content.bytesAvailable() < minimumSize) {
qWarning(imagelogging) << "TGA - Unexpectedly reached end of file";
return QImage();
}
QImage image{ header.width, header.height, QImage::Format_ARGB32 };
char* line;
for (int y = 0; y < header.height; ++y) {
if (header.imageDescriptor.orientation == ORIENTATION_BOTTOM_LEFT) {
line = (char*)image.scanLine(header.height - y - 1);
} else {
line = (char*)image.scanLine(y);
}
for (int x = 0; x < header.width; ++x) {
content.read(line, bytesPerPixel);
*(line + 3) |= alphaMask;
line += 4;
}
}
return image;
} else if (header.imageType == TGAImageType::RunLengthEncodedTrueColor) {
QImage image{ header.width, header.height, QImage::Format_ARGB32 };
for (int y = 0; y < header.height; ++y) {
char* line;
if (header.imageDescriptor.orientation == ORIENTATION_BOTTOM_LEFT) {
line = (char*)image.scanLine(header.height - y - 1);
} else {
line = (char*)image.scanLine(y);
}
int col = 0;
while (col < header.width) {
constexpr char IS_REPETITION_MASK{ (char)0x80 };
constexpr char LENGTH_MASK{ (char)0x7f };
char repetition;
if (content.read(&repetition, 1) != 1) {
qWarning(imagelogging) << "TGA - Unexpectedly reached end of file";
return QImage();
}
bool isRepetition = repetition & IS_REPETITION_MASK;
// The length in `repetition` is always 1 less than the number of following pixels,
// so we need to increment it by 1. Because of this, the length is never 0.
int length = (repetition & LENGTH_MASK) + 1;
if (isRepetition) {
// Read into temporary buffer
char color[4];
if (content.read(color, bytesPerPixel) != bytesPerPixel) {
qWarning(imagelogging) << "TGA - Unexpectedly reached end of file";
return QImage();
}
color[3] |= alphaMask;
// Copy `length` number of times
col += length;
while (length-- > 0) {
*line = color[0];
*(line + 1) = color[1];
*(line + 2) = color[2];
*(line + 3) = color[3];
line += 4;
}
} else {
qint64 minimumSize = length * bytesPerPixel;
if (content.bytesAvailable() < minimumSize) {
qWarning(imagelogging) << "TGA - Unexpectedly reached end of file";
return QImage();
}
// Read in `length` number of pixels
col += length;
while (length-- > 0) {
content.read(line, bytesPerPixel);
*(line + 3) |= alphaMask;
line += 4;
}
}
}
}
return image;
} else {
qWarning(imagelogging) << "TGA - Unsupported image type: " << (int)header.imageType;
}
return QImage();
}

View file

@ -0,0 +1,24 @@
//
// TGAReader.h
// image/src/image
//
// Created by Ryan Huffman
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_image_TGAReader_h
#define hifi_image_TGAReader_h
#include <QImage>
namespace image {
// TODO Move this into a plugin that QImageReader can use
QImage readTGA(QIODevice& contents);
}
#endif // hifi_image_TGAReader_h

View file

@ -29,8 +29,14 @@ QMap<QString, QString> getJointNameMapping(const QVariantHash& mapping) {
QMap<QString, glm::quat> getJointRotationOffsets(const QVariantHash& mapping) {
QMap<QString, glm::quat> jointRotationOffsets;
static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset";
if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) {
auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash();
static const QString JOINT_ROTATION_OFFSET2_FIELD = "jointRotationOffset2";
if (!mapping.isEmpty() && ((mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) || (mapping.contains(JOINT_ROTATION_OFFSET2_FIELD) && mapping[JOINT_ROTATION_OFFSET2_FIELD].type() == QVariant::Hash))) {
QHash<QString, QVariant> offsets;
if (mapping.contains(JOINT_ROTATION_OFFSET_FIELD)) {
offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash();
} else {
offsets = mapping[JOINT_ROTATION_OFFSET2_FIELD].toHash();
}
for (auto itr = offsets.begin(); itr != offsets.end(); itr++) {
QString jointName = itr.key();
QString line = itr.value().toString();
@ -57,6 +63,15 @@ void PrepareJointsTask::run(const baker::BakeContextPointer& context, const Inpu
auto& jointRotationOffsets = output.edit1();
auto& jointIndices = output.edit2();
bool newJointRot = false;
static const QString JOINT_ROTATION_OFFSET2_FIELD = "jointRotationOffset2";
QVariantHash fstHashMap = mapping.second;
if (fstHashMap.contains(JOINT_ROTATION_OFFSET2_FIELD)) {
newJointRot = true;
} else {
newJointRot = false;
}
// Get joint renames
auto jointNameMapping = getJointNameMapping(mapping.second);
// Apply joint metadata from FST file mappings
@ -64,11 +79,12 @@ void PrepareJointsTask::run(const baker::BakeContextPointer& context, const Inpu
jointsOut.push_back(jointIn);
auto& jointOut = jointsOut.back();
auto jointNameMapKey = jointNameMapping.key(jointIn.name);
if (jointNameMapping.contains(jointNameMapKey)) {
jointOut.name = jointNameMapKey;
if (!newJointRot) {
auto jointNameMapKey = jointNameMapping.key(jointIn.name);
if (jointNameMapping.contains(jointNameMapKey)) {
jointOut.name = jointNameMapKey;
}
}
jointIndices.insert(jointOut.name, (int)jointsOut.size());
}
@ -77,10 +93,34 @@ void PrepareJointsTask::run(const baker::BakeContextPointer& context, const Inpu
for (auto itr = offsets.begin(); itr != offsets.end(); itr++) {
QString jointName = itr.key();
int jointIndex = jointIndices.value(jointName) - 1;
if (jointIndex != -1) {
if (jointIndex >= 0) {
glm::quat rotationOffset = itr.value();
jointRotationOffsets.insert(jointIndex, rotationOffset);
qCDebug(model_baker) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset;
}
}
if (newJointRot) {
for (auto& jointOut : jointsOut) {
auto jointNameMapKey = jointNameMapping.key(jointOut.name);
int mappedIndex = jointIndices.value(jointOut.name);
if (jointNameMapping.contains(jointNameMapKey)) {
// delete and replace with hifi name
jointIndices.remove(jointOut.name);
jointOut.name = jointNameMapKey;
jointIndices.insert(jointOut.name, mappedIndex);
} else {
// nothing mapped to this fbx joint name
if (jointNameMapping.contains(jointOut.name)) {
// but the name is in the list of hifi names is mapped to a different joint
int extraIndex = jointIndices.value(jointOut.name);
jointIndices.remove(jointOut.name);
jointOut.name = "";
jointIndices.insert(jointOut.name, extraIndex);
}
}
}
}
}

View file

@ -265,6 +265,7 @@ enum class EntityVersion : PacketVersion {
WebBillboardMode,
ModelScale,
ReOrderParentIDProperties,
CertificateTypeProperty,
// Add new versions above here
NUM_PACKET_TYPE,

View file

@ -13,10 +13,7 @@
#include <mutex>
#include <android/log.h>
#if !defined(EGL_OPENGL_ES3_BIT_KHR)
#define EGL_OPENGL_ES3_BIT_KHR 0x0040
#endif
#include <EGL/eglext.h>
using namespace ovr;
@ -129,7 +126,7 @@ void GLContext::doneCurrent() {
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
bool GLContext::create(EGLDisplay display, EGLContext shareContext) {
bool GLContext::create(EGLDisplay display, EGLContext shareContext, bool noError) {
this->display = display;
auto config = findConfig(display);
@ -139,7 +136,9 @@ bool GLContext::create(EGLDisplay display, EGLContext shareContext) {
return false;
}
EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE };
EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3,
noError ? EGL_CONTEXT_OPENGL_NO_ERROR_KHR : EGL_NONE, EGL_TRUE,
EGL_NONE };
context = eglCreateContext(display, config, shareContext, contextAttribs);
if (context == EGL_NO_CONTEXT) {

View file

@ -25,7 +25,7 @@ struct GLContext {
static EGLConfig findConfig(EGLDisplay display);
bool makeCurrent();
void doneCurrent();
bool create(EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY), EGLContext shareContext = EGL_NO_CONTEXT);
bool create(EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY), EGLContext shareContext = EGL_NO_CONTEXT, bool noError = false);
void destroy();
operator bool() const { return context != EGL_NO_CONTEXT; }
static void initModule();

View file

@ -42,6 +42,8 @@ struct VrSurface : public TaskQueue {
uint32_t readFbo{0};
std::atomic<uint32_t> presentIndex{1};
double displayTime{0};
// Not currently set by anything
bool noErrorContext { false };
static constexpr float EYE_BUFFER_SCALE = 1.0f;
@ -71,7 +73,7 @@ struct VrSurface : public TaskQueue {
EGLContext currentContext = eglGetCurrentContext();
EGLDisplay currentDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
vrglContext.create(currentDisplay, currentContext);
vrglContext.create(currentDisplay, currentContext, noErrorContext);
vrglContext.makeCurrent();
glm::uvec2 eyeTargetSize;
@ -91,14 +93,17 @@ struct VrSurface : public TaskQueue {
}
void shutdown() {
noErrorContext = false;
// Destroy the context?
}
void setHandler(VrHandler *newHandler) {
void setHandler(VrHandler *newHandler, bool noError) {
withLock([&] {
isRenderThread = newHandler != nullptr;
if (handler != newHandler) {
shutdown();
handler = newHandler;
noErrorContext = noError;
init();
if (handler) {
updateVrMode();
@ -142,7 +147,6 @@ struct VrSurface : public TaskQueue {
__android_log_write(ANDROID_LOG_WARN, "QQQ_OVR", "vrapi_LeaveVrMode");
vrapi_SetClockLevels(session, 1, 1);
vrapi_SetExtraLatencyMode(session, VRAPI_EXTRA_LATENCY_MODE_OFF);
vrapi_SetDisplayRefreshRate(session, 60);
vrapi_LeaveVrMode(session);
session = nullptr;
@ -153,16 +157,19 @@ struct VrSurface : public TaskQueue {
ovrJava java{ vm, env, oculusActivity };
ovrModeParms modeParms = vrapi_DefaultModeParms(&java);
modeParms.Flags |= VRAPI_MODE_FLAG_NATIVE_WINDOW;
if (noErrorContext) {
modeParms.Flags |= VRAPI_MODE_FLAG_CREATE_CONTEXT_NO_ERROR;
}
modeParms.Display = (unsigned long long) vrglContext.display;
modeParms.ShareContext = (unsigned long long) vrglContext.context;
modeParms.WindowSurface = (unsigned long long) nativeWindow;
session = vrapi_EnterVrMode(&modeParms);
ovrPosef trackingTransform = vrapi_GetTrackingTransform( session, VRAPI_TRACKING_TRANSFORM_SYSTEM_CENTER_EYE_LEVEL);
vrapi_SetTrackingTransform( session, trackingTransform );
vrapi_SetPerfThread(session, VRAPI_PERF_THREAD_TYPE_RENDERER, pthread_self());
vrapi_SetTrackingSpace( session, VRAPI_TRACKING_SPACE_LOCAL);
vrapi_SetPerfThread(session, VRAPI_PERF_THREAD_TYPE_RENDERER, gettid());
vrapi_SetClockLevels(session, 2, 4);
vrapi_SetExtraLatencyMode(session, VRAPI_EXTRA_LATENCY_MODE_DYNAMIC);
vrapi_SetDisplayRefreshRate(session, 72);
// Generates a warning on the quest: "vrapi_SetDisplayRefreshRate: Dynamic Display Refresh Rate not supported"
// vrapi_SetDisplayRefreshRate(session, 72);
});
}
}
@ -227,8 +234,8 @@ bool VrHandler::vrActive() const {
return SURFACE.session != nullptr;
}
void VrHandler::setHandler(VrHandler* handler) {
SURFACE.setHandler(handler);
void VrHandler::setHandler(VrHandler* handler, bool noError) {
SURFACE.setHandler(handler, noError);
}
void VrHandler::pollTask() {

View file

@ -21,7 +21,7 @@ public:
using HandlerTask = std::function<void(VrHandler*)>;
using OvrMobileTask = std::function<void(ovrMobile*)>;
using OvrJavaTask = std::function<void(const ovrJava*)>;
static void setHandler(VrHandler* handler);
static void setHandler(VrHandler* handler, bool noError = false);
static bool withOvrMobile(const OvrMobileTask& task);
protected:

View file

@ -203,11 +203,6 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
}
assert(entityTreeIsLocked());
if (_entity->getShapeType() == SHAPE_TYPE_STATIC_MESH
|| (_body && _body->getCollisionShape()->getShapeType() == TRIANGLE_MESH_SHAPE_PROXYTYPE)) {
return MOTION_TYPE_STATIC;
}
if (_entity->getLocked()) {
if (_entity->isMoving()) {
return MOTION_TYPE_KINEMATIC;

View file

@ -245,7 +245,7 @@ void CauterizedModel::updateRenderItems() {
Transform renderTransform = modelTransform;
if (useDualQuaternionSkinning) {
if (meshState.clusterDualQuaternions.size() == 1) {
if (meshState.clusterDualQuaternions.size() == 1 || meshState.clusterDualQuaternions.size() == 2) {
const auto& dq = meshState.clusterDualQuaternions[0];
Transform transform(dq.getRotation(),
dq.getScale(),
@ -253,7 +253,7 @@ void CauterizedModel::updateRenderItems() {
renderTransform = modelTransform.worldTransform(transform);
}
} else {
if (meshState.clusterMatrices.size() == 1) {
if (meshState.clusterMatrices.size() == 1 || meshState.clusterMatrices.size() == 2) {
renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0]));
}
}
@ -261,7 +261,7 @@ void CauterizedModel::updateRenderItems() {
renderTransform = modelTransform;
if (useDualQuaternionSkinning) {
if (cauterizedMeshState.clusterDualQuaternions.size() == 1) {
if (cauterizedMeshState.clusterDualQuaternions.size() == 1 || cauterizedMeshState.clusterDualQuaternions.size() == 2) {
const auto& dq = cauterizedMeshState.clusterDualQuaternions[0];
Transform transform(dq.getRotation(),
dq.getScale(),
@ -269,7 +269,7 @@ void CauterizedModel::updateRenderItems() {
renderTransform = modelTransform.worldTransform(Transform(transform));
}
} else {
if (cauterizedMeshState.clusterMatrices.size() == 1) {
if (cauterizedMeshState.clusterMatrices.size() == 1 || cauterizedMeshState.clusterMatrices.size() == 2) {
renderTransform = modelTransform.worldTransform(Transform(cauterizedMeshState.clusterMatrices[0]));
}
}

View file

@ -35,10 +35,10 @@
#include "StencilMaskPass.h"
#include "FadeEffect.h"
#include "DeferredLightingEffect.h"
#include <DisableDeferred.h>
namespace gr {
using graphics::slot::texture::Texture;
using graphics::slot::buffer::Buffer;
@ -49,13 +49,6 @@ namespace ru {
using render_utils::slot::buffer::Buffer;
}
#if defined(USE_GLES)
static bool DISABLE_DEFERRED = true;
#else
static const QString RENDER_FORWARD{ "HIFI_RENDER_FORWARD" };
static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
#endif
//#define WANT_DEBUG
// @note: Originally size entity::NUM_SHAPES

View file

@ -247,7 +247,7 @@ void Model::updateRenderItems() {
Transform renderTransform = modelTransform;
if (useDualQuaternionSkinning) {
if (meshState.clusterDualQuaternions.size() == 1) {
if (meshState.clusterDualQuaternions.size() == 1 || meshState.clusterDualQuaternions.size() == 2) {
const auto& dq = meshState.clusterDualQuaternions[0];
Transform transform(dq.getRotation(),
dq.getScale(),
@ -255,7 +255,7 @@ void Model::updateRenderItems() {
renderTransform = modelTransform.worldTransform(Transform(transform));
}
} else {
if (meshState.clusterMatrices.size() == 1) {
if (meshState.clusterMatrices.size() == 1 || meshState.clusterMatrices.size() == 2) {
renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0]));
}
}

View file

@ -67,11 +67,11 @@ float TextRenderer3D::getFontSize() const {
}
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color,
const glm::vec2& bounds) {
const glm::vec2& bounds, bool forwardRendered) {
// The font does all the OpenGL work
if (_font) {
_color = color;
_font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds);
_font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds, forwardRendered);
}
}

View file

@ -39,7 +39,7 @@ public:
float getFontSize() const; // Pixel size
void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(1.0f),
const glm::vec2& bounds = glm::vec2(-1.0f));
const glm::vec2& bounds = glm::vec2(-1.0f), bool forwardRendered = false);
private:
TextRenderer3D(const char* family, float pointSize, int weight = -1, bool italic = false,

View file

@ -19,6 +19,7 @@
// the interpolated normal
layout(location=RENDER_UTILS_ATTR_NORMAL_WS) out vec3 _normalWS;
layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01;
layout(location=RENDER_UTILS_ATTR_POSITION_ES) out vec4 _positionES;
void main() {
_texCoord01.xy = inTexCoord0.xy;
@ -26,7 +27,7 @@ void main() {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
const vec3 normal = vec3(0, 0, 1);
<$transformModelToWorldDir(cam, obj, normal, _normalWS)$>
}

View file

@ -10,7 +10,14 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
<@include DeferredBufferWrite.slh@>
<@include DefaultMaterials.slh@>
<@include ForwardGlobalLight.slh@>
<$declareEvalGlobalLightingAlphaBlended()$>
<@include gpu/Transform.slh@>
<$declareStandardCameraTransform()$>
<@include render-utils/ShaderConstants.h@>
LAYOUT(binding=0) uniform sampler2D Font;
@ -24,12 +31,14 @@ LAYOUT(binding=0) uniform textParamsBuffer {
TextParams params;
};
// the interpolated normal
layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES;
layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS;
layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01;
#define _texCoord0 _texCoord01.xy
#define _texCoord1 _texCoord01.zw
layout(location=0) out vec4 _fragColor0;
#define TAA_TEXTURE_LOD_BIAS -3.0
const float interiorCutoff = 0.8;
@ -57,9 +66,24 @@ void main() {
a += evalSDF(_texCoord0 + dxTexCoord + dyTexCoord);
a *= 0.25;
packDeferredFragmentTranslucent(
float alpha = a * params.color.a;
if (alpha <= 0.0) {
discard;
}
TransformCamera cam = getTransformCamera();
vec3 fragPosition = _positionES.xyz;
_fragColor0 = vec4(evalGlobalLightingAlphaBlendedWithHaze(
cam._viewInverse,
1.0,
DEFAULT_OCCLUSION,
fragPosition,
normalize(_normalWS),
a * params.color.a,
params.color.rgb,
DEFAULT_ROUGHNESS);
DEFAULT_FRESNEL,
DEFAULT_METALLIC,
DEFAULT_EMISSIVE,
DEFAULT_ROUGHNESS, alpha),
alpha);
}

View file

@ -343,7 +343,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
}
void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString& str, const glm::vec4& color,
EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds) {
EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds, bool forwardRendered) {
if (str == "") {
return;
}
@ -370,7 +370,7 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString
}
// need the gamma corrected color here
batch.setPipeline((color.a < 1.0f) ? _transparentPipeline : _pipeline);
batch.setPipeline(forwardRendered || (color.a < 1.0f) ? _transparentPipeline : _pipeline);
batch.setInputFormat(_format);
batch.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride);
batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture);

View file

@ -46,7 +46,7 @@ public:
// Render string to batch
void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str,
const glm::vec4& color, EffectType effectType,
const glm::vec2& origin, const glm::vec2& bound);
const glm::vec2& origin, const glm::vec2& bound, bool forwardRendered);
static Pointer load(const QString& family);

View file

@ -88,3 +88,43 @@ bool AudioScriptingInterface::isStereoInput() {
}
return stereoEnabled;
}
bool AudioScriptingInterface::getServerEcho() {
bool serverEchoEnabled = false;
if (_localAudioInterface) {
serverEchoEnabled = _localAudioInterface->getServerEcho();
}
return serverEchoEnabled;
}
void AudioScriptingInterface::setServerEcho(bool serverEcho) {
if (_localAudioInterface) {
QMetaObject::invokeMethod(_localAudioInterface, "setServerEcho", Q_ARG(bool, serverEcho));
}
}
void AudioScriptingInterface::toggleServerEcho() {
if (_localAudioInterface) {
QMetaObject::invokeMethod(_localAudioInterface, "toggleServerEcho");
}
}
bool AudioScriptingInterface::getLocalEcho() {
bool localEchoEnabled = false;
if (_localAudioInterface) {
localEchoEnabled = _localAudioInterface->getLocalEcho();
}
return localEchoEnabled;
}
void AudioScriptingInterface::setLocalEcho(bool localEcho) {
if (_localAudioInterface) {
QMetaObject::invokeMethod(_localAudioInterface, "setLocalEcho", Q_ARG(bool, localEcho));
}
}
void AudioScriptingInterface::toggleLocalEcho() {
if (_localAudioInterface) {
QMetaObject::invokeMethod(_localAudioInterface, "toggleLocalEcho");
}
}

View file

@ -66,6 +66,39 @@ public:
_localAudioInterface->getAudioSolo().reset();
}
/**jsdoc
* @function Audio.getServerEcho
*/
Q_INVOKABLE bool getServerEcho();
/**jsdoc
* @function Audio.setServerEcho
* @parm {boolean} serverEcho
*/
Q_INVOKABLE void setServerEcho(bool serverEcho);
/**jsdoc
* @function Audio.toggleServerEcho
*/
Q_INVOKABLE void toggleServerEcho();
/**jsdoc
* @function Audio.getLocalEcho
*/
Q_INVOKABLE bool getLocalEcho();
/**jsdoc
* @function Audio.setLocalEcho
* @parm {boolean} localEcho
*/
Q_INVOKABLE void setLocalEcho(bool localEcho);
/**jsdoc
* @function Audio.toggleLocalEcho
*/
Q_INVOKABLE void toggleLocalEcho();
protected:
AudioScriptingInterface() = default;

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