mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-08 23:42:25 +02:00
Merge branch 'overte-org:master' into Delete/onFirstRun
This commit is contained in:
commit
e8dae14a02
122 changed files with 3455 additions and 1091 deletions
142
CHANGELOG.md
142
CHANGELOG.md
|
@ -12,11 +12,147 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
This project does **not** adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
<!-- ## [Unreleased] 2023.07.2 -->
|
||||
<!-- ## [2024.09.1] Unreleased -->
|
||||
|
||||
<!-- ## [2023.07.1] 2023.07.2 -->
|
||||
## [2024.07.1] 2023.07.12
|
||||
|
||||
### Fixes
|
||||
- Fix more warnings (PR1007)
|
||||
- Fix new debug build warning (PR1013)
|
||||
- Fix RPM packaging (PR1025)
|
||||
- Temporary fix for some script messages not being transmitted (PR1024)
|
||||
- Fix unhandled std::bad_weak_ptr (PR1037)
|
||||
- Fix long messages in Entity script log (PR1029)
|
||||
- Allow events from VR keyboard to overlay UI (PR1046)
|
||||
- Fix Fedora 40 server packages. (PR1057)
|
||||
- Fix crash in setCrashReporterEnabled (PR1065)
|
||||
- Fix mtoon issue on GLES (PR1055)
|
||||
- Fix GHA building hundreds of servers (PR1071)
|
||||
- Fix broken upload paths for RPM distributions. (PR1072)
|
||||
|
||||
### Changes
|
||||
- Text Entity Improvements (PR937)
|
||||
- GitHub Action Runner auto-scaling (PR1021)
|
||||
- Remove some external refrences (PR1026)
|
||||
- Update from deprecated actions/checkout and actions/upload-artifact (PR1023)
|
||||
- Update Ubuntu 20.04 amd64 Qt package (PR1032)
|
||||
- Cut back on master builds. (PR1049)
|
||||
- Increase default vertical FOV (PR1061)
|
||||
- Always enable crash reporting. (PR1064)
|
||||
- Update make-rpm-server to remove - characters. (PR1063)
|
||||
- Update VCPKG on Windows, so it can find newer versions of Visual Studio. (PR1073)
|
||||
|
||||
### Additions
|
||||
- Add a script to more easily build Debian and RPM server packages. (PR1011)
|
||||
- Add Fedora 40 servers (PR1010)
|
||||
- GitHub Actions server release builds (PR1022)
|
||||
- Wiggly lasers (PR1014)
|
||||
- Create Windows release builds. (PR1048)
|
||||
|
||||
### Removals
|
||||
- Remove broken ccache macro (PR1018)
|
||||
- Remove note about secure WebSockets not being supported. (PR1040)
|
||||
- Remove RELEASE_NAME (PR1039)
|
||||
- Remove hifiNeuron.dll (PR1075)
|
||||
- Remove remnants of RELEASE_NAME. (PR1077)
|
||||
|
||||
|
||||
## [2024.06.1] 2023.06.24
|
||||
|
||||
### Fixes
|
||||
- Fix QNetworkRequest::FollowRedirectsAttribute deprecated warning (PR711)
|
||||
- Fix luci (PR724)
|
||||
- Fix setting external port and IP address for manual networking modes (PR746)
|
||||
- particle billboarding is wrong in VR (PR747)
|
||||
- Upgrade openexr (PR752)
|
||||
- Fix OpenEXR on Windows (PR756)
|
||||
- Upgrade Steamworks and fix for ninja build (PR755)
|
||||
- Fixes for Oculus VR plugin (PR758)
|
||||
- Fix Ninja builds with Crashpad enabled on Windows (PR765)
|
||||
- Fix a bug in Entities.getEntityProperties (PR768)
|
||||
- Fixed returnNothingOnEmptyPropertyFlags (PR770)
|
||||
- ✨ "Update Existing Avatar" now properly updates the fst file (PR576)
|
||||
- Fix the doppleganger position in the tutorial (PR781)
|
||||
- Fix entity density not setting and not updating for motion states (PR819)
|
||||
- Add keyboard modifiers to entity pointer events (PR815)
|
||||
- 🐛 Fixed build on Windows (PR827)
|
||||
- Fix recording API documentation not being built (PR829)
|
||||
- Fix the display of Notifications (PR831)
|
||||
- Fix prebuild.py code ordering to make --get-vcpkg-id and --get-vcpkg-path correctly in the repo root (PR836)
|
||||
- Fixed avatar volume adjustment (PR841)
|
||||
- Respect cull face during shadow pass (PR820)
|
||||
- Fix Developer > UI > Show Overlays + Snap UI not hiding (PR823)
|
||||
- Fixed blendshapes in gltf importer (PR840)
|
||||
- Fix require behavior for modules (PR855)
|
||||
- 🐛 Fixed avatar bounding box being inverted (PR866)
|
||||
- Fix collisions on glTF avatars (PR867)
|
||||
- Clean up GeometryCache and remove _glColor4f (PR845)
|
||||
- Fix warnings as errors for MSVC (PR873)
|
||||
- Fix invalid animation reference in assignment client (PR854)
|
||||
- Fix previous commit for not loading textures on agent (PR893)
|
||||
- conversionPenaltyScore should accumulate (PR935)
|
||||
- Fix getEntityProperties for group properties + keylight direction (PR927)
|
||||
- Move helper script engines to their own threads (PR853)
|
||||
- Fix C++20 warnings (PR950)
|
||||
- Fix access check in getSkeletonModelURLFromScript (PR955)
|
||||
- SimplifiedUI adjustemnts (PR960)
|
||||
- Fixed locale problems with cgltf.h (PR978)
|
||||
- Fixed script signal proxy crashes (PR964)
|
||||
- Fixed deadlocks in Recording API (PR959)
|
||||
- Fix tests (PR991)
|
||||
- Add missing dependencymanager call, fixes test crash (PR994)
|
||||
- Skip packet capacity test when built in debug mode, as it triggers an assertion (PR993)
|
||||
- Fix some warnings (PR1003)
|
||||
- Fix Locker issue in RecordingScriptingInterface (PR1006)
|
||||
|
||||
### Changes
|
||||
- Update Linux vcpkg to the same as the Windows version (PR751)
|
||||
- Disable Oculus plugin on Visual Studio 2022 (PR754)
|
||||
- Reorganize startup to allow more control over plugins (PR716)
|
||||
- Script performance improvements (PR744)
|
||||
- Re-enable building Oculus plugin on VS 2022 (PR761)
|
||||
- Use response files on Ninja for long paths (PR764)
|
||||
- Improvement in controller scripts performance (PR766)
|
||||
- Update VCPKG for Linux aarch64 (PR807)
|
||||
- Linux server gha update (PR812)
|
||||
- glTF importer using cgltf library (PR835)
|
||||
- 🔧 Disable building electron screenshare app on windows (PR864)
|
||||
- Change the hash salt from Vircadia to Overte (PR869)
|
||||
- Change vircadia launcher dir (PR870)
|
||||
- Add Oculus to fullbody-enabled headsets list (PR882)
|
||||
- Add Ubuntu 24.04 to server packaging (PR966)
|
||||
- Improve network debugging messages to help with Conan PR (PR995)
|
||||
- Send OpenGL errors to log on debug builds (PR998)
|
||||
|
||||
### Additions
|
||||
- Add initial benchmarks for the script engine (PR710)
|
||||
- Add more ram Options (PR763)
|
||||
- Add VS22 cmake jumpstart script (PR773)
|
||||
- MToon materials (PR805)
|
||||
- Add a way to get VCPKG path and hash ID (PR813)
|
||||
- Entity script logging for script editor (PR673)
|
||||
- Create app: RenderWithZones Manager (PR806)
|
||||
- Added WebP support for binary glTF (PR860)
|
||||
- Support opaque (and black) particles (PR844)
|
||||
- Create App: Tools tab: CSS&HTML adjustments (PR880)
|
||||
- Create Application: Advanced Import functionality (PR877)
|
||||
- Create App.: "Copy Dimensions" and "Paste Dimensions" (PR863)
|
||||
- Create app: Entity List: "Copy ID" on menu and contextual menu. (PR865)
|
||||
- Add qByteArray to script value conversion (PR868)
|
||||
- Custom refresh rate profile (PR899)
|
||||
- support VRMC_materials_mtoon and KHR_materials_unlit (PR936)
|
||||
- Add very basic HTTP and HTTPS network tests (PR996)
|
||||
|
||||
### Removals
|
||||
- Disable Neuron by default (PR753)
|
||||
- Removing markdown from floof chat (PR769)
|
||||
- Remove debug print with URL from ModelLoader (PR837)
|
||||
- Remove legally problematic texture (PR851)
|
||||
|
||||
### Security
|
||||
- Added simple protection for avatar URL (PR887)
|
||||
- add canViewAssetURLs domain permissions (PR152)
|
||||
|
||||
<!-- ## [2023.06.1] 2023.06.12 -->
|
||||
|
||||
## [2023.11.1] 2023.11.24
|
||||
|
||||
|
|
|
@ -148,22 +148,22 @@ endif()
|
|||
|
||||
# OVERTE_WARNINGS
|
||||
#
|
||||
# Here we add the ability to whitelist warnings we've determined we can't fix, or are safe to
|
||||
# Here we add the ability to allowlist warnings we've determined we can't fix, or are safe to
|
||||
# ignore for one reason or another. The way of doing so is compiler-specific, so we deal with
|
||||
# the detection of that in cmake, and just pass it down to the code from here.
|
||||
#
|
||||
# We can also treat warnings as errors. Without the whitelist this will almost certainly lead
|
||||
# We can also treat warnings as errors. Without the allowlist this will almost certainly lead
|
||||
# to a build failure.
|
||||
|
||||
if(NOT DEFINED OVERTE_WARNINGS_WHITELIST)
|
||||
set(OVERTE_WARNINGS_WHITELIST true CACHE BOOL "Whitelist some warnings we can't currently fix")
|
||||
if(NOT DEFINED OVERTE_WARNINGS_ALLOWLIST)
|
||||
set(OVERTE_WARNINGS_ALLOWLIST true CACHE BOOL "Allowlist some warnings we can't currently fix")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED OVERTE_WARNINGS_AS_ERRORS)
|
||||
set(OVERTE_WARNINGS_AS_ERRORS false CACHE BOOL "Count warnings as errors")
|
||||
endif()
|
||||
|
||||
if(OVERTE_WARNINGS_WHITELIST)
|
||||
if(OVERTE_WARNINGS_ALLOWLIST)
|
||||
if (NOT WIN32)
|
||||
set(CMAKE_PLATFORM_INFO_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
include(CMakeDetermineCXXCompiler)
|
||||
|
@ -171,15 +171,15 @@ if(OVERTE_WARNINGS_WHITELIST)
|
|||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
message("GCC compiler detected, suppressing some unsolvable warnings.")
|
||||
add_compile_definitions(OVERTE_WARNINGS_WHITELIST_GCC)
|
||||
add_compile_definitions(OVERTE_WARNINGS_ALLOWLIST_GCC)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
message("Clang compiler detected, suppressing some unsolvable warnings.")
|
||||
add_compile_definitions(OVERTE_WARNINGS_WHITELIST_CLANG)
|
||||
add_compile_definitions(OVERTE_WARNINGS_ALLOWLIST_CLANG)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR (CMAKE_CXX_COMPILER_ID MATCHES "" AND WIN32))
|
||||
message("Microsoft Visual Studio compiler detected, suppressing some unsolvable warnings.")
|
||||
add_compile_definitions(OVERTE_WARNINGS_WHITELIST_MSVC)
|
||||
add_compile_definitions(OVERTE_WARNINGS_ALLOWLIST_MSVC)
|
||||
else()
|
||||
message("We don't know yet how to whitelist warnings for ${CMAKE_CXX_COMPILER_ID}")
|
||||
message("We don't know yet how to allowlist warnings for ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
Copyright (c) 2013-2019, High Fidelity, Inc.
|
||||
Copyright (c) 2019-2021, Vircadia contributors.
|
||||
Copyright (c) 2022-2023, Overte e.V.
|
||||
Copyright (c) 2022-2024, Overte e.V.
|
||||
All rights reserved.
|
||||
https://overte.org
|
||||
|
||||
|
|
|
@ -277,14 +277,14 @@ void AudioMixer::sendStatsPacket() {
|
|||
#ifdef DEBUG_EVENT_QUEUE
|
||||
QJsonObject qtStats;
|
||||
|
||||
_slavePool.queueStats(qtStats);
|
||||
_workerPool.queueStats(qtStats);
|
||||
statsObject["audio_thread_event_queue"] = qtStats;
|
||||
#endif
|
||||
|
||||
// general stats
|
||||
statsObject["useDynamicJitterBuffers"] = _numStaticJitterFrames == DISABLE_STATIC_JITTER_FRAMES;
|
||||
|
||||
statsObject["threads"] = _slavePool.numThreads();
|
||||
statsObject["threads"] = _workerPool.numThreads();
|
||||
|
||||
statsObject["trailing_mix_ratio"] = _trailingMixRatio;
|
||||
statsObject["throttling_ratio"] = _throttlingRatio;
|
||||
|
@ -433,15 +433,15 @@ void AudioMixer::start() {
|
|||
|
||||
auto frameTimer = _frameTiming.timer();
|
||||
|
||||
// process (node-isolated) audio packets across slave threads
|
||||
// process (node-isolated) audio packets across worker threads
|
||||
{
|
||||
auto packetsTimer = _packetsTiming.timer();
|
||||
|
||||
// first clear the concurrent vector of added streams that the slaves will add to when they process packets
|
||||
// first clear the concurrent vector of added streams that the workers will add to when they process packets
|
||||
_workerSharedData.addedStreams.clear();
|
||||
|
||||
nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) {
|
||||
_slavePool.processPackets(cbegin, cend);
|
||||
_workerPool.processPackets(cbegin, cend);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -463,15 +463,15 @@ void AudioMixer::start() {
|
|||
numToRetain = nodeList->size() * (1.0f - _throttlingRatio);
|
||||
}
|
||||
nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) {
|
||||
// mix across slave threads
|
||||
// mix across worker threads
|
||||
auto mixTimer = _mixTiming.timer();
|
||||
_slavePool.mix(cbegin, cend, frame, numToRetain);
|
||||
_workerPool.mix(cbegin, cend, frame, numToRetain);
|
||||
});
|
||||
|
||||
// gather stats
|
||||
_slavePool.each([&](AudioMixerSlave& slave) {
|
||||
_stats.accumulate(slave.stats);
|
||||
slave.stats.reset();
|
||||
_workerPool.each([&](AudioMixerWorker& worker) {
|
||||
_stats.accumulate(worker.stats);
|
||||
worker.stats.reset();
|
||||
});
|
||||
|
||||
++frame;
|
||||
|
@ -573,7 +573,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
|
|||
const QString NUM_THREADS = "num_threads";
|
||||
int numThreads = audioThreadingGroupObject[NUM_THREADS].toString().toInt(&ok);
|
||||
if (ok) {
|
||||
_slavePool.setNumThreads(numThreads);
|
||||
_workerPool.setNumThreads(numThreads);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include <plugins/Forward.h>
|
||||
|
||||
#include "AudioMixerStats.h"
|
||||
#include "AudioMixerSlavePool.h"
|
||||
#include "AudioMixerWorkerPool.h"
|
||||
|
||||
class PositionalAudioStream;
|
||||
class AvatarAudioStream;
|
||||
|
@ -107,7 +107,7 @@ private:
|
|||
int _numStatFrames { 0 };
|
||||
AudioMixerStats _stats;
|
||||
|
||||
AudioMixerSlavePool _slavePool { _workerSharedData };
|
||||
AudioMixerWorkerPool _workerPool { _workerSharedData };
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
|
@ -153,7 +153,7 @@ private:
|
|||
float _throttleStartTarget = 0.9f;
|
||||
float _throttleBackoffTarget = 0.44f;
|
||||
|
||||
AudioMixerSlave::SharedData _workerSharedData;
|
||||
AudioMixerWorker::SharedData _workerSharedData;
|
||||
};
|
||||
|
||||
#endif // hifi_AudioMixer_h
|
||||
|
|
|
@ -201,9 +201,9 @@ void AudioMixerClientData::parsePerAvatarGainSet(ReceivedMessage& message, const
|
|||
float gain = unpackFloatGainFromByte(packedGain);
|
||||
|
||||
if (avatarUUID.isNull()) {
|
||||
// set the MASTER avatar gain
|
||||
setMasterAvatarGain(gain);
|
||||
qCDebug(audio) << "Setting MASTER avatar gain for" << uuid << "to" << gain;
|
||||
// set the PRIMARY avatar gain
|
||||
setPrimaryAvatarGain(gain);
|
||||
qCDebug(audio) << "Setting PRIMARY avatar gain for" << uuid << "to" << gain;
|
||||
} else {
|
||||
// set the per-source avatar gain
|
||||
setGainForAvatar(avatarUUID, gain);
|
||||
|
@ -218,8 +218,8 @@ void AudioMixerClientData::parseInjectorGainSet(ReceivedMessage& message, const
|
|||
message.readPrimitive(&packedGain);
|
||||
float gain = unpackFloatGainFromByte(packedGain);
|
||||
|
||||
setMasterInjectorGain(gain);
|
||||
qCDebug(audio) << "Setting MASTER injector gain for" << uuid << "to" << gain;
|
||||
setPrimaryInjectorGain(gain);
|
||||
qCDebug(audio) << "Setting PRIMARY injector gain for" << uuid << "to" << gain;
|
||||
}
|
||||
|
||||
bool setGainInStreams(const QUuid &nodeID, float gain, std::vector<AudioMixerClientData::MixableStream> &streamVector) {
|
||||
|
|
|
@ -86,10 +86,10 @@ public:
|
|||
// uses randomization to have the AudioMixer send a stats packet to this node around every second
|
||||
bool shouldSendStats(int frameNumber);
|
||||
|
||||
float getMasterAvatarGain() const { return _masterAvatarGain; }
|
||||
void setMasterAvatarGain(float gain) { _masterAvatarGain = gain; }
|
||||
float getMasterInjectorGain() const { return _masterInjectorGain; }
|
||||
void setMasterInjectorGain(float gain) { _masterInjectorGain = gain; }
|
||||
float getPrimaryAvatarGain() const { return _primaryAvatarGain; }
|
||||
void setPrimaryAvatarGain(float gain) { _primaryAvatarGain = gain; }
|
||||
float getPrimaryInjectorGain() const { return _primaryInjectorGain; }
|
||||
void setPrimaryInjectorGain(float gain) { _primaryInjectorGain = gain; }
|
||||
|
||||
AudioLimiter audioLimiter;
|
||||
|
||||
|
@ -140,11 +140,11 @@ public:
|
|||
|
||||
Streams& getStreams() { return _streams; }
|
||||
|
||||
// thread-safe, called from AudioMixerSlave(s) while processing ignore packets for other nodes
|
||||
// thread-safe, called from AudioMixerWorker(s) while processing ignore packets for other nodes
|
||||
void ignoredByNode(QUuid nodeID);
|
||||
void unignoredByNode(QUuid nodeID);
|
||||
|
||||
// start of methods called non-concurrently from single AudioMixerSlave mixing for the owning node
|
||||
// start of methods called non-concurrently from single AudioMixerWorker mixing for the owning node
|
||||
|
||||
const Node::IgnoredNodeIDs& getNewIgnoredNodeIDs() const { return _newIgnoredNodeIDs; }
|
||||
const Node::IgnoredNodeIDs& getNewUnignoredNodeIDs() const { return _newUnignoredNodeIDs; }
|
||||
|
@ -163,7 +163,7 @@ public:
|
|||
bool getHasReceivedFirstMix() const { return _hasReceivedFirstMix; }
|
||||
void setHasReceivedFirstMix(bool hasReceivedFirstMix) { _hasReceivedFirstMix = hasReceivedFirstMix; }
|
||||
|
||||
// end of methods called non-concurrently from single AudioMixerSlave
|
||||
// end of methods called non-concurrently from single AudioMixerWorker
|
||||
|
||||
signals:
|
||||
void injectorStreamFinished(const QUuid& streamID);
|
||||
|
@ -194,8 +194,8 @@ private:
|
|||
|
||||
int _frameToSendStats { 0 };
|
||||
|
||||
float _masterAvatarGain { 1.0f }; // per-listener mixing gain, applied only to avatars
|
||||
float _masterInjectorGain { 1.0f }; // per-listener mixing gain, applied only to injectors
|
||||
float _primaryAvatarGain { 1.0f }; // per-listener mixing gain, applied only to avatars
|
||||
float _primaryInjectorGain { 1.0f }; // per-listener mixing gain, applied only to injectors
|
||||
|
||||
CodecPluginPointer _codec;
|
||||
QString _selectedCodecName;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// AudioMixerSlave.cpp
|
||||
// AudioMixerWorker.cpp
|
||||
// assignment-client/src/audio
|
||||
//
|
||||
// Created by Zach Pomerantz on 11/22/16.
|
||||
|
@ -9,7 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AudioMixerSlave.h"
|
||||
#include "AudioMixerWorker.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -50,12 +50,12 @@ void sendEnvironmentPacket(const SharedNodePointer& node, AudioMixerClientData&
|
|||
|
||||
// mix helpers
|
||||
inline float approximateGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd);
|
||||
inline float computeGain(float masterAvatarGain, float masterInjectorGain, const AvatarAudioStream& listeningNodeStream,
|
||||
inline float computeGain(float primaryAvatarGain, float primaryInjectorGain, const AvatarAudioStream& listeningNodeStream,
|
||||
const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, float distance);
|
||||
inline float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
|
||||
const glm::vec3& relativePosition);
|
||||
|
||||
void AudioMixerSlave::processPackets(const SharedNodePointer& node) {
|
||||
void AudioMixerWorker::processPackets(const SharedNodePointer& node) {
|
||||
AudioMixerClientData* data = (AudioMixerClientData*)node->getLinkedData();
|
||||
if (data) {
|
||||
// process packets and collect the number of streams available for this frame
|
||||
|
@ -63,14 +63,14 @@ void AudioMixerSlave::processPackets(const SharedNodePointer& node) {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioMixerSlave::configureMix(ConstIter begin, ConstIter end, unsigned int frame, int numToRetain) {
|
||||
void AudioMixerWorker::configureMix(ConstIter begin, ConstIter end, unsigned int frame, int numToRetain) {
|
||||
_begin = begin;
|
||||
_end = end;
|
||||
_frame = frame;
|
||||
_numToRetain = numToRetain;
|
||||
}
|
||||
|
||||
void AudioMixerSlave::mix(const SharedNodePointer& node) {
|
||||
void AudioMixerWorker::mix(const SharedNodePointer& node) {
|
||||
// check that the node is valid
|
||||
AudioMixerClientData* data = (AudioMixerClientData*)node->getLinkedData();
|
||||
if (data == nullptr) {
|
||||
|
@ -178,7 +178,7 @@ private:
|
|||
};
|
||||
|
||||
|
||||
void AudioMixerSlave::addStreams(Node& listener, AudioMixerClientData& listenerData) {
|
||||
void AudioMixerWorker::addStreams(Node& listener, AudioMixerClientData& listenerData) {
|
||||
auto& ignoredNodeIDs = listener.getIgnoredNodeIDs();
|
||||
auto& ignoringNodeIDs = listenerData.getIgnoringNodeIDs();
|
||||
|
||||
|
@ -229,7 +229,7 @@ void AudioMixerSlave::addStreams(Node& listener, AudioMixerClientData& listenerD
|
|||
}
|
||||
}
|
||||
|
||||
bool shouldBeRemoved(const MixableStream& stream, const AudioMixerSlave::SharedData& sharedData) {
|
||||
bool shouldBeRemoved(const MixableStream& stream, const AudioMixerWorker::SharedData& sharedData) {
|
||||
return (contains(sharedData.removedNodes, stream.nodeStreamID.nodeLocalID) ||
|
||||
contains(sharedData.removedStreams, stream.nodeStreamID));
|
||||
};
|
||||
|
@ -306,7 +306,7 @@ float approximateVolume(const MixableStream& stream, const AvatarAudioStream* li
|
|||
return stream.positionalStream->getLastPopOutputTrailingLoudness() * gain;
|
||||
};
|
||||
|
||||
bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
|
||||
bool AudioMixerWorker::prepareMix(const SharedNodePointer& listener) {
|
||||
AvatarAudioStream* listenerAudioStream = static_cast<AudioMixerClientData*>(listener->getLinkedData())->getAvatarAudioStream();
|
||||
AudioMixerClientData* listenerData = static_cast<AudioMixerClientData*>(listener->getLinkedData());
|
||||
|
||||
|
@ -338,8 +338,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
|
|||
}
|
||||
|
||||
if (!isThrottling) {
|
||||
updateHRTFParameters(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
|
||||
listenerData->getMasterInjectorGain());
|
||||
updateHRTFParameters(stream, *listenerAudioStream, listenerData->getPrimaryAvatarGain(),
|
||||
listenerData->getPrimaryInjectorGain());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
@ -363,8 +363,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
|
|||
}
|
||||
|
||||
if (!isThrottling) {
|
||||
updateHRTFParameters(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
|
||||
listenerData->getMasterInjectorGain());
|
||||
updateHRTFParameters(stream, *listenerAudioStream, listenerData->getPrimaryAvatarGain(),
|
||||
listenerData->getPrimaryInjectorGain());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
@ -387,7 +387,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
|
|||
return true;
|
||||
}
|
||||
|
||||
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), listenerData->getMasterInjectorGain(),
|
||||
addStream(stream, *listenerAudioStream, listenerData->getPrimaryAvatarGain(), listenerData->getPrimaryInjectorGain(),
|
||||
isSoloing);
|
||||
|
||||
if (shouldBeInactive(stream)) {
|
||||
|
@ -423,7 +423,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
|
|||
return true;
|
||||
}
|
||||
|
||||
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), listenerData->getMasterInjectorGain(),
|
||||
addStream(stream, *listenerAudioStream, listenerData->getPrimaryAvatarGain(), listenerData->getPrimaryInjectorGain(),
|
||||
isSoloing);
|
||||
|
||||
if (shouldBeInactive(stream)) {
|
||||
|
@ -489,10 +489,10 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
|
|||
return hasAudio;
|
||||
}
|
||||
|
||||
void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStream,
|
||||
void AudioMixerWorker::addStream(AudioMixerClientData::MixableStream& mixableStream,
|
||||
AvatarAudioStream& listeningNodeStream,
|
||||
float masterAvatarGain,
|
||||
float masterInjectorGain,
|
||||
float primaryAvatarGain,
|
||||
float primaryInjectorGain,
|
||||
bool isSoloing) {
|
||||
++stats.totalMixes;
|
||||
|
||||
|
@ -505,8 +505,8 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre
|
|||
|
||||
float distance = glm::max(glm::length(relativePosition), EPSILON);
|
||||
float gain = isEcho ? 1.0f
|
||||
: (isSoloing ? masterAvatarGain
|
||||
: computeGain(masterAvatarGain, masterInjectorGain, listeningNodeStream, *streamToAdd,
|
||||
: (isSoloing ? primaryAvatarGain
|
||||
: computeGain(primaryAvatarGain, primaryInjectorGain, listeningNodeStream, *streamToAdd,
|
||||
relativePosition, distance));
|
||||
float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
|
||||
|
||||
|
@ -575,10 +575,10 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre
|
|||
}
|
||||
}
|
||||
|
||||
void AudioMixerSlave::updateHRTFParameters(AudioMixerClientData::MixableStream& mixableStream,
|
||||
void AudioMixerWorker::updateHRTFParameters(AudioMixerClientData::MixableStream& mixableStream,
|
||||
AvatarAudioStream& listeningNodeStream,
|
||||
float masterAvatarGain,
|
||||
float masterInjectorGain) {
|
||||
float primaryAvatarGain,
|
||||
float primaryInjectorGain) {
|
||||
auto streamToAdd = mixableStream.positionalStream;
|
||||
|
||||
// check if this is a server echo of a source back to itself
|
||||
|
@ -587,7 +587,7 @@ void AudioMixerSlave::updateHRTFParameters(AudioMixerClientData::MixableStream&
|
|||
glm::vec3 relativePosition = streamToAdd->getPosition() - listeningNodeStream.getPosition();
|
||||
|
||||
float distance = glm::max(glm::length(relativePosition), EPSILON);
|
||||
float gain = isEcho ? 1.0f : computeGain(masterAvatarGain, masterInjectorGain, listeningNodeStream, *streamToAdd,
|
||||
float gain = isEcho ? 1.0f : computeGain(primaryAvatarGain, primaryInjectorGain, listeningNodeStream, *streamToAdd,
|
||||
relativePosition, distance);
|
||||
float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
|
||||
|
||||
|
@ -596,7 +596,7 @@ void AudioMixerSlave::updateHRTFParameters(AudioMixerClientData::MixableStream&
|
|||
++stats.hrtfUpdates;
|
||||
}
|
||||
|
||||
void AudioMixerSlave::resetHRTFState(AudioMixerClientData::MixableStream& mixableStream) {
|
||||
void AudioMixerWorker::resetHRTFState(AudioMixerClientData::MixableStream& mixableStream) {
|
||||
mixableStream.hrtf->reset();
|
||||
++stats.hrtfResets;
|
||||
}
|
||||
|
@ -713,7 +713,7 @@ float approximateGain(const AvatarAudioStream& listeningNodeStream, const Positi
|
|||
// injector: apply attenuation
|
||||
if (streamToAdd.getType() == PositionalAudioStream::Injector) {
|
||||
gain *= reinterpret_cast<const InjectedAudioStream*>(&streamToAdd)->getAttenuationRatio();
|
||||
// injector: skip master gain
|
||||
// injector: skip primary gain
|
||||
}
|
||||
|
||||
// avatar: skip attenuation - it is too costly to approximate
|
||||
|
@ -723,11 +723,11 @@ float approximateGain(const AvatarAudioStream& listeningNodeStream, const Positi
|
|||
float distance = glm::length(relativePosition);
|
||||
return gain / distance;
|
||||
|
||||
// avatar: skip master gain
|
||||
// avatar: skip primary gain
|
||||
}
|
||||
|
||||
float computeGain(float masterAvatarGain,
|
||||
float masterInjectorGain,
|
||||
float computeGain(float primaryAvatarGain,
|
||||
float primaryInjectorGain,
|
||||
const AvatarAudioStream& listeningNodeStream,
|
||||
const PositionalAudioStream& streamToAdd,
|
||||
const glm::vec3& relativePosition,
|
||||
|
@ -737,8 +737,8 @@ float computeGain(float masterAvatarGain,
|
|||
// injector: apply attenuation
|
||||
if (streamToAdd.getType() == PositionalAudioStream::Injector) {
|
||||
gain *= reinterpret_cast<const InjectedAudioStream*>(&streamToAdd)->getAttenuationRatio();
|
||||
// apply master gain
|
||||
gain *= masterInjectorGain;
|
||||
// apply primary gain
|
||||
gain *= primaryInjectorGain;
|
||||
|
||||
// avatar: apply fixed off-axis attenuation to make them quieter as they turn away
|
||||
} else if (streamToAdd.getType() == PositionalAudioStream::Microphone) {
|
||||
|
@ -754,8 +754,8 @@ float computeGain(float masterAvatarGain,
|
|||
|
||||
gain *= offAxisCoefficient;
|
||||
|
||||
// apply master gain
|
||||
gain *= masterAvatarGain;
|
||||
// apply primary gain
|
||||
gain *= primaryAvatarGain;
|
||||
}
|
||||
|
||||
auto& audioZones = AudioMixer::getAudioZones();
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// AudioMixerSlave.h
|
||||
// AudioMixerWorker.h
|
||||
// assignment-client/src/audio
|
||||
//
|
||||
// Created by Zach Pomerantz on 11/22/16.
|
||||
|
@ -9,8 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AudioMixerSlave_h
|
||||
#define hifi_AudioMixerSlave_h
|
||||
#ifndef hifi_AudioMixerWorker_h
|
||||
#define hifi_AudioMixerWorker_h
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
// Work around https://bugreports.qt.io/browse/QTBUG-80990
|
||||
|
@ -31,7 +31,7 @@
|
|||
class AvatarAudioStream;
|
||||
class AudioHRTF;
|
||||
|
||||
class AudioMixerSlave {
|
||||
class AudioMixerWorker {
|
||||
public:
|
||||
using ConstIter = NodeList::const_iterator;
|
||||
|
||||
|
@ -41,7 +41,7 @@ public:
|
|||
std::vector<NodeIDStreamID> removedStreams;
|
||||
};
|
||||
|
||||
AudioMixerSlave(SharedData& sharedData) : _sharedData(sharedData) {};
|
||||
AudioMixerWorker(SharedData& sharedData) : _sharedData(sharedData) {};
|
||||
|
||||
// process packets for a given node (requires no configuration)
|
||||
void processPackets(const SharedNodePointer& node);
|
||||
|
@ -60,13 +60,13 @@ private:
|
|||
bool prepareMix(const SharedNodePointer& listener);
|
||||
void addStream(AudioMixerClientData::MixableStream& mixableStream,
|
||||
AvatarAudioStream& listeningNodeStream,
|
||||
float masterAvatarGain,
|
||||
float masterInjectorGain,
|
||||
float primaryAvatarGain,
|
||||
float primaryInjectorGain,
|
||||
bool isSoloing);
|
||||
void updateHRTFParameters(AudioMixerClientData::MixableStream& mixableStream,
|
||||
AvatarAudioStream& listeningNodeStream,
|
||||
float masterAvatarGain,
|
||||
float masterInjectorGain);
|
||||
float primaryAvatarGain,
|
||||
float primaryInjectorGain);
|
||||
void resetHRTFState(AudioMixerClientData::MixableStream& mixableStream);
|
||||
|
||||
void addStreams(Node& listener, AudioMixerClientData& listenerData);
|
||||
|
@ -84,4 +84,4 @@ private:
|
|||
SharedData& _sharedData;
|
||||
};
|
||||
|
||||
#endif // hifi_AudioMixerSlave_h
|
||||
#endif // hifi_AudioMixerWorker_h
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// AudioMixerSlavePool.cpp
|
||||
// AudioMixerWorkerPool.cpp
|
||||
// assignment-client/src/audio
|
||||
//
|
||||
// Created by Zach Pomerantz on 11/16/2016.
|
||||
|
@ -9,7 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AudioMixerSlavePool.h"
|
||||
#include "AudioMixerWorkerPool.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
|||
|
||||
#include <ThreadHelpers.h>
|
||||
|
||||
void AudioMixerSlaveThread::run() {
|
||||
void AudioMixerWorkerThread::run() {
|
||||
while (true) {
|
||||
wait();
|
||||
|
||||
|
@ -36,10 +36,10 @@ void AudioMixerSlaveThread::run() {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioMixerSlaveThread::wait() {
|
||||
void AudioMixerWorkerThread::wait() {
|
||||
{
|
||||
Lock lock(_pool._mutex);
|
||||
_pool._slaveCondition.wait(lock, [&] {
|
||||
_pool._workerCondition.wait(lock, [&] {
|
||||
assert(_pool._numStarted <= _pool._numThreads);
|
||||
return _pool._numStarted != _pool._numThreads;
|
||||
});
|
||||
|
@ -52,7 +52,7 @@ void AudioMixerSlaveThread::wait() {
|
|||
_function = _pool._function;
|
||||
}
|
||||
|
||||
void AudioMixerSlaveThread::notify(bool stopping) {
|
||||
void AudioMixerWorkerThread::notify(bool stopping) {
|
||||
{
|
||||
Lock lock(_pool._mutex);
|
||||
assert(_pool._numFinished < _pool._numThreads);
|
||||
|
@ -64,26 +64,26 @@ void AudioMixerSlaveThread::notify(bool stopping) {
|
|||
_pool._poolCondition.notify_one();
|
||||
}
|
||||
|
||||
bool AudioMixerSlaveThread::try_pop(SharedNodePointer& node) {
|
||||
bool AudioMixerWorkerThread::try_pop(SharedNodePointer& node) {
|
||||
return _pool._queue.try_pop(node);
|
||||
}
|
||||
|
||||
void AudioMixerSlavePool::processPackets(ConstIter begin, ConstIter end) {
|
||||
_function = &AudioMixerSlave::processPackets;
|
||||
_configure = [](AudioMixerSlave& slave) {};
|
||||
void AudioMixerWorkerPool::processPackets(ConstIter begin, ConstIter end) {
|
||||
_function = &AudioMixerWorker::processPackets;
|
||||
_configure = [](AudioMixerWorker& worker) {};
|
||||
run(begin, end);
|
||||
}
|
||||
|
||||
void AudioMixerSlavePool::mix(ConstIter begin, ConstIter end, unsigned int frame, int numToRetain) {
|
||||
_function = &AudioMixerSlave::mix;
|
||||
_configure = [=](AudioMixerSlave& slave) {
|
||||
slave.configureMix(_begin, _end, frame, numToRetain);
|
||||
void AudioMixerWorkerPool::mix(ConstIter begin, ConstIter end, unsigned int frame, int numToRetain) {
|
||||
_function = &AudioMixerWorker::mix;
|
||||
_configure = [=](AudioMixerWorker& worker) {
|
||||
worker.configureMix(_begin, _end, frame, numToRetain);
|
||||
};
|
||||
|
||||
run(begin, end);
|
||||
}
|
||||
|
||||
void AudioMixerSlavePool::run(ConstIter begin, ConstIter end) {
|
||||
void AudioMixerWorkerPool::run(ConstIter begin, ConstIter end) {
|
||||
_begin = begin;
|
||||
_end = end;
|
||||
|
||||
|
@ -97,7 +97,7 @@ void AudioMixerSlavePool::run(ConstIter begin, ConstIter end) {
|
|||
|
||||
// run
|
||||
_numStarted = _numFinished = 0;
|
||||
_slaveCondition.notify_all();
|
||||
_workerCondition.notify_all();
|
||||
|
||||
// wait
|
||||
_poolCondition.wait(lock, [&] {
|
||||
|
@ -111,17 +111,17 @@ void AudioMixerSlavePool::run(ConstIter begin, ConstIter end) {
|
|||
assert(_queue.empty());
|
||||
}
|
||||
|
||||
void AudioMixerSlavePool::each(std::function<void(AudioMixerSlave& slave)> functor) {
|
||||
for (auto& slave : _slaves) {
|
||||
functor(*slave.get());
|
||||
void AudioMixerWorkerPool::each(std::function<void(AudioMixerWorker& worker)> functor) {
|
||||
for (auto& worker : _workers) {
|
||||
functor(*worker.get());
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_EVENT_QUEUE
|
||||
void AudioMixerSlavePool::queueStats(QJsonObject& stats) {
|
||||
void AudioMixerWorkerPool::queueStats(QJsonObject& stats) {
|
||||
unsigned i = 0;
|
||||
for (auto& slave : _slaves) {
|
||||
int queueSize = ::hifi::qt::getEventQueueSize(slave.get());
|
||||
for (auto& worker : _workers) {
|
||||
int queueSize = ::hifi::qt::getEventQueueSize(worker.get());
|
||||
QString queueName = QString("audio_thread_event_queue_%1").arg(i);
|
||||
stats[queueName] = queueSize;
|
||||
|
||||
|
@ -130,7 +130,7 @@ void AudioMixerSlavePool::queueStats(QJsonObject& stats) {
|
|||
}
|
||||
#endif // DEBUG_EVENT_QUEUE
|
||||
|
||||
void AudioMixerSlavePool::setNumThreads(int numThreads) {
|
||||
void AudioMixerWorkerPool::setNumThreads(int numThreads) {
|
||||
// clamp to allowed size
|
||||
{
|
||||
int maxThreads = QThread::idealThreadCount();
|
||||
|
@ -150,36 +150,36 @@ void AudioMixerSlavePool::setNumThreads(int numThreads) {
|
|||
resize(numThreads);
|
||||
}
|
||||
|
||||
void AudioMixerSlavePool::resize(int numThreads) {
|
||||
assert(_numThreads == (int)_slaves.size());
|
||||
void AudioMixerWorkerPool::resize(int numThreads) {
|
||||
assert(_numThreads == (int)_workers.size());
|
||||
|
||||
qDebug("%s: set %d threads (was %d)", __FUNCTION__, numThreads, _numThreads);
|
||||
|
||||
Lock lock(_mutex);
|
||||
|
||||
if (numThreads > _numThreads) {
|
||||
// start new slaves
|
||||
// start new workers
|
||||
for (int i = 0; i < numThreads - _numThreads; ++i) {
|
||||
auto slave = new AudioMixerSlaveThread(*this, _workerSharedData);
|
||||
QObject::connect(slave, &QThread::started, [] { setThreadName("AudioMixerSlaveThread"); });
|
||||
slave->start();
|
||||
_slaves.emplace_back(slave);
|
||||
auto worker = new AudioMixerWorkerThread(*this, _workerSharedData);
|
||||
QObject::connect(worker, &QThread::started, [] { setThreadName("AudioMixerWorkerThread"); });
|
||||
worker->start();
|
||||
_workers.emplace_back(worker);
|
||||
}
|
||||
} else if (numThreads < _numThreads) {
|
||||
auto extraBegin = _slaves.begin() + numThreads;
|
||||
auto extraBegin = _workers.begin() + numThreads;
|
||||
|
||||
// mark slaves to stop...
|
||||
auto slave = extraBegin;
|
||||
while (slave != _slaves.end()) {
|
||||
(*slave)->_stop = true;
|
||||
++slave;
|
||||
// mark workers to stop...
|
||||
auto worker = extraBegin;
|
||||
while (worker != _workers.end()) {
|
||||
(*worker)->_stop = true;
|
||||
++worker;
|
||||
}
|
||||
|
||||
// ...cycle them until they do stop...
|
||||
_numStopped = 0;
|
||||
while (_numStopped != (_numThreads - numThreads)) {
|
||||
_numStarted = _numFinished = _numStopped;
|
||||
_slaveCondition.notify_all();
|
||||
_workerCondition.notify_all();
|
||||
_poolCondition.wait(lock, [&] {
|
||||
assert(_numFinished <= _numThreads);
|
||||
return _numFinished == _numThreads;
|
||||
|
@ -187,18 +187,18 @@ void AudioMixerSlavePool::resize(int numThreads) {
|
|||
}
|
||||
|
||||
// ...wait for threads to finish...
|
||||
slave = extraBegin;
|
||||
while (slave != _slaves.end()) {
|
||||
QThread* thread = reinterpret_cast<QThread*>(slave->get());
|
||||
worker = extraBegin;
|
||||
while (worker != _workers.end()) {
|
||||
QThread* thread = reinterpret_cast<QThread*>(worker->get());
|
||||
static const int MAX_THREAD_WAIT_TIME = 10;
|
||||
thread->wait(MAX_THREAD_WAIT_TIME);
|
||||
++slave;
|
||||
++worker;
|
||||
}
|
||||
|
||||
// ...and erase them
|
||||
_slaves.erase(extraBegin, _slaves.end());
|
||||
_workers.erase(extraBegin, _workers.end());
|
||||
}
|
||||
|
||||
_numThreads = _numStarted = _numFinished = numThreads;
|
||||
assert(_numThreads == (int)_slaves.size());
|
||||
assert(_numThreads == (int)_workers.size());
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// AudioMixerSlavePool.h
|
||||
// AudioMixerWorkerPool.h
|
||||
// assignment-client/src/audio
|
||||
//
|
||||
// Created by Zach Pomerantz on 11/16/2016.
|
||||
|
@ -9,8 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AudioMixerSlavePool_h
|
||||
#define hifi_AudioMixerSlavePool_h
|
||||
#ifndef hifi_AudioMixerWorkerPool_h
|
||||
#define hifi_AudioMixerWorkerPool_h
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
@ -20,37 +20,37 @@
|
|||
#include <shared/QtHelpers.h>
|
||||
#include <TBBHelpers.h>
|
||||
|
||||
#include "AudioMixerSlave.h"
|
||||
#include "AudioMixerWorker.h"
|
||||
|
||||
class AudioMixerSlavePool;
|
||||
class AudioMixerWorkerPool;
|
||||
|
||||
class AudioMixerSlaveThread : public QThread, public AudioMixerSlave {
|
||||
class AudioMixerWorkerThread : public QThread, public AudioMixerWorker {
|
||||
Q_OBJECT
|
||||
using ConstIter = NodeList::const_iterator;
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
|
||||
public:
|
||||
AudioMixerSlaveThread(AudioMixerSlavePool& pool, AudioMixerSlave::SharedData& sharedData)
|
||||
: AudioMixerSlave(sharedData), _pool(pool) {}
|
||||
AudioMixerWorkerThread(AudioMixerWorkerPool& pool, AudioMixerWorker::SharedData& sharedData)
|
||||
: AudioMixerWorker(sharedData), _pool(pool) {}
|
||||
|
||||
void run() override final;
|
||||
|
||||
private:
|
||||
friend class AudioMixerSlavePool;
|
||||
friend class AudioMixerWorkerPool;
|
||||
|
||||
void wait();
|
||||
void notify(bool stopping);
|
||||
bool try_pop(SharedNodePointer& node);
|
||||
|
||||
AudioMixerSlavePool& _pool;
|
||||
void (AudioMixerSlave::*_function)(const SharedNodePointer& node) { nullptr };
|
||||
AudioMixerWorkerPool& _pool;
|
||||
void (AudioMixerWorker::*_function)(const SharedNodePointer& node) { nullptr };
|
||||
bool _stop { false };
|
||||
};
|
||||
|
||||
// Slave pool for audio mixers
|
||||
// AudioMixerSlavePool is not thread-safe! It should be instantiated and used from a single thread.
|
||||
class AudioMixerSlavePool {
|
||||
// Worker pool for audio mixers
|
||||
// AudioMixerWorkerPool is not thread-safe! It should be instantiated and used from a single thread.
|
||||
class AudioMixerWorkerPool {
|
||||
using Queue = tbb::concurrent_queue<SharedNodePointer>;
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
|
@ -59,18 +59,18 @@ class AudioMixerSlavePool {
|
|||
public:
|
||||
using ConstIter = NodeList::const_iterator;
|
||||
|
||||
AudioMixerSlavePool(AudioMixerSlave::SharedData& sharedData, int numThreads = QThread::idealThreadCount())
|
||||
AudioMixerWorkerPool(AudioMixerWorker::SharedData& sharedData, int numThreads = QThread::idealThreadCount())
|
||||
: _workerSharedData(sharedData) { setNumThreads(numThreads); }
|
||||
~AudioMixerSlavePool() { resize(0); }
|
||||
~AudioMixerWorkerPool() { resize(0); }
|
||||
|
||||
// process packets on slave threads
|
||||
// process packets on worker threads
|
||||
void processPackets(ConstIter begin, ConstIter end);
|
||||
|
||||
// mix on slave threads
|
||||
// mix on worker threads
|
||||
void mix(ConstIter begin, ConstIter end, unsigned int frame, int numToRetain);
|
||||
|
||||
// iterate over all slaves
|
||||
void each(std::function<void(AudioMixerSlave& slave)> functor);
|
||||
// iterate over all workers
|
||||
void each(std::function<void(AudioMixerWorker& worker)> functor);
|
||||
|
||||
#ifdef DEBUG_EVENT_QUEUE
|
||||
void queueStats(QJsonObject& stats);
|
||||
|
@ -83,18 +83,18 @@ private:
|
|||
void run(ConstIter begin, ConstIter end);
|
||||
void resize(int numThreads);
|
||||
|
||||
std::vector<std::unique_ptr<AudioMixerSlaveThread>> _slaves;
|
||||
std::vector<std::unique_ptr<AudioMixerWorkerThread>> _workers;
|
||||
|
||||
friend void AudioMixerSlaveThread::wait();
|
||||
friend void AudioMixerSlaveThread::notify(bool stopping);
|
||||
friend bool AudioMixerSlaveThread::try_pop(SharedNodePointer& node);
|
||||
friend void AudioMixerWorkerThread::wait();
|
||||
friend void AudioMixerWorkerThread::notify(bool stopping);
|
||||
friend bool AudioMixerWorkerThread::try_pop(SharedNodePointer& node);
|
||||
|
||||
// synchronization state
|
||||
Mutex _mutex;
|
||||
ConditionVariable _slaveCondition;
|
||||
ConditionVariable _workerCondition;
|
||||
ConditionVariable _poolCondition;
|
||||
void (AudioMixerSlave::*_function)(const SharedNodePointer& node);
|
||||
std::function<void(AudioMixerSlave&)> _configure;
|
||||
void (AudioMixerWorker::*_function)(const SharedNodePointer& node);
|
||||
std::function<void(AudioMixerWorker&)> _configure;
|
||||
int _numThreads { 0 };
|
||||
int _numStarted { 0 }; // guarded by _mutex
|
||||
int _numFinished { 0 }; // guarded by _mutex
|
||||
|
@ -105,7 +105,7 @@ private:
|
|||
ConstIter _begin;
|
||||
ConstIter _end;
|
||||
|
||||
AudioMixerSlave::SharedData& _workerSharedData;
|
||||
AudioMixerWorker::SharedData& _workerSharedData;
|
||||
};
|
||||
|
||||
#endif // hifi_AudioMixerSlavePool_h
|
||||
#endif // hifi_AudioMixerWorkerPool_h
|
|
@ -60,7 +60,7 @@ bool AvatarMixer::SessionDisplayName::operator<(const SessionDisplayName& rhs) c
|
|||
|
||||
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message),
|
||||
_slavePool(&_slaveSharedData)
|
||||
_workerPool(&_workerSharedData)
|
||||
{
|
||||
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
|
||||
DependencyManager::set<AssignmentDynamicFactory>();
|
||||
|
@ -297,7 +297,7 @@ void AvatarMixer::start() {
|
|||
auto end = usecTimestampNow();
|
||||
_processQueuedAvatarDataPacketsLockWaitElapsedTime += (end - start);
|
||||
|
||||
_slavePool.processIncomingPackets(cbegin, cend);
|
||||
_workerPool.processIncomingPackets(cbegin, cend);
|
||||
}, &lockWait, &nodeTransform, &functor);
|
||||
auto end = usecTimestampNow();
|
||||
_processQueuedAvatarDataPacketsElapsedTime += (end - start);
|
||||
|
@ -333,7 +333,7 @@ void AvatarMixer::start() {
|
|||
auto start = usecTimestampNow();
|
||||
nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) {
|
||||
auto start = usecTimestampNow();
|
||||
_slavePool.broadcastAvatarData(cbegin, cend, _lastFrameTimestamp, _maxKbpsPerNode, _throttlingRatio);
|
||||
_workerPool.broadcastAvatarData(cbegin, cend, _lastFrameTimestamp, _maxKbpsPerNode, _throttlingRatio);
|
||||
auto end = usecTimestampNow();
|
||||
_broadcastAvatarDataInner += (end - start);
|
||||
}, &lockWait, &nodeTransform, &functor);
|
||||
|
@ -761,14 +761,14 @@ void AvatarMixer::sendStatsPacket() {
|
|||
QJsonObject statsObject;
|
||||
|
||||
statsObject["broadcast_loop_rate"] = _loopRate.rate();
|
||||
statsObject["threads"] = _slavePool.numThreads();
|
||||
statsObject["threads"] = _workerPool.numThreads();
|
||||
statsObject["trailing_mix_ratio"] = _trailingMixRatio;
|
||||
statsObject["throttling_ratio"] = _throttlingRatio;
|
||||
|
||||
#ifdef DEBUG_EVENT_QUEUE
|
||||
QJsonObject qtStats;
|
||||
|
||||
_slavePool.queueStats(qtStats);
|
||||
_workerPool.queueStats(qtStats);
|
||||
statsObject["avatar_thread_event_queue"] = qtStats;
|
||||
#endif
|
||||
|
||||
|
@ -821,41 +821,41 @@ void AvatarMixer::sendStatsPacket() {
|
|||
statsObject["parallelTasks"] = parallelTasks;
|
||||
|
||||
|
||||
AvatarMixerSlaveStats aggregateStats;
|
||||
AvatarMixerWorkerStats aggregateStats;
|
||||
|
||||
// gather stats
|
||||
_slavePool.each([&](AvatarMixerSlave& slave) {
|
||||
AvatarMixerSlaveStats stats;
|
||||
slave.harvestStats(stats);
|
||||
_workerPool.each([&](AvatarMixerWorker& worker) {
|
||||
AvatarMixerWorkerStats stats;
|
||||
worker.harvestStats(stats);
|
||||
aggregateStats += stats;
|
||||
});
|
||||
|
||||
QJsonObject slavesAggregatObject;
|
||||
QJsonObject workersAggregatObject;
|
||||
|
||||
slavesAggregatObject["received_1_nodesProcessed"] = TIGHT_LOOP_STAT(aggregateStats.nodesProcessed);
|
||||
workersAggregatObject["received_1_nodesProcessed"] = TIGHT_LOOP_STAT(aggregateStats.nodesProcessed);
|
||||
|
||||
slavesAggregatObject["sent_1_nodesBroadcastedTo"] = TIGHT_LOOP_STAT(aggregateStats.nodesBroadcastedTo);
|
||||
workersAggregatObject["sent_1_nodesBroadcastedTo"] = TIGHT_LOOP_STAT(aggregateStats.nodesBroadcastedTo);
|
||||
|
||||
float averageNodes = ((float)aggregateStats.nodesBroadcastedTo / (float)tightLoopFrames);
|
||||
|
||||
float averageOthersIncluded = averageNodes ? aggregateStats.numOthersIncluded / averageNodes : 0.0f;
|
||||
slavesAggregatObject["sent_2_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded);
|
||||
workersAggregatObject["sent_2_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded);
|
||||
|
||||
float averageOverBudgetAvatars = averageNodes ? aggregateStats.overBudgetAvatars / averageNodes : 0.0f;
|
||||
slavesAggregatObject["sent_3_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars);
|
||||
slavesAggregatObject["sent_4_averageDataBytes"] = TIGHT_LOOP_STAT(aggregateStats.numDataBytesSent);
|
||||
slavesAggregatObject["sent_5_averageTraitsBytes"] = TIGHT_LOOP_STAT(aggregateStats.numTraitsBytesSent);
|
||||
slavesAggregatObject["sent_6_averageIdentityBytes"] = TIGHT_LOOP_STAT(aggregateStats.numIdentityBytesSent);
|
||||
slavesAggregatObject["sent_7_averageHeroAvatars"] = TIGHT_LOOP_STAT(aggregateStats.numHeroesIncluded);
|
||||
workersAggregatObject["sent_3_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars);
|
||||
workersAggregatObject["sent_4_averageDataBytes"] = TIGHT_LOOP_STAT(aggregateStats.numDataBytesSent);
|
||||
workersAggregatObject["sent_5_averageTraitsBytes"] = TIGHT_LOOP_STAT(aggregateStats.numTraitsBytesSent);
|
||||
workersAggregatObject["sent_6_averageIdentityBytes"] = TIGHT_LOOP_STAT(aggregateStats.numIdentityBytesSent);
|
||||
workersAggregatObject["sent_7_averageHeroAvatars"] = TIGHT_LOOP_STAT(aggregateStats.numHeroesIncluded);
|
||||
|
||||
slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime);
|
||||
slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime);
|
||||
slavesAggregatObject["timing_3_toByteArray"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.toByteArrayElapsedTime);
|
||||
slavesAggregatObject["timing_4_avatarDataPacking"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.avatarDataPackingElapsedTime);
|
||||
slavesAggregatObject["timing_5_packetSending"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.packetSendingElapsedTime);
|
||||
slavesAggregatObject["timing_6_jobElapsedTime"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.jobElapsedTime);
|
||||
workersAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime);
|
||||
workersAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime);
|
||||
workersAggregatObject["timing_3_toByteArray"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.toByteArrayElapsedTime);
|
||||
workersAggregatObject["timing_4_avatarDataPacking"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.avatarDataPackingElapsedTime);
|
||||
workersAggregatObject["timing_5_packetSending"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.packetSendingElapsedTime);
|
||||
workersAggregatObject["timing_6_jobElapsedTime"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.jobElapsedTime);
|
||||
|
||||
statsObject["slaves_aggregate (per frame)"] = slavesAggregatObject;
|
||||
statsObject["workers_aggregate (per frame)"] = workersAggregatObject;
|
||||
|
||||
_handleViewFrustumPacketElapsedTime = 0;
|
||||
_handleAvatarIdentityPacketElapsedTime = 0;
|
||||
|
@ -1016,9 +1016,9 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
|||
numThreads = 1;
|
||||
}
|
||||
qCDebug(avatars) << "Avatar mixer will use specified number of threads:" << numThreads;
|
||||
_slavePool.setNumThreads(numThreads);
|
||||
_workerPool.setNumThreads(numThreads);
|
||||
} else {
|
||||
qCDebug(avatars) << "Avatar mixer will automatically determine number of threads to use. Using:" << _slavePool.numThreads() << "threads.";
|
||||
qCDebug(avatars) << "Avatar mixer will automatically determine number of threads to use. Using:" << _workerPool.numThreads() << "threads.";
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1035,7 +1035,7 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
|||
static const QString PRIORITY_FRACTION_KEY = "priority_fraction";
|
||||
if (avatarMixerGroupObject.contains(PRIORITY_FRACTION_KEY)) {
|
||||
float priorityFraction = float(avatarMixerGroupObject[PRIORITY_FRACTION_KEY].toDouble());
|
||||
_slavePool.setPriorityReservedFraction(std::min(std::max(0.0f, priorityFraction), 1.0f));
|
||||
_workerPool.setPriorityReservedFraction(std::min(std::max(0.0f, priorityFraction), 1.0f));
|
||||
qCDebug(avatars) << "Avatar mixer reserving" << priorityFraction << "of bandwidth for priority avatars";
|
||||
}
|
||||
}
|
||||
|
@ -1058,23 +1058,23 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
|||
qCDebug(avatars) << "This domain requires a minimum avatar height of" << _domainMinimumHeight
|
||||
<< "and a maximum avatar height of" << _domainMaximumHeight;
|
||||
|
||||
static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist";
|
||||
_slaveSharedData.skeletonURLWhitelist = avatarMixerGroupObject[AVATAR_WHITELIST_OPTION]
|
||||
static const QString AVATAR_ALLOWLIST_OPTION = "avatar_allowlist";
|
||||
_workerSharedData.skeletonURLAllowlist = avatarMixerGroupObject[AVATAR_ALLOWLIST_OPTION]
|
||||
.toString().split(',', Qt::KeepEmptyParts);
|
||||
|
||||
static const QString REPLACEMENT_AVATAR_OPTION = "replacement_avatar";
|
||||
_slaveSharedData.skeletonReplacementURL = avatarMixerGroupObject[REPLACEMENT_AVATAR_OPTION]
|
||||
_workerSharedData.skeletonReplacementURL = avatarMixerGroupObject[REPLACEMENT_AVATAR_OPTION]
|
||||
.toString();
|
||||
|
||||
if (_slaveSharedData.skeletonURLWhitelist.count() == 1 && _slaveSharedData.skeletonURLWhitelist[0].isEmpty()) {
|
||||
if (_workerSharedData.skeletonURLAllowlist.count() == 1 && _workerSharedData.skeletonURLAllowlist[0].isEmpty()) {
|
||||
// KeepEmptyParts above will parse "," as ["", ""] (which is ok), but "" as [""] (which is not ok).
|
||||
_slaveSharedData.skeletonURLWhitelist.clear();
|
||||
_workerSharedData.skeletonURLAllowlist.clear();
|
||||
}
|
||||
|
||||
if (_slaveSharedData.skeletonURLWhitelist.isEmpty()) {
|
||||
if (_workerSharedData.skeletonURLAllowlist.isEmpty()) {
|
||||
qCDebug(avatars) << "All avatars are allowed.";
|
||||
} else {
|
||||
qCDebug(avatars) << "Avatars other than" << _slaveSharedData.skeletonURLWhitelist << "will be replaced by" << (_slaveSharedData.skeletonReplacementURL.isEmpty() ? "default" : _slaveSharedData.skeletonReplacementURL.toString());
|
||||
qCDebug(avatars) << "Avatars other than" << _workerSharedData.skeletonURLAllowlist << "will be replaced by" << (_workerSharedData.skeletonReplacementURL.isEmpty() ? "default" : _workerSharedData.skeletonReplacementURL.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1099,7 +1099,7 @@ void AvatarMixer::setupEntityQuery() {
|
|||
priorityZoneQuery["name"] = true; // Handy for debugging.
|
||||
|
||||
_entityViewer.getOctreeQuery().setJSONParameters(priorityZoneQuery);
|
||||
_slaveSharedData.entityTree = entityTree;
|
||||
_workerSharedData.entityTree = entityTree;
|
||||
}
|
||||
|
||||
void AvatarMixer::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include "../entities/EntityTreeHeadlessViewer.h"
|
||||
#include "AvatarMixerClientData.h"
|
||||
|
||||
#include "AvatarMixerSlavePool.h"
|
||||
#include "AvatarMixerWorkerPool.h"
|
||||
|
||||
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
|
||||
class AvatarMixer : public ThreadedAssignment {
|
||||
|
@ -153,8 +153,8 @@ private:
|
|||
|
||||
RateCounter<> _loopRate; // this is the rate that the main thread tight loop runs
|
||||
|
||||
AvatarMixerSlavePool _slavePool;
|
||||
SlaveSharedData _slaveSharedData;
|
||||
AvatarMixerWorkerPool _workerPool;
|
||||
WorkerSharedData _workerSharedData;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarMixer_h
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#include "AvatarLogging.h"
|
||||
|
||||
#include "AvatarMixerSlave.h"
|
||||
#include "AvatarMixerWorker.h"
|
||||
|
||||
AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : NodeData(nodeID, nodeLocalID) {
|
||||
// in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID
|
||||
|
@ -52,7 +52,7 @@ void AvatarMixerClientData::queuePacket(QSharedPointer<ReceivedMessage> message,
|
|||
_packetQueue.push(message);
|
||||
}
|
||||
|
||||
int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData) {
|
||||
int AvatarMixerClientData::processPackets(const WorkerSharedData& workerSharedData) {
|
||||
int packetsProcessed = 0;
|
||||
SharedNodePointer node = _packetQueue.node;
|
||||
assert(_packetQueue.empty() || node);
|
||||
|
@ -65,10 +65,10 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData
|
|||
|
||||
switch (packet->getType()) {
|
||||
case PacketType::AvatarData:
|
||||
parseData(*packet, slaveSharedData);
|
||||
parseData(*packet, workerSharedData);
|
||||
break;
|
||||
case PacketType::SetAvatarTraits:
|
||||
processSetTraitsMessage(*packet, slaveSharedData, *node);
|
||||
processSetTraitsMessage(*packet, workerSharedData, *node);
|
||||
break;
|
||||
case PacketType::BulkAvatarTraitsAck:
|
||||
processBulkAvatarTraitsAckMessage(*packet);
|
||||
|
@ -127,7 +127,7 @@ struct FindContainingZone {
|
|||
|
||||
} // namespace
|
||||
|
||||
int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveSharedData& slaveSharedData) {
|
||||
int AvatarMixerClientData::parseData(ReceivedMessage& message, const WorkerSharedData& workerSharedData) {
|
||||
// pull the sequence number from the data first
|
||||
uint16_t sequenceNumber;
|
||||
|
||||
|
@ -150,7 +150,7 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared
|
|||
|
||||
auto newPosition = _avatar->getClientGlobalPosition();
|
||||
if (newPosition != oldPosition || _avatar->getNeedsHeroCheck()) {
|
||||
EntityTree& entityTree = *slaveSharedData.entityTree;
|
||||
EntityTree& entityTree = *workerSharedData.entityTree;
|
||||
FindContainingZone findContainingZone{ newPosition };
|
||||
entityTree.recurseTreeWithOperation(&FindContainingZone::operation, &findContainingZone);
|
||||
bool currentlyHasPriority = findContainingZone.isInPriorityZone;
|
||||
|
@ -176,7 +176,7 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared
|
|||
}
|
||||
|
||||
void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
||||
const SlaveSharedData& slaveSharedData,
|
||||
const WorkerSharedData& workerSharedData,
|
||||
Node& sendingNode) {
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message.getBytesLeftToRead() < qint64(sizeof(AvatarTraits::TraitVersion))) {
|
||||
|
@ -221,8 +221,8 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
_avatar->processTrait(traitType, message.read(traitSize));
|
||||
_lastReceivedTraitVersions[traitType] = packetTraitVersion;
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
// special handling for skeleton model URL, since we need to make sure it is in the whitelist
|
||||
checkSkeletonURLAgainstWhitelist(slaveSharedData, sendingNode, packetTraitVersion);
|
||||
// special handling for skeleton model URL, since we need to make sure it is in the allowlist
|
||||
checkSkeletonURLAgainstAllowlist(workerSharedData, sendingNode, packetTraitVersion);
|
||||
}
|
||||
|
||||
anyTraitsChanged = true;
|
||||
|
@ -366,36 +366,36 @@ void AvatarMixerClientData::processBulkAvatarTraitsAckMessage(ReceivedMessage& m
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(const SlaveSharedData& slaveSharedData,
|
||||
void AvatarMixerClientData::checkSkeletonURLAgainstAllowlist(const WorkerSharedData& workerSharedData,
|
||||
Node& sendingNode,
|
||||
AvatarTraits::TraitVersion traitVersion) {
|
||||
const auto& whitelist = slaveSharedData.skeletonURLWhitelist;
|
||||
const auto& allowlist = workerSharedData.skeletonURLAllowlist;
|
||||
|
||||
if (!whitelist.isEmpty()) {
|
||||
bool inWhitelist = false;
|
||||
if (!allowlist.isEmpty()) {
|
||||
bool inAllowlist = false;
|
||||
auto avatarURL = _avatar->getSkeletonModelURL();
|
||||
|
||||
// The avatar is in the whitelist if:
|
||||
// 1. The avatar's URL's host matches one of the hosts of the URLs in the whitelist AND
|
||||
// 2. The avatar's URL's path starts with the path of that same URL in the whitelist
|
||||
for (const auto& whiteListedPrefix : whitelist) {
|
||||
auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix);
|
||||
// check if this script URL matches the whitelist domain and, optionally, is beneath the path
|
||||
if (avatarURL.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 &&
|
||||
avatarURL.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) {
|
||||
inWhitelist = true;
|
||||
// The avatar is in the allowlist if:
|
||||
// 1. The avatar's URL's host matches one of the hosts of the URLs in the allowlist AND
|
||||
// 2. The avatar's URL's path starts with the path of that same URL in the allowlist
|
||||
for (const auto& allowListedPrefix : allowlist) {
|
||||
auto allowListURL = QUrl::fromUserInput(allowListedPrefix);
|
||||
// check if this script URL matches the allowlist domain and, optionally, is beneath the path
|
||||
if (avatarURL.host().compare(allowListURL.host(), Qt::CaseInsensitive) == 0 &&
|
||||
avatarURL.path().startsWith(allowListURL.path(), Qt::CaseInsensitive)) {
|
||||
inAllowlist = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inWhitelist) {
|
||||
if (!inAllowlist) {
|
||||
// make sure we're not unecessarily overriding the default avatar with the default avatar
|
||||
if (_avatar->getWireSafeSkeletonModelURL() != slaveSharedData.skeletonReplacementURL) {
|
||||
if (_avatar->getWireSafeSkeletonModelURL() != workerSharedData.skeletonReplacementURL) {
|
||||
// we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change
|
||||
qDebug() << "Overwriting avatar URL" << _avatar->getWireSafeSkeletonModelURL() << "to replacement"
|
||||
<< slaveSharedData.skeletonReplacementURL << "for" << sendingNode.getUUID();
|
||||
_avatar->setSkeletonModelURL(slaveSharedData.skeletonReplacementURL);
|
||||
<< workerSharedData.skeletonReplacementURL << "for" << sendingNode.getUUID();
|
||||
_avatar->setSkeletonModelURL(workerSharedData.skeletonReplacementURL);
|
||||
|
||||
auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true);
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps";
|
|||
const QString OUTBOUND_AVATAR_TRAITS_STATS_KEY = "outbound_av_traits_kbps";
|
||||
const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
|
||||
|
||||
struct SlaveSharedData;
|
||||
struct WorkerSharedData;
|
||||
|
||||
class AvatarMixerClientData : public NodeData {
|
||||
Q_OBJECT
|
||||
|
@ -47,7 +47,7 @@ public:
|
|||
using PerNodeTraitVersions = std::unordered_map<Node::LocalID, AvatarTraits::TraitVersions>;
|
||||
|
||||
using NodeData::parseData; // Avoid clang warning about hiding.
|
||||
int parseData(ReceivedMessage& message, const SlaveSharedData& SlaveSharedData);
|
||||
int parseData(ReceivedMessage& message, const WorkerSharedData& WorkerSharedData);
|
||||
MixerAvatar& getAvatar() { return *_avatar; }
|
||||
const MixerAvatar& getAvatar() const { return *_avatar; }
|
||||
const MixerAvatar* getConstAvatarData() const { return _avatar.get(); }
|
||||
|
@ -130,12 +130,12 @@ public:
|
|||
QVector<JointData>& getLastOtherAvatarSentJoints(NLPacket::LocalID otherAvatar) { return _lastOtherAvatarSentJoints[otherAvatar]; }
|
||||
|
||||
void queuePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node);
|
||||
int processPackets(const SlaveSharedData& slaveSharedData); // returns number of packets processed
|
||||
int processPackets(const WorkerSharedData& workerSharedData); // returns number of packets processed
|
||||
|
||||
void processSetTraitsMessage(ReceivedMessage& message, const SlaveSharedData& slaveSharedData, Node& sendingNode);
|
||||
void processSetTraitsMessage(ReceivedMessage& message, const WorkerSharedData& workerSharedData, Node& sendingNode);
|
||||
void emulateDeleteEntitiesTraitsMessage(const QList<QUuid>& avatarEntityIDs);
|
||||
void processBulkAvatarTraitsAckMessage(ReceivedMessage& message);
|
||||
void checkSkeletonURLAgainstWhitelist(const SlaveSharedData& slaveSharedData, Node& sendingNode,
|
||||
void checkSkeletonURLAgainstAllowlist(const WorkerSharedData& workerSharedData, Node& sendingNode,
|
||||
AvatarTraits::TraitVersion traitVersion);
|
||||
|
||||
using TraitsCheckTimestamp = std::chrono::steady_clock::time_point;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// AvatarMixerSlave.cpp
|
||||
// AvatarMixerWorker.cpp
|
||||
// assignment-client/src/avatar
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/14/2017.
|
||||
|
@ -9,7 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AvatarMixerSlave.h"
|
||||
#include "AvatarMixerWorker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
@ -36,12 +36,12 @@
|
|||
|
||||
namespace chrono = std::chrono;
|
||||
|
||||
void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
|
||||
void AvatarMixerWorker::configure(ConstIter begin, ConstIter end) {
|
||||
_begin = begin;
|
||||
_end = end;
|
||||
}
|
||||
|
||||
void AvatarMixerSlave::configureBroadcast(ConstIter begin, ConstIter end,
|
||||
void AvatarMixerWorker::configureBroadcast(ConstIter begin, ConstIter end,
|
||||
p_high_resolution_clock::time_point lastFrameTimestamp,
|
||||
float maxKbpsPerNode, float throttlingRatio,
|
||||
float priorityReservedFraction) {
|
||||
|
@ -53,13 +53,13 @@ void AvatarMixerSlave::configureBroadcast(ConstIter begin, ConstIter end,
|
|||
_avatarHeroFraction = priorityReservedFraction;
|
||||
}
|
||||
|
||||
void AvatarMixerSlave::harvestStats(AvatarMixerSlaveStats& stats) {
|
||||
void AvatarMixerWorker::harvestStats(AvatarMixerWorkerStats& stats) {
|
||||
stats = _stats;
|
||||
_stats.reset();
|
||||
}
|
||||
|
||||
|
||||
void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) {
|
||||
void AvatarMixerWorker::processIncomingPackets(const SharedNodePointer& node) {
|
||||
auto start = usecTimestampNow();
|
||||
auto nodeData = dynamic_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
|
@ -70,7 +70,7 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) {
|
|||
_stats.processIncomingPacketsElapsedTime += (end - start);
|
||||
}
|
||||
|
||||
int AvatarMixerSlave::sendIdentityPacket(NLPacketList& packetList, const AvatarMixerClientData* nodeData, const Node& destinationNode) {
|
||||
int AvatarMixerWorker::sendIdentityPacket(NLPacketList& packetList, const AvatarMixerClientData* nodeData, const Node& destinationNode) {
|
||||
if (destinationNode.getType() == NodeType::Agent && !destinationNode.isUpstream()) {
|
||||
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray();
|
||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious
|
||||
|
@ -83,7 +83,7 @@ int AvatarMixerSlave::sendIdentityPacket(NLPacketList& packetList, const AvatarM
|
|||
}
|
||||
}
|
||||
|
||||
qint64 AvatarMixerSlave::addTraitsNodeHeader(AvatarMixerClientData* listeningNodeData,
|
||||
qint64 AvatarMixerWorker::addTraitsNodeHeader(AvatarMixerClientData* listeningNodeData,
|
||||
const AvatarMixerClientData* sendingNodeData,
|
||||
NLPacketList& traitsPacketList,
|
||||
qint64 bytesWritten) {
|
||||
|
@ -98,7 +98,7 @@ qint64 AvatarMixerSlave::addTraitsNodeHeader(AvatarMixerClientData* listeningNod
|
|||
return bytesWritten;
|
||||
}
|
||||
|
||||
qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData,
|
||||
qint64 AvatarMixerWorker::addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData,
|
||||
const AvatarMixerClientData* sendingNodeData,
|
||||
NLPacketList& traitsPacketList) {
|
||||
|
||||
|
@ -245,7 +245,7 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis
|
|||
return bytesWritten;
|
||||
}
|
||||
|
||||
int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode) {
|
||||
int AvatarMixerWorker::sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode) {
|
||||
if (AvatarMixer::shouldReplicateTo(agentNode, destinationNode)) {
|
||||
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(true);
|
||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious
|
||||
|
@ -262,7 +262,7 @@ int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const
|
|||
|
||||
static const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
|
||||
|
||||
void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
|
||||
void AvatarMixerWorker::broadcastAvatarData(const SharedNodePointer& node) {
|
||||
quint64 start = usecTimestampNow();
|
||||
|
||||
if ((node->getType() == NodeType::Agent || node->getType() == NodeType::EntityScriptServer) && node->getLinkedData() && node->getActiveSocket() && !node->isUpstream()) {
|
||||
|
@ -311,7 +311,7 @@ namespace {
|
|||
|
||||
} // Close anonymous namespace.
|
||||
|
||||
void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
|
||||
void AvatarMixerWorker::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
|
||||
const Node* destinationNode = node.data();
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -680,7 +680,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|
||||
uint64_t REBROADCAST_IDENTITY_TO_DOWNSTREAM_EVERY_US = 5 * 1000 * 1000;
|
||||
|
||||
void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePointer& node) {
|
||||
void AvatarMixerWorker::broadcastAvatarDataToDownstreamMixer(const SharedNodePointer& node) {
|
||||
_stats.downstreamMixersBroadcastedTo++;
|
||||
|
||||
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// AvatarMixerSlave.h
|
||||
// AvatarMixerWorker.h
|
||||
// assignment-client/src/avatar
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/14/2017.
|
||||
|
@ -9,14 +9,14 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AvatarMixerSlave_h
|
||||
#define hifi_AvatarMixerSlave_h
|
||||
#ifndef hifi_AvatarMixerWorker_h
|
||||
#define hifi_AvatarMixerWorker_h
|
||||
|
||||
#include <NodeList.h>
|
||||
|
||||
class AvatarMixerClientData;
|
||||
|
||||
class AvatarMixerSlaveStats {
|
||||
class AvatarMixerWorkerStats {
|
||||
public:
|
||||
int nodesProcessed { 0 };
|
||||
int packetsProcessed { 0 };
|
||||
|
@ -67,7 +67,7 @@ public:
|
|||
jobElapsedTime = 0;
|
||||
}
|
||||
|
||||
AvatarMixerSlaveStats& operator+=(const AvatarMixerSlaveStats& rhs) {
|
||||
AvatarMixerWorkerStats& operator+=(const AvatarMixerWorkerStats& rhs) {
|
||||
nodesProcessed += rhs.nodesProcessed;
|
||||
packetsProcessed += rhs.packetsProcessed;
|
||||
processIncomingPacketsElapsedTime += rhs.processIncomingPacketsElapsedTime;
|
||||
|
@ -96,15 +96,15 @@ public:
|
|||
class EntityTree;
|
||||
using EntityTreePointer = std::shared_ptr<EntityTree>;
|
||||
|
||||
struct SlaveSharedData {
|
||||
QStringList skeletonURLWhitelist;
|
||||
struct WorkerSharedData {
|
||||
QStringList skeletonURLAllowlist;
|
||||
QUrl skeletonReplacementURL;
|
||||
EntityTreePointer entityTree;
|
||||
};
|
||||
|
||||
class AvatarMixerSlave {
|
||||
class AvatarMixerWorker {
|
||||
public:
|
||||
AvatarMixerSlave(SlaveSharedData* sharedData) : _sharedData(sharedData) {};
|
||||
AvatarMixerWorker(WorkerSharedData* sharedData) : _sharedData(sharedData) {};
|
||||
using ConstIter = NodeList::const_iterator;
|
||||
|
||||
void configure(ConstIter begin, ConstIter end);
|
||||
|
@ -116,7 +116,7 @@ public:
|
|||
void processIncomingPackets(const SharedNodePointer& node);
|
||||
void broadcastAvatarData(const SharedNodePointer& node);
|
||||
|
||||
void harvestStats(AvatarMixerSlaveStats& stats);
|
||||
void harvestStats(AvatarMixerWorkerStats& stats);
|
||||
|
||||
private:
|
||||
int sendIdentityPacket(NLPacketList& packet, const AvatarMixerClientData* nodeData, const Node& destinationNode);
|
||||
|
@ -143,8 +143,8 @@ private:
|
|||
float _throttlingRatio { 0.0f };
|
||||
float _avatarHeroFraction { 0.4f };
|
||||
|
||||
AvatarMixerSlaveStats _stats;
|
||||
SlaveSharedData* _sharedData;
|
||||
AvatarMixerWorkerStats _stats;
|
||||
WorkerSharedData* _sharedData;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarMixerSlave_h
|
||||
#endif // hifi_AvatarMixerWorker_h
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// AvatarMixerSlavePool.cpp
|
||||
// AvatarMixerWorkerPool.cpp
|
||||
// assignment-client/src/avatar
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/14/2017.
|
||||
|
@ -9,12 +9,12 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AvatarMixerSlavePool.h"
|
||||
#include "AvatarMixerWorkerPool.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
|
||||
void AvatarMixerSlaveThread::run() {
|
||||
void AvatarMixerWorkerThread::run() {
|
||||
while (true) {
|
||||
wait();
|
||||
|
||||
|
@ -32,10 +32,10 @@ void AvatarMixerSlaveThread::run() {
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarMixerSlaveThread::wait() {
|
||||
void AvatarMixerWorkerThread::wait() {
|
||||
{
|
||||
Lock lock(_pool._mutex);
|
||||
_pool._slaveCondition.wait(lock, [&] {
|
||||
_pool._workerCondition.wait(lock, [&] {
|
||||
assert(_pool._numStarted <= _pool._numThreads);
|
||||
return _pool._numStarted != _pool._numThreads;
|
||||
});
|
||||
|
@ -47,7 +47,7 @@ void AvatarMixerSlaveThread::wait() {
|
|||
_function = _pool._function;
|
||||
}
|
||||
|
||||
void AvatarMixerSlaveThread::notify(bool stopping) {
|
||||
void AvatarMixerWorkerThread::notify(bool stopping) {
|
||||
{
|
||||
Lock lock(_pool._mutex);
|
||||
assert(_pool._numFinished < _pool._numThreads);
|
||||
|
@ -59,30 +59,30 @@ void AvatarMixerSlaveThread::notify(bool stopping) {
|
|||
_pool._poolCondition.notify_one();
|
||||
}
|
||||
|
||||
bool AvatarMixerSlaveThread::try_pop(SharedNodePointer& node) {
|
||||
bool AvatarMixerWorkerThread::try_pop(SharedNodePointer& node) {
|
||||
return _pool._queue.try_pop(node);
|
||||
}
|
||||
|
||||
void AvatarMixerSlavePool::processIncomingPackets(ConstIter begin, ConstIter end) {
|
||||
_function = &AvatarMixerSlave::processIncomingPackets;
|
||||
_configure = [=](AvatarMixerSlave& slave) {
|
||||
slave.configure(begin, end);
|
||||
void AvatarMixerWorkerPool::processIncomingPackets(ConstIter begin, ConstIter end) {
|
||||
_function = &AvatarMixerWorker::processIncomingPackets;
|
||||
_configure = [=](AvatarMixerWorker& worker) {
|
||||
worker.configure(begin, end);
|
||||
};
|
||||
run(begin, end);
|
||||
}
|
||||
|
||||
void AvatarMixerSlavePool::broadcastAvatarData(ConstIter begin, ConstIter end,
|
||||
void AvatarMixerWorkerPool::broadcastAvatarData(ConstIter begin, ConstIter end,
|
||||
p_high_resolution_clock::time_point lastFrameTimestamp,
|
||||
float maxKbpsPerNode, float throttlingRatio) {
|
||||
_function = &AvatarMixerSlave::broadcastAvatarData;
|
||||
_configure = [=](AvatarMixerSlave& slave) {
|
||||
slave.configureBroadcast(begin, end, lastFrameTimestamp, maxKbpsPerNode, throttlingRatio,
|
||||
_function = &AvatarMixerWorker::broadcastAvatarData;
|
||||
_configure = [=](AvatarMixerWorker& worker) {
|
||||
worker.configureBroadcast(begin, end, lastFrameTimestamp, maxKbpsPerNode, throttlingRatio,
|
||||
_priorityReservedFraction);
|
||||
};
|
||||
run(begin, end);
|
||||
}
|
||||
|
||||
void AvatarMixerSlavePool::run(ConstIter begin, ConstIter end) {
|
||||
void AvatarMixerWorkerPool::run(ConstIter begin, ConstIter end) {
|
||||
_begin = begin;
|
||||
_end = end;
|
||||
|
||||
|
@ -96,7 +96,7 @@ void AvatarMixerSlavePool::run(ConstIter begin, ConstIter end) {
|
|||
|
||||
// run
|
||||
_numStarted = _numFinished = 0;
|
||||
_slaveCondition.notify_all();
|
||||
_workerCondition.notify_all();
|
||||
|
||||
// wait
|
||||
_poolCondition.wait(lock, [&] {
|
||||
|
@ -111,17 +111,17 @@ void AvatarMixerSlavePool::run(ConstIter begin, ConstIter end) {
|
|||
}
|
||||
|
||||
|
||||
void AvatarMixerSlavePool::each(std::function<void(AvatarMixerSlave& slave)> functor) {
|
||||
for (auto& slave : _slaves) {
|
||||
functor(*slave.get());
|
||||
void AvatarMixerWorkerPool::each(std::function<void(AvatarMixerWorker& worker)> functor) {
|
||||
for (auto& worker : _workers) {
|
||||
functor(*worker.get());
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_EVENT_QUEUE
|
||||
void AvatarMixerSlavePool::queueStats(QJsonObject& stats) {
|
||||
void AvatarMixerWorkerPool::queueStats(QJsonObject& stats) {
|
||||
unsigned i = 0;
|
||||
for (auto& slave : _slaves) {
|
||||
int queueSize = ::hifi::qt::getEventQueueSize(slave.get());
|
||||
for (auto& worker : _workers) {
|
||||
int queueSize = ::hifi::qt::getEventQueueSize(worker.get());
|
||||
QString queueName = QString("avatar_thread_event_queue_%1").arg(i);
|
||||
stats[queueName] = queueSize;
|
||||
|
||||
|
@ -130,7 +130,7 @@ void AvatarMixerSlavePool::queueStats(QJsonObject& stats) {
|
|||
}
|
||||
#endif // DEBUG_EVENT_QUEUE
|
||||
|
||||
void AvatarMixerSlavePool::setNumThreads(int numThreads) {
|
||||
void AvatarMixerWorkerPool::setNumThreads(int numThreads) {
|
||||
// clamp to allowed size
|
||||
{
|
||||
int maxThreads = QThread::idealThreadCount();
|
||||
|
@ -150,35 +150,35 @@ void AvatarMixerSlavePool::setNumThreads(int numThreads) {
|
|||
resize(numThreads);
|
||||
}
|
||||
|
||||
void AvatarMixerSlavePool::resize(int numThreads) {
|
||||
assert(_numThreads == (int)_slaves.size());
|
||||
void AvatarMixerWorkerPool::resize(int numThreads) {
|
||||
assert(_numThreads == (int)_workers.size());
|
||||
|
||||
qDebug("%s: set %d threads (was %d)", __FUNCTION__, numThreads, _numThreads);
|
||||
|
||||
Lock lock(_mutex);
|
||||
|
||||
if (numThreads > _numThreads) {
|
||||
// start new slaves
|
||||
// start new workers
|
||||
for (int i = 0; i < numThreads - _numThreads; ++i) {
|
||||
auto slave = new AvatarMixerSlaveThread(*this, _slaveSharedData);
|
||||
slave->start();
|
||||
_slaves.emplace_back(slave);
|
||||
auto worker = new AvatarMixerWorkerThread(*this, _workerSharedData);
|
||||
worker->start();
|
||||
_workers.emplace_back(worker);
|
||||
}
|
||||
} else if (numThreads < _numThreads) {
|
||||
auto extraBegin = _slaves.begin() + numThreads;
|
||||
auto extraBegin = _workers.begin() + numThreads;
|
||||
|
||||
// mark slaves to stop...
|
||||
auto slave = extraBegin;
|
||||
while (slave != _slaves.end()) {
|
||||
(*slave)->_stop = true;
|
||||
++slave;
|
||||
// mark workers to stop...
|
||||
auto worker = extraBegin;
|
||||
while (worker != _workers.end()) {
|
||||
(*worker)->_stop = true;
|
||||
++worker;
|
||||
}
|
||||
|
||||
// ...cycle them until they do stop...
|
||||
_numStopped = 0;
|
||||
while (_numStopped != (_numThreads - numThreads)) {
|
||||
_numStarted = _numFinished = _numStopped;
|
||||
_slaveCondition.notify_all();
|
||||
_workerCondition.notify_all();
|
||||
_poolCondition.wait(lock, [&] {
|
||||
assert(_numFinished <= _numThreads);
|
||||
return _numFinished == _numThreads;
|
||||
|
@ -186,18 +186,18 @@ void AvatarMixerSlavePool::resize(int numThreads) {
|
|||
}
|
||||
|
||||
// ...wait for threads to finish...
|
||||
slave = extraBegin;
|
||||
while (slave != _slaves.end()) {
|
||||
QThread* thread = reinterpret_cast<QThread*>(slave->get());
|
||||
worker = extraBegin;
|
||||
while (worker != _workers.end()) {
|
||||
QThread* thread = reinterpret_cast<QThread*>(worker->get());
|
||||
static const int MAX_THREAD_WAIT_TIME = 10;
|
||||
thread->wait(MAX_THREAD_WAIT_TIME);
|
||||
++slave;
|
||||
++worker;
|
||||
}
|
||||
|
||||
// ...and erase them
|
||||
_slaves.erase(extraBegin, _slaves.end());
|
||||
_workers.erase(extraBegin, _workers.end());
|
||||
}
|
||||
|
||||
_numThreads = _numStarted = _numFinished = numThreads;
|
||||
assert(_numThreads == (int)_slaves.size());
|
||||
assert(_numThreads == (int)_workers.size());
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// AvatarMixerSlavePool.h
|
||||
// AvatarMixerWorkerPool.h
|
||||
// assignment-client/src/avatar
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/14/2017.
|
||||
|
@ -9,8 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AvatarMixerSlavePool_h
|
||||
#define hifi_AvatarMixerSlavePool_h
|
||||
#ifndef hifi_AvatarMixerWorkerPool_h
|
||||
#define hifi_AvatarMixerWorkerPool_h
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
@ -22,38 +22,38 @@
|
|||
#include <NodeList.h>
|
||||
#include <shared/QtHelpers.h>
|
||||
|
||||
#include "AvatarMixerSlave.h"
|
||||
#include "AvatarMixerWorker.h"
|
||||
|
||||
|
||||
class AvatarMixerSlavePool;
|
||||
class AvatarMixerWorkerPool;
|
||||
|
||||
class AvatarMixerSlaveThread : public QThread, public AvatarMixerSlave {
|
||||
class AvatarMixerWorkerThread : public QThread, public AvatarMixerWorker {
|
||||
Q_OBJECT
|
||||
using ConstIter = NodeList::const_iterator;
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
|
||||
public:
|
||||
AvatarMixerSlaveThread(AvatarMixerSlavePool& pool, SlaveSharedData* slaveSharedData) :
|
||||
AvatarMixerSlave(slaveSharedData), _pool(pool) {};
|
||||
AvatarMixerWorkerThread(AvatarMixerWorkerPool& pool, WorkerSharedData* workerSharedData) :
|
||||
AvatarMixerWorker(workerSharedData), _pool(pool) {};
|
||||
|
||||
void run() override final;
|
||||
|
||||
private:
|
||||
friend class AvatarMixerSlavePool;
|
||||
friend class AvatarMixerWorkerPool;
|
||||
|
||||
void wait();
|
||||
void notify(bool stopping);
|
||||
bool try_pop(SharedNodePointer& node);
|
||||
|
||||
AvatarMixerSlavePool& _pool;
|
||||
void (AvatarMixerSlave::*_function)(const SharedNodePointer& node) { nullptr };
|
||||
AvatarMixerWorkerPool& _pool;
|
||||
void (AvatarMixerWorker::*_function)(const SharedNodePointer& node) { nullptr };
|
||||
bool _stop { false };
|
||||
};
|
||||
|
||||
// Slave pool for avatar mixers
|
||||
// AvatarMixerSlavePool is not thread-safe! It should be instantiated and used from a single thread.
|
||||
class AvatarMixerSlavePool {
|
||||
// Worker pool for avatar mixers
|
||||
// AvatarMixerWorkerPool is not thread-safe! It should be instantiated and used from a single thread.
|
||||
class AvatarMixerWorkerPool {
|
||||
using Queue = tbb::concurrent_queue<SharedNodePointer>;
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
|
@ -62,17 +62,17 @@ class AvatarMixerSlavePool {
|
|||
public:
|
||||
using ConstIter = NodeList::const_iterator;
|
||||
|
||||
AvatarMixerSlavePool(SlaveSharedData* slaveSharedData, int numThreads = QThread::idealThreadCount()) :
|
||||
_slaveSharedData(slaveSharedData) { setNumThreads(numThreads); }
|
||||
~AvatarMixerSlavePool() { resize(0); }
|
||||
AvatarMixerWorkerPool(WorkerSharedData* workerSharedData, int numThreads = QThread::idealThreadCount()) :
|
||||
_workerSharedData(workerSharedData) { setNumThreads(numThreads); }
|
||||
~AvatarMixerWorkerPool() { resize(0); }
|
||||
|
||||
// Jobs the slave pool can do...
|
||||
// Jobs the worker pool can do...
|
||||
void processIncomingPackets(ConstIter begin, ConstIter end);
|
||||
void broadcastAvatarData(ConstIter begin, ConstIter end,
|
||||
p_high_resolution_clock::time_point lastFrameTimestamp, float maxKbpsPerNode, float throttlingRatio);
|
||||
|
||||
// iterate over all slaves
|
||||
void each(std::function<void(AvatarMixerSlave& slave)> functor);
|
||||
// iterate over all workers
|
||||
void each(std::function<void(AvatarMixerWorker& worker)> functor);
|
||||
|
||||
#ifdef DEBUG_EVENT_QUEUE
|
||||
void queueStats(QJsonObject& stats);
|
||||
|
@ -88,18 +88,18 @@ private:
|
|||
void run(ConstIter begin, ConstIter end);
|
||||
void resize(int numThreads);
|
||||
|
||||
std::vector<std::unique_ptr<AvatarMixerSlaveThread>> _slaves;
|
||||
std::vector<std::unique_ptr<AvatarMixerWorkerThread>> _workers;
|
||||
|
||||
friend void AvatarMixerSlaveThread::wait();
|
||||
friend void AvatarMixerSlaveThread::notify(bool stopping);
|
||||
friend bool AvatarMixerSlaveThread::try_pop(SharedNodePointer& node);
|
||||
friend void AvatarMixerWorkerThread::wait();
|
||||
friend void AvatarMixerWorkerThread::notify(bool stopping);
|
||||
friend bool AvatarMixerWorkerThread::try_pop(SharedNodePointer& node);
|
||||
|
||||
// synchronization state
|
||||
Mutex _mutex;
|
||||
ConditionVariable _slaveCondition;
|
||||
ConditionVariable _workerCondition;
|
||||
ConditionVariable _poolCondition;
|
||||
void (AvatarMixerSlave::*_function)(const SharedNodePointer& node);
|
||||
std::function<void(AvatarMixerSlave&)> _configure;
|
||||
void (AvatarMixerWorker::*_function)(const SharedNodePointer& node);
|
||||
std::function<void(AvatarMixerWorker&)> _configure;
|
||||
|
||||
// Set from Domain Settings:
|
||||
float _priorityReservedFraction { 0.4f };
|
||||
|
@ -114,7 +114,7 @@ private:
|
|||
ConstIter _begin;
|
||||
ConstIter _end;
|
||||
|
||||
SlaveSharedData* _slaveSharedData;
|
||||
WorkerSharedData* _workerSharedData;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarMixerSlavePool_h
|
||||
#endif // hifi_AvatarMixerWorkerPool_h
|
|
@ -320,11 +320,11 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio
|
|||
tree->setWantEditLogging(wantEditLogging);
|
||||
tree->setWantTerseEditLogging(wantTerseEditLogging);
|
||||
|
||||
QString entityScriptSourceWhitelist;
|
||||
if (readOptionString("entityScriptSourceWhitelist", settingsSectionObject, entityScriptSourceWhitelist)) {
|
||||
tree->setEntityScriptSourceWhitelist(entityScriptSourceWhitelist);
|
||||
QString entityScriptSourceAllowlist;
|
||||
if (readOptionString("entityScriptSourceAllowlist", settingsSectionObject, entityScriptSourceAllowlist)) {
|
||||
tree->setEntityScriptSourceAllowlist(entityScriptSourceAllowlist);
|
||||
} else {
|
||||
tree->setEntityScriptSourceWhitelist("");
|
||||
tree->setEntityScriptSourceAllowlist("");
|
||||
}
|
||||
|
||||
auto entityEditFilters = DependencyManager::get<EntityEditFilters>();
|
||||
|
|
|
@ -26,7 +26,7 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
set_from_env(STABLE_BUILD STABLE_BUILD 0)
|
||||
|
||||
set_from_env(PRELOADED_STARTUP_LOCATION PRELOADED_STARTUP_LOCATION "")
|
||||
set_from_env(PRELOADED_SCRIPT_WHITELIST PRELOADED_SCRIPT_WHITELIST "")
|
||||
set_from_env(PRELOADED_SCRIPT_ALLOWLIST PRELOADED_SCRIPT_ALLOWLIST "")
|
||||
|
||||
set_from_env(BYPASS_SIGNING BYPASS_SIGNING 0)
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace BuildInfo {
|
|||
const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@";
|
||||
const QString BUILD_TIME = "@BUILD_TIME@";
|
||||
const QString PRELOADED_STARTUP_LOCATION = "@PRELOADED_STARTUP_LOCATION@";
|
||||
const QString PRELOADED_SCRIPT_WHITELIST = "@PRELOADED_SCRIPT_WHITELIST@";
|
||||
const QString PRELOADED_SCRIPT_ALLOWLIST = "@PRELOADED_SCRIPT_ALLOWLIST@";
|
||||
|
||||
enum BuildType {
|
||||
Dev,
|
||||
|
|
|
@ -178,7 +178,7 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
}
|
||||
|
||||
// If this user is a known member of a domain group, give them the implied permissions.
|
||||
// Do before processing verifiedUsername in case user is logged into the Directory Services and is a member of a blacklist group.
|
||||
// Do before processing verifiedUsername in case user is logged into the Directory Services and is a member of a blocklist group.
|
||||
if (!verifiedDomainUserName.isEmpty()) {
|
||||
auto userGroups = _domainGroupMemberships[verifiedDomainUserName];
|
||||
foreach (QString userGroup, userGroups) {
|
||||
|
@ -277,8 +277,8 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
}
|
||||
}
|
||||
|
||||
// if this user is a known member of a blacklist group, remove the implied permissions
|
||||
foreach (QUuid groupID, _server->_settingsManager.getBlacklistGroupIDs()) {
|
||||
// if this user is a known member of a blocklist group, remove the implied permissions
|
||||
foreach (QUuid groupID, _server->_settingsManager.getBlocklistGroupIDs()) {
|
||||
QUuid rankID = _server->_settingsManager.isGroupMember(verifiedUsername, groupID);
|
||||
if (rankID != QUuid()) {
|
||||
QUuid rankID = _server->_settingsManager.isGroupMember(verifiedUsername, groupID);
|
||||
|
@ -287,7 +287,7 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
|
||||
GroupRank rank = _server->_settingsManager.getGroupRank(groupID, rankID);
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: user is in blacklist group:" << groupID << " rank:" << rank.name
|
||||
qDebug() << "| user-permissions: user is in blocklist group:" << groupID << " rank:" << rank.name
|
||||
<< "so:" << userPerms;
|
||||
#endif
|
||||
}
|
||||
|
@ -299,20 +299,20 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
userPerms.setVerifiedUserName(verifiedUsername);
|
||||
}
|
||||
|
||||
// If this user is a known member of an domain group that is blacklisted, remove the implied permissions.
|
||||
// If this user is a known member of an domain group that is blocklisted, remove the implied permissions.
|
||||
if (!verifiedDomainUserName.isEmpty()) {
|
||||
auto userGroups = _domainGroupMemberships[verifiedDomainUserName];
|
||||
foreach(QString userGroup, userGroups) {
|
||||
// A domain group is signified by a leading special character, "@".
|
||||
// Multiple domain groups may be specified in one domain server setting as a comma- and/or space-separated lists of
|
||||
// domain group names. For example, "@silver @Gold, @platinum".
|
||||
auto domainGroups = _server->_settingsManager.getDomainServerBlacklistGroupNames()
|
||||
auto domainGroups = _server->_settingsManager.getDomainServerBlocklistGroupNames()
|
||||
.filter(QRegularExpression("^(.*[\\s,])?" + QRegularExpression::escape(userGroup) + "([\\s,].*)?$",
|
||||
QRegularExpression::CaseInsensitiveOption));
|
||||
foreach(QString domainGroup, domainGroups) {
|
||||
userPerms &= ~_server->_settingsManager.getForbiddensForGroup(domainGroup, QUuid());
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: domain user is in blacklist group:" << domainGroup << "so:" << userPerms;
|
||||
qDebug() << "| user-permissions: domain user is in blocklist group:" << domainGroup << "so:" << userPerms;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -1027,7 +1027,7 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) {
|
|||
|
||||
QJsonObject json;
|
||||
QSet<QString> groupIDSet;
|
||||
foreach (QUuid groupID, _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs()) {
|
||||
foreach (QUuid groupID, _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlocklistGroupIDs()) {
|
||||
groupIDSet += groupID.toString().mid(1,36);
|
||||
}
|
||||
|
||||
|
|
|
@ -341,17 +341,17 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
connect(&_settingsManager, &DomainServerSettingsManager::updateNodePermissions, [this] { _metadata->securityChanged(true); });
|
||||
|
||||
qDebug() << "domain-server is running";
|
||||
static const QString AC_SUBNET_WHITELIST_SETTING_PATH = "security.ac_subnet_whitelist";
|
||||
static const QString AC_SUBNET_ALLOWLIST_SETTING_PATH = "security.ac_subnet_allowlist";
|
||||
|
||||
static const Subnet LOCALHOST { QHostAddress("127.0.0.1"), 32 };
|
||||
_acSubnetWhitelist = { LOCALHOST };
|
||||
_acSubnetAllowlist = { LOCALHOST };
|
||||
|
||||
auto whitelist = _settingsManager.valueOrDefaultValueForKeyPath(AC_SUBNET_WHITELIST_SETTING_PATH).toStringList();
|
||||
for (auto& subnet : whitelist) {
|
||||
auto allowlist = _settingsManager.valueOrDefaultValueForKeyPath(AC_SUBNET_ALLOWLIST_SETTING_PATH).toStringList();
|
||||
for (auto& subnet : allowlist) {
|
||||
auto netmaskParts = subnet.trimmed().split("/");
|
||||
|
||||
if (netmaskParts.size() > 2) {
|
||||
qDebug() << "Ignoring subnet in whitelist, malformed: " << subnet;
|
||||
qDebug() << "Ignoring subnet in allowlist, malformed: " << subnet;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -363,7 +363,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
bool ok;
|
||||
netmask = netmaskParts[1].toInt(&ok);
|
||||
if (!ok) {
|
||||
qDebug() << "Ignoring subnet in whitelist, bad netmask: " << subnet;
|
||||
qDebug() << "Ignoring subnet in allowlist, bad netmask: " << subnet;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -371,10 +371,10 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
auto ip = QHostAddress(netmaskParts[0]);
|
||||
|
||||
if (!ip.isNull()) {
|
||||
qDebug() << "Adding AC whitelist subnet: " << subnet << " -> " << (ip.toString() + "/" + QString::number(netmask));
|
||||
_acSubnetWhitelist.push_back({ ip , netmask });
|
||||
qDebug() << "Adding AC allowlist subnet: " << subnet << " -> " << (ip.toString() + "/" + QString::number(netmask));
|
||||
_acSubnetAllowlist.push_back({ ip , netmask });
|
||||
} else {
|
||||
qDebug() << "Ignoring subnet in whitelist, invalid ip portion: " << subnet;
|
||||
qDebug() << "Ignoring subnet in allowlist, invalid ip portion: " << subnet;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1502,8 +1502,8 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer<ReceivedMessage
|
|||
return senderAddr.isInSubnet(mask);
|
||||
};
|
||||
|
||||
auto it = find_if(_acSubnetWhitelist.begin(), _acSubnetWhitelist.end(), isHostAddressInSubnet);
|
||||
if (it == _acSubnetWhitelist.end()) {
|
||||
auto it = find_if(_acSubnetAllowlist.begin(), _acSubnetAllowlist.end(), isHostAddressInSubnet);
|
||||
if (it == _acSubnetAllowlist.end()) {
|
||||
HIFI_FDEBUG("Received an assignment connect request from a disallowed ip address:"
|
||||
<< senderAddr.toString());
|
||||
return;
|
||||
|
|
|
@ -251,7 +251,7 @@ private:
|
|||
|
||||
QString operationToString(const QNetworkAccessManager::Operation &op);
|
||||
|
||||
SubnetList _acSubnetWhitelist;
|
||||
SubnetList _acSubnetAllowlist;
|
||||
|
||||
std::vector<QString> _replicatedUsernames;
|
||||
|
||||
|
|
|
@ -172,20 +172,20 @@ static const QMap<QString, DomainServerExporter::MetricType> TYPE_MAP {
|
|||
{ "avatar_mixer_single_core_tasks_process_events" , DomainServerExporter::MetricType::Counter },
|
||||
{ "avatar_mixer_single_core_tasks_queue_incoming_packet" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_single_core_tasks_send_stats" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_received_1_nodes_processed" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_sent_1_nodes_broadcasted_to" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_sent_2_average_others_included" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_sent_3_average_over_budget_avatars" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_sent_4_average_data_bytes" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_sent_5_average_traits_bytes" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_sent_6_average_identity_bytes" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_sent_7_average_hero_avatars" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_timing_1_process_incoming_packets" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_timing_2_ignore_calculation" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_timing_3_to_byte_array" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_timing_4_avatar_data_packing" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_timing_5_packet_sending" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_slaves_aggregate_per_frame_timing_6_job_elapsed_time" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_received_1_nodes_processed" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_sent_1_nodes_broadcasted_to" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_sent_2_average_others_included" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_sent_3_average_over_budget_avatars" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_sent_4_average_data_bytes" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_sent_5_average_traits_bytes" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_sent_6_average_identity_bytes" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_sent_7_average_hero_avatars" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_timing_1_process_incoming_packets" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_timing_2_ignore_calculation" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_timing_3_to_byte_array" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_timing_4_avatar_data_packing" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_timing_5_packet_sending" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_workers_aggregate_per_frame_timing_6_job_elapsed_time" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_threads" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_throttling_ratio" , DomainServerExporter::MetricType::Gauge },
|
||||
{ "avatar_mixer_trailing_mix_ratio" , DomainServerExporter::MetricType::Gauge },
|
||||
|
@ -254,9 +254,9 @@ static const QMap<QString, DomainServerExporter::MetricType> TYPE_MAP {
|
|||
//
|
||||
// For numeric values with an unit, instead of trying to parse it, the stats will just need to
|
||||
// have a second copy of the metric added, with the value expressed as a number, with the original
|
||||
// being blacklisted here.
|
||||
// being blocklisted here.
|
||||
|
||||
static const QSet<QString> BLACKLIST = {
|
||||
static const QSet<QString> BLOCKLIST = {
|
||||
"asset_server_connection_stats_last_heard", // Timestamp as a string
|
||||
"asset_server_username", // Username
|
||||
"audio_mixer_listeners_jitter_downstream_avg_gap", // Number as string with unit name, alternative added
|
||||
|
@ -379,7 +379,7 @@ void DomainServerExporter::generateMetricsFromJson(QTextStream& stream,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (BLACKLIST.contains(metricName)) {
|
||||
if (BLOCKLIST.contains(metricName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -688,7 +688,7 @@ void DomainServerSettingsManager::packPermissions() {
|
|||
// save settings for groups
|
||||
packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH);
|
||||
|
||||
// save settings for blacklist groups
|
||||
// save settings for blocklist groups
|
||||
packPermissionsForMap("permissions", _groupForbiddens, GROUP_FORBIDDENS_KEYPATH);
|
||||
|
||||
persistToFile();
|
||||
|
@ -869,7 +869,7 @@ bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() {
|
|||
}
|
||||
}
|
||||
|
||||
QList<QUuid> forbiddenGroupIDs = getBlacklistGroupIDs();
|
||||
QList<QUuid> forbiddenGroupIDs = getBlocklistGroupIDs();
|
||||
foreach (QUuid groupID, forbiddenGroupIDs) {
|
||||
QString groupName = _groupNames[groupID];
|
||||
QHash<QUuid, GroupRank>& ranksForGroup = _groupRanks[groupID];
|
||||
|
@ -1743,7 +1743,7 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
|
|||
QJsonObject postedObject(postedSettingsObject);
|
||||
|
||||
static const QString SECURITY_ROOT_KEY = "security";
|
||||
static const QString AC_SUBNET_WHITELIST_KEY = "ac_subnet_whitelist";
|
||||
static const QString AC_SUBNET_ALLOWLIST_KEY = "ac_subnet_allowlist";
|
||||
static const QString BROADCASTING_KEY = "broadcasting";
|
||||
static const QString WIZARD_KEY = "wizard";
|
||||
static const QString DESCRIPTION_ROOT_KEY = "descriptors";
|
||||
|
@ -1863,7 +1863,7 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
|
|||
|
||||
if ((rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY &&
|
||||
rootKey != DESCRIPTION_ROOT_KEY && rootKey != WIZARD_KEY) ||
|
||||
settingKey == AC_SUBNET_WHITELIST_KEY) {
|
||||
settingKey == AC_SUBNET_ALLOWLIST_KEY) {
|
||||
needRestart = true;
|
||||
}
|
||||
} else {
|
||||
|
@ -2038,7 +2038,7 @@ void DomainServerSettingsManager::apiRefreshGroupInformation() {
|
|||
}
|
||||
if (_groupIDs.contains(lowerGroupName)) {
|
||||
// we already know about this one. recall setGroupID in case the group has been
|
||||
// added to another section (the same group is found in both groups and blacklists).
|
||||
// added to another section (the same group is found in both groups and blocklists).
|
||||
changed = setGroupID(groupName, _groupIDs[lowerGroupName]);
|
||||
continue;
|
||||
}
|
||||
|
@ -2245,7 +2245,7 @@ QList<QUuid> DomainServerSettingsManager::getGroupIDs() {
|
|||
return result.values();
|
||||
}
|
||||
|
||||
QList<QUuid> DomainServerSettingsManager::getBlacklistGroupIDs() {
|
||||
QList<QUuid> DomainServerSettingsManager::getBlocklistGroupIDs() {
|
||||
QSet<QUuid> result;
|
||||
foreach (NodePermissionsKey groupKey, _groupForbiddens.keys()) {
|
||||
if (_groupForbiddens[groupKey]->isGroup()) {
|
||||
|
@ -2264,7 +2264,7 @@ QStringList DomainServerSettingsManager::getDomainServerGroupNames() {
|
|||
return result.values();
|
||||
}
|
||||
|
||||
QStringList DomainServerSettingsManager::getDomainServerBlacklistGroupNames() {
|
||||
QStringList DomainServerSettingsManager::getDomainServerBlocklistGroupNames() {
|
||||
// All names as listed in the domain server settings; not necessarily Directory Services groups.
|
||||
QSet<QString> result;
|
||||
foreach (NodePermissionsKey groupKey, _groupForbiddens.keys()) {
|
||||
|
|
|
@ -135,10 +135,10 @@ public:
|
|||
GroupRank getGroupRank(QUuid groupID, QUuid rankID) { return _groupRanks[groupID][rankID]; }
|
||||
|
||||
QList<QUuid> getGroupIDs();
|
||||
QList<QUuid> getBlacklistGroupIDs();
|
||||
QList<QUuid> getBlocklistGroupIDs();
|
||||
|
||||
QStringList getDomainServerGroupNames();
|
||||
QStringList getDomainServerBlacklistGroupNames();
|
||||
QStringList getDomainServerBlocklistGroupNames();
|
||||
|
||||
// these are used to locally cache the result of calling "/api/v1/groups/.../is_member/..." on Directory Services api
|
||||
void clearGroupMemberships(const QString& name) { _groupMembership[name.toLower()].clear(); }
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
// Created by Wayne Chen on 10/18/18
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
// Copyright 2024 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -377,13 +378,13 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
function onHandleLoginCompleted(): {
|
||||
function onHandleLoginCompleted() {
|
||||
console.log("Login Succeeded");
|
||||
loggingInBody.loadingSuccess();
|
||||
}
|
||||
|
||||
function onHandleLoginFailed() {
|
||||
console.log("Login Failed")
|
||||
console.log("Login Failed");
|
||||
loggingInSpinner.visible = false;
|
||||
loggingInGlyph.visible = false;
|
||||
var errorString = "";
|
||||
|
|
|
@ -354,7 +354,7 @@ Rectangle {
|
|||
font: avatarGainSliderText.font
|
||||
}
|
||||
RalewayRegular {
|
||||
// The slider for my card is special, it controls the master gain
|
||||
// The slider for my card is special, it controls the primary gain
|
||||
id: avatarGainSliderText;
|
||||
text: "People volume";
|
||||
size: 16;
|
||||
|
@ -645,7 +645,7 @@ Rectangle {
|
|||
font: noiseReductionThresholdSliderText.font
|
||||
}
|
||||
RalewayRegular {
|
||||
// The slider for my card is special, it controls the master gain
|
||||
// The slider for my card is special, it controls the primary gain
|
||||
id: noiseReductionThresholdSliderText;
|
||||
text: "Audio input threshold";
|
||||
size: 16;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// EntityScriptQMLWhitelist.qml
|
||||
// EntityScriptQMLAllowlist.qml
|
||||
// interface/resources/qml/hifi/dialogs/security
|
||||
//
|
||||
// Created by Kalila L. on 2019.12.05 | realities.dev | somnilibertas@gmail.com
|
||||
|
@ -8,7 +8,7 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Security Settings for the Entity Script QML Whitelist
|
||||
// Security Settings for the Entity Script QML Allowlist
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.8
|
||||
|
@ -23,25 +23,25 @@ import "../../../windows"
|
|||
Rectangle {
|
||||
id: parentBody;
|
||||
|
||||
function getWhitelistAsText() {
|
||||
var whitelist = Settings.getValue("private/settingsSafeURLS");
|
||||
var arrayWhitelist = whitelist.split(",").join("\n");
|
||||
return arrayWhitelist;
|
||||
function getAllowlistAsText() {
|
||||
var allowlist = Settings.getValue("private/settingsSafeURLS");
|
||||
var arrayAllowlist = allowlist.split(",").join("\n");
|
||||
return arrayAllowlist;
|
||||
}
|
||||
|
||||
function setWhitelistAsText(whitelistText) {
|
||||
Settings.setValue("private/settingsSafeURLS", whitelistText.text);
|
||||
function setAllowlistAsText(allowlistText) {
|
||||
Settings.setValue("private/settingsSafeURLS", allowlistText.text);
|
||||
|
||||
var originalSetString = whitelistText.text;
|
||||
var originalSetString = allowlistText.text;
|
||||
var originalSet = originalSetString.split(' ').join('');
|
||||
|
||||
var check = Settings.getValue("private/settingsSafeURLS");
|
||||
var arrayCheck = check.split(",").join("\n");
|
||||
|
||||
setWhitelistSuccess(arrayCheck === originalSet);
|
||||
setAllowlistSuccess(arrayCheck === originalSet);
|
||||
}
|
||||
|
||||
function setWhitelistSuccess(success) {
|
||||
function setAllowlistSuccess(success) {
|
||||
if (success) {
|
||||
notificationText.text = "Successfully saved settings.";
|
||||
} else {
|
||||
|
@ -49,20 +49,20 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
function toggleWhitelist(enabled) {
|
||||
Settings.setValue("private/whitelistEnabled", enabled);
|
||||
console.info("Toggling Whitelist to:", enabled);
|
||||
function toggleAllowlist(enabled) {
|
||||
Settings.setValue("private/allowlistEnabled", enabled);
|
||||
console.info("Toggling Allowlist to:", enabled);
|
||||
}
|
||||
|
||||
function initCheckbox() {
|
||||
var check = Settings.getValue("private/whitelistEnabled", false);
|
||||
var check = Settings.getValue("private/allowlistEnabled", false);
|
||||
|
||||
if (check) {
|
||||
whitelistEnabled.toggle();
|
||||
allowlistEnabled.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
anchors.fill: parent
|
||||
width: parent.width;
|
||||
height: 120;
|
||||
|
@ -70,7 +70,7 @@ Rectangle {
|
|||
|
||||
HifiStylesUit.RalewayRegular {
|
||||
id: titleText;
|
||||
text: "Entity Script / QML Whitelist"
|
||||
text: "Entity Script / QML Allowlist"
|
||||
// Text size
|
||||
size: 24;
|
||||
// Style
|
||||
|
@ -89,13 +89,13 @@ Rectangle {
|
|||
initCheckbox();
|
||||
}
|
||||
|
||||
id: whitelistEnabled;
|
||||
id: allowlistEnabled;
|
||||
|
||||
anchors.right: parent.right;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 10;
|
||||
onToggled: {
|
||||
toggleWhitelist(whitelistEnabled.checked)
|
||||
toggleAllowlist(allowlistEnabled.checked)
|
||||
}
|
||||
|
||||
Label {
|
||||
|
@ -115,7 +115,7 @@ Rectangle {
|
|||
width: parent.width;
|
||||
height: 250;
|
||||
anchors.top: titleText.bottom;
|
||||
|
||||
|
||||
ScrollView {
|
||||
id: textAreaScrollView
|
||||
anchors.fill: parent;
|
||||
|
@ -126,8 +126,8 @@ Rectangle {
|
|||
clip: false;
|
||||
|
||||
TextArea {
|
||||
id: whitelistTextArea
|
||||
text: getWhitelistAsText();
|
||||
id: allowlistTextArea
|
||||
text: getAllowlistAsText();
|
||||
onTextChanged: notificationText.text = "";
|
||||
width: parent.width;
|
||||
height: parent.height;
|
||||
|
@ -136,7 +136,7 @@ Rectangle {
|
|||
color: "white";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Button {
|
||||
id: saveChanges
|
||||
anchors.topMargin: 5;
|
||||
|
@ -155,8 +155,8 @@ Rectangle {
|
|||
elide: Text.ElideRight
|
||||
}
|
||||
text: "Save Changes"
|
||||
onClicked: setWhitelistAsText(whitelistTextArea)
|
||||
|
||||
onClicked: setAllowlistAsText(allowlistTextArea)
|
||||
|
||||
HifiStylesUit.RalewayRegular {
|
||||
id: notificationText;
|
||||
text: ""
|
||||
|
@ -170,15 +170,15 @@ Rectangle {
|
|||
anchors.rightMargin: 10;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HifiStylesUit.RalewayRegular {
|
||||
id: descriptionText;
|
||||
text:
|
||||
"The whitelist checks scripts and QML as they are loaded.<br/>
|
||||
text:
|
||||
"The allowlist checks scripts and QML as they are loaded.<br/>
|
||||
Therefore, if a script is cached or has no reason to load again,<br/>
|
||||
removing it from the whitelist will have no effect until<br/>
|
||||
removing it from the allowlist will have no effect until<br/>
|
||||
it is reloaded.<br/>
|
||||
Separate your whitelisted domains by line, not commas. e.g.
|
||||
Separate your allowlisted domains by line, not commas. e.g.
|
||||
<blockquote>
|
||||
<b>https://google.com/</b><br/>
|
||||
<b>hifi://the-spot/</b><br/>
|
||||
|
@ -186,7 +186,7 @@ Rectangle {
|
|||
<b>https://mydomain.here/</b>
|
||||
</blockquote>
|
||||
Ensure there are no spaces or whitespace.<br/><br/>
|
||||
For QML files, you can only whitelist each file individually<br/>
|
||||
For QML files, you can only allowlist each file individually<br/>
|
||||
ending with '.qml'."
|
||||
// Text size
|
||||
size: 16;
|
|
@ -5,7 +5,7 @@
|
|||
// Created by dr Karol Suprynowicz on 2024/03/24.
|
||||
// Copyright 2024 Overte e.V.
|
||||
//
|
||||
// Based on EntityScriptQMLWhitelist.qml
|
||||
// Based on EntityScriptQMLAllowlist.qml
|
||||
// Created by Kalila L. on 2019.12.05 | realities.dev | somnilibertas@gmail.com
|
||||
// Copyright 2019 Kalila L.
|
||||
//
|
||||
|
@ -27,15 +27,15 @@ import "../../../windows"
|
|||
Rectangle {
|
||||
id: parentBody;
|
||||
|
||||
function getWhitelistAsText() {
|
||||
var whitelist = Settings.getValue("private/scriptPermissionGetAvatarURLSafeURLs");
|
||||
var arrayWhitelist = whitelist.replace(",", "\n");
|
||||
return arrayWhitelist;
|
||||
function getAllowlistAsText() {
|
||||
var allowlist = Settings.getValue("private/scriptPermissionGetAvatarURLSafeURLs");
|
||||
var arrayAllowlist = allowlist.replace(",", "\n");
|
||||
return arrayAllowlist;
|
||||
}
|
||||
|
||||
function setWhitelistAsText(whitelistText) {
|
||||
Settings.setValue("private/scriptPermissionGetAvatarURLSafeURLs", whitelistText.text);
|
||||
notificationText.text = "Whitelist saved.";
|
||||
function setAllowlistAsText(allowlistText) {
|
||||
Settings.setValue("private/scriptPermissionGetAvatarURLSafeURLs", allowlistText.text);
|
||||
notificationText.text = "Allowlist saved.";
|
||||
}
|
||||
|
||||
function setAvatarProtection(enabled) {
|
||||
|
@ -65,7 +65,7 @@ Rectangle {
|
|||
height: 60;
|
||||
|
||||
CheckBox {
|
||||
id: whitelistEnabled;
|
||||
id: allowlistEnabled;
|
||||
|
||||
checked: Settings.getValue("private/scriptPermissionGetAvatarURLEnable", true);
|
||||
|
||||
|
@ -73,7 +73,7 @@ Rectangle {
|
|||
anchors.top: parent.top;
|
||||
anchors.topMargin: 10;
|
||||
onToggled: {
|
||||
setAvatarProtection(whitelistEnabled.checked)
|
||||
setAvatarProtection(allowlistEnabled.checked)
|
||||
}
|
||||
|
||||
Label {
|
||||
|
@ -104,8 +104,8 @@ Rectangle {
|
|||
clip: false;
|
||||
|
||||
TextArea {
|
||||
id: whitelistTextArea
|
||||
text: getWhitelistAsText();
|
||||
id: allowlistTextArea
|
||||
text: getAllowlistAsText();
|
||||
onTextChanged: notificationText.text = "";
|
||||
width: parent.width;
|
||||
height: parent.height;
|
||||
|
@ -133,7 +133,7 @@ Rectangle {
|
|||
elide: Text.ElideRight
|
||||
}
|
||||
text: "Save Changes"
|
||||
onClicked: setWhitelistAsText(whitelistTextArea)
|
||||
onClicked: setAllowlistAsText(allowlistTextArea)
|
||||
|
||||
HifiStylesUit.RalewayRegular {
|
||||
id: notificationText;
|
||||
|
|
|
@ -211,7 +211,7 @@ Flickable {
|
|||
wrapMode: Text.Wrap
|
||||
|
||||
Component.onCompleted: {
|
||||
var gpu = JSON.parse(PlatformInfo.getGPU(PlatformInfo.getMasterGPU()));
|
||||
var gpu = JSON.parse(PlatformInfo.getGPU(PlatformInfo.getPrimaryGPU()));
|
||||
var gpuModel = gpu.model;
|
||||
if (!gpuModel || gpuModel.length === 0) {
|
||||
gpuModel = "Unknown";
|
||||
|
@ -336,7 +336,7 @@ Flickable {
|
|||
textToCopy += "# CPU Cores: " + PlatformInfo.getNumLogicalCores() + "\n";
|
||||
textToCopy += "RAM: " + PlatformInfo.getTotalSystemMemoryMB() + " MB\n";
|
||||
|
||||
var gpu = JSON.parse(PlatformInfo.getGPU(PlatformInfo.getMasterGPU()));
|
||||
var gpu = JSON.parse(PlatformInfo.getGPU(PlatformInfo.getPrimaryGPU()));
|
||||
var gpuModel = gpu.model;
|
||||
if (!gpuModel || gpuModel.length === 0) {
|
||||
gpuModel = "Unknown";
|
||||
|
|
30
interface/resources/shaders/errorShader.frag
Normal file
30
interface/resources/shaders/errorShader.frag
Normal file
|
@ -0,0 +1,30 @@
|
|||
vec3 getErrorColor() {
|
||||
vec3 positionWS = iWorldOrientation * (_positionMS.xyz * iWorldScale) + iWorldPosition;
|
||||
float checkSize = 0.1;
|
||||
vec3 edges = round(mod(positionWS, vec3(checkSize)) / checkSize);
|
||||
float checkerboard = mod(edges.x + edges.y + edges.z, 2.0);
|
||||
return mix(vec3(1, 0, 1), vec3(0.0), checkerboard);
|
||||
}
|
||||
|
||||
// version 1
|
||||
vec3 getProceduralColor() {
|
||||
return getErrorColor();
|
||||
}
|
||||
|
||||
// version 2
|
||||
float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) {
|
||||
diffuse = getErrorColor();
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
// version 3
|
||||
float getProceduralFragment(inout ProceduralFragment data) {
|
||||
data.emissive = getErrorColor();
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
// version 4
|
||||
float getProceduralFragmentWithPosition(inout ProceduralFragmentWithPosition data) {
|
||||
data.emissive = getErrorColor();
|
||||
return 1.0;
|
||||
}
|
7
interface/resources/shaders/errorSkyboxShader.frag
Normal file
7
interface/resources/shaders/errorSkyboxShader.frag
Normal file
|
@ -0,0 +1,7 @@
|
|||
vec3 getSkyboxColor() {
|
||||
vec3 normal = normalize(_normal);
|
||||
float checkSize = 0.1;
|
||||
vec3 edges = round(mod(normal, vec3(checkSize)) / checkSize);
|
||||
float checkerboard = mod(edges.x + edges.y + edges.z, 2.0);
|
||||
return mix(vec3(1, 0, 1), vec3(0.0), checkerboard);
|
||||
}
|
|
@ -3293,9 +3293,9 @@ void Application::initializeUi() {
|
|||
{
|
||||
auto defaultUrlValidator = OffscreenQmlSurface::getUrlValidator();
|
||||
auto newValidator = [=](const QUrl& url) -> bool {
|
||||
QString whitelistPrefix = "[WHITELIST ENTITY SCRIPTS]";
|
||||
QString allowlistPrefix = "[ALLOWLIST ENTITY SCRIPTS]";
|
||||
QList<QString> safeURLS = { "" };
|
||||
safeURLS += qEnvironmentVariable("EXTRA_WHITELIST").trimmed().split(QRegExp("\\s*,\\s*"), Qt::SkipEmptyParts);
|
||||
safeURLS += qEnvironmentVariable("EXTRA_ALLOWLIST").trimmed().split(QRegExp("\\s*,\\s*"), Qt::SkipEmptyParts);
|
||||
|
||||
// PULL SAFEURLS FROM INTERFACE.JSON Settings
|
||||
|
||||
|
@ -3332,7 +3332,7 @@ void Application::initializeUi() {
|
|||
QmlContextCallback platformInfoCallback = [](QQmlContext* context) {
|
||||
context->setContextProperty("PlatformInfo", new PlatformInfoScriptingInterface());
|
||||
};
|
||||
OffscreenQmlSurface::addWhitelistContextHandler({
|
||||
OffscreenQmlSurface::addAllowlistContextHandler({
|
||||
QUrl{ "hifi/tablet/TabletAddressDialog.qml" },
|
||||
QUrl{ "hifi/Card.qml" },
|
||||
QUrl{ "hifi/Pal.qml" },
|
||||
|
@ -3342,7 +3342,7 @@ void Application::initializeUi() {
|
|||
QmlContextCallback ttsCallback = [](QQmlContext* context) {
|
||||
context->setContextProperty("TextToSpeech", DependencyManager::get<TTSScriptingInterface>().data());
|
||||
};
|
||||
OffscreenQmlSurface::addWhitelistContextHandler({
|
||||
OffscreenQmlSurface::addAllowlistContextHandler({
|
||||
QUrl{ "hifi/tts/TTS.qml" }
|
||||
}, ttsCallback);
|
||||
qmlRegisterType<ResourceImageItem>("Hifi", 1, 0, "ResourceImageItem");
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "avatar/AvatarManager.h"
|
||||
#include "avatar/AvatarPackager.h"
|
||||
#include "AvatarBookmarks.h"
|
||||
#include <display-plugins/OpenGLDisplayPlugin.h>
|
||||
#include "DomainAccountManager.h"
|
||||
#include "MainWindow.h"
|
||||
#include "render/DrawStatus.h"
|
||||
|
@ -310,13 +311,13 @@ Menu::Menu() {
|
|||
}
|
||||
});
|
||||
|
||||
// Settings > Entity Script / QML Whitelist
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "Entity Script / QML Whitelist");
|
||||
// Settings > Entity Script / QML Allowlist
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "Entity Script / QML Allowlist");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
|
||||
auto hmd = DependencyManager::get<HMDScriptingInterface>();
|
||||
|
||||
tablet->pushOntoStack("hifi/dialogs/security/EntityScriptQMLWhitelist.qml");
|
||||
tablet->pushOntoStack("hifi/dialogs/security/EntityScriptQMLAllowlist.qml");
|
||||
|
||||
if (!hmd->getShouldShowTablet()) {
|
||||
hmd->toggleShouldShowTablet();
|
||||
|
@ -540,7 +541,7 @@ Menu::Menu() {
|
|||
|
||||
action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::MaterialProceduralShaders, 0, false);
|
||||
connect(action, &QAction::triggered, [action] {
|
||||
ModelMeshPartPayload::enableMaterialProceduralShaders = action->isChecked();
|
||||
Procedural::enableProceduralShaders = action->isChecked();
|
||||
});
|
||||
|
||||
{
|
||||
|
@ -549,6 +550,13 @@ Menu::Menu() {
|
|||
drawStatusConfig, SLOT(setShowFade(bool)));
|
||||
}
|
||||
|
||||
{
|
||||
action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ExtraLinearTosRGBConversion, 0, OpenGLDisplayPlugin::getExtraLinearToSRGBConversion());
|
||||
connect(action, &QAction::triggered, [action] {
|
||||
OpenGLDisplayPlugin::setExtraLinearToSRGBConversion(action->isChecked());
|
||||
});
|
||||
}
|
||||
|
||||
// Developer > Assets >>>
|
||||
// Menu item is not currently needed but code should be kept in case it proves useful again at some stage.
|
||||
//#define WANT_ASSET_MIGRATION
|
||||
|
|
|
@ -189,7 +189,7 @@ namespace MenuOption {
|
|||
const QString RunningScripts = "Running Scripts...";
|
||||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString ScriptedMotorControl = "Enable Scripted Motor Control";
|
||||
const QString EntityScriptQMLWhitelist = "Entity Script / QML Whitelist";
|
||||
const QString EntityScriptQMLAllowlist = "Entity Script / QML Allowlist";
|
||||
const QString ScriptSecurity = "Script Security";
|
||||
const QString ShowTrackedObjects = "Show Tracked Objects";
|
||||
const QString SelfieCamera = "Selfie";
|
||||
|
@ -237,7 +237,8 @@ namespace MenuOption {
|
|||
const QString ComputeBlendshapes = "Compute Blendshapes";
|
||||
const QString HighlightTransitions = "Highlight Transitions";
|
||||
const QString MaterialProceduralShaders = "Enable Procedural Materials";
|
||||
}
|
||||
const QString ExtraLinearTosRGBConversion = "Extra Linear to sRGB Conversion";
|
||||
}
|
||||
|
||||
#endif // hifi_Menu_h
|
||||
|
||||
|
|
|
@ -75,13 +75,13 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
|
|||
|
||||
// Ugly case that prevent us to run deferred everywhere...
|
||||
bool isDeferredCapable = platform::Profiler::isRenderMethodDeferredCapable();
|
||||
auto masterDisplay = platform::getDisplay(platform::getMasterDisplay());
|
||||
auto primaryDisplay = platform::getDisplay(platform::getPrimaryDisplay());
|
||||
|
||||
// eval recommended PPI and Scale
|
||||
float recommendedPpiScale = 1.0f;
|
||||
const float RECOMMENDED_PPI[] = { 200.0f, 200.0f, 120.f, 160.f, 250.f};
|
||||
if (!masterDisplay.empty() && masterDisplay.count(platform::keys::display::ppi)) {
|
||||
float ppi = masterDisplay[platform::keys::display::ppi];
|
||||
if (!primaryDisplay.empty() && primaryDisplay.count(platform::keys::display::ppi)) {
|
||||
float ppi = primaryDisplay[platform::keys::display::ppi];
|
||||
// only scale if the actual ppi is higher than the recommended ppi
|
||||
if (ppi > RECOMMENDED_PPI[preset]) {
|
||||
// make sure the scale is no less than a quarter
|
||||
|
|
|
@ -97,7 +97,7 @@ int main(int argc, const char* argv[]) {
|
|||
);
|
||||
QCommandLineOption protocolVersionOption(
|
||||
"protocolVersion",
|
||||
"Writes the protocol version base64 signature to a file?",
|
||||
"Writes the protocol version base64 signature to a file",
|
||||
"path"
|
||||
);
|
||||
QCommandLineOption noUpdaterOption(
|
||||
|
@ -275,10 +275,18 @@ int main(int argc, const char* argv[]) {
|
|||
"abortAfterInit",
|
||||
"Debug option. Aborts after initialization, right before the program starts running the event loop."
|
||||
);
|
||||
QCommandLineOption getProtocolVersionHashOption(
|
||||
"getProtocolVersionHash",
|
||||
"Debug option. Returns the network protocol version MD5 hash."
|
||||
);
|
||||
QCommandLineOption getProtocolVersionDataOption(
|
||||
"getProtocolVersionData",
|
||||
"Debug option. Returns the network protocol detailed data in JSON."
|
||||
);
|
||||
|
||||
// "--qmljsdebugger", which appears in output from "--help-all".
|
||||
// Those below don't seem to be optional.
|
||||
// --ignore-gpu-blacklist
|
||||
// --ignore-gpu-blocklist
|
||||
// --suppress-settings-reset
|
||||
|
||||
|
||||
|
@ -321,6 +329,8 @@ int main(int argc, const char* argv[]) {
|
|||
parser.addOption(abortAfterStartupOption);
|
||||
parser.addOption(abortAfterInitOption);
|
||||
parser.addOption(getPluginsOption);
|
||||
parser.addOption(getProtocolVersionHashOption);
|
||||
parser.addOption(getProtocolVersionDataOption);
|
||||
|
||||
|
||||
QString applicationPath;
|
||||
|
@ -455,6 +465,34 @@ int main(int argc, const char* argv[]) {
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
if (parser.isSet(getProtocolVersionHashOption)) {
|
||||
std::cout << protocolVersionsSignatureHex().toStdString() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
if (parser.isSet(getProtocolVersionDataOption)) {
|
||||
auto protocolMap = protocolVersionsSignatureMap();
|
||||
QMetaEnum packetMetaEnum = QMetaEnum::fromType<PacketTypeEnum::Value>();
|
||||
|
||||
QJsonArray packetTypesList;
|
||||
auto keyList = protocolMap.keys();
|
||||
std::sort(keyList.begin(), keyList.end()); // Sort by numeric value
|
||||
|
||||
for(const auto packet : keyList) {
|
||||
QJsonObject data;
|
||||
int intValue = static_cast<int>(packet);
|
||||
QString keyName = packetMetaEnum.valueToKey(intValue);
|
||||
|
||||
data["name"] = keyName;
|
||||
data["value"] = intValue;
|
||||
data["version"] = versionForPacketType(packet);
|
||||
|
||||
packetTypesList.append(data);
|
||||
}
|
||||
|
||||
std::cout << QJsonDocument(packetTypesList).toJson().toStdString() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const QString APPLICATION_CONFIG_FILENAME = "config.json";
|
||||
QDir applicationDir(applicationPath);
|
||||
|
|
|
@ -408,9 +408,9 @@ CollisionRegion CollisionPick::getMathematicalPick() const {
|
|||
void CollisionPick::filterIntersections(std::vector<ContactTestResult>& intersections) const {
|
||||
const QVector<QUuid>& ignoreItems = getIgnoreItems();
|
||||
const QVector<QUuid>& includeItems = getIncludeItems();
|
||||
bool isWhitelist = !includeItems.empty();
|
||||
bool isAllowlist = !includeItems.empty();
|
||||
|
||||
if (!isWhitelist && ignoreItems.empty()) {
|
||||
if (!isAllowlist && ignoreItems.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -420,7 +420,7 @@ void CollisionPick::filterIntersections(std::vector<ContactTestResult>& intersec
|
|||
for (int i = 0; i < n; i++) {
|
||||
auto& intersection = intersections[i];
|
||||
const QUuid& id = intersection.foundID;
|
||||
if (!ignoreItems.contains(id) && (!isWhitelist || includeItems.contains(id))) {
|
||||
if (!ignoreItems.contains(id) && (!isAllowlist || includeItems.contains(id))) {
|
||||
filteredIntersections.push_back(intersection);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -533,7 +533,7 @@ void Audio::setAvatarGain(float gain) {
|
|||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
// ask the NodeList to set the master avatar gain
|
||||
// ask the NodeList to set the primary avatar gain
|
||||
DependencyManager::get<NodeList>()->setAvatarGain(QUuid(), gain);
|
||||
});
|
||||
|
||||
|
|
|
@ -182,8 +182,8 @@ bool PlatformInfoScriptingInterface::isStandalone() {
|
|||
int PlatformInfoScriptingInterface::getNumCPUs() {
|
||||
return platform::getNumCPUs();
|
||||
}
|
||||
int PlatformInfoScriptingInterface::getMasterCPU() {
|
||||
return platform::getMasterCPU();
|
||||
int PlatformInfoScriptingInterface::getPrimaryCPU() {
|
||||
return platform::getPrimaryCPU();
|
||||
}
|
||||
QString PlatformInfoScriptingInterface::getCPU(int index) {
|
||||
auto desc = platform::getCPU(index);
|
||||
|
@ -193,8 +193,8 @@ QString PlatformInfoScriptingInterface::getCPU(int index) {
|
|||
int PlatformInfoScriptingInterface::getNumGPUs() {
|
||||
return platform::getNumGPUs();
|
||||
}
|
||||
int PlatformInfoScriptingInterface::getMasterGPU() {
|
||||
return platform::getMasterGPU();
|
||||
int PlatformInfoScriptingInterface::getPrimaryGPU() {
|
||||
return platform::getPrimaryGPU();
|
||||
}
|
||||
QString PlatformInfoScriptingInterface::getGPU(int index) {
|
||||
auto desc = platform::getGPU(index);
|
||||
|
@ -204,8 +204,8 @@ QString PlatformInfoScriptingInterface::getGPU(int index) {
|
|||
int PlatformInfoScriptingInterface::getNumDisplays() {
|
||||
return platform::getNumDisplays();
|
||||
}
|
||||
int PlatformInfoScriptingInterface::getMasterDisplay() {
|
||||
return platform::getMasterDisplay();
|
||||
int PlatformInfoScriptingInterface::getPrimaryDisplay() {
|
||||
return platform::getPrimaryDisplay();
|
||||
}
|
||||
QString PlatformInfoScriptingInterface::getDisplay(int index) {
|
||||
auto desc = platform::getDisplay(index);
|
||||
|
|
|
@ -106,8 +106,8 @@ public slots:
|
|||
* @function PlatformInfo.getGraphicsCardType
|
||||
* @returns {string} The model of the graphics card currently being used.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
* Use <code>JSON.parse({@link PlatformInfo.getGPU|PlatformInfo.getGPU(}
|
||||
* {@link PlatformInfo.getMasterGPU|PlatformInfo.getMasterGPU() )}).model</code>
|
||||
* Use <code>JSON.parse({@link PlatformInfo.getGPU|PlatformInfo.getGPU(}
|
||||
* {@link PlatformInfo.getPrimaryGPU|PlatformInfo.getPrimaryGPU() )}).model</code>
|
||||
* instead.
|
||||
*/
|
||||
QString getGraphicsCardType();
|
||||
|
@ -149,11 +149,11 @@ public slots:
|
|||
int getNumCPUs();
|
||||
|
||||
/*@jsdoc
|
||||
* Gets the index number of the master CPU.
|
||||
* @function PlatformInfo.getMasterCPU
|
||||
* @returns {number} The index of the master CPU.
|
||||
* Gets the index number of the primary CPU.
|
||||
* @function PlatformInfo.getPrimaryCPU
|
||||
* @returns {number} The index of the primary CPU.
|
||||
*/
|
||||
int getMasterCPU();
|
||||
int getPrimaryCPU();
|
||||
|
||||
/*@jsdoc
|
||||
* Gets the platform description of a CPU.
|
||||
|
@ -178,11 +178,11 @@ public slots:
|
|||
int getNumGPUs();
|
||||
|
||||
/*@jsdoc
|
||||
* Gets the index number of the master GPU.
|
||||
* @function PlatformInfo.getMasterGPU
|
||||
* @returns {number} The index of the master GPU.
|
||||
* Gets the index number of the primary GPU.
|
||||
* @function PlatformInfo.getPrimaryGPU
|
||||
* @returns {number} The index of the primary GPU.
|
||||
*/
|
||||
int getMasterGPU();
|
||||
int getPrimaryGPU();
|
||||
|
||||
/*@jsdoc
|
||||
* Gets the platform description of a GPU.
|
||||
|
@ -207,11 +207,11 @@ public slots:
|
|||
int getNumDisplays();
|
||||
|
||||
/*@jsdoc
|
||||
* Gets the index number of the master display.
|
||||
* @function PlatformInfo.getMasterDisplay
|
||||
* @returns {number} The index of the master display.
|
||||
* Gets the index number of the primary display.
|
||||
* @function PlatformInfo.getPrimaryDisplay
|
||||
* @returns {number} The index of the primary display.
|
||||
*/
|
||||
int getMasterDisplay();
|
||||
int getPrimaryDisplay();
|
||||
|
||||
/*@jsdoc
|
||||
* Gets the platform description of a display.
|
||||
|
|
|
@ -221,8 +221,8 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
|||
|
||||
Application::setupQmlSurface(quickView->rootContext(), true);
|
||||
|
||||
//add any whitelisted callbacks
|
||||
OffscreenUi::applyWhiteList(sourceUrl, quickView->rootContext());
|
||||
//add any allowlisted callbacks
|
||||
OffscreenUi::applyAllowList(sourceUrl, quickView->rootContext());
|
||||
|
||||
/*@jsdoc
|
||||
* Configures how a <code>NATIVE</code> window is displayed.
|
||||
|
|
|
@ -538,7 +538,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
|||
}
|
||||
}
|
||||
} else if (targetType == IKTarget::Type::HmdHead) {
|
||||
// An HmdHead target slaves the orientation of the end-effector by distributing rotation
|
||||
// An HmdHead target workers the orientation of the end-effector by distributing rotation
|
||||
// deltas up the hierarchy. Its target position is enforced later (by shifting the hips).
|
||||
deltaRotation = target.getRotation() * glm::inverse(tipOrientation);
|
||||
const float ANGLE_DISTRIBUTION_FACTOR = 0.45f;
|
||||
|
|
|
@ -71,11 +71,11 @@ public:
|
|||
|
||||
using IgnoreBox = AABox;
|
||||
|
||||
// called from single AudioMixerSlave while processing packets for node
|
||||
// called from single AudioMixerWorker while processing packets for node
|
||||
void enableIgnoreBox();
|
||||
void disableIgnoreBox() { _isIgnoreBoxEnabled = false; }
|
||||
|
||||
// thread-safe, called from AudioMixerSlave(s) while preparing mixes
|
||||
// thread-safe, called from AudioMixerWorker(s) while preparing mixes
|
||||
bool isIgnoreBoxEnabled() const { return _isIgnoreBoxEnabled; }
|
||||
const IgnoreBox& getIgnoreBox() const { return _ignoreBox; }
|
||||
|
||||
|
|
|
@ -4417,9 +4417,9 @@ bs_new ()
|
|||
bs = (Bit_stream_struc *) calloc(1, sizeof(Bit_stream_struc));
|
||||
g_return_val_if_fail (bs != NULL, NULL);
|
||||
|
||||
bs->master.cur_bit = 8;
|
||||
bs->master.size = 0;
|
||||
bs->master.cur_used = 0;
|
||||
bs->primary.cur_bit = 8;
|
||||
bs->primary.size = 0;
|
||||
bs->primary.cur_used = 0;
|
||||
bs->read.cur_bit = 8;
|
||||
bs->read.size = 0;
|
||||
bs->read.cur_used = 0;
|
||||
|
@ -4443,11 +4443,11 @@ bs_set_data (Bit_stream_struc * bs, const guint8 * data, gsize size)
|
|||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
g_return_val_if_fail (size != 0, FALSE);
|
||||
|
||||
bs->master.data = data;
|
||||
bs->master.cur_byte = (guint8 *) data;
|
||||
bs->master.size = size;
|
||||
bs->master.bitpos = 0;
|
||||
bs->master.cur_used = 0;
|
||||
bs->primary.data = data;
|
||||
bs->primary.cur_byte = (guint8 *) data;
|
||||
bs->primary.size = size;
|
||||
bs->primary.bitpos = 0;
|
||||
bs->primary.cur_used = 0;
|
||||
bs_reset (bs);
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -4494,7 +4494,7 @@ bs_eat (Bit_stream_struc * bs, BSReader * read, guint32 Nbits)
|
|||
}
|
||||
}
|
||||
|
||||
/* Advance the master position by Nbits */
|
||||
/* Advance the primary position by Nbits */
|
||||
void
|
||||
bs_consume (Bit_stream_struc * bs, guint32 Nbits)
|
||||
{
|
||||
|
@ -4503,7 +4503,7 @@ bs_consume (Bit_stream_struc * bs, guint32 Nbits)
|
|||
GST_DEBUG ("%d Consumed %d bits to end at %" G_GUINT64_FORMAT,
|
||||
n++, Nbits, bs_pos (bs) + Nbits);
|
||||
#endif
|
||||
bs_eat (bs, &bs->master, Nbits);
|
||||
bs_eat (bs, &bs->primary, Nbits);
|
||||
}
|
||||
|
||||
/* Advance the read position by Nbits */
|
||||
|
|
|
@ -197,10 +197,10 @@ typedef struct BSReader
|
|||
|
||||
typedef struct Bit_stream_struc
|
||||
{
|
||||
BSReader master; /* Master tracking position, advanced
|
||||
BSReader primary; /* Primary tracking position, advanced
|
||||
* by bs_consume() */
|
||||
BSReader read; /* Current read position, set back to the
|
||||
* master by bs_reset() */
|
||||
* primary by bs_reset() */
|
||||
} Bit_stream_struc;
|
||||
|
||||
/* Create and initialise a new bitstream reader */
|
||||
|
@ -209,25 +209,25 @@ Bit_stream_struc *bs_new ();
|
|||
/* Release a bitstream reader */
|
||||
void bs_free (Bit_stream_struc * bs);
|
||||
|
||||
/* Reset the current read position to the master position */
|
||||
/* Reset the current read position to the primary position */
|
||||
static inline void
|
||||
bs_reset (Bit_stream_struc * bs)
|
||||
{
|
||||
memcpy (&bs->read, &bs->master, sizeof (BSReader));
|
||||
memcpy (&bs->read, &bs->primary, sizeof (BSReader));
|
||||
}
|
||||
|
||||
/* Reset master and read states */
|
||||
/* Reset primary and read states */
|
||||
static inline void
|
||||
bs_flush (Bit_stream_struc * bs)
|
||||
{
|
||||
g_return_if_fail (bs != NULL);
|
||||
|
||||
bs->master.cur_bit = 8;
|
||||
bs->master.size = 0;
|
||||
bs->master.cur_used = 0;
|
||||
bs->master.cur_byte = NULL;
|
||||
bs->master.data = NULL;
|
||||
bs->master.bitpos = 0;
|
||||
bs->primary.cur_bit = 8;
|
||||
bs->primary.size = 0;
|
||||
bs->primary.cur_used = 0;
|
||||
bs->primary.cur_byte = NULL;
|
||||
bs->primary.data = NULL;
|
||||
bs->primary.bitpos = 0;
|
||||
|
||||
bs_reset (bs);
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ bs_flush (Bit_stream_struc * bs)
|
|||
/* Set data as the stream for processing */
|
||||
gboolean bs_set_data (Bit_stream_struc * bs, const guint8 * data, gsize size);
|
||||
|
||||
/* Advance the master position by Nbits */
|
||||
/* Advance the primary position by Nbits */
|
||||
void bs_consume (Bit_stream_struc * bs, guint32 Nbits);
|
||||
|
||||
/* Number of bits available for reading */
|
||||
|
@ -253,14 +253,14 @@ void bs_skipbits (Bit_stream_struc * bs, guint32 N);
|
|||
/* give number of consumed bytes */
|
||||
static inline gsize bs_get_consumed (Bit_stream_struc * bs)
|
||||
{
|
||||
return bs->master.cur_used;
|
||||
return bs->primary.cur_used;
|
||||
}
|
||||
|
||||
/* Current bitstream position in bits */
|
||||
static inline guint64
|
||||
bs_pos (Bit_stream_struc * bs)
|
||||
{
|
||||
return bs->master.bitpos;
|
||||
return bs->primary.bitpos;
|
||||
}
|
||||
|
||||
/* Current read bitstream position in bits */
|
||||
|
|
|
@ -1704,7 +1704,7 @@ protected:
|
|||
void unpackSkeletonModelURL(const QByteArray& data);
|
||||
void unpackSkeletonData(const QByteArray& data);
|
||||
|
||||
// isReplicated will be true on downstream Avatar Mixers and their clients, but false on the upstream "master"
|
||||
// isReplicated will be true on downstream Avatar Mixers and their clients, but false on the upstream "primary"
|
||||
// Audio Mixer that the replicated avatar is connected to.
|
||||
bool _isReplicated{ false };
|
||||
|
||||
|
|
|
@ -112,14 +112,6 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() {
|
|||
return Parent::internalActivate();
|
||||
}
|
||||
|
||||
gpu::PipelinePointer Basic2DWindowOpenGLDisplayPlugin::getRenderTexturePipeline() {
|
||||
#if defined(Q_OS_ANDROID)
|
||||
return _linearToSRGBPipeline;
|
||||
#else
|
||||
return _drawTexturePipeline;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() {
|
||||
#if defined(Q_OS_ANDROID)
|
||||
auto& virtualPadManager = VirtualPad::Manager::instance();
|
||||
|
|
|
@ -37,8 +37,6 @@ public:
|
|||
|
||||
virtual void pluginUpdate() override {};
|
||||
|
||||
virtual gpu::PipelinePointer getRenderTexturePipeline() override;
|
||||
|
||||
protected:
|
||||
mutable bool _isThrottled = false;
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <gl/GLEscrow.h>
|
||||
#include <gl/Context.h>
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
|
||||
#include <gpu/Texture.h>
|
||||
#include <gpu/FrameIO.h>
|
||||
|
@ -57,6 +58,8 @@ using namespace shader::gpu::program;
|
|||
|
||||
extern QThread* RENDER_THREAD;
|
||||
|
||||
Setting::Handle<bool> OpenGLDisplayPlugin::_extraLinearToSRGBConversionSetting("extraLinearToSRGBConversion", false);
|
||||
|
||||
class PresentThread : public QThread, public Dependency {
|
||||
using Mutex = std::mutex;
|
||||
using Condition = std::condition_variable;
|
||||
|
@ -956,5 +959,16 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne
|
|||
}
|
||||
|
||||
gpu::PipelinePointer OpenGLDisplayPlugin::getRenderTexturePipeline() {
|
||||
return _drawTexturePipeline;
|
||||
#ifdef USE_GLES
|
||||
if (!_extraLinearToSRGBConversionSetting.isSet()) {
|
||||
const gl::ContextInfo &contextInfo = gl::ContextInfo::get();
|
||||
_extraLinearToSRGBConversionSetting.set(std::find(contextInfo.extensions.cbegin(), contextInfo.extensions.cend(), "GL_EXT_framebuffer_sRGB") == contextInfo.extensions.cend());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (getExtraLinearToSRGBConversion()) {
|
||||
return _linearToSRGBPipeline;
|
||||
} else {
|
||||
return _drawTexturePipeline;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QtGui/QImage>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
#include <shared/RateCounter.h>
|
||||
|
||||
|
@ -86,6 +87,9 @@ public:
|
|||
QOpenGLFramebufferObject* target,
|
||||
GLsync* fenceSync) override;
|
||||
|
||||
static void setExtraLinearToSRGBConversion(bool value) { _extraLinearToSRGBConversionSetting.set(value); }
|
||||
static bool getExtraLinearToSRGBConversion() { return _extraLinearToSRGBConversionSetting.get(); };
|
||||
|
||||
protected:
|
||||
friend class PresentThread;
|
||||
|
||||
|
@ -201,4 +205,7 @@ protected:
|
|||
|
||||
QImage getScreenshot(float aspectRatio);
|
||||
QImage getSecondaryCameraScreenshot();
|
||||
|
||||
private:
|
||||
static Setting::Handle<bool> _extraLinearToSRGBConversionSetting;
|
||||
};
|
||||
|
|
|
@ -65,8 +65,8 @@ EntityTree::~EntityTree() {
|
|||
//eraseAllOctreeElements(false); // KEEP THIS
|
||||
}
|
||||
|
||||
void EntityTree::setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist) {
|
||||
_entityScriptSourceWhitelist = entityScriptSourceWhitelist.split(',', Qt::SkipEmptyParts);
|
||||
void EntityTree::setEntityScriptSourceAllowlist(const QString& entityScriptSourceAllowlist) {
|
||||
_entityScriptSourceAllowlist = entityScriptSourceAllowlist.split(',', Qt::SkipEmptyParts);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1411,17 +1411,17 @@ void EntityTree::bumpTimestamp(EntityItemProperties& properties) { //fixme put c
|
|||
properties.setLastEdited(properties.getLastEdited() + LAST_EDITED_SERVERSIDE_BUMP);
|
||||
}
|
||||
|
||||
bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) {
|
||||
bool EntityTree::isScriptInAllowlist(const QString& scriptProperty) {
|
||||
|
||||
// grab a URL representation of the entity script so we can check the host for this script
|
||||
auto entityScriptURL = QUrl::fromUserInput(scriptProperty);
|
||||
|
||||
for (const auto& whiteListedPrefix : _entityScriptSourceWhitelist) {
|
||||
auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix);
|
||||
for (const auto& allowListedPrefix : _entityScriptSourceAllowlist) {
|
||||
auto allowListURL = QUrl::fromUserInput(allowListedPrefix);
|
||||
|
||||
// check if this script URL matches the whitelist domain and, optionally, is beneath the path
|
||||
if (entityScriptURL.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 &&
|
||||
entityScriptURL.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) {
|
||||
// check if this script URL matches the allowlist domain and, optionally, is beneath the path
|
||||
if (entityScriptURL.host().compare(allowListURL.host(), Qt::CaseInsensitive) == 0 &&
|
||||
entityScriptURL.path().startsWith(allowListURL.path(), Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1504,18 +1504,18 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
}
|
||||
}
|
||||
|
||||
if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty()) {
|
||||
if (validEditPacket && !_entityScriptSourceAllowlist.isEmpty()) {
|
||||
|
||||
bool wasDeletedBecauseOfClientScript = false;
|
||||
|
||||
// check the client entity script to make sure its URL is in the whitelist
|
||||
// check the client entity script to make sure its URL is in the allowlist
|
||||
if (!properties.getScript().isEmpty()) {
|
||||
bool clientScriptPassedWhitelist = isScriptInWhitelist(properties.getScript());
|
||||
bool clientScriptPassedAllowlist = isScriptInAllowlist(properties.getScript());
|
||||
|
||||
if (!clientScriptPassedWhitelist) {
|
||||
if (!clientScriptPassedAllowlist) {
|
||||
if (wantEditLogging()) {
|
||||
qCDebug(entities) << "User [" << senderNode->getUUID()
|
||||
<< "] attempting to set entity script not on whitelist, edit rejected";
|
||||
<< "] attempting to set entity script not on allowlist, edit rejected";
|
||||
}
|
||||
|
||||
// If this was an add, we also want to tell the client that sent this edit that the entity was not added.
|
||||
|
@ -1530,20 +1530,20 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
}
|
||||
}
|
||||
|
||||
// check all server entity scripts to make sure their URLs are in the whitelist
|
||||
// check all server entity scripts to make sure their URLs are in the allowlist
|
||||
if (!properties.getServerScripts().isEmpty()) {
|
||||
bool serverScriptPassedWhitelist = isScriptInWhitelist(properties.getServerScripts());
|
||||
bool serverScriptPassedAllowlist = isScriptInAllowlist(properties.getServerScripts());
|
||||
|
||||
if (!serverScriptPassedWhitelist) {
|
||||
if (!serverScriptPassedAllowlist) {
|
||||
if (wantEditLogging()) {
|
||||
qCDebug(entities) << "User [" << senderNode->getUUID()
|
||||
<< "] attempting to set server entity script not on whitelist, edit rejected";
|
||||
<< "] attempting to set server entity script not on allowlist, edit rejected";
|
||||
}
|
||||
|
||||
// If this was an add, we also want to tell the client that sent this edit that the entity was not added.
|
||||
if (isAdd) {
|
||||
// Make sure we didn't already need to send back a delete because the client script failed
|
||||
// the whitelist check
|
||||
// the allowlist check
|
||||
if (!wasDeletedBecauseOfClientScript) {
|
||||
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
|
||||
_recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
|
||||
|
|
|
@ -63,7 +63,7 @@ public:
|
|||
|
||||
|
||||
void setEntityMaxTmpLifetime(float maxTmpEntityLifetime) { _maxTmpEntityLifetime = maxTmpEntityLifetime; }
|
||||
void setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist);
|
||||
void setEntityScriptSourceAllowlist(const QString& entityScriptSourceAllowlist);
|
||||
|
||||
/// Implements our type specific root element factory
|
||||
virtual OctreeElementPointer createNewElement(unsigned char* octalCode = NULL) override;
|
||||
|
@ -300,7 +300,7 @@ protected:
|
|||
|
||||
void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);
|
||||
|
||||
bool isScriptInWhitelist(const QString& scriptURL);
|
||||
bool isScriptInAllowlist(const QString& scriptURL);
|
||||
|
||||
QReadWriteLock _newlyCreatedHooksLock;
|
||||
QVector<NewlyCreatedEntityHook*> _newlyCreatedHooks;
|
||||
|
@ -363,7 +363,7 @@ protected:
|
|||
|
||||
bool filterProperties(const EntityItemPointer& existingEntity, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, FilterType filterType) const;
|
||||
bool _hasEntityEditFilter{ false };
|
||||
QStringList _entityScriptSourceWhitelist;
|
||||
QStringList _entityScriptSourceAllowlist;
|
||||
|
||||
MovingEntitiesOperator _entityMover;
|
||||
QHash<EntityItemID, EntityItemPointer> _entitiesToAdd;
|
||||
|
|
|
@ -126,7 +126,7 @@ protected:
|
|||
const Shaders _shaders;
|
||||
|
||||
|
||||
// The type of the shader, the master key
|
||||
// The type of the shader, the primary key
|
||||
const Type _type;
|
||||
|
||||
// Number of attempts to compile the shader
|
||||
|
|
|
@ -405,7 +405,7 @@ public:
|
|||
Stamp getDataStamp() const { return _storage->getStamp(); }
|
||||
|
||||
// The theoretical size in bytes of data stored in the texture
|
||||
// For the master (level) first level of mip
|
||||
// For the primary (level) first level of mip
|
||||
Size getSize() const override { return _size; }
|
||||
|
||||
// Size and format
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Copyright 2023 Overte e.V.
|
||||
// Copyright 2023-2024 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -696,7 +696,8 @@ void AccountManager::setAccessTokens(const QString& response) {
|
|||
if (!rootObject.contains("access_token") || !rootObject.contains("expires_in")
|
||||
|| !rootObject.contains("token_type")) {
|
||||
// TODO: error handling - malformed token response
|
||||
qCDebug(networking) << "Received a response for password grant that is missing one or more expected values.";
|
||||
qCWarning(networking) << "Error setting access token. Received a response for password grant that is missing one or more expected values.";
|
||||
qCWarning(networking) << "Response:" << QJsonDocument(rootObject).toJson(QJsonDocument::Compact);
|
||||
} else {
|
||||
// clear the path from the response URL so we have the right root URL for this access token
|
||||
QUrl rootURL = rootObject.contains("url") ? rootObject["url"].toString() : _authURL;
|
||||
|
@ -714,7 +715,7 @@ void AccountManager::setAccessTokens(const QString& response) {
|
|||
}
|
||||
} else {
|
||||
// TODO: error handling
|
||||
qCDebug(networking) << "Error in response for password grant -" << rootObject["error_description"].toString();
|
||||
qCWarning(networking) << "Error in response for password grant -" << rootObject["error"].toString();
|
||||
emit loginFailed();
|
||||
}
|
||||
}
|
||||
|
@ -731,7 +732,8 @@ void AccountManager::requestAccessTokenFinished() {
|
|||
if (!rootObject.contains("access_token") || !rootObject.contains("expires_in")
|
||||
|| !rootObject.contains("token_type")) {
|
||||
// TODO: error handling - malformed token response
|
||||
qCDebug(networking) << "Received a response for password grant that is missing one or more expected values.";
|
||||
qCWarning(networking) << "Error requesting access token. Received a response for password grant that is missing one or more expected values.";
|
||||
qCWarning(networking) << "Response:" << QJsonDocument(rootObject).toJson(QJsonDocument::Compact);
|
||||
} else {
|
||||
// clear the path from the response URL so we have the right root URL for this access token
|
||||
QUrl rootURL = requestReply->url();
|
||||
|
@ -750,7 +752,7 @@ void AccountManager::requestAccessTokenFinished() {
|
|||
}
|
||||
} else {
|
||||
// TODO: error handling
|
||||
qCDebug(networking) << "Error in response for password grant -" << rootObject["error_description"].toString();
|
||||
qCWarning(networking) << "Error in response for password grant -" << rootObject["error"].toString();
|
||||
emit loginFailed();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1250,7 +1250,7 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
|
|||
setAvatarGainPacket->writePrimitive(packFloatGainToByte(fastExp2f(gain / 6.02059991f)));
|
||||
|
||||
if (nodeID.isNull()) {
|
||||
qCDebug(networking) << "Sending Set MASTER Avatar Gain packet with Gain:" << gain;
|
||||
qCDebug(networking) << "Sending Set PRIMARY Avatar Gain packet with Gain:" << gain;
|
||||
|
||||
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ void UserActivityLogger::changedDomain(QString domainURL) {
|
|||
}
|
||||
|
||||
void UserActivityLogger::connectedDevice(QString typeOfDevice, QString deviceName) {
|
||||
static QStringList DEVICE_BLACKLIST = {
|
||||
static QStringList DEVICE_BLOCKLIST = {
|
||||
"Desktop",
|
||||
"NullDisplayPlugin",
|
||||
"3D TV - Side by Side Stereo",
|
||||
|
@ -145,7 +145,7 @@ void UserActivityLogger::connectedDevice(QString typeOfDevice, QString deviceNam
|
|||
"Keyboard/Mouse"
|
||||
};
|
||||
|
||||
if (DEVICE_BLACKLIST.contains(deviceName) || deviceName.isEmpty()) {
|
||||
if (DEVICE_BLOCKLIST.contains(deviceName) || deviceName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -130,6 +130,10 @@ void sendWrongProtocolVersionsSignature(bool sendWrongVersion) {
|
|||
|
||||
static QByteArray protocolVersionSignature;
|
||||
static QString protocolVersionSignatureBase64;
|
||||
static QString protocolVersionSignatureHex;
|
||||
static QMap<PacketType, uint8_t> protocolVersionMap;
|
||||
|
||||
|
||||
static void ensureProtocolVersionsSignature() {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
|
@ -139,12 +143,14 @@ static void ensureProtocolVersionsSignature() {
|
|||
stream << numberOfProtocols;
|
||||
for (uint8_t packetType = 0; packetType < numberOfProtocols; packetType++) {
|
||||
uint8_t packetTypeVersion = static_cast<uint8_t>(versionForPacketType(static_cast<PacketType>(packetType)));
|
||||
protocolVersionMap[static_cast<PacketType>(packetType)] = packetTypeVersion;
|
||||
stream << packetTypeVersion;
|
||||
}
|
||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
hash.addData(buffer);
|
||||
protocolVersionSignature = hash.result();
|
||||
protocolVersionSignatureBase64 = protocolVersionSignature.toBase64();
|
||||
protocolVersionSignatureHex = protocolVersionSignature.toHex(0);
|
||||
});
|
||||
}
|
||||
QByteArray protocolVersionsSignature() {
|
||||
|
@ -161,3 +167,13 @@ QString protocolVersionsSignatureBase64() {
|
|||
ensureProtocolVersionsSignature();
|
||||
return protocolVersionSignatureBase64;
|
||||
}
|
||||
|
||||
QString protocolVersionsSignatureHex() {
|
||||
ensureProtocolVersionsSignature();
|
||||
return protocolVersionSignatureHex;
|
||||
}
|
||||
|
||||
QMap<PacketType, uint8_t> protocolVersionsSignatureMap() {
|
||||
ensureProtocolVersionsSignature();
|
||||
return protocolVersionMap;
|
||||
}
|
|
@ -31,8 +31,15 @@ class PacketTypeEnum {
|
|||
Q_GADGET
|
||||
Q_ENUMS(Value)
|
||||
public:
|
||||
// If adding a new packet packetType, you can replace one marked usable or add at the end.
|
||||
// This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t
|
||||
|
||||
/**
|
||||
* @brief Packet type identifier
|
||||
*
|
||||
* Identifies the type of packet being sent.
|
||||
*
|
||||
* @note If adding a new packet packetType, you can replace one marked usable or add at the end.
|
||||
* @note This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t
|
||||
*/
|
||||
enum class Value : uint8_t {
|
||||
Unknown,
|
||||
DomainConnectRequestPending,
|
||||
|
@ -143,6 +150,8 @@ public:
|
|||
NUM_PACKET_TYPE
|
||||
};
|
||||
|
||||
Q_ENUM(Value)
|
||||
|
||||
const static QHash<PacketTypeEnum::Value, PacketTypeEnum::Value> getReplicatedPacketMapping() {
|
||||
const static QHash<PacketTypeEnum::Value, PacketTypeEnum::Value> REPLICATED_PACKET_MAPPING {
|
||||
{ PacketTypeEnum::Value::MicrophoneAudioNoEcho, PacketTypeEnum::Value::ReplicatedMicrophoneAudioNoEcho },
|
||||
|
@ -219,10 +228,60 @@ const int NUM_BYTES_MD5_HASH = 16;
|
|||
// NOTE: There is a max limit of 255, hopefully we have a better way to manage this by then.
|
||||
typedef uint8_t PacketVersion;
|
||||
|
||||
/**
|
||||
* @brief Returns the version number of the given packet type
|
||||
*
|
||||
* If the implementation of a packet type is modified in an incompatible way, the implementation
|
||||
* of this function needs to be modified to return an incremented value.
|
||||
*
|
||||
* This is used to determine whether the protocol is compatible between client and server.
|
||||
*
|
||||
* @note Version is limited to a max of 255.
|
||||
*
|
||||
* @param packetType Type of packet
|
||||
* @return PacketVersion Version
|
||||
*/
|
||||
PacketVersion versionForPacketType(PacketType packetType);
|
||||
QByteArray protocolVersionsSignature(); /// returns a unique signature for all the current protocols
|
||||
|
||||
/**
|
||||
* @brief Returns a unique signature for all the current protocols
|
||||
*
|
||||
* This computes a MD5 hash that expresses the state of the protocol's specification. The calculation
|
||||
* is done in ensureProtocolVersionsSignature and accounts for the following:
|
||||
*
|
||||
* * Number of known packet types
|
||||
* * versionForPacketType(type) for each packet type.
|
||||
*
|
||||
* There's no provision for backwards compatibility, anything that changes this calculation is a protocol break.
|
||||
*
|
||||
* @return QByteArray MD5 digest as a byte array
|
||||
*/
|
||||
QByteArray protocolVersionsSignature();
|
||||
|
||||
/***
|
||||
* @brief Returns a unique signature for all the current protocols
|
||||
*
|
||||
* Same as protocolVersionsSignature(), in base64.
|
||||
*/
|
||||
QString protocolVersionsSignatureBase64();
|
||||
|
||||
/***
|
||||
* @brief Returns a unique signature for all the current protocols
|
||||
*
|
||||
* Same as protocolVersionsSignature(), in hex;
|
||||
*/
|
||||
QString protocolVersionsSignatureHex();
|
||||
|
||||
/***
|
||||
* @brief Returns the data used to compute the protocol version
|
||||
*
|
||||
* The key is the packet type. The value is the version for that packet type.
|
||||
*
|
||||
* Used for aiding in development.
|
||||
*/
|
||||
QMap<PacketType, uint8_t> protocolVersionsSignatureMap();
|
||||
|
||||
|
||||
#if (PR_BUILD || DEV_BUILD)
|
||||
void sendWrongProtocolVersionsSignature(bool sendWrongVersion); /// for debugging version negotiation
|
||||
#endif
|
||||
|
@ -428,4 +487,5 @@ enum class AvatarQueryVersion : PacketVersion {
|
|||
ConicalFrustums = 22
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_PacketHeaders_h
|
||||
|
|
|
@ -655,7 +655,7 @@ uint8_t EntityMotionState::getSimulationPriority() const {
|
|||
return _entity->getSimulationPriority();
|
||||
}
|
||||
|
||||
void EntityMotionState::slaveBidPriority() {
|
||||
void EntityMotionState::workerBidPriority() {
|
||||
_bumpedPriority = glm::max(_bumpedPriority, _entity->getSimulationPriority());
|
||||
}
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ protected:
|
|||
void updateServerPhysicsVariables();
|
||||
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
||||
|
||||
void slaveBidPriority(); // computeNewBidPriority() with value stored in _entity
|
||||
void workerBidPriority(); // computeNewBidPriority() with value stored in _entity
|
||||
|
||||
void clearObjectVelocities() const;
|
||||
|
||||
|
|
|
@ -602,7 +602,7 @@ void PhysicalEntitySimulation::sendOwnershipBids(uint32_t numSubsteps) {
|
|||
// in the EntityMotionState::_serverFoo variables (please see comments in EntityMotionState.h)
|
||||
// therefore we need to immediately send an update so that the values stored are what we're
|
||||
// "telling" the server rather than what we've been "hearing" from the server.
|
||||
_bids[i]->slaveBidPriority();
|
||||
_bids[i]->workerBidPriority();
|
||||
_bids[i]->sendUpdate(_entityPacketSender, numSubsteps);
|
||||
|
||||
addOwnership(_bids[i]);
|
||||
|
|
|
@ -21,15 +21,15 @@ bool enumeratePlatform();
|
|||
|
||||
int getNumCPUs();
|
||||
json getCPU(int index);
|
||||
int getMasterCPU();
|
||||
int getPrimaryCPU();
|
||||
|
||||
int getNumGPUs();
|
||||
json getGPU(int index);
|
||||
int getMasterGPU();
|
||||
int getPrimaryGPU();
|
||||
|
||||
int getNumDisplays();
|
||||
json getDisplay(int index);
|
||||
int getMasterDisplay();
|
||||
int getPrimaryDisplay();
|
||||
|
||||
json getMemory();
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace platform { namespace keys{
|
|||
extern const char* model;
|
||||
extern const char* clockSpeed;
|
||||
extern const char* numCores;
|
||||
extern const char* isMaster;
|
||||
extern const char* isPrimary;
|
||||
}
|
||||
namespace gpu {
|
||||
extern const char* vendor;
|
||||
|
@ -32,7 +32,7 @@ namespace platform { namespace keys{
|
|||
extern const char* videoMemory;
|
||||
extern const char* driver;
|
||||
extern const char* displays;
|
||||
extern const char* isMaster;
|
||||
extern const char* isPrimary;
|
||||
}
|
||||
namespace graphicsAPI {
|
||||
extern const char* name;
|
||||
|
@ -90,7 +90,7 @@ namespace platform { namespace keys{
|
|||
extern const char* modeRefreshrate;
|
||||
extern const char* modeWidth;
|
||||
extern const char* modeHeight;
|
||||
extern const char* isMaster;
|
||||
extern const char* isPrimary;
|
||||
}
|
||||
namespace memory {
|
||||
extern const char* memTotal;
|
||||
|
|
|
@ -32,9 +32,9 @@ Profiler::Tier Profiler::profilePlatform() {
|
|||
return platformTier;
|
||||
}
|
||||
|
||||
// Not filtered yet, let s try to make sense of the master cpu and master gpu info
|
||||
auto cpuInfo = platform::getCPU(platform::getMasterCPU());
|
||||
auto gpuInfo = platform::getGPU(platform::getMasterGPU());
|
||||
// Not filtered yet, let's try to make sense of the primary cpu and primary gpu info
|
||||
auto cpuInfo = platform::getCPU(platform::getPrimaryCPU());
|
||||
auto gpuInfo = platform::getGPU(platform::getPrimaryGPU());
|
||||
if (filterOnProcessors(computerInfo, cpuInfo, gpuInfo, platformTier)) {
|
||||
return platformTier;
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ bool filterOnComputerMACOS(const platform::json& computer, Profiler::Tier& tier)
|
|||
// The simple rule for mac is
|
||||
// if it s an intel gpu then LOW
|
||||
// else go mid
|
||||
auto gpu = platform::getGPU(platform::getMasterGPU());
|
||||
auto gpu = platform::getGPU(platform::getPrimaryGPU());
|
||||
if (gpu.count(keys::gpu::vendor)) {
|
||||
std::string gpuVendor = gpu[keys::gpu::vendor].get<std::string>();
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ void MACOSInstance::enumerateGpusAndDisplays() {
|
|||
auto displaySizeHeightInches = displaySize.height * MM_TO_IN;
|
||||
|
||||
auto displayBounds = CGDisplayBounds(displayID);
|
||||
auto displayMaster =CGDisplayIsMain(displayID);
|
||||
auto displayPrimary =CGDisplayIsMain(displayID);
|
||||
|
||||
auto displayUnit =CGDisplayUnitNumber(displayID);
|
||||
auto displayModel =CGDisplayModelNumber(displayID);
|
||||
|
@ -112,8 +112,8 @@ void MACOSInstance::enumerateGpusAndDisplays() {
|
|||
// refreshrate
|
||||
display[keys::display::modeRefreshrate] = displayRefreshrate;
|
||||
|
||||
// Master display ?
|
||||
display[keys::display::isMaster] = (displayMaster ? true : false);
|
||||
// Primary display ?
|
||||
display[keys::display::isPrimary] = (displayPrimary ? true : false);
|
||||
|
||||
// Macos specific
|
||||
display["macos_unit"] = displayUnit;
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace platform { namespace keys {
|
|||
* @property {string} vendor - The CPU vendor (e.g., <code>"Intel"</code> or <code>"AMD"</code>).
|
||||
* @property {string} model - The CPU model.
|
||||
* @property {number} numCores - The number of logical cores.
|
||||
* @property {boolean} isMaster - <code>true</code> if the CPU is the "master" or primary CPU, <code>false</code> or
|
||||
* @property {boolean} isPrimary - <code>true</code> if the CPU is the primary CPU, <code>false</code> or
|
||||
* <code>undefined</code> if it isn't.
|
||||
*/
|
||||
namespace cpu {
|
||||
|
@ -41,7 +41,7 @@ namespace platform { namespace keys {
|
|||
const char* model = "model";
|
||||
const char* clockSpeed = "clockSpeed"; // FIXME: Not used.
|
||||
const char* numCores = "numCores";
|
||||
const char* isMaster = "isMaster";
|
||||
const char* isPrimary = "isPrimary";
|
||||
}
|
||||
|
||||
/*@jsdoc
|
||||
|
@ -53,7 +53,7 @@ namespace platform { namespace keys {
|
|||
* @property {number} videoMemory - The size of the GPU's video memory, in MB.
|
||||
* @property {number[]} displays - The index numbers of the displays currently being driven by the GPU. An empty array if
|
||||
* the GPU is currently not driving any displays.
|
||||
* @property {boolean} isMaster - <code>true</code> if the GPU is the "master" or primary GPU, <code>false</code> or
|
||||
* @property {boolean} isPrimary - <code>true</code> if the GPU is the primary GPU, <code>false</code> or
|
||||
* <code>undefined</code> if it isn't.
|
||||
*/
|
||||
namespace gpu {
|
||||
|
@ -66,7 +66,7 @@ namespace platform { namespace keys {
|
|||
const char* videoMemory = "videoMemory";
|
||||
const char* driver = "driver";
|
||||
const char* displays = "displays";
|
||||
const char* isMaster = "isMaster";
|
||||
const char* isPrimary = "isPrimary";
|
||||
}
|
||||
|
||||
/*@jsdoc
|
||||
|
@ -175,7 +175,7 @@ namespace platform { namespace keys {
|
|||
* @property {number} modeRefreshrate - The refresh rate of the current display mode, in Hz.
|
||||
* @property {number} modeWidth - The width of the current display mode, in pixels.
|
||||
* @property {number} modeHeight - The height of the current display mode, in pixels.
|
||||
* @property {boolean} isMaster - <code>true</code> if the GPU is the "master" or primary display, <code>false</code> or
|
||||
* @property {boolean} isPrimary - <code>true</code> if the GPU is the primary display, <code>false</code> or
|
||||
* <code>undefined</code> if it isn't.
|
||||
*/
|
||||
namespace display {
|
||||
|
@ -193,7 +193,7 @@ namespace platform { namespace keys {
|
|||
const char* modeRefreshrate = "modeRefreshrate";
|
||||
const char* modeWidth = "modeWidth";
|
||||
const char* modeHeight = "modeHeight";
|
||||
const char* isMaster = "isMaster";
|
||||
const char* isPrimary = "isPrimary";
|
||||
}
|
||||
|
||||
/*@jsdoc
|
||||
|
@ -300,8 +300,8 @@ json platform::getCPU(int index) {
|
|||
return _instance->getCPU(index);
|
||||
}
|
||||
|
||||
int platform::getMasterCPU() {
|
||||
return _instance->getMasterCPU();
|
||||
int platform::getPrimaryCPU() {
|
||||
return _instance->getPrimaryCPU();
|
||||
}
|
||||
|
||||
int platform::getNumGPUs() {
|
||||
|
@ -312,8 +312,8 @@ json platform::getGPU(int index) {
|
|||
return _instance->getGPU(index);
|
||||
}
|
||||
|
||||
int platform::getMasterGPU() {
|
||||
return _instance->getMasterGPU();
|
||||
int platform::getPrimaryGPU() {
|
||||
return _instance->getPrimaryGPU();
|
||||
}
|
||||
|
||||
int platform::getNumDisplays() {
|
||||
|
@ -324,8 +324,8 @@ json platform::getDisplay(int index) {
|
|||
return _instance->getDisplay(index);
|
||||
}
|
||||
|
||||
int platform::getMasterDisplay() {
|
||||
return _instance->getMasterDisplay();
|
||||
int platform::getPrimaryDisplay() {
|
||||
return _instance->getPrimaryDisplay();
|
||||
}
|
||||
|
||||
json platform::getMemory() {
|
||||
|
|
|
@ -41,8 +41,8 @@ bool Instance::enumeratePlatform() {
|
|||
enumerateNics();
|
||||
enumerateGraphicsApis();
|
||||
|
||||
// eval the master index for each platform scopes
|
||||
updateMasterIndices();
|
||||
// eval the primary index for each platform scopes
|
||||
updatePrimaryIndices();
|
||||
|
||||
// And profile the platform and put the tier in "computer"
|
||||
_computer[keys::computer::profileTier] = Profiler::TierNames[Profiler::profilePlatform()];
|
||||
|
@ -50,54 +50,54 @@ bool Instance::enumeratePlatform() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void Instance::updateMasterIndices() {
|
||||
void Instance::updatePrimaryIndices() {
|
||||
// We assume a single CPU at the moment:
|
||||
{
|
||||
if (!_cpus.empty()) {
|
||||
_masterCPU = 0;
|
||||
_cpus[0][keys::cpu::isMaster] = true;
|
||||
_primaryCPU = 0;
|
||||
_cpus[0][keys::cpu::isPrimary] = true;
|
||||
} else {
|
||||
_masterCPU = NOT_FOUND;
|
||||
_primaryCPU = NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
// Go through the displays list
|
||||
{
|
||||
_masterDisplay = NOT_FOUND;
|
||||
_primaryDisplay = NOT_FOUND;
|
||||
for (int i = 0; i < (int) _displays.size(); ++i) {
|
||||
const auto& display = _displays[i];
|
||||
if (display.count(keys::display::isMaster)) {
|
||||
if (display[keys::display::isMaster].get<bool>()) {
|
||||
_masterDisplay = i;
|
||||
if (display.count(keys::display::isPrimary)) {
|
||||
if (display[keys::display::isPrimary].get<bool>()) {
|
||||
_primaryDisplay = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// NO master display found, return the first one or NOT_FOUND if no display
|
||||
if (_masterDisplay == NOT_FOUND) {
|
||||
// NO primary display found, return the first one or NOT_FOUND if no display
|
||||
if (_primaryDisplay == NOT_FOUND) {
|
||||
if (!_displays.empty()) {
|
||||
_masterDisplay = 0;
|
||||
_displays[0][keys::display::isMaster] = true;
|
||||
_primaryDisplay = 0;
|
||||
_displays[0][keys::display::isPrimary] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// From the master display decide the master gpu
|
||||
// From the primary display decide the primary gpu
|
||||
{
|
||||
_masterGPU = NOT_FOUND;
|
||||
if (_masterDisplay != NOT_FOUND) {
|
||||
const auto& display = _displays[_masterDisplay];
|
||||
// FInd the GPU index of the master display
|
||||
_primaryGPU = NOT_FOUND;
|
||||
if (_primaryDisplay != NOT_FOUND) {
|
||||
const auto& display = _displays[_primaryDisplay];
|
||||
// FInd the GPU index of the primary display
|
||||
if (display.count(keys::display::gpu)) {
|
||||
_masterGPU = display[keys::display::gpu];
|
||||
_gpus[_masterGPU][keys::gpu::isMaster] = true;
|
||||
_primaryGPU = display[keys::display::gpu];
|
||||
_gpus[_primaryGPU][keys::gpu::isPrimary] = true;
|
||||
}
|
||||
}
|
||||
// NO master GPU found from master display, bummer, return the first one or NOT_FOUND if no display
|
||||
if (_masterGPU == NOT_FOUND) {
|
||||
// NO primary GPU found from primary display, bummer, return the first one or NOT_FOUND if no display
|
||||
if (_primaryGPU == NOT_FOUND) {
|
||||
if (!_gpus.empty()) {
|
||||
_masterGPU = 0;
|
||||
_gpus[0][keys::gpu::isMaster] = true;
|
||||
_primaryGPU = 0;
|
||||
_gpus[0][keys::gpu::isPrimary] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,15 +23,15 @@ public:
|
|||
|
||||
int getNumCPUs() { return (int)_cpus.size(); }
|
||||
json getCPU(int index);
|
||||
int getMasterCPU() const { return _masterCPU; }
|
||||
int getPrimaryCPU() const { return _primaryCPU; }
|
||||
|
||||
int getNumGPUs() { return (int)_gpus.size(); }
|
||||
json getGPU(int index);
|
||||
int getMasterGPU() const { return _masterGPU; }
|
||||
int getPrimaryGPU() const { return _primaryGPU; }
|
||||
|
||||
int getNumDisplays() { return (int)_displays.size(); }
|
||||
json getDisplay(int index);
|
||||
int getMasterDisplay() const { return _masterDisplay; }
|
||||
int getPrimaryDisplay() const { return _primaryDisplay; }
|
||||
|
||||
json getMemory() { return _memory; }
|
||||
|
||||
|
@ -62,12 +62,12 @@ protected:
|
|||
json _memory;
|
||||
json _computer;
|
||||
|
||||
int _masterCPU{ -1 };
|
||||
int _masterGPU{ -1 };
|
||||
int _masterDisplay{ -1 };
|
||||
int _primaryCPU{ -1 };
|
||||
int _primaryGPU{ -1 };
|
||||
int _primaryDisplay{ -1 };
|
||||
|
||||
// Traverse the cpus, gpus and displays to update the "master" index in each domain
|
||||
void updateMasterIndices();
|
||||
// Traverse the cpus, gpus and displays to update the "primary" index in each domain
|
||||
void updatePrimaryIndices();
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -167,8 +167,8 @@ void WINInstance::enumerateGpusAndDisplays() {
|
|||
// refreshrate
|
||||
display[keys::display::modeRefreshrate] = devMode.dmDisplayFrequency;;
|
||||
|
||||
// Master display ?
|
||||
display[keys::display::isMaster] = (bool) (monitorInfo.dwFlags & MONITORINFOF_PRIMARY);
|
||||
// Primary display ?
|
||||
display[keys::display::isPrimary] = (bool) (monitorInfo.dwFlags & MONITORINFOF_PRIMARY);
|
||||
|
||||
// Add the display index to the list of displays of the gpu
|
||||
displayIndices.push_back((int) _displays.size());
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
Q_LOGGING_CATEGORY(proceduralLog, "hifi.gpu.procedural")
|
||||
|
||||
bool Procedural::enableProceduralShaders = false;
|
||||
|
||||
// User-data parsing constants
|
||||
static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity";
|
||||
static const QString VERTEX_URL_KEY = "vertexShaderURL";
|
||||
|
@ -377,6 +379,27 @@ void Procedural::prepare(gpu::Batch& batch,
|
|||
|
||||
_proceduralPipelines[key] = gpu::Pipeline::create(program, key.isTransparent() ? _transparentState : _opaqueState);
|
||||
|
||||
// Error fallback: pink checkerboard
|
||||
if (_errorFallbackFragmentSource.isEmpty()) {
|
||||
QFile file(_errorFallbackFragmentPath);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
_errorFallbackFragmentSource = QTextStream(&file).readAll();
|
||||
}
|
||||
vertexSource.replacements.erase(PROCEDURAL_BLOCK);
|
||||
fragmentSource.replacements[PROCEDURAL_BLOCK] = _errorFallbackFragmentSource.toStdString();
|
||||
gpu::ShaderPointer errorVertexShader = gpu::Shader::createVertex(vertexSource);
|
||||
gpu::ShaderPointer errorFragmentShader = gpu::Shader::createPixel(fragmentSource);
|
||||
gpu::ShaderPointer errorProgram = gpu::Shader::createProgram(errorVertexShader, errorFragmentShader);
|
||||
_errorPipelines[key] = gpu::Pipeline::create(errorProgram, _opaqueState);
|
||||
|
||||
// Disabled fallback: nothing
|
||||
vertexSource.replacements.erase(PROCEDURAL_BLOCK);
|
||||
fragmentSource.replacements.erase(PROCEDURAL_BLOCK);
|
||||
gpu::ShaderPointer disabledVertexShader = gpu::Shader::createVertex(vertexSource);
|
||||
gpu::ShaderPointer disabledFragmentShader = gpu::Shader::createPixel(fragmentSource);
|
||||
gpu::ShaderPointer disabledProgram = gpu::Shader::createProgram(disabledVertexShader, disabledFragmentShader);
|
||||
_disabledPipelines[key] = gpu::Pipeline::create(disabledProgram, _opaqueState);
|
||||
|
||||
_lastCompile = usecTimestampNow();
|
||||
if (_firstCompile == 0) {
|
||||
_firstCompile = _lastCompile;
|
||||
|
@ -385,8 +408,15 @@ void Procedural::prepare(gpu::Batch& batch,
|
|||
recompiledShader = true;
|
||||
}
|
||||
|
||||
gpu::PipelinePointer finalPipeline = recompiledShader ? _proceduralPipelines[key] : pipeline->second;
|
||||
if (!enableProceduralShaders) {
|
||||
finalPipeline = _disabledPipelines[key];
|
||||
} else if (!finalPipeline || finalPipeline->getProgram()->compilationHasFailed()) {
|
||||
finalPipeline = _errorPipelines[key];
|
||||
}
|
||||
|
||||
// FIXME: need to handle forward rendering
|
||||
batch.setPipeline(recompiledShader ? _proceduralPipelines[key] : pipeline->second);
|
||||
batch.setPipeline(finalPipeline);
|
||||
|
||||
bool recreateUniforms = _shaderDirty || _uniformsDirty || recompiledShader || _prevKey != key;
|
||||
if (recreateUniforms) {
|
||||
|
@ -533,4 +563,6 @@ void graphics::ProceduralMaterial::initializeProcedural() {
|
|||
// FIXME: Setup proper uniform slots and use correct pipelines for forward rendering
|
||||
_procedural._opaqueFragmentSource = gpu::Shader::getFragmentShaderSource(shader::render_utils::fragment::simple_procedural);
|
||||
_procedural._transparentFragmentSource = gpu::Shader::getFragmentShaderSource(shader::render_utils::fragment::simple_procedural_translucent);
|
||||
|
||||
_procedural._errorFallbackFragmentPath = ":" + QUrl("qrc:///shaders/errorShader.frag").path();
|
||||
}
|
|
@ -124,12 +124,16 @@ public:
|
|||
gpu::Shader::Source _opaqueFragmentSource;
|
||||
gpu::Shader::Source _transparentFragmentSource;
|
||||
|
||||
QString _errorFallbackFragmentPath;
|
||||
|
||||
gpu::StatePointer _opaqueState { std::make_shared<gpu::State>() };
|
||||
gpu::StatePointer _transparentState { std::make_shared<gpu::State>() };
|
||||
|
||||
static std::function<void(gpu::StatePointer)> opaqueStencil;
|
||||
static std::function<void(gpu::StatePointer)> transparentStencil;
|
||||
|
||||
static bool enableProceduralShaders;
|
||||
|
||||
protected:
|
||||
// DO NOT TOUCH
|
||||
// We have to pack these in a particular way to match the ProceduralCommon.slh
|
||||
|
@ -176,11 +180,15 @@ protected:
|
|||
bool _shaderDirty { true };
|
||||
bool _uniformsDirty { true };
|
||||
|
||||
QString _errorFallbackFragmentSource;
|
||||
|
||||
// Rendering objects
|
||||
UniformLambdas _uniforms;
|
||||
NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
|
||||
|
||||
std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _proceduralPipelines;
|
||||
std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _errorPipelines;
|
||||
std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _disabledPipelines;
|
||||
|
||||
StandardInputs _standardInputs;
|
||||
gpu::BufferPointer _standardInputsBuffer;
|
||||
|
|
|
@ -22,6 +22,8 @@ ProceduralSkybox::ProceduralSkybox(uint64_t created) : graphics::Skybox(), _crea
|
|||
_procedural._vertexSource = shader::Source::get(shader::graphics::vertex::skybox);
|
||||
_procedural._opaqueFragmentSource = shader::Source::get(shader::procedural::fragment::proceduralSkybox);
|
||||
|
||||
_procedural._errorFallbackFragmentPath = ":" + QUrl("qrc:///shaders/errorSkyboxShader.frag").path();
|
||||
|
||||
_procedural.setDoesFade(false);
|
||||
|
||||
// Adjust the pipeline state for background using the stencil test
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
|
||||
using namespace render;
|
||||
|
||||
bool ModelMeshPartPayload::enableMaterialProceduralShaders = false;
|
||||
|
||||
ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex,
|
||||
const Transform& transform, const uint64_t& created) :
|
||||
_meshIndex(meshIndex),
|
||||
|
@ -345,9 +343,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
|
|||
}
|
||||
|
||||
if (_shapeKey.hasOwnPipeline()) {
|
||||
if (!(enableMaterialProceduralShaders)) {
|
||||
return;
|
||||
}
|
||||
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(_drawMaterials.top().material);
|
||||
auto& schema = _drawMaterials.getSchemaBuffer().get<graphics::MultiMaterial::Schema>();
|
||||
glm::vec4 outColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity);
|
||||
|
|
|
@ -68,8 +68,6 @@ public:
|
|||
|
||||
void setBlendshapeBuffer(const std::unordered_map<int, gpu::BufferPointer>& blendshapeBuffers, const QVector<int>& blendedMeshSizes);
|
||||
|
||||
static bool enableMaterialProceduralShaders;
|
||||
|
||||
private:
|
||||
void initCache(const ModelPointer& model, int shapeID);
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ void Transaction::clear() {
|
|||
|
||||
|
||||
Scene::Scene(glm::vec3 origin, float size) :
|
||||
_masterSpatialTree(origin, size)
|
||||
_primarySpatialTree(origin, size)
|
||||
{
|
||||
_items.push_back(Item()); // add the itemID #0 to nothing
|
||||
}
|
||||
|
@ -306,10 +306,10 @@ void Scene::resetItems(const Transaction::Resets& transactions) {
|
|||
// Update the item's container
|
||||
assert((oldKey.isSpatial() == newKey.isSpatial()) || oldKey._flags.none());
|
||||
if (newKey.isSpatial()) {
|
||||
auto newCell = _masterSpatialTree.resetItem(oldCell, oldKey, item.getBound(nullptr), itemId, newKey);
|
||||
auto newCell = _primarySpatialTree.resetItem(oldCell, oldKey, item.getBound(nullptr), itemId, newKey);
|
||||
item.resetCell(newCell, newKey.isSmall());
|
||||
} else {
|
||||
_masterNonspatialSet.insert(itemId);
|
||||
_primaryNonspatialSet.insert(itemId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -323,9 +323,9 @@ void Scene::removeItems(const Transaction::Removes& transactions) {
|
|||
|
||||
// Remove the item
|
||||
if (oldKey.isSpatial()) {
|
||||
_masterSpatialTree.removeItem(oldCell, oldKey, removedID);
|
||||
_primarySpatialTree.removeItem(oldCell, oldKey, removedID);
|
||||
} else {
|
||||
_masterNonspatialSet.erase(removedID);
|
||||
_primaryNonspatialSet.erase(removedID);
|
||||
}
|
||||
|
||||
// Remove the transition to prevent updating it for nothing
|
||||
|
@ -362,20 +362,20 @@ void Scene::updateItems(const Transaction::Updates& transactions) {
|
|||
// Update the item's container
|
||||
if (oldKey.isSpatial() == newKey.isSpatial()) {
|
||||
if (newKey.isSpatial()) {
|
||||
auto newCell = _masterSpatialTree.resetItem(oldCell, oldKey, item.getBound(nullptr), updateID, newKey);
|
||||
auto newCell = _primarySpatialTree.resetItem(oldCell, oldKey, item.getBound(nullptr), updateID, newKey);
|
||||
item.resetCell(newCell, newKey.isSmall());
|
||||
}
|
||||
} else {
|
||||
if (newKey.isSpatial()) {
|
||||
_masterNonspatialSet.erase(updateID);
|
||||
_primaryNonspatialSet.erase(updateID);
|
||||
|
||||
auto newCell = _masterSpatialTree.resetItem(oldCell, oldKey, item.getBound(nullptr), updateID, newKey);
|
||||
auto newCell = _primarySpatialTree.resetItem(oldCell, oldKey, item.getBound(nullptr), updateID, newKey);
|
||||
item.resetCell(newCell, newKey.isSmall());
|
||||
} else {
|
||||
_masterSpatialTree.removeItem(oldCell, oldKey, updateID);
|
||||
_primarySpatialTree.removeItem(oldCell, oldKey, updateID);
|
||||
item.resetCell();
|
||||
|
||||
_masterNonspatialSet.insert(updateID);
|
||||
_primaryNonspatialSet.insert(updateID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -185,10 +185,10 @@ public:
|
|||
const Item getItemSafe(const ItemID& id) const { if (isAllocatedID(id)) { return _items[id]; } else { return Item(); } }
|
||||
|
||||
// Access the spatialized items
|
||||
const ItemSpatialTree& getSpatialTree() const { return _masterSpatialTree; }
|
||||
const ItemSpatialTree& getSpatialTree() const { return _primarySpatialTree; }
|
||||
|
||||
// Access non-spatialized items (layered objects, backgrounds)
|
||||
const ItemIDSet& getNonspatialSet() const { return _masterNonspatialSet; }
|
||||
const ItemIDSet& getNonspatialSet() const { return _primaryNonspatialSet; }
|
||||
|
||||
// Access a particular Stage (empty if doesn't exist)
|
||||
// Thread safe
|
||||
|
@ -226,8 +226,8 @@ protected:
|
|||
// database of items is protected for editing by a mutex
|
||||
std::mutex _itemsMutex;
|
||||
Item::Vector _items;
|
||||
ItemSpatialTree _masterSpatialTree;
|
||||
ItemIDSet _masterNonspatialSet;
|
||||
ItemSpatialTree _primarySpatialTree;
|
||||
ItemIDSet _primaryNonspatialSet;
|
||||
|
||||
void resetItems(const Transaction::Resets& transactions);
|
||||
void resetTransitionFinishedOperator(const Transaction::TransitionFinishedOperators& transactions);
|
||||
|
|
|
@ -19,21 +19,21 @@ void ScriptGatekeeper::initialize() {
|
|||
return;
|
||||
}
|
||||
|
||||
QVariant rawCurrentWhitelistValues = Setting::Handle<QVariant>(SCRIPT_WHITELIST_ENTRIES_KEY).get();
|
||||
QString settingsSafeValues = rawCurrentWhitelistValues.toString();
|
||||
QVariant rawCurrentAllowlistValues = Setting::Handle<QVariant>(SCRIPT_ALLOWLIST_ENTRIES_KEY).get();
|
||||
QString settingsSafeValues = rawCurrentAllowlistValues.toString();
|
||||
|
||||
Setting::Handle<bool> whitelistEnabled { SCRIPT_WHITELIST_ENABLED_KEY, false };
|
||||
Setting::Handle<bool> allowlistEnabled { SCRIPT_ALLOWLIST_ENABLED_KEY, false };
|
||||
Setting::Handle<bool> isFirstRun { Settings::firstRun, true };
|
||||
|
||||
QString preloadedVal = BuildInfo::PRELOADED_SCRIPT_WHITELIST;
|
||||
QString preloadedVal = BuildInfo::PRELOADED_SCRIPT_ALLOWLIST;
|
||||
|
||||
if (settingsSafeValues.isEmpty() && !preloadedVal.isEmpty() && isFirstRun.get()) {
|
||||
// We assume that the whitelist should be enabled if a preloaded whitelist is attached, so we activate it if it's not already active.
|
||||
if (!whitelistEnabled.get()) {
|
||||
whitelistEnabled.set(true);
|
||||
// We assume that the allowlist should be enabled if a preloaded allowlist is attached, so we activate it if it's not already active.
|
||||
if (!allowlistEnabled.get()) {
|
||||
allowlistEnabled.set(true);
|
||||
}
|
||||
|
||||
Setting::Handle<QVariant>(SCRIPT_WHITELIST_ENTRIES_KEY).set(preloadedVal);
|
||||
Setting::Handle<QVariant>(SCRIPT_ALLOWLIST_ENTRIES_KEY).set(preloadedVal);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
|
|
|
@ -17,14 +17,14 @@
|
|||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
/// Manages script whitelisting in a domain
|
||||
/// Manages script allowlisting in a domain
|
||||
class ScriptGatekeeper : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
void initialize();
|
||||
|
||||
QString SCRIPT_WHITELIST_ENABLED_KEY{ "private/whitelistEnabled" };
|
||||
QString SCRIPT_WHITELIST_ENTRIES_KEY{ "private/settingsSafeURLS" };
|
||||
QString SCRIPT_ALLOWLIST_ENABLED_KEY{ "private/allowlistEnabled" };
|
||||
QString SCRIPT_ALLOWLIST_ENTRIES_KEY{ "private/settingsSafeURLS" };
|
||||
|
||||
private:
|
||||
bool _initialized { false };
|
||||
|
|
|
@ -2192,17 +2192,17 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c
|
|||
exception = testConstructor;
|
||||
}
|
||||
} else {
|
||||
// ENTITY SCRIPT WHITELIST STARTS HERE
|
||||
// ENTITY SCRIPT ALLOWLIST STARTS HERE
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
bool passList = false; // assume unsafe
|
||||
QString whitelistPrefix = "[WHITELIST ENTITY SCRIPTS]";
|
||||
QString allowlistPrefix = "[ALLOWLIST ENTITY SCRIPTS]";
|
||||
QList<QString> safeURLPrefixes = { "file:///", "atp:", "cache:" };
|
||||
safeURLPrefixes += qEnvironmentVariable("EXTRA_WHITELIST").trimmed().split(QRegExp("\\s*,\\s*"), Qt::SkipEmptyParts);
|
||||
safeURLPrefixes += qEnvironmentVariable("EXTRA_ALLOWLIST").trimmed().split(QRegExp("\\s*,\\s*"), Qt::SkipEmptyParts);
|
||||
|
||||
// Entity Script Whitelist toggle check.
|
||||
Setting::Handle<bool> whitelistEnabled {"private/whitelistEnabled", false };
|
||||
// Entity Script Allowlist toggle check.
|
||||
Setting::Handle<bool> allowlistEnabled {"private/allowlistEnabled", false };
|
||||
|
||||
if (!whitelistEnabled.get()) {
|
||||
if (!allowlistEnabled.get()) {
|
||||
passList = true;
|
||||
}
|
||||
|
||||
|
@ -2212,39 +2212,39 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c
|
|||
safeURLPrefixes += settingsSafeURLS;
|
||||
// END Pull SAFEURLS from the Interface.JSON settings.
|
||||
|
||||
// Get current domain whitelist bypass, in case an entire domain is whitelisted.
|
||||
// Get current domain allowlist bypass, in case an entire domain is allowlisted.
|
||||
QString currentDomain = DependencyManager::get<AddressManager>()->getDomainURL().host();
|
||||
|
||||
QString domainSafeIP = nodeList->getDomainHandler().getHostname();
|
||||
QString domainSafeURL = URL_SCHEME_OVERTE + "://" + currentDomain;
|
||||
for (const auto& str : safeURLPrefixes) {
|
||||
if (domainSafeURL.startsWith(str) || domainSafeIP.startsWith(str)) {
|
||||
qCDebug(scriptengine) << whitelistPrefix << "Whitelist Bypassed, entire domain is whitelisted. Current Domain Host: "
|
||||
qCDebug(scriptengine) << allowlistPrefix << "Allowlist Bypassed, entire domain is allowlisted. Current Domain Host: "
|
||||
<< nodeList->getDomainHandler().getHostname()
|
||||
<< "Current Domain: " << currentDomain;
|
||||
passList = true;
|
||||
}
|
||||
}
|
||||
// END bypass whitelist based on current domain.
|
||||
// END bypass allowlist based on current domain.
|
||||
|
||||
// Start processing scripts through the whitelist.
|
||||
if (ScriptManager::getContext() == "entity_server") { // If running on the server, do not engage whitelist.
|
||||
// Start processing scripts through the allowlist.
|
||||
if (ScriptManager::getContext() == "entity_server") { // If running on the server, do not engage allowlist.
|
||||
passList = true;
|
||||
} else if (!passList) { // If waved through, do not engage whitelist.
|
||||
} else if (!passList) { // If waved through, do not engage allowlist.
|
||||
for (const auto& str : safeURLPrefixes) {
|
||||
qCDebug(scriptengine) << whitelistPrefix << "Script URL: " << scriptOrURL << "TESTING AGAINST" << str << "RESULTS IN"
|
||||
qCDebug(scriptengine) << allowlistPrefix << "Script URL: " << scriptOrURL << "TESTING AGAINST" << str << "RESULTS IN"
|
||||
<< scriptOrURL.startsWith(str);
|
||||
if (!str.isEmpty() && scriptOrURL.startsWith(str)) {
|
||||
passList = true;
|
||||
qCDebug(scriptengine) << whitelistPrefix << "Script approved.";
|
||||
qCDebug(scriptengine) << allowlistPrefix << "Script approved.";
|
||||
break; // Bail early since we found a match.
|
||||
}
|
||||
}
|
||||
}
|
||||
// END processing of scripts through the whitelist.
|
||||
// END processing of scripts through the allowlist.
|
||||
|
||||
if (!passList) { // If the entity failed to pass for any reason, it's blocked and an error is thrown.
|
||||
qCDebug(scriptengine) << whitelistPrefix << "(disabled entity script)" << entityID.toString() << scriptOrURL;
|
||||
qCDebug(scriptengine) << allowlistPrefix << "(disabled entity script)" << entityID.toString() << scriptOrURL;
|
||||
exception = _engine->makeError(_engine->newValue("UNSAFE_ENTITY_SCRIPTS == 0"));
|
||||
} else {
|
||||
QTimer timeout;
|
||||
|
@ -2267,7 +2267,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c
|
|||
exception = testConstructor;
|
||||
}
|
||||
}
|
||||
// ENTITY SCRIPT WHITELIST ENDS HERE, uncomment below for original full disabling.
|
||||
// ENTITY SCRIPT ALLOWLIST ENDS HERE, uncomment below for original full disabling.
|
||||
|
||||
// qCDebug(scriptengine) << "(disabled entity script)" << entityID.toString() << scriptOrURL;
|
||||
// exception = makeError("UNSAFE_ENTITY_SCRIPTS == 0");
|
||||
|
@ -2303,17 +2303,17 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c
|
|||
return; // done processing script
|
||||
}*/
|
||||
|
||||
// ENTITY SCRIPT WHITELIST STARTS HERE
|
||||
// ENTITY SCRIPT ALLOWLIST STARTS HERE
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
bool passList = false; // assume unsafe
|
||||
QString whitelistPrefix = "[WHITELIST ENTITY SCRIPTS]";
|
||||
QString allowlistPrefix = "[ALLOWLIST ENTITY SCRIPTS]";
|
||||
QList<QString> safeURLPrefixes = { "file:///", "atp:", "cache:" };
|
||||
safeURLPrefixes += qEnvironmentVariable("EXTRA_WHITELIST").trimmed().split(QRegExp("\\s*,\\s*"), Qt::SkipEmptyParts);
|
||||
safeURLPrefixes += qEnvironmentVariable("EXTRA_ALLOWLIST").trimmed().split(QRegExp("\\s*,\\s*"), Qt::SkipEmptyParts);
|
||||
|
||||
// Entity Script Whitelist toggle check.
|
||||
Setting::Handle<bool> whitelistEnabled {"private/whitelistEnabled", false };
|
||||
// Entity Script Allowlist toggle check.
|
||||
Setting::Handle<bool> allowlistEnabled {"private/allowlistEnabled", false };
|
||||
|
||||
if (!whitelistEnabled.get()) {
|
||||
if (!allowlistEnabled.get()) {
|
||||
passList = true;
|
||||
}
|
||||
|
||||
|
@ -2323,40 +2323,40 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c
|
|||
safeURLPrefixes += settingsSafeURLS;
|
||||
// END Pull SAFEURLS from the Interface.JSON settings.
|
||||
|
||||
// Get current domain whitelist bypass, in case an entire domain is whitelisted.
|
||||
// Get current domain allowlist bypass, in case an entire domain is allowlisted.
|
||||
QString currentDomain = DependencyManager::get<AddressManager>()->getDomainURL().host();
|
||||
|
||||
QString domainSafeIP = nodeList->getDomainHandler().getHostname();
|
||||
QString domainSafeURL = URL_SCHEME_OVERTE + "://" + currentDomain;
|
||||
for (const auto& str : safeURLPrefixes) {
|
||||
if (domainSafeURL.startsWith(str) || domainSafeIP.startsWith(str)) {
|
||||
qCDebug(scriptengine) << whitelistPrefix << "Whitelist Bypassed, entire domain is whitelisted. Current Domain Host: "
|
||||
qCDebug(scriptengine) << allowlistPrefix << "Allowlist Bypassed, entire domain is allowlisted. Current Domain Host: "
|
||||
<< nodeList->getDomainHandler().getHostname()
|
||||
<< "Current Domain: " << currentDomain;
|
||||
passList = true;
|
||||
}
|
||||
}
|
||||
// END bypass whitelist based on current domain.
|
||||
// END bypass allowlist based on current domain.
|
||||
|
||||
// Start processing scripts through the whitelist.
|
||||
if (ScriptManager::getContext() == "entity_server") { // If running on the server, do not engage whitelist.
|
||||
// Start processing scripts through the allowlist.
|
||||
if (ScriptManager::getContext() == "entity_server") { // If running on the server, do not engage allowlist.
|
||||
passList = true;
|
||||
} else if (!passList) { // If waved through, do not engage whitelist.
|
||||
} else if (!passList) { // If waved through, do not engage allowlist.
|
||||
for (const auto& str : safeURLPrefixes) {
|
||||
qCDebug(scriptengine) << whitelistPrefix << "Script URL: " << scriptOrURL << "TESTING AGAINST" << str << "RESULTS IN"
|
||||
qCDebug(scriptengine) << allowlistPrefix << "Script URL: " << scriptOrURL << "TESTING AGAINST" << str << "RESULTS IN"
|
||||
<< scriptOrURL.startsWith(str);
|
||||
if (!str.isEmpty() && scriptOrURL.startsWith(str)) {
|
||||
passList = true;
|
||||
qCDebug(scriptengine) << whitelistPrefix << "Script approved.";
|
||||
qCDebug(scriptengine) << allowlistPrefix << "Script approved.";
|
||||
break; // Bail early since we found a match.
|
||||
}
|
||||
}
|
||||
}
|
||||
// END processing of scripts through the whitelist.
|
||||
// END processing of scripts through the allowlist.
|
||||
|
||||
ScriptValue exception;
|
||||
if (!passList) { // If the entity failed to pass for any reason, it's blocked and an error is thrown.
|
||||
qCDebug(scriptengine) << whitelistPrefix << "(disabled entity script)" << entityID.toString() << scriptOrURL;
|
||||
qCDebug(scriptengine) << allowlistPrefix << "(disabled entity script)" << entityID.toString() << scriptOrURL;
|
||||
exception = _engine->makeError(_engine->newValue("UNSAFE_ENTITY_SCRIPTS == 0"));
|
||||
}
|
||||
|
||||
|
@ -2369,7 +2369,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c
|
|||
emit unhandledException(scriptRuntimeException);
|
||||
return;
|
||||
}
|
||||
// ENTITY SCRIPT WHITELIST ENDS HERE, uncomment below for original full disabling.
|
||||
// ENTITY SCRIPT ALLOWLIST ENDS HERE, uncomment below for original full disabling.
|
||||
|
||||
// qCDebug(scriptengine) << "(disabled entity script)" << entityID.toString() << scriptOrURL;
|
||||
// exception = makeError("UNSAFE_ENTITY_SCRIPTS == 0");
|
||||
|
|
|
@ -110,17 +110,17 @@ public slots:
|
|||
bool getPersonalMuteStatus(const QUuid& nodeID);
|
||||
|
||||
/*@jsdoc
|
||||
* Sets an avatar's gain (volume) for you and you only, or sets the master gain.
|
||||
* Sets an avatar's gain (volume) for you and you only, or sets the primary gain.
|
||||
* @function Users.setAvatarGain
|
||||
* @param {Uuid} nodeID - The session ID of the avatar to set the gain for, or <code>null</code> to set the master gain.
|
||||
* @param {Uuid} nodeID - The session ID of the avatar to set the gain for, or <code>null</code> to set the primary gain.
|
||||
* @param {number} gain - The gain to set, in dB.
|
||||
*/
|
||||
void setAvatarGain(const QUuid& nodeID, float gain);
|
||||
|
||||
/*@jsdoc
|
||||
* Gets an avatar's gain (volume) for you and you only, or gets the master gain.
|
||||
* Gets an avatar's gain (volume) for you and you only, or gets the primary gain.
|
||||
* @function Users.getAvatarGain
|
||||
* @param {Uuid} nodeID - The session ID of the avatar to get the gain for, or <code>null</code> to get the master gain.
|
||||
* @param {Uuid} nodeID - The session ID of the avatar to get the gain for, or <code>null</code> to get the primary gain.
|
||||
* @returns {number} The gain, in dB.
|
||||
*/
|
||||
float getAvatarGain(const QUuid& nodeID);
|
||||
|
|
|
@ -854,11 +854,11 @@ void printSystemInformation() {
|
|||
|
||||
qCDebug(shared) << "Environment Variables";
|
||||
// List of env variables to include in the log. For privacy reasons we don't send all env variables.
|
||||
const QStringList envWhitelist = {
|
||||
const QStringList envAllowlist = {
|
||||
"QTWEBENGINE_REMOTE_DEBUGGING"
|
||||
};
|
||||
auto envVariables = QProcessEnvironment::systemEnvironment();
|
||||
for (auto& env : envWhitelist)
|
||||
for (auto& env : envAllowlist)
|
||||
{
|
||||
qCDebug(shared).noquote().nospace() << "\t" <<
|
||||
(envVariables.contains(env) ? " = " + envVariables.value(env) : " NOT FOUND");
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
*/
|
||||
|
||||
|
||||
#ifdef OVERTE_WARNINGS_WHITELIST_GCC
|
||||
#ifdef OVERTE_WARNINGS_ALLOWLIST_GCC
|
||||
|
||||
#define OVERTE_IGNORE_DEPRECATED_BEGIN \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
|
@ -27,7 +27,7 @@
|
|||
|
||||
#define OVERTE_IGNORE_DEPRECATED_END _Pragma("GCC diagnostic pop")
|
||||
|
||||
#elif OVERTE_WARNINGS_WHITELIST_CLANG
|
||||
#elif OVERTE_WARNINGS_ALLOWLIST_CLANG
|
||||
|
||||
#define OVERTE_IGNORE_DEPRECATED_BEGIN \
|
||||
_Pragma("clang diagnostic push") \
|
||||
|
@ -35,7 +35,7 @@
|
|||
|
||||
#define OVERTE_IGNORE_DEPRECATED_END _Pragma("clang diagnostic pop")
|
||||
|
||||
#elif OVERTE_WARNINGS_WHITELIST_MSVC
|
||||
#elif OVERTE_WARNINGS_ALLOWLIST_MSVC
|
||||
|
||||
#define OVERTE_IGNORE_DEPRECATED_BEGIN \
|
||||
_Pragma("warning(push)") \
|
||||
|
|
|
@ -63,11 +63,11 @@
|
|||
|
||||
namespace hifi { namespace qml { namespace offscreen {
|
||||
|
||||
class OffscreenQmlWhitelist : public Dependency, private ReadWriteLockable {
|
||||
class OffscreenQmlAllowlist : public Dependency, private ReadWriteLockable {
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
void addWhitelistContextHandler(const std::initializer_list<QUrl>& urls, const QmlContextCallback& callback) {
|
||||
void addAllowlistContextHandler(const std::initializer_list<QUrl>& urls, const QmlContextCallback& callback) {
|
||||
withWriteLock([&] {
|
||||
for (auto url : urls) {
|
||||
if (url.isRelative()) {
|
||||
|
@ -93,11 +93,11 @@ private:
|
|||
QHash<QUrl, QList<QmlContextCallback>> _callbacks;
|
||||
};
|
||||
|
||||
QSharedPointer<OffscreenQmlWhitelist> getQmlWhitelist() {
|
||||
QSharedPointer<OffscreenQmlAllowlist> getQmlAllowlist() {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] { DependencyManager::set<OffscreenQmlWhitelist>(); });
|
||||
std::call_once(once, [&] { DependencyManager::set<OffscreenQmlAllowlist>(); });
|
||||
|
||||
return DependencyManager::get<OffscreenQmlWhitelist>();
|
||||
return DependencyManager::get<OffscreenQmlAllowlist>();
|
||||
}
|
||||
|
||||
// Class to handle changing QML audio output device using another thread
|
||||
|
@ -292,9 +292,9 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
|
|||
engine->setObjectOwnership(tablet, QQmlEngine::CppOwnership);
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::addWhitelistContextHandler(const std::initializer_list<QUrl>& urls,
|
||||
void OffscreenQmlSurface::addAllowlistContextHandler(const std::initializer_list<QUrl>& urls,
|
||||
const QmlContextCallback& callback) {
|
||||
getQmlWhitelist()->addWhitelistContextHandler(urls, callback);
|
||||
getQmlAllowlist()->addAllowlistContextHandler(urls, callback);
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::onRootContextCreated(QQmlContext* qmlContext) {
|
||||
|
@ -319,17 +319,17 @@ void OffscreenQmlSurface::onRootContextCreated(QQmlContext* qmlContext) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::applyWhiteList(const QUrl& url, QQmlContext* context) {
|
||||
QList<QmlContextCallback> callbacks = getQmlWhitelist()->getCallbacksForUrl(url);
|
||||
void OffscreenQmlSurface::applyAllowList(const QUrl& url, QQmlContext* context) {
|
||||
QList<QmlContextCallback> callbacks = getQmlAllowlist()->getCallbacksForUrl(url);
|
||||
for(const auto& callback : callbacks){
|
||||
callback(context);
|
||||
}
|
||||
}
|
||||
|
||||
QQmlContext* OffscreenQmlSurface::contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext) {
|
||||
// Get any whitelist functionality
|
||||
QList<QmlContextCallback> callbacks = getQmlWhitelist()->getCallbacksForUrl(qmlSource);
|
||||
// If we have whitelisted content, we must load a new context
|
||||
// Get any allowlist functionality
|
||||
QList<QmlContextCallback> callbacks = getQmlAllowlist()->getCallbacksForUrl(qmlSource);
|
||||
// If we have allowlisted content, we must load a new context
|
||||
forceNewContext |= !callbacks.empty();
|
||||
|
||||
QQmlContext* targetContext = Parent::contextForUrl(qmlSource, parent, forceNewContext);
|
||||
|
|
|
@ -28,9 +28,9 @@ class OffscreenQmlSurface : public hifi::qml::OffscreenSurface {
|
|||
public:
|
||||
~OffscreenQmlSurface();
|
||||
|
||||
static void addWhitelistContextHandler(const std::initializer_list<QUrl>& urls, const QmlContextCallback& callback);
|
||||
static void addWhitelistContextHandler(const QUrl& url, const QmlContextCallback& callback) { addWhitelistContextHandler({ { url } }, callback); };
|
||||
static void applyWhiteList(const QUrl& url,QQmlContext* context);
|
||||
static void addAllowlistContextHandler(const std::initializer_list<QUrl>& urls, const QmlContextCallback& callback);
|
||||
static void addAllowlistContextHandler(const QUrl& url, const QmlContextCallback& callback) { addAllowlistContextHandler({ { url } }, callback); };
|
||||
static void applyAllowList(const QUrl& url,QQmlContext* context);
|
||||
|
||||
bool isFocusText() const { return _focusText; }
|
||||
bool getCleaned() { return _isCleaned; }
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace {
|
|||
return false;
|
||||
}
|
||||
|
||||
// We can potentially add whitelisting logic or development environment variables that
|
||||
// We can potentially add allowlisting logic or development environment variables that
|
||||
// will allow people to override this setting on a per-client basis here.
|
||||
QString targetFilePath = QFileInfo(requestUrl.toLocalFile()).canonicalFilePath();
|
||||
|
||||
|
|
|
@ -487,13 +487,17 @@ Rectangle {
|
|||
var last_item_index = channel.count - 1;
|
||||
var last_item = channel.get(last_item_index);
|
||||
|
||||
if (last_message_user === username && elapsed_minutes < 1 && last_item){
|
||||
message = "<br>" + message
|
||||
last_item.text = last_item.text += "\n" + message;
|
||||
scrollToBottom()
|
||||
last_message_time = new Date();
|
||||
return;
|
||||
}
|
||||
// FIXME: When adding a new message this would check to see if we could append the incoming message
|
||||
// to the bottom of the last message. This current implimentation causes issues with scrollToBottom()
|
||||
// Specifically, scrolling to the bottom does not like image embeds.
|
||||
// This needs to be reworked entirely before it can be reimplimented
|
||||
// if (last_message_user === username && elapsed_minutes < 1 && last_item){
|
||||
// message = "<br>" + message
|
||||
// last_item.text = last_item.text += "\n" + message;
|
||||
// scrollToBottom()
|
||||
// last_message_time = new Date();
|
||||
// return;
|
||||
// }
|
||||
|
||||
last_message_user = username;
|
||||
last_message_time = new Date();
|
||||
|
@ -510,7 +514,7 @@ Rectangle {
|
|||
mess = mess.replace(arrow, "<");
|
||||
|
||||
var link = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
|
||||
mess = mess.replace(link, (match) => {return "<a onclick='Window.openUrl("+match+")' href='" + match + "'>" + match + "</a> <a onclick='Window.openUrl("+match+")'>⮺</a>"});
|
||||
mess = mess.replace(link, (match) => {return `<a style="color:#4EBAFD" onclick='Window.openUrl("+match+")' href='` + match + `'>` + match + `</a> <a onclick='Window.openUrl(`+match+`)'>⮺</a>`});
|
||||
|
||||
var newline = /\n/gi;
|
||||
mess = mess.replace(newline, "<br>");
|
||||
|
|
|
@ -36,8 +36,8 @@ var pickID = Picks.createPick(PickType.Ray, {
|
|||
filter: PICK_FILTERS,
|
||||
enabled: true,
|
||||
});
|
||||
var blacklist = [ overlayID ]; // exclude hover text from ray pick results
|
||||
Picks.setIgnoreItems(pickID, blacklist);
|
||||
var blocklist = [ overlayID ]; // exclude hover text from ray pick results
|
||||
Picks.setIgnoreItems(pickID, blocklist);
|
||||
Script.scriptEnding.connect(function() {
|
||||
Picks.removePick(pickID);
|
||||
});
|
||||
|
|
|
@ -56,7 +56,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
this.veryhighVarianceCount = 0;
|
||||
this.orderedPluginNames = [];
|
||||
this.tabletID = null;
|
||||
this.blacklist = [];
|
||||
this.blocklist = [];
|
||||
this.pointerManager = new PointerManager();
|
||||
this.grabSphereOverlays = [null, null];
|
||||
this.targetIDs = {};
|
||||
|
@ -169,8 +169,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
this.setIgnorePointerItems = function() {
|
||||
if (HMD.tabletID && HMD.tabletID !== this.tabletID) {
|
||||
this.tabletID = HMD.tabletID;
|
||||
Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist);
|
||||
Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist);
|
||||
Pointers.setIgnoreItems(_this.leftPointer, _this.blocklist);
|
||||
Pointers.setIgnoreItems(_this.rightPointer, _this.blocklist);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -547,19 +547,19 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
}
|
||||
};
|
||||
|
||||
this.leftBlacklistTabletIDs = [];
|
||||
this.rightBlacklistTabletIDs = [];
|
||||
this.leftBlocklistTabletIDs = [];
|
||||
this.rightBlocklistTabletIDs = [];
|
||||
|
||||
this.setLeftBlacklist = function () {
|
||||
Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist.concat(_this.leftBlacklistTabletIDs));
|
||||
this.setLeftBlocklist = function () {
|
||||
Pointers.setIgnoreItems(_this.leftPointer, _this.blocklist.concat(_this.leftBlocklistTabletIDs));
|
||||
};
|
||||
this.setRightBlacklist = function () {
|
||||
Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist.concat(_this.rightBlacklistTabletIDs));
|
||||
this.setRightBlocklist = function () {
|
||||
Pointers.setIgnoreItems(_this.rightPointer, _this.blocklist.concat(_this.rightBlocklistTabletIDs));
|
||||
};
|
||||
|
||||
this.setBlacklist = function() {
|
||||
_this.setLeftBlacklist();
|
||||
_this.setRightBlacklist();
|
||||
this.setBlocklist = function() {
|
||||
_this.setLeftBlocklist();
|
||||
_this.setRightBlocklist();
|
||||
};
|
||||
|
||||
var MAPPING_NAME = "com.highfidelity.controllerDispatcher";
|
||||
|
@ -634,34 +634,34 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
var message;
|
||||
if (sender === MyAvatar.sessionUUID) {
|
||||
try {
|
||||
if (channel === 'Hifi-Hand-RayPick-Blacklist') {
|
||||
if (channel === 'Hifi-Hand-RayPick-Blocklist') {
|
||||
message = JSON.parse(data);
|
||||
var action = message.action;
|
||||
var id = message.id;
|
||||
var index = _this.blacklist.indexOf(id);
|
||||
var index = _this.blocklist.indexOf(id);
|
||||
|
||||
if (action === 'add' && index === -1) {
|
||||
_this.blacklist.push(id);
|
||||
_this.setBlacklist();
|
||||
_this.blocklist.push(id);
|
||||
_this.setBlocklist();
|
||||
}
|
||||
|
||||
if (action === 'remove') {
|
||||
if (index > -1) {
|
||||
_this.blacklist.splice(index, 1);
|
||||
_this.setBlacklist();
|
||||
_this.blocklist.splice(index, 1);
|
||||
_this.setBlocklist();
|
||||
}
|
||||
}
|
||||
|
||||
if (action === "tablet") {
|
||||
var tabletIDs = message.blacklist ?
|
||||
var tabletIDs = message.blocklist ?
|
||||
[HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID] :
|
||||
[];
|
||||
if (message.hand === LEFT_HAND) {
|
||||
_this.leftBlacklistTabletIDs = tabletIDs;
|
||||
_this.setLeftBlacklist();
|
||||
_this.leftBlocklistTabletIDs = tabletIDs;
|
||||
_this.setLeftBlocklist();
|
||||
} else {
|
||||
_this.rightBlacklistTabletIDs = tabletIDs;
|
||||
_this.setRightBlacklist();
|
||||
_this.rightBlocklistTabletIDs = tabletIDs;
|
||||
_this.setRightBlocklist();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -737,7 +737,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
Entities.mousePressOnEntity.connect(mousePress);
|
||||
|
||||
var controllerDispatcher = new ControllerDispatcher();
|
||||
Messages.subscribe('Hifi-Hand-RayPick-Blacklist');
|
||||
Messages.subscribe('Hifi-Hand-RayPick-Blocklist');
|
||||
Messages.messageReceived.connect(controllerDispatcher.handleMessage);
|
||||
|
||||
Picks.handLaserDelayChanged.connect(controllerDispatcher.handLaserDelayChanged);
|
||||
|
|
|
@ -299,7 +299,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
action: 'remove',
|
||||
id: this.ignoredEntities[i]
|
||||
};
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blocklist', JSON.stringify(data));
|
||||
}
|
||||
this.ignoredEntities = [];
|
||||
};
|
||||
|
@ -394,7 +394,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
action: 'add',
|
||||
id: intersection.objectID
|
||||
};
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blocklist', JSON.stringify(data));
|
||||
this.ignoredEntities.push(intersection.objectID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -229,7 +229,7 @@ Script.include("/~/system/libraries/utils.js");
|
|||
enableDispatcherModule("RightHandInEditMode", rightHandInEditMode);
|
||||
|
||||
var INEDIT_STATUS_CHANNEL = "Hifi-InEdit-Status";
|
||||
var HAND_RAYPICK_BLACKLIST_CHANNEL = "Hifi-Hand-RayPick-Blacklist";
|
||||
var HAND_RAYPICK_BLOCKLIST_CHANNEL = "Hifi-Hand-RayPick-Blocklist";
|
||||
this.handleMessage = function (channel, data, sender) {
|
||||
if (channel === INEDIT_STATUS_CHANNEL && sender === MyAvatar.sessionUUID) {
|
||||
var message;
|
||||
|
@ -247,10 +247,10 @@ Script.include("/~/system/libraries/utils.js");
|
|||
} else {
|
||||
rightHandInEditMode.isEditing = message.editing;
|
||||
}
|
||||
Messages.sendLocalMessage(HAND_RAYPICK_BLACKLIST_CHANNEL, JSON.stringify({
|
||||
Messages.sendLocalMessage(HAND_RAYPICK_BLOCKLIST_CHANNEL, JSON.stringify({
|
||||
action: "tablet",
|
||||
hand: message.hand,
|
||||
blacklist: message.editing
|
||||
blocklist: message.editing
|
||||
}));
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
enableDispatcherModule("RightHandInVREditMode", rightHandInVREditMode);
|
||||
|
||||
var INVREDIT_STATUS_CHANNEL = "Hifi-InVREdit-Status";
|
||||
var HAND_RAYPICK_BLACKLIST_CHANNEL = "Hifi-Hand-RayPick-Blacklist";
|
||||
var HAND_RAYPICK_BLOCKLIST_CHANNEL = "Hifi-Hand-RayPick-Blocklist";
|
||||
this.handleMessage = function (channel, data, sender) {
|
||||
if (channel === INVREDIT_STATUS_CHANNEL && sender === MyAvatar.sessionUUID) {
|
||||
var message;
|
||||
|
@ -168,10 +168,10 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
} else {
|
||||
rightHandInVREditMode.isEditing = message.editing;
|
||||
}
|
||||
Messages.sendLocalMessage(HAND_RAYPICK_BLACKLIST_CHANNEL, JSON.stringify({
|
||||
Messages.sendLocalMessage(HAND_RAYPICK_BLOCKLIST_CHANNEL, JSON.stringify({
|
||||
action: "tablet",
|
||||
hand: message.hand,
|
||||
blacklist: message.editing
|
||||
blocklist: message.editing
|
||||
}));
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
action: 'add',
|
||||
id: objectID
|
||||
};
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blocklist', JSON.stringify(data));
|
||||
this.ignoredObjects.push(objectID);
|
||||
}
|
||||
} else if (intersection.type === Picks.INTERSECTED_ENTITY) {
|
||||
|
@ -122,7 +122,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
action: 'add',
|
||||
id: objectID
|
||||
};
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blocklist', JSON.stringify(data));
|
||||
this.ignoredObjects.push(objectID);
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
action: 'remove',
|
||||
id: this.ignoredObjects[index]
|
||||
};
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blocklist', JSON.stringify(data));
|
||||
}
|
||||
|
||||
this.ignoredObjects = [];
|
||||
|
|
|
@ -58,7 +58,7 @@ function runDefaultsTogether() {
|
|||
function runDefaultsSeparately() {
|
||||
for (var i in CONTOLLER_SCRIPTS) {
|
||||
if (CONTOLLER_SCRIPTS.hasOwnProperty(i)) {
|
||||
print("loading " + CONTOLLER_SCRIPTS[j]);
|
||||
print("loading " + CONTOLLER_SCRIPTS[i]);
|
||||
Script.load(CONTOLLER_SCRIPTS[i]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
// Program ----
|
||||
function onMouseLookChanged(newMouseLook) {
|
||||
disableMouseLook();
|
||||
mouseLookEnabled = newMouseLook;
|
||||
}
|
||||
|
||||
|
@ -38,8 +39,6 @@
|
|||
// Toggle using the m key
|
||||
if (event.text.toLowerCase() === "m") {
|
||||
if (Camera.captureMouse) {
|
||||
mouseLookActive = false;
|
||||
Settings.setValue("mouselook-active", false);
|
||||
disableMouseLook();
|
||||
} else {
|
||||
mouseLookActive = true;
|
||||
|
@ -82,12 +81,16 @@
|
|||
if (hmdActive) return;
|
||||
if (tablet.tabletShown) return;
|
||||
if (overlayActive) return;
|
||||
if (!mouseLookEnabled) return; // Mouse look disabled via setting
|
||||
if (!mouseLookActive) return; // Mouse look disabled via the hotkey
|
||||
|
||||
Camera.captureMouse = true;
|
||||
}
|
||||
|
||||
function disableMouseLook() {
|
||||
mouseLookActive = false;
|
||||
Settings.setValue("mouselook-active", false);
|
||||
|
||||
Camera.captureMouse = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// Created by Ryan Huffman on 13 Nov 2014
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
// Copyright 2022 Overte e.V.
|
||||
// Copyright 2022-2024 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -63,131 +63,298 @@
|
|||
</table>
|
||||
</div>
|
||||
<div id="uiMaterialAssistant" style="display: none;">
|
||||
<div id="uiMaterialAssistant-sidewalk">
|
||||
|
||||
<div id="uiMaterialAssistant-formContainer">
|
||||
<div id="uiMaterialAssistant-headerContainer">
|
||||
<div style="width: 85%; text-align: left;"><font class="uiMaterialAssistant-title">MATERIAL DATA</font></div>
|
||||
<div style="width: 15%; text-align: right;"><span id="uiMaterialAssistant-closeButton">✖</span></div>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<font class="uiMaterialAssistant-label">Name: </font>
|
||||
<input name = "ma-name" id = "ma-name" type = "text" class="uiMaterialAssistant-input" style= "width: 90%;">
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-albedo-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Albedo (Color): </font>
|
||||
<div id="ma-albedo-colorPicker" class="uiMaterialAssistant-color-picker"></div>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-albedoMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Albedo Map (RGB) URL: </font>
|
||||
<input name = "ma-albedoMap" id = "ma-albedoMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;"><br>
|
||||
<font class="uiMaterialAssistant-Explain">The Albedo (Color) can be used to tint the texture of the Albedo Map.</font>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-metallic-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Metallic: </font>
|
||||
<input class="uiMaterialAssistant-input" name = "ma-metallic" id = "ma-metallic" readonly type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="1" max="1000" value="1" class="uiMaterialAssistant-slider" name = "ma-metallic-slider" id = "ma-metallic-slider"><br>
|
||||
<div style="width: 100%; display: flex;">
|
||||
<div style="width: 15%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|< Nonmetal</font></div>
|
||||
<div style="width: 15%; text-align: left;"><font class = "uiMaterialAssistant-Explain">| Hair</font></div>
|
||||
<div style="width: 60%; text-align: left;"><font class = "uiMaterialAssistant-Explain">| Chitin</font></div>
|
||||
<div style="width: 10%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Metal >|</font></div>
|
||||
</div><br>
|
||||
<font class="uiMaterialAssistant-label">Metallic Map (Grayscale) URL: </font>
|
||||
<input name = "ma-metallicMap" id = "ma-metallicMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-roughness-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Roughness:</font>
|
||||
<input name = "ma-roughness" id = "ma-roughness" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-roughness-slider" id = "ma-roughness-slider"><br>
|
||||
<div style="width: 100%; display: flex;">
|
||||
<div style="width: 50%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|< Glossy, polished, lustrous</font></div>
|
||||
<div style="width: 50%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Unpolished, mat, rough >|</font></div>
|
||||
</div><br>
|
||||
<font class="uiMaterialAssistant-label">Roughness Map (Grayscale) URL: </font>
|
||||
<input name = "ma-roughnessMap" id = "ma-roughnessMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-normalMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Normal Map URL: </font>
|
||||
<input name = "ma-normalMap" id = "ma-normalMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-opacity-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Opacity: </font>
|
||||
<input name = "ma-opacity" id = "ma-opacity" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-opacity-slider" id = "ma-opacity-slider"><br>
|
||||
<div style="width: 100%; display: flex;">
|
||||
<div style="width: 50%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|< Transparent</font></div>
|
||||
<div style="width: 50%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Opaque >|</font></div>
|
||||
<div id="uiMaterialAssistant-headerContainer">
|
||||
<div style="width: 85%; text-align: left;"><font class="uiMaterialAssistant-title">MATERIAL DATA</font></div>
|
||||
<div style="width: 15%; text-align: right;"><span id="uiMaterialAssistant-closeButton">✖ </span></div>
|
||||
</div>
|
||||
<div id="uiMaterialAssistant-scrollable">
|
||||
<div id="uiMaterialAssistant-sidewalk">
|
||||
<div id="uiMaterialAssistant-formContainer">
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<font class="uiMaterialAssistant-label">Name: </font>
|
||||
<input name = "ma-name" id = "ma-name" type = "text" class="uiMaterialAssistant-input" style= "width: 90%;">
|
||||
</div>
|
||||
<br>
|
||||
<font class="uiMaterialAssistant-label">Opacity Map Mode:</font><br><br>
|
||||
<input type="radio" class="uiMaterialAssistant-radio" checked name = "ma-opacityMapMode" id = "ma-opacityMapMode-dont" value = "OPACITY_MAP_OPAQUE"> Do not used
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-opacityMapMode" id = "ma-opacityMapMode-mask" value = "OPACITY_MAP_MASK"> Cut off mask
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-opacityMapMode" id = "ma-opacityMapMode-blend" value = "OPACITY_MAP_BLEND"> Blend<br>
|
||||
<font class="uiMaterialAssistant-Explain"><br>Note: For an opacity map, the alpha layer of the Albedo Map will be used.<br>
|
||||
'Blend' mode will used the alpha value to determine the opacity of a pixel.<br>
|
||||
'Cut off mask' mode will use the 'Cut off threshold' to determine if a pixel will be opaque or transparent, based on the alpha value from the map.</font><br><br>
|
||||
<font class="uiMaterialAssistant-label">Cut Off Threshold: </font>
|
||||
<input name = "ma-opacityCutoff" id = "ma-opacityCutoff" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-opacityCutoff-slider" id = "ma-opacityCutoff-slider"><br>
|
||||
<div style="width: 100%; display: flex;">
|
||||
<div style="width: 50%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|< Transparent</font></div>
|
||||
<div style="width: 50%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Opaque >|</font></div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<font class="uiMaterialAssistant-label">Model: </font><br><br>
|
||||
<input type="radio" class="uiMaterialAssistant-radio" checked name = "ma-Model" id = "ma-Model-hifi_pbr" value = "hifi_pbr"> PBR
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-Model" id = "ma-Model-vrm_mtoon" value = "vrm_mtoon"> MToon
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-Model" id = "ma-Model-hifi_shader_simple" value = "hifi_shader_simple"> Shader (Simple)<br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<div style="width: 100%; display: flex;">
|
||||
<div style="width: 70%; text-align: left;">
|
||||
<button class="uiMaterialAssistant-active" id="ma-emissive-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Emissive: </font>
|
||||
<div id="ma-emissive-colorPicker" class="uiMaterialAssistant-color-picker"></div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<div id="maContainerAlbedo">
|
||||
<button class="uiMaterialAssistant-active" id="ma-albedo-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Albedo (Color): </font>
|
||||
<div id="ma-albedo-colorPicker" class="uiMaterialAssistant-color-picker"></div>
|
||||
</div>
|
||||
<div style="width: 30%; text-align: left;">
|
||||
<input type="checkbox" class="uiMaterialAssistant-checkbox" name = "ma-unlit" id = "ma-unlit">
|
||||
<font class="uiMaterialAssistant-label">Unlit</font>
|
||||
<br><br>
|
||||
<div id="maContainerAlbedoMap">
|
||||
<button class="uiMaterialAssistant-active" id="ma-albedoMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Albedo Map (RGB) URL: </font>
|
||||
<input name = "ma-albedoMap" id = "ma-albedoMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;"><br>
|
||||
<font class="uiMaterialAssistant-Explain">The Albedo (Color) can be used to tint the texture of the Albedo Map.</font>
|
||||
</div>
|
||||
</div>
|
||||
<font class="uiMaterialAssistant-label">Bloom Factor:</font>
|
||||
<input name = "ma-bloom" id = "ma-bloom" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="100" max="900" value="100" class="uiMaterialAssistant-slider" name = "ma-bloom-slider" id = "ma-bloom-slider"><br>
|
||||
<font class="uiMaterialAssistant-label">Emissive Map (RGB) URL:</font>
|
||||
<input name = "ma-emissiveMap" id = "ma-emissiveMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
|
||||
<div class="uiMaterialAssistant-group" id="maContainerMetallic">
|
||||
<button class="uiMaterialAssistant-active" id="ma-metallic-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Metallic: </font>
|
||||
<input class="uiMaterialAssistant-input" name = "ma-metallic" id = "ma-metallic" readonly type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="1" max="1000" value="1" class="uiMaterialAssistant-slider" name = "ma-metallic-slider" id = "ma-metallic-slider"><br>
|
||||
<div style="width: 100%; display: flex;">
|
||||
<div style="width: 15%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|< Nonmetal</font></div>
|
||||
<div style="width: 15%; text-align: left;"><font class = "uiMaterialAssistant-Explain">| Hair</font></div>
|
||||
<div style="width: 60%; text-align: left;"><font class = "uiMaterialAssistant-Explain">| Chitin</font></div>
|
||||
<div style="width: 10%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Metal >|</font></div>
|
||||
</div><br>
|
||||
<font class="uiMaterialAssistant-label">Metallic Map (Red channel) URL: </font>
|
||||
<input name = "ma-metallicMap" id = "ma-metallicMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
<div style="display: flex;">
|
||||
<div style="text-align: left;"><input type="checkbox" class="uiMaterialAssistant-checkbox" name = "ma-useSpecular" id = "ma-useSpecular"></div>
|
||||
<div style="text-align: left; padding: 3px;"><font class="uiMaterialAssistant-label"> Instead, use Specular Map </font></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group" id="maContainerRoughness">
|
||||
<button class="uiMaterialAssistant-active" id="ma-roughness-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Roughness:</font>
|
||||
<input name = "ma-roughness" id = "ma-roughness" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-roughness-slider" id = "ma-roughness-slider"><br>
|
||||
<div style="width: 100%; display: flex;">
|
||||
<div style="width: 50%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|< Glossy, polished, lustrous</font></div>
|
||||
<div style="width: 50%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Unpolished, mat, rough >|</font></div>
|
||||
</div><br>
|
||||
<font class="uiMaterialAssistant-label">Roughness Map (Red channel) URL: </font>
|
||||
<input name = "ma-roughnessMap" id = "ma-roughnessMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
<div style="display: flex;">
|
||||
<div style="text-align: left;"><input type="checkbox" class="uiMaterialAssistant-checkbox" name = "ma-useGloss" id = "ma-useGloss"></div>
|
||||
<div style="text-align: left; padding: 3px;"><font class="uiMaterialAssistant-label"> Instead, use Gloss Map </font></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group" id="maContainerNormalMap">
|
||||
<button class="uiMaterialAssistant-active" id="ma-normalMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Normal Map URL: </font>
|
||||
<input name = "ma-normalMap" id = "ma-normalMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
<div style="display: flex;">
|
||||
<div style="text-align: left;"><input type="checkbox" class="uiMaterialAssistant-checkbox" name = "ma-useBump" id = "ma-useBump"></div>
|
||||
<div style="text-align: left; padding: 3px;"><font class="uiMaterialAssistant-label"> Instead, use Bump Map </font></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<div id="maContainerOpacity">
|
||||
<button class="uiMaterialAssistant-active" id="ma-opacity-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Opacity: </font>
|
||||
<input name = "ma-opacity" id = "ma-opacity" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-opacity-slider" id = "ma-opacity-slider"><br>
|
||||
<div style="width: 100%; display: flex;">
|
||||
<div style="width: 50%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|< Transparent</font></div>
|
||||
<div style="width: 50%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Opaque >|</font></div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div id="maContainerOpacityMap">
|
||||
<font class="uiMaterialAssistant-label">Opacity Map Mode:</font><br><br>
|
||||
<input type="radio" class="uiMaterialAssistant-radio" checked name = "ma-opacityMapMode" id = "ma-opacityMapMode-dont" value = "OPACITY_MAP_OPAQUE"> Not used
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-opacityMapMode" id = "ma-opacityMapMode-mask" value = "OPACITY_MAP_MASK"> Cut off mask
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-opacityMapMode" id = "ma-opacityMapMode-blend" value = "OPACITY_MAP_BLEND"> Blend<br>
|
||||
<font class="uiMaterialAssistant-Explain"><br>Note: For an opacity map, the alpha layer of the Albedo Map will be used.<br>
|
||||
'Blend' mode will used the alpha value to determine the opacity of a pixel.<br>
|
||||
'Cut off mask' mode will use the 'Cut off threshold' to determine if a pixel will be opaque or transparent, based on the alpha value from the map.</font><br><br>
|
||||
<font class="uiMaterialAssistant-label">Cut Off Threshold: </font>
|
||||
<input name = "ma-opacityCutoff" id = "ma-opacityCutoff" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-opacityCutoff-slider" id = "ma-opacityCutoff-slider"><br>
|
||||
<div style="width: 100%; display: flex;">
|
||||
<div style="width: 50%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|< Transparent</font></div>
|
||||
<div style="width: 50%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Opaque >|</font></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group" id = "maContainerEmissive">
|
||||
<div style="width: 100%; display: flex;">
|
||||
<div style="width: 65%; text-align: left;">
|
||||
<button class="uiMaterialAssistant-active" id="ma-emissive-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Emissive: </font>
|
||||
<div id="ma-emissive-colorPicker" class="uiMaterialAssistant-color-picker"></div>
|
||||
</div>
|
||||
<div style="width: 35%; text-align: left;">
|
||||
<div id = "maContainerUnlit">
|
||||
<div style="display: flex;">
|
||||
<div style="text-align: left;"><input type="checkbox" class="uiMaterialAssistant-checkbox" name = "ma-unlit" id = "ma-unlit"></div>
|
||||
<div style="text-align: left; padding: 3px;"><font class="uiMaterialAssistant-label"> Unlit</font></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<font class="uiMaterialAssistant-label">Bloom Factor:</font>
|
||||
<input name = "ma-bloom" id = "ma-bloom" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="100" max="900" value="100" class="uiMaterialAssistant-slider" name = "ma-bloom-slider" id = "ma-bloom-slider"><br>
|
||||
<div id = "maContainerEmissiveMap">
|
||||
<font class="uiMaterialAssistant-label">Emissive Map (RGB) URL:</font>
|
||||
<input name = "ma-emissiveMap" id = "ma-emissiveMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group" id="maContainerScattering">
|
||||
<button class="uiMaterialAssistant-active" id="ma-scattering-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Scattering: </font>
|
||||
<input name = "ma-scattering" id = "ma-scattering" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-scattering-slider" id = "ma-scattering-slider"><br>
|
||||
<font class="uiMaterialAssistant-label">Scattering Map (Red channel) URL: </font>
|
||||
<input name = "ma-scatteringMap" id = "ma-scatteringMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
<font class="uiMaterialAssistant-Explain">Scattering or Scattering Map won't be effective without the presence of a Normal/Bump Map.</font>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group" id="maContainerOcclusionMap">
|
||||
<button class="uiMaterialAssistant-active" id="ma-occlusionMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Occlusion Map (Red channel) URL: </font>
|
||||
<input name = "ma-occlusionMap" id = "ma-occlusionMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
<font class="uiMaterialAssistant-Explain">Note: 'Occlusion Map' and 'Light Map' are using a separated UV Map.</font>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group" id="maContainerLightMap">
|
||||
<button class="uiMaterialAssistant-active" id="ma-lightMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Light Map (Red channel) URL: </font>
|
||||
<input name = "ma-lightMap" id = "ma-lightMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
<font class="uiMaterialAssistant-Explain">Note: 'Light Map' and 'Occlusion Map' are using a separated UV Map.</font>
|
||||
</div>
|
||||
|
||||
<div id="maContainerMtoon">
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-shade-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Shade (Color): </font>
|
||||
<div id="ma-shade-colorPicker" class="uiMaterialAssistant-color-picker"></div>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-shadeMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Shade Map (RGB) URL: </font>
|
||||
<input name = "ma-shadeMap" id = "ma-shadeMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;"><br>
|
||||
<font class="uiMaterialAssistant-Explain">The Shade (Color) can be used to tint the texture of the Shade Map.</font>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-matcap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Matcap (Color): </font>
|
||||
<div id="ma-matcap-colorPicker" class="uiMaterialAssistant-color-picker"></div>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-matcapMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Matcap Map (RGB) URL: </font>
|
||||
<input name = "ma-matcapMap" id = "ma-matcapMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;"><br>
|
||||
<font class="uiMaterialAssistant-Explain">The Matcap (Color) can be used to tint the texture of the Matcap Map.</font>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-shadingShift-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Shading Shift: </font>
|
||||
<input name = "ma-shadingShift" id = "ma-shadingShift" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-shadingShift-slider" id = "ma-shadingShift-slider">
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-shadingShiftMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Shading Shift Map (Red Channel) URL: </font>
|
||||
<input name = "ma-shadingShiftMap" id = "ma-shadingShiftMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;"><br>
|
||||
<font class="uiMaterialAssistant-Explain">The Shading Shift value can be used to multiply the effect of the Shading Shift Map. Shading Shift Map can be RGB texture, but it will only used the "red" channel.</font>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-shadingToony-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Shading Toony: </font>
|
||||
<input name = "ma-shadingToony" id = "ma-shadingToony" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-shadingToony-slider" id = "ma-shadingToony-slider">
|
||||
<font class="uiMaterialAssistant-Explain"> </font>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-parametricRim-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Parametric Rim (Color): </font>
|
||||
<div id="ma-parametricRim-colorPicker" class="uiMaterialAssistant-color-picker"></div>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-parametricRimFresnelPower-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Parametric Rim Fresnel Power: </font>
|
||||
<input name = "ma-parametricRimFresnelPower" id = "ma-parametricRimFresnelPower" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="20000" value="0" class="uiMaterialAssistant-slider" name = "ma-parametricRimFresnelPower-slider" id = "ma-parametricRimFresnelPower-slider">
|
||||
<font class="uiMaterialAssistant-Explain"> </font>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-parametricRimLift-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Parametric Rim Lift Factor: </font>
|
||||
<input name = "ma-parametricRimLift" id = "ma-parametricRimLift" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="-10000" max="10000" value="0" class="uiMaterialAssistant-slider" name = "ma-parametricRimLift-slider" id = "ma-parametricRimLift-slider">
|
||||
<font class="uiMaterialAssistant-Explain"> </font>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-rimMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Rim Map (RGB) URL: </font>
|
||||
<input name = "ma-rimMap" id = "ma-rimMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;"><br>
|
||||
<font class="uiMaterialAssistant-Explain"> </font>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-rimLightingMix-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Rim Lighting Mix: </font>
|
||||
<input name = "ma-rimLightingMix" id = "ma-rimLightingMix" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-rimLightingMix-slider" id = "ma-rimLightingMix-slider">
|
||||
<font class="uiMaterialAssistant-Explain">How much to mix between the rim color and normal lighting.</font>
|
||||
</div>
|
||||
<!-- ############ Not supported yet, but the code is ready (commented) ##########################################################
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-outlineWidthMode-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Outline Width Mode : </font><br><br>
|
||||
<input type="radio" class="uiMaterialAssistant-radio" checked name = "ma-outlineWidthMode" id = "ma-outlineWidthMode-none" value = "none"> None
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-outlineWidthMode" id = "ma-outlineWidthMode-world" value = "worldCoordinates"> World Coordinates
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-outlineWidthMode" id = "ma-outlineWidthMode-screen" value = "screenCoordinates"> Screen Coordinates<br>
|
||||
<font class="uiMaterialAssistant-Explain">'World Coordinates' will render an outline with a constant world size, i.e. its apparent size depends on distance.
|
||||
'Screen Coordinates' will render an outline with a constant screen size, i.e. its apparent size remains constant.</font>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-outlineWidth-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Outline Width: </font>
|
||||
<input name = "ma-outlineWidth" id = "ma-outlineWidth" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-outlineWidth-slider" id = "ma-outlineWidth-slider">
|
||||
<font class="uiMaterialAssistant-Explain">The width of the outline, in meters if the mode is 'World Coordinates', or a ratio of the screen height if the Mode is 'Screen Coordinates'.</font>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-outline-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Outline (Color): </font>
|
||||
<div id="ma-outline-colorPicker" class="uiMaterialAssistant-color-picker"></div>
|
||||
</div>
|
||||
############################################################################################################ -->
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-uvAnimationMaskMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">UV Animation Mask Map (Blue Channel) URL: </font>
|
||||
<input name = "ma-uvAnimationMaskMap" id = "ma-uvAnimationMaskMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;"><br>
|
||||
<font class="uiMaterialAssistant-Explain">UV Animation Mask Map can be RGB texture, but it will only used the "blue" channel.</font>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-uvAnimationScrollXSpeed-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">UV Animation Scroll X Speed: </font>
|
||||
<input name = "ma-uvAnimationScrollXSpeed" id = "ma-uvAnimationScrollXSpeed" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="-3000" max="3000" value="0" class="uiMaterialAssistant-slider" name = "ma-uvAnimationScrollXSpeed-slider" id = "ma-uvAnimationScrollXSpeed-slider">
|
||||
<font class="uiMaterialAssistant-Explain">The speed of the UV scrolling animation in the X dimension, in UV units per second.</font>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-uvAnimationScrollYSpeed-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">UV Animation Scroll Y Speed: </font>
|
||||
<input name = "ma-uvAnimationScrollYSpeed" id = "ma-uvAnimationScrollYSpeed" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="-3000" max="3000" value="0" class="uiMaterialAssistant-slider" name = "ma-uvAnimationScrollYSpeed-slider" id = "ma-uvAnimationScrollYSpeed-slider">
|
||||
<font class="uiMaterialAssistant-Explain">The speed of the UV scrolling animation in the Y dimension, in UV units per second.</font>
|
||||
<br><br>
|
||||
<button class="uiMaterialAssistant-active" id="ma-uvAnimationRotationSpeed-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">UV Animation Rotation Speed: </font>
|
||||
<input name = "ma-uvAnimationRotationSpeed" id = "ma-uvAnimationRotationSpeed" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="-10000" max="10000" value="0" class="uiMaterialAssistant-slider" name = "ma-uvAnimationRotationSpeed-slider" id = "ma-uvAnimationRotationSpeed-slider">
|
||||
<font class="uiMaterialAssistant-Explain">The speed of the UV scrolling rotation around the center (0.5 UV, 0.5 UV), in radians per second.</font>
|
||||
</div>
|
||||
</div>
|
||||
<div id="maContainerShaderSimple">
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<font class="uiMaterialAssistant-label">Procedural (json): </font>
|
||||
<button class="uiMaterialAssistant-smallButton" id= "ma-addProceduralTemplate">Use Basic Template</button>
|
||||
<textarea name = "ma-procedural" id = "ma-procedural" class="uiMaterialAssistant-textarea"></textarea><br>
|
||||
<font class="uiMaterialAssistant-Explain"> </font>
|
||||
</div>
|
||||
</div>
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<font class="uiMaterialAssistant-label">Material displayed on surface: </font><br><br>
|
||||
<input type="radio" class="uiMaterialAssistant-radio" checked name = "ma-cullFaceMode" id = "ma-cullFaceMode-back" value = "CULL_BACK"> Outside
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-cullFaceMode" id = "ma-cullFaceMode-front" value = "CULL_FRONT"> Inside
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-cullFaceMode" id = "ma-cullFaceMode-none" value = "CULL_NONE"> Both<br>
|
||||
</div>
|
||||
<br><br><br><br><br>
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-scattering-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Scattering: </font>
|
||||
<input name = "ma-scattering" id = "ma-scattering" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
|
||||
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-scattering-slider" id = "ma-scattering-slider"><br>
|
||||
<font class="uiMaterialAssistant-label">Scattering Map (Grayscale) URL: </font>
|
||||
<input name = "ma-scatteringMap" id = "ma-scatteringMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<button class="uiMaterialAssistant-active" id="ma-occlusionMap-isActive"></button>
|
||||
<font class="uiMaterialAssistant-label">Occlusion Map (Grayscale) URL: </font>
|
||||
<input name = "ma-occlusionMap" id = "ma-occlusionMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
|
||||
</div>
|
||||
|
||||
<div class="uiMaterialAssistant-group">
|
||||
<font class="uiMaterialAssistant-label">Material displayed on surface: </font><br><br>
|
||||
<input type="radio" class="uiMaterialAssistant-radio" checked name = "ma-cullFaceMode" id = "ma-cullFaceMode-back" value = "CULL_BACK"> Outside
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-cullFaceMode" id = "ma-cullFaceMode-front" value = "CULL_FRONT"> Inside
|
||||
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-cullFaceMode" id = "ma-cullFaceMode-none" value = "CULL_NONE"> Both<br>
|
||||
</div>
|
||||
<br><br><br><br><br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue