Merge branch 'tablet-ui-edit-js' of github.com:sethalves/hifi into tablet-ui-edit-js
|
@ -434,8 +434,16 @@ void Agent::executeScript() {
|
||||||
connect(&_avatarAudioTimerThread, &QThread::finished, audioTimerWorker, &QObject::deleteLater);
|
connect(&_avatarAudioTimerThread, &QThread::finished, audioTimerWorker, &QObject::deleteLater);
|
||||||
_avatarAudioTimerThread.start();
|
_avatarAudioTimerThread.start();
|
||||||
|
|
||||||
// 60Hz timer for avatar
|
// Agents should run at 45hz
|
||||||
QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatar);
|
static const int AVATAR_DATA_HZ = 45;
|
||||||
|
static const int AVATAR_DATA_IN_MSECS = MSECS_PER_SECOND / AVATAR_DATA_HZ;
|
||||||
|
QTimer* avatarDataTimer = new QTimer(this);
|
||||||
|
connect(avatarDataTimer, &QTimer::timeout, this, &Agent::processAgentAvatar);
|
||||||
|
avatarDataTimer->setSingleShot(false);
|
||||||
|
avatarDataTimer->setInterval(AVATAR_DATA_IN_MSECS);
|
||||||
|
avatarDataTimer->setTimerType(Qt::PreciseTimer);
|
||||||
|
avatarDataTimer->start();
|
||||||
|
|
||||||
_scriptEngine->run();
|
_scriptEngine->run();
|
||||||
|
|
||||||
Frame::clearFrameHandler(AUDIO_FRAME_TYPE);
|
Frame::clearFrameHandler(AUDIO_FRAME_TYPE);
|
||||||
|
@ -538,7 +546,7 @@ void Agent::processAgentAvatar() {
|
||||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||||
|
|
||||||
AvatarData::AvatarDataDetail dataDetail = (randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO) ? AvatarData::SendAllData : AvatarData::CullSmallData;
|
AvatarData::AvatarDataDetail dataDetail = (randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO) ? AvatarData::SendAllData : AvatarData::CullSmallData;
|
||||||
QByteArray avatarByteArray = scriptedAvatar->toByteArray(dataDetail, 0, scriptedAvatar->getLastSentJointData());
|
QByteArray avatarByteArray = scriptedAvatar->toByteArrayStateful(dataDetail);
|
||||||
scriptedAvatar->doneEncoding(true);
|
scriptedAvatar->doneEncoding(true);
|
||||||
|
|
||||||
static AvatarDataSequenceNumber sequenceNumber = 0;
|
static AvatarDataSequenceNumber sequenceNumber = 0;
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include <ThreadedAssignment.h>
|
#include <ThreadedAssignment.h>
|
||||||
#include "AvatarMixerClientData.h"
|
#include "AvatarMixerClientData.h"
|
||||||
|
|
||||||
|
#include "AvatarMixerSlavePool.h"
|
||||||
|
|
||||||
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
|
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
|
||||||
class AvatarMixer : public ThreadedAssignment {
|
class AvatarMixer : public ThreadedAssignment {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -36,8 +38,8 @@ public slots:
|
||||||
void sendStatsPacket() override;
|
void sendStatsPacket() override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void queueIncomingPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node);
|
||||||
void handleViewFrustumPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleViewFrustumPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
|
||||||
void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message);
|
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message);
|
||||||
void handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
|
@ -45,22 +47,29 @@ private slots:
|
||||||
void handleRequestsDomainListDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleRequestsDomainListDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void domainSettingsRequestComplete();
|
void domainSettingsRequestComplete();
|
||||||
void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID);
|
void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID);
|
||||||
|
void start();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void broadcastAvatarData();
|
AvatarMixerClientData* getOrCreateClientData(SharedNodePointer node);
|
||||||
|
std::chrono::microseconds timeFrame(p_high_resolution_clock::time_point& timestamp);
|
||||||
|
void throttle(std::chrono::microseconds duration, int frame);
|
||||||
|
|
||||||
void parseDomainServerSettings(const QJsonObject& domainSettings);
|
void parseDomainServerSettings(const QJsonObject& domainSettings);
|
||||||
void sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode);
|
void sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode);
|
||||||
|
|
||||||
QThread _broadcastThread;
|
void manageDisplayName(const SharedNodePointer& node);
|
||||||
|
|
||||||
p_high_resolution_clock::time_point _lastFrameTimestamp;
|
p_high_resolution_clock::time_point _lastFrameTimestamp;
|
||||||
|
|
||||||
float _trailingSleepRatio { 1.0f };
|
// FIXME - new throttling - use these values somehow
|
||||||
float _performanceThrottlingRatio { 0.0f };
|
float _trailingMixRatio { 0.0f };
|
||||||
|
float _throttlingRatio { 0.0f };
|
||||||
|
|
||||||
|
|
||||||
int _sumListeners { 0 };
|
int _sumListeners { 0 };
|
||||||
int _numStatFrames { 0 };
|
int _numStatFrames { 0 };
|
||||||
|
int _numTightLoopFrames { 0 };
|
||||||
int _sumIdentityPackets { 0 };
|
int _sumIdentityPackets { 0 };
|
||||||
|
|
||||||
float _maxKbpsPerNode = 0.0f;
|
float _maxKbpsPerNode = 0.0f;
|
||||||
|
@ -68,11 +77,40 @@ private:
|
||||||
float _domainMinimumScale { MIN_AVATAR_SCALE };
|
float _domainMinimumScale { MIN_AVATAR_SCALE };
|
||||||
float _domainMaximumScale { MAX_AVATAR_SCALE };
|
float _domainMaximumScale { MAX_AVATAR_SCALE };
|
||||||
|
|
||||||
QTimer* _broadcastTimer = nullptr;
|
|
||||||
|
|
||||||
RateCounter<> _broadcastRate;
|
RateCounter<> _broadcastRate;
|
||||||
p_high_resolution_clock::time_point _lastDebugMessage;
|
p_high_resolution_clock::time_point _lastDebugMessage;
|
||||||
QHash<QString, QPair<int, int>> _sessionDisplayNames;
|
QHash<QString, QPair<int, int>> _sessionDisplayNames;
|
||||||
|
|
||||||
|
quint64 _displayNameManagementElapsedTime { 0 }; // total time spent in broadcastAvatarData/display name management... since last stats window
|
||||||
|
quint64 _ignoreCalculationElapsedTime { 0 };
|
||||||
|
quint64 _avatarDataPackingElapsedTime { 0 };
|
||||||
|
quint64 _packetSendingElapsedTime { 0 };
|
||||||
|
|
||||||
|
quint64 _broadcastAvatarDataElapsedTime { 0 }; // total time spent in broadcastAvatarData since last stats window
|
||||||
|
quint64 _broadcastAvatarDataInner { 0 };
|
||||||
|
quint64 _broadcastAvatarDataLockWait { 0 };
|
||||||
|
quint64 _broadcastAvatarDataNodeTransform { 0 };
|
||||||
|
quint64 _broadcastAvatarDataNodeFunctor { 0 };
|
||||||
|
|
||||||
|
quint64 _handleViewFrustumPacketElapsedTime { 0 };
|
||||||
|
quint64 _handleAvatarIdentityPacketElapsedTime { 0 };
|
||||||
|
quint64 _handleKillAvatarPacketElapsedTime { 0 };
|
||||||
|
quint64 _handleNodeIgnoreRequestPacketElapsedTime { 0 };
|
||||||
|
quint64 _handleRadiusIgnoreRequestPacketElapsedTime { 0 };
|
||||||
|
quint64 _handleRequestsDomainListDataPacketElapsedTime { 0 };
|
||||||
|
quint64 _processQueuedAvatarDataPacketsElapsedTime { 0 };
|
||||||
|
quint64 _processQueuedAvatarDataPacketsLockWaitElapsedTime { 0 };
|
||||||
|
|
||||||
|
quint64 _processEventsElapsedTime { 0 };
|
||||||
|
quint64 _sendStatsElapsedTime { 0 };
|
||||||
|
quint64 _queueIncomingPacketElapsedTime { 0 };
|
||||||
|
quint64 _lastStatsTime { usecTimestampNow() };
|
||||||
|
|
||||||
|
RateCounter<> _loopRate; // this is the rate that the main thread tight loop runs
|
||||||
|
|
||||||
|
|
||||||
|
AvatarMixerSlavePool _slavePool;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AvatarMixer_h
|
#endif // hifi_AvatarMixer_h
|
||||||
|
|
|
@ -16,10 +16,52 @@
|
||||||
|
|
||||||
#include "AvatarMixerClientData.h"
|
#include "AvatarMixerClientData.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AvatarMixerClientData::queuePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node) {
|
||||||
|
if (!_packetQueue.node) {
|
||||||
|
_packetQueue.node = node;
|
||||||
|
}
|
||||||
|
_packetQueue.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
int AvatarMixerClientData::processPackets() {
|
||||||
|
int packetsProcessed = 0;
|
||||||
|
SharedNodePointer node = _packetQueue.node;
|
||||||
|
assert(_packetQueue.empty() || node);
|
||||||
|
_packetQueue.node.clear();
|
||||||
|
|
||||||
|
while (!_packetQueue.empty()) {
|
||||||
|
auto& packet = _packetQueue.front();
|
||||||
|
|
||||||
|
packetsProcessed++;
|
||||||
|
|
||||||
|
switch (packet->getType()) {
|
||||||
|
case PacketType::AvatarData:
|
||||||
|
parseData(*packet);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
_packetQueue.pop();
|
||||||
|
}
|
||||||
|
assert(_packetQueue.empty());
|
||||||
|
|
||||||
|
return packetsProcessed;
|
||||||
|
}
|
||||||
|
|
||||||
int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
||||||
|
|
||||||
// pull the sequence number from the data first
|
// pull the sequence number from the data first
|
||||||
message.readPrimitive(&_lastReceivedSequenceNumber);
|
uint16_t sequenceNumber;
|
||||||
|
|
||||||
|
message.readPrimitive(&sequenceNumber);
|
||||||
|
|
||||||
|
if (sequenceNumber < _lastReceivedSequenceNumber && _lastReceivedSequenceNumber != UINT16_MAX) {
|
||||||
|
incrementNumOutOfOrderSends();
|
||||||
|
}
|
||||||
|
_lastReceivedSequenceNumber = sequenceNumber;
|
||||||
|
|
||||||
// compute the offset to the data payload
|
// compute the offset to the data payload
|
||||||
return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead()));
|
return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
|
@ -41,6 +42,7 @@ public:
|
||||||
|
|
||||||
int parseData(ReceivedMessage& message) override;
|
int parseData(ReceivedMessage& message) override;
|
||||||
AvatarData& getAvatar() { return *_avatar; }
|
AvatarData& getAvatar() { return *_avatar; }
|
||||||
|
const AvatarData* getConstAvatarData() const { return _avatar.get(); }
|
||||||
|
|
||||||
bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid);
|
bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid);
|
||||||
|
|
||||||
|
@ -85,9 +87,9 @@ public:
|
||||||
|
|
||||||
void loadJSONStats(QJsonObject& jsonObject) const;
|
void loadJSONStats(QJsonObject& jsonObject) const;
|
||||||
|
|
||||||
glm::vec3 getPosition() { return _avatar ? _avatar->getPosition() : glm::vec3(0); }
|
glm::vec3 getPosition() const { return _avatar ? _avatar->getPosition() : glm::vec3(0); }
|
||||||
glm::vec3 getGlobalBoundingBoxCorner() { return _avatar ? _avatar->getGlobalBoundingBoxCorner() : glm::vec3(0); }
|
glm::vec3 getGlobalBoundingBoxCorner() const { return _avatar ? _avatar->getGlobalBoundingBoxCorner() : glm::vec3(0); }
|
||||||
bool isRadiusIgnoring(const QUuid& other) { return _radiusIgnoredOthers.find(other) != _radiusIgnoredOthers.end(); }
|
bool isRadiusIgnoring(const QUuid& other) const { return _radiusIgnoredOthers.find(other) != _radiusIgnoredOthers.end(); }
|
||||||
void addToRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.insert(other); }
|
void addToRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.insert(other); }
|
||||||
void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other);
|
void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other);
|
||||||
void ignoreOther(SharedNodePointer self, SharedNodePointer other);
|
void ignoreOther(SharedNodePointer self, SharedNodePointer other);
|
||||||
|
@ -118,9 +120,15 @@ public:
|
||||||
return _lastOtherAvatarSentJoints[otherAvatar];
|
return _lastOtherAvatarSentJoints[otherAvatar];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void queuePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node);
|
||||||
|
int processPackets(); // returns number of packets processed
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct PacketQueue : public std::queue<QSharedPointer<ReceivedMessage>> {
|
||||||
|
QWeakPointer<Node> node;
|
||||||
|
};
|
||||||
|
PacketQueue _packetQueue;
|
||||||
|
|
||||||
AvatarSharedPointer _avatar { new AvatarData() };
|
AvatarSharedPointer _avatar { new AvatarData() };
|
||||||
|
|
||||||
uint16_t _lastReceivedSequenceNumber { 0 };
|
uint16_t _lastReceivedSequenceNumber { 0 };
|
||||||
|
|
443
assignment-client/src/avatars/AvatarMixerSlave.cpp
Normal file
|
@ -0,0 +1,443 @@
|
||||||
|
//
|
||||||
|
// AvatarMixerSlave.cpp
|
||||||
|
// assignment-client/src/avatar
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 2/14/2017.
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
#include <glm/gtx/vector_angle.hpp>
|
||||||
|
|
||||||
|
#include <AvatarLogging.h>
|
||||||
|
#include <LogHandler.h>
|
||||||
|
#include <NetworkAccessManager.h>
|
||||||
|
#include <NodeList.h>
|
||||||
|
#include <Node.h>
|
||||||
|
#include <OctreeConstants.h>
|
||||||
|
#include <udt/PacketHeaders.h>
|
||||||
|
#include <SharedUtil.h>
|
||||||
|
#include <StDev.h>
|
||||||
|
#include <UUID.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "AvatarMixer.h"
|
||||||
|
#include "AvatarMixerClientData.h"
|
||||||
|
#include "AvatarMixerSlave.h"
|
||||||
|
|
||||||
|
|
||||||
|
void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
|
||||||
|
_begin = begin;
|
||||||
|
_end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMixerSlave::configureBroadcast(ConstIter begin, ConstIter end,
|
||||||
|
p_high_resolution_clock::time_point lastFrameTimestamp,
|
||||||
|
float maxKbpsPerNode, float throttlingRatio) {
|
||||||
|
_begin = begin;
|
||||||
|
_end = end;
|
||||||
|
_lastFrameTimestamp = lastFrameTimestamp;
|
||||||
|
_maxKbpsPerNode = maxKbpsPerNode;
|
||||||
|
_throttlingRatio = throttlingRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMixerSlave::harvestStats(AvatarMixerSlaveStats& stats) {
|
||||||
|
stats = _stats;
|
||||||
|
_stats.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) {
|
||||||
|
auto start = usecTimestampNow();
|
||||||
|
auto nodeData = dynamic_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||||
|
if (nodeData) {
|
||||||
|
_stats.nodesProcessed++;
|
||||||
|
_stats.packetsProcessed += nodeData->processPackets();
|
||||||
|
}
|
||||||
|
auto end = usecTimestampNow();
|
||||||
|
_stats.processIncomingPacketsElapsedTime += (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) {
|
||||||
|
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray();
|
||||||
|
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
|
||||||
|
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122());
|
||||||
|
identityPacket->write(individualData);
|
||||||
|
DependencyManager::get<NodeList>()->sendPacket(std::move(identityPacket), *destinationNode);
|
||||||
|
_stats.numIdentityPackets++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
|
||||||
|
|
||||||
|
// only send extra avatar data (avatars out of view, ignored) every Nth AvatarData frame
|
||||||
|
// Extra avatar data will be sent (AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND/EXTRA_AVATAR_DATA_FRAME_RATIO) times
|
||||||
|
// per second.
|
||||||
|
// This value should be a power of two for performance purposes, as the mixer performs a modulo operation every frame
|
||||||
|
// to determine whether the extra data should be sent.
|
||||||
|
static const int EXTRA_AVATAR_DATA_FRAME_RATIO = 16;
|
||||||
|
|
||||||
|
// FIXME - There is some old logic (unchanged as of 2/17/17) that randomly decides to send an identity
|
||||||
|
// packet. That logic had the following comment about the constants it uses...
|
||||||
|
//
|
||||||
|
// An 80% chance of sending a identity packet within a 5 second interval.
|
||||||
|
// assuming 60 htz update rate.
|
||||||
|
//
|
||||||
|
// Assuming the calculation of the constant is in fact correct for 80% and 60hz and 5 seconds (an assumption
|
||||||
|
// that I have not verified) then the constant is definitely wrong now, since we send at 45hz.
|
||||||
|
const float IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f;
|
||||||
|
|
||||||
|
void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
|
||||||
|
quint64 start = usecTimestampNow();
|
||||||
|
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
// setup for distributed random floating point values
|
||||||
|
std::random_device randomDevice;
|
||||||
|
std::mt19937 generator(randomDevice());
|
||||||
|
std::uniform_real_distribution<float> distribution;
|
||||||
|
|
||||||
|
if (node->getLinkedData() && (node->getType() == NodeType::Agent) && node->getActiveSocket()) {
|
||||||
|
_stats.nodesBroadcastedTo++;
|
||||||
|
|
||||||
|
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||||
|
|
||||||
|
nodeData->resetInViewStats();
|
||||||
|
|
||||||
|
const AvatarData& avatar = nodeData->getAvatar();
|
||||||
|
glm::vec3 myPosition = avatar.getClientGlobalPosition();
|
||||||
|
|
||||||
|
// reset the internal state for correct random number distribution
|
||||||
|
distribution.reset();
|
||||||
|
|
||||||
|
// reset the max distance for this frame
|
||||||
|
float maxAvatarDistanceThisFrame = 0.0f;
|
||||||
|
|
||||||
|
// reset the number of sent avatars
|
||||||
|
nodeData->resetNumAvatarsSentLastFrame();
|
||||||
|
|
||||||
|
// keep a counter of the number of considered avatars
|
||||||
|
int numOtherAvatars = 0;
|
||||||
|
|
||||||
|
// keep track of outbound data rate specifically for avatar data
|
||||||
|
int numAvatarDataBytes = 0;
|
||||||
|
|
||||||
|
// keep track of the number of other avatars held back in this frame
|
||||||
|
int numAvatarsHeldBack = 0;
|
||||||
|
|
||||||
|
// keep track of the number of other avatar frames skipped
|
||||||
|
int numAvatarsWithSkippedFrames = 0;
|
||||||
|
|
||||||
|
// use the data rate specifically for avatar data for FRD adjustment checks
|
||||||
|
float avatarDataRateLastSecond = nodeData->getOutboundAvatarDataKbps();
|
||||||
|
|
||||||
|
// When this is true, the AvatarMixer will send Avatar data to a client about avatars that are not in the view frustrum
|
||||||
|
bool getsOutOfView = nodeData->getRequestsDomainListData();
|
||||||
|
|
||||||
|
// When this is true, the AvatarMixer will send Avatar data to a client about avatars that they've ignored
|
||||||
|
bool getsIgnoredByMe = getsOutOfView;
|
||||||
|
|
||||||
|
// When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them
|
||||||
|
bool getsAnyIgnored = getsIgnoredByMe && node->getCanKick();
|
||||||
|
|
||||||
|
// Check if it is time to adjust what we send this client based on the observed
|
||||||
|
// bandwidth to this node. We do this once a second, which is also the window for
|
||||||
|
// the bandwidth reported by node->getOutboundBandwidth();
|
||||||
|
if (nodeData->getNumFramesSinceFRDAdjustment() > AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) {
|
||||||
|
|
||||||
|
const float FRD_ADJUSTMENT_ACCEPTABLE_RATIO = 0.8f;
|
||||||
|
const float HYSTERISIS_GAP = (1 - FRD_ADJUSTMENT_ACCEPTABLE_RATIO);
|
||||||
|
const float HYSTERISIS_MIDDLE_PERCENTAGE = (1 - (HYSTERISIS_GAP * 0.5f));
|
||||||
|
|
||||||
|
// get the current full rate distance so we can work with it
|
||||||
|
float currentFullRateDistance = nodeData->getFullRateDistance();
|
||||||
|
|
||||||
|
if (avatarDataRateLastSecond > _maxKbpsPerNode) {
|
||||||
|
|
||||||
|
// is the FRD greater than the farthest avatar?
|
||||||
|
// if so, before we calculate anything, set it to that distance
|
||||||
|
currentFullRateDistance = std::min(currentFullRateDistance, nodeData->getMaxAvatarDistance());
|
||||||
|
|
||||||
|
// we're adjusting the full rate distance to target a bandwidth in the middle
|
||||||
|
// of the hysterisis gap
|
||||||
|
currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond;
|
||||||
|
|
||||||
|
nodeData->setFullRateDistance(currentFullRateDistance);
|
||||||
|
nodeData->resetNumFramesSinceFRDAdjustment();
|
||||||
|
} else if (currentFullRateDistance < nodeData->getMaxAvatarDistance()
|
||||||
|
&& avatarDataRateLastSecond < _maxKbpsPerNode * FRD_ADJUSTMENT_ACCEPTABLE_RATIO) {
|
||||||
|
// we are constrained AND we've recovered to below the acceptable ratio
|
||||||
|
// lets adjust the full rate distance to target a bandwidth in the middle of the hyterisis gap
|
||||||
|
currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond;
|
||||||
|
|
||||||
|
nodeData->setFullRateDistance(currentFullRateDistance);
|
||||||
|
nodeData->resetNumFramesSinceFRDAdjustment();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nodeData->incrementNumFramesSinceFRDAdjustment();
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup a PacketList for the avatarPackets
|
||||||
|
auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData);
|
||||||
|
|
||||||
|
// this is an AGENT we have received head data from
|
||||||
|
// send back a packet with other active node data to this node
|
||||||
|
std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) {
|
||||||
|
|
||||||
|
bool shouldConsider = false;
|
||||||
|
quint64 startIgnoreCalculation = usecTimestampNow();
|
||||||
|
|
||||||
|
// make sure we have data for this avatar, that it isn't the same node,
|
||||||
|
// and isn't an avatar that the viewing node has ignored
|
||||||
|
// or that has ignored the viewing node
|
||||||
|
if (!otherNode->getLinkedData()
|
||||||
|
|| otherNode->getUUID() == node->getUUID()
|
||||||
|
|| (node->isIgnoringNodeWithID(otherNode->getUUID()) && !getsIgnoredByMe)
|
||||||
|
|| (otherNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) {
|
||||||
|
|
||||||
|
shouldConsider = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const AvatarMixerClientData* otherData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||||
|
|
||||||
|
shouldConsider = true; // assume we will consider...
|
||||||
|
|
||||||
|
// Check to see if the space bubble is enabled
|
||||||
|
if (node->isIgnoreRadiusEnabled() || otherNode->isIgnoreRadiusEnabled()) {
|
||||||
|
// Define the minimum bubble size
|
||||||
|
static const glm::vec3 minBubbleSize = glm::vec3(0.3f, 1.3f, 0.3f);
|
||||||
|
// Define the scale of the box for the current node
|
||||||
|
glm::vec3 nodeBoxScale = (nodeData->getPosition() - nodeData->getGlobalBoundingBoxCorner()) * 2.0f;
|
||||||
|
// Define the scale of the box for the current other node
|
||||||
|
glm::vec3 otherNodeBoxScale = (otherData->getPosition() - otherData->getGlobalBoundingBoxCorner()) * 2.0f;
|
||||||
|
|
||||||
|
// Set up the bounding box for the current node
|
||||||
|
AABox nodeBox(nodeData->getGlobalBoundingBoxCorner(), nodeBoxScale);
|
||||||
|
// Clamp the size of the bounding box to a minimum scale
|
||||||
|
if (glm::any(glm::lessThan(nodeBoxScale, minBubbleSize))) {
|
||||||
|
nodeBox.setScaleStayCentered(minBubbleSize);
|
||||||
|
}
|
||||||
|
// Set up the bounding box for the current other node
|
||||||
|
AABox otherNodeBox(otherData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
|
||||||
|
// Clamp the size of the bounding box to a minimum scale
|
||||||
|
if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) {
|
||||||
|
otherNodeBox.setScaleStayCentered(minBubbleSize);
|
||||||
|
}
|
||||||
|
// Quadruple the scale of both bounding boxes
|
||||||
|
nodeBox.embiggen(4.0f);
|
||||||
|
otherNodeBox.embiggen(4.0f);
|
||||||
|
|
||||||
|
// Perform the collision check between the two bounding boxes
|
||||||
|
if (nodeBox.touches(otherNodeBox)) {
|
||||||
|
nodeData->ignoreOther(node, otherNode);
|
||||||
|
shouldConsider = getsAnyIgnored;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Not close enough to ignore
|
||||||
|
if (shouldConsider) {
|
||||||
|
nodeData->removeFromRadiusIgnoringSet(node, otherNode->getUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 endIgnoreCalculation = usecTimestampNow();
|
||||||
|
_stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldConsider) {
|
||||||
|
quint64 startAvatarDataPacking = usecTimestampNow();
|
||||||
|
|
||||||
|
++numOtherAvatars;
|
||||||
|
|
||||||
|
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||||
|
|
||||||
|
// make sure we send out identity packets to and from new arrivals.
|
||||||
|
bool forceSend = !nodeData->checkAndSetHasReceivedFirstPacketsFrom(otherNode->getUUID());
|
||||||
|
|
||||||
|
// FIXME - this clause seems suspicious "... || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp ..."
|
||||||
|
if (otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0
|
||||||
|
&& (forceSend
|
||||||
|
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|
||||||
|
|| distribution(generator) < IDENTITY_SEND_PROBABILITY)) {
|
||||||
|
|
||||||
|
sendIdentityPacket(otherNodeData, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
const AvatarData* otherAvatar = otherNodeData->getConstAvatarData();
|
||||||
|
// Decide whether to send this avatar's data based on it's distance from us
|
||||||
|
|
||||||
|
// The full rate distance is the distance at which EVERY update will be sent for this avatar
|
||||||
|
// at twice the full rate distance, there will be a 50% chance of sending this avatar's update
|
||||||
|
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition();
|
||||||
|
float distanceToAvatar = glm::length(myPosition - otherPosition);
|
||||||
|
|
||||||
|
// potentially update the max full rate distance for this frame
|
||||||
|
maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar);
|
||||||
|
|
||||||
|
// This code handles the random dropping of avatar data based on the ratio of
|
||||||
|
// "getFullRateDistance" to actual distance.
|
||||||
|
//
|
||||||
|
// NOTE: If the recieving node is in "PAL mode" then it's asked to get things even that
|
||||||
|
// are out of view, this also appears to disable this random distribution.
|
||||||
|
if (distanceToAvatar != 0.0f
|
||||||
|
&& !getsOutOfView
|
||||||
|
&& distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) {
|
||||||
|
|
||||||
|
quint64 endAvatarDataPacking = usecTimestampNow();
|
||||||
|
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
|
||||||
|
shouldConsider = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldConsider) {
|
||||||
|
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
|
||||||
|
AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber();
|
||||||
|
|
||||||
|
// FIXME - This code does appear to be working. But it seems brittle.
|
||||||
|
// It supports determining if the frame of data for this "other"
|
||||||
|
// avatar has already been sent to the reciever. This has been
|
||||||
|
// verified to work on a desktop display that renders at 60hz and
|
||||||
|
// therefore sends to mixer at 30hz. Each second you'd expect to
|
||||||
|
// have 15 (45hz-30hz) duplicate frames. In this case, the stat
|
||||||
|
// avg_other_av_skips_per_second does report 15.
|
||||||
|
//
|
||||||
|
// make sure we haven't already sent this data from this sender to this receiver
|
||||||
|
// or that somehow we haven't sent
|
||||||
|
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
|
||||||
|
++numAvatarsHeldBack;
|
||||||
|
|
||||||
|
quint64 endAvatarDataPacking = usecTimestampNow();
|
||||||
|
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
|
||||||
|
shouldConsider = false;
|
||||||
|
} else if (lastSeqFromSender - lastSeqToReceiver > 1) {
|
||||||
|
// this is a skip - we still send the packet but capture the presence of the skip so we see it happening
|
||||||
|
++numAvatarsWithSkippedFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're going to send this avatar
|
||||||
|
if (shouldConsider) {
|
||||||
|
|
||||||
|
// determine if avatar is in view, to determine how much data to include...
|
||||||
|
glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f;
|
||||||
|
AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
|
||||||
|
bool isInView = nodeData->otherAvatarInView(otherNodeBox);
|
||||||
|
|
||||||
|
// this throttles the extra data to only be sent every Nth message
|
||||||
|
if (!isInView && !getsOutOfView && (lastSeqToReceiver % EXTRA_AVATAR_DATA_FRAME_RATIO > 0)) {
|
||||||
|
quint64 endAvatarDataPacking = usecTimestampNow();
|
||||||
|
|
||||||
|
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
|
||||||
|
shouldConsider = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldConsider) {
|
||||||
|
// start a new segment in the PacketList for this avatar
|
||||||
|
avatarPacketList->startSegment();
|
||||||
|
|
||||||
|
AvatarData::AvatarDataDetail detail;
|
||||||
|
if (!isInView && !getsOutOfView) {
|
||||||
|
detail = AvatarData::MinimumData;
|
||||||
|
nodeData->incrementAvatarOutOfView();
|
||||||
|
} else {
|
||||||
|
detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO
|
||||||
|
? AvatarData::SendAllData : AvatarData::CullSmallData;
|
||||||
|
nodeData->incrementAvatarInView();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
bool includeThisAvatar = true;
|
||||||
|
auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID());
|
||||||
|
QVector<JointData>& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID());
|
||||||
|
bool distanceAdjust = true;
|
||||||
|
glm::vec3 viewerPosition = myPosition;
|
||||||
|
AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray
|
||||||
|
bool dropFaceTracking = false;
|
||||||
|
|
||||||
|
quint64 start = usecTimestampNow();
|
||||||
|
QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
|
||||||
|
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther);
|
||||||
|
quint64 end = usecTimestampNow();
|
||||||
|
_stats.toByteArrayElapsedTime += (end - start);
|
||||||
|
|
||||||
|
static const int MAX_ALLOWED_AVATAR_DATA = (1400 - NUM_BYTES_RFC4122_UUID);
|
||||||
|
if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) {
|
||||||
|
qCWarning(avatars) << "otherAvatar.toByteArray() resulted in very large buffer:" << bytes.size() << "... attempt to drop facial data";
|
||||||
|
|
||||||
|
dropFaceTracking = true; // first try dropping the facial data
|
||||||
|
bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
|
||||||
|
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther);
|
||||||
|
|
||||||
|
if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) {
|
||||||
|
qCWarning(avatars) << "otherAvatar.toByteArray() without facial data resulted in very large buffer:" << bytes.size() << "... reduce to MinimumData";
|
||||||
|
bytes = otherAvatar->toByteArray(AvatarData::MinimumData, lastEncodeForOther, lastSentJointsForOther,
|
||||||
|
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) {
|
||||||
|
qCWarning(avatars) << "otherAvatar.toByteArray() MinimumData resulted in very large buffer:" << bytes.size() << "... FAIL!!";
|
||||||
|
includeThisAvatar = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeThisAvatar) {
|
||||||
|
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122());
|
||||||
|
numAvatarDataBytes += avatarPacketList->write(bytes);
|
||||||
|
_stats.numOthersIncluded++;
|
||||||
|
|
||||||
|
// increment the number of avatars sent to this reciever
|
||||||
|
nodeData->incrementNumAvatarsSentLastFrame();
|
||||||
|
|
||||||
|
// set the last sent sequence number for this sender on the receiver
|
||||||
|
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
|
||||||
|
otherNodeData->getLastReceivedSequenceNumber());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
avatarPacketList->endSegment();
|
||||||
|
|
||||||
|
quint64 endAvatarDataPacking = usecTimestampNow();
|
||||||
|
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quint64 startPacketSending = usecTimestampNow();
|
||||||
|
|
||||||
|
// close the current packet so that we're always sending something
|
||||||
|
avatarPacketList->closeCurrentPacket(true);
|
||||||
|
|
||||||
|
_stats.numPacketsSent += (int)avatarPacketList->getNumPackets();
|
||||||
|
_stats.numBytesSent += numAvatarDataBytes;
|
||||||
|
|
||||||
|
// send the avatar data PacketList
|
||||||
|
nodeList->sendPacketList(std::move(avatarPacketList), *node);
|
||||||
|
|
||||||
|
// record the bytes sent for other avatar data in the AvatarMixerClientData
|
||||||
|
nodeData->recordSentAvatarData(numAvatarDataBytes);
|
||||||
|
|
||||||
|
// record the number of avatars held back this frame
|
||||||
|
nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack);
|
||||||
|
nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames);
|
||||||
|
|
||||||
|
if (numOtherAvatars == 0) {
|
||||||
|
// update the full rate distance to FLOAT_MAX since we didn't have any other avatars to send
|
||||||
|
nodeData->setMaxAvatarDistance(FLT_MAX);
|
||||||
|
} else {
|
||||||
|
nodeData->setMaxAvatarDistance(maxAvatarDistanceThisFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 endPacketSending = usecTimestampNow();
|
||||||
|
_stats.packetSendingElapsedTime += (endPacketSending - startPacketSending);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 end = usecTimestampNow();
|
||||||
|
_stats.jobElapsedTime += (end - start);
|
||||||
|
}
|
||||||
|
|
101
assignment-client/src/avatars/AvatarMixerSlave.h
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
//
|
||||||
|
// AvatarMixerSlave.h
|
||||||
|
// assignment-client/src/avatar
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 2/14/2017.
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AvatarMixerSlave_h
|
||||||
|
#define hifi_AvatarMixerSlave_h
|
||||||
|
|
||||||
|
class AvatarMixerClientData;
|
||||||
|
|
||||||
|
class AvatarMixerSlaveStats {
|
||||||
|
public:
|
||||||
|
int nodesProcessed { 0 };
|
||||||
|
int packetsProcessed { 0 };
|
||||||
|
quint64 processIncomingPacketsElapsedTime { 0 };
|
||||||
|
|
||||||
|
int nodesBroadcastedTo { 0 };
|
||||||
|
int numPacketsSent { 0 };
|
||||||
|
int numBytesSent { 0 };
|
||||||
|
int numIdentityPackets { 0 };
|
||||||
|
int numOthersIncluded { 0 };
|
||||||
|
quint64 ignoreCalculationElapsedTime { 0 };
|
||||||
|
quint64 avatarDataPackingElapsedTime { 0 };
|
||||||
|
quint64 packetSendingElapsedTime { 0 };
|
||||||
|
quint64 toByteArrayElapsedTime { 0 };
|
||||||
|
quint64 jobElapsedTime { 0 };
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
// receiving job stats
|
||||||
|
nodesProcessed = 0;
|
||||||
|
packetsProcessed = 0;
|
||||||
|
processIncomingPacketsElapsedTime = 0;
|
||||||
|
|
||||||
|
// sending job stats
|
||||||
|
nodesBroadcastedTo = 0;
|
||||||
|
numPacketsSent = 0;
|
||||||
|
numBytesSent = 0;
|
||||||
|
numIdentityPackets = 0;
|
||||||
|
numOthersIncluded = 0;
|
||||||
|
ignoreCalculationElapsedTime = 0;
|
||||||
|
avatarDataPackingElapsedTime = 0;
|
||||||
|
packetSendingElapsedTime = 0;
|
||||||
|
toByteArrayElapsedTime = 0;
|
||||||
|
jobElapsedTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarMixerSlaveStats& operator+=(const AvatarMixerSlaveStats& rhs) {
|
||||||
|
nodesProcessed += rhs.nodesProcessed;
|
||||||
|
packetsProcessed += rhs.packetsProcessed;
|
||||||
|
processIncomingPacketsElapsedTime += rhs.processIncomingPacketsElapsedTime;
|
||||||
|
|
||||||
|
nodesBroadcastedTo += rhs.nodesBroadcastedTo;
|
||||||
|
numPacketsSent += rhs.numPacketsSent;
|
||||||
|
numBytesSent += rhs.numBytesSent;
|
||||||
|
numIdentityPackets += rhs.numIdentityPackets;
|
||||||
|
numOthersIncluded += rhs.numOthersIncluded;
|
||||||
|
ignoreCalculationElapsedTime += rhs.ignoreCalculationElapsedTime;
|
||||||
|
avatarDataPackingElapsedTime += rhs.avatarDataPackingElapsedTime;
|
||||||
|
packetSendingElapsedTime += rhs.packetSendingElapsedTime;
|
||||||
|
toByteArrayElapsedTime += rhs.toByteArrayElapsedTime;
|
||||||
|
jobElapsedTime += rhs.jobElapsedTime;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class AvatarMixerSlave {
|
||||||
|
public:
|
||||||
|
using ConstIter = NodeList::const_iterator;
|
||||||
|
|
||||||
|
void configure(ConstIter begin, ConstIter end);
|
||||||
|
void configureBroadcast(ConstIter begin, ConstIter end,
|
||||||
|
p_high_resolution_clock::time_point lastFrameTimestamp,
|
||||||
|
float maxKbpsPerNode, float throttlingRatio);
|
||||||
|
|
||||||
|
void processIncomingPackets(const SharedNodePointer& node);
|
||||||
|
void broadcastAvatarData(const SharedNodePointer& node);
|
||||||
|
|
||||||
|
void harvestStats(AvatarMixerSlaveStats& stats);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode);
|
||||||
|
|
||||||
|
// frame state
|
||||||
|
ConstIter _begin;
|
||||||
|
ConstIter _end;
|
||||||
|
|
||||||
|
p_high_resolution_clock::time_point _lastFrameTimestamp;
|
||||||
|
float _maxKbpsPerNode { 0.0f };
|
||||||
|
float _throttlingRatio { 0.0f };
|
||||||
|
|
||||||
|
AvatarMixerSlaveStats _stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AvatarMixerSlave_h
|
208
assignment-client/src/avatars/AvatarMixerSlavePool.cpp
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
//
|
||||||
|
// AvatarMixerSlavePool.cpp
|
||||||
|
// assignment-client/src/avatar
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 2/14/2017.
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "AvatarMixerSlavePool.h"
|
||||||
|
|
||||||
|
void AvatarMixerSlaveThread::run() {
|
||||||
|
while (true) {
|
||||||
|
wait();
|
||||||
|
|
||||||
|
// iterate over all available nodes
|
||||||
|
SharedNodePointer node;
|
||||||
|
while (try_pop(node)) {
|
||||||
|
(this->*_function)(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stopping = _stop;
|
||||||
|
notify(stopping);
|
||||||
|
if (stopping) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMixerSlaveThread::wait() {
|
||||||
|
{
|
||||||
|
Lock lock(_pool._mutex);
|
||||||
|
_pool._slaveCondition.wait(lock, [&] {
|
||||||
|
assert(_pool._numStarted <= _pool._numThreads);
|
||||||
|
return _pool._numStarted != _pool._numThreads;
|
||||||
|
});
|
||||||
|
++_pool._numStarted;
|
||||||
|
}
|
||||||
|
if (_pool._configure) {
|
||||||
|
_pool._configure(*this);
|
||||||
|
}
|
||||||
|
_function = _pool._function;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMixerSlaveThread::notify(bool stopping) {
|
||||||
|
{
|
||||||
|
Lock lock(_pool._mutex);
|
||||||
|
assert(_pool._numFinished < _pool._numThreads);
|
||||||
|
++_pool._numFinished;
|
||||||
|
if (stopping) {
|
||||||
|
++_pool._numStopped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_pool._poolCondition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvatarMixerSlaveThread::try_pop(SharedNodePointer& node) {
|
||||||
|
return _pool._queue.try_pop(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AVATAR_SINGLE_THREADED
|
||||||
|
static AvatarMixerSlave slave;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void AvatarMixerSlavePool::processIncomingPackets(ConstIter begin, ConstIter end) {
|
||||||
|
_function = &AvatarMixerSlave::processIncomingPackets;
|
||||||
|
_configure = [&](AvatarMixerSlave& slave) {
|
||||||
|
slave.configure(begin, end);
|
||||||
|
};
|
||||||
|
run(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMixerSlavePool::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);
|
||||||
|
};
|
||||||
|
run(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMixerSlavePool::run(ConstIter begin, ConstIter end) {
|
||||||
|
_begin = begin;
|
||||||
|
_end = end;
|
||||||
|
|
||||||
|
#ifdef AUDIO_SINGLE_THREADED
|
||||||
|
_configure(slave);
|
||||||
|
std::for_each(begin, end, [&](const SharedNodePointer& node) {
|
||||||
|
_function(slave, node);
|
||||||
|
});
|
||||||
|
#else
|
||||||
|
// fill the queue
|
||||||
|
std::for_each(_begin, _end, [&](const SharedNodePointer& node) {
|
||||||
|
_queue.emplace(node);
|
||||||
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
Lock lock(_mutex);
|
||||||
|
|
||||||
|
// run
|
||||||
|
_numStarted = _numFinished = 0;
|
||||||
|
_slaveCondition.notify_all();
|
||||||
|
|
||||||
|
// wait
|
||||||
|
_poolCondition.wait(lock, [&] {
|
||||||
|
assert(_numFinished <= _numThreads);
|
||||||
|
return _numFinished == _numThreads;
|
||||||
|
});
|
||||||
|
|
||||||
|
assert(_numStarted == _numThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(_queue.empty());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AvatarMixerSlavePool::each(std::function<void(AvatarMixerSlave& slave)> functor) {
|
||||||
|
#ifdef AVATAR_SINGLE_THREADED
|
||||||
|
functor(slave);
|
||||||
|
#else
|
||||||
|
for (auto& slave : _slaves) {
|
||||||
|
functor(*slave.get());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMixerSlavePool::setNumThreads(int numThreads) {
|
||||||
|
// clamp to allowed size
|
||||||
|
{
|
||||||
|
int maxThreads = QThread::idealThreadCount();
|
||||||
|
if (maxThreads == -1) {
|
||||||
|
// idealThreadCount returns -1 if cores cannot be detected
|
||||||
|
static const int MAX_THREADS_IF_UNKNOWN = 4;
|
||||||
|
maxThreads = MAX_THREADS_IF_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int clampedThreads = std::min(std::max(1, numThreads), maxThreads);
|
||||||
|
if (clampedThreads != numThreads) {
|
||||||
|
qWarning("%s: clamped to %d (was %d)", __FUNCTION__, clampedThreads, numThreads);
|
||||||
|
numThreads = clampedThreads;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(numThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarMixerSlavePool::resize(int numThreads) {
|
||||||
|
assert(_numThreads == (int)_slaves.size());
|
||||||
|
|
||||||
|
#ifdef AVATAR_SINGLE_THREADED
|
||||||
|
qDebug("%s: running single threaded", __FUNCTION__, numThreads);
|
||||||
|
#else
|
||||||
|
qDebug("%s: set %d threads (was %d)", __FUNCTION__, numThreads, _numThreads);
|
||||||
|
|
||||||
|
Lock lock(_mutex);
|
||||||
|
|
||||||
|
if (numThreads > _numThreads) {
|
||||||
|
// start new slaves
|
||||||
|
for (int i = 0; i < numThreads - _numThreads; ++i) {
|
||||||
|
auto slave = new AvatarMixerSlaveThread(*this);
|
||||||
|
slave->start();
|
||||||
|
_slaves.emplace_back(slave);
|
||||||
|
}
|
||||||
|
} else if (numThreads < _numThreads) {
|
||||||
|
auto extraBegin = _slaves.begin() + numThreads;
|
||||||
|
|
||||||
|
// mark slaves to stop...
|
||||||
|
auto slave = extraBegin;
|
||||||
|
while (slave != _slaves.end()) {
|
||||||
|
(*slave)->_stop = true;
|
||||||
|
++slave;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...cycle them until they do stop...
|
||||||
|
_numStopped = 0;
|
||||||
|
while (_numStopped != (_numThreads - numThreads)) {
|
||||||
|
_numStarted = _numFinished = _numStopped;
|
||||||
|
_slaveCondition.notify_all();
|
||||||
|
_poolCondition.wait(lock, [&] {
|
||||||
|
assert(_numFinished <= _numThreads);
|
||||||
|
return _numFinished == _numThreads;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...wait for threads to finish...
|
||||||
|
slave = extraBegin;
|
||||||
|
while (slave != _slaves.end()) {
|
||||||
|
QThread* thread = reinterpret_cast<QThread*>(slave->get());
|
||||||
|
static const int MAX_THREAD_WAIT_TIME = 10;
|
||||||
|
thread->wait(MAX_THREAD_WAIT_TIME);
|
||||||
|
++slave;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...and erase them
|
||||||
|
_slaves.erase(extraBegin, _slaves.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
_numThreads = _numStarted = _numFinished = numThreads;
|
||||||
|
assert(_numThreads == (int)_slaves.size());
|
||||||
|
#endif
|
||||||
|
}
|
104
assignment-client/src/avatars/AvatarMixerSlavePool.h
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
//
|
||||||
|
// AvatarMixerSlavePool.h
|
||||||
|
// assignment-client/src/avatar
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 2/14/2017.
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AvatarMixerSlavePool_h
|
||||||
|
#define hifi_AvatarMixerSlavePool_h
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <tbb/concurrent_queue.h>
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
#include <NodeList.h>
|
||||||
|
|
||||||
|
#include "AvatarMixerSlave.h"
|
||||||
|
|
||||||
|
class AvatarMixerSlavePool;
|
||||||
|
|
||||||
|
class AvatarMixerSlaveThread : public QThread, public AvatarMixerSlave {
|
||||||
|
Q_OBJECT
|
||||||
|
using ConstIter = NodeList::const_iterator;
|
||||||
|
using Mutex = std::mutex;
|
||||||
|
using Lock = std::unique_lock<Mutex>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AvatarMixerSlaveThread(AvatarMixerSlavePool& pool) : _pool(pool) {}
|
||||||
|
|
||||||
|
void run() override final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class AvatarMixerSlavePool;
|
||||||
|
|
||||||
|
void wait();
|
||||||
|
void notify(bool stopping);
|
||||||
|
bool try_pop(SharedNodePointer& node);
|
||||||
|
|
||||||
|
AvatarMixerSlavePool& _pool;
|
||||||
|
void (AvatarMixerSlave::*_function)(const SharedNodePointer& node) { nullptr };
|
||||||
|
bool _stop { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
// Slave pool for audio mixers
|
||||||
|
// AvatarMixerSlavePool is not thread-safe! It should be instantiated and used from a single thread.
|
||||||
|
class AvatarMixerSlavePool {
|
||||||
|
using Queue = tbb::concurrent_queue<SharedNodePointer>;
|
||||||
|
using Mutex = std::mutex;
|
||||||
|
using Lock = std::unique_lock<Mutex>;
|
||||||
|
using ConditionVariable = std::condition_variable;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using ConstIter = NodeList::const_iterator;
|
||||||
|
|
||||||
|
AvatarMixerSlavePool(int numThreads = QThread::idealThreadCount()) { setNumThreads(numThreads); }
|
||||||
|
~AvatarMixerSlavePool() { resize(0); }
|
||||||
|
|
||||||
|
// Jobs the slave 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);
|
||||||
|
|
||||||
|
void setNumThreads(int numThreads);
|
||||||
|
int numThreads() { return _numThreads; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void run(ConstIter begin, ConstIter end);
|
||||||
|
void resize(int numThreads);
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<AvatarMixerSlaveThread>> _slaves;
|
||||||
|
|
||||||
|
friend void AvatarMixerSlaveThread::wait();
|
||||||
|
friend void AvatarMixerSlaveThread::notify(bool stopping);
|
||||||
|
friend bool AvatarMixerSlaveThread::try_pop(SharedNodePointer& node);
|
||||||
|
|
||||||
|
// synchronization state
|
||||||
|
Mutex _mutex;
|
||||||
|
ConditionVariable _slaveCondition;
|
||||||
|
ConditionVariable _poolCondition;
|
||||||
|
void (AvatarMixerSlave::*_function)(const SharedNodePointer& node);
|
||||||
|
std::function<void(AvatarMixerSlave&)> _configure;
|
||||||
|
int _numThreads { 0 };
|
||||||
|
int _numStarted { 0 }; // guarded by _mutex
|
||||||
|
int _numFinished { 0 }; // guarded by _mutex
|
||||||
|
int _numStopped { 0 }; // guarded by _mutex
|
||||||
|
|
||||||
|
// frame state
|
||||||
|
Queue _queue;
|
||||||
|
ConstIter _begin;
|
||||||
|
ConstIter _end;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AvatarMixerSlavePool_h
|
|
@ -14,10 +14,9 @@
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include "ScriptableAvatar.h"
|
#include "ScriptableAvatar.h"
|
||||||
|
|
||||||
QByteArray ScriptableAvatar::toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector<JointData>& lastSentJointData,
|
QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail) {
|
||||||
bool distanceAdjust, glm::vec3 viewerPosition, QVector<JointData>* sentJointDataOut) {
|
|
||||||
_globalPosition = getPosition();
|
_globalPosition = getPosition();
|
||||||
return AvatarData::toByteArray(dataDetail, lastSentTime, lastSentJointData, distanceAdjust, viewerPosition, sentJointDataOut);
|
return AvatarData::toByteArrayStateful(dataDetail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,7 @@ public:
|
||||||
Q_INVOKABLE AnimationDetails getAnimationDetails();
|
Q_INVOKABLE AnimationDetails getAnimationDetails();
|
||||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||||
|
|
||||||
virtual QByteArray toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector<JointData>& lastSentJointData,
|
virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail) override;
|
||||||
bool distanceAdjust = false, glm::vec3 viewerPosition = glm::vec3(0), QVector<JointData>* sentJointDataOut = nullptr) override;
|
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
|
@ -1299,6 +1299,22 @@
|
||||||
"placeholder": 5.0,
|
"placeholder": 5.0,
|
||||||
"default": 5.0,
|
"default": 5.0,
|
||||||
"advanced": true
|
"advanced": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "auto_threads",
|
||||||
|
"label": "Automatically determine thread count",
|
||||||
|
"type": "checkbox",
|
||||||
|
"help": "Allow system to determine number of threads (recommended)",
|
||||||
|
"default": false,
|
||||||
|
"advanced": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "num_threads",
|
||||||
|
"label": "Number of Threads",
|
||||||
|
"help": "Threads to spin up for avatar mixing (if not automatically set)",
|
||||||
|
"placeholder": "1",
|
||||||
|
"default": "1",
|
||||||
|
"advanced": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
26
interface/resources/icons/create-icons/20-text-01.svg
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:none;stroke:#000000;stroke-width:18;stroke-miterlimit:10;}
|
||||||
|
.st1{fill:none;stroke:#000000;stroke-width:18;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
.st2{fill:none;stroke:#000000;stroke-width:19;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M220.3,378.4l-81.1-232c-1-2.8-3.6-4.3-6.6-4.3h-9.2c-3,0-5.6,1.5-6.6,4.3L34,378.5c-0.8,2.1-0.4,5.1,0.9,7
|
||||||
|
c1.3,1.9,3.4,3.5,5.7,3.5h15.9c3,0,5.6-2.6,6.6-5.4L90.2,305h73.7l27,78.6c1,2.8,3.6,5.4,6.6,5.4h16.2c2.3,0,4.4-1.8,5.7-3.6
|
||||||
|
C220.7,383.5,221.1,380.5,220.3,378.4z M100.3,276l25.6-73.1c0.6-1.7,1.3-3.3,1.9-5.1c0.6,1.8,1.2,3,1.7,4.5l24.9,73.7H100.3z"/>
|
||||||
|
<path d="M351.6,215.5c-9.7-11.4-24.3-17.2-43.2-17.2c-17.5,0-35.5,4.7-53.2,14.1c-3.1,1.7-4.6,5.4-3.3,8.7l5.2,13.6
|
||||||
|
c0.7,1.9,2.3,3.4,4.2,4.1c1.9,0.7,4.1,0.5,5.8-0.6c14.1-8.2,27.7-12.3,40.6-12.3c10.9,0,18.8,3.2,23.3,9.6c5.2,7.3,8,18.4,8,33
|
||||||
|
v4.6l-22.7,0.7c-25,0.6-44.7,6-58.2,16.1c-14.3,10.7-21.6,25.7-21.6,44.7c0,17.1,4.9,30.9,14.5,40.8c9.7,10,23.2,15.1,40.1,15.1
|
||||||
|
c12,0,22.8-2.7,32.2-8c5.6-3.1,11.1-7.7,16.4-13.6l1.6,13.3c0.4,3.5,3.4,6.8,6.9,6.8h10.2c3.9,0,7.6-4.4,7.6-8.3V265.9
|
||||||
|
C366,243.2,361.1,226.7,351.6,215.5z M265.1,335.2c0-11.2,3.5-19.1,10.7-24.1c8-5.6,22.1-9,42-10.1l19.1-0.9v9.6
|
||||||
|
c0,17.2-3.8,29.9-12,39.1c-8.1,9-19,13.4-33.3,13.4c-8.8,0-15.3-2.2-19.8-6.9C267.3,350.8,265.1,344.2,265.1,335.2z"/>
|
||||||
|
<path d="M451.3,439.9c-10,0-15.3-8.1-15.3-18c-0.1-22.2-0.2-298.2-0.4-320.4c-0.1-10,5.2-18.2,15.2-18.3c0.1,0,0.1,0,0.2,0
|
||||||
|
c9.9,0,15.2,8,15.3,18c0.2,22.2,0.3,298.3,0.4,320.5C466.7,431.7,461.4,439.9,451.3,439.9C451.4,439.9,451.3,439.9,451.3,439.9z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
17
interface/resources/icons/create-icons/21-cube-01.svg
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:none;stroke:#000000;stroke-width:18;stroke-miterlimit:10;}
|
||||||
|
.st1{fill:none;stroke:#000000;stroke-width:18;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
.st2{fill:none;stroke:#000000;stroke-width:19;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path d="M451.6,90.8L189.4,64.9c-3.2-0.3-6.2,0.5-8.8,2.4l0.1,0L54.9,155.4l0,0c-0.5,0.3-0.5,0.7-0.9,1c-2.8,2.5-4,6.1-4,9.9v262.5
|
||||||
|
c0,6.8,4.8,12.6,11.6,13.2l262.3,25.9c0.4,0,0.8,0.1,1.2,0.1c2.7,0,5.3-0.9,7.6-2.4l0,0l125.8-88.2l0,0c0.5-0.3,0.3-0.7,0.7-1
|
||||||
|
c2.8-2.5,3.8-6.1,3.8-9.9V104.1C463,97.2,458.4,91.5,451.6,90.8z M430.1,364.7l-1.8,1.4c-0.2,0.1-0.3,0.3-0.6,0.4c0,0,0.4,0,0.4,0
|
||||||
|
L339,429.2v-230l98-69.5v222.2C436,358,435,361.1,430.1,364.7z M203.3,93l210.6,20.8l-92.4,64.7L99.6,156.6l86.7-60.8
|
||||||
|
c0.2,0,0.3,0,0.5,0c0,0,0,0,0.1,0c3.4-2.1,7.4-3.4,11.4-3.4C200,92.4,201.7,92.6,203.3,93z M313,440L76,416.7V181l237,23.3V440z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
21
interface/resources/icons/create-icons/22-sphere-01.svg
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:none;stroke:#000000;stroke-width:18;stroke-miterlimit:10;}
|
||||||
|
.st1{fill:none;stroke:#000000;stroke-width:18;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
.st2{fill:none;stroke:#000000;stroke-width:19;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path d="M417.6,96.6c-90.2-90.2-236.9-90.2-327.1,0c-43.7,43.7-67.8,101.8-67.8,163.6c0,61.8,24.1,119.9,67.8,163.6
|
||||||
|
c45.1,45.1,104.3,67.6,163.6,67.6c59.2,0,118.5-22.5,163.6-67.6c43.7-43.7,67.8-101.8,67.8-163.6
|
||||||
|
C485.4,198.4,461.3,140.3,417.6,96.6z M112.7,118.8c30-30,67.2-48.4,106.1-55.3c-7.4,11.3-14,26-19.8,44
|
||||||
|
c-13.2,41.4-20.5,96.3-20.5,154.5c0,2.8,0,7,0.1,9.8c6.4,1.6,12.8,1.7,19.3,2.8c1.9,0.3,3.9,0.6,5.9,0.8c-0.1-4.4-0.1-8.8-0.1-13.3
|
||||||
|
c0-116.9,30-193.4,52.3-200c0.5-0.5,1-1,1.4-1.6c50.1,0.8,99.9,20.3,138,58.4c36.6,36.6,57.3,85,58.5,136.6
|
||||||
|
c-4.8,22.4-81.7,53.2-200.2,53.2c-115.3,0-191.3-29.1-199.6-51.4C54.9,204.9,75.6,155.9,112.7,118.8z M395.5,401.6
|
||||||
|
C356,441.1,304,460.5,252.1,460c-14.1-8.9-30.3-43.3-39.9-96.6l-25.6-1.4c3.2,19.2,7.4,38.5,12.5,54.3c5.1,16,10.9,29.4,17.3,40.1
|
||||||
|
c-38-7.2-74.3-25.5-103.7-54.9C83.2,372.1,64,334.9,57.1,294.6c11,6.9,25.2,13.2,42.3,18.7c41.4,13.2,96.3,20.5,154.5,20.5
|
||||||
|
c58.2,0,113-7.3,154.5-20.5c17.4-5.6,31.7-11.9,42.9-19C444.2,334.7,425.1,372,395.5,401.6z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
38
interface/resources/icons/create-icons/23-zone-01.svg
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:none;stroke:#000000;stroke-width:18;stroke-miterlimit:10;}
|
||||||
|
.st1{fill:none;stroke:#000000;stroke-width:18;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
.st2{fill:none;stroke:#000000;stroke-width:19;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path d="M380.7,139.8c-2.7-6.6-9.2-10.8-16.3-10.8H292v34h29.9L159,326.1V290h-36v79h1.3c0.1,2,0.5,4.1,1.3,5.9
|
||||||
|
c2.7,6.6,9.1,11.1,16.2,11.1H225v-35h-40.8L346,189.7V215h35v-62.4C382,148.5,382.4,144,380.7,139.8z"/>
|
||||||
|
<path d="M338.4,437.6c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7l0,0
|
||||||
|
C348.1,459.3,338.4,449.6,338.4,437.6z M266.9,437.6c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0
|
||||||
|
c0,12-9.7,21.7-21.7,21.7l0,0C276.6,459.3,266.9,449.6,266.9,437.6z M195.4,437.6c0-12,9.7-21.7,21.7-21.7l0,0
|
||||||
|
c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7l0,0C205.1,459.3,195.4,449.6,195.4,437.6z M123.9,437.6
|
||||||
|
c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7l0,0C133.6,459.3,123.9,449.6,123.9,437.6z"/>
|
||||||
|
<path d="M74.1,459.3c-5.7,0-11.3-2.3-15.4-6.4c-4-4-6.4-9.6-6.4-15.3c0-5.7,2.3-11.3,6.4-15.3c4-4,9.6-6.4,15.4-6.4
|
||||||
|
c5.7,0,11.3,2.3,15.3,6.4c4,4,6.4,9.6,6.4,15.3c0,5.7-2.3,11.3-6.4,15.3C85.4,457,79.8,459.3,74.1,459.3z"/>
|
||||||
|
<path d="M52.4,366.1c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7l0,0
|
||||||
|
C62.1,387.8,52.4,378,52.4,366.1z M52.4,294.6c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7
|
||||||
|
l0,0C62.1,316.3,52.4,306.5,52.4,294.6z M52.4,223.1c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0
|
||||||
|
c0,12-9.7,21.7-21.7,21.7l0,0C62.1,244.8,52.4,235,52.4,223.1z M52.4,151.5c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7
|
||||||
|
l0,0c0,12-9.7,21.7-21.7,21.7l0,0C62.1,173.2,52.4,163.5,52.4,151.5z"/>
|
||||||
|
<path d="M338.4,80c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7l0,0
|
||||||
|
C348.1,101.7,338.4,92,338.4,80z M266.9,80c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7l0,0
|
||||||
|
C276.6,101.7,266.9,92,266.9,80z M195.4,80c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7l0,0
|
||||||
|
C205.1,101.7,195.4,92,195.4,80z M123.9,80c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7l0,0
|
||||||
|
C133.6,101.7,123.9,92,123.9,80z"/>
|
||||||
|
<path d="M431.6,101.7c-5.7,0-11.3-2.3-15.3-6.4c-4-4-6.4-9.6-6.4-15.3c0-5.7,2.3-11.3,6.4-15.3c4-4,9.6-6.4,15.3-6.4
|
||||||
|
s11.3,2.3,15.3,6.4c4,4,6.4,9.6,6.4,15.3c0,5.7-2.3,11.3-6.4,15.3C442.9,99.4,437.4,101.7,431.6,101.7z"/>
|
||||||
|
<path d="M409.9,366.1c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7l0,0
|
||||||
|
C419.7,387.8,409.9,378.1,409.9,366.1z M409.9,294.6c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0
|
||||||
|
c0,12-9.7,21.7-21.7,21.7l0,0C419.7,316.3,409.9,306.5,409.9,294.6z M409.9,223.1c0-12,9.7-21.7,21.7-21.7l0,0
|
||||||
|
c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7l0,0C419.7,244.7,409.9,235,409.9,223.1z M409.9,151.5
|
||||||
|
c0-12,9.7-21.7,21.7-21.7l0,0c12,0,21.7,9.7,21.7,21.7l0,0c0,12-9.7,21.7-21.7,21.7l0,0C419.7,173.2,409.9,163.5,409.9,151.5z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.4 KiB |
34
interface/resources/icons/create-icons/24-light-01.svg
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:none;stroke:#000000;stroke-width:18;stroke-miterlimit:10;}
|
||||||
|
.st1{fill:none;stroke:#000000;stroke-width:18;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
.st2{fill:none;stroke:#000000;stroke-width:19;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<circle cx="259.2" cy="252.5" r="38.3"/>
|
||||||
|
<path d="M259.2,361.8c-60.3,0-109.3-49-109.3-109.3s49-109.3,109.3-109.3s109.3,49,109.3,109.3S319.4,361.8,259.2,361.8z
|
||||||
|
M259.2,171.8c-44.5,0-80.7,36.2-80.7,80.7s36.2,80.7,80.7,80.7s80.7-36.2,80.7-80.7S303.7,171.8,259.2,171.8z"/>
|
||||||
|
<path d="M414.1,268.5c-7.9,0-14.2-6.4-14.3-14.2c0-7.9,6.3-14.3,14.2-14.3c17.1-0.1,34.5-0.2,51.6-0.3c0,0,0.1,0,0.1,0
|
||||||
|
c7.8,0,14.2,6.3,14.3,14.2c0.1,7.9-6.3,14.3-14.2,14.4C448.8,268.3,431.4,268.4,414.1,268.5C414.2,268.5,414.1,268.5,414.1,268.5z"
|
||||||
|
/>
|
||||||
|
<path d="M105.8,268.5C105.8,268.5,105.8,268.5,105.8,268.5c-17.3-0.1-34.6-0.2-51.7-0.4c-7.9-0.1-14.2-6.5-14.1-14.4
|
||||||
|
c0.1-7.8,6.5-14.1,14.3-14.1c0.1,0,0.1,0,0.2,0c17,0.2,34.3,0.3,51.5,0.4c7.9,0,14.3,6.5,14.2,14.3
|
||||||
|
C120.1,262.2,113.7,268.5,105.8,268.5z"/>
|
||||||
|
<path d="M369.1,159.5c-3.6,0-7.3-1.4-10-4.1c-5.6-5.6-5.6-14.6-0.1-20.2c12-12.1,24.2-24.5,36.3-36.7c5.5-5.6,14.6-5.7,20.2-0.2
|
||||||
|
c5.6,5.5,5.7,14.6,0.2,20.2c-12.1,12.3-24.3,24.7-36.4,36.8C376.4,158.1,372.8,159.5,369.1,159.5z"/>
|
||||||
|
<path d="M114.4,413.7c-3.7,0-7.4-1.4-10.2-4.3c-5.5-5.6-5.4-14.7,0.2-20.2c12.2-11.9,24.5-24.1,36.7-36.1
|
||||||
|
c5.6-5.6,14.6-5.5,20.2,0.1c5.6,5.6,5.5,14.6-0.1,20.2c-12.2,12.1-24.6,24.3-36.8,36.2C121.6,412.3,118,413.7,114.4,413.7z"/>
|
||||||
|
<path d="M260.2,114.2c-7.9,0-14.2-6.4-14.3-14.2c-0.1-17-0.2-34.4-0.3-51.6c-0.1-7.9,6.3-14.3,14.2-14.4c0,0,0.1,0,0.1,0
|
||||||
|
c7.8,0,14.2,6.3,14.3,14.2c0.1,17.3,0.3,34.7,0.3,51.7C274.5,107.8,268.1,114.2,260.2,114.2C260.2,114.2,260.2,114.2,260.2,114.2z"
|
||||||
|
/>
|
||||||
|
<path d="M259.9,474.1c-0.1,0-0.1,0-0.2,0c-7.9-0.1-14.2-6.5-14.1-14.4c0.2-16.9,0.3-34.3,0.4-51.5c0-7.9,6.4-14.2,14.3-14.2
|
||||||
|
c0,0,0,0,0.1,0c7.9,0,14.3,6.5,14.2,14.3c-0.1,17.3-0.2,34.6-0.4,51.7C274,467.8,267.7,474.1,259.9,474.1z"/>
|
||||||
|
<path d="M151.2,159.3c-3.6,0-7.3-1.4-10-4.1c-12.2-12.1-24.6-24.3-36.7-36.3c-5.6-5.5-5.7-14.6-0.2-20.2c5.5-5.6,14.6-5.7,20.2-0.2
|
||||||
|
c12.2,12,24.6,24.2,36.8,36.4c5.6,5.6,5.6,14.6,0.1,20.2C158.5,157.9,154.9,159.3,151.2,159.3z"/>
|
||||||
|
<path d="M405.4,414c-3.7,0-7.4-1.4-10.2-4.3c-11.9-12.2-24-24.5-36.1-36.7c-5.6-5.6-5.5-14.6,0.1-20.2c5.6-5.6,14.6-5.5,20.2,0.1
|
||||||
|
c12.1,12.2,24.3,24.6,36.2,36.8c5.5,5.6,5.4,14.7-0.2,20.2C412.6,412.6,409,414,405.4,414z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
30
interface/resources/icons/create-icons/25-web-1-01.svg
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:none;stroke:#000000;stroke-width:18;stroke-miterlimit:10;}
|
||||||
|
.st1{fill:none;stroke:#000000;stroke-width:18;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
.st2{fill:none;stroke:#000000;stroke-width:19;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
.st3{stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path d="M438,121.5c0-8-6.5-14.5-14.5-14.5h-333c-8,0-14.5,6.5-14.5,14.5v298c0,8,6.5,14.5,14.5,14.5h333c8,0,14.5-6.5,14.5-14.5
|
||||||
|
V121.5z M219.4,130H391c8.3,0,15,7.2,15,16s-6.7,16-15,16H219.4c-8.3,0-15-7.2-15-16S211.1,130,219.4,130z M171.5,128.6
|
||||||
|
c9.2,0,16.7,7.5,16.7,16.7c0,9.2-7.5,16.7-16.7,16.7c-9.2,0-16.7-7.5-16.7-16.7C154.8,136,162.3,128.6,171.5,128.6z M121,128.6
|
||||||
|
c9.2,0,16.7,7.5,16.7,16.7c0,9.2-7.5,16.7-16.7,16.7c-9.2,0-16.7-7.5-16.7-16.7C104.4,136,111.8,128.6,121,128.6z M412,405H104V186
|
||||||
|
h308V405z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st3" d="M161.6,320.5h-14.3l-20.5-59.3h12.9l14.7,44.8l14.7-44.8h12.9l14.7,44.8l14.7-44.8h12.9L204,320.5h-14.3
|
||||||
|
l-14-40.6L161.6,320.5z"/>
|
||||||
|
<path class="st3" d="M299.4,296.3h-46.5c0.3,4.3,2.3,7.8,5.9,10.4c3.6,2.6,7.8,4,12.5,4c7.4,0,13.1-2.3,16.9-7l7.1,7.8
|
||||||
|
c-6.4,6.6-14.7,9.9-25,9.9c-8.3,0-15.4-2.8-21.2-8.3c-5.8-5.5-8.7-13-8.7-22.3c0-9.3,3-16.7,8.9-22.2c5.9-5.5,12.9-8.2,21-8.2
|
||||||
|
c8.1,0,14.9,2.4,20.6,7.3c5.6,4.9,8.5,11.6,8.5,20.1V296.3z M252.9,286.5h34c0-5-1.6-8.8-4.7-11.5s-7-4-11.5-4
|
||||||
|
c-4.6,0-8.7,1.4-12.3,4.2C254.7,278,252.9,281.8,252.9,286.5z"/>
|
||||||
|
<path class="st3" d="M353.6,260.3c7.9,0,14.7,2.8,20.4,8.2c5.6,5.5,8.5,12.8,8.5,22c0,9.1-2.8,16.6-8.4,22.3
|
||||||
|
c-5.6,5.7-12.1,8.6-19.6,8.6s-14.2-3.3-20.1-9.8v8.9h-12.5v-82.7h12.5v33.8C339.2,264.1,345.6,260.3,353.6,260.3z M334.1,291
|
||||||
|
c0,5.6,1.7,10.3,5.1,13.9c3.4,3.6,7.6,5.4,12.5,5.4c4.9,0,9.2-1.8,12.8-5.3c3.6-3.6,5.5-8.2,5.5-13.9c0-5.7-1.8-10.4-5.3-14.2
|
||||||
|
c-3.6-3.8-7.8-5.6-12.8-5.6c-5,0-9.2,1.9-12.6,5.6C335.8,280.6,334.1,285.3,334.1,291z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
29
interface/resources/icons/create-icons/90-particles-01.svg
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<path d="M331.8,283.4c0-12.8,10.4-23.2,23.2-23.2l0,0c12.8,0,23.2,10.4,23.2,23.2l0,0c0,12.8-10.4,23.2-23.2,23.2l0,0
|
||||||
|
C342.1,306.6,331.8,296.2,331.8,283.4z"/>
|
||||||
|
<path d="M277.8,350.9c0-12.8,10.4-23.2,23.2-23.2l0,0c12.8,0,23.2,10.4,23.2,23.2l0,0c0,12.8-10.4,23.2-23.2,23.2l0,0
|
||||||
|
C288.1,374.2,277.8,363.8,277.8,350.9z"/>
|
||||||
|
<path d="M216.3,368.8c0-12.8,10.4-23.2,23.2-23.2l0,0c12.8,0,23.2,10.4,23.2,23.2l0,0c0,12.8-10.4,23.2-23.2,23.2l0,0
|
||||||
|
C226.7,392,216.3,381.6,216.3,368.8z"/>
|
||||||
|
<path d="M169.9,308.9c0-12.8,10.4-23.2,23.2-23.2l0,0c12.8,0,23.2,10.4,23.2,23.2l0,0c0,12.8-10.4,23.2-23.2,23.2l0,0
|
||||||
|
C180.3,332.1,169.9,321.7,169.9,308.9z"/>
|
||||||
|
<path d="M251.2,447.4c-4.9-3.6-8.3-9.1-9.2-15.3c-0.9-6,0.6-12.3,4.2-17.2c3.6-4.9,9.1-8.3,15.2-9.1c6-0.9,12.3,0.6,17.3,4.3
|
||||||
|
c4.9,3.6,8.3,9.1,9.1,15.2c0.9,6-0.6,12.3-4.2,17.2s-9.1,8.3-15.2,9.1C262.4,452.6,256.1,451,251.2,447.4z"/>
|
||||||
|
<path d="M67.6,246.1c0-12.8,10.4-23.2,23.2-23.2l0,0c12.8,0,23.2,10.4,23.2,23.2l0,0c0,12.8-10.4,23.2-23.2,23.2l0,0
|
||||||
|
C78,269.3,67.6,258.8,67.6,246.1z"/>
|
||||||
|
<path d="M178.8,199.5c0-12.8,10.4-23.2,23.2-23.2l0,0c12.8,0,23.2,10.4,23.2,23.2l0,0c0,12.8-10.4,23.2-23.2,23.2l0,0
|
||||||
|
C189.1,222.7,178.8,212.2,178.8,199.5z"/>
|
||||||
|
<path d="M250.3,293.9c0-12.8,10.4-23.2,23.2-23.2l0,0c12.8,0,23.2,10.4,23.2,23.2l0,0c0,12.8-10.4,23.2-23.2,23.2l0,0
|
||||||
|
C260.7,317.1,250.3,306.6,250.3,293.9z"/>
|
||||||
|
<path d="M413,242.1c0-12.8,10.4-23.2,23.2-23.2l0,0c12.8,0,23.2,10.4,23.2,23.2l0,0c0,12.8-10.4,23.2-23.2,23.2l0,0
|
||||||
|
C423.5,265.3,413,255,413,242.1z"/>
|
||||||
|
<path d="M302.1,203.7c0-12.8,10.4-23.2,23.2-23.2l0,0c12.8,0,23.2,10.4,23.2,23.2l0,0c0,12.8-10.4,23.2-23.2,23.2l0,0
|
||||||
|
C312.6,226.9,302.1,216.5,302.1,203.7z"/>
|
||||||
|
<path d="M132.3,113.5c0-12.8,10.4-23.2,23.2-23.2l0,0c12.8,0,23.2,10.4,23.2,23.2l0,0c0,12.8-10.4,23.2-23.2,23.2l0,0
|
||||||
|
C142.8,136.6,132.3,126.2,132.3,113.5z"/>
|
||||||
|
<path d="M366.6,136.7c0-12.8,10.4-23.2,23.2-23.2l0,0c12.8,0,23.2,10.4,23.2,23.2l0,0c0,12.8-10.4,23.2-23.2,23.2l0,0
|
||||||
|
C377.1,159.9,366.6,149.5,366.6,136.7z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
20
interface/resources/icons/create-icons/94-model-01.svg
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<path d="M494.2,117.2c-2.2-5.3-7.8-8.4-13.5-7.3l-89.9,16.4l44.9-72.6c2.8-4.6,2.3-10.5-1.2-14.5s-9.3-5.3-14.2-3l-213.1,98
|
||||||
|
c-15.3-4.9-72.2-27.3-111.4-43.2c-0.3-0.1-0.6-0.2-0.8-0.3c0,0-0.1,0-0.1,0c-0.4-0.1-0.9-0.2-1.3-0.3c-0.1,0-0.2-0.1-0.3-0.1
|
||||||
|
c-0.5-0.1-0.9-0.2-1.4-0.2c0,0,0,0-0.1,0c-0.5,0-0.9,0.1-1.4,0.1c-0.1,0-0.1,0-0.2,0c0,0-0.1,0-0.1,0c-0.5,0-0.9,0.1-1.3,0.2
|
||||||
|
c-0.1,0-0.2,0-0.3,0.1c-0.4,0.1-0.8,0.2-1.2,0.3c-0.1,0-0.1,0-0.2,0.1c-0.4,0.1-0.8,0.3-1.2,0.5c-0.1,0-0.2,0.1-0.3,0.1
|
||||||
|
c-0.8,0.4-1.6,0.9-2.3,1.5c-0.1,0.1-0.2,0.1-0.2,0.2c-0.3,0.3-0.7,0.6-1,0.9c0,0-0.1,0.1-0.1,0.1c-0.2,0.2-0.4,0.4-0.5,0.6
|
||||||
|
c-0.1,0.1-0.2,0.2-0.3,0.4c-0.1,0.1-0.1,0.2-0.2,0.3c-0.3,0.4-0.5,0.8-0.7,1.2c0,0,0,0,0,0l-27.3,52.1l-32.7,40.1
|
||||||
|
c-3.4,4.2-3.7,10.2-0.6,14.7c2.3,3.3,6.1,5.2,10,5.2c1.3,0,2.6-0.2,3.9-0.7l50.1-17l40.6-1.9l-26.7,50.3c-2.4,4.5-1.7,9.9,1.6,13.7
|
||||||
|
c0.3,0.3,25.6,29.3,51.7,57.7c15.4,16.8,28,30.1,37.6,39.6c6.6,6.5,11.6,11.2,15.6,14.4l-16,61l-45.7,18.2
|
||||||
|
c-6.3,2.5-9.3,9.6-6.8,15.8c1.9,4.8,6.5,7.7,11.3,7.7c1.5,0,3-0.3,4.5-0.9l44.5-17.7l17.2,15.4c2.3,2.1,5.2,3.1,8.1,3.1
|
||||||
|
c3.4,0,6.7-1.4,9.1-4.1c4.5-5,4-12.7-1-17.2L212.1,431l16-60.7l75.7,89.3c2.4,2.8,5.8,4.3,9.3,4.3c1.1,0,2.1-0.1,3.2-0.4l84.6-22.8
|
||||||
|
c4.8-1.3,8.4-5.4,8.9-10.4c0.6-5-2-9.8-6.4-12.1l-136.2-71.8l44.4-91.2L489.9,132C494.6,128.7,496.4,122.6,494.2,117.2z
|
||||||
|
M294.4,234.2l-122.6-55.4l41.5-20.6c0,0,0,0,0,0l180.4-82.9L294.4,234.2z M146.4,160.8l-24.8-33.2c16.7,6.6,39.3,15.5,54,21
|
||||||
|
L146.4,160.8z M87.3,166.6l-9.6-12.3l15.1-28.9l27.4,37.3l1.7,2.3L87.3,166.6z M123.2,243.6l22.8-43.3c18.3,44.8,35.2,91,47,121
|
||||||
|
C173.7,301.1,147.7,271.5,123.2,243.6z M317.4,438l-57.5-67.8l104.6,55.1L317.4,438z M223.3,336.7c-5.5-14.1-42.6-104.1-30.8-76.4
|
||||||
|
c0.6,1.4-20.5-49.2-27.9-66.3l120.9,58.9l-47.1,95L223.3,336.7z M330.5,216.7l43-62.3l70.9-16.2L330.5,216.7z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
108
interface/resources/images/sphere-01.svg
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 50 150" style="enable-background:new 0 0 50 150;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{opacity:0.9;}
|
||||||
|
.st1{fill:#FFFFFF;}
|
||||||
|
.st2{fill:#1E1E1E;}
|
||||||
|
.st3{fill:#EAEAEA;}
|
||||||
|
.st4{opacity:0.47;}
|
||||||
|
.st5{opacity:0.47;fill:#EAEAEA;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<g class="st0">
|
||||||
|
<path class="st1" d="M50,46c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4V4c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V46z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st0">
|
||||||
|
<path class="st2" d="M50,96c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4V54c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V96z"/>
|
||||||
|
</g>
|
||||||
|
<path d="M32.7,9.4c-4.3-4.3-11.2-4.3-15.5,0c-2.1,2.1-3.2,4.8-3.2,7.7c0,2.9,1.1,5.7,3.2,7.7C19.4,27,22.2,28,25,28
|
||||||
|
c2.8,0,5.6-1.1,7.7-3.2c2.1-2.1,3.2-4.8,3.2-7.7C35.9,14.2,34.8,11.4,32.7,9.4z M18.3,10.4c1.4-1.4,3.2-2.3,5-2.6
|
||||||
|
C23,8.3,22.7,9,22.4,9.9c-0.6,2-1,4.6-1,7.3c0,0.1,0,0.3,0,0.5c0.3,0.1,0.6,0.1,0.9,0.1c0.1,0,0.2,0,0.3,0c0-0.2,0-0.4,0-0.6
|
||||||
|
c0-5.5,1.4-9.2,2.5-9.5c0,0,0,0,0.1-0.1c2.4,0,4.7,1,6.5,2.8c1.7,1.7,2.7,4,2.8,6.5c-0.2,1.1-3.9,2.5-9.5,2.5
|
||||||
|
c-5.5,0-9.1-1.4-9.4-2.4C15.6,14.5,16.6,12.2,18.3,10.4z M31.7,23.8c-1.9,1.9-4.3,2.8-6.8,2.8c-0.7-0.4-1.4-2.1-1.9-4.6l-1.2-0.1
|
||||||
|
c0.2,0.9,0.4,1.8,0.6,2.6c0.2,0.8,0.5,1.4,0.8,1.9c-1.8-0.3-3.5-1.2-4.9-2.6c-1.4-1.4-2.3-3.2-2.6-5.1c0.5,0.3,1.2,0.6,2,0.9
|
||||||
|
c2,0.6,4.6,1,7.3,1s5.3-0.3,7.3-1c0.8-0.3,1.5-0.6,2-0.9C34,20.6,33.1,22.4,31.7,23.8z"/>
|
||||||
|
<g>
|
||||||
|
<path d="M11.1,37.5c0,0-0.1-0.1-0.2-0.2c-0.1-0.1-0.2-0.1-0.4-0.2c-0.2-0.1-0.3-0.1-0.5-0.2c-0.2,0-0.4-0.1-0.6-0.1
|
||||||
|
c-0.3,0-0.6,0.1-0.8,0.2c-0.2,0.1-0.3,0.3-0.3,0.5c0,0.1,0,0.2,0.1,0.3c0.1,0.1,0.2,0.2,0.3,0.2s0.3,0.1,0.5,0.2
|
||||||
|
c0.2,0.1,0.4,0.1,0.6,0.2c0.3,0.1,0.6,0.2,0.9,0.3c0.3,0.1,0.5,0.2,0.6,0.4c0.2,0.1,0.3,0.3,0.4,0.5c0.1,0.2,0.1,0.4,0.1,0.7
|
||||||
|
c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.2-0.3,0.4-0.5,0.6s-0.5,0.3-0.8,0.3c-0.3,0.1-0.6,0.1-0.9,0.1c-0.5,0-1-0.1-1.4-0.2
|
||||||
|
s-0.9-0.3-1.3-0.6l0.5-1.1c0.1,0.1,0.2,0.1,0.3,0.2c0.1,0.1,0.3,0.2,0.5,0.3s0.4,0.2,0.6,0.2c0.2,0.1,0.5,0.1,0.7,0.1
|
||||||
|
c0.7,0,1-0.2,1-0.7c0-0.1,0-0.3-0.1-0.4c-0.1-0.1-0.2-0.2-0.3-0.3c-0.1-0.1-0.3-0.1-0.5-0.2c-0.2-0.1-0.4-0.1-0.7-0.2
|
||||||
|
c-0.3-0.1-0.6-0.2-0.8-0.3S7.7,39,7.6,38.9c-0.2-0.1-0.3-0.3-0.3-0.5c-0.1-0.2-0.1-0.4-0.1-0.6c0-0.3,0.1-0.6,0.2-0.9
|
||||||
|
c0.1-0.3,0.3-0.5,0.5-0.6s0.5-0.3,0.7-0.4c0.3-0.1,0.6-0.1,0.9-0.1c0.5,0,0.9,0.1,1.2,0.2c0.4,0.1,0.7,0.3,1,0.5L11.1,37.5z"/>
|
||||||
|
<path d="M13.1,42.2v-6.4h2.7c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
|
||||||
|
c0,0.3,0,0.5-0.1,0.8c-0.1,0.3-0.2,0.5-0.4,0.7c-0.2,0.2-0.4,0.4-0.6,0.5c-0.2,0.1-0.5,0.2-0.8,0.2h-1.5v2.1H13.1z M14.3,39h1.4
|
||||||
|
c0.2,0,0.4-0.1,0.6-0.3c0.2-0.2,0.2-0.4,0.2-0.8c0-0.2,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.3C16.2,37.1,16.1,37,16,37
|
||||||
|
c-0.1,0-0.2-0.1-0.3-0.1h-1.4V39z"/>
|
||||||
|
<path d="M24.3,35.8v6.4H23v-2.7h-2.9v2.7h-1.2v-6.4h1.2v2.6H23v-2.6H24.3z"/>
|
||||||
|
<path d="M30.3,41.1v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H30.3z"/>
|
||||||
|
<path d="M31.5,42.2v-6.4h2.8c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
|
||||||
|
c0,0.4-0.1,0.8-0.3,1.1c-0.2,0.3-0.5,0.6-0.8,0.7l1.5,2.4h-1.4L34,40.1h-1.2v2.1H31.5z M32.7,39h1.6c0.1,0,0.2,0,0.3-0.1
|
||||||
|
c0.1-0.1,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3s0.1-0.3,0.1-0.4c0-0.1,0-0.3-0.1-0.4s-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
|
||||||
|
c-0.1-0.1-0.2-0.1-0.3-0.1h-1.5V39z"/>
|
||||||
|
<path d="M42.2,41.1v1.1h-4.4v-6.4h4.4v1.1H39v1.5h2.7v1H39v1.7H42.2z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st3" d="M33.1,59.4c-4.3-4.3-11.2-4.3-15.5,0c-2.1,2.1-3.2,4.8-3.2,7.7c0,2.9,1.1,5.7,3.2,7.7c2.1,2.1,4.9,3.2,7.7,3.2
|
||||||
|
c2.8,0,5.6-1.1,7.7-3.2c2.1-2.1,3.2-4.8,3.2-7.7C36.3,64.2,35.1,61.5,33.1,59.4z M18.6,60.4c1.4-1.4,3.2-2.3,5-2.6
|
||||||
|
c-0.3,0.5-0.7,1.2-0.9,2.1c-0.6,2-1,4.6-1,7.3c0,0.1,0,0.3,0,0.5c0.3,0.1,0.6,0.1,0.9,0.1c0.1,0,0.2,0,0.3,0c0-0.2,0-0.4,0-0.6
|
||||||
|
c0-5.5,1.4-9.2,2.5-9.5c0,0,0,0,0.1-0.1c2.4,0,4.7,1,6.5,2.8c1.7,1.7,2.7,4,2.8,6.5c-0.2,1.1-3.9,2.5-9.5,2.5
|
||||||
|
c-5.5,0-9.1-1.4-9.4-2.4C15.9,64.5,16.9,62.2,18.6,60.4z M32,73.8c-1.9,1.9-4.3,2.8-6.8,2.8c-0.7-0.4-1.4-2.1-1.9-4.6l-1.2-0.1
|
||||||
|
c0.2,0.9,0.4,1.8,0.6,2.6c0.2,0.8,0.5,1.4,0.8,1.9c-1.8-0.3-3.5-1.2-4.9-2.6c-1.4-1.4-2.3-3.2-2.6-5.1c0.5,0.3,1.2,0.6,2,0.9
|
||||||
|
c2,0.6,4.6,1,7.3,1s5.3-0.3,7.3-1c0.8-0.3,1.5-0.6,2-0.9C34.3,70.7,33.4,72.4,32,73.8z"/>
|
||||||
|
<g>
|
||||||
|
<path class="st3" d="M11.5,87.5c0,0-0.1-0.1-0.2-0.2c-0.1-0.1-0.2-0.1-0.4-0.2S10.5,87,10.3,87c-0.2,0-0.4-0.1-0.6-0.1
|
||||||
|
c-0.3,0-0.6,0.1-0.8,0.2s-0.3,0.3-0.3,0.5c0,0.1,0,0.2,0.1,0.3C8.9,88,9,88.1,9.1,88.2c0.1,0.1,0.3,0.1,0.5,0.2
|
||||||
|
c0.2,0.1,0.4,0.1,0.6,0.2c0.3,0.1,0.6,0.2,0.9,0.3s0.5,0.2,0.6,0.4c0.2,0.1,0.3,0.3,0.4,0.5c0.1,0.2,0.1,0.4,0.1,0.7
|
||||||
|
c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.2-0.3,0.4-0.5,0.6c-0.2,0.2-0.5,0.3-0.8,0.3c-0.3,0.1-0.6,0.1-0.9,0.1c-0.5,0-1-0.1-1.4-0.2
|
||||||
|
C8,92,7.6,91.8,7.2,91.5l0.5-1.1c0.1,0.1,0.2,0.1,0.3,0.2c0.1,0.1,0.3,0.2,0.5,0.3s0.4,0.2,0.6,0.2c0.2,0.1,0.5,0.1,0.7,0.1
|
||||||
|
c0.7,0,1-0.2,1-0.7c0-0.1,0-0.3-0.1-0.4c-0.1-0.1-0.2-0.2-0.3-0.3c-0.1-0.1-0.3-0.1-0.5-0.2c-0.2-0.1-0.4-0.1-0.7-0.2
|
||||||
|
c-0.3-0.1-0.6-0.2-0.8-0.3c-0.2-0.1-0.4-0.2-0.6-0.3s-0.3-0.3-0.3-0.5c-0.1-0.2-0.1-0.4-0.1-0.6c0-0.3,0.1-0.6,0.2-0.9
|
||||||
|
c0.1-0.3,0.3-0.5,0.5-0.6s0.5-0.3,0.7-0.4s0.6-0.1,0.9-0.1c0.5,0,0.9,0.1,1.2,0.2s0.7,0.3,1,0.5L11.5,87.5z"/>
|
||||||
|
<path class="st3" d="M13.4,92.2v-6.4h2.7c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5s0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
|
||||||
|
c0,0.3,0,0.5-0.1,0.8c-0.1,0.3-0.2,0.5-0.4,0.7c-0.2,0.2-0.4,0.4-0.6,0.5c-0.2,0.1-0.5,0.2-0.8,0.2h-1.5v2.1H13.4z M14.7,89h1.4
|
||||||
|
c0.2,0,0.4-0.1,0.6-0.3c0.2-0.2,0.2-0.4,0.2-0.8c0-0.2,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.3s-0.2-0.2-0.3-0.2
|
||||||
|
c-0.1,0-0.2-0.1-0.3-0.1h-1.4V89z"/>
|
||||||
|
<path class="st3" d="M24.6,85.9v6.4h-1.2v-2.7h-2.9v2.7h-1.2v-6.4h1.2v2.6h2.9v-2.6H24.6z"/>
|
||||||
|
<path class="st3" d="M30.6,91.2v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H30.6z"/>
|
||||||
|
<path class="st3" d="M31.8,92.2v-6.4h2.8c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
|
||||||
|
c0,0.4-0.1,0.8-0.3,1.1s-0.5,0.6-0.8,0.7l1.5,2.4h-1.4l-1.3-2.1h-1.2v2.1H31.8z M33.1,89h1.6c0.1,0,0.2,0,0.3-0.1
|
||||||
|
c0.1-0.1,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3s0.1-0.3,0.1-0.4c0-0.1,0-0.3-0.1-0.4s-0.1-0.2-0.2-0.3S35,87.1,34.9,87
|
||||||
|
c-0.1-0.1-0.2-0.1-0.3-0.1h-1.5V89z"/>
|
||||||
|
<path class="st3" d="M42.5,91.2v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5H42v1h-2.7v1.7H42.5z"/>
|
||||||
|
</g>
|
||||||
|
<g class="st4">
|
||||||
|
<path class="st2" d="M50,146c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V146z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st5" d="M33.1,109.4c-4.3-4.3-11.2-4.3-15.5,0c-2.1,2.1-3.2,4.8-3.2,7.7c0,2.9,1.1,5.7,3.2,7.7c2.1,2.1,4.9,3.2,7.7,3.2
|
||||||
|
c2.8,0,5.6-1.1,7.7-3.2c2.1-2.1,3.2-4.8,3.2-7.7C36.3,114.2,35.1,111.5,33.1,109.4z M18.6,110.4c1.4-1.4,3.2-2.3,5-2.6
|
||||||
|
c-0.3,0.5-0.7,1.2-0.9,2.1c-0.6,2-1,4.6-1,7.3c0,0.1,0,0.3,0,0.5c0.3,0.1,0.6,0.1,0.9,0.1c0.1,0,0.2,0,0.3,0c0-0.2,0-0.4,0-0.6
|
||||||
|
c0-5.5,1.4-9.2,2.5-9.5c0,0,0,0,0.1-0.1c2.4,0,4.7,1,6.5,2.8c1.7,1.7,2.7,4,2.8,6.5c-0.2,1.1-3.9,2.5-9.5,2.5
|
||||||
|
c-5.5,0-9.1-1.4-9.4-2.4C15.9,114.5,16.9,112.2,18.6,110.4z M32,123.8c-1.9,1.9-4.3,2.8-6.8,2.8c-0.7-0.4-1.4-2.1-1.9-4.6l-1.2-0.1
|
||||||
|
c0.2,0.9,0.4,1.8,0.6,2.6c0.2,0.8,0.5,1.4,0.8,1.9c-1.8-0.3-3.5-1.2-4.9-2.6c-1.4-1.4-2.3-3.2-2.6-5.1c0.5,0.3,1.2,0.6,2,0.9
|
||||||
|
c2,0.6,4.6,1,7.3,1s5.3-0.3,7.3-1c0.8-0.3,1.5-0.6,2-0.9C34.3,120.7,33.4,122.4,32,123.8z"/>
|
||||||
|
<g class="st4">
|
||||||
|
<path class="st3" d="M11.5,137.5c0,0-0.1-0.1-0.2-0.2c-0.1-0.1-0.2-0.1-0.4-0.2s-0.3-0.1-0.5-0.2c-0.2,0-0.4-0.1-0.6-0.1
|
||||||
|
c-0.3,0-0.6,0.1-0.8,0.2s-0.3,0.3-0.3,0.5c0,0.1,0,0.2,0.1,0.3c0.1,0.1,0.2,0.2,0.3,0.2c0.1,0.1,0.3,0.1,0.5,0.2
|
||||||
|
c0.2,0.1,0.4,0.1,0.6,0.2c0.3,0.1,0.6,0.2,0.9,0.3s0.5,0.2,0.6,0.4c0.2,0.1,0.3,0.3,0.4,0.5c0.1,0.2,0.1,0.4,0.1,0.7
|
||||||
|
c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.2-0.3,0.4-0.5,0.6c-0.2,0.2-0.5,0.3-0.8,0.3c-0.3,0.1-0.6,0.1-0.9,0.1c-0.5,0-1-0.1-1.4-0.2
|
||||||
|
c-0.5-0.1-0.9-0.3-1.3-0.6l0.5-1.1c0.1,0.1,0.2,0.1,0.3,0.2c0.1,0.1,0.3,0.2,0.5,0.3s0.4,0.2,0.6,0.2c0.2,0.1,0.5,0.1,0.7,0.1
|
||||||
|
c0.7,0,1-0.2,1-0.7c0-0.1,0-0.3-0.1-0.4c-0.1-0.1-0.2-0.2-0.3-0.3c-0.1-0.1-0.3-0.1-0.5-0.2c-0.2-0.1-0.4-0.1-0.7-0.2
|
||||||
|
c-0.3-0.1-0.6-0.2-0.8-0.3c-0.2-0.1-0.4-0.2-0.6-0.3s-0.3-0.3-0.3-0.5c-0.1-0.2-0.1-0.4-0.1-0.6c0-0.3,0.1-0.6,0.2-0.9
|
||||||
|
c0.1-0.3,0.3-0.5,0.5-0.6s0.5-0.3,0.7-0.4s0.6-0.1,0.9-0.1c0.5,0,0.9,0.1,1.2,0.2s0.7,0.3,1,0.5L11.5,137.5z"/>
|
||||||
|
<path class="st3" d="M13.4,142.2v-6.4h2.7c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5s0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
|
||||||
|
c0,0.3,0,0.5-0.1,0.8c-0.1,0.3-0.2,0.5-0.4,0.7c-0.2,0.2-0.4,0.4-0.6,0.5c-0.2,0.1-0.5,0.2-0.8,0.2h-1.5v2.1H13.4z M14.7,139h1.4
|
||||||
|
c0.2,0,0.4-0.1,0.6-0.3c0.2-0.2,0.2-0.4,0.2-0.8c0-0.2,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.3s-0.2-0.2-0.3-0.2
|
||||||
|
c-0.1,0-0.2-0.1-0.3-0.1h-1.4V139z"/>
|
||||||
|
<path class="st3" d="M24.6,135.9v6.4h-1.2v-2.7h-2.9v2.7h-1.2v-6.4h1.2v2.6h2.9v-2.6H24.6z"/>
|
||||||
|
<path class="st3" d="M30.6,141.2v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H30.6z"/>
|
||||||
|
<path class="st3" d="M31.8,142.2v-6.4h2.8c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
|
||||||
|
c0,0.4-0.1,0.8-0.3,1.1s-0.5,0.6-0.8,0.7l1.5,2.4h-1.4l-1.3-2.1h-1.2v2.1H31.8z M33.1,139h1.6c0.1,0,0.2,0,0.3-0.1
|
||||||
|
c0.1-0.1,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3s0.1-0.3,0.1-0.4c0-0.1,0-0.3-0.1-0.4s-0.1-0.2-0.2-0.3s-0.2-0.2-0.3-0.2
|
||||||
|
c-0.1-0.1-0.2-0.1-0.3-0.1h-1.5V139z"/>
|
||||||
|
<path class="st3" d="M42.5,141.2v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5H42v1h-2.7v1.7H42.5z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 9.1 KiB |
|
@ -1,10 +1,13 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.0
|
import QtQuick.Controls 1.4
|
||||||
import QtWebEngine 1.1
|
import QtWebEngine 1.1
|
||||||
import QtWebChannel 1.0
|
import QtWebChannel 1.0
|
||||||
import QtQuick.Controls.Styles 1.4
|
import QtQuick.Controls.Styles 1.4
|
||||||
import "../../controls"
|
import "../../controls"
|
||||||
|
import "../toolbars"
|
||||||
import HFWebEngineProfile 1.0
|
import HFWebEngineProfile 1.0
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import "../../styles-uit"
|
||||||
|
|
||||||
StackView {
|
StackView {
|
||||||
id: editRoot
|
id: editRoot
|
||||||
|
@ -33,109 +36,222 @@ StackView {
|
||||||
height: 60
|
height: 60
|
||||||
|
|
||||||
Tab {
|
Tab {
|
||||||
title: "Create"
|
title: "CREATE"
|
||||||
active: true
|
active: true
|
||||||
enabled: true
|
enabled: true
|
||||||
property string originalUrl: ""
|
property string originalUrl: ""
|
||||||
|
|
||||||
Flow {
|
Rectangle {
|
||||||
id: createEntitiesFlow
|
color: "#404040"
|
||||||
spacing: 16
|
|
||||||
|
|
||||||
Button {
|
Text {
|
||||||
iconSource: "../../../../../scripts/system/assets/images/tools/assets-01.svg"
|
color: "#ffffff"
|
||||||
text: "ASSETS"
|
text: "Choose an Entity Type to Create:"
|
||||||
onClicked: {
|
font.pixelSize: 14
|
||||||
editRoot.sendToScript({
|
font.bold: true
|
||||||
method: "newEntityButtonClicked", params: { buttonName: "openAssetBrowserButton" }
|
anchors.top: parent.top
|
||||||
});
|
anchors.topMargin: 28
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 28
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
id: createEntitiesFlow
|
||||||
|
spacing: 35
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 55
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 55
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 70
|
||||||
|
|
||||||
|
|
||||||
|
NewEntityButton {
|
||||||
|
icon: "icons/create-icons/94-model-01.svg"
|
||||||
|
text: "MODEL"
|
||||||
|
onClicked: {
|
||||||
|
editRoot.sendToScript({
|
||||||
|
method: "newEntityButtonClicked", params: { buttonName: "newModelButton" }
|
||||||
|
});
|
||||||
|
editTabView.currentIndex = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewEntityButton {
|
||||||
|
icon: "icons/create-icons/21-cube-01.svg"
|
||||||
|
text: "CUBE"
|
||||||
|
onClicked: {
|
||||||
|
editRoot.sendToScript({
|
||||||
|
method: "newEntityButtonClicked", params: { buttonName: "newCubeButton" }
|
||||||
|
});
|
||||||
|
editTabView.currentIndex = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewEntityButton {
|
||||||
|
icon: "icons/create-icons/22-sphere-01.svg"
|
||||||
|
text: "SPHERE"
|
||||||
|
onClicked: {
|
||||||
|
editRoot.sendToScript({
|
||||||
|
method: "newEntityButtonClicked", params: { buttonName: "newSphereButton" }
|
||||||
|
});
|
||||||
|
editTabView.currentIndex = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewEntityButton {
|
||||||
|
icon: "icons/create-icons/24-light-01.svg"
|
||||||
|
text: "LIGHT"
|
||||||
|
onClicked: {
|
||||||
|
editRoot.sendToScript({
|
||||||
|
method: "newEntityButtonClicked", params: { buttonName: "newLightButton" }
|
||||||
|
});
|
||||||
|
editTabView.currentIndex = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewEntityButton {
|
||||||
|
icon: "icons/create-icons/20-text-01.svg"
|
||||||
|
text: "TEXT"
|
||||||
|
onClicked: {
|
||||||
|
editRoot.sendToScript({
|
||||||
|
method: "newEntityButtonClicked", params: { buttonName: "newTextButton" }
|
||||||
|
});
|
||||||
|
editTabView.currentIndex = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewEntityButton {
|
||||||
|
icon: "icons/create-icons/25-web-1-01.svg"
|
||||||
|
text: "WEB"
|
||||||
|
onClicked: {
|
||||||
|
editRoot.sendToScript({
|
||||||
|
method: "newEntityButtonClicked", params: { buttonName: "newWebButton" }
|
||||||
|
});
|
||||||
|
editTabView.currentIndex = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewEntityButton {
|
||||||
|
icon: "icons/create-icons/23-zone-01.svg"
|
||||||
|
text: "ZONE"
|
||||||
|
onClicked: {
|
||||||
|
editRoot.sendToScript({
|
||||||
|
method: "newEntityButtonClicked", params: { buttonName: "newZoneButton" }
|
||||||
|
});
|
||||||
|
editTabView.currentIndex = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewEntityButton {
|
||||||
|
icon: "icons/create-icons/90-particles-01.svg"
|
||||||
|
text: "PARTICLE"
|
||||||
|
onClicked: {
|
||||||
|
editRoot.sendToScript({
|
||||||
|
method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" }
|
||||||
|
});
|
||||||
|
editTabView.currentIndex = 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Item {
|
||||||
iconSource: "../../../../../scripts/system/assets/images/tools/model-01.svg"
|
id: assetServerButton
|
||||||
text: "MODEL"
|
width: 370
|
||||||
onClicked: {
|
height: 38
|
||||||
editRoot.sendToScript({
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
method: "newEntityButtonClicked", params: { buttonName: "newModelButton" }
|
anchors.top: createEntitiesFlow.bottom
|
||||||
});
|
anchors.topMargin: 35
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: assetServerButtonBg
|
||||||
|
color: "black"
|
||||||
|
opacity: 1
|
||||||
|
radius: 6
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 0
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
Rectangle {
|
||||||
iconSource: "../../../../../scripts/system/assets/images/tools/cube-01.svg"
|
id: assetServerButtonGradient
|
||||||
text: "CUBE"
|
gradient: Gradient {
|
||||||
onClicked: {
|
GradientStop {
|
||||||
editRoot.sendToScript({
|
position: 0
|
||||||
method: "newEntityButtonClicked", params: { buttonName: "newCubeButton" }
|
color: "#383838"
|
||||||
});
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
GradientStop {
|
||||||
iconSource: "../../../../../scripts/system/assets/images/tools/sphere-01.svg"
|
position: 1
|
||||||
text: "SPHERE"
|
color: "black"
|
||||||
onClicked: {
|
}
|
||||||
editRoot.sendToScript({
|
}
|
||||||
method: "newEntityButtonClicked", params: { buttonName: "newSphereButton" }
|
opacity: 1
|
||||||
});
|
radius: 6
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 0
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
Text {
|
||||||
iconSource: "../../../../../scripts/system/assets/images/tools/light-01.svg"
|
color: "#ffffff"
|
||||||
text: "LIGHT"
|
text: "OPEN THIS DOMAIN'S ASSET SERVER"
|
||||||
onClicked: {
|
font.bold: true
|
||||||
editRoot.sendToScript({
|
font.pixelSize: 14
|
||||||
method: "newEntityButtonClicked", params: { buttonName: "newLightButton" }
|
anchors.centerIn: parent
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
MouseArea {
|
||||||
iconSource: "../../../../../scripts/system/assets/images/tools/text-01.svg"
|
anchors.fill: parent
|
||||||
text: "TEXT"
|
hoverEnabled: true
|
||||||
onClicked: {
|
enabled: true
|
||||||
editRoot.sendToScript({
|
onClicked: {
|
||||||
method: "newEntityButtonClicked", params: { buttonName: "newTextButton" }
|
editRoot.sendToScript({
|
||||||
});
|
method: "newEntityButtonClicked", params: { buttonName: "openAssetBrowserButton" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onEntered: {
|
||||||
|
assetServerButton.state = "hover state";
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
assetServerButton.state = "base state";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
states: [
|
||||||
iconSource: "../../../../../scripts/system/assets/images/tools/web-01.svg"
|
State {
|
||||||
text: "WEB"
|
name: "hover state"
|
||||||
onClicked: {
|
|
||||||
editRoot.sendToScript({
|
|
||||||
method: "newEntityButtonClicked", params: { buttonName: "newWebButton" }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
PropertyChanges {
|
||||||
iconSource: "../../../../../scripts/system/assets/images/tools/zone-01.svg"
|
target: assetServerButtonGradient
|
||||||
text: "ZONE"
|
opacity: 0
|
||||||
onClicked: {
|
}
|
||||||
editRoot.sendToScript({
|
},
|
||||||
method: "newEntityButtonClicked", params: { buttonName: "newZoneButton" }
|
State {
|
||||||
});
|
name: "base state"
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
PropertyChanges {
|
||||||
iconSource: "../../../../../scripts/system/assets/images/tools/particle-01.svg"
|
target: assetServerButtonGradient
|
||||||
text: "PARTICLE"
|
opacity: 1
|
||||||
onClicked: {
|
}
|
||||||
editRoot.sendToScript({
|
}
|
||||||
method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" }
|
]
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tab {
|
Tab {
|
||||||
title: "List"
|
title: "LIST"
|
||||||
active: true
|
active: true
|
||||||
enabled: true
|
enabled: true
|
||||||
property string originalUrl: ""
|
property string originalUrl: ""
|
||||||
|
@ -150,7 +266,7 @@ StackView {
|
||||||
}
|
}
|
||||||
|
|
||||||
Tab {
|
Tab {
|
||||||
title: "Properties"
|
title: "PROPERTIES"
|
||||||
active: true
|
active: true
|
||||||
enabled: true
|
enabled: true
|
||||||
property string originalUrl: ""
|
property string originalUrl: ""
|
||||||
|
@ -165,7 +281,7 @@ StackView {
|
||||||
}
|
}
|
||||||
|
|
||||||
Tab {
|
Tab {
|
||||||
title: "Grid"
|
title: "GRID"
|
||||||
active: true
|
active: true
|
||||||
enabled: true
|
enabled: true
|
||||||
property string originalUrl: ""
|
property string originalUrl: ""
|
||||||
|
@ -180,7 +296,7 @@ StackView {
|
||||||
}
|
}
|
||||||
|
|
||||||
Tab {
|
Tab {
|
||||||
title: "Particles"
|
title: "P"
|
||||||
active: true
|
active: true
|
||||||
enabled: true
|
enabled: true
|
||||||
property string originalUrl: ""
|
property string originalUrl: ""
|
||||||
|
@ -198,17 +314,40 @@ StackView {
|
||||||
style: TabViewStyle {
|
style: TabViewStyle {
|
||||||
frameOverlap: 1
|
frameOverlap: 1
|
||||||
tab: Rectangle {
|
tab: Rectangle {
|
||||||
color: styleData.selected ? "slategrey" :"grey"
|
color: styleData.selected ? "#404040" :"black"
|
||||||
implicitWidth: text.width + 25
|
implicitWidth: text.width + 42
|
||||||
implicitHeight: 60
|
implicitHeight: 40
|
||||||
radius: 2
|
|
||||||
Text {
|
Text {
|
||||||
id: text
|
id: text
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: styleData.title
|
text: styleData.title
|
||||||
|
font.pixelSize: 16
|
||||||
|
font.bold: true
|
||||||
color: styleData.selected ? "white" : "white"
|
color: styleData.selected ? "white" : "white"
|
||||||
|
property string glyphtext: ""
|
||||||
|
HiFiGlyphs {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
size: 30
|
||||||
|
color: "#ffffff"
|
||||||
|
text: text.glyphtext
|
||||||
|
}
|
||||||
|
Component.onCompleted: if (styleData.title == "P") {
|
||||||
|
text.text = " ";
|
||||||
|
text.glyphtext = "\ue004";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tabBar: Rectangle {
|
||||||
|
color: "black"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
160
interface/resources/qml/hifi/tablet/NewEntityButton.qml
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: newEntityButton
|
||||||
|
property var uuid;
|
||||||
|
property string text: "ENTITY"
|
||||||
|
property string icon: "icons/edit-icon.svg"
|
||||||
|
property string activeText: newEntityButton.text
|
||||||
|
property string activeIcon: newEntityButton.icon
|
||||||
|
property bool isActive: false
|
||||||
|
property bool inDebugMode: false
|
||||||
|
property bool isEntered: false
|
||||||
|
property double sortOrder: 100
|
||||||
|
property int stableOrder: 0
|
||||||
|
property var tabletRoot;
|
||||||
|
width: 100
|
||||||
|
height: 100
|
||||||
|
|
||||||
|
signal clicked()
|
||||||
|
|
||||||
|
function changeProperty(key, value) {
|
||||||
|
tabletButton[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
onIsActiveChanged: {
|
||||||
|
if (tabletButton.isEntered) {
|
||||||
|
tabletButton.state = (tabletButton.isActive) ? "hover active state" : "hover sate";
|
||||||
|
} else {
|
||||||
|
tabletButton.state = (tabletButton.isActive) ? "active state" : "base sate";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: buttonBg
|
||||||
|
color: "#1c1c1c"
|
||||||
|
opacity: 1
|
||||||
|
radius: 8
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function urlHelper(src) {
|
||||||
|
if (src.match(/\bhttp/)) {
|
||||||
|
return src;
|
||||||
|
} else {
|
||||||
|
return "../../../" + src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: buttonOutline
|
||||||
|
color: "#00000000"
|
||||||
|
opacity: 0
|
||||||
|
radius: 8
|
||||||
|
z: 1
|
||||||
|
border.width: 2
|
||||||
|
border.color: "#ffffff"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
DropShadow {
|
||||||
|
id: glow
|
||||||
|
visible: false
|
||||||
|
anchors.fill: parent
|
||||||
|
horizontalOffset: 0
|
||||||
|
verticalOffset: 0
|
||||||
|
color: "#ffffff"
|
||||||
|
radius: 20
|
||||||
|
z: -1
|
||||||
|
samples: 41
|
||||||
|
source: buttonOutline
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: icon
|
||||||
|
width: 50
|
||||||
|
height: 50
|
||||||
|
visible: false
|
||||||
|
anchors.bottom: text.top
|
||||||
|
anchors.bottomMargin: 5
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
fillMode: Image.Stretch
|
||||||
|
source: newEntityButton.urlHelper(newEntityButton.icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorOverlay {
|
||||||
|
id: iconColorOverlay
|
||||||
|
anchors.fill: icon
|
||||||
|
source: icon
|
||||||
|
color: "#ffffff"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: text
|
||||||
|
color: "#ffffff"
|
||||||
|
text: newEntityButton.text
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: 16
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 12
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
enabled: true
|
||||||
|
onClicked: {
|
||||||
|
newEntityButton.clicked();
|
||||||
|
}
|
||||||
|
onEntered: {
|
||||||
|
newEntityButton.state = "hover state";
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
newEntityButton.state = "base state";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "hover state"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: buttonOutline
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: glow
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "base state"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: glow
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -313,6 +313,6 @@ Fadable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseEntered: console.log("Mouse entered " + window)
|
// onMouseEntered: console.log("Mouse entered " + window)
|
||||||
onMouseExited: console.log("Mouse exited " + window)
|
// onMouseExited: console.log("Mouse exited " + window)
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,8 +227,7 @@ void MyAvatar::simulateAttachments(float deltaTime) {
|
||||||
// don't update attachments here, do it in harvestResultsFromPhysicsSimulation()
|
// don't update attachments here, do it in harvestResultsFromPhysicsSimulation()
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray MyAvatar::toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector<JointData>& lastSentJointData,
|
QByteArray MyAvatar::toByteArrayStateful(AvatarDataDetail dataDetail) {
|
||||||
bool distanceAdjust, glm::vec3 viewerPosition, QVector<JointData>* sentJointDataOut) {
|
|
||||||
CameraMode mode = qApp->getCamera()->getMode();
|
CameraMode mode = qApp->getCamera()->getMode();
|
||||||
_globalPosition = getPosition();
|
_globalPosition = getPosition();
|
||||||
_globalBoundingBoxDimensions.x = _characterController.getCapsuleRadius();
|
_globalBoundingBoxDimensions.x = _characterController.getCapsuleRadius();
|
||||||
|
@ -239,12 +238,12 @@ QByteArray MyAvatar::toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTi
|
||||||
// fake the avatar position that is sent up to the AvatarMixer
|
// fake the avatar position that is sent up to the AvatarMixer
|
||||||
glm::vec3 oldPosition = getPosition();
|
glm::vec3 oldPosition = getPosition();
|
||||||
setPosition(getSkeletonPosition());
|
setPosition(getSkeletonPosition());
|
||||||
QByteArray array = AvatarData::toByteArray(dataDetail, lastSentTime, lastSentJointData, distanceAdjust, viewerPosition, sentJointDataOut);
|
QByteArray array = AvatarData::toByteArrayStateful(dataDetail);
|
||||||
// copy the correct position back
|
// copy the correct position back
|
||||||
setPosition(oldPosition);
|
setPosition(oldPosition);
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
return AvatarData::toByteArray(dataDetail, lastSentTime, lastSentJointData, distanceAdjust, viewerPosition, sentJointDataOut);
|
return AvatarData::toByteArrayStateful(dataDetail);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::centerBody() {
|
void MyAvatar::centerBody() {
|
||||||
|
|
|
@ -338,8 +338,7 @@ private:
|
||||||
glm::quat getWorldBodyOrientation() const;
|
glm::quat getWorldBodyOrientation() const;
|
||||||
|
|
||||||
|
|
||||||
virtual QByteArray toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector<JointData>& lastSentJointData,
|
virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail) override;
|
||||||
bool distanceAdjust = false, glm::vec3 viewerPosition = glm::vec3(0), QVector<JointData>* sentJointDataOut = nullptr) override;
|
|
||||||
|
|
||||||
void simulate(float deltaTime);
|
void simulate(float deltaTime);
|
||||||
void updateFromTrackers(float deltaTime);
|
void updateFromTrackers(float deltaTime);
|
||||||
|
|
|
@ -120,10 +120,6 @@ void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) {
|
||||||
updateAttitude();
|
updateAttitude();
|
||||||
}
|
}
|
||||||
|
|
||||||
float AvatarData::getTargetScale() const {
|
|
||||||
return _targetScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarData::setTargetScale(float targetScale) {
|
void AvatarData::setTargetScale(float targetScale) {
|
||||||
auto newValue = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
auto newValue = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
||||||
if (_targetScale != newValue) {
|
if (_targetScale != newValue) {
|
||||||
|
@ -141,10 +137,10 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) {
|
||||||
_handPosition = glm::inverse(getOrientation()) * (handPosition - getPosition());
|
_handPosition = glm::inverse(getOrientation()) * (handPosition - getPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarData::lazyInitHeadData() {
|
void AvatarData::lazyInitHeadData() const {
|
||||||
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
||||||
if (!_headData) {
|
if (!_headData) {
|
||||||
_headData = new HeadData(this);
|
_headData = new HeadData(const_cast<AvatarData*>(this));
|
||||||
}
|
}
|
||||||
if (_forceFaceTrackerConnected) {
|
if (_forceFaceTrackerConnected) {
|
||||||
_headData->_isFaceTrackerConnected = true;
|
_headData->_isFaceTrackerConnected = true;
|
||||||
|
@ -152,39 +148,7 @@ void AvatarData::lazyInitHeadData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool AvatarData::avatarBoundingBoxChangedSince(quint64 time) {
|
float AvatarData::getDistanceBasedMinRotationDOT(glm::vec3 viewerPosition) const {
|
||||||
return _avatarBoundingBoxChanged >= time;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AvatarData::avatarScaleChangedSince(quint64 time) {
|
|
||||||
return _avatarScaleChanged >= time;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AvatarData::lookAtPositionChangedSince(quint64 time) {
|
|
||||||
return _headData->lookAtPositionChangedSince(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AvatarData::audioLoudnessChangedSince(quint64 time) {
|
|
||||||
return _headData->audioLoudnessChangedSince(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AvatarData::sensorToWorldMatrixChangedSince(quint64 time) {
|
|
||||||
return _sensorToWorldMatrixChanged >= time;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AvatarData::additionalFlagsChangedSince(quint64 time) {
|
|
||||||
return _additionalFlagsChanged >= time;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AvatarData::parentInfoChangedSince(quint64 time) {
|
|
||||||
return _parentChanged >= time;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AvatarData::faceTrackerInfoChangedSince(quint64 time) {
|
|
||||||
return true; // FIXME!
|
|
||||||
}
|
|
||||||
|
|
||||||
float AvatarData::getDistanceBasedMinRotationDOT(glm::vec3 viewerPosition) {
|
|
||||||
auto distance = glm::distance(_globalPosition, viewerPosition);
|
auto distance = glm::distance(_globalPosition, viewerPosition);
|
||||||
float result = ROTATION_CHANGE_179D; // assume worst
|
float result = ROTATION_CHANGE_179D; // assume worst
|
||||||
if (distance < AVATAR_DISTANCE_LEVEL_1) {
|
if (distance < AVATAR_DISTANCE_LEVEL_1) {
|
||||||
|
@ -199,20 +163,24 @@ float AvatarData::getDistanceBasedMinRotationDOT(glm::vec3 viewerPosition) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
float AvatarData::getDistanceBasedMinTranslationDistance(glm::vec3 viewerPosition) {
|
float AvatarData::getDistanceBasedMinTranslationDistance(glm::vec3 viewerPosition) const {
|
||||||
return AVATAR_MIN_TRANSLATION; // Eventually make this distance sensitive as well
|
return AVATAR_MIN_TRANSLATION; // Eventually make this distance sensitive as well
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector<JointData>& lastSentJointData,
|
// we want to track outbound data in this case...
|
||||||
bool distanceAdjust, glm::vec3 viewerPosition, QVector<JointData>* sentJointDataOut) {
|
QByteArray AvatarData::toByteArrayStateful(AvatarDataDetail dataDetail) {
|
||||||
|
AvatarDataPacket::HasFlags hasFlagsOut;
|
||||||
|
auto lastSentTime = _lastToByteArray;
|
||||||
|
_lastToByteArray = usecTimestampNow();
|
||||||
|
return AvatarData::toByteArray(dataDetail, lastSentTime, getLastSentJointData(),
|
||||||
|
hasFlagsOut, false, false, glm::vec3(0), nullptr,
|
||||||
|
&_outboundDataRate);
|
||||||
|
}
|
||||||
|
|
||||||
// if no timestamp was included, then assume the avatarData is single instance
|
QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector<JointData>& lastSentJointData,
|
||||||
// and is tracking its own last encoding time.
|
AvatarDataPacket::HasFlags& hasFlagsOut, bool dropFaceTracking, bool distanceAdjust,
|
||||||
if (lastSentTime == 0) {
|
glm::vec3 viewerPosition, QVector<JointData>* sentJointDataOut, AvatarDataRate* outboundDataRateOut) const {
|
||||||
lastSentTime = _lastToByteArray;
|
|
||||||
_lastToByteArray = usecTimestampNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cullSmallChanges = (dataDetail == CullSmallData);
|
bool cullSmallChanges = (dataDetail == CullSmallData);
|
||||||
bool sendAll = (dataDetail == SendAllData);
|
bool sendAll = (dataDetail == SendAllData);
|
||||||
|
@ -259,26 +227,26 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
// separately
|
// separately
|
||||||
bool hasParentInfo = sendAll || parentInfoChangedSince(lastSentTime);
|
bool hasParentInfo = sendAll || parentInfoChangedSince(lastSentTime);
|
||||||
bool hasAvatarLocalPosition = hasParent() && (sendAll ||
|
bool hasAvatarLocalPosition = hasParent() && (sendAll ||
|
||||||
tranlationChangedSince(lastSentTime) ||
|
tranlationChangedSince(lastSentTime) ||
|
||||||
parentInfoChangedSince(lastSentTime));
|
parentInfoChangedSince(lastSentTime));
|
||||||
|
|
||||||
bool hasFaceTrackerInfo = hasFaceTracker() && (sendAll || faceTrackerInfoChangedSince(lastSentTime));
|
bool hasFaceTrackerInfo = !dropFaceTracking && hasFaceTracker() && (sendAll || faceTrackerInfoChangedSince(lastSentTime));
|
||||||
bool hasJointData = sendAll || !sendMinimum;
|
bool hasJointData = sendAll || !sendMinimum;
|
||||||
|
|
||||||
// Leading flags, to indicate how much data is actually included in the packet...
|
// Leading flags, to indicate how much data is actually included in the packet...
|
||||||
AvatarDataPacket::HasFlags packetStateFlags =
|
AvatarDataPacket::HasFlags packetStateFlags =
|
||||||
(hasAvatarGlobalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION : 0)
|
(hasAvatarGlobalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION : 0)
|
||||||
| (hasAvatarBoundingBox ? AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX : 0)
|
| (hasAvatarBoundingBox ? AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX : 0)
|
||||||
| (hasAvatarOrientation ? AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION : 0)
|
| (hasAvatarOrientation ? AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION : 0)
|
||||||
| (hasAvatarScale ? AvatarDataPacket::PACKET_HAS_AVATAR_SCALE : 0)
|
| (hasAvatarScale ? AvatarDataPacket::PACKET_HAS_AVATAR_SCALE : 0)
|
||||||
| (hasLookAtPosition ? AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION : 0)
|
| (hasLookAtPosition ? AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION : 0)
|
||||||
| (hasAudioLoudness ? AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS : 0)
|
| (hasAudioLoudness ? AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS : 0)
|
||||||
| (hasSensorToWorldMatrix ? AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX : 0)
|
| (hasSensorToWorldMatrix ? AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX : 0)
|
||||||
| (hasAdditionalFlags ? AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS : 0)
|
| (hasAdditionalFlags ? AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS : 0)
|
||||||
| (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0)
|
| (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0)
|
||||||
| (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0)
|
| (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0)
|
||||||
| (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0)
|
| (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0)
|
||||||
| (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0);
|
| (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0);
|
||||||
|
|
||||||
memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags));
|
memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags));
|
||||||
destinationBuffer += sizeof(packetStateFlags);
|
destinationBuffer += sizeof(packetStateFlags);
|
||||||
|
@ -293,7 +261,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
|
|
||||||
_globalPositionRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->globalPositionRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasAvatarBoundingBox) {
|
if (hasAvatarBoundingBox) {
|
||||||
|
@ -311,7 +281,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox);
|
destinationBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
_avatarBoundingBoxRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->avatarBoundingBoxRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasAvatarOrientation) {
|
if (hasAvatarOrientation) {
|
||||||
|
@ -320,7 +292,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, localOrientation);
|
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, localOrientation);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
_avatarOrientationRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->avatarOrientationRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasAvatarScale) {
|
if (hasAvatarScale) {
|
||||||
|
@ -331,7 +305,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += sizeof(AvatarDataPacket::AvatarScale);
|
destinationBuffer += sizeof(AvatarDataPacket::AvatarScale);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
_avatarScaleRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->avatarScaleRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasLookAtPosition) {
|
if (hasLookAtPosition) {
|
||||||
|
@ -344,7 +320,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += sizeof(AvatarDataPacket::LookAtPosition);
|
destinationBuffer += sizeof(AvatarDataPacket::LookAtPosition);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
_lookAtPositionRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->lookAtPositionRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasAudioLoudness) {
|
if (hasAudioLoudness) {
|
||||||
|
@ -354,7 +332,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += sizeof(AvatarDataPacket::AudioLoudness);
|
destinationBuffer += sizeof(AvatarDataPacket::AudioLoudness);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
_audioLoudnessRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->audioLoudnessRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasSensorToWorldMatrix) {
|
if (hasSensorToWorldMatrix) {
|
||||||
|
@ -370,7 +350,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += sizeof(AvatarDataPacket::SensorToWorldMatrix);
|
destinationBuffer += sizeof(AvatarDataPacket::SensorToWorldMatrix);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
_sensorToWorldRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->sensorToWorldRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasAdditionalFlags) {
|
if (hasAdditionalFlags) {
|
||||||
|
@ -403,7 +385,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += sizeof(AvatarDataPacket::AdditionalFlags);
|
destinationBuffer += sizeof(AvatarDataPacket::AdditionalFlags);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
_additionalFlagsRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->additionalFlagsRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasParentInfo) {
|
if (hasParentInfo) {
|
||||||
|
@ -415,7 +399,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += sizeof(AvatarDataPacket::ParentInfo);
|
destinationBuffer += sizeof(AvatarDataPacket::ParentInfo);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
_parentInfoRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->parentInfoRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasAvatarLocalPosition) {
|
if (hasAvatarLocalPosition) {
|
||||||
|
@ -428,7 +414,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += sizeof(AvatarDataPacket::AvatarLocalPosition);
|
destinationBuffer += sizeof(AvatarDataPacket::AvatarLocalPosition);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
_localPositionRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->localPositionRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it is connected, pack up the data
|
// If it is connected, pack up the data
|
||||||
|
@ -448,7 +436,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
|
destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
_faceTrackerRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->faceTrackerRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it is connected, pack up the data
|
// If it is connected, pack up the data
|
||||||
|
@ -540,9 +530,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
|
glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
|
||||||
if (data.translationSet) {
|
if (data.translationSet) {
|
||||||
validity |= (1 << validityBit);
|
validity |= (1 << validityBit);
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
translationSentCount++;
|
translationSentCount++;
|
||||||
#endif
|
#endif
|
||||||
maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension);
|
maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension);
|
||||||
maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension);
|
maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension);
|
||||||
maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension);
|
maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension);
|
||||||
|
@ -592,24 +582,25 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
if (sendAll) {
|
if (sendAll) {
|
||||||
qCDebug(avatars) << "AvatarData::toByteArray" << cullSmallChanges << sendAll
|
qCDebug(avatars) << "AvatarData::toByteArray" << cullSmallChanges << sendAll
|
||||||
<< "rotations:" << rotationSentCount << "translations:" << translationSentCount
|
<< "rotations:" << rotationSentCount << "translations:" << translationSentCount
|
||||||
<< "largest:" << maxTranslationDimension
|
<< "largest:" << maxTranslationDimension
|
||||||
<< "size:"
|
<< "size:"
|
||||||
<< (beforeRotations - startPosition) << "+"
|
<< (beforeRotations - startPosition) << "+"
|
||||||
<< (beforeTranslations - beforeRotations) << "+"
|
<< (beforeTranslations - beforeRotations) << "+"
|
||||||
<< (destinationBuffer - beforeTranslations) << "="
|
<< (destinationBuffer - beforeTranslations) << "="
|
||||||
<< (destinationBuffer - startPosition);
|
<< (destinationBuffer - startPosition);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int numBytes = destinationBuffer - startSection;
|
int numBytes = destinationBuffer - startSection;
|
||||||
_jointDataRateOutbound.increment(numBytes);
|
if (outboundDataRateOut) {
|
||||||
|
outboundDataRateOut->jointDataRate.increment(numBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int avatarDataSize = destinationBuffer - startPosition;
|
int avatarDataSize = destinationBuffer - startPosition;
|
||||||
return avatarDataByteArray.left(avatarDataSize);
|
return avatarDataByteArray.left(avatarDataSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: This is never used in a "distanceAdjust" mode, so it's ok that it doesn't use a variable minimum rotation/translation
|
// NOTE: This is never used in a "distanceAdjust" mode, so it's ok that it doesn't use a variable minimum rotation/translation
|
||||||
void AvatarData::doneEncoding(bool cullSmallChanges) {
|
void AvatarData::doneEncoding(bool cullSmallChanges) {
|
||||||
// The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData.
|
// The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData.
|
||||||
|
@ -1089,29 +1080,29 @@ float AvatarData::getDataRate(const QString& rateName) const {
|
||||||
} else if (rateName == "jointData") {
|
} else if (rateName == "jointData") {
|
||||||
return _jointDataRate.rate() / BYTES_PER_KILOBIT;
|
return _jointDataRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "globalPositionOutbound") {
|
} else if (rateName == "globalPositionOutbound") {
|
||||||
return _globalPositionRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.globalPositionRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "localPositionOutbound") {
|
} else if (rateName == "localPositionOutbound") {
|
||||||
return _localPositionRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.localPositionRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "avatarBoundingBoxOutbound") {
|
} else if (rateName == "avatarBoundingBoxOutbound") {
|
||||||
return _avatarBoundingBoxRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.avatarBoundingBoxRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "avatarOrientationOutbound") {
|
} else if (rateName == "avatarOrientationOutbound") {
|
||||||
return _avatarOrientationRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.avatarOrientationRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "avatarScaleOutbound") {
|
} else if (rateName == "avatarScaleOutbound") {
|
||||||
return _avatarScaleRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.avatarScaleRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "lookAtPositionOutbound") {
|
} else if (rateName == "lookAtPositionOutbound") {
|
||||||
return _lookAtPositionRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.lookAtPositionRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "audioLoudnessOutbound") {
|
} else if (rateName == "audioLoudnessOutbound") {
|
||||||
return _audioLoudnessRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.audioLoudnessRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "sensorToWorkMatrixOutbound") {
|
} else if (rateName == "sensorToWorkMatrixOutbound") {
|
||||||
return _sensorToWorldRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.sensorToWorldRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "additionalFlagsOutbound") {
|
} else if (rateName == "additionalFlagsOutbound") {
|
||||||
return _additionalFlagsRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.additionalFlagsRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "parentInfoOutbound") {
|
} else if (rateName == "parentInfoOutbound") {
|
||||||
return _parentInfoRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.parentInfoRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "faceTrackerOutbound") {
|
} else if (rateName == "faceTrackerOutbound") {
|
||||||
return _faceTrackerRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.faceTrackerRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "jointDataOutbound") {
|
} else if (rateName == "jointDataOutbound") {
|
||||||
return _jointDataRateOutbound.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.jointDataRate.rate() / BYTES_PER_KILOBIT;
|
||||||
}
|
}
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
@ -1445,7 +1436,7 @@ void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& ide
|
||||||
}
|
}
|
||||||
|
|
||||||
static const QUrl emptyURL("");
|
static const QUrl emptyURL("");
|
||||||
const QUrl& AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) {
|
QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const {
|
||||||
// We don't put file urls on the wire, but instead convert to empty.
|
// We don't put file urls on the wire, but instead convert to empty.
|
||||||
return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL;
|
return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL;
|
||||||
}
|
}
|
||||||
|
@ -1483,7 +1474,7 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray AvatarData::identityByteArray() {
|
QByteArray AvatarData::identityByteArray() const {
|
||||||
QByteArray identityData;
|
QByteArray identityData;
|
||||||
QDataStream identityStream(&identityData, QIODevice::Append);
|
QDataStream identityStream(&identityData, QIODevice::Append);
|
||||||
const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL);
|
const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL);
|
||||||
|
@ -1646,13 +1637,7 @@ void AvatarData::sendAvatarDataPacket() {
|
||||||
|
|
||||||
bool cullSmallData = (randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO);
|
bool cullSmallData = (randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO);
|
||||||
auto dataDetail = cullSmallData ? SendAllData : CullSmallData;
|
auto dataDetail = cullSmallData ? SendAllData : CullSmallData;
|
||||||
QVector<JointData> lastSentJointData;
|
QByteArray avatarByteArray = toByteArrayStateful(dataDetail);
|
||||||
{
|
|
||||||
QReadLocker readLock(&_jointDataLock);
|
|
||||||
_lastSentJointData.resize(_jointData.size());
|
|
||||||
lastSentJointData = _lastSentJointData;
|
|
||||||
}
|
|
||||||
QByteArray avatarByteArray = toByteArray(dataDetail, 0, lastSentJointData);
|
|
||||||
doneEncoding(cullSmallData);
|
doneEncoding(cullSmallData);
|
||||||
|
|
||||||
static AvatarDataSequenceNumber sequenceNumber = 0;
|
static AvatarDataSequenceNumber sequenceNumber = 0;
|
||||||
|
|
|
@ -288,6 +288,23 @@ class AttachmentData;
|
||||||
class Transform;
|
class Transform;
|
||||||
using TransformPointer = std::shared_ptr<Transform>;
|
using TransformPointer = std::shared_ptr<Transform>;
|
||||||
|
|
||||||
|
class AvatarDataRate {
|
||||||
|
public:
|
||||||
|
RateCounter<> globalPositionRate;
|
||||||
|
RateCounter<> localPositionRate;
|
||||||
|
RateCounter<> avatarBoundingBoxRate;
|
||||||
|
RateCounter<> avatarOrientationRate;
|
||||||
|
RateCounter<> avatarScaleRate;
|
||||||
|
RateCounter<> lookAtPositionRate;
|
||||||
|
RateCounter<> audioLoudnessRate;
|
||||||
|
RateCounter<> sensorToWorldRate;
|
||||||
|
RateCounter<> additionalFlagsRate;
|
||||||
|
RateCounter<> parentInfoRate;
|
||||||
|
RateCounter<> faceTrackerRate;
|
||||||
|
RateCounter<> jointDataRate;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class AvatarData : public QObject, public SpatiallyNestable {
|
class AvatarData : public QObject, public SpatiallyNestable {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -351,8 +368,11 @@ public:
|
||||||
SendAllData
|
SendAllData
|
||||||
} AvatarDataDetail;
|
} AvatarDataDetail;
|
||||||
|
|
||||||
|
virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail);
|
||||||
|
|
||||||
virtual QByteArray toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector<JointData>& lastSentJointData,
|
virtual QByteArray toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector<JointData>& lastSentJointData,
|
||||||
bool distanceAdjust = false, glm::vec3 viewerPosition = glm::vec3(0), QVector<JointData>* sentJointDataOut = nullptr);
|
AvatarDataPacket::HasFlags& hasFlagsOut, bool dropFaceTracking, bool distanceAdjust, glm::vec3 viewerPosition,
|
||||||
|
QVector<JointData>* sentJointDataOut, AvatarDataRate* outboundDataRateOut = nullptr) const;
|
||||||
|
|
||||||
virtual void doneEncoding(bool cullSmallChanges);
|
virtual void doneEncoding(bool cullSmallChanges);
|
||||||
|
|
||||||
|
@ -380,7 +400,7 @@ public:
|
||||||
void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time.
|
void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time.
|
||||||
virtual void updateAttitude() {} // Tell skeleton mesh about changes
|
virtual void updateAttitude() {} // Tell skeleton mesh about changes
|
||||||
|
|
||||||
glm::quat getHeadOrientation() {
|
glm::quat getHeadOrientation() const {
|
||||||
lazyInitHeadData();
|
lazyInitHeadData();
|
||||||
return _headData->getOrientation();
|
return _headData->getOrientation();
|
||||||
}
|
}
|
||||||
|
@ -419,7 +439,6 @@ public:
|
||||||
void setAudioAverageLoudness(float value) { _headData->setAudioAverageLoudness(value); }
|
void setAudioAverageLoudness(float value) { _headData->setAudioAverageLoudness(value); }
|
||||||
|
|
||||||
// Scale
|
// Scale
|
||||||
float getTargetScale() const;
|
|
||||||
virtual void setTargetScale(float targetScale);
|
virtual void setTargetScale(float targetScale);
|
||||||
|
|
||||||
float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); }
|
float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); }
|
||||||
|
@ -494,7 +513,7 @@ public:
|
||||||
// displayNameChanged returns true if displayName has changed, false otherwise.
|
// displayNameChanged returns true if displayName has changed, false otherwise.
|
||||||
void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged);
|
void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged);
|
||||||
|
|
||||||
QByteArray identityByteArray();
|
QByteArray identityByteArray() const;
|
||||||
|
|
||||||
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
|
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
|
||||||
const QString& getDisplayName() const { return _displayName; }
|
const QString& getDisplayName() const { return _displayName; }
|
||||||
|
@ -534,8 +553,8 @@ public:
|
||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
void fromJson(const QJsonObject& json, bool useFrameSkeleton = true);
|
void fromJson(const QJsonObject& json, bool useFrameSkeleton = true);
|
||||||
|
|
||||||
glm::vec3 getClientGlobalPosition() { return _globalPosition; }
|
glm::vec3 getClientGlobalPosition() const { return _globalPosition; }
|
||||||
glm::vec3 getGlobalBoundingBoxCorner() { return _globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions; }
|
glm::vec3 getGlobalBoundingBoxCorner() const { return _globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions; }
|
||||||
|
|
||||||
Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const;
|
Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const;
|
||||||
Q_INVOKABLE void setAvatarEntityData(const AvatarEntityMap& avatarEntityData);
|
Q_INVOKABLE void setAvatarEntityData(const AvatarEntityMap& avatarEntityData);
|
||||||
|
@ -550,7 +569,7 @@ public:
|
||||||
Q_INVOKABLE float getDataRate(const QString& rateName = QString("")) const;
|
Q_INVOKABLE float getDataRate(const QString& rateName = QString("")) const;
|
||||||
Q_INVOKABLE float getUpdateRate(const QString& rateName = QString("")) const;
|
Q_INVOKABLE float getUpdateRate(const QString& rateName = QString("")) const;
|
||||||
|
|
||||||
int getJointCount() { return _jointData.size(); }
|
int getJointCount() const { return _jointData.size(); }
|
||||||
|
|
||||||
QVector<JointData> getLastSentJointData() {
|
QVector<JointData> getLastSentJointData() {
|
||||||
QReadLocker readLock(&_jointDataLock);
|
QReadLocker readLock(&_jointDataLock);
|
||||||
|
@ -571,28 +590,27 @@ public slots:
|
||||||
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
|
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
|
||||||
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; }
|
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; }
|
||||||
|
|
||||||
float getTargetScale() { return _targetScale; }
|
float getTargetScale() const { return _targetScale; } // why is this a slot?
|
||||||
|
|
||||||
void resetLastSent() { _lastToByteArray = 0; }
|
void resetLastSent() { _lastToByteArray = 0; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void lazyInitHeadData();
|
void lazyInitHeadData() const;
|
||||||
|
|
||||||
float getDistanceBasedMinRotationDOT(glm::vec3 viewerPosition);
|
float getDistanceBasedMinRotationDOT(glm::vec3 viewerPosition) const;
|
||||||
float getDistanceBasedMinTranslationDistance(glm::vec3 viewerPosition);
|
float getDistanceBasedMinTranslationDistance(glm::vec3 viewerPosition) const;
|
||||||
|
|
||||||
bool avatarBoundingBoxChangedSince(quint64 time);
|
bool avatarBoundingBoxChangedSince(quint64 time) const { return _avatarBoundingBoxChanged >= time; }
|
||||||
bool avatarScaleChangedSince(quint64 time);
|
bool avatarScaleChangedSince(quint64 time) const { return _avatarScaleChanged >= time; }
|
||||||
bool lookAtPositionChangedSince(quint64 time);
|
bool lookAtPositionChangedSince(quint64 time) const { return _headData->lookAtPositionChangedSince(time); }
|
||||||
bool audioLoudnessChangedSince(quint64 time);
|
bool audioLoudnessChangedSince(quint64 time) const { return _headData->audioLoudnessChangedSince(time); }
|
||||||
bool sensorToWorldMatrixChangedSince(quint64 time);
|
bool sensorToWorldMatrixChangedSince(quint64 time) const { return _sensorToWorldMatrixChanged >= time; }
|
||||||
bool additionalFlagsChangedSince(quint64 time);
|
bool additionalFlagsChangedSince(quint64 time) const { return _additionalFlagsChanged >= time; }
|
||||||
|
bool parentInfoChangedSince(quint64 time) const { return _parentChanged >= time; }
|
||||||
|
bool faceTrackerInfoChangedSince(quint64 time) const { return true; } // FIXME
|
||||||
|
|
||||||
bool hasParent() { return !getParentID().isNull(); }
|
bool hasParent() const { return !getParentID().isNull(); }
|
||||||
bool parentInfoChangedSince(quint64 time);
|
bool hasFaceTracker() const { return _headData ? _headData->_isFaceTrackerConnected : false; }
|
||||||
|
|
||||||
bool hasFaceTracker() { return _headData ? _headData->_isFaceTrackerConnected : false; }
|
|
||||||
bool faceTrackerInfoChangedSince(quint64 time);
|
|
||||||
|
|
||||||
glm::vec3 _handPosition;
|
glm::vec3 _handPosition;
|
||||||
virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; }
|
virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; }
|
||||||
|
@ -616,7 +634,7 @@ protected:
|
||||||
bool _forceFaceTrackerConnected;
|
bool _forceFaceTrackerConnected;
|
||||||
bool _hasNewJointData { true }; // set in AvatarData, cleared in Avatar
|
bool _hasNewJointData { true }; // set in AvatarData, cleared in Avatar
|
||||||
|
|
||||||
HeadData* _headData { nullptr };
|
mutable HeadData* _headData { nullptr };
|
||||||
|
|
||||||
QUrl _skeletonModelURL;
|
QUrl _skeletonModelURL;
|
||||||
bool _firstSkeletonCheck { true };
|
bool _firstSkeletonCheck { true };
|
||||||
|
@ -624,7 +642,7 @@ protected:
|
||||||
QVector<AttachmentData> _attachmentData;
|
QVector<AttachmentData> _attachmentData;
|
||||||
QString _displayName;
|
QString _displayName;
|
||||||
QString _sessionDisplayName { };
|
QString _sessionDisplayName { };
|
||||||
const QUrl& cannonicalSkeletonModelURL(const QUrl& empty);
|
QUrl cannonicalSkeletonModelURL(const QUrl& empty) const;
|
||||||
|
|
||||||
float _displayNameTargetAlpha;
|
float _displayNameTargetAlpha;
|
||||||
float _displayNameAlpha;
|
float _displayNameAlpha;
|
||||||
|
@ -695,18 +713,7 @@ protected:
|
||||||
RateCounter<> _jointDataUpdateRate;
|
RateCounter<> _jointDataUpdateRate;
|
||||||
|
|
||||||
// Some rate data for outgoing data
|
// Some rate data for outgoing data
|
||||||
RateCounter<> _globalPositionRateOutbound;
|
AvatarDataRate _outboundDataRate;
|
||||||
RateCounter<> _localPositionRateOutbound;
|
|
||||||
RateCounter<> _avatarBoundingBoxRateOutbound;
|
|
||||||
RateCounter<> _avatarOrientationRateOutbound;
|
|
||||||
RateCounter<> _avatarScaleRateOutbound;
|
|
||||||
RateCounter<> _lookAtPositionRateOutbound;
|
|
||||||
RateCounter<> _audioLoudnessRateOutbound;
|
|
||||||
RateCounter<> _sensorToWorldRateOutbound;
|
|
||||||
RateCounter<> _additionalFlagsRateOutbound;
|
|
||||||
RateCounter<> _parentInfoRateOutbound;
|
|
||||||
RateCounter<> _faceTrackerRateOutbound;
|
|
||||||
RateCounter<> _jointDataRateOutbound;
|
|
||||||
|
|
||||||
glm::vec3 _globalBoundingBoxDimensions;
|
glm::vec3 _globalBoundingBoxDimensions;
|
||||||
glm::vec3 _globalBoundingBoxOffset;
|
glm::vec3 _globalBoundingBoxOffset;
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <tbb/concurrent_unordered_map.h>
|
#include <tbb/concurrent_unordered_map.h>
|
||||||
|
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include "DomainHandler.h"
|
#include "DomainHandler.h"
|
||||||
#include "Node.h"
|
#include "Node.h"
|
||||||
|
@ -182,15 +183,33 @@ public:
|
||||||
// This allows multiple threads (i.e. a thread pool) to share a lock
|
// This allows multiple threads (i.e. a thread pool) to share a lock
|
||||||
// without deadlocking when a dying node attempts to acquire a write lock
|
// without deadlocking when a dying node attempts to acquire a write lock
|
||||||
template<typename NestedNodeLambda>
|
template<typename NestedNodeLambda>
|
||||||
void nestedEach(NestedNodeLambda functor) {
|
void nestedEach(NestedNodeLambda functor,
|
||||||
QReadLocker readLock(&_nodeMutex);
|
int* lockWaitOut = nullptr,
|
||||||
|
int* nodeTransformOut = nullptr,
|
||||||
|
int* functorOut = nullptr) {
|
||||||
|
auto start = usecTimestampNow();
|
||||||
|
{
|
||||||
|
QReadLocker readLock(&_nodeMutex);
|
||||||
|
auto endLock = usecTimestampNow();
|
||||||
|
if (lockWaitOut) {
|
||||||
|
*lockWaitOut = (endLock - start);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<SharedNodePointer> nodes(_nodeHash.size());
|
std::vector<SharedNodePointer> nodes(_nodeHash.size());
|
||||||
std::transform(_nodeHash.cbegin(), _nodeHash.cend(), nodes.begin(), [](const NodeHash::value_type& it) {
|
std::transform(_nodeHash.cbegin(), _nodeHash.cend(), nodes.begin(), [](const NodeHash::value_type& it) {
|
||||||
return it.second;
|
return it.second;
|
||||||
});
|
});
|
||||||
|
auto endTransform = usecTimestampNow();
|
||||||
|
if (nodeTransformOut) {
|
||||||
|
*nodeTransformOut = (endTransform - endLock);
|
||||||
|
}
|
||||||
|
|
||||||
functor(nodes.cbegin(), nodes.cend());
|
functor(nodes.cbegin(), nodes.cend());
|
||||||
|
auto endFunctor = usecTimestampNow();
|
||||||
|
if (functorOut) {
|
||||||
|
*functorOut = (endFunctor - endTransform);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename NodeLambda>
|
template<typename NodeLambda>
|
||||||
|
|
|
@ -179,9 +179,9 @@ public:
|
||||||
const glm::vec3& localVelocity,
|
const glm::vec3& localVelocity,
|
||||||
const glm::vec3& localAngularVelocity);
|
const glm::vec3& localAngularVelocity);
|
||||||
|
|
||||||
bool scaleChangedSince(quint64 time) { return _scaleChanged > time; }
|
bool scaleChangedSince(quint64 time) const { return _scaleChanged > time; }
|
||||||
bool tranlationChangedSince(quint64 time) { return _translationChanged > time; }
|
bool tranlationChangedSince(quint64 time) const { return _translationChanged > time; }
|
||||||
bool rotationChangedSince(quint64 time) { return _rotationChanged > time; }
|
bool rotationChangedSince(quint64 time) const { return _rotationChanged > time; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const NestableType _nestableType; // EntityItem or an AvatarData
|
const NestableType _nestableType; // EntityItem or an AvatarData
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// newEditEntities.js
|
// edit.js
|
||||||
// examples
|
|
||||||
//
|
//
|
||||||
// Created by Brad Hefta-Gaub on 10/2/14.
|
// Created by Brad Hefta-Gaub on 10/2/14.
|
||||||
// Persist toolbar by HRS 6/11/15.
|
// Persist toolbar by HRS 6/11/15.
|
||||||
|
@ -13,7 +12,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTook, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger, Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */
|
/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger, Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */
|
||||||
|
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
|
@ -208,27 +207,7 @@ var toolBar = (function () {
|
||||||
var buttonHandlers = {}; // only used to tablet mode
|
var buttonHandlers = {}; // only used to tablet mode
|
||||||
|
|
||||||
function addButton(name, image, handler) {
|
function addButton(name, image, handler) {
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
buttonHandlers[name] = handler;
|
||||||
var imageUrl = TOOLS_PATH + image;
|
|
||||||
var button = toolBar.addButton({
|
|
||||||
objectName: name,
|
|
||||||
imageURL: imageUrl,
|
|
||||||
imageOffOut: 1,
|
|
||||||
imageOffIn: 2,
|
|
||||||
imageOnOut: 0,
|
|
||||||
imageOnIn: 2,
|
|
||||||
alpha: 0.9,
|
|
||||||
visible: true
|
|
||||||
});
|
|
||||||
if (handler) {
|
|
||||||
button.clicked.connect(function () {
|
|
||||||
Script.setTimeout(handler, 100);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return button;
|
|
||||||
} else {
|
|
||||||
buttonHandlers[name] = handler;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var SHAPE_TYPE_NONE = 0;
|
var SHAPE_TYPE_NONE = 0;
|
||||||
|
@ -272,7 +251,6 @@ var toolBar = (function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml.
|
function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml.
|
||||||
print("fromQml: " + JSON.stringify(message));
|
|
||||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
tablet.popFromStack();
|
tablet.popFromStack();
|
||||||
switch (message.method) {
|
switch (message.method) {
|
||||||
|
@ -299,42 +277,24 @@ var toolBar = (function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
activeButton = tablet.addButton({
|
||||||
systemToolbar = Toolbars.getToolbar(SYSTEM_TOOLBAR);
|
icon: "icons/tablet-icons/edit-i.svg",
|
||||||
activeButton = systemToolbar.addButton({
|
activeIcon: "icons/tablet-icons/edit-a.svg",
|
||||||
objectName: EDIT_TOGGLE_BUTTON,
|
text: "EDIT",
|
||||||
imageURL: TOOLS_PATH + "edit.svg",
|
sortOrder: 10
|
||||||
visible: true,
|
});
|
||||||
alpha: 0.9,
|
tablet.screenChanged.connect(function (type, url) {
|
||||||
defaultState: 1
|
if (isActive && (type !== "QML" || url !== "Edit.qml")) {
|
||||||
});
|
that.toggle();
|
||||||
systemToolbar.fromQml.connect(fromQml);
|
}
|
||||||
} else {
|
});
|
||||||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
tablet.fromQml.connect(fromQml);
|
||||||
activeButton = tablet.addButton({
|
|
||||||
icon: "icons/tablet-icons/edit-i.svg",
|
|
||||||
activeIcon: "icons/tablet-icons/edit-a.svg",
|
|
||||||
text: "EDIT",
|
|
||||||
sortOrder: 10
|
|
||||||
});
|
|
||||||
tablet.screenChanged.connect(function (type, url) {
|
|
||||||
if (isActive && (type !== "QML" || url !== "Edit.qml")) {
|
|
||||||
that.toggle();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
tablet.fromQml.connect(fromQml);
|
|
||||||
}
|
|
||||||
|
|
||||||
activeButton.clicked.connect(function() {
|
activeButton.clicked.connect(function() {
|
||||||
that.toggle();
|
that.toggle();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
|
||||||
toolBar = Toolbars.getToolbar(EDIT_TOOLBAR);
|
|
||||||
toolBar.writeProperty("shown", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
addButton("openAssetBrowserButton", "assets-01.svg", function(){
|
addButton("openAssetBrowserButton", "assets-01.svg", function(){
|
||||||
Window.showAssetServer();
|
Window.showAssetServer();
|
||||||
});
|
});
|
||||||
|
@ -348,34 +308,9 @@ var toolBar = (function () {
|
||||||
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
|
||||||
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
|
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
|
||||||
|
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
// tablet version of new-model dialog
|
||||||
// HUD-ui version of new-model dialog
|
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
var result = Window.customPrompt({
|
tablet.pushOntoStack("NewModelDialog.qml");
|
||||||
textInput: {
|
|
||||||
label: "Model URL"
|
|
||||||
},
|
|
||||||
comboBox: {
|
|
||||||
label: "Automatic Collisions",
|
|
||||||
index: SHAPE_TYPE_DEFAULT,
|
|
||||||
items: SHAPE_TYPES
|
|
||||||
},
|
|
||||||
checkBox: {
|
|
||||||
label: "Dynamic",
|
|
||||||
checked: DYNAMIC_DEFAULT,
|
|
||||||
disableForItems: [
|
|
||||||
SHAPE_TYPE_STATIC_MESH
|
|
||||||
],
|
|
||||||
checkStateOnDisable: false,
|
|
||||||
warningOnDisable: "Models with automatic collisions set to 'Exact' cannot be dynamic"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
handleNewModelDialogResult(result);
|
|
||||||
} else {
|
|
||||||
// tablet version of new-model dialog
|
|
||||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
|
||||||
tablet.pushOntoStack("NewModelDialog.qml");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
addButton("newCubeButton", "cube-01.svg", function () {
|
addButton("newCubeButton", "cube-01.svg", function () {
|
||||||
|
@ -497,11 +432,11 @@ var toolBar = (function () {
|
||||||
entityListTool.clearEntityList();
|
entityListTool.clearEntityList();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
that.toggle = function () {
|
that.toggle = function () {
|
||||||
that.setActive(!isActive);
|
that.setActive(!isActive);
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
activeButton.editProperties({isActive: isActive});
|
||||||
activeButton.editProperties({isActive: isActive});
|
if (!isActive) {
|
||||||
|
tablet.gotoHomeScreen();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -532,10 +467,8 @@ var toolBar = (function () {
|
||||||
cameraManager.disable();
|
cameraManager.disable();
|
||||||
selectionDisplay.triggerMapping.disable();
|
selectionDisplay.triggerMapping.disable();
|
||||||
} else {
|
} else {
|
||||||
if (!Settings.getValue("HUDUIEnabled")) {
|
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
tablet.loadQMLSource("Edit.qml");
|
||||||
tablet.loadQMLSource("Edit.qml");
|
|
||||||
}
|
|
||||||
UserActivityLogger.enabledEdit();
|
UserActivityLogger.enabledEdit();
|
||||||
entityListTool.setVisible(true);
|
entityListTool.setVisible(true);
|
||||||
gridTool.setVisible(true);
|
gridTool.setVisible(true);
|
||||||
|
@ -546,15 +479,6 @@ var toolBar = (function () {
|
||||||
// everybody else to think that Interface has lost focus overall. fogbugzid:558
|
// everybody else to think that Interface has lost focus overall. fogbugzid:558
|
||||||
// Window.setFocus();
|
// Window.setFocus();
|
||||||
}
|
}
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
|
||||||
// Sets visibility of tool buttons, excluding the power button
|
|
||||||
toolBar.writeProperty("shown", active);
|
|
||||||
var visible = toolBar.readProperty("visible");
|
|
||||||
if (active && !visible) {
|
|
||||||
toolBar.writeProperty("shown", false);
|
|
||||||
toolBar.writeProperty("shown", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
|
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
|
||||||
Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
|
Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
|
||||||
};
|
};
|
||||||
|
@ -815,6 +739,12 @@ function mouseClickEvent(event) {
|
||||||
orientation = MyAvatar.orientation;
|
orientation = MyAvatar.orientation;
|
||||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
||||||
|
|
||||||
|
if (event.isShifted) {
|
||||||
|
particleExplorerTool.destroyWebView();
|
||||||
|
}
|
||||||
|
if (properties.type !== "ParticleEffect") {
|
||||||
|
particleExplorerTool.destroyWebView();
|
||||||
|
}
|
||||||
|
|
||||||
if (!event.isShifted) {
|
if (!event.isShifted) {
|
||||||
selectionManager.setSelections([foundEntity]);
|
selectionManager.setSelections([foundEntity]);
|
||||||
|
@ -1493,16 +1423,8 @@ var PropertiesTool = function (opts) {
|
||||||
var that = {};
|
var that = {};
|
||||||
|
|
||||||
var webView = null;
|
var webView = null;
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
webView = new OverlayWebWindow({
|
webView.setVisible = function(value) {};
|
||||||
title: 'Entity Properties',
|
|
||||||
source: ENTITY_PROPERTIES_URL,
|
|
||||||
toolWindow: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
|
||||||
webView.setVisible = function(value) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
var visible = false;
|
var visible = false;
|
||||||
|
|
||||||
|
@ -1924,18 +1846,7 @@ function selectParticleEntity(entityID) {
|
||||||
|
|
||||||
selectedParticleEntity = entityID;
|
selectedParticleEntity = entityID;
|
||||||
particleExplorerTool.setActiveParticleEntity(entityID);
|
particleExplorerTool.setActiveParticleEntity(entityID);
|
||||||
|
particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
|
||||||
particleExplorerTool.webView.webEventReceived.connect(function (data) {
|
|
||||||
data = JSON.parse(data);
|
|
||||||
if (data.messageType === "page_loaded") {
|
|
||||||
particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// in the tablet version, the page was loaded earlier
|
|
||||||
particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entityListTool.webView.webEventReceived.connect(function (data) {
|
entityListTool.webView.webEventReceived.connect(function (data) {
|
||||||
|
@ -1949,7 +1860,7 @@ entityListTool.webView.webEventReceived.connect(function (data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Destroy the old particles web view first
|
// Destroy the old particles web view first
|
||||||
selectParticleEntity(ids[0])
|
selectParticleEntity(ids[0]);
|
||||||
} else {
|
} else {
|
||||||
selectedParticleEntity = 0;
|
selectedParticleEntity = 0;
|
||||||
particleExplorerTool.destroyWebView();
|
particleExplorerTool.destroyWebView();
|
||||||
|
|
|
@ -871,6 +871,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
|
||||||
float: right;
|
float: right;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
background-color: #ff0000;
|
background-color: #ff0000;
|
||||||
|
min-width: 90px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#entity-list {
|
#entity-list {
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
var ENTITY_LIST_HTML_URL = Script.resolvePath('../html/entityList.html');
|
"use strict";
|
||||||
|
|
||||||
|
// entityList.js
|
||||||
|
//
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
/* global EntityListTool, Tablet, selectionManager, Entities, Camera, MyAvatar, Vec3, Menu, Messages,
|
||||||
|
cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible */
|
||||||
|
|
||||||
EntityListTool = function(opts) {
|
EntityListTool = function(opts) {
|
||||||
var that = {};
|
var that = {};
|
||||||
|
|
||||||
var webView = null;
|
var webView = null;
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
var url = ENTITY_LIST_HTML_URL;
|
webView.setVisible = function(value) {};
|
||||||
webView = new OverlayWebWindow({
|
|
||||||
title: 'Entity List', source: url, toolWindow: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
|
||||||
webView.setVisible = function(value) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
var filterInView = false;
|
var filterInView = false;
|
||||||
var searchRadius = 100;
|
var searchRadius = 100;
|
||||||
|
@ -30,7 +34,7 @@ EntityListTool = function(opts) {
|
||||||
|
|
||||||
that.toggleVisible = function() {
|
that.toggleVisible = function() {
|
||||||
that.setVisible(!visible);
|
that.setVisible(!visible);
|
||||||
}
|
};
|
||||||
|
|
||||||
selectionManager.addEventListener(function() {
|
selectionManager.addEventListener(function() {
|
||||||
var selectedIDs = [];
|
var selectedIDs = [];
|
||||||
|
@ -49,7 +53,7 @@ EntityListTool = function(opts) {
|
||||||
that.clearEntityList = function () {
|
that.clearEntityList = function () {
|
||||||
var data = {
|
var data = {
|
||||||
type: 'clearEntityList'
|
type: 'clearEntityList'
|
||||||
}
|
};
|
||||||
webView.emitScriptEvent(JSON.stringify(data));
|
webView.emitScriptEvent(JSON.stringify(data));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,8 +95,8 @@ EntityListTool = function(opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectedIDs = [];
|
var selectedIDs = [];
|
||||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
for (var j = 0; j < selectionManager.selections.length; j++) {
|
||||||
selectedIDs.push(selectionManager.selections[i].id);
|
selectedIDs.push(selectionManager.selections[j].id);
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
|
@ -101,7 +105,7 @@ EntityListTool = function(opts) {
|
||||||
selectedIDs: selectedIDs,
|
selectedIDs: selectedIDs,
|
||||||
};
|
};
|
||||||
webView.emitScriptEvent(JSON.stringify(data));
|
webView.emitScriptEvent(JSON.stringify(data));
|
||||||
}
|
};
|
||||||
|
|
||||||
webView.webEventReceived.connect(function(data) {
|
webView.webEventReceived.connect(function(data) {
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
|
|
|
@ -229,15 +229,8 @@ GridTool = function(opts) {
|
||||||
var listeners = [];
|
var listeners = [];
|
||||||
|
|
||||||
var webView = null;
|
var webView = null;
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
var url = GRID_CONTROLS_HTML_URL;
|
webView.setVisible = function(value) {};
|
||||||
webView = new OverlayWebWindow({
|
|
||||||
title: 'Grid', source: url, toolWindow: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
|
||||||
webView.setVisible = function(value) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
horizontalGrid.addListener(function(data) {
|
horizontalGrid.addListener(function(data) {
|
||||||
webView.emitScriptEvent(JSON.stringify(data));
|
webView.emitScriptEvent(JSON.stringify(data));
|
||||||
|
|
|
@ -32,6 +32,8 @@ var gui = null;
|
||||||
var settings = new Settings();
|
var settings = new Settings();
|
||||||
var updateInterval;
|
var updateInterval;
|
||||||
|
|
||||||
|
var active = false;
|
||||||
|
|
||||||
var currentInputField;
|
var currentInputField;
|
||||||
var storedController;
|
var storedController;
|
||||||
//CHANGE TO WHITELIST
|
//CHANGE TO WHITELIST
|
||||||
|
@ -363,6 +365,19 @@ function listenForSettingsUpdates() {
|
||||||
} else {
|
} else {
|
||||||
loadGUI();
|
loadGUI();
|
||||||
}
|
}
|
||||||
|
if (!active) {
|
||||||
|
// gui.toggleHide();
|
||||||
|
gui.closed = false;
|
||||||
|
}
|
||||||
|
active = true;
|
||||||
|
|
||||||
|
} else if (data.messageType === "particle_close") {
|
||||||
|
// none of this seems to work.
|
||||||
|
// if (active) {
|
||||||
|
// gui.toggleHide();
|
||||||
|
// }
|
||||||
|
active = false;
|
||||||
|
gui.closed = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,33 +18,21 @@ ParticleExplorerTool = function() {
|
||||||
var that = {};
|
var that = {};
|
||||||
|
|
||||||
that.createWebView = function() {
|
that.createWebView = function() {
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
var url = PARTICLE_EXPLORER_HTML_URL;
|
that.webView.setVisible = function(value) {};
|
||||||
that.webView = new OverlayWebWindow({
|
|
||||||
title: 'Particle Explorer',
|
|
||||||
source: url,
|
|
||||||
toolWindow: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
|
||||||
that.webView.setVisible = function(value) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
that.webView.setVisible(true);
|
|
||||||
that.webView.webEventReceived.connect(that.webEventReceived);
|
that.webView.webEventReceived.connect(that.webEventReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
that.destroyWebView = function() {
|
that.destroyWebView = function() {
|
||||||
if (!that.webView) {
|
if (!that.webView) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
|
||||||
that.webView.close();
|
|
||||||
that.webView = null;
|
|
||||||
}
|
|
||||||
that.activeParticleEntity = 0;
|
that.activeParticleEntity = 0;
|
||||||
|
|
||||||
|
var messageData = {
|
||||||
|
messageType: "particle_close"
|
||||||
|
};
|
||||||
|
that.webView.emitScriptEvent(JSON.stringify(messageData));
|
||||||
}
|
}
|
||||||
|
|
||||||
that.webEventReceived = function(data) {
|
that.webEventReceived = function(data) {
|
||||||
|
@ -58,9 +46,5 @@ ParticleExplorerTool = function() {
|
||||||
that.activeParticleEntity = id;
|
that.activeParticleEntity = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings.getValue("HUDUIEnabled")) {
|
|
||||||
that.createWebView();
|
|
||||||
}
|
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function onWebEventReceived(event) {
|
function onWebEventReceived(event) {
|
||||||
print("Script received a web event, its type is " + typeof event);
|
|
||||||
if (typeof event === "string") {
|
if (typeof event === "string") {
|
||||||
event = JSON.parse(event);
|
event = JSON.parse(event);
|
||||||
}
|
}
|
||||||
|
|