Merge pull request #9931 from sethalves/tablet-ui

Tablet ui -- merge from upstream
This commit is contained in:
Seth Alves 2017-03-17 06:54:10 -08:00 committed by GitHub
commit d45266f28b
33 changed files with 769 additions and 323 deletions

View file

@ -371,25 +371,39 @@ void Agent::executeScript() {
using namespace recording; using namespace recording;
static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::getAudioFrameName()); static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::getAudioFrameName());
Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) { Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) {
const QByteArray& audio = frame->data;
static quint16 audioSequenceNumber{ 0 }; static quint16 audioSequenceNumber{ 0 };
Transform audioTransform;
QByteArray audio(frame->data);
if (_isNoiseGateEnabled) {
static int numSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
_noiseGate.gateSamples(reinterpret_cast<int16_t*>(audio.data()), numSamples);
}
computeLoudness(&audio, scriptedAvatar);
// the codec needs a flush frame before sending silent packets, so
// do not send one if the gate closed in this block (eventually this can be crossfaded).
auto packetType = PacketType::MicrophoneAudioNoEcho;
if (scriptedAvatar->getAudioLoudness() == 0.0f && !_noiseGate.closedInLastBlock()) {
packetType = PacketType::SilentAudioFrame;
}
Transform audioTransform;
auto headOrientation = scriptedAvatar->getHeadOrientation(); auto headOrientation = scriptedAvatar->getHeadOrientation();
audioTransform.setTranslation(scriptedAvatar->getPosition()); audioTransform.setTranslation(scriptedAvatar->getPosition());
audioTransform.setRotation(headOrientation); audioTransform.setRotation(headOrientation);
computeLoudness(&audio, scriptedAvatar);
QByteArray encodedBuffer; QByteArray encodedBuffer;
if (_encoder) { if (_encoder) {
_encoder->encode(audio, encodedBuffer); _encoder->encode(audio, encodedBuffer);
} else { } else {
encodedBuffer = audio; encodedBuffer = audio;
} }
AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber,
audioTransform, scriptedAvatar->getPosition(), glm::vec3(0), audioTransform, scriptedAvatar->getPosition(), glm::vec3(0),
PacketType::MicrophoneAudioNoEcho, _selectedCodecName); packetType, _selectedCodecName);
}); });
auto avatarHashMap = DependencyManager::set<AvatarHashMap>(); auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
@ -483,6 +497,14 @@ void Agent::setIsListeningToAudioStream(bool isListeningToAudioStream) {
_isListeningToAudioStream = isListeningToAudioStream; _isListeningToAudioStream = isListeningToAudioStream;
} }
void Agent::setIsNoiseGateEnabled(bool isNoiseGateEnabled) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setIsNoiseGateEnabled", Q_ARG(bool, isNoiseGateEnabled));
return;
}
_isNoiseGateEnabled = isNoiseGateEnabled;
}
void Agent::setIsAvatar(bool isAvatar) { void Agent::setIsAvatar(bool isAvatar) {
// this must happen on Agent's main thread // this must happen on Agent's main thread
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {

View file

@ -29,6 +29,7 @@
#include <plugins/CodecPlugin.h> #include <plugins/CodecPlugin.h>
#include "AudioNoiseGate.h"
#include "MixedAudioStream.h" #include "MixedAudioStream.h"
#include "avatars/ScriptableAvatar.h" #include "avatars/ScriptableAvatar.h"
@ -38,6 +39,7 @@ class Agent : public ThreadedAssignment {
Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar) Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
Q_PROPERTY(bool isPlayingAvatarSound READ isPlayingAvatarSound) Q_PROPERTY(bool isPlayingAvatarSound READ isPlayingAvatarSound)
Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream) Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream)
Q_PROPERTY(bool isNoiseGateEnabled READ isNoiseGateEnabled WRITE setIsNoiseGateEnabled)
Q_PROPERTY(float lastReceivedAudioLoudness READ getLastReceivedAudioLoudness) Q_PROPERTY(float lastReceivedAudioLoudness READ getLastReceivedAudioLoudness)
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID) Q_PROPERTY(QUuid sessionUUID READ getSessionUUID)
@ -52,6 +54,9 @@ public:
bool isListeningToAudioStream() const { return _isListeningToAudioStream; } bool isListeningToAudioStream() const { return _isListeningToAudioStream; }
void setIsListeningToAudioStream(bool isListeningToAudioStream); void setIsListeningToAudioStream(bool isListeningToAudioStream);
bool isNoiseGateEnabled() const { return _isNoiseGateEnabled; }
void setIsNoiseGateEnabled(bool isNoiseGateEnabled);
float getLastReceivedAudioLoudness() const { return _lastReceivedAudioLoudness; } float getLastReceivedAudioLoudness() const { return _lastReceivedAudioLoudness; }
QUuid getSessionUUID() const; QUuid getSessionUUID() const;
@ -106,6 +111,9 @@ private:
QTimer* _avatarIdentityTimer = nullptr; QTimer* _avatarIdentityTimer = nullptr;
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers; QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
AudioNoiseGate _noiseGate;
bool _isNoiseGateEnabled { false };
CodecPluginPointer _codec; CodecPluginPointer _codec;
QString _selectedCodecName; QString _selectedCodecName;
Encoder* _encoder { nullptr }; Encoder* _encoder { nullptr };

View file

@ -455,13 +455,13 @@ void EntityScriptServer::addingEntity(const EntityItemID& entityID) {
void EntityScriptServer::deletingEntity(const EntityItemID& entityID) { void EntityScriptServer::deletingEntity(const EntityItemID& entityID) {
if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) { if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) {
_entitiesScriptEngine->unloadEntityScript(entityID); _entitiesScriptEngine->unloadEntityScript(entityID, true);
} }
} }
void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, const bool reload) { void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, const bool reload) {
if (_entityViewer.getTree() && !_shuttingDown) { if (_entityViewer.getTree() && !_shuttingDown) {
_entitiesScriptEngine->unloadEntityScript(entityID); _entitiesScriptEngine->unloadEntityScript(entityID, true);
checkAndCallPreload(entityID, reload); checkAndCallPreload(entityID, reload);
} }
} }

View file

@ -246,10 +246,13 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
_agentPermissions[editorKey]->set(NodePermissions::Permission::canAdjustLocks); _agentPermissions[editorKey]->set(NodePermissions::Permission::canAdjustLocks);
} }
QList<QHash<NodePermissionsKey, NodePermissionsPointer>> permissionsSets; std::list<std::unordered_map<NodePermissionsKey, NodePermissionsPointer>> permissionsSets{
permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get(); _standardAgentPermissions.get(),
_agentPermissions.get()
};
foreach (auto permissionsSet, permissionsSets) { foreach (auto permissionsSet, permissionsSets) {
foreach (NodePermissionsKey userKey, permissionsSet.keys()) { for (auto entry : permissionsSet) {
const auto& userKey = entry.first;
if (onlyEditorsAreRezzers) { if (onlyEditorsAreRezzers) {
if (permissionsSet[userKey]->can(NodePermissions::Permission::canAdjustLocks)) { if (permissionsSet[userKey]->can(NodePermissions::Permission::canAdjustLocks)) {
permissionsSet[userKey]->set(NodePermissions::Permission::canRezPermanentEntities); permissionsSet[userKey]->set(NodePermissions::Permission::canRezPermanentEntities);
@ -300,7 +303,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
} }
QVariantMap& DomainServerSettingsManager::getDescriptorsMap() { QVariantMap& DomainServerSettingsManager::getDescriptorsMap() {
static const QString DESCRIPTORS{ "descriptors" }; static const QString DESCRIPTORS{ "descriptors" };
auto& settingsMap = getSettingsMap(); auto& settingsMap = getSettingsMap();
@ -1355,18 +1357,12 @@ QStringList DomainServerSettingsManager::getAllKnownGroupNames() {
// extract all the group names from the group-permissions and group-forbiddens settings // extract all the group names from the group-permissions and group-forbiddens settings
QSet<QString> result; QSet<QString> result;
QHashIterator<NodePermissionsKey, NodePermissionsPointer> i(_groupPermissions.get()); for (const auto& entry : _groupPermissions.get()) {
while (i.hasNext()) { result += entry.first.first;
i.next();
NodePermissionsKey key = i.key();
result += key.first;
} }
QHashIterator<NodePermissionsKey, NodePermissionsPointer> j(_groupForbiddens.get()); for (const auto& entry : _groupForbiddens.get()) {
while (j.hasNext()) { result += entry.first.first;
j.next();
NodePermissionsKey key = j.key();
result += key.first;
} }
return result.toList(); return result.toList();
@ -1377,20 +1373,17 @@ bool DomainServerSettingsManager::setGroupID(const QString& groupName, const QUu
_groupIDs[groupName.toLower()] = groupID; _groupIDs[groupName.toLower()] = groupID;
_groupNames[groupID] = groupName; _groupNames[groupID] = groupName;
QHashIterator<NodePermissionsKey, NodePermissionsPointer> i(_groupPermissions.get());
while (i.hasNext()) { for (const auto& entry : _groupPermissions.get()) {
i.next(); auto& perms = entry.second;
NodePermissionsPointer perms = i.value();
if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) { if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) {
changed = true; changed = true;
perms->setGroupID(groupID); perms->setGroupID(groupID);
} }
} }
QHashIterator<NodePermissionsKey, NodePermissionsPointer> j(_groupForbiddens.get()); for (const auto& entry : _groupForbiddens.get()) {
while (j.hasNext()) { auto& perms = entry.second;
j.next();
NodePermissionsPointer perms = j.value();
if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) { if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) {
changed = true; changed = true;
perms->setGroupID(groupID); perms->setGroupID(groupID);

View file

@ -1,6 +1,6 @@
QPlainTextEdit { QPlainTextEdit {
font-family: Inconsolata, Lucida Console, Andale Mono, Monaco; font-family: Inconsolata, Consolas, Courier New, monospace;
font-size: 16px; font-size: 16px;
padding-left: 28px; padding-left: 28px;
padding-top: 7px; padding-top: 7px;
@ -11,7 +11,7 @@ QPlainTextEdit {
} }
QLineEdit { QLineEdit {
font-family: Inconsolata, Lucida Console, Andale Mono, Monaco; font-family: Inconsolata, Consolas, Courier New, monospace;
padding-left: 7px; padding-left: 7px;
background-color: #CCCCCC; background-color: #CCCCCC;
border-width: 0; border-width: 0;

View file

@ -20,55 +20,28 @@ using namespace render;
CauterizedMeshPartPayload::CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) CauterizedMeshPartPayload::CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform)
: ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {}
void CauterizedMeshPartPayload::updateTransformForSkinnedCauterizedMesh(const Transform& transform, void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(
const QVector<glm::mat4>& clusterMatrices, const Transform& renderTransform,
const QVector<glm::mat4>& cauterizedClusterMatrices) { const gpu::BufferPointer& buffer) {
_transform = transform; _cauterizedTransform = renderTransform;
_cauterizedTransform = transform; _cauterizedClusterBuffer = buffer;
if (clusterMatrices.size() > 0) {
_worldBound = AABox();
for (auto& clusterMatrix : clusterMatrices) {
AABox clusterBound = _localBound;
clusterBound.transform(clusterMatrix);
_worldBound += clusterBound;
}
_worldBound.transform(transform);
if (clusterMatrices.size() == 1) {
_transform = _transform.worldTransform(Transform(clusterMatrices[0]));
if (cauterizedClusterMatrices.size() != 0) {
_cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0]));
} else {
_cauterizedTransform = _transform;
}
}
} else {
_worldBound = _localBound;
_worldBound.transform(_drawTransform);
}
} }
void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const {
// Still relying on the raw data from the model // Still relying on the raw data from the model
const Model::MeshState& state = _model->getMeshState(_meshIndex);
SkeletonModel* skeleton = static_cast<SkeletonModel*>(_model); SkeletonModel* skeleton = static_cast<SkeletonModel*>(_model);
bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization(); bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization();
if (state.clusterBuffer) {
if (useCauterizedMesh) { if (useCauterizedMesh) {
const Model::MeshState& cState = skeleton->getCauterizeMeshState(_meshIndex); if (_cauterizedClusterBuffer) {
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, cState.clusterBuffer); batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _cauterizedClusterBuffer);
} else {
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer);
} }
batch.setModelTransform(_transform);
} else {
if (useCauterizedMesh) {
batch.setModelTransform(_cauterizedTransform); batch.setModelTransform(_cauterizedTransform);
} else { } else {
batch.setModelTransform(_transform); if (_clusterBuffer) {
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer);
} }
batch.setModelTransform(_transform);
} }
} }

View file

@ -17,12 +17,13 @@
class CauterizedMeshPartPayload : public ModelMeshPartPayload { class CauterizedMeshPartPayload : public ModelMeshPartPayload {
public: public:
CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
void updateTransformForSkinnedCauterizedMesh(const Transform& transform,
const QVector<glm::mat4>& clusterMatrices, void updateTransformForCauterizedMesh(const Transform& renderTransform, const gpu::BufferPointer& buffer);
const QVector<glm::mat4>& cauterizedClusterMatrices);
void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override;
private: private:
gpu::BufferPointer _cauterizedClusterBuffer;
Transform _cauterizedTransform; Transform _cauterizedTransform;
}; };

View file

@ -191,6 +191,9 @@ void CauterizedModel::updateRenderItems() {
return; return;
} }
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box.
self->updateClusterMatrices();
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
Transform modelTransform; Transform modelTransform;
@ -209,15 +212,22 @@ void CauterizedModel::updateRenderItems() {
if (data._model && data._model->isLoaded()) { if (data._model && data._model->isLoaded()) {
// Ensure the model geometry was not reset between frames // Ensure the model geometry was not reset between frames
if (deleteGeometryCounter == data._model->getGeometryCounter()) { if (deleteGeometryCounter == data._model->getGeometryCounter()) {
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. // this stuff identical to what happens in regular Model
data._model->updateClusterMatrices();
// update the model transform and bounding box for this render item.
const Model::MeshState& state = data._model->getMeshState(data._meshIndex); const Model::MeshState& state = data._model->getMeshState(data._meshIndex);
Transform renderTransform = modelTransform;
if (state.clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0]));
}
data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer);
// this stuff for cauterized mesh
CauterizedModel* cModel = static_cast<CauterizedModel*>(data._model); CauterizedModel* cModel = static_cast<CauterizedModel*>(data._model);
assert(data._meshIndex < cModel->_cauterizeMeshStates.size()); const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex);
const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex); renderTransform = modelTransform;
data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices); if (cState.clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(cState.clusterMatrices[0]));
}
data.updateTransformForCauterizedMesh(renderTransform, cState.clusterBuffer);
} }
} }
}); });

View file

@ -154,9 +154,12 @@ MyAvatar::MyAvatar(RigPointer rig) :
if (recordingInterface->getPlayFromCurrentLocation()) { if (recordingInterface->getPlayFromCurrentLocation()) {
setRecordingBasis(); setRecordingBasis();
} }
_wasCharacterControllerEnabled = _characterController.isEnabled();
_characterController.setEnabled(false);
} else { } else {
clearRecordingBasis(); clearRecordingBasis();
useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName); useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
_characterController.setEnabled(_wasCharacterControllerEnabled);
} }
auto audioIO = DependencyManager::get<AudioClient>(); auto audioIO = DependencyManager::get<AudioClient>();

View file

@ -411,6 +411,7 @@ private:
SharedSoundPointer _collisionSound; SharedSoundPointer _collisionSound;
MyCharacterController _characterController; MyCharacterController _characterController;
bool _wasCharacterControllerEnabled { true };
AvatarWeakPointer _lookAtTargetAvatar; AvatarWeakPointer _lookAtTargetAvatar;
glm::vec3 _targetAvatarPosition; glm::vec3 _targetAvatarPosition;

View file

@ -28,17 +28,23 @@ const int SEARCH_BUTTON_LEFT = 25;
const int SEARCH_BUTTON_WIDTH = 20; const int SEARCH_BUTTON_WIDTH = 20;
const int SEARCH_TOGGLE_BUTTON_WIDTH = 50; const int SEARCH_TOGGLE_BUTTON_WIDTH = 50;
const int SEARCH_TEXT_WIDTH = 240; const int SEARCH_TEXT_WIDTH = 240;
const int TIME_STAMP_LENGTH = 16;
const int FONT_WEIGHT = 75;
const QColor HIGHLIGHT_COLOR = QColor("#3366CC"); const QColor HIGHLIGHT_COLOR = QColor("#3366CC");
const QColor BOLD_COLOR = QColor("#445c8c");
const QString BOLD_PATTERN = "\\[\\d*\\/.*:\\d*:\\d*\\]";
class KeywordHighlighter : public QSyntaxHighlighter { class Highlighter : public QSyntaxHighlighter {
public: public:
KeywordHighlighter(QTextDocument* parent = nullptr); Highlighter(QTextDocument* parent = nullptr);
void setBold(int indexToBold);
QString keyword; QString keyword;
protected: protected:
void highlightBlock(const QString& text) override; void highlightBlock(const QString& text) override;
private: private:
QTextCharFormat boldFormat;
QTextCharFormat keywordFormat; QTextCharFormat keywordFormat;
}; };
@ -101,9 +107,8 @@ void BaseLogDialog::initControls() {
_logTextBox = new QPlainTextEdit(this); _logTextBox = new QPlainTextEdit(this);
_logTextBox->setReadOnly(true); _logTextBox->setReadOnly(true);
_logTextBox->show(); _logTextBox->show();
_highlighter = new KeywordHighlighter(_logTextBox->document()); _highlighter = new Highlighter(_logTextBox->document());
connect(_logTextBox, SIGNAL(selectionChanged()), SLOT(updateSelection())); connect(_logTextBox, SIGNAL(selectionChanged()), SLOT(updateSelection()));
} }
void BaseLogDialog::showEvent(QShowEvent* event) { void BaseLogDialog::showEvent(QShowEvent* event) {
@ -116,7 +121,9 @@ void BaseLogDialog::resizeEvent(QResizeEvent* event) {
void BaseLogDialog::appendLogLine(QString logLine) { void BaseLogDialog::appendLogLine(QString logLine) {
if (logLine.contains(_searchTerm, Qt::CaseInsensitive)) { if (logLine.contains(_searchTerm, Qt::CaseInsensitive)) {
int indexToBold = _logTextBox->document()->characterCount();
_logTextBox->appendPlainText(logLine.trimmed()); _logTextBox->appendPlainText(logLine.trimmed());
_highlighter->setBold(indexToBold);
} }
} }
@ -175,6 +182,7 @@ void BaseLogDialog::showLogData() {
_logTextBox->clear(); _logTextBox->clear();
_logTextBox->appendPlainText(getCurrentLog()); _logTextBox->appendPlainText(getCurrentLog());
_logTextBox->ensureCursorVisible(); _logTextBox->ensureCursorVisible();
_highlighter->rehighlight();
} }
void BaseLogDialog::updateSelection() { void BaseLogDialog::updateSelection() {
@ -187,16 +195,28 @@ void BaseLogDialog::updateSelection() {
} }
} }
KeywordHighlighter::KeywordHighlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) { Highlighter::Highlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) {
boldFormat.setFontWeight(FONT_WEIGHT);
boldFormat.setForeground(BOLD_COLOR);
keywordFormat.setForeground(HIGHLIGHT_COLOR); keywordFormat.setForeground(HIGHLIGHT_COLOR);
} }
void KeywordHighlighter::highlightBlock(const QString& text) { void Highlighter::highlightBlock(const QString& text) {
QRegExp expression(BOLD_PATTERN);
int index = text.indexOf(expression, 0);
while (index >= 0) {
int length = expression.matchedLength();
setFormat(index, length, boldFormat);
index = text.indexOf(expression, index + length);
}
if (keyword.isNull() || keyword.isEmpty()) { if (keyword.isNull() || keyword.isEmpty()) {
return; return;
} }
int index = text.indexOf(keyword, 0, Qt::CaseInsensitive); index = text.indexOf(keyword, 0, Qt::CaseInsensitive);
int length = keyword.length(); int length = keyword.length();
while (index >= 0) { while (index >= 0) {
@ -204,3 +224,7 @@ void KeywordHighlighter::highlightBlock(const QString& text) {
index = text.indexOf(keyword, index + length, Qt::CaseInsensitive); index = text.indexOf(keyword, index + length, Qt::CaseInsensitive);
} }
} }
void Highlighter::setBold(int indexToBold) {
setFormat(indexToBold, TIME_STAMP_LENGTH, boldFormat);
}

View file

@ -23,7 +23,7 @@ const int BUTTON_MARGIN = 8;
class QPushButton; class QPushButton;
class QLineEdit; class QLineEdit;
class QPlainTextEdit; class QPlainTextEdit;
class KeywordHighlighter; class Highlighter;
class BaseLogDialog : public QDialog { class BaseLogDialog : public QDialog {
Q_OBJECT Q_OBJECT
@ -56,7 +56,7 @@ private:
QPushButton* _searchPrevButton { nullptr }; QPushButton* _searchPrevButton { nullptr };
QPushButton* _searchNextButton { nullptr }; QPushButton* _searchNextButton { nullptr };
QString _searchTerm; QString _searchTerm;
KeywordHighlighter* _highlighter { nullptr }; Highlighter* _highlighter { nullptr };
void initControls(); void initControls();
void showLogData(); void showLogData();

View file

@ -45,13 +45,13 @@
#include <AudioReverb.h> #include <AudioReverb.h>
#include <AudioLimiter.h> #include <AudioLimiter.h>
#include <AudioConstants.h> #include <AudioConstants.h>
#include <AudioNoiseGate.h>
#include <shared/RateCounter.h> #include <shared/RateCounter.h>
#include <plugins/CodecPlugin.h> #include <plugins/CodecPlugin.h>
#include "AudioIOStats.h" #include "AudioIOStats.h"
#include "AudioNoiseGate.h"
#ifdef _WIN32 #ifdef _WIN32
#pragma warning( push ) #pragma warning( push )

View file

@ -1,6 +1,6 @@
// //
// AudioNoiseGate.cpp // AudioNoiseGate.cpp
// interface/src/audio // libraries/audio
// //
// Created by Stephen Birarda on 2014-12-16. // Created by Stephen Birarda on 2014-12-16.
// Copyright 2014 High Fidelity, Inc. // Copyright 2014 High Fidelity, Inc.
@ -9,29 +9,23 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "AudioNoiseGate.h"
#include <cstdlib> #include <cstdlib>
#include <string.h> #include <string.h>
#include <AudioConstants.h> #include "AudioConstants.h"
#include "AudioNoiseGate.h"
const float AudioNoiseGate::CLIPPING_THRESHOLD = 0.90f; const float AudioNoiseGate::CLIPPING_THRESHOLD = 0.90f;
AudioNoiseGate::AudioNoiseGate() : AudioNoiseGate::AudioNoiseGate() :
_inputBlockCounter(0),
_lastLoudness(0.0f), _lastLoudness(0.0f),
_quietestBlock(std::numeric_limits<float>::max()),
_loudestBlock(0.0f),
_didClipInLastBlock(false), _didClipInLastBlock(false),
_dcOffset(0.0f), _dcOffset(0.0f),
_measuredFloor(0.0f), _measuredFloor(0.0f),
_sampleCounter(0), _sampleCounter(0),
_isOpen(false), _isOpen(false),
_blocksToClose(0) _blocksToClose(0) {}
{
}
void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) { void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) {
// //
@ -104,20 +98,6 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
_lastLoudness = fabs(loudness / numSamples); _lastLoudness = fabs(loudness / numSamples);
if (_quietestBlock > _lastLoudness) {
_quietestBlock = _lastLoudness;
}
if (_loudestBlock < _lastLoudness) {
_loudestBlock = _lastLoudness;
}
const int FRAMES_FOR_NOISE_DETECTION = 400;
if (_inputBlockCounter++ > FRAMES_FOR_NOISE_DETECTION) {
_quietestBlock = std::numeric_limits<float>::max();
_loudestBlock = 0.0f;
_inputBlockCounter = 0;
}
// If Noise Gate is enabled, check and turn the gate on and off // If Noise Gate is enabled, check and turn the gate on and off
float averageOfAllSampleBlocks = 0.0f; float averageOfAllSampleBlocks = 0.0f;
_sampleBlocks[_sampleCounter++] = _lastLoudness; _sampleBlocks[_sampleCounter++] = _lastLoudness;

View file

@ -1,6 +1,6 @@
// //
// AudioNoiseGate.h // AudioNoiseGate.h
// interface/src/audio // libraries/audio
// //
// Created by Stephen Birarda on 2014-12-16. // Created by Stephen Birarda on 2014-12-16.
// Copyright 2014 High Fidelity, Inc. // Copyright 2014 High Fidelity, Inc.
@ -33,10 +33,7 @@ public:
static const float CLIPPING_THRESHOLD; static const float CLIPPING_THRESHOLD;
private: private:
int _inputBlockCounter;
float _lastLoudness; float _lastLoudness;
float _quietestBlock;
float _loudestBlock;
bool _didClipInLastBlock; bool _didClipInLastBlock;
float _dcOffset; float _dcOffset;
float _measuredFloor; float _measuredFloor;

View file

@ -947,7 +947,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
if (_tree && !_shuttingDown && _entitiesScriptEngine) { if (_tree && !_shuttingDown && _entitiesScriptEngine) {
_entitiesScriptEngine->unloadEntityScript(entityID); _entitiesScriptEngine->unloadEntityScript(entityID, true);
} }
forceRecheckEntities(); // reset our state to force checking our inside/outsideness of entities forceRecheckEntities(); // reset our state to force checking our inside/outsideness of entities

View file

@ -13,18 +13,31 @@
#define hifi_NodePermissions_h #define hifi_NodePermissions_h
#include <memory> #include <memory>
#include <unordered_map>
#include <QString> #include <QString>
#include <QMap> #include <QMap>
#include <QVariant> #include <QVariant>
#include <QUuid> #include <QUuid>
#include <QHash>
#include <utility>
#include "GroupRank.h" #include "GroupRank.h"
class NodePermissions; class NodePermissions;
using NodePermissionsPointer = std::shared_ptr<NodePermissions>; using NodePermissionsPointer = std::shared_ptr<NodePermissions>;
using NodePermissionsKey = QPair<QString, QUuid>; // name, rankID using NodePermissionsKey = std::pair<QString, QUuid>; // name, rankID
using NodePermissionsKeyList = QList<QPair<QString, QUuid>>; using NodePermissionsKeyList = QList<QPair<QString, QUuid>>;
namespace std {
template<>
struct hash<NodePermissionsKey> {
size_t operator()(const NodePermissionsKey& key) const {
size_t result = qHash(key.first);
result <<= 32;
result |= qHash(key.second);
return result;
}
};
}
class NodePermissions { class NodePermissions {
public: public:
@ -100,27 +113,40 @@ public:
NodePermissionsMap() { } NodePermissionsMap() { }
NodePermissionsPointer& operator[](const NodePermissionsKey& key) { NodePermissionsPointer& operator[](const NodePermissionsKey& key) {
NodePermissionsKey dataKey(key.first.toLower(), key.second); NodePermissionsKey dataKey(key.first.toLower(), key.second);
if (!_data.contains(dataKey)) { if (0 == _data.count(dataKey)) {
_data[dataKey] = NodePermissionsPointer(new NodePermissions(key)); _data[dataKey] = NodePermissionsPointer(new NodePermissions(key));
} }
return _data[dataKey]; return _data[dataKey];
} }
NodePermissionsPointer operator[](const NodePermissionsKey& key) const { NodePermissionsPointer operator[](const NodePermissionsKey& key) const {
return _data.value(NodePermissionsKey(key.first.toLower(), key.second)); NodePermissionsPointer result;
auto itr = _data.find(NodePermissionsKey(key.first.toLower(), key.second));
if (_data.end() != itr) {
result = itr->second;
}
return result;
} }
bool contains(const NodePermissionsKey& key) const { bool contains(const NodePermissionsKey& key) const {
return _data.contains(NodePermissionsKey(key.first.toLower(), key.second)); return 0 != _data.count(NodePermissionsKey(key.first.toLower(), key.second));
} }
bool contains(const QString& keyFirst, QUuid keySecond) const { bool contains(const QString& keyFirst, const QUuid& keySecond) const {
return _data.contains(NodePermissionsKey(keyFirst.toLower(), keySecond)); return 0 != _data.count(NodePermissionsKey(keyFirst.toLower(), keySecond));
} }
QList<NodePermissionsKey> keys() const { return _data.keys(); }
QHash<NodePermissionsKey, NodePermissionsPointer> get() { return _data; } QList<NodePermissionsKey> keys() const {
QList<NodePermissionsKey> result;
for (const auto& entry : _data) {
result.push_back(entry.first);
}
return result;
}
const std::unordered_map<NodePermissionsKey, NodePermissionsPointer>& get() { return _data; }
void clear() { _data.clear(); } void clear() { _data.clear(); }
void remove(const NodePermissionsKey& key) { _data.remove(key); } void remove(const NodePermissionsKey& key) { _data.erase(key); }
private: private:
QHash<NodePermissionsKey, NodePermissionsPointer> _data; std::unordered_map<NodePermissionsKey, NodePermissionsPointer> _data;
}; };

View file

@ -15,6 +15,10 @@
using namespace udt; using namespace udt;
PacketQueue::PacketQueue() {
_channels.emplace_back(new std::list<PacketPointer>());
}
MessageNumber PacketQueue::getNextMessageNumber() { MessageNumber PacketQueue::getNextMessageNumber() {
static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_SIZE; static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_SIZE;
_currentMessageNumber = (_currentMessageNumber + 1) % MAX_MESSAGE_NUMBER; _currentMessageNumber = (_currentMessageNumber + 1) % MAX_MESSAGE_NUMBER;
@ -24,7 +28,7 @@ MessageNumber PacketQueue::getNextMessageNumber() {
bool PacketQueue::isEmpty() const { bool PacketQueue::isEmpty() const {
LockGuard locker(_packetsLock); LockGuard locker(_packetsLock);
// Only the main channel and it is empty // Only the main channel and it is empty
return (_channels.size() == 1) && _channels.front().empty(); return (_channels.size() == 1) && _channels.front()->empty();
} }
PacketQueue::PacketPointer PacketQueue::takePacket() { PacketQueue::PacketPointer PacketQueue::takePacket() {
@ -34,19 +38,19 @@ PacketQueue::PacketPointer PacketQueue::takePacket() {
} }
// Find next non empty channel // Find next non empty channel
if (_channels[nextIndex()].empty()) { if (_channels[nextIndex()]->empty()) {
nextIndex(); nextIndex();
} }
auto& channel = _channels[_currentIndex]; auto& channel = _channels[_currentIndex];
Q_ASSERT(!channel.empty()); Q_ASSERT(!channel->empty());
// Take front packet // Take front packet
auto packet = std::move(channel.front()); auto packet = std::move(channel->front());
channel.pop_front(); channel->pop_front();
// Remove now empty channel (Don't remove the main channel) // Remove now empty channel (Don't remove the main channel)
if (channel.empty() && _currentIndex != 0) { if (channel->empty() && _currentIndex != 0) {
channel.swap(_channels.back()); channel->swap(*_channels.back());
_channels.pop_back(); _channels.pop_back();
--_currentIndex; --_currentIndex;
} }
@ -61,7 +65,7 @@ unsigned int PacketQueue::nextIndex() {
void PacketQueue::queuePacket(PacketPointer packet) { void PacketQueue::queuePacket(PacketPointer packet) {
LockGuard locker(_packetsLock); LockGuard locker(_packetsLock);
_channels.front().push_back(std::move(packet)); _channels.front()->push_back(std::move(packet));
} }
void PacketQueue::queuePacketList(PacketListPointer packetList) { void PacketQueue::queuePacketList(PacketListPointer packetList) {
@ -70,5 +74,6 @@ void PacketQueue::queuePacketList(PacketListPointer packetList) {
} }
LockGuard locker(_packetsLock); LockGuard locker(_packetsLock);
_channels.push_back(std::move(packetList->_packets)); _channels.emplace_back(new std::list<PacketPointer>());
_channels.back()->swap(packetList->_packets);
} }

View file

@ -30,10 +30,11 @@ class PacketQueue {
using LockGuard = std::lock_guard<Mutex>; using LockGuard = std::lock_guard<Mutex>;
using PacketPointer = std::unique_ptr<Packet>; using PacketPointer = std::unique_ptr<Packet>;
using PacketListPointer = std::unique_ptr<PacketList>; using PacketListPointer = std::unique_ptr<PacketList>;
using Channel = std::list<PacketPointer>; using Channel = std::unique_ptr<std::list<PacketPointer>>;
using Channels = std::vector<Channel>; using Channels = std::vector<Channel>;
public: public:
PacketQueue();
void queuePacket(PacketPointer packet); void queuePacket(PacketPointer packet);
void queuePacketList(PacketListPointer packetList); void queuePacketList(PacketListPointer packetList);
@ -49,7 +50,7 @@ private:
MessageNumber _currentMessageNumber { 0 }; MessageNumber _currentMessageNumber { 0 };
mutable Mutex _packetsLock; // Protects the packets to be sent. mutable Mutex _packetsLock; // Protects the packets to be sent.
Channels _channels = Channels(1); // One channel per packet list + Main channel Channels _channels; // One channel per packet list + Main channel
unsigned int _currentIndex { 0 }; unsigned int _currentIndex { 0 };
}; };

View file

@ -33,6 +33,7 @@ void Deck::queueClip(ClipPointer clip, float timeOffset) {
// FIXME disabling multiple clips for now // FIXME disabling multiple clips for now
_clips.clear(); _clips.clear();
_length = 0.0f;
// if the time offset is not zero, wrap in an OffsetClip // if the time offset is not zero, wrap in an OffsetClip
if (timeOffset != 0.0f) { if (timeOffset != 0.0f) {
@ -153,8 +154,8 @@ void Deck::processFrames() {
// if doing relative movement // if doing relative movement
emit looped(); emit looped();
} else { } else {
// otherwise pause playback // otherwise stop playback
pause(); stop();
} }
return; return;
} }

View file

@ -372,19 +372,12 @@ void ModelMeshPartPayload::notifyLocationChanged() {
} }
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector<glm::mat4>& clusterMatrices) { void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform,
_transform = transform; const gpu::BufferPointer& buffer) {
_transform = renderTransform;
if (clusterMatrices.size() > 0) {
_worldBound = _adjustedLocalBound; _worldBound = _adjustedLocalBound;
_worldBound.transform(_transform); _worldBound.transform(boundTransform);
if (clusterMatrices.size() == 1) { _clusterBuffer = buffer;
_transform = _transform.worldTransform(Transform(clusterMatrices[0]));
}
} else {
_worldBound = _localBound;
_worldBound.transform(_transform);
}
} }
ItemKey ModelMeshPartPayload::getKey() const { ItemKey ModelMeshPartPayload::getKey() const {
@ -532,9 +525,8 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const {
void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const {
// Still relying on the raw data from the model // Still relying on the raw data from the model
const Model::MeshState& state = _model->getMeshState(_meshIndex); if (_clusterBuffer) {
if (state.clusterBuffer) { batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer);
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer);
} }
batch.setModelTransform(_transform); batch.setModelTransform(_transform);
} }
@ -590,8 +582,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
auto locations = args->_pipeline->locations; auto locations = args->_pipeline->locations;
assert(locations); assert(locations);
// Bind the model transform and the skinCLusterMatrices if needed
_model->updateClusterMatrices();
bindTransform(batch, locations, args->_renderMode); bindTransform(batch, locations, args->_renderMode);
//Bind the index buffer and vertex buffer and Blend shapes if needed //Bind the index buffer and vertex buffer and Blend shapes if needed

View file

@ -89,8 +89,9 @@ public:
typedef Payload::DataPointer Pointer; typedef Payload::DataPointer Pointer;
void notifyLocationChanged() override; void notifyLocationChanged() override;
void updateTransformForSkinnedMesh(const Transform& transform, void updateTransformForSkinnedMesh(const Transform& renderTransform,
const QVector<glm::mat4>& clusterMatrices); const Transform& boundTransform,
const gpu::BufferPointer& buffer);
float computeFadeAlpha() const; float computeFadeAlpha() const;
@ -108,6 +109,7 @@ public:
void computeAdjustedLocalBound(const QVector<glm::mat4>& clusterMatrices); void computeAdjustedLocalBound(const QVector<glm::mat4>& clusterMatrices);
gpu::BufferPointer _clusterBuffer;
Model* _model; Model* _model;
int _meshIndex; int _meshIndex;

View file

@ -227,6 +227,10 @@ void Model::updateRenderItems() {
return; return;
} }
// lazy update of cluster matrices used for rendering.
// We need to update them here so we can correctly update the bounding box.
self->updateClusterMatrices();
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
uint32_t deleteGeometryCounter = self->_deleteGeometryCounter; uint32_t deleteGeometryCounter = self->_deleteGeometryCounter;
@ -240,12 +244,12 @@ void Model::updateRenderItems() {
Transform modelTransform = data._model->getTransform(); Transform modelTransform = data._model->getTransform();
modelTransform.setScale(glm::vec3(1.0f)); modelTransform.setScale(glm::vec3(1.0f));
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. const Model::MeshState& state = data._model->getMeshState(data._meshIndex);
data._model->updateClusterMatrices(); Transform renderTransform = modelTransform;
if (state.clusterMatrices.size() == 1) {
// update the model transform and bounding box for this render item. renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0]));
const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); }
data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices); data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer);
} }
} }
}); });

View file

@ -541,16 +541,6 @@ void ScriptEngine::init() {
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
entityScriptingInterface->init(); entityScriptingInterface->init();
connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) {
if (_entityScripts.contains(entityID)) {
if (isEntityScriptRunning(entityID)) {
qCWarning(scriptengine) << "deletingEntity while entity script is still running!" << entityID;
}
_entityScripts.remove(entityID);
emit entityScriptDetailsUpdated();
}
});
// register various meta-types // register various meta-types
registerMetaTypes(this); registerMetaTypes(this);
@ -1844,7 +1834,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
processDeferredEntityLoads(entityScript, entityID); processDeferredEntityLoads(entityScript, entityID);
} }
void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
@ -1852,7 +1842,8 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
#endif #endif
QMetaObject::invokeMethod(this, "unloadEntityScript", QMetaObject::invokeMethod(this, "unloadEntityScript",
Q_ARG(const EntityItemID&, entityID)); Q_ARG(const EntityItemID&, entityID),
Q_ARG(bool, shouldRemoveFromMap));
return; return;
} }
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
@ -1867,7 +1858,12 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
} else { } else {
qCDebug(scriptengine) << "unload called while !running" << entityID << oldDetails.status; qCDebug(scriptengine) << "unload called while !running" << entityID << oldDetails.status;
} }
if (oldDetails.status != EntityScriptStatus::UNLOADED) {
if (shouldRemoveFromMap) {
// this was a deleted entity, we've been asked to remove it from the map
_entityScripts.remove(entityID);
emit entityScriptDetailsUpdated();
} else if (oldDetails.status != EntityScriptStatus::UNLOADED) {
EntityScriptDetails newDetails; EntityScriptDetails newDetails;
newDetails.status = EntityScriptStatus::UNLOADED; newDetails.status = EntityScriptStatus::UNLOADED;
newDetails.lastModified = QDateTime::currentMSecsSinceEpoch(); newDetails.lastModified = QDateTime::currentMSecsSinceEpoch();
@ -1875,6 +1871,7 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
newDetails.scriptText = oldDetails.scriptText; newDetails.scriptText = oldDetails.scriptText;
setEntityScriptDetails(entityID, newDetails); setEntityScriptDetails(entityID, newDetails);
} }
stopAllTimersForEntityScript(entityID); stopAllTimersForEntityScript(entityID);
{ {
// FIXME: shouldn't have to do this here, but currently something seems to be firing unloads moments after firing initial load requests // FIXME: shouldn't have to do this here, but currently something seems to be firing unloads moments after firing initial load requests

View file

@ -171,7 +171,7 @@ public:
return _entityScripts.contains(entityID) && _entityScripts[entityID].status == EntityScriptStatus::RUNNING; return _entityScripts.contains(entityID) && _entityScripts[entityID].status == EntityScriptStatus::RUNNING;
} }
Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload); Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload);
Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap = false); // will call unload method
Q_INVOKABLE void unloadAllEntityScripts(); Q_INVOKABLE void unloadAllEntityScripts();
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName,
const QStringList& params = QStringList()) override; const QStringList& params = QStringList()) override;

