mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-10 17:23:15 +02:00
Merge branch 'master' into M21222
# Conflicts: # libraries/avatars/src/AvatarData.h
This commit is contained in:
commit
7445d73857
118 changed files with 3674 additions and 775 deletions
BIN
.vs/slnx.sqlite
Normal file
BIN
.vs/slnx.sqlite
Normal file
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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']
|
||||
},
|
||||
|
|
|
@ -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" : [
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -14,7 +14,6 @@
|
|||
<glyph glyph-name="headphones-mic" unicode="f" 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="g" 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="h" 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="i" 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="j" 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="k" 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="l" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="T" 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="" 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="" 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="" 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="" 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="" 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="" 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="i" 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="" 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 |
Binary file not shown.
Binary file not shown.
|
@ -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="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe005;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe006;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe007;">
|
||||
|
@ -1134,10 +1138,6 @@
|
|||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe029;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe025;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe02b;">
|
||||
|
@ -1210,6 +1210,30 @@
|
|||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe036;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe037;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe025;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe006;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe039;">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="i" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="i">
|
||||
</li>
|
||||
<li>
|
||||
<div data-icon="" class="icon"></div>
|
||||
<input type="text" readonly="readonly" value="&#xe038;">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<script>(function() {
|
||||
|
@ -1229,4 +1253,4 @@
|
|||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
|
@ -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";
|
||||
}
|
Binary file not shown.
27
interface/resources/icons/standalone-optimized.svg
Normal file
27
interface/resources/icons/standalone-optimized.svg
Normal 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 |
1
interface/resources/icons/tablet-icons/mic-ptt-a.svg
Normal file
1
interface/resources/icons/tablet-icons/mic-ptt-a.svg
Normal 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 |
24
interface/resources/icons/tablet-icons/mic-ptt-i.svg
Normal file
24
interface/resources/icons/tablet-icons/mic-ptt-i.svg
Normal 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 |
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) ||
|
||||
|
|
67
interface/resources/qml/hifi/audio/LoopbackAudio.qml
Normal file
67
interface/resources/qml/hifi/audio/LoopbackAudio.qml
Normal 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") : "";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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") : "";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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 {
|
||||
|
|
59
interface/resources/qml/hifi/tablet/OculusConfiguration.qml
Normal file
59
interface/resources/qml/hifi/tablet/OculusConfiguration.qml
Normal 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);
|
||||
}
|
||||
}
|
144
interface/resources/qml/hifi/tablet/TADLightbox.qml
Normal file
144
interface/resources/qml/hifi/tablet/TADLightbox.qml
Normal 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
|
||||
//
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) –
|
||||
* @property {number} inputLevel - The loudness of the audio input, range <code>0.0</code> (no sound) –
|
||||
* <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> – <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> – <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 — configured on the server — 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 — configured on the server — 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> –
|
||||
* <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> –
|
||||
* <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) – <code>1.0</code> (the
|
||||
* @param {number} level - The loudness of the input audio, range <code>0.0</code> (no sound) – <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 };
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -60,6 +60,7 @@ enum class Action {
|
|||
|
||||
CONTEXT_MENU,
|
||||
TOGGLE_MUTE,
|
||||
TOGGLE_PUSHTOTALK,
|
||||
CYCLE_CAMERA,
|
||||
TOGGLE_OVERLAY,
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, "");
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
202
libraries/image/src/image/TGAReader.cpp
Normal file
202
libraries/image/src/image/TGAReader.cpp
Normal 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();
|
||||
}
|
24
libraries/image/src/image/TGAReader.h
Normal file
24
libraries/image/src/image/TGAReader.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -265,6 +265,7 @@ enum class EntityVersion : PacketVersion {
|
|||
WebBillboardMode,
|
||||
ModelScale,
|
||||
ReOrderParentIDProperties,
|
||||
CertificateTypeProperty,
|
||||
|
||||
// Add new versions above here
|
||||
NUM_PACKET_TYPE,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)$>
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue