mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 12:14:00 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into dk/zoneFilters2
This commit is contained in:
commit
5ca80803c0
82 changed files with 2450 additions and 363 deletions
|
@ -138,7 +138,6 @@ void Agent::handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, Sh
|
|||
|
||||
void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
_receivedAudioStream.parseData(*message);
|
||||
|
||||
_lastReceivedAudioLoudness = _receivedAudioStream.getNextOutputFrameLoudness();
|
||||
_receivedAudioStream.clearBuffer();
|
||||
}
|
||||
|
@ -323,12 +322,14 @@ void Agent::scriptRequestFinished() {
|
|||
request->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
void Agent::executeScript() {
|
||||
_scriptEngine = std::unique_ptr<ScriptEngine>(new ScriptEngine(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload));
|
||||
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
|
||||
|
||||
// setup an Avatar for the script to use
|
||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||
|
||||
connect(_scriptEngine.get(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection);
|
||||
scriptedAvatar->setForceFaceTrackerConnected(true);
|
||||
|
||||
|
@ -338,11 +339,33 @@ void Agent::executeScript() {
|
|||
// give this AvatarData object to the script engine
|
||||
_scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data());
|
||||
|
||||
auto player = DependencyManager::get<recording::Deck>();
|
||||
connect(player.data(), &recording::Deck::playbackStateChanged, [=] {
|
||||
if (player->isPlaying()) {
|
||||
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
||||
if (recordingInterface->getPlayFromCurrentLocation()) {
|
||||
scriptedAvatar->setRecordingBasis();
|
||||
}
|
||||
} else {
|
||||
scriptedAvatar->clearRecordingBasis();
|
||||
}
|
||||
});
|
||||
|
||||
using namespace recording;
|
||||
static const FrameType AVATAR_FRAME_TYPE = Frame::registerFrameType(AvatarData::FRAME_NAME);
|
||||
// FIXME how to deal with driving multiple avatars locally?
|
||||
Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this, scriptedAvatar](Frame::ConstPointer frame) {
|
||||
|
||||
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
||||
bool useFrameSkeleton = recordingInterface->getPlayerUseSkeletonModel();
|
||||
|
||||
// FIXME - the ability to switch the avatar URL is not actually supported when playing back from a recording
|
||||
if (!useFrameSkeleton) {
|
||||
static std::once_flag warning;
|
||||
std::call_once(warning, [] {
|
||||
qWarning() << "Recording.setPlayerUseSkeletonModel(false) is not currently supported.";
|
||||
});
|
||||
}
|
||||
|
||||
AvatarData::fromFrame(frame->data, *scriptedAvatar);
|
||||
});
|
||||
|
||||
|
@ -352,8 +375,11 @@ void Agent::executeScript() {
|
|||
const QByteArray& audio = frame->data;
|
||||
static quint16 audioSequenceNumber{ 0 };
|
||||
Transform audioTransform;
|
||||
|
||||
auto headOrientation = scriptedAvatar->getHeadOrientation();
|
||||
audioTransform.setTranslation(scriptedAvatar->getPosition());
|
||||
audioTransform.setRotation(scriptedAvatar->getOrientation());
|
||||
audioTransform.setRotation(headOrientation);
|
||||
|
||||
QByteArray encodedBuffer;
|
||||
if (_encoder) {
|
||||
_encoder->encode(audio, encodedBuffer);
|
||||
|
@ -537,7 +563,10 @@ void Agent::encodeFrameOfZeros(QByteArray& encodedZeros) {
|
|||
}
|
||||
|
||||
void Agent::processAgentAvatarAudio() {
|
||||
if (_isAvatar && (_isListeningToAudioStream || _avatarSound)) {
|
||||
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
||||
bool isPlayingRecording = recordingInterface->isPlaying();
|
||||
|
||||
if (_isAvatar && ((_isListeningToAudioStream && !isPlayingRecording) || _avatarSound)) {
|
||||
// if we have an avatar audio stream then send it out to our audio-mixer
|
||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||
bool silentFrame = true;
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
|
||||
quint16 assignmentMonitorPort);
|
||||
~AssignmentClient();
|
||||
|
||||
private slots:
|
||||
void sendAssignmentRequest();
|
||||
void assignmentCompleted();
|
||||
|
|
|
@ -11,9 +11,13 @@
|
|||
|
||||
#include "EntityScriptServer.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <AudioConstants.h>
|
||||
#include <AudioInjectorManager.h>
|
||||
#include <ClientServerUtils.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
#include <LogHandler.h>
|
||||
#include <MessagesClient.h>
|
||||
#include <plugins/CodecPlugin.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
|
@ -24,12 +28,29 @@
|
|||
#include <UUID.h>
|
||||
#include <WebSocketServerClass.h>
|
||||
|
||||
#include "ClientServerUtils.h"
|
||||
#include "EntityScriptServerLogging.h"
|
||||
#include "../entities/AssignmentParentFinder.h"
|
||||
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::lock_guard<Mutex>;
|
||||
|
||||
static std::mutex logBufferMutex;
|
||||
static std::string logBuffer;
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
auto logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message);
|
||||
|
||||
if (!logMessage.isEmpty()) {
|
||||
Lock lock(logBufferMutex);
|
||||
logBuffer.append(logMessage.toStdString() + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
int EntityScriptServer::_entitiesScriptEngineCount = 0;
|
||||
|
||||
EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssignment(message) {
|
||||
qInstallMessageHandler(messageHandler);
|
||||
|
||||
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
|
||||
|
||||
ResourceManager::init();
|
||||
|
@ -57,6 +78,17 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
|
||||
packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket");
|
||||
|
||||
static const int LOG_INTERVAL = MSECS_PER_SECOND / 10;
|
||||
auto timer = new QTimer(this);
|
||||
timer->setInterval(LOG_INTERVAL);
|
||||
connect(timer, &QTimer::timeout, this, &EntityScriptServer::pushLogs);
|
||||
timer->start();
|
||||
}
|
||||
|
||||
EntityScriptServer::~EntityScriptServer() {
|
||||
qInstallMessageHandler(LogHandler::verboseMessageHandler);
|
||||
}
|
||||
|
||||
static const QString ENTITY_SCRIPT_SERVER_LOGGING_NAME = "entity-script-server";
|
||||
|
@ -68,7 +100,7 @@ void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<Rec
|
|||
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
if (_entityViewer.getTree() && !_shuttingDown) {
|
||||
qDebug() << "Reloading: " << entityID;
|
||||
qCDebug(entity_script_server) << "Reloading: " << entityID;
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||
checkAndCallPreload(entityID, true);
|
||||
}
|
||||
|
@ -100,6 +132,99 @@ void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer<Receiv
|
|||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::handleSettings() {
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
auto& domainHandler = nodeList->getDomainHandler();
|
||||
const QJsonObject& settingsObject = domainHandler.getSettingsObject();
|
||||
|
||||
static const QString ENTITY_SCRIPT_SERVER_SETTINGS_KEY = "entity_script_server";
|
||||
|
||||
if (!settingsObject.contains(ENTITY_SCRIPT_SERVER_SETTINGS_KEY)) {
|
||||
qWarning() << "Received settings from the domain-server with no entity_script_server section.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto entityScriptServerSettings = settingsObject[ENTITY_SCRIPT_SERVER_SETTINGS_KEY].toObject();
|
||||
|
||||
static const QString MAX_ENTITY_PPS_OPTION = "max_total_entity_pps";
|
||||
static const QString ENTITY_PPS_PER_SCRIPT = "entity_pps_per_script";
|
||||
|
||||
if (!entityScriptServerSettings.contains(MAX_ENTITY_PPS_OPTION) || !entityScriptServerSettings.contains(ENTITY_PPS_PER_SCRIPT)) {
|
||||
qWarning() << "Received settings from the domain-server with no max_total_entity_pps or entity_pps_per_script properties.";
|
||||
return;
|
||||
}
|
||||
|
||||
_maxEntityPPS = std::max(0, entityScriptServerSettings[MAX_ENTITY_PPS_OPTION].toInt());
|
||||
_entityPPSPerScript = std::max(0, entityScriptServerSettings[ENTITY_PPS_PER_SCRIPT].toInt());
|
||||
|
||||
qDebug() << QString("Received entity script server settings, Max Entity PPS: %1, Entity PPS Per Entity Script: %2")
|
||||
.arg(_maxEntityPPS).arg(_entityPPSPerScript);
|
||||
}
|
||||
|
||||
void EntityScriptServer::updateEntityPPS() {
|
||||
int numRunningScripts = _entitiesScriptEngine->getNumRunningEntityScripts();
|
||||
int pps;
|
||||
if (std::numeric_limits<int>::max() / _entityPPSPerScript < numRunningScripts) {
|
||||
qWarning() << QString("Integer multiplaction would overflow, clamping to maxint: %1 * %2").arg(numRunningScripts).arg(_entityPPSPerScript);
|
||||
pps = std::numeric_limits<int>::max();
|
||||
pps = std::min(_maxEntityPPS, pps);
|
||||
} else {
|
||||
pps = _entityPPSPerScript * numRunningScripts;
|
||||
pps = std::min(_maxEntityPPS, pps);
|
||||
}
|
||||
_entityEditSender.setPacketsPerSecond(pps);
|
||||
qDebug() << QString("Updating entity PPS to: %1 @ %2 PPS per script = %3 PPS").arg(numRunningScripts).arg(_entityPPSPerScript).arg(pps);
|
||||
}
|
||||
|
||||
void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
|
||||
// about each other.
|
||||
bool enable = false;
|
||||
message->readPrimitive(&enable);
|
||||
|
||||
auto senderUUID = senderNode->getUUID();
|
||||
auto it = _logListeners.find(senderUUID);
|
||||
|
||||
if (enable && senderNode->getCanRez()) {
|
||||
if (it == std::end(_logListeners)) {
|
||||
_logListeners.insert(senderUUID);
|
||||
qCInfo(entity_script_server) << "Node" << senderUUID << "subscribed to log stream";
|
||||
}
|
||||
} else {
|
||||
if (it != std::end(_logListeners)) {
|
||||
_logListeners.erase(it);
|
||||
qCInfo(entity_script_server) << "Node" << senderUUID << "unsubscribed from log stream";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::pushLogs() {
|
||||
std::string buffer;
|
||||
{
|
||||
Lock lock(logBufferMutex);
|
||||
std::swap(logBuffer, buffer);
|
||||
}
|
||||
|
||||
if (buffer.empty()) {
|
||||
return;
|
||||
}
|
||||
if (_logListeners.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
for (auto uuid : _logListeners) {
|
||||
auto node = nodeList->nodeWithUUID(uuid);
|
||||
if (node && node->getActiveSocket()) {
|
||||
auto packet = NLPacketList::create(PacketType::EntityServerScriptLog, QByteArray(), true, true);
|
||||
packet->write(buffer.data(), buffer.size());
|
||||
nodeList->sendPacketList(std::move(packet), *node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::run() {
|
||||
// make sure we request our script once the agent connects to the domain
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -114,6 +239,9 @@ void EntityScriptServer::run() {
|
|||
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
|
||||
messagesThread->start();
|
||||
|
||||
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
|
||||
connect(&domainHandler, &DomainHandler::settingsReceived, this, &EntityScriptServer::handleSettings);
|
||||
|
||||
// make sure we hear about connected nodes so we can grab an ATP script if a request is pending
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &EntityScriptServer::nodeActivated);
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &EntityScriptServer::nodeKilled);
|
||||
|
@ -154,9 +282,67 @@ void EntityScriptServer::run() {
|
|||
connect(tree, &EntityTree::entityServerScriptChanging, this, &EntityScriptServer::entityServerScriptChanging, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void EntityScriptServer::cleanupOldKilledListeners() {
|
||||
auto threshold = usecTimestampNow() - 5 * USECS_PER_SECOND;
|
||||
using ValueType = std::pair<QUuid, quint64>;
|
||||
auto it = std::remove_if(std::begin(_killedListeners), std::end(_killedListeners), [&](ValueType value) {
|
||||
return value.second < threshold;
|
||||
});
|
||||
_killedListeners.erase(it, std::end(_killedListeners));
|
||||
}
|
||||
|
||||
void EntityScriptServer::nodeActivated(SharedNodePointer activatedNode) {
|
||||
if (activatedNode->getType() == NodeType::AudioMixer) {
|
||||
negotiateAudioFormat();
|
||||
switch (activatedNode->getType()) {
|
||||
case NodeType::AudioMixer:
|
||||
negotiateAudioFormat();
|
||||
break;
|
||||
case NodeType::Agent: {
|
||||
auto activatedNodeUUID = activatedNode->getUUID();
|
||||
using ValueType = std::pair<QUuid, quint64>;
|
||||
auto it = std::find_if(std::begin(_killedListeners), std::end(_killedListeners), [&](ValueType value) {
|
||||
return value.first == activatedNodeUUID;
|
||||
});
|
||||
if (it != std::end(_killedListeners)) {
|
||||
_killedListeners.erase(it);
|
||||
_logListeners.insert(activatedNodeUUID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::nodeKilled(SharedNodePointer killedNode) {
|
||||
switch (killedNode->getType()) {
|
||||
case NodeType::EntityServer: {
|
||||
if (!_shuttingDown) {
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->unloadAllEntityScripts();
|
||||
_entitiesScriptEngine->stop();
|
||||
}
|
||||
|
||||
resetEntitiesScriptEngine();
|
||||
|
||||
_entityViewer.clear();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeType::Agent: {
|
||||
cleanupOldKilledListeners();
|
||||
|
||||
auto killedNodeUUID = killedNode->getUUID();
|
||||
auto it = _logListeners.find(killedNodeUUID);
|
||||
if (it != std::end(_logListeners)) {
|
||||
_logListeners.erase(killedNodeUUID);
|
||||
_killedListeners.emplace_back(killedNodeUUID, usecTimestampNow());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,7 +374,7 @@ void EntityScriptServer::handleSelectedAudioFormat(QSharedPointer<ReceivedMessag
|
|||
void EntityScriptServer::selectAudioFormat(const QString& selectedCodecName) {
|
||||
_selectedCodecName = selectedCodecName;
|
||||
|
||||
qDebug() << "Selected Codec:" << _selectedCodecName;
|
||||
qCDebug(entity_script_server) << "Selected Codec:" << _selectedCodecName;
|
||||
|
||||
// release any old codec encoder/decoder first...
|
||||
if (_codec && _encoder) {
|
||||
|
@ -202,7 +388,7 @@ void EntityScriptServer::selectAudioFormat(const QString& selectedCodecName) {
|
|||
if (_selectedCodecName == plugin->getName()) {
|
||||
_codec = plugin;
|
||||
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO);
|
||||
qDebug() << "Selected Codec Plugin:" << _codec.get();
|
||||
qCDebug(entity_script_server) << "Selected Codec Plugin:" << _codec.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -232,7 +418,9 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
|
|||
newEngine->runInThread();
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(newEngine.data());
|
||||
|
||||
disconnect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated, this, &EntityScriptServer::updateEntityPPS);
|
||||
_entitiesScriptEngine.swap(newEngine);
|
||||
connect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated, this, &EntityScriptServer::updateEntityPPS);
|
||||
}
|
||||
|
||||
|
||||
|
@ -288,26 +476,13 @@ void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, const
|
|||
QString scriptUrl = entity->getServerScripts();
|
||||
if (!scriptUrl.isEmpty()) {
|
||||
scriptUrl = ResourceManager::normalizeURL(scriptUrl);
|
||||
qDebug() << "Loading entity server script" << scriptUrl << "for" << entityID;
|
||||
qCDebug(entity_script_server) << "Loading entity server script" << scriptUrl << "for" << entityID;
|
||||
ScriptEngine::loadEntityScript(_entitiesScriptEngine, entityID, scriptUrl, reload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::nodeKilled(SharedNodePointer killedNode) {
|
||||
if (!_shuttingDown && killedNode->getType() == NodeType::EntityServer) {
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->unloadAllEntityScripts();
|
||||
_entitiesScriptEngine->stop();
|
||||
}
|
||||
|
||||
resetEntitiesScriptEngine();
|
||||
|
||||
_entityViewer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::sendStatsPacket() {
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,11 @@
|
|||
#ifndef hifi_EntityScriptServer_h
|
||||
#define hifi_EntityScriptServer_h
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <EntityEditPacketSender.h>
|
||||
#include <EntityTreeHeadlessViewer.h>
|
||||
|
@ -20,11 +24,15 @@
|
|||
#include <ScriptEngine.h>
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
static const int DEFAULT_MAX_ENTITY_PPS = 9000;
|
||||
static const int DEFAULT_ENTITY_PPS_PER_SCRIPT = 900;
|
||||
|
||||
class EntityScriptServer : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EntityScriptServer(ReceivedMessage& message);
|
||||
~EntityScriptServer();
|
||||
|
||||
virtual void aboutToFinish() override;
|
||||
|
||||
|
@ -42,6 +50,13 @@ private slots:
|
|||
void handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleEntityScriptGetStatusPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
||||
void handleSettings();
|
||||
void updateEntityPPS();
|
||||
|
||||
void handleEntityServerScriptLogPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
||||
void pushLogs();
|
||||
|
||||
private:
|
||||
void negotiateAudioFormat();
|
||||
void selectAudioFormat(const QString& selectedCodecName);
|
||||
|
@ -55,6 +70,8 @@ private:
|
|||
void entityServerScriptChanging(const EntityItemID& entityID, const bool reload);
|
||||
void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false);
|
||||
|
||||
void cleanupOldKilledListeners();
|
||||
|
||||
bool _shuttingDown { false };
|
||||
|
||||
static int _entitiesScriptEngineCount;
|
||||
|
@ -62,6 +79,12 @@ private:
|
|||
EntityEditPacketSender _entityEditSender;
|
||||
EntityTreeHeadlessViewer _entityViewer;
|
||||
|
||||
int _maxEntityPPS { DEFAULT_MAX_ENTITY_PPS };
|
||||
int _entityPPSPerScript { DEFAULT_ENTITY_PPS_PER_SCRIPT };
|
||||
|
||||
std::set<QUuid> _logListeners;
|
||||
std::vector<std::pair<QUuid, quint64>> _killedListeners;
|
||||
|
||||
QString _selectedCodecName;
|
||||
CodecPluginPointer _codec;
|
||||
Encoder* _encoder { nullptr };
|
||||
|
|
14
assignment-client/src/scripts/EntityScriptServerLogging.cpp
Normal file
14
assignment-client/src/scripts/EntityScriptServerLogging.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// EntityScriptServerLogging.cpp
|
||||
// assignment-client/src/scripts
|
||||
//
|
||||
// Created by Clement on 2/2/17.
|
||||
// Copyright 2015 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 "EntityScriptServerLogging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(entity_script_server, "hifi.entity-script-server")
|
19
assignment-client/src/scripts/EntityScriptServerLogging.h
Normal file
19
assignment-client/src/scripts/EntityScriptServerLogging.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// EntityScriptServerLogging.h
|
||||
// assignment-client/src/scripts
|
||||
//
|
||||
// Created by Clement on 2/2/17.
|
||||
// Copyright 2015 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_EntityScriptServerLogging_h
|
||||
#define hifi_EntityScriptServerLogging_h
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(entity_script_server)
|
||||
|
||||
#endif // hifi_EntityScriptServerLogging_h
|
|
@ -178,7 +178,7 @@
|
|||
{
|
||||
"name": "maximum_user_capacity_redirect_location",
|
||||
"label": "Redirect to Location on Maximum Capacity",
|
||||
"help": "Is there another domain, you'd like to redirect clients to when the maximum number of avatars are connected.",
|
||||
"help": "The location to redirect users to when the maximum number of avatars are connected.",
|
||||
"placeholder": "",
|
||||
"default": "",
|
||||
"advanced": false
|
||||
|
@ -822,6 +822,29 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "entity_script_server",
|
||||
"label": "Entity Script Server (ESS)",
|
||||
"assignment-types": [5],
|
||||
"settings": [
|
||||
{
|
||||
"name": "entity_pps_per_script",
|
||||
"label": "Entity PPS per script",
|
||||
"help": "The number of packets per second (PPS) that can be sent to the entity server for each server entity script. This contributes to a total overall amount.<br/>Example: 1000 PPS with 5 entites gives a total PPS of 5000 that is shared among the entity scripts. A single could use 4000 PPS, leaving 1000 for the rest, for example.",
|
||||
"default": 900,
|
||||
"type": "int",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "max_total_entity_pps",
|
||||
"label": "Maximum Total Entity PPS",
|
||||
"help": "The maximum total packets per seconds (PPS) that can be sent to the entity server.<br/>Example: 5 scripts @ 1000 PPS per script = 5000 total PPS. A maximum total PPS of 4000 would cap this 5000 total PPS to 4000.",
|
||||
"default": 9000,
|
||||
"type": "int",
|
||||
"advanced": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "avatars",
|
||||
"label": "Avatars",
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"to": "Actions.Up",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "deadZone", "min": 0.95 },
|
||||
{ "type": "deadZone", "min": 0.6 },
|
||||
"invert"
|
||||
]
|
||||
},
|
||||
|
|
|
@ -105,6 +105,14 @@ Item {
|
|||
visible: root.expanded
|
||||
text: "Asset Mbps In/Out: " + root.assetMbpsIn.toFixed(2) + "/" + root.assetMbpsOut.toFixed(2)
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded
|
||||
text: "Fully Simulated Avatars: " + root.fullySimulatedAvatarCount
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded
|
||||
text: "Partially Simulated Avatars: " + root.partiallySimulatedAvatarCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,7 +225,10 @@ Item {
|
|||
text: " Batch: " + root.batchFrameTime.toFixed(1) + " ms"
|
||||
}
|
||||
StatText {
|
||||
text: " GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
|
||||
text: " GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
|
||||
}
|
||||
StatText {
|
||||
text: " Avatar: " + root.avatarSimulationTime.toFixed(1) + " ms"
|
||||
}
|
||||
StatText {
|
||||
text: "Triangles: " + root.triangles +
|
||||
|
|
|
@ -129,7 +129,7 @@ Rectangle {
|
|||
property int dropSamples: 9;
|
||||
property int dropSpread: 0;
|
||||
DropShadow {
|
||||
visible: showPlace && desktop.gradientsSupported;
|
||||
visible: showPlace && (desktop ? desktop.gradientsSupported : false)
|
||||
source: place;
|
||||
anchors.fill: place;
|
||||
horizontalOffset: dropHorizontalOffset;
|
||||
|
|
|
@ -16,6 +16,8 @@ import QtQuick.Controls 1.4
|
|||
import "../styles-uit"
|
||||
import "../controls-uit" as HifiControls
|
||||
|
||||
// references HMD, Users, UserActivityLogger from root context
|
||||
|
||||
Rectangle {
|
||||
id: pal
|
||||
// Size
|
||||
|
@ -26,7 +28,7 @@ Rectangle {
|
|||
// Properties
|
||||
property int myCardHeight: 90
|
||||
property int rowHeight: 70
|
||||
property int actionButtonWidth: 75
|
||||
property int actionButtonWidth: 55
|
||||
property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth
|
||||
property var myData: ({displayName: "", userName: "", audioLevel: 0.0, admin: true}) // valid dummy until set
|
||||
property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring.
|
||||
|
@ -35,7 +37,9 @@ Rectangle {
|
|||
// Keep a local list of per-avatar gainSliderValueDBs. Far faster than fetching this data from the server.
|
||||
// NOTE: if another script modifies the per-avatar gain, this value won't be accurate!
|
||||
property var gainSliderValueDB: ({});
|
||||
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
// The letterbox used for popup messages
|
||||
LetterboxMessage {
|
||||
id: letterboxMessage
|
||||
|
@ -54,8 +58,8 @@ Rectangle {
|
|||
property bool punctuationMode: false
|
||||
id: palContainer
|
||||
// Size
|
||||
width: pal.width - 50
|
||||
height: pal.height - 50
|
||||
width: pal.width - 10
|
||||
height: pal.height - 10
|
||||
// Style
|
||||
color: pal.color
|
||||
// Anchors
|
||||
|
@ -397,7 +401,7 @@ Rectangle {
|
|||
width: 20
|
||||
height: 28
|
||||
anchors.right: adminTab.right
|
||||
anchors.rightMargin: 31 + hifi.dimensions.scrollbarBackgroundWidth
|
||||
anchors.rightMargin: 10 + hifi.dimensions.scrollbarBackgroundWidth
|
||||
anchors.top: adminTab.top
|
||||
anchors.topMargin: 2
|
||||
RalewayRegular {
|
||||
|
@ -422,6 +426,8 @@ Rectangle {
|
|||
onExited: adminHelpText.color = hifi.colors.redHighlight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: myCard.currentlyEditingDisplayName && HMD.active
|
||||
|
@ -432,7 +438,7 @@ Rectangle {
|
|||
right: parent.right
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Timer used when selecting table rows that aren't yet present in the model
|
||||
// (i.e. when selecting avatars using edit.js or sphere overlays)
|
||||
Timer {
|
||||
|
|
555
interface/resources/qml/hifi/tablet/TabletAddressDialog.qml
Normal file
555
interface/resources/qml/hifi/tablet/TabletAddressDialog.qml
Normal file
|
@ -0,0 +1,555 @@
|
|||
//
|
||||
// TabletAddressDialog.qml
|
||||
//
|
||||
// Created by Dante Ruiz on 2016/07/16
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0
|
||||
import QtQuick 2.4
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../controls"
|
||||
import "../../styles"
|
||||
import "../../windows"
|
||||
import "../"
|
||||
import "../toolbars"
|
||||
import "../../styles-uit" as HifiStyles
|
||||
import "../../controls-uit" as HifiControls
|
||||
|
||||
Item {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
HifiStyles.HifiConstants { id: hifiStyleConstants }
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
property var allStories: [];
|
||||
property int cardWidth: 370;
|
||||
property int cardHeight: 320;
|
||||
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
fillDestinations();
|
||||
updateLocationText();
|
||||
root.parentChanged.connect(center);
|
||||
center();
|
||||
}
|
||||
Component.onDestruction: {
|
||||
root.parentChanged.disconnect(center);
|
||||
}
|
||||
|
||||
function center() {
|
||||
// Explicitly center in order to avoid warnings at shutdown
|
||||
anchors.centerIn = parent;
|
||||
}
|
||||
|
||||
|
||||
function resetAfterTeleport() {
|
||||
//storyCardFrame.shown = root.shown = false;
|
||||
}
|
||||
function goCard(targetString) {
|
||||
if (0 !== targetString.indexOf('hifi://')) {
|
||||
return;
|
||||
}
|
||||
addressLine.text = targetString;
|
||||
toggleOrGo(true);
|
||||
clearAddressLineTimer.start();
|
||||
}
|
||||
|
||||
property bool isCursorVisible: false // Override default cursor visibility.
|
||||
|
||||
|
||||
AddressBarDialog {
|
||||
id: addressBarDialog
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
anchors {
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
onMetaverseServerUrlChanged: updateLocationTextTimer.start();
|
||||
Rectangle {
|
||||
id: topBar
|
||||
height: 90
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: "#2b2b2b"
|
||||
|
||||
}
|
||||
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: "#1e1e1e"
|
||||
}
|
||||
}
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.topMargin: 0
|
||||
anchors.top: parent.top
|
||||
|
||||
Row {
|
||||
id: thing
|
||||
spacing: 2 * hifi.layout.spacing
|
||||
|
||||
anchors {
|
||||
top: parent.top;
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
TextButton {
|
||||
id: allTab;
|
||||
text: "ALL";
|
||||
property string includeActions: 'snapshot, concurrency';
|
||||
selected: allTab === selectedTab;
|
||||
action: tabSelect;
|
||||
}
|
||||
|
||||
TextButton {
|
||||
id: placeTab;
|
||||
text: "PLACES";
|
||||
property string includeActions: 'concurrency';
|
||||
selected: placeTab === selectedTab;
|
||||
action: tabSelect;
|
||||
|
||||
}
|
||||
|
||||
TextButton {
|
||||
id: snapTab;
|
||||
text: "SNAP";
|
||||
property string includeActions: 'snapshot';
|
||||
selected: snapTab === selectedTab;
|
||||
action: tabSelect;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: bgMain
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: "#2b2b2b"
|
||||
|
||||
}
|
||||
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: "#0f212e"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
anchors.bottom: backgroundImage.top
|
||||
anchors.bottomMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.top: topBar.bottom
|
||||
anchors.topMargin: 0
|
||||
|
||||
ListModel { id: suggestions }
|
||||
|
||||
ListView {
|
||||
id: scroll
|
||||
|
||||
property int stackedCardShadowHeight: 10;
|
||||
clip: true
|
||||
spacing: 14
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: 50
|
||||
}
|
||||
model: suggestions
|
||||
orientation: ListView.Vertical
|
||||
|
||||
delegate: Card {
|
||||
width: cardWidth;
|
||||
height: cardHeight;
|
||||
goFunction: goCard;
|
||||
userName: model.username;
|
||||
placeName: model.place_name;
|
||||
hifiUrl: model.place_name + model.path;
|
||||
thumbnail: model.thumbnail_url;
|
||||
imageUrl: model.image_url;
|
||||
action: model.action;
|
||||
timestamp: model.created_at;
|
||||
onlineUsers: model.online_users;
|
||||
storyId: model.metaverseId;
|
||||
drillDownToPlace: model.drillDownToPlace;
|
||||
shadowHeight: scroll.stackedCardShadowHeight;
|
||||
hoverThunk: function () { scroll.currentIndex = index; }
|
||||
unhoverThunk: function () { scroll.currentIndex = -1; }
|
||||
}
|
||||
|
||||
highlightMoveDuration: -1;
|
||||
highlightMoveVelocity: -1;
|
||||
highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.blueHighlight; z: 1; }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: backgroundImage
|
||||
width: 480
|
||||
height: 70
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: "#c2ced8"
|
||||
|
||||
}
|
||||
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: "#c2ced8"
|
||||
}
|
||||
}
|
||||
|
||||
anchors {
|
||||
bottom: parent.keyboardEnabled ? keyboard.top : parent.bottom
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
|
||||
ToolbarButton {
|
||||
id: homeButton
|
||||
imageURL: "../../../images/home.svg"
|
||||
onClicked: {
|
||||
addressBarDialog.loadHome();
|
||||
root.shown = false;
|
||||
}
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: homeButton.width / 2
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
property int inputAreaHeight: 70
|
||||
property int inputAreaStep: (height - inputAreaHeight) / 2
|
||||
|
||||
HifiStyles.RalewayLight {
|
||||
id: notice;
|
||||
font.pixelSize: hifi.fonts.pixelSize * 0.50;
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: parent.inputAreaStep + 12
|
||||
left: addressLine.left
|
||||
right: addressLine.right
|
||||
}
|
||||
}
|
||||
HifiStyles.FiraSansRegular {
|
||||
id: location;
|
||||
font.pixelSize: addressLine.font.pixelSize;
|
||||
color: "gray";
|
||||
clip: true;
|
||||
anchors.fill: addressLine;
|
||||
visible: addressLine.text.length === 0
|
||||
}
|
||||
|
||||
TextInput {
|
||||
id: addressLine
|
||||
focus: true
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: homeButton.right
|
||||
right: parent.right
|
||||
leftMargin: homeButton.width
|
||||
rightMargin: homeButton.width / 2
|
||||
topMargin: parent.inputAreaStep + (2 * hifi.layout.spacing)
|
||||
bottomMargin: parent.inputAreaStep
|
||||
}
|
||||
font.pixelSize: hifi.fonts.pixelSize * 0.75
|
||||
cursorVisible: false
|
||||
onTextChanged: {
|
||||
filterChoicesByText();
|
||||
updateLocationText(text.length > 0);
|
||||
if (!isCursorVisible && text.length > 0) {
|
||||
isCursorVisible = true;
|
||||
cursorVisible = true;
|
||||
}
|
||||
}
|
||||
onAccepted: {
|
||||
addressBarDialog.keyboardEnabled = false;
|
||||
}
|
||||
onActiveFocusChanged: {
|
||||
cursorVisible = isCursorVisible && focus;
|
||||
}
|
||||
MouseArea {
|
||||
// If user clicks in address bar show cursor to indicate ability to enter address.
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
isCursorVisible = true;
|
||||
//parent.cursorVisible = true;
|
||||
parent.forceActiveFocus();
|
||||
addressBarDialog.keyboardEnabled = HMD.active
|
||||
tabletRoot.playButtonClickSound();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
// Delay updating location text a bit to avoid flicker of content and so that connection status is valid.
|
||||
id: updateLocationTextTimer
|
||||
running: false
|
||||
interval: 500 // ms
|
||||
repeat: false
|
||||
onTriggered: updateLocationText(false);
|
||||
}
|
||||
|
||||
Timer {
|
||||
// Delay clearing address line so as to avoid flicker of "not connected" being displayed after entering an address.
|
||||
id: clearAddressLineTimer
|
||||
running: false
|
||||
interval: 100 // ms
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
addressLine.text = "";
|
||||
isCursorVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: parent.keyboardEnabled
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects.
|
||||
// TODO: make available to other .qml.
|
||||
var request = new XMLHttpRequest();
|
||||
// QT bug: apparently doesn't handle onload. Workaround using readyState.
|
||||
request.onreadystatechange = function () {
|
||||
var READY_STATE_DONE = 4;
|
||||
var HTTP_OK = 200;
|
||||
if (request.readyState >= READY_STATE_DONE) {
|
||||
var error = (request.status !== HTTP_OK) && request.status.toString() + ':' + request.statusText,
|
||||
response = !error && request.responseText,
|
||||
contentType = !error && request.getResponseHeader('content-type');
|
||||
if (!error && contentType.indexOf('application/json') === 0) {
|
||||
try {
|
||||
response = JSON.parse(response);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
}
|
||||
cb(error, response);
|
||||
}
|
||||
};
|
||||
request.open("GET", url, true);
|
||||
request.send();
|
||||
}
|
||||
|
||||
function identity(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey
|
||||
if (!error && (data.status === 'success')) {
|
||||
return;
|
||||
}
|
||||
if (!error) { // Create a message from the data
|
||||
error = data.status + ': ' + data.error;
|
||||
}
|
||||
if (typeof(error) === 'string') { // Make a proper Error object
|
||||
error = new Error(error);
|
||||
}
|
||||
error.message += ' in ' + url; // Include the url.
|
||||
cb(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function resolveUrl(url) {
|
||||
return (url.indexOf('/') === 0) ? (addressBarDialog.metaverseServerUrl + url) : url;
|
||||
}
|
||||
|
||||
function makeModelData(data) { // create a new obj from data
|
||||
// ListModel elements will only ever have those properties that are defined by the first obj that is added.
|
||||
// So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story.
|
||||
var name = data.place_name,
|
||||
tags = data.tags || [data.action, data.username],
|
||||
description = data.description || "",
|
||||
thumbnail_url = data.thumbnail_url || "";
|
||||
return {
|
||||
place_name: name,
|
||||
username: data.username || "",
|
||||
path: data.path || "",
|
||||
created_at: data.created_at || "",
|
||||
action: data.action || "",
|
||||
thumbnail_url: resolveUrl(thumbnail_url),
|
||||
image_url: resolveUrl(data.details.image_url),
|
||||
|
||||
metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
|
||||
|
||||
tags: tags,
|
||||
description: description,
|
||||
online_users: data.details.concurrency || 0,
|
||||
drillDownToPlace: false,
|
||||
|
||||
searchText: [name].concat(tags, description || []).join(' ').toUpperCase()
|
||||
}
|
||||
}
|
||||
function suggestable(place) {
|
||||
if (place.action === 'snapshot') {
|
||||
return true;
|
||||
}
|
||||
return (place.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain.
|
||||
}
|
||||
property var selectedTab: allTab;
|
||||
function tabSelect(textButton) {
|
||||
selectedTab = textButton;
|
||||
fillDestinations();
|
||||
}
|
||||
property var placeMap: ({});
|
||||
function addToSuggestions(place) {
|
||||
var collapse = allTab.selected && (place.action !== 'concurrency');
|
||||
if (collapse) {
|
||||
var existing = placeMap[place.place_name];
|
||||
if (existing) {
|
||||
existing.drillDownToPlace = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
suggestions.append(place);
|
||||
if (collapse) {
|
||||
placeMap[place.place_name] = suggestions.get(suggestions.count - 1);
|
||||
} else if (place.action === 'concurrency') {
|
||||
suggestions.get(suggestions.count - 1).drillDownToPlace = true; // Don't change raw place object (in allStories).
|
||||
}
|
||||
}
|
||||
property int requestId: 0;
|
||||
function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model
|
||||
var options = [
|
||||
'now=' + new Date().toISOString(),
|
||||
'include_actions=' + selectedTab.includeActions,
|
||||
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
|
||||
'require_online=true',
|
||||
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
|
||||
'page=' + pageNumber
|
||||
];
|
||||
var url = metaverseBase + 'user_stories?' + options.join('&');
|
||||
var thisRequestId = ++requestId;
|
||||
getRequest(url, function (error, data) {
|
||||
if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) {
|
||||
return;
|
||||
}
|
||||
var stories = data.user_stories.map(function (story) { // explicit single-argument function
|
||||
return makeModelData(story, url);
|
||||
});
|
||||
allStories = allStories.concat(stories);
|
||||
stories.forEach(makeFilteredPlaceProcessor());
|
||||
if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now
|
||||
return getUserStoryPage(pageNumber + 1, cb);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}
|
||||
function makeFilteredPlaceProcessor() { // answer a function(placeData) that adds it to suggestions if it matches
|
||||
var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity),
|
||||
data = allStories;
|
||||
function matches(place) {
|
||||
if (!words.length) {
|
||||
return suggestable(place);
|
||||
}
|
||||
return words.every(function (word) {
|
||||
return place.searchText.indexOf(word) >= 0;
|
||||
});
|
||||
}
|
||||
return function (place) {
|
||||
if (matches(place)) {
|
||||
addToSuggestions(place);
|
||||
}
|
||||
};
|
||||
}
|
||||
function filterChoicesByText() {
|
||||
suggestions.clear();
|
||||
placeMap = {};
|
||||
allStories.forEach(makeFilteredPlaceProcessor());
|
||||
}
|
||||
|
||||
function fillDestinations() {
|
||||
allStories = [];
|
||||
suggestions.clear();
|
||||
placeMap = {};
|
||||
getUserStoryPage(1, function (error) {
|
||||
console.log('user stories query', error || 'ok', allStories.length);
|
||||
});
|
||||
}
|
||||
|
||||
function updateLocationText(enteringAddress) {
|
||||
if (enteringAddress) {
|
||||
notice.text = "Go to a place, @user, path or network address";
|
||||
notice.color = hifiStyleConstants.colors.baseGrayHighlight;
|
||||
} else {
|
||||
notice.text = AddressManager.isConnected ? "Your location:" : "Not Connected";
|
||||
notice.color = AddressManager.isConnected ? hifiStyleConstants.colors.baseGrayHighlight : hifiStyleConstants.colors.redHighlight;
|
||||
// Display hostname, which includes ip address, localhost, and other non-placenames.
|
||||
location.text = (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '');
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
updateLocationText(false);
|
||||
if (visible) {
|
||||
addressLine.forceActiveFocus();
|
||||
fillDestinations();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleOrGo(fromSuggestions) {
|
||||
if (addressLine.text !== "") {
|
||||
addressBarDialog.loadAddress(addressLine.text, fromSuggestions)
|
||||
}
|
||||
root.shown = false;
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
root.shown = false
|
||||
clearAddressLineTimer.start();
|
||||
event.accepted = true
|
||||
break
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
toggleOrGo()
|
||||
clearAddressLineTimer.start();
|
||||
event.accepted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,16 @@ Item {
|
|||
loader.item.scriptURL = injectedJavaScriptUrl;
|
||||
}
|
||||
|
||||
// used to send a message from qml to interface script.
|
||||
signal sendToScript(var message);
|
||||
|
||||
// used to receive messages from interface script
|
||||
function fromScript(message) {
|
||||
if (loader.item.hasOwnProperty("fromScript")) {
|
||||
loader.item.fromScript(message);
|
||||
}
|
||||
}
|
||||
|
||||
SoundEffect {
|
||||
id: buttonClickSound
|
||||
volume: 0.1
|
||||
|
@ -55,6 +65,9 @@ Item {
|
|||
}
|
||||
});
|
||||
}
|
||||
if (loader.item.hasOwnProperty("sendToScript")) {
|
||||
loader.item.sendToScript.connect(tabletRoot.sendToScript);
|
||||
}
|
||||
loader.item.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include <QtMultimedia/QMediaPlayer>
|
||||
|
||||
#include <QFontDatabase>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
|
@ -62,6 +63,7 @@
|
|||
#include <DebugDraw.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <EntityScriptClient.h>
|
||||
#include <EntityScriptServerLogClient.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
#include <ErrorDialog.h>
|
||||
#include <FileScriptingInterface.h>
|
||||
|
@ -174,7 +176,6 @@
|
|||
#include "FrameTimingsScriptingInterface.h"
|
||||
#include <GPUIdent.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <EntityScriptClient.h>
|
||||
|
||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
|
@ -436,6 +437,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
}
|
||||
|
||||
DependencyManager::set<tracing::Tracer>();
|
||||
PROFILE_SET_THREAD_NAME("Main Thread");
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
// Select appropriate audio DLL
|
||||
|
@ -518,6 +520,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<CompositorHelper>();
|
||||
DependencyManager::set<OffscreenQmlSurfaceCache>();
|
||||
DependencyManager::set<EntityScriptClient>();
|
||||
DependencyManager::set<EntityScriptServerLogClient>();
|
||||
return previousSessionCrashed;
|
||||
}
|
||||
|
||||
|
@ -5522,6 +5525,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
||||
scriptEngine->registerGlobalObject("Recording", recordingInterface.data());
|
||||
|
||||
auto entityScriptServerLog = DependencyManager::get<EntityScriptServerLogClient>();
|
||||
scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data());
|
||||
|
||||
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
||||
connect(scriptEngine, &ScriptEngine::printedMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onPrintedMessage);
|
||||
connect(scriptEngine, &ScriptEngine::errorMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onErrorMessage);
|
||||
|
@ -6282,6 +6288,17 @@ void Application::toggleLogDialog() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::toggleEntityScriptServerLogDialog() {
|
||||
if (! _entityScriptServerLogDialog) {
|
||||
_entityScriptServerLogDialog = new EntityScriptServerLogDialog(nullptr);
|
||||
}
|
||||
|
||||
if (_entityScriptServerLogDialog->isVisible()) {
|
||||
_entityScriptServerLogDialog->hide();
|
||||
} else {
|
||||
_entityScriptServerLogDialog->show();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
|
||||
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
#include "scripting/DialogsManagerScriptingInterface.h"
|
||||
#include "ui/ApplicationOverlay.h"
|
||||
#include "ui/BandwidthDialog.h"
|
||||
#include "ui/EntityScriptServerLogDialog.h"
|
||||
#include "ui/LodToolsDialog.h"
|
||||
#include "ui/LogDialog.h"
|
||||
#include "ui/OctreeStatsDialog.h"
|
||||
|
@ -314,6 +315,7 @@ public slots:
|
|||
Q_INVOKABLE void loadDialog();
|
||||
Q_INVOKABLE void loadScriptURLDialog() const;
|
||||
void toggleLogDialog();
|
||||
void toggleEntityScriptServerLogDialog();
|
||||
void toggleRunningScriptsWidget() const;
|
||||
Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
|
||||
|
||||
|
@ -566,6 +568,7 @@ private:
|
|||
NodeToOctreeSceneStats _octreeServerSceneStats;
|
||||
ControllerScriptingInterface* _controllerScriptingInterface{ nullptr };
|
||||
QPointer<LogDialog> _logDialog;
|
||||
QPointer<EntityScriptServerLogDialog> _entityScriptServerLogDialog;
|
||||
|
||||
FileLogger* _logger;
|
||||
|
||||
|
|
|
@ -702,7 +702,14 @@ Menu::Menu() {
|
|||
|
||||
// Developer > Log...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::Log, Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
qApp, SLOT(toggleLogDialog()));
|
||||
qApp, SLOT(toggleLogDialog()));
|
||||
auto essLogAction = addActionToQMenuAndActionHash(developerMenu, MenuOption::EntityScriptServerLog, 0,
|
||||
qApp, SLOT(toggleEntityScriptServerLogDialog()));
|
||||
QObject::connect(nodeList.data(), &NodeList::canRezChanged, essLogAction, [essLogAction] {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
essLogAction->setEnabled(nodeList->getThisNodeCanRez());
|
||||
});
|
||||
essLogAction->setEnabled(nodeList->getThisNodeCanRez());
|
||||
|
||||
action = addActionToQMenuAndActionHash(developerMenu, "Script Log (HMD friendly)...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
|
|
|
@ -98,6 +98,7 @@ namespace MenuOption {
|
|||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString EnableCharacterController = "Enable avatar collisions";
|
||||
const QString EnableInverseKinematics = "Enable Inverse Kinematics";
|
||||
const QString EntityScriptServerLog = "Entity Script Server Log";
|
||||
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
||||
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
|
||||
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
|
||||
|
|
|
@ -307,12 +307,22 @@ bool Avatar::shouldDie() const {
|
|||
|
||||
void Avatar::simulate(float deltaTime, bool inView) {
|
||||
PROFILE_RANGE(simulation, "simulate");
|
||||
|
||||
_simulationRate.increment();
|
||||
if (inView) {
|
||||
_simulationInViewRate.increment();
|
||||
}
|
||||
|
||||
|
||||
PerformanceTimer perfTimer("simulate");
|
||||
{
|
||||
PROFILE_RANGE(simulation, "updateJoints");
|
||||
if (inView && _hasNewJointData) {
|
||||
_skeletonModel->getRig()->copyJointsFromJointData(_jointData);
|
||||
_jointDataSimulationRate.increment();
|
||||
|
||||
_skeletonModel->simulate(deltaTime, true);
|
||||
_skeletonModelSimulationRate.increment();
|
||||
|
||||
locationChanged(); // joints changed, so if there are any children, update them.
|
||||
_hasNewJointData = false;
|
||||
|
@ -328,6 +338,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
|
|||
} else {
|
||||
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
|
||||
_skeletonModel->simulate(deltaTime, false);
|
||||
_skeletonModelSimulationRate.increment();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -357,6 +368,21 @@ void Avatar::simulate(float deltaTime, bool inView) {
|
|||
}
|
||||
}
|
||||
|
||||
float Avatar::getSimulationRate(const QString& rateName) const {
|
||||
if (rateName == "") {
|
||||
return _simulationRate.rate();
|
||||
} else if (rateName == "avatar") {
|
||||
return _simulationRate.rate();
|
||||
} else if (rateName == "avatarInView") {
|
||||
return _simulationInViewRate.rate();
|
||||
} else if (rateName == "skeletonModel") {
|
||||
return _skeletonModelSimulationRate.rate();
|
||||
} else if (rateName == "jointData") {
|
||||
return _jointDataSimulationRate.rate();
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const {
|
||||
const float HEAD_SPHERE_RADIUS = 0.1f;
|
||||
glm::vec3 theirLookAt = dynamic_pointer_cast<Avatar>(avatar)->getHead()->getLookAtPosition();
|
||||
|
@ -1010,7 +1036,7 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
|
||||
void Avatar::setModelURLFinished(bool success) {
|
||||
if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
|
||||
qDebug() << "Using default after failing to load Avatar model: " << _skeletonModelURL;
|
||||
qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL;
|
||||
// call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that
|
||||
// we don't redo this every time we receive an identity packet from the avatar with the bad url.
|
||||
QMetaObject::invokeMethod(_skeletonModel.get(), "setURL",
|
||||
|
|
|
@ -182,6 +182,8 @@ public:
|
|||
void animateScaleChanges(float deltaTime);
|
||||
void setTargetScale(float targetScale) override;
|
||||
|
||||
Q_INVOKABLE float getSimulationRate(const QString& rateName = QString("")) const;
|
||||
|
||||
public slots:
|
||||
|
||||
// FIXME - these should be migrated to use Pose data instead
|
||||
|
@ -259,6 +261,13 @@ protected:
|
|||
void addToScene(AvatarSharedPointer self);
|
||||
void ensureInScene(AvatarSharedPointer self);
|
||||
|
||||
// Some rate tracking support
|
||||
RateCounter<> _simulationRate;
|
||||
RateCounter<> _simulationInViewRate;
|
||||
RateCounter<> _skeletonModelSimulationRate;
|
||||
RateCounter<> _jointDataSimulationRate;
|
||||
|
||||
|
||||
private:
|
||||
uint64_t _lastRenderUpdateTime { 0 };
|
||||
int _leftPointerGeometryID { 0 };
|
||||
|
|
|
@ -132,11 +132,23 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
|
|||
|
||||
Q_LOGGING_CATEGORY(trace_simulation_avatar, "trace.simulation.avatar");
|
||||
|
||||
float AvatarManager::getAvatarDataRate(const QUuid& sessionID, const QString& rateName) {
|
||||
float AvatarManager::getAvatarDataRate(const QUuid& sessionID, const QString& rateName) const {
|
||||
auto avatar = getAvatarBySessionID(sessionID);
|
||||
return avatar->getDataRate(rateName);
|
||||
return avatar ? avatar->getDataRate(rateName) : 0.0f;
|
||||
}
|
||||
|
||||
float AvatarManager::getAvatarUpdateRate(const QUuid& sessionID, const QString& rateName) const {
|
||||
auto avatar = getAvatarBySessionID(sessionID);
|
||||
return avatar ? avatar->getUpdateRate(rateName) : 0.0f;
|
||||
}
|
||||
|
||||
float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName) const {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(getAvatarBySessionID(sessionID));
|
||||
return avatar ? avatar->getSimulationRate(rateName) : 0.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class AvatarPriority {
|
||||
public:
|
||||
AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {}
|
||||
|
@ -218,6 +230,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
const uint64_t MAX_UPDATE_BUDGET = 2000; // usec
|
||||
uint64_t renderExpiry = startTime + RENDER_UPDATE_BUDGET;
|
||||
uint64_t maxExpiry = startTime + MAX_UPDATE_BUDGET;
|
||||
|
||||
int fullySimulatedAvatars = 0;
|
||||
int partiallySimulatedAvatars = 0;
|
||||
while (!sortedAvatars.empty()) {
|
||||
const AvatarPriority& sortData = sortedAvatars.top();
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar);
|
||||
|
@ -246,11 +261,13 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
avatar->simulate(deltaTime, inView);
|
||||
avatar->updateRenderItem(pendingChanges);
|
||||
avatar->setLastRenderUpdateTime(startTime);
|
||||
fullySimulatedAvatars++;
|
||||
} else if (now < maxExpiry) {
|
||||
// we've spent most of our time budget, but we still simulate() the avatar as it if were out of view
|
||||
// --> some avatars may freeze until their priority trickles up
|
||||
const bool inView = false;
|
||||
avatar->simulate(deltaTime, inView);
|
||||
partiallySimulatedAvatars++;
|
||||
} else {
|
||||
// we've spent ALL of our time budget --> bail on the rest of the avatar updates
|
||||
// --> some scale or fade animations may glitch
|
||||
|
@ -259,6 +276,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
}
|
||||
sortedAvatars.pop();
|
||||
}
|
||||
|
||||
_avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;
|
||||
_fullySimulatedAvatars = fullySimulatedAvatars;
|
||||
_partiallySimulatedAvatars = partiallySimulatedAvatars;
|
||||
qApp->getMain3DScene()->enqueuePendingChanges(pendingChanges);
|
||||
|
||||
simulateAvatarFades(deltaTime);
|
||||
|
@ -492,7 +513,7 @@ void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
|
|||
}
|
||||
|
||||
|
||||
AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) {
|
||||
AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) const {
|
||||
if (sessionID == AVATAR_SELF_ID || sessionID == _myAvatar->getSessionUUID()) {
|
||||
return _myAvatar;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,11 @@ public:
|
|||
void init();
|
||||
|
||||
std::shared_ptr<MyAvatar> getMyAvatar() { return _myAvatar; }
|
||||
AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) override;
|
||||
AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const override;
|
||||
|
||||
int getFullySimulatedAvatars() const { return _fullySimulatedAvatars; }
|
||||
int getPartiallySimulatedAvatars() const { return _partiallySimulatedAvatars; }
|
||||
float getAvatarSimulationTime() const { return _avatarSimulationTime; }
|
||||
|
||||
void updateMyAvatar(float deltaTime);
|
||||
void updateOtherAvatars(float deltaTime);
|
||||
|
@ -69,7 +73,10 @@ public:
|
|||
void handleOutgoingChanges(const VectorOfMotionStates& motionStates);
|
||||
void handleCollisionEvents(const CollisionEvents& collisionEvents);
|
||||
|
||||
Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString(""));
|
||||
Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
|
||||
Q_INVOKABLE float getAvatarUpdateRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
|
||||
Q_INVOKABLE float getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
|
||||
|
||||
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
|
||||
const QScriptValue& avatarIdsToInclude = QScriptValue(),
|
||||
const QScriptValue& avatarIdsToDiscard = QScriptValue());
|
||||
|
@ -109,6 +116,9 @@ private:
|
|||
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
|
||||
|
||||
RateCounter<> _myAvatarSendRate;
|
||||
int _fullySimulatedAvatars { 0 };
|
||||
int _partiallySimulatedAvatars { 0 };
|
||||
float _avatarSimulationTime { 0.0f };
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ void CauterizedModel::updateClusterMatrices() {
|
|||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||
auto jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
||||
#if GLM_ARCH & GLM_ARCH_SSE2
|
||||
#if (GLM_ARCH & GLM_ARCH_SSE2) && !(defined Q_OS_MACOS)
|
||||
glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix;
|
||||
glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out);
|
||||
state.clusterMatrices[j] = out;
|
||||
|
@ -155,7 +155,7 @@ void CauterizedModel::updateClusterMatrices() {
|
|||
if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) {
|
||||
jointMatrix = cauterizeMatrix;
|
||||
}
|
||||
#if GLM_ARCH & GLM_ARCH_SSE2
|
||||
#if (GLM_ARCH & GLM_ARCH_SSE2) && !(defined Q_OS_MACOS)
|
||||
glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix;
|
||||
glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out);
|
||||
state.clusterMatrices[j] = out;
|
||||
|
|
|
@ -795,6 +795,11 @@ void MyAvatar::saveData() {
|
|||
}
|
||||
settings.endArray();
|
||||
|
||||
if (_avatarEntityData.size() == 0) {
|
||||
// HACK: manually remove empty 'avatarEntityData' else deleted avatarEntityData may show up in settings file
|
||||
settings.remove("avatarEntityData");
|
||||
}
|
||||
|
||||
settings.beginWriteArray("avatarEntityData");
|
||||
int avatarEntityIndex = 0;
|
||||
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
|
||||
|
@ -931,6 +936,10 @@ void MyAvatar::loadData() {
|
|||
updateAvatarEntity(entityID, properties);
|
||||
}
|
||||
settings.endArray();
|
||||
if (avatarEntityCount == 0) {
|
||||
// HACK: manually remove empty 'avatarEntityData' else legacy data may persist in settings file
|
||||
settings.remove("avatarEntityData");
|
||||
}
|
||||
setAvatarEntityDataChanged(true);
|
||||
|
||||
setDisplayName(settings.value("displayName").toString());
|
||||
|
@ -1165,7 +1174,6 @@ void MyAvatar::clearJointsData() {
|
|||
}
|
||||
|
||||
void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||
|
||||
Avatar::setSkeletonModelURL(skeletonModelURL);
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
_skeletonModel->setVisibleInScene(true, scene);
|
||||
|
|
|
@ -134,3 +134,9 @@ void TestScriptingInterface::startTraceEvent(QString name) {
|
|||
void TestScriptingInterface::endTraceEvent(QString name) {
|
||||
tracing::traceEvent(trace_test(), name, tracing::DurationEnd);
|
||||
}
|
||||
|
||||
void TestScriptingInterface::profileRange(const QString& name, QScriptValue fn) {
|
||||
PROFILE_RANGE(script, name);
|
||||
fn.call();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <functional>
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class QScriptValue;
|
||||
|
||||
class TestScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -69,6 +71,7 @@ public slots:
|
|||
|
||||
void endTraceEvent(QString name);
|
||||
|
||||
Q_INVOKABLE void profileRange(const QString& name, QScriptValue function);
|
||||
|
||||
private:
|
||||
bool waitForCondition(qint64 maxWaitMs, std::function<bool()> condition);
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include <SettingHandle.h>
|
||||
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "DomainHandler.h"
|
||||
#include "MainWindow.h"
|
||||
|
@ -147,6 +149,15 @@ void WindowScriptingInterface::setPreviousBrowseLocation(const QString& location
|
|||
Setting::Handle<QVariant>(LAST_BROWSE_LOCATION_SETTING).set(location);
|
||||
}
|
||||
|
||||
/// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and
|
||||
/// might be in same thread as a script that sets the reticle to invisible
|
||||
void WindowScriptingInterface::ensureReticleVisible() const {
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
if (!compositorHelper->getReticleVisible()) {
|
||||
compositorHelper->setReticleVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Display an open file dialog. If `directory` is an invalid file or directory the browser will start at the current
|
||||
/// working directory.
|
||||
/// \param const QString& title title of the window
|
||||
|
@ -154,6 +165,7 @@ void WindowScriptingInterface::setPreviousBrowseLocation(const QString& location
|
|||
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
|
||||
/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue`
|
||||
QScriptValue WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) {
|
||||
ensureReticleVisible();
|
||||
QString path = directory;
|
||||
if (path.isEmpty()) {
|
||||
path = getPreviousBrowseLocation();
|
||||
|
@ -175,6 +187,7 @@ QScriptValue WindowScriptingInterface::browse(const QString& title, const QStrin
|
|||
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
|
||||
/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue`
|
||||
QScriptValue WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) {
|
||||
ensureReticleVisible();
|
||||
QString path = directory;
|
||||
if (path.isEmpty()) {
|
||||
path = getPreviousBrowseLocation();
|
||||
|
|
|
@ -83,6 +83,8 @@ private:
|
|||
QString getPreviousBrowseLocation() const;
|
||||
void setPreviousBrowseLocation(const QString& location);
|
||||
|
||||
void ensureReticleVisible() const;
|
||||
|
||||
int createMessageBox(QString title, QString text, int buttons, int defaultButton);
|
||||
QHash<int, QQuickItem*> _messageBoxes;
|
||||
int _lastMessageBoxID{ -1 };
|
||||
|
|
135
interface/src/ui/BaseLogDialog.cpp
Normal file
135
interface/src/ui/BaseLogDialog.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// BaseLogDialog.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Clement Brisset on 1/31/17.
|
||||
// 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 "BaseLogDialog.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QLineEdit>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPushButton>
|
||||
#include <QSyntaxHighlighter>
|
||||
|
||||
#include <PathUtils.h>
|
||||
|
||||
const int TOP_BAR_HEIGHT = 46;
|
||||
const int INITIAL_WIDTH = 720;
|
||||
const int INITIAL_HEIGHT = 480;
|
||||
const int MINIMAL_WIDTH = 570;
|
||||
const int SEARCH_BUTTON_LEFT = 25;
|
||||
const int SEARCH_BUTTON_WIDTH = 20;
|
||||
const int SEARCH_TEXT_WIDTH = 240;
|
||||
const QColor HIGHLIGHT_COLOR = QColor("#3366CC");
|
||||
|
||||
class KeywordHighlighter : public QSyntaxHighlighter {
|
||||
public:
|
||||
KeywordHighlighter(QTextDocument* parent = nullptr);
|
||||
QString keyword;
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString& text) override;
|
||||
|
||||
private:
|
||||
QTextCharFormat keywordFormat;
|
||||
|
||||
};
|
||||
|
||||
BaseLogDialog::BaseLogDialog(QWidget* parent) : QDialog(parent, Qt::Window) {
|
||||
setWindowTitle("Base Log");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
QFile styleSheet(PathUtils::resourcesPath() + "styles/log_dialog.qss");
|
||||
if (styleSheet.open(QIODevice::ReadOnly)) {
|
||||
QDir::setCurrent(PathUtils::resourcesPath());
|
||||
setStyleSheet(styleSheet.readAll());
|
||||
}
|
||||
|
||||
initControls();
|
||||
|
||||
resize(INITIAL_WIDTH, INITIAL_HEIGHT);
|
||||
setMinimumWidth(MINIMAL_WIDTH);
|
||||
}
|
||||
|
||||
BaseLogDialog::~BaseLogDialog() {
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void BaseLogDialog::initControls() {
|
||||
_searchButton = new QPushButton(this);
|
||||
// set object name for css styling
|
||||
_searchButton->setObjectName("searchButton");
|
||||
_leftPad = SEARCH_BUTTON_LEFT;
|
||||
_searchButton->setGeometry(_leftPad, ELEMENT_MARGIN, SEARCH_BUTTON_WIDTH, ELEMENT_HEIGHT);
|
||||
_leftPad += SEARCH_BUTTON_WIDTH;
|
||||
_searchButton->show();
|
||||
connect(_searchButton, SIGNAL(clicked()), SLOT(handleSearchButton()));
|
||||
|
||||
_searchTextBox = new QLineEdit(this);
|
||||
// disable blue outline in Mac
|
||||
_searchTextBox->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
_searchTextBox->setGeometry(_leftPad, ELEMENT_MARGIN, SEARCH_TEXT_WIDTH, ELEMENT_HEIGHT);
|
||||
_leftPad += SEARCH_TEXT_WIDTH + CHECKBOX_MARGIN;
|
||||
_searchTextBox->show();
|
||||
connect(_searchTextBox, SIGNAL(textChanged(QString)), SLOT(handleSearchTextChanged(QString)));
|
||||
|
||||
_logTextBox = new QPlainTextEdit(this);
|
||||
_logTextBox->setReadOnly(true);
|
||||
_logTextBox->show();
|
||||
_highlighter = new KeywordHighlighter(_logTextBox->document());
|
||||
|
||||
}
|
||||
|
||||
void BaseLogDialog::showEvent(QShowEvent* event) {
|
||||
showLogData();
|
||||
}
|
||||
|
||||
void BaseLogDialog::resizeEvent(QResizeEvent* event) {
|
||||
_logTextBox->setGeometry(0, TOP_BAR_HEIGHT, width(), height() - TOP_BAR_HEIGHT);
|
||||
}
|
||||
|
||||
void BaseLogDialog::appendLogLine(QString logLine) {
|
||||
if (logLine.contains(_searchTerm, Qt::CaseInsensitive)) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
}
|
||||
}
|
||||
|
||||
void BaseLogDialog::handleSearchButton() {
|
||||
_searchTextBox->setFocus();
|
||||
}
|
||||
|
||||
void BaseLogDialog::handleSearchTextChanged(QString searchText) {
|
||||
_searchTerm = searchText;
|
||||
_highlighter->keyword = searchText;
|
||||
_highlighter->rehighlight();
|
||||
}
|
||||
|
||||
void BaseLogDialog::showLogData() {
|
||||
_logTextBox->clear();
|
||||
_logTextBox->appendPlainText(getCurrentLog());
|
||||
_logTextBox->ensureCursorVisible();
|
||||
}
|
||||
|
||||
KeywordHighlighter::KeywordHighlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) {
|
||||
keywordFormat.setForeground(HIGHLIGHT_COLOR);
|
||||
}
|
||||
|
||||
void KeywordHighlighter::highlightBlock(const QString& text) {
|
||||
if (keyword.isNull() || keyword.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int index = text.indexOf(keyword, 0, Qt::CaseInsensitive);
|
||||
int length = keyword.length();
|
||||
|
||||
while (index >= 0) {
|
||||
setFormat(index, length, keywordFormat);
|
||||
index = text.indexOf(keyword, index + length, Qt::CaseInsensitive);
|
||||
}
|
||||
}
|
60
interface/src/ui/BaseLogDialog.h
Normal file
60
interface/src/ui/BaseLogDialog.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// BaseLogDialog.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Clement Brisset on 1/31/17.
|
||||
// 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_BaseLogDialog_h
|
||||
#define hifi_BaseLogDialog_h
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
const int ELEMENT_MARGIN = 7;
|
||||
const int ELEMENT_HEIGHT = 32;
|
||||
const int CHECKBOX_MARGIN = 12;
|
||||
const int CHECKBOX_WIDTH = 140;
|
||||
|
||||
class QPushButton;
|
||||
class QLineEdit;
|
||||
class QPlainTextEdit;
|
||||
class KeywordHighlighter;
|
||||
|
||||
class BaseLogDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BaseLogDialog(QWidget* parent);
|
||||
~BaseLogDialog();
|
||||
|
||||
public slots:
|
||||
void appendLogLine(QString logLine);
|
||||
|
||||
private slots:
|
||||
void handleSearchButton();
|
||||
void handleSearchTextChanged(QString text);
|
||||
|
||||
protected:
|
||||
int _leftPad { 0 };
|
||||
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
virtual QString getCurrentLog() = 0;
|
||||
|
||||
private:
|
||||
QPushButton* _searchButton { nullptr };
|
||||
QLineEdit* _searchTextBox { nullptr };
|
||||
QPlainTextEdit* _logTextBox { nullptr };
|
||||
QString _searchTerm;
|
||||
KeywordHighlighter* _highlighter { nullptr };
|
||||
|
||||
void initControls();
|
||||
void showLogData();
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_BaseLogDialog_h
|
23
interface/src/ui/EntityScriptServerLogDialog.cpp
Normal file
23
interface/src/ui/EntityScriptServerLogDialog.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// EntityScriptServerLogDialog.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Clement Brisset on 1/31/17.
|
||||
// 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 "EntityScriptServerLogDialog.h"
|
||||
|
||||
#include <EntityScriptServerLogClient.h>
|
||||
|
||||
EntityScriptServerLogDialog::EntityScriptServerLogDialog(QWidget* parent) : BaseLogDialog(parent) {
|
||||
setWindowTitle("Entity Script Server Log");
|
||||
|
||||
|
||||
auto client = DependencyManager::get<EntityScriptServerLogClient>();
|
||||
QObject::connect(client.data(), &EntityScriptServerLogClient::receivedNewLogLines,
|
||||
this, &EntityScriptServerLogDialog::appendLogLine);
|
||||
}
|
27
interface/src/ui/EntityScriptServerLogDialog.h
Normal file
27
interface/src/ui/EntityScriptServerLogDialog.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// EntityScriptServerLogDialog.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Clement Brisset on 1/31/17.
|
||||
// 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_EntityScriptServerLogDialog_h
|
||||
#define hifi_EntityScriptServerLogDialog_h
|
||||
|
||||
#include "BaseLogDialog.h"
|
||||
|
||||
class EntityScriptServerLogDialog : public BaseLogDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EntityScriptServerLogDialog(QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
QString getCurrentLog() override { return QString(); };
|
||||
};
|
||||
|
||||
#endif // hifi_EntityScriptServerLogDialog_h
|
|
@ -9,90 +9,21 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
#include "LogDialog.h"
|
||||
|
||||
#include <QDesktopWidget>
|
||||
#include <QTextBlock>
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#include <shared/AbstractLoggerInterface.h>
|
||||
|
||||
#include <PathUtils.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ui/LogDialog.h"
|
||||
|
||||
const int TOP_BAR_HEIGHT = 46;
|
||||
const int INITIAL_WIDTH = 720;
|
||||
const int MINIMAL_WIDTH = 570;
|
||||
const int ELEMENT_MARGIN = 7;
|
||||
const int ELEMENT_HEIGHT = 32;
|
||||
const int SEARCH_BUTTON_LEFT = 25;
|
||||
const int SEARCH_BUTTON_WIDTH = 20;
|
||||
const int SEARCH_TEXT_WIDTH = 240;
|
||||
const int CHECKBOX_MARGIN = 12;
|
||||
const int CHECKBOX_WIDTH = 140;
|
||||
const int REVEAL_BUTTON_WIDTH = 122;
|
||||
const float INITIAL_HEIGHT_RATIO = 0.6f;
|
||||
const QString HIGHLIGHT_COLOR = "#3366CC";
|
||||
|
||||
int qTextCursorMeta = qRegisterMetaType<QTextCursor>("QTextCursor");
|
||||
int qTextBlockMeta = qRegisterMetaType<QTextBlock>("QTextBlock");
|
||||
|
||||
LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : QDialog(parent, Qt::Window) {
|
||||
|
||||
LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLogDialog(parent) {
|
||||
_logger = logger;
|
||||
setWindowTitle("Log");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
QFile styleSheet(PathUtils::resourcesPath() + "styles/log_dialog.qss");
|
||||
if (styleSheet.open(QIODevice::ReadOnly)) {
|
||||
QDir::setCurrent(PathUtils::resourcesPath());
|
||||
setStyleSheet(styleSheet.readAll());
|
||||
}
|
||||
|
||||
initControls();
|
||||
|
||||
QDesktopWidget desktop;
|
||||
QRect screen = desktop.screenGeometry();
|
||||
resize(INITIAL_WIDTH, static_cast<int>(screen.height() * INITIAL_HEIGHT_RATIO));
|
||||
move(screen.center() - rect().center());
|
||||
setMinimumWidth(MINIMAL_WIDTH);
|
||||
|
||||
connect(_logger, SIGNAL(logReceived(QString)), this, SLOT(appendLogLine(QString)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
LogDialog::~LogDialog() {
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void LogDialog::initControls() {
|
||||
|
||||
int left;
|
||||
_searchButton = new QPushButton(this);
|
||||
// set object name for css styling
|
||||
_searchButton->setObjectName("searchButton");
|
||||
left = SEARCH_BUTTON_LEFT;
|
||||
_searchButton->setGeometry(left, ELEMENT_MARGIN, SEARCH_BUTTON_WIDTH, ELEMENT_HEIGHT);
|
||||
left += SEARCH_BUTTON_WIDTH;
|
||||
_searchButton->show();
|
||||
connect(_searchButton, SIGNAL(clicked()), SLOT(handleSearchButton()));
|
||||
|
||||
_searchTextBox = new QLineEdit(this);
|
||||
// disable blue outline in Mac
|
||||
_searchTextBox->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
_searchTextBox->setGeometry(left, ELEMENT_MARGIN, SEARCH_TEXT_WIDTH, ELEMENT_HEIGHT);
|
||||
left += SEARCH_TEXT_WIDTH + CHECKBOX_MARGIN;
|
||||
_searchTextBox->show();
|
||||
connect(_searchTextBox, SIGNAL(textChanged(QString)), SLOT(handleSearchTextChanged(QString)));
|
||||
|
||||
_extraDebuggingBox = new QCheckBox("Extra debugging", this);
|
||||
_extraDebuggingBox->setGeometry(left, ELEMENT_MARGIN, CHECKBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
_extraDebuggingBox->setGeometry(_leftPad, ELEMENT_MARGIN, CHECKBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
if (_logger->extraDebugging()) {
|
||||
_extraDebuggingBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
|
@ -105,72 +36,25 @@ void LogDialog::initControls() {
|
|||
_revealLogButton->show();
|
||||
connect(_revealLogButton, SIGNAL(clicked()), SLOT(handleRevealButton()));
|
||||
|
||||
_logTextBox = new QPlainTextEdit(this);
|
||||
_logTextBox->setReadOnly(true);
|
||||
_logTextBox->show();
|
||||
_highlighter = new KeywordHighlighter(_logTextBox->document());
|
||||
|
||||
connect(_logger, SIGNAL(logReceived(QString)), this, SLOT(appendLogLine(QString)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void LogDialog::showEvent(QShowEvent*) {
|
||||
showLogData();
|
||||
}
|
||||
|
||||
void LogDialog::resizeEvent(QResizeEvent*) {
|
||||
_logTextBox->setGeometry(0, TOP_BAR_HEIGHT, width(), height() - TOP_BAR_HEIGHT);
|
||||
void LogDialog::resizeEvent(QResizeEvent* event) {
|
||||
BaseLogDialog::resizeEvent(event);
|
||||
_revealLogButton->setGeometry(width() - ELEMENT_MARGIN - REVEAL_BUTTON_WIDTH,
|
||||
ELEMENT_MARGIN,
|
||||
REVEAL_BUTTON_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
}
|
||||
|
||||
void LogDialog::appendLogLine(QString logLine) {
|
||||
if (isVisible()) {
|
||||
if (logLine.contains(_searchTerm, Qt::CaseInsensitive)) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LogDialog::handleSearchButton() {
|
||||
_searchTextBox->setFocus();
|
||||
}
|
||||
|
||||
void LogDialog::handleRevealButton() {
|
||||
_logger->locateLog();
|
||||
}
|
||||
|
||||
void LogDialog::handleExtraDebuggingCheckbox(const int state) {
|
||||
void LogDialog::handleExtraDebuggingCheckbox(int state) {
|
||||
_logger->setExtraDebugging(state != 0);
|
||||
}
|
||||
|
||||
void LogDialog::handleSearchTextChanged(const QString searchText) {
|
||||
_searchTerm = searchText;
|
||||
_highlighter->keyword = searchText;
|
||||
showLogData();
|
||||
}
|
||||
|
||||
void LogDialog::showLogData() {
|
||||
_logTextBox->clear();
|
||||
_logTextBox->insertPlainText(_logger->getLogData());
|
||||
_logTextBox->ensureCursorVisible();
|
||||
}
|
||||
|
||||
KeywordHighlighter::KeywordHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent), keywordFormat() {
|
||||
keywordFormat.setForeground(QColor(HIGHLIGHT_COLOR));
|
||||
}
|
||||
|
||||
void KeywordHighlighter::highlightBlock(const QString &text) {
|
||||
|
||||
if (keyword.isNull() || keyword.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int index = text.indexOf(keyword, 0, Qt::CaseInsensitive);
|
||||
int length = keyword.length();
|
||||
|
||||
while (index >= 0) {
|
||||
setFormat(index, length, keywordFormat);
|
||||
index = text.indexOf(keyword, index + length, Qt::CaseInsensitive);
|
||||
}
|
||||
QString LogDialog::getCurrentLog() {
|
||||
return _logger->getLogData();
|
||||
}
|
||||
|
|
|
@ -12,64 +12,32 @@
|
|||
#ifndef hifi_LogDialog_h
|
||||
#define hifi_LogDialog_h
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMutex>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include <QSyntaxHighlighter>
|
||||
#include "BaseLogDialog.h"
|
||||
|
||||
#include <shared/AbstractLoggerInterface.h>
|
||||
class QCheckBox;
|
||||
class QPushButton;
|
||||
class QResizeEvent;
|
||||
class AbstractLoggerInterface;
|
||||
|
||||
class KeywordHighlighter : public QSyntaxHighlighter {
|
||||
class LogDialog : public BaseLogDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
KeywordHighlighter(QTextDocument *parent = 0);
|
||||
QString keyword;
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString &text) override;
|
||||
|
||||
private:
|
||||
QTextCharFormat keywordFormat;
|
||||
|
||||
};
|
||||
|
||||
class LogDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LogDialog(QWidget*, AbstractLoggerInterface*);
|
||||
~LogDialog();
|
||||
|
||||
public slots:
|
||||
void appendLogLine(QString logLine);
|
||||
LogDialog(QWidget* parent, AbstractLoggerInterface* logger);
|
||||
|
||||
private slots:
|
||||
void handleSearchButton();
|
||||
void handleRevealButton();
|
||||
void handleExtraDebuggingCheckbox(const int);
|
||||
void handleSearchTextChanged(const QString);
|
||||
void handleExtraDebuggingCheckbox(int);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent*) override;
|
||||
void showEvent(QShowEvent*) override;
|
||||
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
QString getCurrentLog() override;
|
||||
|
||||
private:
|
||||
QPushButton* _searchButton;
|
||||
QLineEdit* _searchTextBox;
|
||||
QCheckBox* _extraDebuggingBox;
|
||||
QPushButton* _revealLogButton;
|
||||
QPlainTextEdit* _logTextBox;
|
||||
QString _searchTerm;
|
||||
KeywordHighlighter* _highlighter;
|
||||
|
||||
AbstractLoggerInterface* _logger;
|
||||
|
||||
void initControls();
|
||||
void showLogData();
|
||||
};
|
||||
|
||||
#endif // hifi_LogDialog_h
|
||||
|
|
|
@ -121,6 +121,8 @@ void Stats::updateStats(bool force) {
|
|||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
// we need to take one avatar out so we don't include ourselves
|
||||
STAT_UPDATE(avatarCount, avatarManager->size() - 1);
|
||||
STAT_UPDATE(fullySimulatedAvatarCount, avatarManager->getFullySimulatedAvatars());
|
||||
STAT_UPDATE(partiallySimulatedAvatarCount, avatarManager->getPartiallySimulatedAvatars());
|
||||
STAT_UPDATE(serverCount, (int)nodeList->size());
|
||||
STAT_UPDATE(framerate, qApp->getFps());
|
||||
if (qApp->getActiveDisplayPlugin()) {
|
||||
|
@ -306,6 +308,8 @@ void Stats::updateStats(bool force) {
|
|||
// Update Frame timing (in ms)
|
||||
STAT_UPDATE(gpuFrameTime, (float)gpuContext->getFrameTimerGPUAverage());
|
||||
STAT_UPDATE(batchFrameTime, (float)gpuContext->getFrameTimerBatchAverage());
|
||||
STAT_UPDATE(avatarSimulationTime, (float)avatarManager->getAvatarSimulationTime());
|
||||
|
||||
|
||||
STAT_UPDATE(gpuBuffers, (int)gpu::Context::getBufferGPUCount());
|
||||
STAT_UPDATE(gpuBufferMemory, (int)BYTES_TO_MB(gpu::Context::getBufferGPUMemoryUsage()));
|
||||
|
|
|
@ -49,6 +49,8 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, simrate, 0)
|
||||
STATS_PROPERTY(int, avatarSimrate, 0)
|
||||
STATS_PROPERTY(int, avatarCount, 0)
|
||||
STATS_PROPERTY(int, fullySimulatedAvatarCount, 0)
|
||||
STATS_PROPERTY(int, partiallySimulatedAvatarCount, 0)
|
||||
STATS_PROPERTY(int, packetInCount, 0)
|
||||
STATS_PROPERTY(int, packetOutCount, 0)
|
||||
STATS_PROPERTY(float, mbpsIn, 0)
|
||||
|
@ -111,6 +113,7 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, gpuFreeMemory, 0)
|
||||
STATS_PROPERTY(float, gpuFrameTime, 0)
|
||||
STATS_PROPERTY(float, batchFrameTime, 0)
|
||||
STATS_PROPERTY(float, avatarSimulationTime, 0)
|
||||
|
||||
public:
|
||||
static Stats* getInstance();
|
||||
|
@ -156,6 +159,8 @@ signals:
|
|||
void simrateChanged();
|
||||
void avatarSimrateChanged();
|
||||
void avatarCountChanged();
|
||||
void fullySimulatedAvatarCountChanged();
|
||||
void partiallySimulatedAvatarCountChanged();
|
||||
void packetInCountChanged();
|
||||
void packetOutCountChanged();
|
||||
void mbpsInChanged();
|
||||
|
@ -216,6 +221,7 @@ signals:
|
|||
void gpuFreeMemoryChanged();
|
||||
void gpuFrameTimeChanged();
|
||||
void batchFrameTimeChanged();
|
||||
void avatarSimulationTimeChanged();
|
||||
void rectifiedTextureCountChanged();
|
||||
void decimatedTextureCountChanged();
|
||||
|
||||
|
|
|
@ -23,14 +23,20 @@
|
|||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <GeometryUtil.h>
|
||||
#include <scripting/HMDScriptingInterface.h>
|
||||
#include <gl/OffscreenQmlSurface.h>
|
||||
#include <PathUtils.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <TabletScriptingInterface.h>
|
||||
#include <TextureCache.h>
|
||||
#include <UsersScriptingInterface.h>
|
||||
#include <UserActivityLoggerScriptingInterface.h>
|
||||
#include <AbstractViewStateInterface.h>
|
||||
#include <gl/OffscreenQmlSurface.h>
|
||||
#include <gl/OffscreenQmlSurfaceCache.h>
|
||||
#include <AddressManager.h>
|
||||
#include "scripting/AccountScriptingInterface.h"
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
|
||||
static const float DPI = 30.47f;
|
||||
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
|
||||
|
@ -149,10 +155,17 @@ void Web3DOverlay::loadSourceURL() {
|
|||
_webSurface->load(_url, [&](QQmlContext* context, QObject* obj) {});
|
||||
_webSurface->resume();
|
||||
|
||||
_webSurface->getRootContext()->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
_webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
_webSurface->getRootContext()->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
|
||||
|
||||
if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto flags = tabletScriptingInterface->getFlags();
|
||||
_webSurface->getRootContext()->setContextProperty("offscreenFlags", flags);
|
||||
_webSurface->getRootContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
|
||||
_webSurface->getRootContext()->setContextProperty("Account", AccountScriptingInterface::getInstance());
|
||||
_webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data());
|
||||
|
||||
// Override min fps for tablet UI, for silky smooth scrolling
|
||||
|
|
|
@ -283,14 +283,20 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
destinationBuffer += sizeof(packetStateFlags);
|
||||
|
||||
if (hasAvatarGlobalPosition) {
|
||||
auto startSection = destinationBuffer;
|
||||
auto data = reinterpret_cast<AvatarDataPacket::AvatarGlobalPosition*>(destinationBuffer);
|
||||
data->globalPosition[0] = _globalPosition.x;
|
||||
data->globalPosition[1] = _globalPosition.y;
|
||||
data->globalPosition[2] = _globalPosition.z;
|
||||
destinationBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
|
||||
_globalPositionRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
if (hasAvatarBoundingBox) {
|
||||
auto startSection = destinationBuffer;
|
||||
auto data = reinterpret_cast<AvatarDataPacket::AvatarBoundingBox*>(destinationBuffer);
|
||||
|
||||
data->avatarDimensions[0] = _globalBoundingBoxDimensions.x;
|
||||
|
@ -302,36 +308,56 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
data->boundOriginOffset[2] = _globalBoundingBoxOffset.z;
|
||||
|
||||
destinationBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
_avatarBoundingBoxRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
if (hasAvatarOrientation) {
|
||||
auto startSection = destinationBuffer;
|
||||
auto localOrientation = getLocalOrientation();
|
||||
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, localOrientation);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
_avatarOrientationRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
if (hasAvatarScale) {
|
||||
auto startSection = destinationBuffer;
|
||||
auto data = reinterpret_cast<AvatarDataPacket::AvatarScale*>(destinationBuffer);
|
||||
auto scale = getDomainLimitedScale();
|
||||
packFloatRatioToTwoByte((uint8_t*)(&data->scale), scale);
|
||||
destinationBuffer += sizeof(AvatarDataPacket::AvatarScale);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
_avatarScaleRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
if (hasLookAtPosition) {
|
||||
auto startSection = destinationBuffer;
|
||||
auto data = reinterpret_cast<AvatarDataPacket::LookAtPosition*>(destinationBuffer);
|
||||
auto lookAt = _headData->getLookAtPosition();
|
||||
data->lookAtPosition[0] = lookAt.x;
|
||||
data->lookAtPosition[1] = lookAt.y;
|
||||
data->lookAtPosition[2] = lookAt.z;
|
||||
destinationBuffer += sizeof(AvatarDataPacket::LookAtPosition);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
_lookAtPositionRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
if (hasAudioLoudness) {
|
||||
auto startSection = destinationBuffer;
|
||||
auto data = reinterpret_cast<AvatarDataPacket::AudioLoudness*>(destinationBuffer);
|
||||
data->audioLoudness = packFloatGainToByte(_headData->getAudioLoudness() / AUDIO_LOUDNESS_SCALE);
|
||||
destinationBuffer += sizeof(AvatarDataPacket::AudioLoudness);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
_audioLoudnessRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
if (hasSensorToWorldMatrix) {
|
||||
auto startSection = destinationBuffer;
|
||||
auto data = reinterpret_cast<AvatarDataPacket::SensorToWorldMatrix*>(destinationBuffer);
|
||||
glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
|
||||
packOrientationQuatToSixBytes(data->sensorToWorldQuat, glmExtractRotation(sensorToWorldMatrix));
|
||||
|
@ -341,9 +367,13 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
data->sensorToWorldTrans[1] = sensorToWorldMatrix[3][1];
|
||||
data->sensorToWorldTrans[2] = sensorToWorldMatrix[3][2];
|
||||
destinationBuffer += sizeof(AvatarDataPacket::SensorToWorldMatrix);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
_sensorToWorldRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
if (hasAdditionalFlags) {
|
||||
auto startSection = destinationBuffer;
|
||||
auto data = reinterpret_cast<AvatarDataPacket::AdditionalFlags*>(destinationBuffer);
|
||||
|
||||
uint8_t flags { 0 };
|
||||
|
@ -370,27 +400,39 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
}
|
||||
data->flags = flags;
|
||||
destinationBuffer += sizeof(AvatarDataPacket::AdditionalFlags);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
_additionalFlagsRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
if (hasAvatarLocalPosition) {
|
||||
auto startSection = destinationBuffer;
|
||||
auto data = reinterpret_cast<AvatarDataPacket::AvatarLocalPosition*>(destinationBuffer);
|
||||
auto localPosition = getLocalPosition();
|
||||
data->localPosition[0] = localPosition.x;
|
||||
data->localPosition[1] = localPosition.y;
|
||||
data->localPosition[2] = localPosition.z;
|
||||
destinationBuffer += sizeof(AvatarDataPacket::AvatarLocalPosition);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
_localPositionRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
if (hasParentInfo) {
|
||||
auto startSection = destinationBuffer;
|
||||
auto parentInfo = reinterpret_cast<AvatarDataPacket::ParentInfo*>(destinationBuffer);
|
||||
QByteArray referentialAsBytes = parentID.toRfc4122();
|
||||
memcpy(parentInfo->parentUUID, referentialAsBytes.data(), referentialAsBytes.size());
|
||||
parentInfo->parentJointIndex = _parentJointIndex;
|
||||
destinationBuffer += sizeof(AvatarDataPacket::ParentInfo);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
_parentInfoRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
// If it is connected, pack up the data
|
||||
if (hasFaceTrackerInfo) {
|
||||
auto startSection = destinationBuffer;
|
||||
auto faceTrackerInfo = reinterpret_cast<AvatarDataPacket::FaceTrackerInfo*>(destinationBuffer);
|
||||
|
||||
faceTrackerInfo->leftEyeBlink = _headData->_leftEyeBlink;
|
||||
|
@ -403,10 +445,14 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
// followed by a variable number of float coefficients
|
||||
memcpy(destinationBuffer, _headData->_blendshapeCoefficients.data(), _headData->_blendshapeCoefficients.size() * sizeof(float));
|
||||
destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
_faceTrackerRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
// If it is connected, pack up the data
|
||||
if (hasJointData) {
|
||||
auto startSection = destinationBuffer;
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
|
||||
// joint rotation data
|
||||
|
@ -554,6 +600,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
<< (destinationBuffer - startPosition);
|
||||
}
|
||||
#endif
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
_jointDataRateOutbound.increment(numBytes);
|
||||
}
|
||||
|
||||
int avatarDataSize = destinationBuffer - startPosition;
|
||||
|
@ -670,6 +719,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition);
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_globalPositionRate.increment(numBytesRead);
|
||||
_globalPositionUpdateRate.increment();
|
||||
|
||||
// if we don't have a parent, make sure to also set our local position
|
||||
if (!hasParent()) {
|
||||
|
@ -698,6 +748,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
sourceBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox);
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_avatarBoundingBoxRate.increment(numBytesRead);
|
||||
_avatarBoundingBoxUpdateRate.increment();
|
||||
}
|
||||
|
||||
if (hasAvatarOrientation) {
|
||||
|
@ -713,6 +764,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
}
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_avatarOrientationRate.increment(numBytesRead);
|
||||
_avatarOrientationUpdateRate.increment();
|
||||
}
|
||||
|
||||
if (hasAvatarScale) {
|
||||
|
@ -732,6 +784,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
sourceBuffer += sizeof(AvatarDataPacket::AvatarScale);
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_avatarScaleRate.increment(numBytesRead);
|
||||
_avatarScaleUpdateRate.increment();
|
||||
}
|
||||
|
||||
if (hasLookAtPosition) {
|
||||
|
@ -750,6 +803,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
sourceBuffer += sizeof(AvatarDataPacket::LookAtPosition);
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_lookAtPositionRate.increment(numBytesRead);
|
||||
_lookAtPositionUpdateRate.increment();
|
||||
}
|
||||
|
||||
if (hasAudioLoudness) {
|
||||
|
@ -770,6 +824,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
_headData->setAudioLoudness(audioLoudness);
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_audioLoudnessRate.increment(numBytesRead);
|
||||
_audioLoudnessUpdateRate.increment();
|
||||
}
|
||||
|
||||
if (hasSensorToWorldMatrix) {
|
||||
|
@ -790,6 +845,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
sourceBuffer += sizeof(AvatarDataPacket::SensorToWorldMatrix);
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_sensorToWorldRate.increment(numBytesRead);
|
||||
_sensorToWorldUpdateRate.increment();
|
||||
}
|
||||
|
||||
if (hasAdditionalFlags) {
|
||||
|
@ -833,6 +889,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
}
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_additionalFlagsRate.increment(numBytesRead);
|
||||
_additionalFlagsUpdateRate.increment();
|
||||
}
|
||||
|
||||
// FIXME -- make sure to handle the existance of a parent vs a change in the parent...
|
||||
|
@ -855,7 +912,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_parentInfoRate.increment(numBytesRead);
|
||||
} else {
|
||||
_parentInfoUpdateRate.increment();
|
||||
}
|
||||
else {
|
||||
// FIXME - this aint totally right, for switching to parent/no-parent
|
||||
_parentID = QUuid();
|
||||
}
|
||||
|
@ -877,6 +936,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
sourceBuffer += sizeof(AvatarDataPacket::AvatarLocalPosition);
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_localPositionRate.increment(numBytesRead);
|
||||
_localPositionUpdateRate.increment();
|
||||
}
|
||||
|
||||
if (hasFaceTrackerInfo) {
|
||||
|
@ -899,6 +959,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
sourceBuffer += coefficientsSize;
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_faceTrackerRate.increment(numBytesRead);
|
||||
_faceTrackerUpdateRate.increment();
|
||||
}
|
||||
|
||||
if (hasJointData) {
|
||||
|
@ -991,17 +1052,19 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_jointDataRate.increment(numBytesRead);
|
||||
_jointDataUpdateRate.increment();
|
||||
}
|
||||
|
||||
int numBytesRead = sourceBuffer - startPosition;
|
||||
_averageBytesReceived.updateAverage(numBytesRead);
|
||||
|
||||
_parseBufferRate.increment(numBytesRead);
|
||||
_parseBufferUpdateRate.increment();
|
||||
|
||||
return numBytesRead;
|
||||
}
|
||||
|
||||
float AvatarData::getDataRate(const QString& rateName) {
|
||||
float AvatarData::getDataRate(const QString& rateName) const {
|
||||
if (rateName == "") {
|
||||
return _parseBufferRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "globalPosition") {
|
||||
|
@ -1028,10 +1091,64 @@ float AvatarData::getDataRate(const QString& rateName) {
|
|||
return _faceTrackerRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "jointData") {
|
||||
return _jointDataRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "globalPositionOutbound") {
|
||||
return _globalPositionRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "localPositionOutbound") {
|
||||
return _localPositionRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "avatarBoundingBoxOutbound") {
|
||||
return _avatarBoundingBoxRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "avatarOrientationOutbound") {
|
||||
return _avatarOrientationRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "avatarScaleOutbound") {
|
||||
return _avatarScaleRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "lookAtPositionOutbound") {
|
||||
return _lookAtPositionRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "audioLoudnessOutbound") {
|
||||
return _audioLoudnessRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "sensorToWorkMatrixOutbound") {
|
||||
return _sensorToWorldRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "additionalFlagsOutbound") {
|
||||
return _additionalFlagsRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "parentInfoOutbound") {
|
||||
return _parentInfoRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "faceTrackerOutbound") {
|
||||
return _faceTrackerRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "jointDataOutbound") {
|
||||
return _jointDataRateOutbound.rate() / BYTES_PER_KILOBIT;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float AvatarData::getUpdateRate(const QString& rateName) const {
|
||||
if (rateName == "") {
|
||||
return _parseBufferUpdateRate.rate();
|
||||
} else if (rateName == "globalPosition") {
|
||||
return _globalPositionUpdateRate.rate();
|
||||
} else if (rateName == "localPosition") {
|
||||
return _localPositionUpdateRate.rate();
|
||||
} else if (rateName == "avatarBoundingBox") {
|
||||
return _avatarBoundingBoxUpdateRate.rate();
|
||||
} else if (rateName == "avatarOrientation") {
|
||||
return _avatarOrientationUpdateRate.rate();
|
||||
} else if (rateName == "avatarScale") {
|
||||
return _avatarScaleUpdateRate.rate();
|
||||
} else if (rateName == "lookAtPosition") {
|
||||
return _lookAtPositionUpdateRate.rate();
|
||||
} else if (rateName == "audioLoudness") {
|
||||
return _audioLoudnessUpdateRate.rate();
|
||||
} else if (rateName == "sensorToWorkMatrix") {
|
||||
return _sensorToWorldUpdateRate.rate();
|
||||
} else if (rateName == "additionalFlags") {
|
||||
return _additionalFlagsUpdateRate.rate();
|
||||
} else if (rateName == "parentInfo") {
|
||||
return _parentInfoUpdateRate.rate();
|
||||
} else if (rateName == "faceTracker") {
|
||||
return _faceTrackerUpdateRate.rate();
|
||||
} else if (rateName == "jointData") {
|
||||
return _jointDataUpdateRate.rate();
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
int AvatarData::getAverageBytesReceivedPerSecond() const {
|
||||
return lrint(_averageBytesReceived.getAverageSampleValuePerSecond());
|
||||
|
@ -1842,8 +1959,7 @@ QJsonObject AvatarData::toJson() const {
|
|||
return root;
|
||||
}
|
||||
|
||||
void AvatarData::fromJson(const QJsonObject& json) {
|
||||
|
||||
void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) {
|
||||
int version;
|
||||
if (json.contains(JSON_AVATAR_VERSION)) {
|
||||
version = json[JSON_AVATAR_VERSION].toInt();
|
||||
|
@ -1865,7 +1981,7 @@ void AvatarData::fromJson(const QJsonObject& json) {
|
|||
|
||||
if (json.contains(JSON_AVATAR_BODY_MODEL)) {
|
||||
auto bodyModelURL = json[JSON_AVATAR_BODY_MODEL].toString();
|
||||
if (bodyModelURL != getSkeletonModelURL().toString()) {
|
||||
if (useFrameSkeleton && bodyModelURL != getSkeletonModelURL().toString()) {
|
||||
setSkeletonModelURL(bodyModelURL);
|
||||
}
|
||||
}
|
||||
|
@ -1958,8 +2074,9 @@ QByteArray AvatarData::toFrame(const AvatarData& avatar) {
|
|||
}
|
||||
|
||||
|
||||
void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) {
|
||||
void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result, bool useFrameSkeleton) {
|
||||
QJsonDocument doc = QJsonDocument::fromBinaryData(frameData);
|
||||
|
||||
#ifdef WANT_JSON_DEBUG
|
||||
{
|
||||
QJsonObject obj = doc.object();
|
||||
|
@ -1967,7 +2084,7 @@ void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) {
|
|||
qCDebug(avatars).noquote() << QJsonDocument(obj).toJson(QJsonDocument::JsonFormat::Indented);
|
||||
}
|
||||
#endif
|
||||
result.fromJson(doc.object());
|
||||
result.fromJson(doc.object(), useFrameSkeleton);
|
||||
}
|
||||
|
||||
float AvatarData::getBodyYaw() const {
|
||||
|
|
|
@ -329,7 +329,7 @@ public:
|
|||
|
||||
static const QString FRAME_NAME;
|
||||
|
||||
static void fromFrame(const QByteArray& frameData, AvatarData& avatar);
|
||||
static void fromFrame(const QByteArray& frameData, AvatarData& avatar, bool useFrameSkeleton = true);
|
||||
static QByteArray toFrame(const AvatarData& avatar);
|
||||
|
||||
AvatarData();
|
||||
|
@ -380,8 +380,27 @@ public:
|
|||
void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time.
|
||||
virtual void updateAttitude() {} // Tell skeleton mesh about changes
|
||||
|
||||
glm::quat getHeadOrientation() const { return _headData->getOrientation(); }
|
||||
void setHeadOrientation(const glm::quat& orientation) { _headData->setOrientation(orientation); }
|
||||
glm::quat getHeadOrientation() {
|
||||
lazyInitHeadData();
|
||||
return _headData->getOrientation();
|
||||
}
|
||||
void setHeadOrientation(const glm::quat& orientation) {
|
||||
if (_headData) {
|
||||
_headData->setOrientation(orientation);
|
||||
}
|
||||
}
|
||||
|
||||
void setLookAtPosition(const glm::vec3& lookAtPosition) {
|
||||
if (_headData) {
|
||||
_headData->setLookAtPosition(lookAtPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void setBlendshapeCoefficients(const QVector<float>& blendshapeCoefficients) {
|
||||
if (_headData) {
|
||||
_headData->setBlendshapeCoefficients(blendshapeCoefficients);
|
||||
}
|
||||
}
|
||||
|
||||
// access to Head().set/getMousePitch (degrees)
|
||||
float getHeadPitch() const { return _headData->getBasePitch(); }
|
||||
|
@ -513,7 +532,7 @@ public:
|
|||
TransformPointer getRecordingBasis() const;
|
||||
void setRecordingBasis(TransformPointer recordingBasis = TransformPointer());
|
||||
QJsonObject toJson() const;
|
||||
void fromJson(const QJsonObject& json);
|
||||
void fromJson(const QJsonObject& json, bool useFrameSkeleton = true);
|
||||
|
||||
glm::vec3 getClientGlobalPosition() { return _globalPosition; }
|
||||
glm::vec3 getGlobalBoundingBoxCorner() { return _globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions; }
|
||||
|
@ -528,7 +547,8 @@ public:
|
|||
Q_INVOKABLE glm::mat4 getControllerLeftHandMatrix() const;
|
||||
Q_INVOKABLE glm::mat4 getControllerRightHandMatrix() const;
|
||||
|
||||
float getDataRate(const QString& rateName = QString(""));
|
||||
Q_INVOKABLE float getDataRate(const QString& rateName = QString("")) const;
|
||||
Q_INVOKABLE float getUpdateRate(const QString& rateName = QString("")) const;
|
||||
|
||||
int getJointCount() { return _jointData.size(); }
|
||||
|
||||
|
@ -596,7 +616,7 @@ protected:
|
|||
bool _forceFaceTrackerConnected;
|
||||
bool _hasNewJointData; // set in AvatarData, cleared in Avatar
|
||||
|
||||
HeadData* _headData;
|
||||
HeadData* _headData { nullptr };
|
||||
|
||||
QUrl _skeletonModelURL;
|
||||
bool _firstSkeletonCheck { true };
|
||||
|
@ -644,7 +664,7 @@ protected:
|
|||
|
||||
quint64 _lastToByteArray { 0 }; // tracks the last time we did a toByteArray
|
||||
|
||||
// Some rate data for incoming data
|
||||
// Some rate data for incoming data in bytes
|
||||
RateCounter<> _parseBufferRate;
|
||||
RateCounter<> _globalPositionRate;
|
||||
RateCounter<> _localPositionRate;
|
||||
|
@ -659,6 +679,35 @@ protected:
|
|||
RateCounter<> _faceTrackerRate;
|
||||
RateCounter<> _jointDataRate;
|
||||
|
||||
// Some rate data for incoming data updates
|
||||
RateCounter<> _parseBufferUpdateRate;
|
||||
RateCounter<> _globalPositionUpdateRate;
|
||||
RateCounter<> _localPositionUpdateRate;
|
||||
RateCounter<> _avatarBoundingBoxUpdateRate;
|
||||
RateCounter<> _avatarOrientationUpdateRate;
|
||||
RateCounter<> _avatarScaleUpdateRate;
|
||||
RateCounter<> _lookAtPositionUpdateRate;
|
||||
RateCounter<> _audioLoudnessUpdateRate;
|
||||
RateCounter<> _sensorToWorldUpdateRate;
|
||||
RateCounter<> _additionalFlagsUpdateRate;
|
||||
RateCounter<> _parentInfoUpdateRate;
|
||||
RateCounter<> _faceTrackerUpdateRate;
|
||||
RateCounter<> _jointDataUpdateRate;
|
||||
|
||||
// Some rate data for outgoing data
|
||||
RateCounter<> _globalPositionRateOutbound;
|
||||
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 _globalBoundingBoxOffset;
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID,
|
|||
return avatar;
|
||||
}
|
||||
|
||||
AvatarSharedPointer AvatarHashMap::findAvatar(const QUuid& sessionUUID) {
|
||||
AvatarSharedPointer AvatarHashMap::findAvatar(const QUuid& sessionUUID) const {
|
||||
QReadLocker locker(&_hashLock);
|
||||
if (_avatarHash.contains(sessionUUID)) {
|
||||
return _avatarHash.value(sessionUUID);
|
||||
|
|
|
@ -40,7 +40,7 @@ public:
|
|||
Q_INVOKABLE QVector<QUuid> getAvatarIdentifiers();
|
||||
Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID);
|
||||
|
||||
virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) { return findAvatar(sessionID); }
|
||||
virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); }
|
||||
int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters);
|
||||
|
||||
signals:
|
||||
|
@ -65,7 +65,7 @@ protected:
|
|||
virtual AvatarSharedPointer newSharedAvatar();
|
||||
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
AvatarSharedPointer newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
virtual AvatarSharedPointer findAvatar(const QUuid& sessionUUID); // uses a QReadLocker on the hashLock
|
||||
virtual AvatarSharedPointer findAvatar(const QUuid& sessionUUID) const; // uses a QReadLocker on the hashLock
|
||||
virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason);
|
||||
|
||||
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason);
|
||||
|
@ -73,7 +73,7 @@ protected:
|
|||
AvatarHash _avatarHash;
|
||||
// "Case-based safety": Most access to the _avatarHash is on the same thread. Write access is protected by a write-lock.
|
||||
// If you read from a different thread, you must read-lock the _hashLock. (Scripted write access is not supported).
|
||||
QReadWriteLock _hashLock;
|
||||
mutable QReadWriteLock _hashLock;
|
||||
|
||||
private:
|
||||
QUuid _lastOwnerSessionUUID;
|
||||
|
|
|
@ -118,6 +118,8 @@ public:
|
|||
|
||||
|
||||
virtual void run() override {
|
||||
PROFILE_SET_THREAD_NAME("Present Thread");
|
||||
|
||||
// FIXME determine the best priority balance between this and the main thread...
|
||||
// It may be dependent on the display plugin being used, since VR plugins should
|
||||
// have higher priority on rendering (although we could say that the Oculus plugin
|
||||
|
|
85
libraries/entities/src/EntityScriptServerLogClient.cpp
Normal file
85
libraries/entities/src/EntityScriptServerLogClient.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// EntityScriptServerLogClient.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Created by Clement Brisset on 2/1/17.
|
||||
// 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 "EntityScriptServerLogClient.h"
|
||||
|
||||
EntityScriptServerLogClient::EntityScriptServerLogClient() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket");
|
||||
|
||||
QObject::connect(nodeList.data(), &NodeList::nodeActivated, this, &EntityScriptServerLogClient::nodeActivated);
|
||||
QObject::connect(nodeList.data(), &NodeList::nodeKilled, this, &EntityScriptServerLogClient::nodeKilled);
|
||||
|
||||
QObject::connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptServerLogClient::canRezChanged);
|
||||
}
|
||||
|
||||
void EntityScriptServerLogClient::connectNotify(const QMetaMethod& signal) {
|
||||
// This needs to be delayed or "receivers()" will return completely inconsistent values
|
||||
QMetaObject::invokeMethod(this, "connectionsChanged", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void EntityScriptServerLogClient::disconnectNotify(const QMetaMethod& signal) {
|
||||
// This needs to be delayed or "receivers()" will return completely inconsistent values
|
||||
QMetaObject::invokeMethod(this, "connectionsChanged", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void EntityScriptServerLogClient::connectionsChanged() {
|
||||
auto numReceivers = receivers(SIGNAL(receivedNewLogLines(QString)));
|
||||
if (!_subscribed && numReceivers > 0) {
|
||||
enableToEntityServerScriptLog(DependencyManager::get<NodeList>()->getThisNodeCanRez());
|
||||
} else if (_subscribed && numReceivers == 0) {
|
||||
enableToEntityServerScriptLog(false);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServerLogClient::enableToEntityServerScriptLog(bool enable) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
if (auto node = nodeList->soloNodeOfType(NodeType::EntityScriptServer)) {
|
||||
auto packet = NLPacket::create(PacketType::EntityServerScriptLog, sizeof(bool), true);
|
||||
packet->writePrimitive(enable);
|
||||
nodeList->sendPacket(std::move(packet), *node);
|
||||
|
||||
if (_subscribed != enable) {
|
||||
if (enable) {
|
||||
emit receivedNewLogLines("====================== Subscribded to the Entity Script Server's log ======================");
|
||||
} else {
|
||||
emit receivedNewLogLines("==================== Unsubscribded from the Entity Script Server's log ====================");
|
||||
}
|
||||
}
|
||||
_subscribed = enable;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServerLogClient::handleEntityServerScriptLogPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
emit receivedNewLogLines(QString::fromUtf8(message->readAll()));
|
||||
}
|
||||
|
||||
void EntityScriptServerLogClient::nodeActivated(SharedNodePointer activatedNode) {
|
||||
if (_subscribed && activatedNode->getType() == NodeType::EntityScriptServer) {
|
||||
_subscribed = false;
|
||||
enableToEntityServerScriptLog(DependencyManager::get<NodeList>()->getThisNodeCanRez());
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServerLogClient::nodeKilled(SharedNodePointer killedNode) {
|
||||
if (killedNode->getType() == NodeType::EntityScriptServer) {
|
||||
emit receivedNewLogLines("====================== Connection to the Entity Script Server lost ======================");
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServerLogClient::canRezChanged(bool canRez) {
|
||||
auto numReceivers = receivers(SIGNAL(receivedNewLogLines(QString)));
|
||||
if (numReceivers > 0) {
|
||||
enableToEntityServerScriptLog(canRez);
|
||||
}
|
||||
}
|
46
libraries/entities/src/EntityScriptServerLogClient.h
Normal file
46
libraries/entities/src/EntityScriptServerLogClient.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// EntityScriptServerLogClient.h
|
||||
// interface/src
|
||||
//
|
||||
// Created by Clement Brisset on 2/1/17.
|
||||
// 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_EntityScriptServerLogClient_h
|
||||
#define hifi_EntityScriptServerLogClient_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <NodeList.h>
|
||||
|
||||
class EntityScriptServerLogClient : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EntityScriptServerLogClient();
|
||||
|
||||
signals:
|
||||
void receivedNewLogLines(QString logLines);
|
||||
|
||||
protected:
|
||||
void connectNotify(const QMetaMethod& signal) override;
|
||||
void disconnectNotify(const QMetaMethod& signal) override;
|
||||
|
||||
private slots:
|
||||
void enableToEntityServerScriptLog(bool enable);
|
||||
void handleEntityServerScriptLogPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
||||
void nodeActivated(SharedNodePointer activatedNode);
|
||||
void nodeKilled(SharedNodePointer killedNode);
|
||||
void canRezChanged(bool canRez);
|
||||
|
||||
void connectionsChanged();
|
||||
|
||||
private:
|
||||
bool _subscribed { false };
|
||||
};
|
||||
|
||||
#endif // hifi_EntityScriptServerLogClient_h
|
|
@ -27,6 +27,7 @@
|
|||
#include "ZoneEntityItem.h"
|
||||
#include "WebEntityItem.h"
|
||||
#include <EntityScriptClient.h>
|
||||
#include <Profile.h>
|
||||
|
||||
|
||||
EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) :
|
||||
|
@ -178,6 +179,8 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti
|
|||
|
||||
|
||||
QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
_activityTracking.addedEntityCount++;
|
||||
|
||||
EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties);
|
||||
|
@ -259,6 +262,8 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
|
|||
}
|
||||
|
||||
EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
EntityItemProperties results;
|
||||
if (_entityTree) {
|
||||
_entityTree->withReadLock([&] {
|
||||
|
@ -305,6 +310,8 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
|
|||
}
|
||||
|
||||
QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
_activityTracking.editedEntityCount++;
|
||||
|
||||
EntityItemProperties properties = scriptSideProperties;
|
||||
|
@ -456,6 +463,8 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
}
|
||||
|
||||
void EntityScriptingInterface::deleteEntity(QUuid id) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
_activityTracking.deletedEntityCount++;
|
||||
|
||||
EntityItemID entityID(id);
|
||||
|
@ -511,6 +520,8 @@ void EntityScriptingInterface::setEntitiesScriptEngine(EntitiesScriptEngineProvi
|
|||
}
|
||||
|
||||
void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, const QStringList& params) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
||||
if (_entitiesScriptEngine) {
|
||||
EntityItemID entityID{ id };
|
||||
|
@ -519,6 +530,8 @@ void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method,
|
|||
}
|
||||
|
||||
QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
EntityItemID result;
|
||||
if (_entityTree) {
|
||||
EntityItemPointer closestEntity;
|
||||
|
@ -542,6 +555,8 @@ void EntityScriptingInterface::dumpTree() const {
|
|||
}
|
||||
|
||||
QVector<QUuid> EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
QVector<QUuid> result;
|
||||
if (_entityTree) {
|
||||
QVector<EntityItemPointer> entities;
|
||||
|
@ -557,6 +572,8 @@ QVector<QUuid> EntityScriptingInterface::findEntities(const glm::vec3& center, f
|
|||
}
|
||||
|
||||
QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
QVector<QUuid> result;
|
||||
if (_entityTree) {
|
||||
QVector<EntityItemPointer> entities;
|
||||
|
@ -573,6 +590,8 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn
|
|||
}
|
||||
|
||||
QVector<QUuid> EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frustum) const {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
QVector<QUuid> result;
|
||||
|
||||
const QString POSITION_PROPERTY = "position";
|
||||
|
@ -616,6 +635,7 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frust
|
|||
|
||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking,
|
||||
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
QVector<EntityItemID> entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
|
||||
QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
|
||||
|
@ -723,6 +743,8 @@ RayToEntityIntersectionResult::RayToEntityIntersectionResult() :
|
|||
}
|
||||
|
||||
QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("intersects", value.intersects);
|
||||
obj.setProperty("accurate", value.accurate);
|
||||
|
@ -770,6 +792,8 @@ QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, c
|
|||
}
|
||||
|
||||
void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
value.intersects = object.property("intersects").toVariant().toBool();
|
||||
value.accurate = object.property("accurate").toVariant().toBool();
|
||||
QScriptValue entityIDValue = object.property("entityID");
|
||||
|
@ -807,6 +831,8 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
|
||||
bool EntityScriptingInterface::setVoxels(QUuid entityID,
|
||||
std::function<bool(PolyVoxEntityItem&)> actor) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
if (!_entityTree) {
|
||||
return false;
|
||||
}
|
||||
|
@ -831,6 +857,8 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID,
|
|||
}
|
||||
|
||||
bool EntityScriptingInterface::setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
if (!_entityTree) {
|
||||
return false;
|
||||
}
|
||||
|
@ -870,18 +898,24 @@ bool EntityScriptingInterface::setPoints(QUuid entityID, std::function<bool(Line
|
|||
|
||||
|
||||
bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return setVoxels(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setSphere(center, radius, value);
|
||||
});
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& position, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return setVoxels(entityID, [position, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setVoxelInVolume(position, value);
|
||||
});
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return setVoxels(entityID, [value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setAll(value);
|
||||
});
|
||||
|
@ -889,12 +923,16 @@ bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) {
|
|||
|
||||
bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition,
|
||||
const glm::vec3& cuboidSize, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return setVoxels(entityID, [lowPosition, cuboidSize, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setCuboid(lowPosition, cuboidSize, value);
|
||||
});
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector<glm::vec3>& points) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
EntityItemPointer entity = static_cast<EntityItemPointer>(_entityTree->findEntityByEntityItemID(entityID));
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID;
|
||||
|
@ -913,6 +951,8 @@ bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector<glm::v
|
|||
}
|
||||
|
||||
bool EntityScriptingInterface::appendPoint(QUuid entityID, const glm::vec3& point) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
EntityItemPointer entity = static_cast<EntityItemPointer>(_entityTree->findEntityByEntityItemID(entityID));
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID;
|
||||
|
@ -988,6 +1028,8 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
|
|||
QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
||||
const QUuid& entityID,
|
||||
const QVariantMap& arguments) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
QUuid actionID = QUuid::createUuid();
|
||||
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
|
||||
bool success = false;
|
||||
|
@ -1019,6 +1061,8 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
|
||||
|
||||
bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
|
||||
bool success = entity->updateAction(simulation, actionID, arguments);
|
||||
if (success) {
|
||||
|
@ -1029,6 +1073,8 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid&
|
|||
}
|
||||
|
||||
bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& actionID) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
bool success = false;
|
||||
actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
|
||||
success = entity->removeAction(simulation, actionID);
|
||||
|
@ -1042,6 +1088,8 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid&
|
|||
}
|
||||
|
||||
QVector<QUuid> EntityScriptingInterface::getActionIDs(const QUuid& entityID) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
QVector<QUuid> result;
|
||||
actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
|
||||
QList<QUuid> actionIDs = entity->getActionIDs();
|
||||
|
@ -1052,6 +1100,8 @@ QVector<QUuid> EntityScriptingInterface::getActionIDs(const QUuid& entityID) {
|
|||
}
|
||||
|
||||
QVariantMap EntityScriptingInterface::getActionArguments(const QUuid& entityID, const QUuid& actionID) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
QVariantMap result;
|
||||
actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
|
||||
result = entity->getActionArguments(actionID);
|
||||
|
|
|
@ -537,6 +537,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
FBXGeometry* geometryPtr = new FBXGeometry;
|
||||
FBXGeometry& geometry = *geometryPtr;
|
||||
|
||||
geometry.originalURL = url;
|
||||
|
||||
float unitScaleFactor = 1.0f;
|
||||
glm::vec3 ambientColor;
|
||||
QString hifiGlobalNodeID;
|
||||
|
|
|
@ -288,6 +288,7 @@ class FBXGeometry {
|
|||
public:
|
||||
using Pointer = std::shared_ptr<FBXGeometry>;
|
||||
|
||||
QString originalURL;
|
||||
QString author;
|
||||
QString applicationName; ///< the name of the application that generated the model
|
||||
|
||||
|
|
|
@ -62,7 +62,10 @@ template<class T> QVariant readBinaryArray(QDataStream& in, int& position) {
|
|||
position += sizeof(T) * arrayLength;
|
||||
in.readRawData(arrayData.data(), arrayData.size());
|
||||
}
|
||||
memcpy(&values[0], arrayData.constData(), arrayData.size());
|
||||
|
||||
if (arrayData.size() > 0) {
|
||||
memcpy(&values[0], arrayData.constData(), arrayData.size());
|
||||
}
|
||||
} else {
|
||||
values.reserve(arrayLength);
|
||||
const unsigned int DEFLATE_ENCODING = 1;
|
||||
|
|
|
@ -604,6 +604,9 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::function<void(QQmlContext*, QOb
|
|||
qFatal("Could not load object as root item");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
connect(newItem, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(QVariant)));
|
||||
|
||||
// The root item is ready. Associate it with the window.
|
||||
_rootItem = newItem;
|
||||
_rootItem->setParentItem(_quickWindow->contentItem());
|
||||
|
@ -952,4 +955,13 @@ void OffscreenQmlSurface::emitWebEvent(const QVariant& message) {
|
|||
}
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::sendToQml(const QVariant& message) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "emitQmlEvent", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
||||
} else if (_rootItem) {
|
||||
// call fromScript method on qml root
|
||||
QMetaObject::invokeMethod(_rootItem, "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
||||
}
|
||||
}
|
||||
|
||||
#include "OffscreenQmlSurface.moc"
|
||||
|
|
|
@ -107,6 +107,11 @@ signals:
|
|||
void scriptEventReceived(const QVariant& message);
|
||||
void webEventReceived(const QVariant& message);
|
||||
|
||||
// qml event bridge
|
||||
public slots:
|
||||
void sendToQml(const QVariant& message);
|
||||
signals:
|
||||
void fromQml(QVariant message);
|
||||
|
||||
protected:
|
||||
bool filterEnabled(QObject* originalDestination, QEvent* event) const;
|
||||
|
|
|
@ -328,12 +328,12 @@ void GLBackend::render(const Batch& batch) {
|
|||
|
||||
|
||||
void GLBackend::syncCache() {
|
||||
PROFILE_RANGE(render_gpu_gl_detail, __FUNCTION__);
|
||||
|
||||
syncTransformStateCache();
|
||||
syncPipelineStateCache();
|
||||
syncInputStateCache();
|
||||
syncOutputStateCache();
|
||||
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
}
|
||||
|
||||
#ifdef GPU_STEREO_DRAWCALL_DOUBLED
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//
|
||||
//
|
||||
// GLBackendPipeline.cpp
|
||||
// libraries/gpu/src/gpu
|
||||
//
|
||||
|
|
|
@ -28,11 +28,8 @@ void GLBackend::resetPipelineState(State::Signature nextSignature) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLBackend::syncPipelineStateCache() {
|
||||
State::Data state;
|
||||
|
||||
// force a few states regardless
|
||||
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
||||
|
||||
// Point size is always on
|
||||
|
@ -43,6 +40,25 @@ void GLBackend::syncPipelineStateCache() {
|
|||
|
||||
// Default line width accross the board
|
||||
glLineWidth(1.0f);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
|
||||
}
|
||||
|
||||
void GLBackend::syncPipelineStateCache() {
|
||||
State::Data state;
|
||||
|
||||
// force a few states regardless
|
||||
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
||||
|
||||
// Point size is always on
|
||||
// FIXME CORE
|
||||
//glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||||
glEnable(GL_PROGRAM_POINT_SIZE_EXT);
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
|
||||
// Default line width accross the board
|
||||
glLineWidth(1.0f);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
|
||||
getCurrentGLState(state);
|
||||
State::Signature signature = State::evalSignature(state);
|
||||
|
|
|
@ -151,6 +151,7 @@ bool GLTextureTransferHelper::process() {
|
|||
#endif
|
||||
return true;
|
||||
}
|
||||
PROFILE_COUNTER_IF_CHANGED(render_gpu_gl, "transferringTextures", int, (int) _transferringTextures.size())
|
||||
|
||||
static auto lastReport = usecTimestampNow();
|
||||
auto now = usecTimestampNow();
|
||||
|
|
|
@ -297,6 +297,7 @@ void GL45Texture::startTransfer() {
|
|||
}
|
||||
|
||||
bool GL45Texture::continueTransfer() {
|
||||
PROFILE_RANGE(render_gpu_gl, "continueTransfer")
|
||||
size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1;
|
||||
for (uint8_t face = 0; face < maxFace; ++face) {
|
||||
for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) {
|
||||
|
@ -306,6 +307,8 @@ bool GL45Texture::continueTransfer() {
|
|||
_sparseInfo.allocatedPages += _sparseInfo.getPageCount(size);
|
||||
}
|
||||
if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) {
|
||||
PROFILE_RANGE_EX(render_gpu_gl, "texSubImage", 0x0000ffff, (size.x * size.y * maxFace / 1024));
|
||||
|
||||
auto mip = _gpuObject.accessStoredMipFace(mipLevel, face);
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat());
|
||||
if (GL_TEXTURE_2D == _target) {
|
||||
|
@ -379,6 +382,8 @@ void GL45Texture::stripToMip(uint16_t newMinMip) {
|
|||
return;
|
||||
}
|
||||
|
||||
PROFILE_RANGE(render_gpu_gl, "GL45Texture::stripToMip");
|
||||
|
||||
auto mipLevels = usedMipLevels();
|
||||
{
|
||||
Lock lock(texturesByMipCountsMutex);
|
||||
|
@ -420,29 +425,35 @@ void GL45Texture::stripToMip(uint16_t newMinMip) {
|
|||
auto newLevels = usedMipLevels();
|
||||
|
||||
// Create and setup the new texture (allocate)
|
||||
glCreateTextures(_target, 1, &const_cast<GLuint&>(_id));
|
||||
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip);
|
||||
Vec3u newDimensions = _gpuObject.evalMipDimensions(_mipOffset);
|
||||
glTextureStorage2D(_id, newLevels, _internalFormat, newDimensions.x, newDimensions.y);
|
||||
{
|
||||
Vec3u newDimensions = _gpuObject.evalMipDimensions(_mipOffset);
|
||||
PROFILE_RANGE_EX(render_gpu_gl, "Re-Allocate", 0xff0000ff, (newDimensions.x * newDimensions.y));
|
||||
|
||||
glCreateTextures(_target, 1, &const_cast<GLuint&>(_id));
|
||||
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip);
|
||||
glTextureStorage2D(_id, newLevels, _internalFormat, newDimensions.x, newDimensions.y);
|
||||
}
|
||||
|
||||
// Copy the contents of the old texture to the new
|
||||
GLuint fbo { 0 };
|
||||
glCreateFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
for (uint16 targetMip = _minMip; targetMip <= _maxMip; ++targetMip) {
|
||||
uint16 sourceMip = targetMip + mipDelta;
|
||||
Vec3u mipDimensions = _gpuObject.evalMipDimensions(targetMip + _mipOffset);
|
||||
for (GLenum target : getFaceTargets(_target)) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip);
|
||||
(void)CHECK_GL_ERROR();
|
||||
glCopyTextureSubImage2D(_id, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y);
|
||||
(void)CHECK_GL_ERROR();
|
||||
{
|
||||
PROFILE_RANGE(render_gpu_gl, "Blit");
|
||||
// Preferred path only available in 4.3
|
||||
for (uint16 targetMip = _minMip; targetMip <= _maxMip; ++targetMip) {
|
||||
uint16 sourceMip = targetMip + mipDelta;
|
||||
Vec3u mipDimensions = _gpuObject.evalMipDimensions(targetMip + _mipOffset);
|
||||
for (GLenum target : getFaceTargets(_target)) {
|
||||
glCopyImageSubData(
|
||||
oldId, target, sourceMip, 0, 0, 0,
|
||||
_id, target, targetMip, 0, 0, 0,
|
||||
mipDimensions.x, mipDimensions.y, 1
|
||||
);
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
glDeleteTextures(1, &oldId);
|
||||
}
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
glDeleteTextures(1, &oldId);
|
||||
}
|
||||
|
||||
// Re-sync the sampler to force access to the new mip level
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <Profile.h>
|
||||
|
||||
#include "ModelLogging.h"
|
||||
|
||||
using namespace model;
|
||||
using namespace gpu;
|
||||
|
||||
|
@ -142,8 +141,9 @@ const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& val
|
|||
validAlpha = (numOpaques != NUM_PIXELS);
|
||||
}
|
||||
|
||||
if (!validAlpha && image.format() != QImage::Format_RGB888) {
|
||||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
// Force all the color images to be rgba32bits
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
return image;
|
||||
|
@ -200,7 +200,7 @@ const QImage& image, bool isLinear, bool doCompress) {
|
|||
}
|
||||
}
|
||||
|
||||
#define CPU_MIPMAPS 0
|
||||
#define CPU_MIPMAPS 1
|
||||
|
||||
void generateMips(gpu::Texture* texture, QImage& image, gpu::Element formatMip) {
|
||||
#if CPU_MIPMAPS
|
||||
|
@ -286,15 +286,16 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src
|
|||
PROFILE_RANGE(resource_parse, "createNormalTextureFromNormalImage");
|
||||
QImage image = processSourceImage(srcImage, false);
|
||||
|
||||
if (image.format() != QImage::Format_RGB888) {
|
||||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
// Make sure the normal map source image is RGBA32
|
||||
if (image.format() != QImage::Format_RGBA8888) {
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
}
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
|
||||
gpu::Element formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
gpu::Element formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
|
||||
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
theTexture->setSource(srcImageName);
|
||||
|
@ -330,7 +331,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm
|
|||
const double pStrength = 2.0;
|
||||
int width = image.width();
|
||||
int height = image.height();
|
||||
QImage result(width, height, QImage::Format_RGB888);
|
||||
// THe end result image for normal map is RGBA32 even though the A is not used
|
||||
QImage result(width, height, QImage::Format_RGBA8888);
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
const int iNextClamped = clampPixelCoordinate(i + 1, width - 1);
|
||||
|
@ -378,8 +380,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm
|
|||
gpu::Texture* theTexture = nullptr;
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
gpu::Element formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
gpu::Element formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
|
||||
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
theTexture->setSource(srcImageName);
|
||||
|
@ -398,8 +400,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma
|
|||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
}
|
||||
} else {
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
if (image.format() != QImage::Format_RGBA8888) {
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,8 +435,8 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s
|
|||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
}
|
||||
} else {
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
if (image.format() != QImage::Format_RGBA8888) {
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,8 +474,8 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag
|
|||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
}
|
||||
} else {
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
if (image.format() != QImage::Format_RGBA8888) {
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -762,8 +764,8 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm
|
|||
gpu::Texture* theTexture = nullptr;
|
||||
if ((srcImage.width() > 0) && (srcImage.height() > 0)) {
|
||||
QImage image = processSourceImage(srcImage, true);
|
||||
if (image.format() != QImage::Format_RGB888) {
|
||||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
gpu::Element formatGPU;
|
||||
|
|
|
@ -162,4 +162,4 @@ void EntityScriptClient::forceFailureOfPendingRequests(SharedNodePointer node) {
|
|||
messageMapIt->second.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,7 +111,8 @@ public:
|
|||
EntityScriptGetStatusReply,
|
||||
ReloadEntityServerScript,
|
||||
EntityPhysics,
|
||||
LAST_PACKET_TYPE = EntityPhysics
|
||||
EntityServerScriptLog,
|
||||
LAST_PACKET_TYPE = EntityServerScriptLog
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1178,7 +1178,7 @@ void Model::updateClusterMatrices() {
|
|||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||
auto jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
||||
#if GLM_ARCH & GLM_ARCH_SSE2
|
||||
#if (GLM_ARCH & GLM_ARCH_SSE2) && !(defined Q_OS_MACOS)
|
||||
glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix;
|
||||
glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out);
|
||||
state.clusterMatrices[j] = out;
|
||||
|
|
|
@ -64,6 +64,8 @@
|
|||
#include "ScriptEngines.h"
|
||||
#include "TabletScriptingInterface.h"
|
||||
|
||||
#include <Profile.h>
|
||||
|
||||
#include "MIDIEvent.h"
|
||||
|
||||
static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3";
|
||||
|
@ -863,6 +865,10 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi
|
|||
}
|
||||
|
||||
void ScriptEngine::run() {
|
||||
auto filenameParts = _fileNameString.split("/");
|
||||
auto name = filenameParts.size() > 0 ? filenameParts[filenameParts.size() - 1] : "unknown";
|
||||
PROFILE_SET_THREAD_NAME("Script: " + name);
|
||||
|
||||
if (DependencyManager::get<ScriptEngines>()->isStopped()) {
|
||||
return; // bail early - avoid setting state in init(), as evaluate() will bail too
|
||||
}
|
||||
|
@ -923,7 +929,10 @@ void ScriptEngine::run() {
|
|||
bool processedEvents = false;
|
||||
while (!_isFinished && clock::now() < sleepUntil) {
|
||||
|
||||
QCoreApplication::processEvents(); // before we sleep again, give events a chance to process
|
||||
{
|
||||
PROFILE_RANGE(script, "processEvents-sleep");
|
||||
QCoreApplication::processEvents(); // before we sleep again, give events a chance to process
|
||||
}
|
||||
processedEvents = true;
|
||||
|
||||
// If after processing events, we're past due, exit asap
|
||||
|
@ -938,6 +947,8 @@ void ScriptEngine::run() {
|
|||
std::this_thread::sleep_until(smallSleepUntil);
|
||||
}
|
||||
|
||||
PROFILE_RANGE(script, "ScriptMainLoop");
|
||||
|
||||
#ifdef SCRIPT_DELAY_DEBUG
|
||||
{
|
||||
auto actuallySleptUntil = clock::now();
|
||||
|
@ -961,6 +972,7 @@ void ScriptEngine::run() {
|
|||
|
||||
// Only call this if we didn't processEvents as part of waiting for next frame
|
||||
if (!processedEvents) {
|
||||
PROFILE_RANGE(script, "processEvents");
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
|
@ -985,7 +997,10 @@ void ScriptEngine::run() {
|
|||
float deltaTime = (float) (now - _lastUpdate) / (float) USECS_PER_SECOND;
|
||||
if (!_isFinished) {
|
||||
auto preUpdate = clock::now();
|
||||
emit update(deltaTime);
|
||||
{
|
||||
PROFILE_RANGE(script, "ScriptUpdate");
|
||||
emit update(deltaTime);
|
||||
}
|
||||
auto postUpdate = clock::now();
|
||||
auto elapsed = (postUpdate - preUpdate);
|
||||
totalUpdates += std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
|
||||
|
@ -1393,6 +1408,16 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin
|
|||
}
|
||||
}
|
||||
|
||||
int ScriptEngine::getNumRunningEntityScripts() const {
|
||||
int sum = 0;
|
||||
for (auto& st : _entityScripts) {
|
||||
if (st.status == RUNNING) {
|
||||
++sum;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const {
|
||||
auto it = _entityScripts.constFind(entityID);
|
||||
if (it == _entityScripts.constEnd()) {
|
||||
|
@ -1456,6 +1481,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
newDetails.status = ERROR_LOADING_SCRIPT;
|
||||
newDetails.errorInfo = "Failed to load script";
|
||||
_entityScripts[entityID] = newDetails;
|
||||
emit entityScriptDetailsUpdated();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1467,6 +1493,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||
newDetails.errorInfo = "Bad syntax";
|
||||
_entityScripts[entityID] = newDetails;
|
||||
emit entityScriptDetailsUpdated();
|
||||
return; // done processing script
|
||||
}
|
||||
|
||||
|
@ -1497,6 +1524,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||
newDetails.errorInfo = exceptionMessage;
|
||||
_entityScripts[entityID] = newDetails;
|
||||
emit entityScriptDetailsUpdated();
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1523,6 +1551,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||
newDetails.errorInfo = "Could not find constructor";
|
||||
_entityScripts[entityID] = newDetails;
|
||||
emit entityScriptDetailsUpdated();
|
||||
|
||||
return; // done processing script
|
||||
}
|
||||
|
@ -1544,6 +1573,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
newDetails.lastModified = lastModified;
|
||||
newDetails.definingSandboxURL = sandboxURL;
|
||||
_entityScripts[entityID] = newDetails;
|
||||
emit entityScriptDetailsUpdated();
|
||||
|
||||
if (isURL) {
|
||||
setParentURL("");
|
||||
|
@ -1575,6 +1605,7 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
|
|||
}
|
||||
_entityScripts.remove(entityID);
|
||||
stopAllTimersForEntityScript(entityID);
|
||||
emit entityScriptDetailsUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1596,6 +1627,7 @@ void ScriptEngine::unloadAllEntityScripts() {
|
|||
}
|
||||
}
|
||||
_entityScripts.clear();
|
||||
emit entityScriptDetailsUpdated();
|
||||
|
||||
#ifdef DEBUG_ENGINE_STATE
|
||||
qCDebug(scriptengine) << "---- CURRENT STATE OF ENGINE: --------------------------";
|
||||
|
|
|
@ -199,6 +199,7 @@ public:
|
|||
void scriptWarningMessage(const QString& message);
|
||||
void scriptInfoMessage(const QString& message);
|
||||
|
||||
int getNumRunningEntityScripts() const;
|
||||
bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const;
|
||||
|
||||
public slots:
|
||||
|
@ -222,6 +223,10 @@ signals:
|
|||
void reloadScript(const QString& scriptName, bool isUserLoaded);
|
||||
void doneRunning();
|
||||
|
||||
// Emitted when an entity script is added or removed, or when the status of an entity
|
||||
// script is updated (goes from RUNNING to ERROR_RUNNING_SCRIPT, for example)
|
||||
void entityScriptDetailsUpdated();
|
||||
|
||||
protected:
|
||||
void init();
|
||||
|
||||
|
|
|
@ -183,6 +183,18 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr
|
|||
_qmlTabletRoot = qmlTabletRoot;
|
||||
if (_qmlTabletRoot && _qmlOffscreenSurface) {
|
||||
QObject::connect(_qmlOffscreenSurface, SIGNAL(webEventReceived(QVariant)), this, SIGNAL(webEventReceived(QVariant)));
|
||||
|
||||
// forward qml surface events to interface js
|
||||
connect(dynamic_cast<OffscreenQmlSurface*>(_qmlOffscreenSurface), &OffscreenQmlSurface::fromQml, [this](QVariant message) {
|
||||
if (message.canConvert<QJSValue>()) {
|
||||
emit fromQml(qvariant_cast<QJSValue>(message).toVariant());
|
||||
} else if (message.canConvert<QString>()) {
|
||||
emit fromQml(message.toString());
|
||||
} else {
|
||||
qWarning() << "fromQml: Unsupported message type " << message;
|
||||
}
|
||||
});
|
||||
|
||||
gotoHomeScreen();
|
||||
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "setUsername", Q_ARG(const QVariant&, QVariant(getUsername())));
|
||||
|
@ -197,6 +209,7 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr
|
|||
} else {
|
||||
removeButtonsFromHomeScreen();
|
||||
_state = State::Uninitialized;
|
||||
emit screenChanged(QVariant("Closed"), QVariant(""));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,10 +221,21 @@ void TabletProxy::gotoMenuScreen() {
|
|||
QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToMenuScreen()), Qt::DirectConnection);
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(VRMENU_SOURCE_URL)));
|
||||
_state = State::Menu;
|
||||
emit screenChanged(QVariant("Menu"), QVariant(VRMENU_SOURCE_URL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TabletProxy::loadQMLSource(const QVariant& path) {
|
||||
if (_qmlTabletRoot) {
|
||||
if (_state != State::QML) {
|
||||
removeButtonsFromHomeScreen();
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, path));
|
||||
_state = State::QML;
|
||||
emit screenChanged(QVariant("QML"), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
void TabletProxy::gotoHomeScreen() {
|
||||
if (_qmlTabletRoot) {
|
||||
if (_state != State::Home) {
|
||||
|
@ -220,6 +244,7 @@ void TabletProxy::gotoHomeScreen() {
|
|||
QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(TABLET_SOURCE_URL)));
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "playButtonClickSound");
|
||||
_state = State::Home;
|
||||
emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,6 +261,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
|
|||
if (_state != State::Web) {
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL)));
|
||||
_state = State::Web;
|
||||
emit screenChanged(QVariant("Web"), QVariant(url));
|
||||
}
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "loadWebUrl", Q_ARG(const QVariant&, QVariant(url)),
|
||||
Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl)));
|
||||
|
@ -298,6 +324,12 @@ void TabletProxy::emitScriptEvent(QVariant msg) {
|
|||
}
|
||||
}
|
||||
|
||||
void TabletProxy::sendToQml(QVariant msg) {
|
||||
if (_qmlOffscreenSurface) {
|
||||
QMetaObject::invokeMethod(_qmlOffscreenSurface, "sendToQml", Qt::AutoConnection, Q_ARG(QVariant, msg));
|
||||
}
|
||||
}
|
||||
|
||||
void TabletProxy::addButtonsToHomeScreen() {
|
||||
auto tablet = getQmlTablet();
|
||||
if (!tablet) {
|
||||
|
|
|
@ -89,6 +89,8 @@ public:
|
|||
Q_INVOKABLE void gotoWebScreen(const QString& url);
|
||||
Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl);
|
||||
|
||||
Q_INVOKABLE void loadQMLSource(const QVariant& path);
|
||||
|
||||
/**jsdoc
|
||||
* Creates a new button, adds it to this and returns it.
|
||||
* @function TabletProxy#addButton
|
||||
|
@ -120,6 +122,13 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void emitScriptEvent(QVariant msg);
|
||||
|
||||
/**jsdoc
|
||||
* Used to send an event to the qml embedded in the tablet
|
||||
* @function TabletProxy#sendToQml
|
||||
* @param msg {object|string}
|
||||
*/
|
||||
Q_INVOKABLE void sendToQml(QVariant msg);
|
||||
|
||||
Q_INVOKABLE bool onHomeScreen();
|
||||
|
||||
QObject* getTabletSurface();
|
||||
|
@ -137,6 +146,22 @@ signals:
|
|||
*/
|
||||
void webEventReceived(QVariant msg);
|
||||
|
||||
/**jsdoc
|
||||
* Signaled when this tablet receives an event from the qml embedded in the tablet
|
||||
* @function TabletProxy#fromQml
|
||||
* @param msg {object|string}
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void fromQml(QVariant msg);
|
||||
|
||||
/**jsdoc
|
||||
* Signales when this tablet screen changes.
|
||||
* @function TabletProxy#screenChanged
|
||||
* @param type {string} - "Home", "Web", "Menu", "QML", "Closed"
|
||||
* @param url {string} - only valid for Web and QML.
|
||||
*/
|
||||
void screenChanged(QVariant type, QVariant url);
|
||||
|
||||
private slots:
|
||||
void addButtonsToHomeScreen();
|
||||
void addButtonsToMenuScreen();
|
||||
|
@ -149,7 +174,7 @@ protected:
|
|||
QQuickItem* _qmlTabletRoot { nullptr };
|
||||
QObject* _qmlOffscreenSurface { nullptr };
|
||||
|
||||
enum class State { Uninitialized, Home, Web, Menu };
|
||||
enum class State { Uninitialized, Home, Web, Menu, QML };
|
||||
State _state { State::Uninitialized };
|
||||
};
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont
|
|||
prefixString.append(QString(" [%1]").arg(_targetName));
|
||||
}
|
||||
|
||||
QString logMessage = QString("%1 %2").arg(prefixString, message.split("\n").join("\n" + prefixString + " "));
|
||||
QString logMessage = QString("%1 %2").arg(prefixString, message.split('\n').join('\n' + prefixString + " "));
|
||||
fprintf(stdout, "%s\n", qPrintable(logMessage));
|
||||
return logMessage;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
Q_LOGGING_CATEGORY(trace_app, "trace.app")
|
||||
Q_LOGGING_CATEGORY(trace_app_detail, "trace.app.detail")
|
||||
Q_LOGGING_CATEGORY(trace_metadata, "trace.metadata")
|
||||
Q_LOGGING_CATEGORY(trace_network, "trace.network")
|
||||
Q_LOGGING_CATEGORY(trace_parse, "trace.parse")
|
||||
Q_LOGGING_CATEGORY(trace_render, "trace.render")
|
||||
|
@ -18,6 +19,8 @@ Q_LOGGING_CATEGORY(trace_render_gpu, "trace.render.gpu")
|
|||
Q_LOGGING_CATEGORY(trace_resource, "trace.resource")
|
||||
Q_LOGGING_CATEGORY(trace_resource_network, "trace.resource.network")
|
||||
Q_LOGGING_CATEGORY(trace_resource_parse, "trace.resource.parse")
|
||||
Q_LOGGING_CATEGORY(trace_script, "trace.script")
|
||||
Q_LOGGING_CATEGORY(trace_script_entities, "trace.script.entities")
|
||||
Q_LOGGING_CATEGORY(trace_simulation, "trace.simulation")
|
||||
Q_LOGGING_CATEGORY(trace_simulation_detail, "trace.simulation.detail")
|
||||
Q_LOGGING_CATEGORY(trace_simulation_animation, "trace.simulation.animation")
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
// When profiling something that may happen many times per frame, use a xxx_detail category so that they may easily be filtered out of trace results
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_app)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_app_detail)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_metadata)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_network)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_render)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_render_detail)
|
||||
|
@ -23,6 +24,8 @@ Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu)
|
|||
Q_DECLARE_LOGGING_CATEGORY(trace_resource)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_resource_parse)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_resource_network)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_script)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_script_entities)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_simulation)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_simulation_detail)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_simulation_animation)
|
||||
|
@ -69,6 +72,10 @@ inline void counter(const QLoggingCategory& category, const QString& name, const
|
|||
}
|
||||
}
|
||||
|
||||
inline void metadata(const QString& metadataType, const QVariantMap& args) {
|
||||
tracing::traceEvent(trace_metadata(), metadataType, tracing::Metadata, "", args);
|
||||
}
|
||||
|
||||
#define PROFILE_RANGE(category, name) Duration profileRangeThis(trace_##category(), name);
|
||||
#define PROFILE_RANGE_EX(category, name, argbColor, payload, ...) Duration profileRangeThis(trace_##category(), name, argbColor, (uint64_t)payload, ##__VA_ARGS__);
|
||||
#define PROFILE_RANGE_BEGIN(category, rangeId, name, argbColor) rangeId = Duration::beginRange(trace_##category(), name, argbColor)
|
||||
|
@ -78,6 +85,7 @@ inline void counter(const QLoggingCategory& category, const QString& name, const
|
|||
#define PROFILE_COUNTER_IF_CHANGED(category, name, type, value) { static type lastValue = 0; type newValue = value; if (newValue != lastValue) { counter(trace_##category(), name, { { name, newValue }}); lastValue = newValue; } }
|
||||
#define PROFILE_COUNTER(category, name, ...) counter(trace_##category(), name, ##__VA_ARGS__);
|
||||
#define PROFILE_INSTANT(category, name, ...) instant(trace_##category(), name, ##__VA_ARGS__);
|
||||
#define PROFILE_SET_THREAD_NAME(threadName) metadata("thread_name", { { "name", threadName } });
|
||||
|
||||
#define SAMPLE_PROFILE_RANGE(chance, category, name, ...) if (randFloat() <= chance) { PROFILE_RANGE(category, name); }
|
||||
#define SAMPLE_PROFILE_RANGE_EX(chance, category, name, ...) if (randFloat() <= chance) { PROFILE_RANGE_EX(category, name, argbColor, payload, ##__VA_ARGS__); }
|
||||
|
|
|
@ -28,9 +28,10 @@ QString Settings::fileName() const {
|
|||
}
|
||||
|
||||
void Settings::remove(const QString& key) {
|
||||
if (key == "" || _manager->contains(key)) {
|
||||
_manager->remove(key);
|
||||
}
|
||||
// NOTE: you can't check _manager->contains(key) here because it will return 'false'
|
||||
// when the key is a valid child group with child keys.
|
||||
// However, calling remove() without checking will do the right thing.
|
||||
_manager->remove(key);
|
||||
}
|
||||
|
||||
QStringList Settings::childGroups() const {
|
||||
|
@ -46,6 +47,7 @@ QStringList Settings::allKeys() const {
|
|||
}
|
||||
|
||||
bool Settings::contains(const QString& key) const {
|
||||
// NOTE: this will return 'false' if key is a valid child group with child keys.
|
||||
return _manager->contains(key);
|
||||
}
|
||||
|
||||
|
|
|
@ -132,6 +132,9 @@ void Tracer::serialize(const QString& originalPath) {
|
|||
{
|
||||
std::lock_guard<std::mutex> guard(_eventsMutex);
|
||||
currentEvents.swap(_events);
|
||||
for (auto& event : _metadataEvents) {
|
||||
currentEvents.push_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
// If the file exists and we can't remove it, fail early
|
||||
|
@ -194,33 +197,53 @@ void Tracer::serialize(const QString& originalPath) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void Tracer::traceEvent(const QLoggingCategory& category,
|
||||
const QString& name, EventType type,
|
||||
qint64 timestamp, qint64 processID, qint64 threadID,
|
||||
const QString& id,
|
||||
const QVariantMap& args, const QVariantMap& extra) {
|
||||
void Tracer::traceEvent(const QLoggingCategory& category,
|
||||
const QString& name, EventType type,
|
||||
qint64 timestamp, qint64 processID, qint64 threadID,
|
||||
const QString& id,
|
||||
const QVariantMap& args, const QVariantMap& extra) {
|
||||
std::lock_guard<std::mutex> guard(_eventsMutex);
|
||||
if (!_enabled) {
|
||||
|
||||
// We always want to store metadata events even if tracing is not enabled so that when
|
||||
// tracing is enabled we will be able to associate that metadata with that trace.
|
||||
// Metadata events should be used sparingly - as of 12/30/16 the Chrome Tracing
|
||||
// spec only supports thread+process metadata, so we should only expect to see metadata
|
||||
// events created when a new thread or process is created.
|
||||
if (!_enabled && type != Metadata) {
|
||||
return;
|
||||
}
|
||||
|
||||
_events.push_back({
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
timestamp,
|
||||
processID,
|
||||
threadID,
|
||||
category,
|
||||
args,
|
||||
extra
|
||||
});
|
||||
if (type == Metadata) {
|
||||
_metadataEvents.push_back({
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
timestamp,
|
||||
processID,
|
||||
threadID,
|
||||
category,
|
||||
args,
|
||||
extra
|
||||
});
|
||||
} else {
|
||||
_events.push_back({
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
timestamp,
|
||||
processID,
|
||||
threadID,
|
||||
category,
|
||||
args,
|
||||
extra
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Tracer::traceEvent(const QLoggingCategory& category,
|
||||
const QString& name, EventType type, const QString& id,
|
||||
const QVariantMap& args, const QVariantMap& extra) {
|
||||
if (!_enabled) {
|
||||
if (!_enabled && type != Metadata) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -97,6 +97,7 @@ private:
|
|||
|
||||
bool _enabled { false };
|
||||
std::list<TraceEvent> _events;
|
||||
std::list<TraceEvent> _metadataEvents;
|
||||
std::mutex _eventsMutex;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
//
|
||||
// BetterClientSimulationBotFromRecording.js
|
||||
// examples
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/6/17.
|
||||
// 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
|
||||
//
|
||||
|
||||
|
||||
var WANT_DEBUGGING = false;
|
||||
|
||||
randFloat = function(low, high) {
|
||||
return low + Math.random() * (high - low);
|
||||
}
|
||||
|
||||
var AVATARS_ARRAY = [
|
||||
"http://mpassets.highfidelity.com/0c2c264b-2fd2-46a4-bf80-de681881f66b-v1/F_MotRac.fst",
|
||||
"http://mpassets.highfidelity.com/bd80a6d7-7173-489e-87c6-f7ee56e65530-v1/M_RetFut.fst",
|
||||
"http://mpassets.highfidelity.com/47c8d706-d486-4c2d-afcc-70d4e1e25117-v1/M_RetSpaSuit.fst",
|
||||
"http://mpassets.highfidelity.com/548d0792-0bac-4933-bbfc-57d71912d77e-v1/M_OutMer.fst",
|
||||
"http://mpassets.highfidelity.com/13277c09-892f-4a5e-b9a5-8994a37d68bf-v1/F_WasWar.fst",
|
||||
"http://mpassets.highfidelity.com/2d384111-0f0e-42e2-b800-66bfcab4aefb-v1/F_VooQue.fst",
|
||||
"http://mpassets.highfidelity.com/57e4d1cd-9f52-4c95-9051-326f9bb114ea-v1/F_SteAvi.fst",
|
||||
"http://mpassets.highfidelity.com/da2ad4cd-47d4-41da-b764-41f39ff77e30-v1/F_JerGir.fst",
|
||||
"http://mpassets.highfidelity.com/96c747ab-f71b-44ee-8eb9-d19fc9593dda-v1/F_CatBur.fst",
|
||||
"http://mpassets.highfidelity.com/ede82c38-c66e-4f67-9e0b-0bb0782db18f-v1/M_WesOut.fst",
|
||||
"http://mpassets.highfidelity.com/8872ae86-a763-4db3-8373-d27514c1481e-v1/M_VinAvi.fst",
|
||||
"http://mpassets.highfidelity.com/faf505f1-4fd1-4ed2-8909-816af246c48f-v1/M_VicGen.fst",
|
||||
"http://mpassets.highfidelity.com/d807a7d2-5122-4436-a6f9-3173c94d1c49-v1/M_SuaGen.fst",
|
||||
"http://mpassets.highfidelity.com/1dd41735-06f4-45a3-9ec0-d05215ace77b-v1/M_MarSen.fst",
|
||||
"http://mpassets.highfidelity.com/2cad3894-8ab3-4ba5-a723-0234f93fbd6a-v1/M_BowBea.fst",
|
||||
"http://mpassets.highfidelity.com/cf0eb1be-9ec7-4756-8eaf-ac8f3ec09eba-v1/F_ClaDef.fst",
|
||||
"http://mpassets.highfidelity.com/0cedeca3-c1a4-4be9-9fd5-dad716afcc7e-v1/F_Cyria.fst",
|
||||
"http://mpassets.highfidelity.com/dc55803b-9215-47dd-9408-eb835dac4082-v1/F_ParGir.fst",
|
||||
"http://mpassets.highfidelity.com/775a8fb3-cfe7-494d-b603-a0a2d6910e55-v1/F_VinCov.fst",
|
||||
"http://mpassets.highfidelity.com/eba0d8f8-aa72-4a6b-ab64-4d3fd4695b20-v1/F_VogHei.fst",
|
||||
"http://mpassets.highfidelity.com/4f400c78-38f9-42af-b03b-11b5451d41b9-v1/M_MidRog.fst",
|
||||
"http://mpassets.highfidelity.com/ad774d79-13f1-46e2-87c9-de49a261b264-v1/F_GunSli.fst",
|
||||
"http://mpassets.highfidelity.com/5acbaefa-5455-49a2-8d40-89d12aa393ca-v1/M_KniWol.fst",
|
||||
"http://mpassets.highfidelity.com/aaa1b0a8-3e1b-492a-9aee-600e5dc907db-v1/F_RetSciSuit.fst",
|
||||
"http://mpassets.highfidelity.com/d8da10b6-25c1-40e2-9a66-369316c722d7-v1/F_AniSuit.fst",
|
||||
"http://mpassets.highfidelity.com/f3fbb9f4-e159-49ed-ac32-03af9056b17e-v1/matthew.fst",
|
||||
"http://mpassets.highfidelity.com/0c954ba0-4d87-4353-b65e-c45509f85658-v1/priscilla.fst",
|
||||
"http://mpassets.highfidelity.com/e76946cc-c272-4adf-9bb6-02cde0a4b57d-v1/9e8c5c42a0cbd436962d6bd36f032ab3.fst",
|
||||
"http://mpassets.highfidelity.com/72e083ee-194d-4113-9c61-0591d8257493-v1/skeleton_Rigged.fst",
|
||||
"http://mpassets.highfidelity.com/f14bf7c9-49a1-4249-988a-0a577ed78957-v1/beingOfLight.fst",
|
||||
"http://mpassets.highfidelity.com/1b7e1e7c-6c0b-4f20-9cd0-1d5ccedae620-v1/bb64e937acf86447f6829767e958073c.fst",
|
||||
"http://mpassets.highfidelity.com/67d7c7aa-c300-4d03-85f4-86480130eaa5-v1/F_StarCrew.fst",
|
||||
"http://mpassets.highfidelity.com/d293ef06-c659-467a-9288-c3cbaff0372a-v1/arya_avatar.fst",
|
||||
"http://mpassets.highfidelity.com/faf249d5-12a8-48e2-a08e-fb0c33087011-v1/F_Ranger.fst",
|
||||
"http://mpassets.highfidelity.com/b4502145-15eb-4023-b7d6-a81c5cbf6abf-v1/F_FitTra.fst",
|
||||
"http://mpassets.highfidelity.com/548d0792-0bac-4933-bbfc-57d71912d77e-v1/M_OutMer.fst",
|
||||
"http://mpassets.highfidelity.com/caa61e5d-5629-4165-81d8-6a7eb55e942d-v1/F_DeaSur.fst",
|
||||
"http://mpassets.highfidelity.com/2cad3894-8ab3-4ba5-a723-0234f93fbd6a-v1/M_BowBea.fst",
|
||||
"http://mpassets.highfidelity.com/fd4fa45a-9d2a-463e-a484-f9d1b3bba724-v1/M_BeaWar.fst",
|
||||
"http://mpassets.highfidelity.com/367a5b60-8a92-4d56-a152-a00f3086f02b-v1/M_Espio.fst",
|
||||
"http://mpassets.highfidelity.com/ab466729-31da-4b4c-a33c-366f7c1d38e5-v1/M_MMAFig.fst",
|
||||
"http://mpassets.highfidelity.com/b0795a0c-493d-4abd-b4cc-5f32e6d6df46-v1/M_SalMer.fst",
|
||||
"http://mpassets.highfidelity.com/0a1d44bf-a988-4199-b29e-a532ab85a2e8-v1/M_StaShi.fst",
|
||||
"http://mpassets.highfidelity.com/d807a7d2-5122-4436-a6f9-3173c94d1c49-v1/M_SuaGen.fst",
|
||||
"http://mpassets.highfidelity.com/cb20212c-36f2-4d41-bdad-132361ca6ff4-v1/M_TreTee.fst",
|
||||
"http://mpassets.highfidelity.com/830988dc-619a-4e88-96e1-a19fa0aaa30f-v1/M_UrbEnf.fst",
|
||||
"http://mpassets.highfidelity.com/faf505f1-4fd1-4ed2-8909-816af246c48f-v1/M_VicGen.fst",
|
||||
"http://mpassets.highfidelity.com/883ac86f-dd29-4676-8bda-7dd52fb6465f-v1/M_WasWan.fst",
|
||||
"http://mpassets.highfidelity.com/ede82c38-c66e-4f67-9e0b-0bb0782db18f-v1/M_WesOut.fst",
|
||||
"http://mpassets.highfidelity.com/04c9a1e9-0390-4a7f-b6c6-5f135c19e3fb-v1/F_ArmTro.fst",
|
||||
"http://mpassets.highfidelity.com/e863348f-a777-4f36-86e6-af6e65ffa161-v1/F_BloSam.fst",
|
||||
"http://mpassets.highfidelity.com/cf0eb1be-9ec7-4756-8eaf-ac8f3ec09eba-v1/F_ClaDef.fst",
|
||||
"http://mpassets.highfidelity.com/0cedeca3-c1a4-4be9-9fd5-dad716afcc7e-v1/F_Cyria.fst",
|
||||
"http://mpassets.highfidelity.com/da2ad4cd-47d4-41da-b764-41f39ff77e30-v1/F_JerGir.fst",
|
||||
"http://mpassets.highfidelity.com/534d42f8-ec13-4145-929f-5c8facac2fb7-v1/F_LegFig.fst",
|
||||
"http://mpassets.highfidelity.com/dc55803b-9215-47dd-9408-eb835dac4082-v1/F_ParGir.fst",
|
||||
"http://mpassets.highfidelity.com/f823e831-d8c4-4191-a3bd-427e406e69f9-v1/F_Shinjuku.fst",
|
||||
"http://mpassets.highfidelity.com/eba0d8f8-aa72-4a6b-ab64-4d3fd4695b20-v1/F_VogHei.fst",
|
||||
"http://mpassets.highfidelity.com/13277c09-892f-4a5e-b9a5-8994a37d68bf-v1/F_WasWar.fst",
|
||||
"http://mpassets.highfidelity.com/9b589fbb-59e4-47a9-8b3f-bf8d3a0bd1d8-v1/M_LawSur.fst",
|
||||
"http://mpassets.highfidelity.com/4f400c78-38f9-42af-b03b-11b5451d41b9-v1/M_MidRog.fst",
|
||||
"http://mpassets.highfidelity.com/c90d755d-0456-48fd-b98c-09c4d85cd481-v1/M_MouOff.fst",
|
||||
"http://mpassets.highfidelity.com/c2ed3b9a-b3a9-4424-9fd2-8a798209f32b-v1/M_PerTra.fst",
|
||||
"http://mpassets.highfidelity.com/c48928ac-7657-41f4-bbdc-9b47385736ab-v1/M_SpaMar.fst",
|
||||
"http://mpassets.highfidelity.com/d029ae8d-2905-4eb7-ba46-4bd1b8cb9d73-v1/4618d52e711fbb34df442b414da767bb.fst",
|
||||
"http://mpassets.highfidelity.com/c85c497d-c87b-42b1-9bbf-5405e05a0ad3-v1/M_ArmSol.fst",
|
||||
"http://mpassets.highfidelity.com/1dd41735-06f4-45a3-9ec0-d05215ace77b-v1/M_MarSen.fst",
|
||||
"http://mpassets.highfidelity.com/bd80a6d7-7173-489e-87c6-f7ee56e65530-v1/M_RetFut.fst",
|
||||
"http://mpassets.highfidelity.com/8872ae86-a763-4db3-8373-d27514c1481e-v1/M_VinAvi.fst",
|
||||
"http://mpassets.highfidelity.com/f798d926-9a9e-481a-b298-af0e45451252-v1/F_Assassin.fst",
|
||||
"http://mpassets.highfidelity.com/ad774d79-13f1-46e2-87c9-de49a261b264-v1/F_GunSli.fst",
|
||||
"http://mpassets.highfidelity.com/aaa1b0a8-3e1b-492a-9aee-600e5dc907db-v1/F_RetSciSuit.fst"
|
||||
];
|
||||
|
||||
|
||||
var AVATAR_URL = AVATARS_ARRAY[Math.floor(Math.random() * AVATARS_ARRAY.length)];
|
||||
print("RANDOM AVATAR SELECTED:" + AVATAR_URL);
|
||||
|
||||
var RECORDINGS_ARRAY = [
|
||||
"http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/waiting6.hfr",
|
||||
"http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/waiting7.hfr",
|
||||
"http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/waiting10.hfr",
|
||||
"http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/bot1.hfr",
|
||||
"http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/bot2.hfr",
|
||||
"http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/bot3.hfr",
|
||||
"http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/bot4.hfr"
|
||||
];
|
||||
|
||||
var RECORDING_URL = RECORDINGS_ARRAY[Math.floor(Math.random() * RECORDINGS_ARRAY.length)];
|
||||
print("RANDOM RECORDING SELECTED:" + RECORDING_URL);
|
||||
|
||||
// not quite what I want...
|
||||
var LOCATIONS_ARRAY = [
|
||||
{ min_x: 97.0, max_x: 103.0, y:-0.6, min_z: 30.8, max_z: 40 },
|
||||
{ min_x: 92.7, max_x: 106.6, y:-0.3, min_z: 43 , max_z: 43 },
|
||||
{ min_x: 92.7, max_x: 106.6, y: 0.3, min_z: 45 , max_z: 45 },
|
||||
{ min_x: 92.7, max_x: 106.6, y: 1 , min_z: 47 , max_z: 47 },
|
||||
{ min_x: 92.7, max_x: 106.6, y: 1.7, min_z: 51.9, max_z: 51.9 },
|
||||
];
|
||||
|
||||
var LOCATION_PARAMS = LOCATIONS_ARRAY[Math.floor(Math.random() * LOCATIONS_ARRAY.length)];
|
||||
|
||||
var LOCATION = { x: randFloat(LOCATION_PARAMS.min_x, LOCATION_PARAMS.max_x), y: LOCATION_PARAMS.y, z: randFloat(LOCATION_PARAMS.min_z, LOCATION_PARAMS.max_z) };
|
||||
|
||||
Vec3.print("RANDOM LOCATION SELECTED:", LOCATION);
|
||||
|
||||
var playFromCurrentLocation = true;
|
||||
var loop = true;
|
||||
|
||||
|
||||
// Set position here if playFromCurrentLocation is true
|
||||
Avatar.position = LOCATION;
|
||||
Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
Avatar.scale = 1.0;
|
||||
Agent.isAvatar = true;
|
||||
|
||||
// make the agent "listen" to the audio stream to cause additional audio-mixer load, technically this isn't needed when you're playing a recording
|
||||
// but if you switch to a non-recording bot, you will need this, so we can leave this.
|
||||
Agent.isListeningToAudioStream = true;
|
||||
Avatar.skeletonModelURL = AVATAR_URL; // FIXME - currently setting an avatar while playing a recording doesn't work it will be ignored
|
||||
|
||||
Recording.loadRecording(RECORDING_URL);
|
||||
|
||||
count = 300; // This is necessary to wait for the audio mixer to connect
|
||||
function update(event) {
|
||||
if (count > 0) {
|
||||
count--;
|
||||
return;
|
||||
}
|
||||
if (count == 0) {
|
||||
Recording.setPlayFromCurrentLocation(playFromCurrentLocation);
|
||||
Recording.setPlayerLoop(loop);
|
||||
Recording.setPlayerUseDisplayName(true);
|
||||
Recording.setPlayerUseAttachments(true);
|
||||
Recording.setPlayerUseHeadModel(false);
|
||||
Recording.setPlayerUseSkeletonModel(false); // FIXME - this would allow you to override the recording avatar, but that's not currently working
|
||||
Recording.startPlaying();
|
||||
Vec3.print("Playing from ", Avatar.position);
|
||||
count--;
|
||||
} else if (WANT_DEBUGGING) {
|
||||
count = 100;
|
||||
Vec3.print("Avatar at: ", Avatar.position);
|
||||
Quat.print("Avatar head orientation: ", Avatar.headOrientation);
|
||||
print("outbound:"
|
||||
+" GP: " + Avatar.getDataRate("globalPositionOutbound").toFixed(2) + "\n"
|
||||
+" LP: " + Avatar.getDataRate("localPositionOutbound").toFixed(2) + "\n"
|
||||
+" BB: " + Avatar.getDataRate("avatarBoundingBoxOutbound").toFixed(2) + "\n"
|
||||
+" AO: " + Avatar.getDataRate("avatarOrientationOutbound").toFixed(2) + "\n"
|
||||
+" AS: " + Avatar.getDataRate("avatarScaleOutbound").toFixed(2) + "\n"
|
||||
+" LA: " + Avatar.getDataRate("lookAtPositionOutbound").toFixed(2) + "\n"
|
||||
+" AL: " + Avatar.getDataRate("audioLoudnessOutbound").toFixed(2) + "\n"
|
||||
+" SW: " + Avatar.getDataRate("sensorToWorkMatrixOutbound").toFixed(2) + "\n"
|
||||
+" AF: " + Avatar.getDataRate("additionalFlagsOutbound").toFixed(2) + "\n"
|
||||
+" PI: " + Avatar.getDataRate("parentInfoOutbound").toFixed(2) + "\n"
|
||||
+" FT: " + Avatar.getDataRate("faceTrackerOutbound").toFixed(2) + "\n"
|
||||
+" JD: " + Avatar.getDataRate("jointDataOutbound").toFixed(2));
|
||||
}
|
||||
|
||||
if (!Recording.isPlaying()) {
|
||||
Script.update.disconnect(update);
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(update);
|
|
@ -60,19 +60,26 @@ function updateOverlays() {
|
|||
var overlayPosition = avatar.getJointPosition("Head");
|
||||
overlayPosition.y += 1.05;
|
||||
|
||||
var text = " All: " + AvatarManager.getAvatarDataRate(avatarID).toFixed(2) + "\n"
|
||||
+" GP: " + AvatarManager.getAvatarDataRate(avatarID,"globalPosition").toFixed(2) + "\n"
|
||||
+" LP: " + AvatarManager.getAvatarDataRate(avatarID,"localPosition").toFixed(2) + "\n"
|
||||
+" BB: " + AvatarManager.getAvatarDataRate(avatarID,"avatarBoundingBox").toFixed(2) + "\n"
|
||||
+" AO: " + AvatarManager.getAvatarDataRate(avatarID,"avatarOrientation").toFixed(2) + "\n"
|
||||
+" AS: " + AvatarManager.getAvatarDataRate(avatarID,"avatarScale").toFixed(2) + "\n"
|
||||
+" LA: " + AvatarManager.getAvatarDataRate(avatarID,"lookAtPosition").toFixed(2) + "\n"
|
||||
+" AL: " + AvatarManager.getAvatarDataRate(avatarID,"audioLoudness").toFixed(2) + "\n"
|
||||
+" SW: " + AvatarManager.getAvatarDataRate(avatarID,"sensorToWorkMatrix").toFixed(2) + "\n"
|
||||
+" AF: " + AvatarManager.getAvatarDataRate(avatarID,"additionalFlags").toFixed(2) + "\n"
|
||||
+" PI: " + AvatarManager.getAvatarDataRate(avatarID,"parentInfo").toFixed(2) + "\n"
|
||||
+" FT: " + AvatarManager.getAvatarDataRate(avatarID,"faceTracker").toFixed(2) + "\n"
|
||||
+" JD: " + AvatarManager.getAvatarDataRate(avatarID,"jointData").toFixed(2);
|
||||
var text = avatarID + "\n"
|
||||
+"--- Data from Mixer ---\n"
|
||||
+"All: " + AvatarManager.getAvatarDataRate(avatarID).toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID).toFixed(2) + "hz)" + "\n"
|
||||
+" GP: " + AvatarManager.getAvatarDataRate(avatarID,"globalPosition").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"globalPosition").toFixed(2) + "hz)" + "\n"
|
||||
+" LP: " + AvatarManager.getAvatarDataRate(avatarID,"localPosition").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"localPosition").toFixed(2) + "hz)" + "\n"
|
||||
+" BB: " + AvatarManager.getAvatarDataRate(avatarID,"avatarBoundingBox").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"avatarBoundingBox").toFixed(2) + "hz)" + "\n"
|
||||
+" AO: " + AvatarManager.getAvatarDataRate(avatarID,"avatarOrientation").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"avatarOrientation").toFixed(2) + "hz)" + "\n"
|
||||
+" AS: " + AvatarManager.getAvatarDataRate(avatarID,"avatarScale").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"avatarScale").toFixed(2) + "hz)" + "\n"
|
||||
+" LA: " + AvatarManager.getAvatarDataRate(avatarID,"lookAtPosition").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"lookAtPosition").toFixed(2) + "hz)" + "\n"
|
||||
+" AL: " + AvatarManager.getAvatarDataRate(avatarID,"audioLoudness").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"audioLoudness").toFixed(2) + "hz)" + "\n"
|
||||
+" SW: " + AvatarManager.getAvatarDataRate(avatarID,"sensorToWorkMatrix").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"sensorToWorkMatrix").toFixed(2) + "hz)" + "\n"
|
||||
+" AF: " + AvatarManager.getAvatarDataRate(avatarID,"additionalFlags").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"additionalFlags").toFixed(2) + "hz)" + "\n"
|
||||
+" PI: " + AvatarManager.getAvatarDataRate(avatarID,"parentInfo").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"parentInfo").toFixed(2) + "hz)" + "\n"
|
||||
+" FT: " + AvatarManager.getAvatarDataRate(avatarID,"faceTracker").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"faceTracker").toFixed(2) + "hz)" + "\n"
|
||||
+" JD: " + AvatarManager.getAvatarDataRate(avatarID,"jointData").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"jointData").toFixed(2) + "hz)" + "\n"
|
||||
+"--- Simulation ---\n"
|
||||
+"All: " + AvatarManager.getAvatarSimulationRate(avatarID,"avatar").toFixed(2) + "hz \n"
|
||||
+" inView: " + AvatarManager.getAvatarSimulationRate(avatarID,"avatarInView").toFixed(2) + "hz \n"
|
||||
+" SM: " + AvatarManager.getAvatarSimulationRate(avatarID,"skeletonModel").toFixed(2) + "hz \n"
|
||||
+" JD: " + AvatarManager.getAvatarSimulationRate(avatarID,"jointData").toFixed(2) + "hz \n"
|
||||
|
||||
if (avatarID in debugOverlays) {
|
||||
// keep the overlay above the current position of this avatar
|
||||
|
@ -85,8 +92,8 @@ function updateOverlays() {
|
|||
var newOverlay = Overlays.addOverlay("text3d", {
|
||||
position: overlayPosition,
|
||||
dimensions: {
|
||||
x: 1,
|
||||
y: 13 * 0.13
|
||||
x: 1.25,
|
||||
y: 19 * 0.13
|
||||
},
|
||||
lineHeight: 0.1,
|
||||
font:{size:0.1},
|
||||
|
|
28
scripts/developer/debugging/essDebugWindow.js
Normal file
28
scripts/developer/debugging/essDebugWindow.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// essDebugWindow.js
|
||||
//
|
||||
// Created by Clement Brisset on 2/2/17.
|
||||
// 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
|
||||
//
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
||||
// Set up the qml ui
|
||||
var qml = Script.resolvePath('debugWindow.qml');
|
||||
var window = new OverlayWindow({
|
||||
title: 'Entity Script Server Log Window',
|
||||
source: qml,
|
||||
width: 400, height: 900,
|
||||
});
|
||||
window.setPosition(25, 50);
|
||||
window.closed.connect(function() { Script.stop(); });
|
||||
|
||||
EntityScriptServerLog.receivedNewLogLines.connect(function(message) {
|
||||
window.sendToQml(message.trim());
|
||||
});
|
||||
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
|
@ -26,6 +26,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
//
|
||||
// add lines where the hand ray picking is happening
|
||||
//
|
||||
|
||||
var WANT_DEBUG = false;
|
||||
var WANT_DEBUG_STATE = false;
|
||||
var WANT_DEBUG_SEARCH_NAME = null;
|
||||
|
@ -752,6 +753,7 @@ function MyController(hand) {
|
|||
this.previouslyUnhooked = {};
|
||||
|
||||
this.shouldScale = false;
|
||||
this.isScalingAvatar = false;
|
||||
|
||||
// handPosition is where the avatar's hand appears to be, in-world.
|
||||
this.getHandPosition = function () {
|
||||
|
@ -824,11 +826,7 @@ function MyController(hand) {
|
|||
|
||||
this.update = function(deltaTime, timestamp) {
|
||||
this.updateSmoothedTrigger();
|
||||
// If both trigger and grip buttons squeezed and nothing is held, rescale my avatar!
|
||||
if (this.hand === RIGHT_HAND && this.state === STATE_SEARCHING &&
|
||||
this.getOtherHandController().state === STATE_SEARCHING) {
|
||||
this.maybeScaleMyAvatar();
|
||||
}
|
||||
this.maybeScaleMyAvatar();
|
||||
|
||||
if (this.ignoreInput()) {
|
||||
|
||||
|
@ -2595,22 +2593,29 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.maybeScaleMyAvatar = function() {
|
||||
if (!myAvatarScalingEnabled) {
|
||||
if (!myAvatarScalingEnabled || this.shouldScale || this.hand === LEFT_HAND) {
|
||||
// If scaling disabled, or if we are currently scaling an entity, don't scale avatar
|
||||
// and only rescale avatar for one hand (so we're not doing it twice)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.shouldScale) {
|
||||
// Only scale avatar if both triggers and grips are squeezed
|
||||
var tryingToScale = this.secondarySqueezed() && this.getOtherHandController().secondarySqueezed() &&
|
||||
this.triggerSmoothedSqueezed() && this.getOtherHandController().triggerSmoothedSqueezed();
|
||||
|
||||
|
||||
if (!this.isScalingAvatar) {
|
||||
// If both secondary triggers squeezed, start scaling
|
||||
if (this.secondarySqueezed() && this.getOtherHandController().secondarySqueezed()) {
|
||||
if (tryingToScale) {
|
||||
this.scalingStartDistance = Vec3.length(Vec3.subtract(this.getHandPosition(),
|
||||
this.getOtherHandController().getHandPosition()));
|
||||
this.scalingStartAvatarScale = MyAvatar.scale;
|
||||
this.shouldScale = true;
|
||||
this.isScalingAvatar = true;
|
||||
}
|
||||
} else if (!this.secondarySqueezed() || !this.getOtherHandController().secondarySqueezed()) {
|
||||
this.shouldScale = false;
|
||||
} else if (!tryingToScale) {
|
||||
this.isScalingAvatar = false;
|
||||
}
|
||||
if (this.shouldScale) {
|
||||
if (this.isScalingAvatar) {
|
||||
var scalingCurrentDistance = Vec3.length(Vec3.subtract(this.getHandPosition(),
|
||||
this.getOtherHandController().getHandPosition()));
|
||||
var newAvatarScale = (scalingCurrentDistance / this.scalingStartDistance) * this.scalingStartAvatarScale;
|
||||
|
|
112
scripts/system/nameTag.js
Normal file
112
scripts/system/nameTag.js
Normal file
|
@ -0,0 +1,112 @@
|
|||
"use strict";
|
||||
|
||||
/*jslint vars: true, plusplus: true*/
|
||||
/*global Entities, Script, Quat, Vec3, MyAvatar, print*/
|
||||
// nameTag.js
|
||||
//
|
||||
// Created by Triplelexx on 17/01/31
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Running the script creates a text entity that will hover over the user's head showing their display name.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
const CLIENTONLY = false;
|
||||
const NULL_UUID = "{00000000-0000-0000-0000-000000000000}";
|
||||
const ENTITY_CHECK_INTERVAL = 5000; // ms = 5 seconds
|
||||
const STARTUP_DELAY = 2000; // ms = 2 second
|
||||
const OLD_AGE = 3500; // we recreate the entity if older than this time in seconds
|
||||
const TTL = 2; // time to live in seconds if script is not running
|
||||
const HEIGHT_ABOVE_HEAD = 0.2;
|
||||
const HEAD_OFFSET = -0.025;
|
||||
const SIZE_Y = 0.075;
|
||||
const LETTER_OFFSET = 0.03; // arbitrary value to dynamically change width, could be more accurate by detecting characters
|
||||
const LINE_HEIGHT = 0.05;
|
||||
|
||||
var nameTagEntityID = NULL_UUID;
|
||||
var lastCheckForEntity = 0;
|
||||
|
||||
// create the name tag entity after a brief delay
|
||||
Script.setTimeout(function() {
|
||||
addNameTag();
|
||||
}, STARTUP_DELAY);
|
||||
|
||||
function addNameTag() {
|
||||
var nameTagPosition = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(HEAD_OFFSET, Quat.getFront(MyAvatar.orientation)));
|
||||
nameTagPosition.y += HEIGHT_ABOVE_HEAD;
|
||||
var nameTagProperties = {
|
||||
name: MyAvatar.displayName + ' Name Tag',
|
||||
type: 'Text',
|
||||
text: MyAvatar.displayName,
|
||||
lineHeight: LINE_HEIGHT,
|
||||
parentID: MyAvatar.sessionUUID,
|
||||
dimensions: dimensionsFromName(),
|
||||
position: nameTagPosition
|
||||
}
|
||||
nameTagEntityID = Entities.addEntity(nameTagProperties, CLIENTONLY);
|
||||
}
|
||||
|
||||
function updateNameTag() {
|
||||
var nameTagProps = Entities.getEntityProperties(nameTagEntityID);
|
||||
var nameTagPosition = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(HEAD_OFFSET, Quat.getFront(MyAvatar.orientation)));
|
||||
nameTagPosition.y += HEIGHT_ABOVE_HEAD;
|
||||
|
||||
Entities.editEntity(nameTagEntityID, {
|
||||
position: nameTagPosition,
|
||||
dimensions: dimensionsFromName(),
|
||||
// lifetime is in seconds we add TTL on top of the next poll time
|
||||
lifetime: Math.round(nameTagProps.age) + (ENTITY_CHECK_INTERVAL / 1000) + TTL,
|
||||
text: MyAvatar.displayName
|
||||
});
|
||||
};
|
||||
|
||||
function deleteNameTag() {
|
||||
if(nameTagEntityID !== NULL_UUID) {
|
||||
Entities.deleteEntity(nameTagEntityID);
|
||||
nameTagEntityID = NULL_UUID;
|
||||
}
|
||||
}
|
||||
|
||||
function dimensionsFromName() {
|
||||
return {
|
||||
x: LETTER_OFFSET * MyAvatar.displayName.length,
|
||||
y: SIZE_Y,
|
||||
z: 0.0
|
||||
}
|
||||
};
|
||||
|
||||
// cleanup on ending
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
function cleanup() {
|
||||
deleteNameTag();
|
||||
}
|
||||
|
||||
Script.update.connect(update);
|
||||
function update() {
|
||||
// if no entity we return
|
||||
if(nameTagEntityID == NULL_UUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(Date.now() - lastCheckForEntity > ENTITY_CHECK_INTERVAL) {
|
||||
checkForEntity();
|
||||
lastCheckForEntity = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
function checkForEntity() {
|
||||
var nameTagProps = Entities.getEntityProperties(nameTagEntityID);
|
||||
// it is possible for the age to not be a valid number, we check for this and return accordingly
|
||||
if(nameTagProps.age == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// it's too old or we receive undefined make a new one, otherwise update
|
||||
if(nameTagProps.age > OLD_AGE || nameTagProps.age == undefined) {
|
||||
deleteNameTag();
|
||||
addNameTag();
|
||||
} else {
|
||||
updateNameTag();
|
||||
}
|
||||
}
|
|
@ -203,8 +203,7 @@ var pal = new OverlayWindow({
|
|||
height: 640,
|
||||
visible: false
|
||||
});
|
||||
pal.fromQml.connect(function (message) { // messages are {method, params}, like json-rpc. See also sendToQml.
|
||||
print('From PAL QML:', JSON.stringify(message));
|
||||
function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml.
|
||||
switch (message.method) {
|
||||
case 'selected':
|
||||
selectedIds = message.params;
|
||||
|
@ -259,7 +258,15 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like
|
|||
default:
|
||||
print('Unrecognized message from Pal.qml:', JSON.stringify(message));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sendToQml(message) {
|
||||
if (Settings.getValue("HUDUIEnabled")) {
|
||||
pal.sendToQml(message);
|
||||
} else {
|
||||
tablet.sendToQml(message);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Main operations.
|
||||
|
@ -298,10 +305,10 @@ function populateUserList(selectData) {
|
|||
data.push(avatarPalDatum);
|
||||
print('PAL data:', JSON.stringify(avatarPalDatum));
|
||||
});
|
||||
pal.sendToQml({ method: 'users', params: data });
|
||||
sendToQml({ method: 'users', params: data });
|
||||
if (selectData) {
|
||||
selectData[2] = true;
|
||||
pal.sendToQml({ method: 'select', params: selectData });
|
||||
sendToQml({ method: 'select', params: selectData });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,7 +329,7 @@ function usernameFromIDReply(id, username, machineFingerprint, isAdmin) {
|
|||
}
|
||||
print('Username Data:', JSON.stringify(data));
|
||||
// Ship the data off to QML
|
||||
pal.sendToQml({ method: 'updateUsername', params: data });
|
||||
sendToQml({ method: 'updateUsername', params: data });
|
||||
}
|
||||
|
||||
var pingPong = true;
|
||||
|
@ -396,7 +403,7 @@ function handleClick(pickRay) {
|
|||
ExtendedOverlay.applyPickRay(pickRay, function (overlay) {
|
||||
// Don't select directly. Tell qml, who will give us back a list of ids.
|
||||
var message = {method: 'select', params: [[overlay.key], !overlay.selected, false]};
|
||||
pal.sendToQml(message);
|
||||
sendToQml(message);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
@ -492,6 +499,7 @@ if (Settings.getValue("HUDUIEnabled")) {
|
|||
visible: true,
|
||||
alpha: 0.9
|
||||
});
|
||||
pal.fromQml.connect(fromQml);
|
||||
} else {
|
||||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
button = tablet.addButton({
|
||||
|
@ -499,7 +507,9 @@ if (Settings.getValue("HUDUIEnabled")) {
|
|||
icon: "icons/tablet-icons/people-i.svg",
|
||||
sortOrder: 7
|
||||
});
|
||||
tablet.fromQml.connect(fromQml);
|
||||
}
|
||||
|
||||
var isWired = false;
|
||||
var audioTimer;
|
||||
var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too)
|
||||
|
@ -518,10 +528,26 @@ function off() {
|
|||
Users.requestsDomainListData = false;
|
||||
}
|
||||
function onClicked() {
|
||||
if (!pal.visible) {
|
||||
if (Settings.getValue("HUDUIEnabled")) {
|
||||
if (!pal.visible) {
|
||||
Users.requestsDomainListData = true;
|
||||
populateUserList();
|
||||
pal.raise();
|
||||
isWired = true;
|
||||
Script.update.connect(updateOverlays);
|
||||
Controller.mousePressEvent.connect(handleMouseEvent);
|
||||
Controller.mouseMoveEvent.connect(handleMouseMoveEvent);
|
||||
triggerMapping.enable();
|
||||
triggerPressMapping.enable();
|
||||
audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS);
|
||||
} else {
|
||||
off();
|
||||
}
|
||||
pal.setVisible(!pal.visible);
|
||||
} else {
|
||||
tablet.loadQMLSource("../Pal.qml");
|
||||
Users.requestsDomainListData = true;
|
||||
populateUserList();
|
||||
pal.raise();
|
||||
isWired = true;
|
||||
Script.update.connect(updateOverlays);
|
||||
Controller.mousePressEvent.connect(handleMouseEvent);
|
||||
|
@ -529,10 +555,7 @@ function onClicked() {
|
|||
triggerMapping.enable();
|
||||
triggerPressMapping.enable();
|
||||
audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS);
|
||||
} else {
|
||||
off();
|
||||
}
|
||||
pal.setVisible(!pal.visible);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -550,7 +573,7 @@ function receiveMessage(channel, messageString, senderID) {
|
|||
if (!pal.visible) {
|
||||
onClicked();
|
||||
}
|
||||
pal.sendToQml(message); // Accepts objects, not just strings.
|
||||
sendToQml(message); // Accepts objects, not just strings.
|
||||
break;
|
||||
default:
|
||||
print('Unrecognized PAL message', messageString);
|
||||
|
@ -607,13 +630,13 @@ function createAudioInterval(interval) {
|
|||
var userId = id || 0;
|
||||
param[userId] = level;
|
||||
});
|
||||
pal.sendToQml({method: 'updateAudioLevel', params: param});
|
||||
sendToQml({method: 'updateAudioLevel', params: param});
|
||||
}, interval);
|
||||
}
|
||||
|
||||
function avatarDisconnected(nodeID) {
|
||||
// remove from the pal list
|
||||
pal.sendToQml({method: 'avatarDisconnected', params: [nodeID]});
|
||||
sendToQml({method: 'avatarDisconnected', params: [nodeID]});
|
||||
}
|
||||
//
|
||||
// Button state.
|
||||
|
@ -624,11 +647,20 @@ function onVisibleChanged() {
|
|||
button.clicked.connect(onClicked);
|
||||
pal.visibleChanged.connect(onVisibleChanged);
|
||||
pal.closed.connect(off);
|
||||
|
||||
if (!Settings.getValue("HUDUIEnabled")) {
|
||||
tablet.screenChanged.connect(function (type, url) {
|
||||
if (type !== "QML" || url !== "../Pal.qml") {
|
||||
off();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Users.usernameFromIDReply.connect(usernameFromIDReply);
|
||||
Users.avatarDisconnected.connect(avatarDisconnected);
|
||||
|
||||
function clearLocalQMLDataAndClosePAL() {
|
||||
pal.sendToQml({ method: 'clearLocalQMLData' });
|
||||
sendToQml({ method: 'clearLocalQMLData' });
|
||||
if (pal.visible) {
|
||||
onClicked(); // Close the PAL
|
||||
}
|
||||
|
|
64
scripts/system/tablet-goto.js
Normal file
64
scripts/system/tablet-goto.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
"use strict";
|
||||
|
||||
//
|
||||
// goto.js
|
||||
// scripts/system/
|
||||
//
|
||||
// Created by Dante Ruiz on 8 February 2017
|
||||
// Copyright 2016 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
|
||||
//
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
var gotoQmlSource = "TabletAddressDialog.qml";
|
||||
var button;
|
||||
var buttonName = "GOTO";
|
||||
var toolBar = null;
|
||||
var tablet = null;
|
||||
function onAddressBarShown(visible) {
|
||||
if (toolBar) {
|
||||
button.editProperties({isActive: visible});
|
||||
}
|
||||
}
|
||||
|
||||
function onClicked(){
|
||||
if (toolBar) {
|
||||
DialogsManager.toggleAddressBar();
|
||||
} else {
|
||||
tablet.loadQMLSource(gotoQmlSource);
|
||||
}
|
||||
}
|
||||
if (Settings.getValue("HUDUIEnabled")) {
|
||||
toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
|
||||
button = toolBar.addButton({
|
||||
objectName: buttonName,
|
||||
imageURL: Script.resolvePath("assets/images/tools/directory.svg"),
|
||||
visible: true,
|
||||
alpha: 0.9
|
||||
});
|
||||
} else {
|
||||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
button = tablet.addButton({
|
||||
icon: "icons/tablet-icons/goto-i.svg",
|
||||
text: buttonName,
|
||||
sortOrder: 8
|
||||
});
|
||||
}
|
||||
|
||||
button.clicked.connect(onClicked);
|
||||
DialogsManager.addressBarShown.connect(onAddressBarShown);
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
button.clicked.disconnect(onClicked);
|
||||
if (tablet) {
|
||||
tablet.removeButton(button);
|
||||
}
|
||||
if (toolBar) {
|
||||
toolBar.removeButton(buttonName);
|
||||
}
|
||||
DialogsManager.addressBarShown.disconnect(onAddressBarShown);
|
||||
});
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
Binary file not shown.
Binary file not shown.
BIN
unpublishedScripts/marketplace/stopwatch/models/Stopwatch.fbx
Normal file
BIN
unpublishedScripts/marketplace/stopwatch/models/Stopwatch.fbx
Normal file
Binary file not shown.
|
@ -8,34 +8,39 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var positionToSpawn = Vec3.sum(MyAvatar.position, Quat.getFront(MyAvatar.rotation));
|
||||
var forward = Quat.getFront(MyAvatar.orientation);
|
||||
Vec3.print("Forward: ", forward);
|
||||
var positionToSpawn = Vec3.sum(MyAvatar.position, Vec3.multiply(3, forward));
|
||||
var scale = 0.5;
|
||||
positionToSpawn.y += 0.5;
|
||||
|
||||
var stopwatchID = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "stopwatch/base",
|
||||
position: positionToSpawn,
|
||||
modelURL: "http://hifi-content.s3.amazonaws.com/alan/dev/Stopwatch.fbx",
|
||||
dimensions: {"x":4.129462242126465,"y":1.058512806892395,"z":5.773681640625}
|
||||
modelURL: Script.resolvePath("models/Stopwatch.fbx"),
|
||||
dimensions: Vec3.multiply(scale, {"x":4.129462242126465,"y":1.058512806892395,"z":5.773681640625}),
|
||||
rotation: Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollDegrees(90, 0, 0))
|
||||
});
|
||||
|
||||
var secondHandID = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "stopwatch/seconds",
|
||||
parentID: stopwatchID,
|
||||
localPosition: {"x":-0.004985813982784748,"y":0.39391064643859863,"z":0.8312804698944092},
|
||||
dimensions: {"x":0.14095762372016907,"y":0.02546107769012451,"z":1.6077008247375488},
|
||||
localPosition: Vec3.multiply(scale, {"x":-0.004985813982784748,"y":0.39391064643859863,"z":0.8312804698944092}),
|
||||
dimensions: Vec3.multiply(scale, {"x":0.14095762372016907,"y":0.02546107769012451,"z":1.6077008247375488}),
|
||||
registrationPoint: {"x":0.5,"y":0.5,"z":1},
|
||||
modelURL: "http://hifi-content.s3.amazonaws.com/alan/dev/Stopwatch-sec-hand.fbx",
|
||||
modelURL: Script.resolvePath("models/Stopwatch-sec-hand.fbx"),
|
||||
});
|
||||
|
||||
var minuteHandID = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "stopwatch/minutes",
|
||||
parentID: stopwatchID,
|
||||
localPosition: {"x":-0.0023056098725646734,"y":0.3308190703392029,"z":0.21810021996498108},
|
||||
dimensions: {"x":0.045471154153347015,"y":0.015412690117955208,"z":0.22930574417114258},
|
||||
localPosition: Vec3.multiply(scale, {"x":-0.0023056098725646734,"y":0.3308190703392029,"z":0.21810021996498108}),
|
||||
dimensions: Vec3.multiply(scale, {"x":0.045471154153347015,"y":0.015412690117955208,"z":0.22930574417114258}),
|
||||
registrationPoint: {"x":0.5,"y":0.5,"z":1},
|
||||
modelURL: "http://hifi-content.s3.amazonaws.com/alan/dev/Stopwatch-min-hand.fbx",
|
||||
modelURL: Script.resolvePath("models/Stopwatch-min-hand.fbx"),
|
||||
});
|
||||
|
||||
Entities.editEntity(stopwatchID, {
|
||||
|
@ -46,3 +51,5 @@ Entities.editEntity(stopwatchID, {
|
|||
script: Script.resolvePath("stopwatchClient.js"),
|
||||
serverScripts: Script.resolvePath("stopwatchServer.js")
|
||||
});
|
||||
|
||||
Script.stop()
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
self.secondHandID = null;
|
||||
self.minuteHandID = null;
|
||||
|
||||
self.tickSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/huffman/tick.wav");
|
||||
self.tickSound = SoundCache.getSound(Script.resolvePath("sounds/tick.wav"));
|
||||
self.tickInjector = null;
|
||||
self.tickIntervalID = null;
|
||||
|
||||
self.chimeSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/huffman/chime.wav");
|
||||
self.chimeSound = SoundCache.getSound(Script.resolvePath("sounds/chime.wav"));
|
||||
|
||||
self.preload = function(entityID) {
|
||||
print("Preloading stopwatch: ", entityID);
|
||||
|
|
Loading…
Reference in a new issue