View file

@ -9,12 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
/* globals HIFI_PUBLIC_BUCKET:true, Tool, ToolBar */
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
Script.include("/~/system/libraries/toolBars.js"); Script.include("/~/system/libraries/toolBars.js");
var recordingFile = "recording.hfr"; var recordingFile = "recording.hfr";
function setPlayerOptions() { function setDefaultPlayerOptions() {
Recording.setPlayFromCurrentLocation(true); Recording.setPlayFromCurrentLocation(true);
Recording.setPlayerUseDisplayName(false); Recording.setPlayerUseDisplayName(false);
Recording.setPlayerUseAttachments(false); Recording.setPlayerUseAttachments(false);
@ -38,16 +40,16 @@ var saveIcon;
var loadIcon; var loadIcon;
var spacing; var spacing;
var timerOffset; var timerOffset;
setupToolBar();
var timer = null; var timer = null;
var slider = null; var slider = null;
setupToolBar();
setupTimer(); setupTimer();
var watchStop = false; var watchStop = false;
function setupToolBar() { function setupToolBar() {
if (toolBar != null) { if (toolBar !== null) {
print("Multiple calls to Recorder.js:setupToolBar()"); print("Multiple calls to Recorder.js:setupToolBar()");
return; return;
} }
@ -56,6 +58,8 @@ function setupToolBar() {
toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
toolBar.onMove = onToolbarMove;
toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF); toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF);
recordIcon = toolBar.addTool({ recordIcon = toolBar.addTool({
@ -86,7 +90,7 @@ function setupToolBar() {
visible: true visible: true
}, false); }, false);
timerOffset = toolBar.width; timerOffset = toolBar.width + ToolBar.SPACING;
spacing = toolBar.addSpacing(0); spacing = toolBar.addSpacing(0);
saveIcon = toolBar.addTool({ saveIcon = toolBar.addTool({
@ -112,15 +116,15 @@ function setupTimer() {
text: (0.00).toFixed(3), text: (0.00).toFixed(3),
backgroundColor: COLOR_OFF, backgroundColor: COLOR_OFF,
x: 0, y: 0, x: 0, y: 0,
width: 0, height: 0, width: 200, height: 25,
leftMargin: 10, topMargin: 10, leftMargin: 5, topMargin: 3,
alpha: 1.0, backgroundAlpha: 1.0, alpha: 1.0, backgroundAlpha: 1.0,
visible: true visible: true
}); });
slider = { x: 0, y: 0, slider = { x: 0, y: 0,
w: 200, h: 20, w: 200, h: 20,
pos: 0.0, // 0.0 <= pos <= 1.0 pos: 0.0 // 0.0 <= pos <= 1.0
}; };
slider.background = Overlays.addOverlay("text", { slider.background = Overlays.addOverlay("text", {
text: "", text: "",
@ -144,20 +148,40 @@ function setupTimer() {
}); });
} }
function onToolbarMove(newX, newY, deltaX, deltaY) {
Overlays.editOverlay(timer, {
x: newX + timerOffset - ToolBar.SPACING,
y: newY
});
slider.x = newX - ToolBar.SPACING;
slider.y = newY - slider.h - ToolBar.SPACING;
Overlays.editOverlay(slider.background, {
x: slider.x,
y: slider.y
});
Overlays.editOverlay(slider.foreground, {
x: slider.x,
y: slider.y
});
}
function updateTimer() { function updateTimer() {
var text = ""; var text = "";
if (Recording.isRecording()) { if (Recording.isRecording()) {
text = formatTime(Recording.recorderElapsed()); text = formatTime(Recording.recorderElapsed());
} else { } else {
text = formatTime(Recording.playerElapsed()) + " / " + text = formatTime(Recording.playerElapsed()) + " / " + formatTime(Recording.playerLength());
formatTime(Recording.playerLength());
} }
var timerWidth = text.length * 8 + ((Recording.isRecording()) ? 15 : 0);
Overlays.editOverlay(timer, { Overlays.editOverlay(timer, {
text: text text: text,
}) width: timerWidth
toolBar.changeSpacing(text.length * 8 + ((Recording.isRecording()) ? 15 : 0), spacing); });
toolBar.changeSpacing(timerWidth + ToolBar.SPACING, spacing);
if (Recording.isRecording()) { if (Recording.isRecording()) {
slider.pos = 1.0; slider.pos = 1.0;
@ -173,7 +197,7 @@ function updateTimer() {
function formatTime(time) { function formatTime(time) {
var MIN_PER_HOUR = 60; var MIN_PER_HOUR = 60;
var SEC_PER_MIN = 60; var SEC_PER_MIN = 60;
var MSEC_PER_SEC = 1000; var MSEC_DIGITS = 3;
var hours = Math.floor(time / (SEC_PER_MIN * MIN_PER_HOUR)); var hours = Math.floor(time / (SEC_PER_MIN * MIN_PER_HOUR));
time -= hours * (SEC_PER_MIN * MIN_PER_HOUR); time -= hours * (SEC_PER_MIN * MIN_PER_HOUR);
@ -184,37 +208,19 @@ function formatTime(time) {
var seconds = time; var seconds = time;
var text = ""; var text = "";
text += (hours > 0) ? hours + ":" : text += (hours > 0) ? hours + ":" : "";
""; text += (minutes > 0) ? ((minutes < 10 && text !== "") ? "0" : "") + minutes + ":" : "";
text += (minutes > 0) ? ((minutes < 10 && text != "") ? "0" : "") + minutes + ":" : text += ((seconds < 10 && text !== "") ? "0" : "") + seconds.toFixed(MSEC_DIGITS);
"";
text += ((seconds < 10 && text != "") ? "0" : "") + seconds.toFixed(3);
return text; return text;
} }
function moveUI() { function moveUI() {
var relative = { x: 70, y: 40 }; var relative = { x: 70, y: 40 };
toolBar.move(relative.x, windowDimensions.y - relative.y); toolBar.move(relative.x, windowDimensions.y - relative.y);
Overlays.editOverlay(timer, {
x: relative.x + timerOffset - ToolBar.SPACING,
y: windowDimensions.y - relative.y - ToolBar.SPACING
});
slider.x = relative.x - ToolBar.SPACING;
slider.y = windowDimensions.y - relative.y - slider.h - ToolBar.SPACING;
Overlays.editOverlay(slider.background, {
x: slider.x,
y: slider.y,
});
Overlays.editOverlay(slider.foreground, {
x: slider.x,
y: slider.y,
});
} }
function mousePressEvent(event) { function mousePressEvent(event) {
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
if (recordIcon === toolBar.clicked(clickedOverlay, false) && !Recording.isPlaying()) { if (recordIcon === toolBar.clicked(clickedOverlay, false) && !Recording.isPlaying()) {
if (!Recording.isRecording()) { if (!Recording.isRecording()) {
@ -226,7 +232,11 @@ function mousePressEvent(event) {
toolBar.setAlpha(ALPHA_OFF, loadIcon); toolBar.setAlpha(ALPHA_OFF, loadIcon);
} else { } else {
Recording.stopRecording(); Recording.stopRecording();
toolBar.selectTool(recordIcon, true ); toolBar.selectTool(recordIcon, true);
setDefaultPlayerOptions();
// Plays the recording at the same spot as you recorded it
Recording.setPlayFromCurrentLocation(false);
Recording.setPlayerTime(0);
Recording.loadLastRecording(); Recording.loadLastRecording();
toolBar.setAlpha(ALPHA_ON, playIcon); toolBar.setAlpha(ALPHA_ON, playIcon);
toolBar.setAlpha(ALPHA_ON, playLoopIcon); toolBar.setAlpha(ALPHA_ON, playLoopIcon);
@ -240,7 +250,6 @@ function mousePressEvent(event) {
toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, saveIcon);
toolBar.setAlpha(ALPHA_ON, loadIcon); toolBar.setAlpha(ALPHA_ON, loadIcon);
} else if (Recording.playerLength() > 0) { } else if (Recording.playerLength() > 0) {
setPlayerOptions();
Recording.setPlayerLoop(false); Recording.setPlayerLoop(false);
Recording.startPlaying(); Recording.startPlaying();
toolBar.setAlpha(ALPHA_OFF, recordIcon); toolBar.setAlpha(ALPHA_OFF, recordIcon);
@ -255,7 +264,6 @@ function mousePressEvent(event) {
toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, saveIcon);
toolBar.setAlpha(ALPHA_ON, loadIcon); toolBar.setAlpha(ALPHA_ON, loadIcon);
} else if (Recording.playerLength() > 0) { } else if (Recording.playerLength() > 0) {
setPlayerOptions();
Recording.setPlayerLoop(true); Recording.setPlayerLoop(true);
Recording.startPlaying(); Recording.startPlaying();
toolBar.setAlpha(ALPHA_OFF, recordIcon); toolBar.setAlpha(ALPHA_OFF, recordIcon);
@ -263,7 +271,7 @@ function mousePressEvent(event) {
toolBar.setAlpha(ALPHA_OFF, loadIcon); toolBar.setAlpha(ALPHA_OFF, loadIcon);
} }
} else if (saveIcon === toolBar.clicked(clickedOverlay)) { } else if (saveIcon === toolBar.clicked(clickedOverlay)) {
if (!Recording.isRecording() && !Recording.isPlaying() && Recording.playerLength() != 0) { if (!Recording.isRecording() && !Recording.isPlaying() && Recording.playerLength() !== 0) {
recordingFile = Window.save("Save recording to file", ".", "Recordings (*.hfr)"); recordingFile = Window.save("Save recording to file", ".", "Recordings (*.hfr)");
if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) {
Recording.saveRecording(recordingFile); Recording.saveRecording(recordingFile);
@ -274,6 +282,7 @@ function mousePressEvent(event) {
recordingFile = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); recordingFile = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)");
if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) {
Recording.loadRecording(recordingFile); Recording.loadRecording(recordingFile);
setDefaultPlayerOptions();
} }
if (Recording.playerLength() > 0) { if (Recording.playerLength() > 0) {
toolBar.setAlpha(ALPHA_ON, playIcon); toolBar.setAlpha(ALPHA_ON, playIcon);
@ -308,7 +317,7 @@ function mouseReleaseEvent(event) {
function update() { function update() {
var newDimensions = Controller.getViewportDimensions(); var newDimensions = Controller.getViewportDimensions();
if (windowDimensions.x != newDimensions.x || windowDimensions.y != newDimensions.y) { if (windowDimensions.x !== newDimensions.x || windowDimensions.y !== newDimensions.y) {
windowDimensions = newDimensions; windowDimensions = newDimensions;
moveUI(); moveUI();
} }

View file

@ -160,6 +160,7 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
visible: false visible: false
}); });
this.spacing = []; this.spacing = [];
this.onMove = null;
this.addTool = function(properties, selectable, selected) { this.addTool = function(properties, selectable, selected) {
if (direction == ToolBar.HORIZONTAL) { if (direction == ToolBar.HORIZONTAL) {
@ -254,6 +255,9 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
y: y - ToolBar.SPACING y: y - ToolBar.SPACING
}); });
} }
if (this.onMove !== null) {
this.onMove(x, y, dx, dy);
};
} }
this.setAlpha = function(alpha, tool) { this.setAlpha = function(alpha, tool) {

View file

@ -2,7 +2,6 @@
Script.include("/~/system/libraries/utils.js"); Script.include("/~/system/libraries/utils.js");
var SETTING_KEY = "com.highfidelity.avatar.isSitting"; var SETTING_KEY = "com.highfidelity.avatar.isSitting";
var ROLE = "fly";
var ANIMATION_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/animations/sitting_idle.fbx"; var ANIMATION_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/animations/sitting_idle.fbx";
var ANIMATION_FPS = 30; var ANIMATION_FPS = 30;
var ANIMATION_FIRST_FRAME = 1; var ANIMATION_FIRST_FRAME = 1;
@ -10,23 +9,25 @@
var RELEASE_KEYS = ['w', 'a', 's', 'd', 'UP', 'LEFT', 'DOWN', 'RIGHT']; var RELEASE_KEYS = ['w', 'a', 's', 'd', 'UP', 'LEFT', 'DOWN', 'RIGHT'];
var RELEASE_TIME = 500; // ms var RELEASE_TIME = 500; // ms
var RELEASE_DISTANCE = 0.2; // meters var RELEASE_DISTANCE = 0.2; // meters
var MAX_IK_ERROR = 20; var MAX_IK_ERROR = 30;
var DESKTOP_UI_CHECK_INTERVAL = 250; var DESKTOP_UI_CHECK_INTERVAL = 100;
var DESKTOP_MAX_DISTANCE = 5; var DESKTOP_MAX_DISTANCE = 5;
var SIT_DELAY = 25 var SIT_DELAY = 25
var MAX_RESET_DISTANCE = 0.5
this.entityID = null; this.entityID = null;
this.timers = {}; this.timers = {};
this.animStateHandlerID = null; this.animStateHandlerID = null;
this.interval = null;
this.preload = function(entityID) { this.preload = function(entityID) {
this.entityID = entityID; this.entityID = entityID;
} }
this.unload = function() { this.unload = function() {
if (MyAvatar.sessionUUID === this.getSeatUser()) { if (Settings.getValue(SETTING_KEY) === this.entityID) {
this.sitUp(this.entityID); this.sitUp();
} }
if (this.interval) { if (this.interval !== null) {
Script.clearInterval(this.interval); Script.clearInterval(this.interval);
this.interval = null; this.interval = null;
} }
@ -34,10 +35,11 @@
} }
this.setSeatUser = function(user) { this.setSeatUser = function(user) {
try {
var userData = Entities.getEntityProperties(this.entityID, ["userData"]).userData; var userData = Entities.getEntityProperties(this.entityID, ["userData"]).userData;
userData = JSON.parse(userData); userData = JSON.parse(userData);
if (user) { if (user !== null) {
userData.seat.user = user; userData.seat.user = user;
} else { } else {
delete userData.seat.user; delete userData.seat.user;
@ -46,30 +48,47 @@
Entities.editEntity(this.entityID, { Entities.editEntity(this.entityID, {
userData: JSON.stringify(userData) userData: JSON.stringify(userData)
}); });
} catch (e) {
// Do Nothing
}
} }
this.getSeatUser = function() { this.getSeatUser = function() {
try {
var properties = Entities.getEntityProperties(this.entityID, ["userData", "position"]); var properties = Entities.getEntityProperties(this.entityID, ["userData", "position"]);
var userData = JSON.parse(properties.userData); var userData = JSON.parse(properties.userData);
if (userData.seat.user && userData.seat.user !== MyAvatar.sessionUUID) { // If MyAvatar return my uuid
var avatar = AvatarList.getAvatar(userData.seat.user); if (userData.seat.user === MyAvatar.sessionUUID) {
if (avatar && Vec3.distance(avatar.position, properties.position) > RELEASE_DISTANCE) {
return null;
}
}
return userData.seat.user; return userData.seat.user;
} }
// If Avatar appears to be sitting
if (userData.seat.user) {
var avatar = AvatarList.getAvatar(userData.seat.user);
if (avatar && (Vec3.distance(avatar.position, properties.position) < RELEASE_DISTANCE)) {
return userData.seat.user;
}
}
} catch (e) {
// Do nothing
}
// Nobody on the seat
return null;
}
// Is the seat used
this.checkSeatForAvatar = function() { this.checkSeatForAvatar = function() {
var seatUser = this.getSeatUser(); var seatUser = this.getSeatUser();
var avatarIdentifiers = AvatarList.getAvatarIdentifiers();
for (var i in avatarIdentifiers) { // If MyAvatar appears to be sitting
var avatar = AvatarList.getAvatar(avatarIdentifiers[i]); if (seatUser === MyAvatar.sessionUUID) {
if (avatar && avatar.sessionUUID === seatUser) { var properties = Entities.getEntityProperties(this.entityID, ["position"]);
return true; return Vec3.distance(MyAvatar.position, properties.position) < RELEASE_DISTANCE;
} }
}
return false; return seatUser !== null;
} }
this.sitDown = function() { this.sitDown = function() {
@ -78,40 +97,50 @@
return; return;
} }
this.setSeatUser(MyAvatar.sessionUUID);
var previousValue = Settings.getValue(SETTING_KEY); var previousValue = Settings.getValue(SETTING_KEY);
Settings.setValue(SETTING_KEY, this.entityID); Settings.setValue(SETTING_KEY, this.entityID);
this.setSeatUser(MyAvatar.sessionUUID);
if (previousValue === "") { if (previousValue === "") {
MyAvatar.characterControllerEnabled = false; MyAvatar.characterControllerEnabled = false;
MyAvatar.hmdLeanRecenterEnabled = false; MyAvatar.hmdLeanRecenterEnabled = false;
MyAvatar.overrideRoleAnimation(ROLE, ANIMATION_URL, ANIMATION_FPS, true, ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME); var ROLES = MyAvatar.getAnimationRoles();
for (i in ROLES) {
MyAvatar.overrideRoleAnimation(ROLES[i], ANIMATION_URL, ANIMATION_FPS, true, ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME);
}
MyAvatar.resetSensorsAndBody(); MyAvatar.resetSensorsAndBody();
} }
var that = this; var properties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
Script.setTimeout(function() {
var properties = Entities.getEntityProperties(that.entityID, ["position", "rotation"]);
var index = MyAvatar.getJointIndex("Hips"); var index = MyAvatar.getJointIndex("Hips");
MyAvatar.pinJoint(index, properties.position, properties.rotation); MyAvatar.pinJoint(index, properties.position, properties.rotation);
that.animStateHandlerID = MyAvatar.addAnimationStateHandler(function(properties) { this.animStateHandlerID = MyAvatar.addAnimationStateHandler(function(properties) {
return { headType: 0 }; return { headType: 0 };
}, ["headType"]); }, ["headType"]);
Script.update.connect(that, that.update); Script.update.connect(this, this.update);
Controller.keyPressEvent.connect(that, that.keyPressed); Controller.keyPressEvent.connect(this, this.keyPressed);
Controller.keyReleaseEvent.connect(that, that.keyReleased); Controller.keyReleaseEvent.connect(this, this.keyReleased);
for (var i in RELEASE_KEYS) { for (var i in RELEASE_KEYS) {
Controller.captureKeyEvents({ text: RELEASE_KEYS[i] }); Controller.captureKeyEvents({ text: RELEASE_KEYS[i] });
} }
}, SIT_DELAY);
} }
this.sitUp = function() { this.sitUp = function() {
this.setSeatUser(null); MyAvatar.removeAnimationStateHandler(this.animStateHandlerID);
Script.update.disconnect(this, this.update);
Controller.keyPressEvent.disconnect(this, this.keyPressed);
Controller.keyReleaseEvent.disconnect(this, this.keyReleased);
for (var i in RELEASE_KEYS) {
Controller.releaseKeyEvents({ text: RELEASE_KEYS[i] });
}
this.setSeatUser(null);
if (Settings.getValue(SETTING_KEY) === this.entityID) { if (Settings.getValue(SETTING_KEY) === this.entityID) {
MyAvatar.restoreRoleAnimation(ROLE); Settings.setValue(SETTING_KEY, "");
var ROLES = MyAvatar.getAnimationRoles();
for (i in ROLES) {
MyAvatar.restoreRoleAnimation(ROLES[i]);
}
MyAvatar.characterControllerEnabled = true; MyAvatar.characterControllerEnabled = true;
MyAvatar.hmdLeanRecenterEnabled = true; MyAvatar.hmdLeanRecenterEnabled = true;
@ -124,16 +153,6 @@
MyAvatar.bodyPitch = 0.0; MyAvatar.bodyPitch = 0.0;
MyAvatar.bodyRoll = 0.0; MyAvatar.bodyRoll = 0.0;
}, SIT_DELAY); }, SIT_DELAY);
Settings.setValue(SETTING_KEY, "");
}
MyAvatar.removeAnimationStateHandler(this.animStateHandlerID);
Script.update.disconnect(this, this.update);
Controller.keyPressEvent.disconnect(this, this.keyPressed);
Controller.keyReleaseEvent.disconnect(this, this.keyReleased);
for (var i in RELEASE_KEYS) {
Controller.releaseKeyEvents({ text: RELEASE_KEYS[i] });
} }
} }
@ -183,15 +202,22 @@
} }
} }
this.update = function(dt) { this.update = function(dt) {
if (MyAvatar.sessionUUID === this.getSeatUser()) { if (MyAvatar.sessionUUID === this.getSeatUser()) {
var properties = Entities.getEntityProperties(this.entityID, ["position"]); var properties = Entities.getEntityProperties(this.entityID);
var avatarDistance = Vec3.distance(MyAvatar.position, properties.position); var avatarDistance = Vec3.distance(MyAvatar.position, properties.position);
var ikError = MyAvatar.getIKErrorOnLastSolve(); var ikError = MyAvatar.getIKErrorOnLastSolve();
if (avatarDistance > RELEASE_DISTANCE || ikError > MAX_IK_ERROR) { if (avatarDistance > RELEASE_DISTANCE || ikError > MAX_IK_ERROR) {
print("IK error: " + ikError + ", distance from chair: " + avatarDistance); print("IK error: " + ikError + ", distance from chair: " + avatarDistance);
this.sitUp(this.entityID);
// Move avatar in front of the chair to avoid getting stuck in collision hulls
if (avatarDistance < MAX_RESET_DISTANCE) {
var offset = { x: 0, y: 1.0, z: -0.5 - properties.dimensions.z * properties.registrationPoint.z };
var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset));
MyAvatar.position = position;
}
this.sitUp();
} }
} }
} }
@ -203,6 +229,18 @@
if (RELEASE_KEYS.indexOf(event.text) !== -1) { if (RELEASE_KEYS.indexOf(event.text) !== -1) {
var that = this; var that = this;
this.timers[event.text] = Script.setTimeout(function() { this.timers[event.text] = Script.setTimeout(function() {
delete that.timers[event.text];
var properties = Entities.getEntityProperties(that.entityID);
var avatarDistance = Vec3.distance(MyAvatar.position, properties.position);
// Move avatar in front of the chair to avoid getting stuck in collision hulls
if (avatarDistance < MAX_RESET_DISTANCE) {
var offset = { x: 0, y: 1.0, z: -0.5 - properties.dimensions.z * properties.registrationPoint.z };
var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset));
MyAvatar.position = position;
}
that.sitUp(); that.sitUp();
}, RELEASE_TIME); }, RELEASE_TIME);
} }
@ -223,7 +261,7 @@
} }
this.hoverEnterEntity = function(event) { this.hoverEnterEntity = function(event) {
if (isInEditMode() || (MyAvatar.sessionUUID === this.getSeatUser())) { if (isInEditMode() || this.interval !== null) {
return; return;
} }
@ -239,18 +277,18 @@
}, DESKTOP_UI_CHECK_INTERVAL); }, DESKTOP_UI_CHECK_INTERVAL);
} }
this.hoverLeaveEntity = function(event) { this.hoverLeaveEntity = function(event) {
if (this.interval) { if (this.interval !== null) {
Script.clearInterval(this.interval); Script.clearInterval(this.interval);
this.interval = null; this.interval = null;
} }
this.cleanupOverlay(); this.cleanupOverlay();
} }
this.clickDownOnEntity = function () { this.clickDownOnEntity = function (id, event) {
if (isInEditMode() || (MyAvatar.sessionUUID === this.getSeatUser())) { if (isInEditMode()) {
return; return;
} }
if (this.canSitDesktop()) { if (event.isPrimaryButton && this.canSitDesktop()) {
this.sitDown(); this.sitDown();
} }
} }

View file

@ -17,3 +17,5 @@ set_target_properties(ac-client PROPERTIES FOLDER "Tools")
add_subdirectory(skeleton-dump) add_subdirectory(skeleton-dump)
set_target_properties(skeleton-dump PROPERTIES FOLDER "Tools") set_target_properties(skeleton-dump PROPERTIES FOLDER "Tools")
add_subdirectory(atp-get)
set_target_properties(atp-get PROPERTIES FOLDER "Tools")

View file

@ -0,0 +1,3 @@
set(TARGET_NAME atp-get)
setup_hifi_project(Core Widgets)
link_hifi_libraries(shared networking)

View file

@ -0,0 +1,269 @@
//
// ATPGetApp.cpp
// tools/atp-get/src
//
// Created by Seth Alves on 2017-3-15
// 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 <QDataStream>
#include <QTextStream>
#include <QThread>
#include <QFile>
#include <QLoggingCategory>
#include <QCommandLineParser>
#include <NetworkLogging.h>
#include <SharedLogging.h>
#include <AddressManager.h>
#include <DependencyManager.h>
#include <SettingHandle.h>
#include "ATPGetApp.h"
ATPGetApp::ATPGetApp(int argc, char* argv[]) :
QCoreApplication(argc, argv)
{
// parse command-line
QCommandLineParser parser;
parser.setApplicationDescription("High Fidelity ATP-Get");
const QCommandLineOption helpOption = parser.addHelpOption();
const QCommandLineOption verboseOutput("v", "verbose output");
parser.addOption(verboseOutput);
const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1");
parser.addOption(domainAddressOption);
const QCommandLineOption cacheSTUNOption("s", "cache stun-server response");
parser.addOption(cacheSTUNOption);
const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT));
parser.addOption(listenPortOption);
if (!parser.parse(QCoreApplication::arguments())) {
qCritical() << parser.errorText() << endl;
parser.showHelp();
Q_UNREACHABLE();
}
if (parser.isSet(helpOption)) {
parser.showHelp();
Q_UNREACHABLE();
}
_verbose = parser.isSet(verboseOutput);
if (!_verbose) {
QLoggingCategory::setFilterRules("qt.network.ssl.warning=false");
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtDebugMsg, false);
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtInfoMsg, false);
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtWarningMsg, false);
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtDebugMsg, false);
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtInfoMsg, false);
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtWarningMsg, false);
}
QStringList filenames = parser.positionalArguments();
if (filenames.empty() || filenames.size() > 2) {
qDebug() << "give remote url and optional local filename as arguments";
parser.showHelp();
Q_UNREACHABLE();
}
_url = QUrl(filenames[0]);
if (_url.scheme() != "atp") {
qDebug() << "url should start with atp:";
parser.showHelp();
Q_UNREACHABLE();
}
if (filenames.size() == 2) {
_localOutputFile = filenames[1];
}
QString domainServerAddress = "127.0.0.1:40103";
if (parser.isSet(domainAddressOption)) {
domainServerAddress = parser.value(domainAddressOption);
}
if (_verbose) {
qDebug() << "domain-server address is" << domainServerAddress;
}
int listenPort = INVALID_PORT;
if (parser.isSet(listenPortOption)) {
listenPort = parser.value(listenPortOption).toInt();
}
Setting::init();
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
DependencyManager::set<AccountManager>([&]{ return QString("Mozilla/5.0 (HighFidelityATPGet)"); });
DependencyManager::set<AddressManager>();
DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
auto nodeList = DependencyManager::get<NodeList>();
// start the nodeThread so its event loop is running
QThread* nodeThread = new QThread(this);
nodeThread->setObjectName("NodeList Thread");
nodeThread->start();
// make sure the node thread is given highest priority
nodeThread->setPriority(QThread::TimeCriticalPriority);
// setup a timer for domain-server check ins
QTimer* domainCheckInTimer = new QTimer(nodeList.data());
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
// put the NodeList and datagram processing on the node thread
nodeList->moveToThread(nodeThread);
const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
// connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
// connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ATPGetApp::domainConnectionRefused);
connect(nodeList.data(), &NodeList::nodeAdded, this, &ATPGetApp::nodeAdded);
connect(nodeList.data(), &NodeList::nodeKilled, this, &ATPGetApp::nodeKilled);
connect(nodeList.data(), &NodeList::nodeActivated, this, &ATPGetApp::nodeActivated);
// connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID);
// connect(nodeList.data(), &NodeList::uuidChanged, this, &ATPGetApp::setSessionUUID);
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ATPGetApp::notifyPacketVersionMismatch);
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer);
DependencyManager::get<AddressManager>()->handleLookupString(domainServerAddress, false);
auto assetClient = DependencyManager::set<AssetClient>();
assetClient->init();
QTimer* doTimer = new QTimer(this);
doTimer->setSingleShot(true);
connect(doTimer, &QTimer::timeout, this, &ATPGetApp::timedOut);
doTimer->start(4000);
}
ATPGetApp::~ATPGetApp() {
}
void ATPGetApp::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
qDebug() << "domainConnectionRefused";
}
void ATPGetApp::domainChanged(const QString& domainHostname) {
if (_verbose) {
qDebug() << "domainChanged";
}
}
void ATPGetApp::nodeAdded(SharedNodePointer node) {
if (_verbose) {
qDebug() << "node added: " << node->getType();
}
}
void ATPGetApp::nodeActivated(SharedNodePointer node) {
if (node->getType() == NodeType::AssetServer) {
lookup();
}
}
void ATPGetApp::nodeKilled(SharedNodePointer node) {
qDebug() << "nodeKilled";
}
void ATPGetApp::timedOut() {
finish(1);
}
void ATPGetApp::notifyPacketVersionMismatch() {
if (_verbose) {
qDebug() << "packet version mismatch";
}
finish(1);
}
void ATPGetApp::lookup() {
auto path = _url.path();
qDebug() << "path is " << path;
auto request = DependencyManager::get<AssetClient>()->createGetMappingRequest(path);
QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable {
auto result = request->getError();
if (result == GetMappingRequest::NotFound) {
qDebug() << "not found";
} else if (result == GetMappingRequest::NoError) {
qDebug() << "found, hash is " << request->getHash();
download(request->getHash());
} else {
qDebug() << "error -- " << request->getError() << " -- " << request->getErrorString();
}
request->deleteLater();
});
request->start();
}
void ATPGetApp::download(AssetHash hash) {
auto assetClient = DependencyManager::get<AssetClient>();
auto assetRequest = new AssetRequest(hash);
connect(assetRequest, &AssetRequest::finished, this, [this](AssetRequest* request) mutable {
Q_ASSERT(request->getState() == AssetRequest::Finished);
if (request->getError() == AssetRequest::Error::NoError) {
QString data = QString::fromUtf8(request->getData());
if (_localOutputFile == "") {
QTextStream cout(stdout);
cout << data;
} else {
QFile outputHandle(_localOutputFile);
if (outputHandle.open(QIODevice::ReadWrite)) {
QTextStream stream( &outputHandle );
stream << data;
} else {
qDebug() << "couldn't open output file:" << _localOutputFile;
}
}
}
request->deleteLater();
finish(0);
});
assetRequest->start();
}
void ATPGetApp::finish(int exitCode) {
auto nodeList = DependencyManager::get<NodeList>();
// send the domain a disconnect packet, force stoppage of domain-server check-ins
nodeList->getDomainHandler().disconnect();
nodeList->setIsShuttingDown(true);
// tell the packet receiver we're shutting down, so it can drop packets
nodeList->getPacketReceiver().setShouldDropPackets(true);
QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
// remove the NodeList from the DependencyManager
DependencyManager::destroy<NodeList>();
// ask the node thread to quit and wait until it is done
nodeThread->quit();
nodeThread->wait();
QCoreApplication::exit(exitCode);
}

View file

@ -0,0 +1,52 @@
//
// ATPGetApp.h
// tools/atp-get/src
//
// Created by Seth Alves on 2017-3-15
// 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_ATPGetApp_h
#define hifi_ATPGetApp_h
#include <QApplication>
#include <udt/Constants.h>
#include <udt/Socket.h>
#include <ReceivedMessage.h>
#include <NetworkPeer.h>
#include <NodeList.h>
#include <AssetRequest.h>
#include <MappingRequest.h>
class ATPGetApp : public QCoreApplication {
Q_OBJECT
public:
ATPGetApp(int argc, char* argv[]);
~ATPGetApp();
private slots:
void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo);
void domainChanged(const QString& domainHostname);
void nodeAdded(SharedNodePointer node);
void nodeActivated(SharedNodePointer node);
void nodeKilled(SharedNodePointer node);
void notifyPacketVersionMismatch();
private:
NodeList* _nodeList;
void timedOut();
void lookup();
void download(AssetHash hash);
void finish(int exitCode);
bool _verbose;
QUrl _url;
QString _localOutputFile;
};
#endif // hifi_ATPGetApp_h

View file

@ -0,0 +1,31 @@
//
// main.cpp
// tools/atp-get/src
//
// Created by Seth Alves on 2017-3-15
// 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 <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <BuildInfo.h>
#include "ATPGetApp.h"
using namespace std;
int main(int argc, char * argv[]) {
QCoreApplication::setApplicationName(BuildInfo::AC_CLIENT_SERVER_NAME);
QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
ATPGetApp app(argc, argv);
return app.exec();
}