Merge branch 'tablet-ui' into tablet-ui-login-logout

This commit is contained in:
Vladyslav Stelmakhovskyi 2017-03-18 14:42:11 +01:00
commit 696825580e
48 changed files with 979 additions and 519 deletions

View file

@ -371,25 +371,39 @@ void Agent::executeScript() {
using namespace recording;
static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::getAudioFrameName());
Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) {
const QByteArray& audio = frame->data;
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();
audioTransform.setTranslation(scriptedAvatar->getPosition());
audioTransform.setRotation(headOrientation);
computeLoudness(&audio, scriptedAvatar);
QByteArray encodedBuffer;
if (_encoder) {
_encoder->encode(audio, encodedBuffer);
} else {
encodedBuffer = audio;
}
AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber,
audioTransform, scriptedAvatar->getPosition(), glm::vec3(0),
PacketType::MicrophoneAudioNoEcho, _selectedCodecName);
packetType, _selectedCodecName);
});
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
@ -483,6 +497,14 @@ void Agent::setIsListeningToAudioStream(bool 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) {
// this must happen on Agent's main thread
if (QThread::currentThread() != thread()) {

View file

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

View file

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

View file

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

View file

@ -177,7 +177,7 @@ ScrollingWindow {
SHAPE_TYPE_STATIC_MESH
],
checkStateOnDisable: false,
warningOnDisable: "Models with automatic collisions set to 'Exact' cannot be dynamic"
warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic"
}
});

View file

@ -107,10 +107,10 @@ ModalWindow {
QtObject {
id: d;
readonly property int minWidth: 480;
readonly property int maxWdith: 1280;
readonly property int minHeight: 120;
readonly property int maxHeight: 720;
readonly property int minWidth: 480
readonly property int maxWdith: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, pane.width);
@ -259,6 +259,7 @@ ModalWindow {
visible: Boolean(root.warning);
text: hifi.glyphs.alert;
size: hifi.dimensions.controlLineHeight;
width: 20 // Line up with checkbox.
}
}

View file

@ -12,7 +12,7 @@ import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs
import "../controls-uit" as ControlsUIT
import "../controls-uit"
import "../styles-uit"
import "../windows"
@ -20,7 +20,9 @@ TabletModalWindow {
id: root;
HifiConstants { id: hifi; }
y: hifi.dimensions.tabletMenuHeader
anchors.fill: parent
width: parent.width
height: parent.height
title: ""
visible: true;
@ -108,18 +110,24 @@ TabletModalWindow {
TabletModalFrame {
id: modalWindowItem
width: parent.width - 12
width: parent.width - 6
height: 240
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
MouseArea {
// Clicking dialog background defocuses active control.
anchors.fill: parent
onClicked: parent.forceActiveFocus();
}
QtObject {
id: d;
readonly property int minWidth: 470
readonly property int maxWidth: 470
readonly property int minHeight: 240
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
@ -130,8 +138,8 @@ TabletModalWindow {
((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + hifi.dimensions.contentSpacing.y) : 0);
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth);
root.height = (targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ?
d.maxHeight : targetHeight);
modalWindowItem.height = (targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ?
d.maxHeight : targetHeight);
if (checkBoxField.visible && comboBoxField.visible) {
checkBoxField.width = extraInputs.width / 2;
comboBoxField.width = extraInputs.width / 2;
@ -140,141 +148,149 @@ TabletModalWindow {
}
}
}
// Item {
// anchors {
// // top: parent.top;
// // bottom: extraInputs.visible ? extraInputs.top : buttons.top;
// left: parent.left;
// right: parent.right;
// topMargin: 20
// }
Item {
anchors {
top: parent.top;
bottom: extraInputs.visible ? extraInputs.top : buttons.top;
left: parent.left;
right: parent.right;
leftMargin: 12
rightMargin: 12
}
// FIXME make a text field type that can be bound to a history for autocompletion
ControlsUIT.TextField {
TextField {
id: textField;
label: root.textInput.label;
focus: root.textInput ? true : false;
visible: root.textInput ? true : false;
anchors {
top: parent.top
left: parent.left;
right: parent.right;
leftMargin: 5; rightMargin: 5;
topMargin: textField.controlHeight - textField.height + 5
bottom: keyboard.top;
bottomMargin: hifi.dimensions.contentSpacing.y;
}
}
ControlsUIT.Keyboard {
property alias keyboardOverride: root.keyboardOverride
property alias keyboardRaised: root.keyboardRaised
property alias punctuationMode: root.punctuationMode
Keyboard {
id: keyboard
raised: keyboardEnabled && keyboardRaised
numeric: punctuationMode
anchors {
left: parent.left
right: parent.right
leftMargin: 5; rightMargin: 5;
top: textField.bottom
topMargin: raised ? hifi.dimensions.contentSpacing.y : 0
bottom: parent.bottom
bottomMargin: raised ? hifi.dimensions.contentSpacing.y : 0
}
}
// }
}
Row {
id: extraInputs;
visible: Boolean(root.checkBox || root.comboBox);
Item {
id: extraInputs;
visible: Boolean(root.checkBox || root.comboBox);
anchors {
left: parent.left;
right: parent.right;
bottom: buttons.top;
bottomMargin: hifi.dimensions.contentSpacing.y;
leftMargin: 12
rightMargin: 12
}
height: comboBoxField.controlHeight;
onHeightChanged: d.resize();
onWidthChanged: d.resize();
z: 20
CheckBox {
id: checkBoxField;
text: root.checkBox.label;
focus: Boolean(root.checkBox);
visible: Boolean(root.checkBox);
anchors {
left: parent.left;
right: parent.right;
leftMargin: 5; rightMargin: 5;
top: keyboard.bottom;
topMargin: hifi.dimensions.contentSpacing.y + 20;
}
height: comboBoxField.controlHeight;
onHeightChanged: d.resize();
onWidthChanged: d.resize();
ControlsUIT.CheckBox {
id: checkBoxField;
text: root.checkBox.label;
focus: Boolean(root.checkBox);
visible: Boolean(root.checkBox);
// anchors {
// left: parent.left;
// bottom: parent.bottom;
// leftMargin: 6; // Magic number to align with warning icon
// bottomMargin: 6;
// }
}
ControlsUIT.ComboBox {
id: comboBoxField;
label: root.comboBox.label;
focus: Boolean(root.comboBox);
visible: Boolean(root.comboBox);
// anchors {
// right: parent.right;
// bottom: parent.bottom;
// }
model: root.comboBox ? root.comboBox.items : [];
onAccepted: {
updateCheckbox();
focus = true;
}
}
}
Row {
id: buttons;
focus: true;
spacing: hifi.dimensions.contentSpacing.x;
layoutDirection: Qt.RightToLeft;
onHeightChanged: d.resize();
onWidthChanged: {
d.resize();
resizeWarningText();
}
anchors {
bottom: parent.bottom;
left: parent.left;
right: parent.right;
bottomMargin: hifi.dimensions.contentSpacing.y;
leftMargin: 5; rightMargin: 5;
}
function resizeWarningText() {
var rowWidth = buttons.width;
var buttonsWidth = acceptButton.width + cancelButton.width + hifi.dimensions.contentSpacing.x * 2;
var warningIconWidth = warningIcon.width + hifi.dimensions.contentSpacing.x;
warningText.width = rowWidth - buttonsWidth - warningIconWidth;
}
ControlsUIT.Button {
id: cancelButton;
action: cancelAction;
}
ControlsUIT.Button {
id: acceptButton;
action: acceptAction;
}
Text {
id: warningText;
visible: Boolean(root.warning);
text: root.warning;
wrapMode: Text.WordWrap;
font.italic: true;
maximumLineCount: 2;
}
HiFiGlyphs {
id: warningIcon;
visible: Boolean(root.warning);
text: hifi.glyphs.alert;
size: hifi.dimensions.controlLineHeight;
leftMargin: 6; // Magic number to align with warning icon
bottomMargin: 6;
}
}
// }//column
ComboBox {
id: comboBoxField;
label: root.comboBox.label;
focus: Boolean(root.comboBox);
visible: Boolean(root.comboBox);
anchors {
right: parent.right;
bottom: parent.bottom;
}
model: root.comboBox ? root.comboBox.items : [];
onAccepted: {
updateCheckbox();
focus = true;
}
}
}
Row {
id: buttons;
focus: true;
spacing: hifi.dimensions.contentSpacing.x;
layoutDirection: Qt.RightToLeft;
onHeightChanged: d.resize();
onWidthChanged: {
d.resize();
resizeWarningText();
}
z: 10
anchors {
bottom: parent.bottom;
left: parent.left;
right: parent.right;
bottomMargin: hifi.dimensions.contentSpacing.y;
leftMargin: 12
rightMargin: 12
}
function resizeWarningText() {
var rowWidth = buttons.width;
var buttonsWidth = acceptButton.width + cancelButton.width + hifi.dimensions.contentSpacing.x * 2;
var warningIconWidth = warningIcon.width + hifi.dimensions.contentSpacing.x;
warningText.width = rowWidth - buttonsWidth - warningIconWidth;
}
Button {
id: cancelButton;
action: cancelAction;
}
Button {
id: acceptButton;
action: acceptAction;
}
Text {
id: warningText;
visible: Boolean(root.warning);
text: root.warning;
wrapMode: Text.WordWrap;
font.italic: true;
maximumLineCount: 2;
}
HiFiGlyphs {
id: warningIcon;
visible: Boolean(root.warning);
text: hifi.glyphs.alert;
size: hifi.dimensions.controlLineHeight;
width: 20 // Line up with checkbox.
}
}
Action {
id: cancelAction;
text: qsTr("Cancel");

View file

@ -176,7 +176,7 @@ Rectangle {
SHAPE_TYPE_STATIC_MESH
],
checkStateOnDisable: false,
warningOnDisable: "Models with automatic collisions set to 'Exact' cannot be dynamic"
warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic"
}
});

View file

@ -9,34 +9,30 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "tabletWindows"
import "../../dialogs"
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
StackView {
id: profileRoot
initialItem: root
objectName: "stack"
property var title: "Audio Preferences"
property string title: "Audio Settings"
property var eventBridge;
signal sendToScript(var message);
function pushSource(path) {
editRoot.push(Qt.reslovedUrl(path));
profileRoot.push(Qt.reslovedUrl(path));
}
function popSource() {
profileRoot.pop();
}
TabletPreferencesDialog {
id: root
property string title: "Audio Settings"
objectName: "TabletAudioPreferences"
width: parent.width
height: parent.height
showCategories: ["Audio"]
}
}

View file

@ -9,34 +9,30 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "tabletWindows"
import "../../dialogs"
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
StackView {
id: profileRoot
initialItem: root
objectName: "stack"
property var title: "Avatar Preferences"
property string title: "Avatar Settings"
property var eventBridge;
signal sendToScript(var message);
function pushSource(path) {
editRoot.push(Qt.reslovedUrl(path));
profileRoot.push(Qt.reslovedUrl(path));
}
function popSource() {
profileRoot.pop();
}
TabletPreferencesDialog {
id: root
property string title: "Avatar Preferences"
objectName: "TabletAvatarPreferences"
width: parent.width
height: parent.height
showCategories: ["Avatar Basics", "Avatar Tuning", "Avatar Camera"]
}
}

View file

@ -9,35 +9,30 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "tabletWindows"
import "../../dialogs"
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
StackView {
id: profileRoot
initialItem: root
objectName: "stack"
property var title: "General Prefernces"
property string title: "General Settings"
property var eventBridge;
signal sendToScript(var message);
function pushSource(path) {
editRoot.push(Qt.reslovedUrl(path));
profileRoot.push(Qt.reslovedUrl(path));
}
function popSource() {
profileRoot.pop();
}
TabletPreferencesDialog {
id: root
property string title: "General Preferences"
objectName: "TabletGeneralPreferences"
width: parent.width
height: parent.height
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect"]
}
}

View file

@ -1,34 +1,38 @@
import QtQuick 2.5
import Qt.labs.settings 1.0
//
// TabletGraphicsPreferences.qml
//
// Created by Vlad Stelmahovsky on 12 Mar 2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "tabletWindows"
import "../../dialogs"
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
StackView {
id: profileRoot
initialItem: root
objectName: "stack"
property string title: "Graphics Settings"
property var eventBridge;
signal sendToScript(var message);
function pushSource(path) {
editRoot.push(Qt.reslovedUrl(path));
profileRoot.push(Qt.reslovedUrl(path));
}
function popSource() {
profileRoot.pop();
}
TabletPreferencesDialog {
id: root
property string title: "Graphics Settings"
objectName: "TabletGraphicsPreferences"
width: parent.width
height: parent.height
showCategories: ["Graphics"]
}
}

View file

@ -1,34 +1,38 @@
import QtQuick 2.5
import Qt.labs.settings 1.0
//
// TabletLodPreferences.qml
//
// Created by Vlad Stelmahovsky on 11 Mar 2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "tabletWindows"
import "../../dialogs"
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
StackView {
id: profileRoot
initialItem: root
objectName: "stack"
property string title: "LOD Settings"
property var eventBridge;
signal sendToScript(var message);
function pushSource(path) {
editRoot.push(Qt.reslovedUrl(path));
profileRoot.push(Qt.reslovedUrl(path));
}
function popSource() {
profileRoot.pop();
}
TabletPreferencesDialog {
id: root
property string title: "LOD Settings"
objectName: "TabletLodPreferences"
width: parent.width
height: parent.height
showCategories: ["Level of Detail Tuning"]
}
}

View file

@ -60,7 +60,7 @@ FocusScope {
anchors.fill: parent
hoverEnabled: true
onEntered: iconColorOverlay.color = "#1fc6a6";
onExited: iconColorOverlay.color = "#ffffff";
onExited: iconColorOverlay.color = "#34a2c7";
// navigate back to root level menu
onClicked: {
buildMenu();

View file

@ -9,34 +9,30 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "tabletWindows"
import "../../dialogs"
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
StackView {
id: profileRoot
initialItem: root
objectName: "stack"
property var title: "Network Preferences"
property var title: "Networking Settings"
property var eventBridge;
signal sendToScript(var message);
function pushSource(path) {
editRoot.push(Qt.reslovedUrl(path));
profileRoot.push(Qt.reslovedUrl(path));
}
function popSource() {
profileRoot.pop();
}
TabletPreferencesDialog {
id: root
property string title: "Networking Settings"
objectName: "NetworkingPreferences"
width: parent.width
height: parent.height
objectName: "TabletNetworkingPreferences"
showCategories: ["Networking"]
}
}

View file

@ -58,21 +58,10 @@ Item {
Tablet.getTablet("com.highfidelity.interface.tablet.system").gotoHomeScreen();
}
HifiControls.TabletHeader {
id: header
title: parent.title
anchors {
top: parent.top
left: parent.left
right: parent.right
}
}
Rectangle {
id: main
anchors {
top: header.bottom
top: parent.top
bottom: footer.top
left: parent.left
right: parent.right

View file

@ -1,6 +1,6 @@
QPlainTextEdit {
font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;
font-family: Inconsolata, Consolas, Courier New, monospace;
font-size: 16px;
padding-left: 28px;
padding-top: 7px;
@ -11,7 +11,7 @@ QPlainTextEdit {
}
QLineEdit {
font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;
font-family: Inconsolata, Consolas, Courier New, monospace;
padding-left: 7px;
background-color: #CCCCCC;
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)
: ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {}
void CauterizedMeshPartPayload::updateTransformForSkinnedCauterizedMesh(const Transform& transform,
const QVector<glm::mat4>& clusterMatrices,
const QVector<glm::mat4>& cauterizedClusterMatrices) {
_transform = transform;
_cauterizedTransform = transform;
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::updateTransformForCauterizedMesh(
const Transform& renderTransform,
const gpu::BufferPointer& buffer) {
_cauterizedTransform = renderTransform;
_cauterizedClusterBuffer = buffer;
}
void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const {
// Still relying on the raw data from the model
const Model::MeshState& state = _model->getMeshState(_meshIndex);
SkeletonModel* skeleton = static_cast<SkeletonModel*>(_model);
bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization();
if (state.clusterBuffer) {
if (useCauterizedMesh) {
const Model::MeshState& cState = skeleton->getCauterizeMeshState(_meshIndex);
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, cState.clusterBuffer);
} else {
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer);
if (useCauterizedMesh) {
if (_cauterizedClusterBuffer) {
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _cauterizedClusterBuffer);
}
batch.setModelTransform(_cauterizedTransform);
} else {
if (_clusterBuffer) {
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer);
}
batch.setModelTransform(_transform);
} else {
if (useCauterizedMesh) {
batch.setModelTransform(_cauterizedTransform);
} else {
batch.setModelTransform(_transform);
}
}
}

View file

@ -17,12 +17,13 @@
class CauterizedMeshPartPayload : public ModelMeshPartPayload {
public:
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,
const QVector<glm::mat4>& cauterizedClusterMatrices);
void updateTransformForCauterizedMesh(const Transform& renderTransform, const gpu::BufferPointer& buffer);
void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override;
private:
gpu::BufferPointer _cauterizedClusterBuffer;
Transform _cauterizedTransform;
};

View file

@ -26,8 +26,8 @@ CauterizedModel::~CauterizedModel() {
}
void CauterizedModel::deleteGeometry() {
Model::deleteGeometry();
_cauterizeMeshStates.clear();
Model::deleteGeometry();
_cauterizeMeshStates.clear();
}
bool CauterizedModel::updateGeometry() {
@ -41,7 +41,7 @@ bool CauterizedModel::updateGeometry() {
_cauterizeMeshStates.append(state);
}
}
return needsFullUpdate;
return needsFullUpdate;
}
void CauterizedModel::createVisibleRenderItemSet() {
@ -86,13 +86,13 @@ void CauterizedModel::createVisibleRenderItemSet() {
}
}
} else {
Model::createVisibleRenderItemSet();
Model::createVisibleRenderItemSet();
}
}
void CauterizedModel::createCollisionRenderItemSet() {
// Temporary HACK: use base class method for now
Model::createCollisionRenderItemSet();
Model::createCollisionRenderItemSet();
}
void CauterizedModel::updateClusterMatrices() {
@ -122,8 +122,8 @@ void CauterizedModel::updateClusterMatrices() {
state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) state.clusterMatrices.constData());
}
}
}
}
}
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
if (!_cauterizeBoneSet.empty()) {
@ -191,6 +191,9 @@ void CauterizedModel::updateRenderItems() {
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();
Transform modelTransform;
@ -209,15 +212,22 @@ void CauterizedModel::updateRenderItems() {
if (data._model && data._model->isLoaded()) {
// Ensure the model geometry was not reset between frames
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.
data._model->updateClusterMatrices();
// update the model transform and bounding box for this render item.
// this stuff identical to what happens in regular Model
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);
assert(data._meshIndex < cModel->_cauterizeMeshStates.size());
const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex);
data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices);
const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex);
renderTransform = modelTransform;
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()) {
setRecordingBasis();
}
_wasCharacterControllerEnabled = _characterController.isEnabled();
_characterController.setEnabled(false);
} else {
clearRecordingBasis();
useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
_characterController.setEnabled(_wasCharacterControllerEnabled);
}
auto audioIO = DependencyManager::get<AudioClient>();

View file

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

View file

@ -28,17 +28,23 @@ const int SEARCH_BUTTON_LEFT = 25;
const int SEARCH_BUTTON_WIDTH = 20;
const int SEARCH_TOGGLE_BUTTON_WIDTH = 50;
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 BOLD_COLOR = QColor("#445c8c");
const QString BOLD_PATTERN = "\\[\\d*\\/.*:\\d*:\\d*\\]";
class KeywordHighlighter : public QSyntaxHighlighter {
class Highlighter : public QSyntaxHighlighter {
public:
KeywordHighlighter(QTextDocument* parent = nullptr);
Highlighter(QTextDocument* parent = nullptr);
void setBold(int indexToBold);
QString keyword;
protected:
void highlightBlock(const QString& text) override;
private:
QTextCharFormat boldFormat;
QTextCharFormat keywordFormat;
};
@ -89,7 +95,7 @@ void BaseLogDialog::initControls() {
_leftPad += SEARCH_TOGGLE_BUTTON_WIDTH + BUTTON_MARGIN;
_searchPrevButton->show();
connect(_searchPrevButton, SIGNAL(clicked()), SLOT(toggleSearchPrev()));
_searchNextButton = new QPushButton(this);
_searchNextButton->setObjectName("searchNextButton");
_searchNextButton->setGeometry(_leftPad, ELEMENT_MARGIN, SEARCH_TOGGLE_BUTTON_WIDTH, ELEMENT_HEIGHT);
@ -101,9 +107,8 @@ void BaseLogDialog::initControls() {
_logTextBox = new QPlainTextEdit(this);
_logTextBox->setReadOnly(true);
_logTextBox->show();
_highlighter = new KeywordHighlighter(_logTextBox->document());
_highlighter = new Highlighter(_logTextBox->document());
connect(_logTextBox, SIGNAL(selectionChanged()), SLOT(updateSelection()));
}
void BaseLogDialog::showEvent(QShowEvent* event) {
@ -116,7 +121,9 @@ void BaseLogDialog::resizeEvent(QResizeEvent* event) {
void BaseLogDialog::appendLogLine(QString logLine) {
if (logLine.contains(_searchTerm, Qt::CaseInsensitive)) {
int indexToBold = _logTextBox->document()->characterCount();
_logTextBox->appendPlainText(logLine.trimmed());
_highlighter->setBold(indexToBold);
}
}
@ -128,7 +135,7 @@ void BaseLogDialog::handleSearchTextChanged(QString searchText) {
if (searchText.isEmpty()) {
return;
}
QTextCursor cursor = _logTextBox->textCursor();
if (cursor.hasSelection()) {
QString selectedTerm = cursor.selectedText();
@ -136,16 +143,16 @@ void BaseLogDialog::handleSearchTextChanged(QString searchText) {
return;
}
}
cursor.setPosition(0, QTextCursor::MoveAnchor);
_logTextBox->setTextCursor(cursor);
bool foundTerm = _logTextBox->find(searchText);
if (!foundTerm) {
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
_logTextBox->setTextCursor(cursor);
}
_searchTerm = searchText;
_highlighter->keyword = searchText;
_highlighter->rehighlight();
@ -175,6 +182,7 @@ void BaseLogDialog::showLogData() {
_logTextBox->clear();
_logTextBox->appendPlainText(getCurrentLog());
_logTextBox->ensureCursorVisible();
_highlighter->rehighlight();
}
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);
}
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()) {
return;
}
int index = text.indexOf(keyword, 0, Qt::CaseInsensitive);
index = text.indexOf(keyword, 0, Qt::CaseInsensitive);
int length = keyword.length();
while (index >= 0) {
@ -204,3 +224,7 @@ void KeywordHighlighter::highlightBlock(const QString& text) {
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 QLineEdit;
class QPlainTextEdit;
class KeywordHighlighter;
class Highlighter;
class BaseLogDialog : public QDialog {
Q_OBJECT
@ -56,7 +56,7 @@ private:
QPushButton* _searchPrevButton { nullptr };
QPushButton* _searchNextButton { nullptr };
QString _searchTerm;
KeywordHighlighter* _highlighter { nullptr };
Highlighter* _highlighter { nullptr };
void initControls();
void showLogData();

View file

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

View file

@ -1,6 +1,6 @@
//
// AudioNoiseGate.cpp
// interface/src/audio
// libraries/audio
//
// Created by Stephen Birarda on 2014-12-16.
// Copyright 2014 High Fidelity, Inc.
@ -9,29 +9,23 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AudioNoiseGate.h"
#include <cstdlib>
#include <string.h>
#include <AudioConstants.h>
#include "AudioNoiseGate.h"
#include "AudioConstants.h"
const float AudioNoiseGate::CLIPPING_THRESHOLD = 0.90f;
AudioNoiseGate::AudioNoiseGate() :
_inputBlockCounter(0),
_lastLoudness(0.0f),
_quietestBlock(std::numeric_limits<float>::max()),
_loudestBlock(0.0f),
_didClipInLastBlock(false),
_dcOffset(0.0f),
_measuredFloor(0.0f),
_sampleCounter(0),
_isOpen(false),
_blocksToClose(0)
{
}
_blocksToClose(0) {}
void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) {
//
@ -80,7 +74,7 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
float loudness = 0;
int thisSample = 0;
int samplesOverNoiseGate = 0;
const float NOISE_GATE_HEIGHT = 7.0f;
const int NOISE_GATE_WIDTH = 5;
const int NOISE_GATE_CLOSE_BLOCK_DELAY = 5;
@ -88,36 +82,22 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
// Check clipping, and check if should open noise gate
_didClipInLastBlock = false;
for (int i = 0; i < numSamples; i++) {
thisSample = std::abs(samples[i]);
if (thisSample >= ((float) AudioConstants::MAX_SAMPLE_VALUE * CLIPPING_THRESHOLD)) {
_didClipInLastBlock = true;
}
loudness += thisSample;
// Noise Reduction: Count peaks above the average loudness
if (thisSample > (_measuredFloor * NOISE_GATE_HEIGHT)) {
samplesOverNoiseGate++;
}
}
_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
float averageOfAllSampleBlocks = 0.0f;
_sampleBlocks[_sampleCounter++] = _lastLoudness;
@ -130,7 +110,7 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
averageOfAllSampleBlocks += _sampleBlocks[j];
}
thisAverage /= NOISE_GATE_BLOCKS_TO_AVERAGE;
if (thisAverage < smallestSample) {
smallestSample = thisAverage;
}
@ -138,7 +118,7 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
averageOfAllSampleBlocks /= NUMBER_OF_NOISE_SAMPLE_BLOCKS;
_measuredFloor = smallestSample;
_sampleCounter = 0;
}
_closedInLastBlock = false;
@ -156,7 +136,7 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
}
if (!_isOpen) {
// First block after being closed gets faded to silence, we fade across
// the entire block on fading out. All subsequent blocks are muted by being slammed
// the entire block on fading out. All subsequent blocks are muted by being slammed
// to zeros
if (_closedInLastBlock) {
float fadeSlope = (1.0f / numSamples);

View file

@ -1,6 +1,6 @@
//
// AudioNoiseGate.h
// interface/src/audio
// libraries/audio
//
// Created by Stephen Birarda on 2014-12-16.
// Copyright 2014 High Fidelity, Inc.
@ -19,24 +19,21 @@ const int NUMBER_OF_NOISE_SAMPLE_BLOCKS = 300;
class AudioNoiseGate {
public:
AudioNoiseGate();
void gateSamples(int16_t* samples, int numSamples);
void removeDCOffset(int16_t* samples, int numSamples);
bool clippedInLastBlock() const { return _didClipInLastBlock; }
bool closedInLastBlock() const { return _closedInLastBlock; }
bool openedInLastBlock() const { return _openedInLastBlock; }
bool isOpen() const { return _isOpen; }
float getMeasuredFloor() const { return _measuredFloor; }
float getLastLoudness() const { return _lastLoudness; }
static const float CLIPPING_THRESHOLD;
private:
int _inputBlockCounter;
float _lastLoudness;
float _quietestBlock;
float _loudestBlock;
bool _didClipInLastBlock;
float _dcOffset;
float _measuredFloor;
@ -48,4 +45,4 @@ private:
int _blocksToClose;
};
#endif // hifi_AudioNoiseGate_h
#endif // hifi_AudioNoiseGate_h

View file

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

View file

@ -13,18 +13,31 @@
#define hifi_NodePermissions_h
#include <memory>
#include <unordered_map>
#include <QString>
#include <QMap>
#include <QVariant>
#include <QUuid>
#include <QHash>
#include <utility>
#include "GroupRank.h"
class 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>>;
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 {
public:
@ -100,27 +113,40 @@ public:
NodePermissionsMap() { }
NodePermissionsPointer& operator[](const NodePermissionsKey& key) {
NodePermissionsKey dataKey(key.first.toLower(), key.second);
if (!_data.contains(dataKey)) {
if (0 == _data.count(dataKey)) {
_data[dataKey] = NodePermissionsPointer(new NodePermissions(key));
}
return _data[dataKey];
}
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 {
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 {
return _data.contains(NodePermissionsKey(keyFirst.toLower(), keySecond));
bool contains(const QString& keyFirst, const QUuid& keySecond) const {
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 remove(const NodePermissionsKey& key) { _data.remove(key); }
void remove(const NodePermissionsKey& key) { _data.erase(key); }
private:
QHash<NodePermissionsKey, NodePermissionsPointer> _data;
std::unordered_map<NodePermissionsKey, NodePermissionsPointer> _data;
};

View file

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

View file

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

View file

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

View file

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

View file

@ -227,6 +227,10 @@ void Model::updateRenderItems() {
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();
uint32_t deleteGeometryCounter = self->_deleteGeometryCounter;
@ -240,12 +244,12 @@ void Model::updateRenderItems() {
Transform modelTransform = data._model->getTransform();
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.
data._model->updateClusterMatrices();
// update the model transform and bounding box for this render item.
const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex);
data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices);
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);
}
}
});
@ -1048,7 +1052,7 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
}
void Model::computeMeshPartLocalBounds() {
for (auto& part : _modelMeshRenderItemsSet) {
for (auto& part : _modelMeshRenderItemsSet) {
assert(part->_meshIndex < _modelMeshRenderItemsSet.size());
const Model::MeshState& state = _meshStates.at(part->_meshIndex);
part->computeAdjustedLocalBound(state.clusterMatrices);

View file

@ -541,16 +541,6 @@ void ScriptEngine::init() {
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
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
registerMetaTypes(this);
@ -1844,7 +1834,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
processDeferredEntityLoads(entityScript, entityID);
}
void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
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
QMetaObject::invokeMethod(this, "unloadEntityScript",
Q_ARG(const EntityItemID&, entityID));
Q_ARG(const EntityItemID&, entityID),
Q_ARG(bool, shouldRemoveFromMap));
return;
}
#ifdef THREAD_DEBUGGING
@ -1867,7 +1858,12 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
} else {
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;
newDetails.status = EntityScriptStatus::UNLOADED;
newDetails.lastModified = QDateTime::currentMSecsSinceEpoch();
@ -1875,6 +1871,7 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
newDetails.scriptText = oldDetails.scriptText;
setEntityScriptDetails(entityID, newDetails);
}
stopAllTimersForEntityScript(entityID);
{
// 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;
}
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 callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName,
const QStringList& params = QStringList()) override;

View file

@ -56,7 +56,14 @@ public:
QQuickWindow* getTabletWindow();
QObject* getFlags();
signals:
/** jsdoc
* Signaled when a tablet message or dialog is created
* @function TabletProxy#tabletNotification
* @returns {Signal}
*/
void tabletNotification();
private:
void processMenuEvents(QObject* object, const QKeyEvent* event);
void processTabletEvents(QObject* object, const QKeyEvent* event);

View file

@ -223,6 +223,7 @@ QQuickItem* OffscreenUi::createMessageBox(Icon icon, const QString& title, const
invokeResult = QMetaObject::invokeMethod(tabletRoot, "messageBox",
Q_RETURN_ARG(QVariant, result),
Q_ARG(QVariant, QVariant::fromValue(map)));
emit tabletScriptingInterface->tabletNotification();
}
if (!invokeResult) {
@ -430,6 +431,7 @@ QQuickItem* OffscreenUi::createInputDialog(const Icon icon, const QString& title
invokeResult = QMetaObject::invokeMethod(tabletRoot, "inputDialog",
Q_RETURN_ARG(QVariant, result),
Q_ARG(QVariant, QVariant::fromValue(map)));
emit tabletScriptingInterface->tabletNotification();
}
if (!invokeResult) {
qWarning() << "Failed to create message box";
@ -457,6 +459,7 @@ QQuickItem* OffscreenUi::createCustomInputDialog(const Icon icon, const QString&
invokeResult = QMetaObject::invokeMethod(tabletRoot, "inputDialog",
Q_RETURN_ARG(QVariant, result),
Q_ARG(QVariant, QVariant::fromValue(map)));
emit tabletScriptingInterface->tabletNotification();
}
if (!invokeResult) {
@ -614,6 +617,7 @@ QString OffscreenUi::fileDialog(const QVariantMap& properties) {
invokeResult = QMetaObject::invokeMethod(tabletRoot, "fileDialog",
Q_RETURN_ARG(QVariant, buildDialogResult),
Q_ARG(QVariant, QVariant::fromValue(properties)));
emit tabletScriptingInterface->tabletNotification();
}
if (!invokeResult) {

View file

@ -9,12 +9,14 @@
// 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/";
Script.include("/~/system/libraries/toolBars.js");
var recordingFile = "recording.hfr";
function setPlayerOptions() {
function setDefaultPlayerOptions() {
Recording.setPlayFromCurrentLocation(true);
Recording.setPlayerUseDisplayName(false);
Recording.setPlayerUseAttachments(false);
@ -38,16 +40,16 @@ var saveIcon;
var loadIcon;
var spacing;
var timerOffset;
setupToolBar();
var timer = null;
var slider = null;
setupToolBar();
setupTimer();
var watchStop = false;
function setupToolBar() {
if (toolBar != null) {
if (toolBar !== null) {
print("Multiple calls to Recorder.js:setupToolBar()");
return;
}
@ -56,6 +58,8 @@ function setupToolBar() {
toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
toolBar.onMove = onToolbarMove;
toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF);
recordIcon = toolBar.addTool({
@ -86,7 +90,7 @@ function setupToolBar() {
visible: true
}, false);
timerOffset = toolBar.width;
timerOffset = toolBar.width + ToolBar.SPACING;
spacing = toolBar.addSpacing(0);
saveIcon = toolBar.addTool({
@ -112,15 +116,15 @@ function setupTimer() {
text: (0.00).toFixed(3),
backgroundColor: COLOR_OFF,
x: 0, y: 0,
width: 0, height: 0,
leftMargin: 10, topMargin: 10,
width: 200, height: 25,
leftMargin: 5, topMargin: 3,
alpha: 1.0, backgroundAlpha: 1.0,
visible: true
});
slider = { x: 0, y: 0,
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", {
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() {
var text = "";
if (Recording.isRecording()) {
text = formatTime(Recording.recorderElapsed());
} else {
text = formatTime(Recording.playerElapsed()) + " / " +
formatTime(Recording.playerLength());
text = formatTime(Recording.playerElapsed()) + " / " + formatTime(Recording.playerLength());
}
var timerWidth = text.length * 8 + ((Recording.isRecording()) ? 15 : 0);
Overlays.editOverlay(timer, {
text: text
})
toolBar.changeSpacing(text.length * 8 + ((Recording.isRecording()) ? 15 : 0), spacing);
text: text,
width: timerWidth
});
toolBar.changeSpacing(timerWidth + ToolBar.SPACING, spacing);
if (Recording.isRecording()) {
slider.pos = 1.0;
@ -173,7 +197,7 @@ function updateTimer() {
function formatTime(time) {
var MIN_PER_HOUR = 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));
time -= hours * (SEC_PER_MIN * MIN_PER_HOUR);
@ -184,37 +208,19 @@ function formatTime(time) {
var seconds = time;
var text = "";
text += (hours > 0) ? hours + ":" :
"";
text += (minutes > 0) ? ((minutes < 10 && text != "") ? "0" : "") + minutes + ":" :
"";
text += ((seconds < 10 && text != "") ? "0" : "") + seconds.toFixed(3);
text += (hours > 0) ? hours + ":" : "";
text += (minutes > 0) ? ((minutes < 10 && text !== "") ? "0" : "") + minutes + ":" : "";
text += ((seconds < 10 && text !== "") ? "0" : "") + seconds.toFixed(MSEC_DIGITS);
return text;
}
function moveUI() {
var relative = { x: 70, y: 40 };
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) {
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 (!Recording.isRecording()) {
@ -226,7 +232,11 @@ function mousePressEvent(event) {
toolBar.setAlpha(ALPHA_OFF, loadIcon);
} else {
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();
toolBar.setAlpha(ALPHA_ON, playIcon);
toolBar.setAlpha(ALPHA_ON, playLoopIcon);
@ -240,7 +250,6 @@ function mousePressEvent(event) {
toolBar.setAlpha(ALPHA_ON, saveIcon);
toolBar.setAlpha(ALPHA_ON, loadIcon);
} else if (Recording.playerLength() > 0) {
setPlayerOptions();
Recording.setPlayerLoop(false);
Recording.startPlaying();
toolBar.setAlpha(ALPHA_OFF, recordIcon);
@ -255,7 +264,6 @@ function mousePressEvent(event) {
toolBar.setAlpha(ALPHA_ON, saveIcon);
toolBar.setAlpha(ALPHA_ON, loadIcon);
} else if (Recording.playerLength() > 0) {
setPlayerOptions();
Recording.setPlayerLoop(true);
Recording.startPlaying();
toolBar.setAlpha(ALPHA_OFF, recordIcon);
@ -263,7 +271,7 @@ function mousePressEvent(event) {
toolBar.setAlpha(ALPHA_OFF, loadIcon);
}
} 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)");
if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) {
Recording.saveRecording(recordingFile);
@ -274,6 +282,7 @@ function mousePressEvent(event) {
recordingFile = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)");
if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) {
Recording.loadRecording(recordingFile);
setDefaultPlayerOptions();
}
if (Recording.playerLength() > 0) {
toolBar.setAlpha(ALPHA_ON, playIcon);
@ -282,8 +291,8 @@ function mousePressEvent(event) {
}
}
} else if (Recording.playerLength() > 0 &&
slider.x < event.x && event.x < slider.x + slider.w &&
slider.y < event.y && event.y < slider.y + slider.h) {
slider.x < event.x && event.x < slider.x + slider.w &&
slider.y < event.y && event.y < slider.y + slider.h) {
isSliding = true;
slider.pos = (event.x - slider.x) / slider.w;
Recording.setPlayerTime(slider.pos * Recording.playerLength());
@ -308,7 +317,7 @@ function mouseReleaseEvent(event) {
function update() {
var newDimensions = Controller.getViewportDimensions();
if (windowDimensions.x != newDimensions.x || windowDimensions.y != newDimensions.y) {
if (windowDimensions.x !== newDimensions.x || windowDimensions.y !== newDimensions.y) {
windowDimensions = newDimensions;
moveUI();
}

View file

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

View file

@ -94,11 +94,13 @@ var NotificationType = {
LOD_WARNING: 2,
CONNECTION_REFUSED: 3,
EDIT_ERROR: 4,
TABLET: 5,
properties: [
{ text: "Snapshot" },
{ text: "Level of Detail" },
{ text: "Connection Refused" },
{ text: "Edit error" }
{ text: "Edit error" },
{ text: "Tablet" }
],
getTypeFromMenuItem: function(menuItemName) {
if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) {
@ -535,6 +537,10 @@ function onSnapshotTaken(pathStillSnapshot, pathAnimatedSnapshot, notify) {
}
}
function tabletNotification() {
createNotification("Tablet needs your attention", NotificationType.TABLET);
}
function processingGif() {
createNotification("Processing GIF snapshot...", NotificationType.SNAPSHOT);
}
@ -641,7 +647,7 @@ Window.snapshotTaken.connect(onSnapshotTaken);
Window.processingGif.connect(processingGif);
Window.notifyEditError = onEditError;
Window.notify = onNotify;
Tablet.tabletNotification.connect(tabletNotification);
setup();
}()); // END LOCAL_SCOPE

View file

@ -2,7 +2,6 @@
Script.include("/~/system/libraries/utils.js");
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_FPS = 30;
var ANIMATION_FIRST_FRAME = 1;
@ -10,23 +9,25 @@
var RELEASE_KEYS = ['w', 'a', 's', 'd', 'UP', 'LEFT', 'DOWN', 'RIGHT'];
var RELEASE_TIME = 500; // ms
var RELEASE_DISTANCE = 0.2; // meters
var MAX_IK_ERROR = 20;
var DESKTOP_UI_CHECK_INTERVAL = 250;
var MAX_IK_ERROR = 30;
var DESKTOP_UI_CHECK_INTERVAL = 100;
var DESKTOP_MAX_DISTANCE = 5;
var SIT_DELAY = 25
var MAX_RESET_DISTANCE = 0.5
this.entityID = null;
this.timers = {};
this.animStateHandlerID = null;
this.interval = null;
this.preload = function(entityID) {
this.entityID = entityID;
}
this.unload = function() {
if (MyAvatar.sessionUUID === this.getSeatUser()) {
this.sitUp(this.entityID);
if (Settings.getValue(SETTING_KEY) === this.entityID) {
this.sitUp();
}
if (this.interval) {
if (this.interval !== null) {
Script.clearInterval(this.interval);
this.interval = null;
}
@ -34,42 +35,60 @@
}
this.setSeatUser = function(user) {
var userData = Entities.getEntityProperties(this.entityID, ["userData"]).userData;
userData = JSON.parse(userData);
try {
var userData = Entities.getEntityProperties(this.entityID, ["userData"]).userData;
userData = JSON.parse(userData);
if (user) {
userData.seat.user = user;
} else {
delete userData.seat.user;
if (user !== null) {
userData.seat.user = user;
} else {
delete userData.seat.user;
}
Entities.editEntity(this.entityID, {
userData: JSON.stringify(userData)
});
} catch (e) {
// Do Nothing
}
Entities.editEntity(this.entityID, {
userData: JSON.stringify(userData)
});
}
this.getSeatUser = function() {
var properties = Entities.getEntityProperties(this.entityID, ["userData", "position"]);
var userData = JSON.parse(properties.userData);
try {
var properties = Entities.getEntityProperties(this.entityID, ["userData", "position"]);
var userData = JSON.parse(properties.userData);
if (userData.seat.user && userData.seat.user !== MyAvatar.sessionUUID) {
var avatar = AvatarList.getAvatar(userData.seat.user);
if (avatar && Vec3.distance(avatar.position, properties.position) > RELEASE_DISTANCE) {
return null;
// If MyAvatar return my uuid
if (userData.seat.user === MyAvatar.sessionUUID) {
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
}
return userData.seat.user;
// Nobody on the seat
return null;
}
// Is the seat used
this.checkSeatForAvatar = function() {
var seatUser = this.getSeatUser();
var avatarIdentifiers = AvatarList.getAvatarIdentifiers();
for (var i in avatarIdentifiers) {
var avatar = AvatarList.getAvatar(avatarIdentifiers[i]);
if (avatar && avatar.sessionUUID === seatUser) {
return true;
}
// If MyAvatar appears to be sitting
if (seatUser === MyAvatar.sessionUUID) {
var properties = Entities.getEntityProperties(this.entityID, ["position"]);
return Vec3.distance(MyAvatar.position, properties.position) < RELEASE_DISTANCE;
}
return false;
return seatUser !== null;
}
this.sitDown = function() {
@ -78,40 +97,50 @@
return;
}
this.setSeatUser(MyAvatar.sessionUUID);
var previousValue = Settings.getValue(SETTING_KEY);
Settings.setValue(SETTING_KEY, this.entityID);
this.setSeatUser(MyAvatar.sessionUUID);
if (previousValue === "") {
MyAvatar.characterControllerEnabled = 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();
}
var that = this;
Script.setTimeout(function() {
var properties = Entities.getEntityProperties(that.entityID, ["position", "rotation"]);
var index = MyAvatar.getJointIndex("Hips");
MyAvatar.pinJoint(index, properties.position, properties.rotation);
var properties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
var index = MyAvatar.getJointIndex("Hips");
MyAvatar.pinJoint(index, properties.position, properties.rotation);
that.animStateHandlerID = MyAvatar.addAnimationStateHandler(function(properties) {
return { headType: 0 };
}, ["headType"]);
Script.update.connect(that, that.update);
Controller.keyPressEvent.connect(that, that.keyPressed);
Controller.keyReleaseEvent.connect(that, that.keyReleased);
for (var i in RELEASE_KEYS) {
Controller.captureKeyEvents({ text: RELEASE_KEYS[i] });
}
}, SIT_DELAY);
this.animStateHandlerID = MyAvatar.addAnimationStateHandler(function(properties) {
return { headType: 0 };
}, ["headType"]);
Script.update.connect(this, this.update);
Controller.keyPressEvent.connect(this, this.keyPressed);
Controller.keyReleaseEvent.connect(this, this.keyReleased);
for (var i in RELEASE_KEYS) {
Controller.captureKeyEvents({ text: RELEASE_KEYS[i] });
}
}
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) {
MyAvatar.restoreRoleAnimation(ROLE);
Settings.setValue(SETTING_KEY, "");
var ROLES = MyAvatar.getAnimationRoles();
for (i in ROLES) {
MyAvatar.restoreRoleAnimation(ROLES[i]);
}
MyAvatar.characterControllerEnabled = true;
MyAvatar.hmdLeanRecenterEnabled = true;
@ -124,16 +153,6 @@
MyAvatar.bodyPitch = 0.0;
MyAvatar.bodyRoll = 0.0;
}, 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) {
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 ikError = MyAvatar.getIKErrorOnLastSolve();
if (avatarDistance > RELEASE_DISTANCE || ikError > MAX_IK_ERROR) {
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) {
var that = this;
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();
}, RELEASE_TIME);
}
@ -223,7 +261,7 @@
}
this.hoverEnterEntity = function(event) {
if (isInEditMode() || (MyAvatar.sessionUUID === this.getSeatUser())) {
if (isInEditMode() || this.interval !== null) {
return;
}
@ -239,18 +277,18 @@
}, DESKTOP_UI_CHECK_INTERVAL);
}
this.hoverLeaveEntity = function(event) {
if (this.interval) {
if (this.interval !== null) {
Script.clearInterval(this.interval);
this.interval = null;
}
this.cleanupOverlay();
}
this.clickDownOnEntity = function () {
if (isInEditMode() || (MyAvatar.sessionUUID === this.getSeatUser())) {
this.clickDownOnEntity = function (id, event) {
if (isInEditMode()) {
return;
}
if (this.canSitDesktop()) {
if (event.isPrimaryButton && this.canSitDesktop()) {
this.sitDown();
}
}

View file

@ -17,3 +17,5 @@ set_target_properties(ac-client PROPERTIES FOLDER "Tools")
add_subdirectory(skeleton-dump)
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();
}