From ba9225438595b534bbd0262d1ee72565ea87f835 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 9 Mar 2015 13:31:54 -0700 Subject: [PATCH 01/34] Fix skeleton detection when uploading avatar model --- interface/src/ModelUploader.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index 9a56a75798..9310de53d4 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -179,21 +179,20 @@ bool ModelUploader::zip() { FBXGeometry geometry = readFBX(fbxContents, QVariantHash()); // Make sure that a skeleton model has a skeleton - if (_modelType == SKELETON_MODEL) { - if (geometry.rootJointIndex == -1) { - - QString message = "Your selected skeleton model has no skeleton.\n\nThe upload will be canceled."; - QMessageBox msgBox; - msgBox.setWindowTitle("Model Upload"); - msgBox.setText(message); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); - - return false; - } - } + if (_modelType == SKELETON_MODEL && !geometry.getJointNames().contains("Hips")) { + qDebug() << QString("[Warning] %1 does not contain a skeleton.").arg(filename); + QString message = "Your selected skeleton model has no skeleton.\n\nThe upload will be canceled."; + QMessageBox msgBox; + msgBox.setWindowTitle("Model Upload"); + msgBox.setText(message); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + + return false; + } + // make sure we have some basic mappings populateBasicMapping(mapping, filename, geometry); From 60867cc9310e3615c042bfc384eda88f63b53ff7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 10 Mar 2015 11:48:31 +0100 Subject: [PATCH 02/34] Added ModelSelector class --- interface/src/ModelSelector.cpp | 12 ++++++++++++ interface/src/ModelSelector.h | 15 +++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 interface/src/ModelSelector.cpp create mode 100644 interface/src/ModelSelector.h diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp new file mode 100644 index 0000000000..3ab0aaf21d --- /dev/null +++ b/interface/src/ModelSelector.cpp @@ -0,0 +1,12 @@ +// +// ModelSelector.cpp +// +// +// Created by Clement on 3/10/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ModelSelector.h" diff --git a/interface/src/ModelSelector.h b/interface/src/ModelSelector.h new file mode 100644 index 0000000000..4865357598 --- /dev/null +++ b/interface/src/ModelSelector.h @@ -0,0 +1,15 @@ +// +// ModelSelector.h +// +// +// Created by Clement on 3/10/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ModelSelector_h +#define hifi_ModelSelector_h + +#endif // hifi_ModelSelector_h \ No newline at end of file From f55e9b6c1c48ceddce921e88cf7234bab9c553bf Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 10 Mar 2015 11:49:07 +0100 Subject: [PATCH 03/34] ModelSelector implementation --- interface/src/ModelSelector.cpp | 77 +++++++++++++++++++++++++++++++++ interface/src/ModelSelector.h | 31 +++++++++++++ 2 files changed, 108 insertions(+) diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp index 3ab0aaf21d..97c7f780e2 100644 --- a/interface/src/ModelSelector.cpp +++ b/interface/src/ModelSelector.cpp @@ -9,4 +9,81 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include +#include +#include +#include +#include + #include "ModelSelector.h" + +static const QString AVATAR_HEAD_STRING = "Avatar Head"; +static const QString AVATAR_BODY_STRING = "Avatar Body"; +static const QString AVATAR_ATTACHEMENT_STRING = "Avatar Attachment"; +static const QString ENTITY_MODEL_STRING = "Entity Model"; + +ModelSelector::ModelSelector() { + QFormLayout* form = new QFormLayout(this); + + setWindowTitle("Select Model"); + setLayout(form); + + _browseButton = new QPushButton("Browse", this); + connect(_browseButton, &QPushButton::clicked, this, &ModelSelector::browse); + form->addRow("Model File:", _browseButton); + + _modelType = new QComboBox(this); + _modelType->addItem(AVATAR_HEAD_STRING); + _modelType->addItem(AVATAR_BODY_STRING); + _modelType->addItem(AVATAR_ATTACHEMENT_STRING); + _modelType->addItem(ENTITY_MODEL_STRING); + form->addRow("Model Type:", _modelType); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(buttons, &QDialogButtonBox::accepted, this, &ModelSelector::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + form->addRow(buttons); +} + +QFileInfo ModelSelector::getFileInfo() const { + return _modelFile; +} + +ModelType ModelSelector::getModelType() const { + QString text = _modelType->currentText(); + + if (text == AVATAR_HEAD_STRING) { + return HEAD_MODEL; + } else if (text == AVATAR_BODY_STRING) { + return SKELETON_MODEL; + } else if (text == AVATAR_ATTACHEMENT_STRING) { + return ATTACHMENT_MODEL; + } else if (text == ENTITY_MODEL_STRING) { + return ENTITY_MODEL; + } else { + Q_UNREACHABLE(); + } +} + +void ModelSelector::accept() { + if (!_modelFile.isFile()) { + return; + } + QDialog::accept(); +} + +void ModelSelector::browse() { + static Setting::Handle lastModelBrowseLocation("LastModelBrowseLocation", + QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); + QString filename = QFileDialog::getOpenFileName(NULL, "Select your model file ...", + lastModelBrowseLocation.get(), + "Model files (*.fst *.fbx)"); + QFileInfo fileInfo(filename); + + if (fileInfo.isFile() && fileInfo.completeSuffix().contains(QRegExp("fst|fbx"))) { + _modelFile = fileInfo; + _browseButton->setText(fileInfo.fileName()); + lastModelBrowseLocation.set(fileInfo.path()); + } +} \ No newline at end of file diff --git a/interface/src/ModelSelector.h b/interface/src/ModelSelector.h index 4865357598..aaa35e01c3 100644 --- a/interface/src/ModelSelector.h +++ b/interface/src/ModelSelector.h @@ -12,4 +12,35 @@ #ifndef hifi_ModelSelector_h #define hifi_ModelSelector_h +#include +#include + +#include + +#include "ui/ModelsBrowser.h" + +class QComboBox; +class QPushButton; + +class ModelSelector : public QDialog { + Q_OBJECT + +public: + ModelSelector(); + + QFileInfo getFileInfo() const; + ModelType getModelType() const; + + public slots: + virtual void accept(); + + private slots: + void browse(); + +private: + QFileInfo _modelFile; + QPushButton* _browseButton; + QComboBox* _modelType; +}; + #endif // hifi_ModelSelector_h \ No newline at end of file From d0d9d4bbacfb088eacb52ba0a8e351fdcf84bb46 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 10 Mar 2015 11:50:13 +0100 Subject: [PATCH 04/34] Introduced ModelPackager --- interface/src/ModelPackager.cpp | 36 +++++++++++++++++++++++++++++++++ interface/src/ModelPackager.h | 28 +++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 interface/src/ModelPackager.cpp create mode 100644 interface/src/ModelPackager.h diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp new file mode 100644 index 0000000000..091aa5753c --- /dev/null +++ b/interface/src/ModelPackager.cpp @@ -0,0 +1,36 @@ +// +// ModelPackager.cpp +// +// +// Created by Clement on 3/9/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ModelSelector.h" +#include "ModelPackager.h" + +void ModelPackager::package() { + ModelPackager packager; + if (packager.selectModel()) { + packager.editProperties(); + packager.zipModel(); + } +} + +bool ModelPackager::selectModel() { + ModelSelector selector; + return selector.exec() == QDialog::Accepted; +} + +void ModelPackager::editProperties() { + +} + +void ModelPackager::zipModel() { + +} + + diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h new file mode 100644 index 0000000000..c5d94643d9 --- /dev/null +++ b/interface/src/ModelPackager.h @@ -0,0 +1,28 @@ +// +// ModelPackager.h +// +// +// Created by Clement on 3/9/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ModelPackager_h +#define hifi_ModelPackager_h + +class ModelPackager { +public: + static void package(); + +private: + bool selectModel(); + void editProperties(); + void zipModel(); +}; + + + + +#endif // hifi_ModelPackager_h \ No newline at end of file From f1460e5c5b4d345330c6ef8e2b07d37e347bfd23 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 10 Mar 2015 11:50:49 +0100 Subject: [PATCH 05/34] Fixed missing forward declaration --- interface/src/ui/ModelsBrowser.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/ui/ModelsBrowser.h b/interface/src/ui/ModelsBrowser.h index fa09a67826..0c8bb59c85 100644 --- a/interface/src/ui/ModelsBrowser.h +++ b/interface/src/ui/ModelsBrowser.h @@ -16,6 +16,8 @@ #include #include +class QNetworkReply; + enum ModelType { ENTITY_MODEL, HEAD_MODEL, From 8bb974f01b1805a81653e708d42e051bdbb697dd Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 10 Mar 2015 14:50:38 +0100 Subject: [PATCH 06/34] Move ModelPropertiesDialog to own file --- interface/src/ModelPropertiesDialog.cpp | 244 ++++++++++++++++++++++++ interface/src/ModelPropertiesDialog.h | 83 ++++++++ 2 files changed, 327 insertions(+) create mode 100644 interface/src/ModelPropertiesDialog.cpp create mode 100644 interface/src/ModelPropertiesDialog.h diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp new file mode 100644 index 0000000000..de98407a2a --- /dev/null +++ b/interface/src/ModelPropertiesDialog.cpp @@ -0,0 +1,244 @@ +// +// ModelPropertiesDialog.cpp +// +// +// Created by Clement on 3/10/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ModelPropertiesDialog.h" + + +ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping, + const QString& basePath, const FBXGeometry& geometry) : +_modelType(modelType), +_originalMapping(originalMapping), +_basePath(basePath), +_geometry(geometry) +{ + setWindowTitle("Set Model Properties"); + + QFormLayout* form = new QFormLayout(); + setLayout(form); + + form->addRow("Name:", _name = new QLineEdit()); + + form->addRow("Texture Directory:", _textureDirectory = new QPushButton()); + connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory())); + + form->addRow("Scale:", _scale = new QDoubleSpinBox()); + _scale->setMaximum(FLT_MAX); + _scale->setSingleStep(0.01); + + if (_modelType != ENTITY_MODEL) { + if (_modelType == ATTACHMENT_MODEL) { + QHBoxLayout* translation = new QHBoxLayout(); + form->addRow("Translation:", translation); + translation->addWidget(_translationX = createTranslationBox()); + translation->addWidget(_translationY = createTranslationBox()); + translation->addWidget(_translationZ = createTranslationBox()); + form->addRow("Pivot About Center:", _pivotAboutCenter = new QCheckBox()); + form->addRow("Pivot Joint:", _pivotJoint = createJointBox()); + connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint())); + _pivotAboutCenter->setChecked(true); + + } else { + form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox()); + form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox()); + form->addRow("Neck Joint:", _neckJoint = createJointBox()); + } + if (_modelType == SKELETON_MODEL) { + form->addRow("Root Joint:", _rootJoint = createJointBox()); + form->addRow("Lean Joint:", _leanJoint = createJointBox()); + form->addRow("Head Joint:", _headJoint = createJointBox()); + form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox()); + form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox()); + + form->addRow("Free Joints:", _freeJoints = new QVBoxLayout()); + QPushButton* newFreeJoint = new QPushButton("New Free Joint"); + _freeJoints->addWidget(newFreeJoint); + connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint())); + } + } + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | + QDialogButtonBox::Cancel | QDialogButtonBox::Reset); + connect(buttons, SIGNAL(accepted()), SLOT(accept())); + connect(buttons, SIGNAL(rejected()), SLOT(reject())); + connect(buttons->button(QDialogButtonBox::Reset), SIGNAL(clicked(bool)), SLOT(reset())); + + form->addRow(buttons); + + // reset to initialize the fields + reset(); +} + +QVariantHash ModelPropertiesDialog::getMapping() const { + QVariantHash mapping = _originalMapping; + mapping.insert(NAME_FIELD, _name->text()); + mapping.insert(TEXDIR_FIELD, _textureDirectory->text()); + mapping.insert(SCALE_FIELD, QString::number(_scale->value())); + + // update the joint indices + QVariantHash jointIndices; + for (int i = 0; i < _geometry.joints.size(); i++) { + jointIndices.insert(_geometry.joints.at(i).name, QString::number(i)); + } + mapping.insert(JOINT_INDEX_FIELD, jointIndices); + + if (_modelType != ENTITY_MODEL) { + QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); + if (_modelType == ATTACHMENT_MODEL) { + glm::vec3 pivot; + if (_pivotAboutCenter->isChecked()) { + pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f; + + } else if (_pivotJoint->currentIndex() != 0) { + pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform); + } + mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value()); + mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value()); + mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value()); + + } else { + insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText()); + insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText()); + insertJointMapping(joints, "jointNeck", _neckJoint->currentText()); + } + if (_modelType == SKELETON_MODEL) { + insertJointMapping(joints, "jointRoot", _rootJoint->currentText()); + insertJointMapping(joints, "jointLean", _leanJoint->currentText()); + insertJointMapping(joints, "jointHead", _headJoint->currentText()); + insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText()); + insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText()); + + mapping.remove(FREE_JOINT_FIELD); + for (int i = 0; i < _freeJoints->count() - 1; i++) { + QComboBox* box = static_cast(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget()); + mapping.insertMulti(FREE_JOINT_FIELD, box->currentText()); + } + } + mapping.insert(JOINT_FIELD, joints); + } + + return mapping; +} + +static void setJointText(QComboBox* box, const QString& text) { + box->setCurrentIndex(qMax(box->findText(text), 0)); +} + +void ModelPropertiesDialog::reset() { + _name->setText(_originalMapping.value(NAME_FIELD).toString()); + _textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString()); + _scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble()); + + QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash(); + + if (_modelType != ENTITY_MODEL) { + if (_modelType == ATTACHMENT_MODEL) { + _translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble()); + _translationY->setValue(_originalMapping.value(TRANSLATION_Y_FIELD).toDouble()); + _translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble()); + _pivotAboutCenter->setChecked(true); + _pivotJoint->setCurrentIndex(0); + + } else { + setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString()); + setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString()); + setJointText(_neckJoint, jointHash.value("jointNeck").toString()); + } + if (_modelType == SKELETON_MODEL) { + setJointText(_rootJoint, jointHash.value("jointRoot").toString()); + setJointText(_leanJoint, jointHash.value("jointLean").toString()); + setJointText(_headJoint, jointHash.value("jointHead").toString()); + setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString()); + setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString()); + + while (_freeJoints->count() > 1) { + delete _freeJoints->itemAt(0)->widget(); + } + foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) { + QString jointName = joint.toString(); + if (_geometry.jointIndices.contains(jointName)) { + createNewFreeJoint(jointName); + } + } + } + } +} + +void ModelPropertiesDialog::chooseTextureDirectory() { + QString directory = QFileDialog::getExistingDirectory(this, "Choose Texture Directory", + _basePath + "/" + _textureDirectory->text()); + if (directory.isEmpty()) { + return; + } + if (!directory.startsWith(_basePath)) { + QMessageBox::warning(NULL, "Invalid texture directory", "Texture directory must be child of base path."); + return; + } + _textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1)); +} + +void ModelPropertiesDialog::updatePivotJoint() { + _pivotJoint->setEnabled(!_pivotAboutCenter->isChecked()); +} + +void ModelPropertiesDialog::createNewFreeJoint(const QString& joint) { + QWidget* freeJoint = new QWidget(); + QHBoxLayout* freeJointLayout = new QHBoxLayout(); + freeJointLayout->setContentsMargins(QMargins()); + freeJoint->setLayout(freeJointLayout); + QComboBox* jointBox = createJointBox(false); + jointBox->setCurrentText(joint); + freeJointLayout->addWidget(jointBox, 1); + QPushButton* deleteJoint = new QPushButton("Delete"); + freeJointLayout->addWidget(deleteJoint); + freeJoint->connect(deleteJoint, SIGNAL(clicked(bool)), SLOT(deleteLater())); + _freeJoints->insertWidget(_freeJoints->count() - 1, freeJoint); +} + +QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const { + QComboBox* box = new QComboBox(); + if (withNone) { + box->addItem("(none)"); + } + foreach (const FBXJoint& joint, _geometry.joints) { + if (joint.isSkeletonJoint || !_geometry.hasSkeletonJoints) { + box->addItem(joint.name); + } + } + return box; +} + +QDoubleSpinBox* ModelPropertiesDialog::createTranslationBox() const { + QDoubleSpinBox* box = new QDoubleSpinBox(); + const double MAX_TRANSLATION = 1000000.0; + box->setMinimum(-MAX_TRANSLATION); + box->setMaximum(MAX_TRANSLATION); + return box; +} + +void ModelPropertiesDialog::insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const { + if (_geometry.jointIndices.contains(name)) { + joints.insert(joint, name); + } else { + joints.remove(joint); + } +} diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h new file mode 100644 index 0000000000..65c5be6c21 --- /dev/null +++ b/interface/src/ModelPropertiesDialog.h @@ -0,0 +1,83 @@ +// +// ModelPropertiesDialog.h +// +// +// Created by Clement on 3/10/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ModelPropertiesDialog_h +#define hifi_ModelPropertiesDialog_h + +#include + +#include + +#include "ui/ModelsBrowser.h" + +class QDoubleSpinBox; +class QComboBox; +class QCheckBox; +class QVBoxLayout; + +static const QString NAME_FIELD = "name"; +static const QString FILENAME_FIELD = "filename"; +static const QString TEXDIR_FIELD = "texdir"; +static const QString LOD_FIELD = "lod"; +static const QString JOINT_INDEX_FIELD = "jointIndex"; +static const QString SCALE_FIELD = "scale"; +static const QString TRANSLATION_X_FIELD = "tx"; +static const QString TRANSLATION_Y_FIELD = "ty"; +static const QString TRANSLATION_Z_FIELD = "tz"; +static const QString JOINT_FIELD = "joint"; +static const QString FREE_JOINT_FIELD = "freeJoint"; +static const QString BLENDSHAPE_FIELD = "bs"; + +/// A dialog that allows customization of various model properties. +class ModelPropertiesDialog : public QDialog { + Q_OBJECT + +public: + ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping, + const QString& basePath, const FBXGeometry& geometry); + + QVariantHash getMapping() const; + +private slots: + void reset(); + void chooseTextureDirectory(); + void updatePivotJoint(); + void createNewFreeJoint(const QString& joint = QString()); + +private: + QComboBox* createJointBox(bool withNone = true) const; + QDoubleSpinBox* createTranslationBox() const; + void insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const; + + ModelType _modelType; + QVariantHash _originalMapping; + QString _basePath; + FBXGeometry _geometry; + QLineEdit* _name = nullptr; + QPushButton* _textureDirectory = nullptr; + QDoubleSpinBox* _scale = nullptr; + QDoubleSpinBox* _translationX = nullptr; + QDoubleSpinBox* _translationY = nullptr; + QDoubleSpinBox* _translationZ = nullptr; + QCheckBox* _pivotAboutCenter = nullptr; + QComboBox* _pivotJoint = nullptr; + QComboBox* _leftEyeJoint = nullptr; + QComboBox* _rightEyeJoint = nullptr; + QComboBox* _neckJoint = nullptr; + QComboBox* _rootJoint = nullptr; + QComboBox* _leanJoint = nullptr; + QComboBox* _headJoint = nullptr; + QComboBox* _leftHandJoint = nullptr; + QComboBox* _rightHandJoint = nullptr; + QVBoxLayout* _freeJoints = nullptr; +}; + +#endif // hifi_ModelPropertiesDialog_h \ No newline at end of file From 3d11dde8ad44d867b1b926f822891b9630015951 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 10 Mar 2015 20:27:31 -0700 Subject: [PATCH 07/34] Check that the user configures a skeleton root joint Instead of just checking that the model has a "Hips" joint. --- interface/src/ModelUploader.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index ad57fe21ad..a6b51aa938 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -178,11 +178,22 @@ bool ModelUploader::zip() { QByteArray fbxContents = fbx.readAll(); FBXGeometry geometry = readFBX(fbxContents, QVariantHash()); - // Make sure that a skeleton model has a skeleton - if (_modelType == SKELETON_MODEL && !geometry.getJointNames().contains("Hips")) { - qDebug() << QString("[Warning] %1 does not contain a skeleton.").arg(filename); + // make sure we have some basic mappings + populateBasicMapping(mapping, filename, geometry); + + // open the dialog to configure the rest + ModelPropertiesDialog properties(_modelType, mapping, basePath, geometry); + if (properties.exec() == QDialog::Rejected) { + return false; + } + mapping = properties.getMapping(); - QString message = "Your selected skeleton model has no skeleton.\n\nThe upload will be canceled."; + // Make sure that a mapping for the root joint has been specified + QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); + if (!joints.contains("jointRoot")) { + qDebug() << QString("[Warning] %1 root joint not configured for skeleton.").arg(filename); + + QString message = "Your did not configure a root joint for your skeleton model.\n\nThe upload will be canceled."; QMessageBox msgBox; msgBox.setWindowTitle("Model Upload"); msgBox.setText(message); @@ -193,16 +204,6 @@ bool ModelUploader::zip() { return false; } - // make sure we have some basic mappings - populateBasicMapping(mapping, filename, geometry); - - // open the dialog to configure the rest - ModelPropertiesDialog properties(_modelType, mapping, basePath, geometry); - if (properties.exec() == QDialog::Rejected) { - return false; - } - mapping = properties.getMapping(); - QByteArray nameField = mapping.value(NAME_FIELD).toByteArray(); QString urlBase; if (!nameField.isEmpty()) { From 40c593bd0d69ae25f2c2a186dc9d37fbee4ae99b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 11 Mar 2015 16:17:13 +0100 Subject: [PATCH 08/34] Working model packager --- interface/src/ModelPackager.cpp | 381 +++++++++++++++++++++++++++++++- interface/src/ModelPackager.h | 31 ++- 2 files changed, 399 insertions(+), 13 deletions(-) diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 091aa5753c..bbd78e274c 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -9,28 +9,391 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include +#include +#include +#include + #include "ModelSelector.h" +#include "ModelPropertiesDialog.h" + #include "ModelPackager.h" -void ModelPackager::package() { +static const int MAX_TEXTURE_SIZE = 1024; + +bool ModelPackager::package() { ModelPackager packager; - if (packager.selectModel()) { - packager.editProperties(); - packager.zipModel(); + if (!packager.selectModel()) { + return false; } + if (!packager.loadModel()) { + return false; + } + if (!packager.editProperties()) { + return false; + } + if (!packager.zipModel()) { + return false; + } + return true; } bool ModelPackager::selectModel() { ModelSelector selector; - return selector.exec() == QDialog::Accepted; + if(selector.exec() == QDialog::Accepted) { + _modelFile = selector.getFileInfo(); + _modelType = selector.getModelType(); + return true; + } + return false; } -void ModelPackager::editProperties() { +bool ModelPackager::loadModel() { + // First we check the FST file (if any) + if (_modelFile.completeSuffix().contains("fst")) { + QFile fst(_modelFile.filePath()); + if (!fst.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(NULL, + QString("ModelPackager::loadModel()"), + QString("Could not open FST file %1").arg(_modelFile.filePath()), + QMessageBox::Ok); + qWarning() << QString("ModelPackager::loadModel(): Could not open FST file %1").arg(_modelFile.filePath()); + return false; + } + qDebug() << "Reading FST file : " << _modelFile.filePath(); + _mapping = readMapping(fst.readAll()); + fst.close(); + + _fbxInfo = QFileInfo(_modelFile.path() + "/" + _mapping.value(FILENAME_FIELD).toString()); + } else { + _fbxInfo = QFileInfo(_modelFile.filePath()); + } -} - -void ModelPackager::zipModel() { + // open the fbx file + QFile fbx(_fbxInfo.filePath()); + if (!_fbxInfo.exists() || !_fbxInfo.isFile() || !fbx.open(QIODevice::ReadOnly)) { + QMessageBox::warning(NULL, + QString("ModelPackager::loadModel()"), + QString("Could not open FBX file %1").arg(_fbxInfo.filePath()), + QMessageBox::Ok); + qWarning() << QString("ModelPackager::loadModel(): Could not open FBX file %1").arg(_fbxInfo.filePath()); + return false; + } + qDebug() << "Reading FBX file : " << _fbxInfo.filePath(); + QByteArray fbxContents = fbx.readAll(); + _geometry = readFBX(fbxContents, QVariantHash()); +#if 0 /// Temporarily remove this check until CtrlAltDavid can come up with a fix. + // Make sure that a skeleton model has a skeleton + if (_modelType == SKELETON_MODEL) { + if (_geometry.rootJointIndex == -1) { + + QString message = "Your selected skeleton model has no skeleton.\n\nThe upload will be canceled."; + QMessageBox msgBox; + msgBox.setWindowTitle("Model Upload"); + msgBox.setText(message); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + + return false; + } + } +#endif + + // make sure we have some basic mappings + populateBasicMapping(_mapping, _fbxInfo.filePath(), _geometry); + return true; +} + +bool ModelPackager::editProperties() { + // open the dialog to configure the rest + ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), _geometry); + if (properties.exec() == QDialog::Rejected) { + return false; + } + _mapping = properties.getMapping(); + return true; +} + +void copyDirectoryContent(QDir& from, QDir& to) { + for (auto entry : from.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | + QDir::NoSymLinks | QDir::Readable)) { + if (entry.isDir()) { + to.mkdir(entry.fileName()); + from.cd(entry.fileName()); + to.cd(entry.fileName()); + copyDirectoryContent(from, to); + from.cdUp(); + to.cdUp(); + } else { // Files + QFile file(entry.absoluteFilePath()); + QString newPath = to.absolutePath() + "/" + entry.fileName(); + if (to.exists(entry.fileName())) { + QFile overridenFile(newPath); + overridenFile.remove(); + } + file.copy(newPath); + } + } +} + +bool ModelPackager::zipModel() { + QTemporaryDir dir; + dir.setAutoRemove(true); + QDir tempDir(dir.path()); + + QByteArray nameField = _mapping.value(NAME_FIELD).toByteArray(); + tempDir.mkpath(nameField + "/textures"); + QDir fbxDir(tempDir.path() + "/" + nameField); + QDir texDir(fbxDir.path() + "/textures"); + + // Copy textures + listTextures(); + if (!_textures.empty()) { + QByteArray texdirField = _mapping.value(TEXDIR_FIELD).toByteArray(); + _texDir = _modelFile.path() + "/" + texdirField; + copyTextures(_texDir, texDir); + } + + // Copy LODs + QVariantHash lodField = _mapping.value(LOD_FIELD).toHash(); + if (!lodField.empty()) { + for (auto it = lodField.constBegin(); it != lodField.constEnd(); ++it) { + QString oldPath = _modelFile.path() + "/" + it.key(); + QFile lod(oldPath); + QString newPath = fbxDir.path() + "/" + QFileInfo(lod).fileName(); + if (lod.exists()) { + lod.copy(newPath); + } + } + } + + // Copy FBX + QFile fbx(_fbxInfo.filePath()); + QByteArray filenameField = _mapping.value(FILENAME_FIELD).toByteArray(); + QString newPath = fbxDir.path() + "/" + QFileInfo(filenameField).fileName(); + fbx.copy(newPath); + + // Correct FST + _mapping[FILENAME_FIELD] = tempDir.relativeFilePath(newPath); + _mapping[TEXDIR_FIELD] = tempDir.relativeFilePath(texDir.path()); + + // Copy FST + QFile fst(tempDir.path() + "/" + nameField + ".fst"); + if (fst.open(QIODevice::WriteOnly)) { + fst.write(writeMapping(_mapping)); + fst.close(); + } else { + qDebug() << "Couldn't write FST file" << fst.fileName(); + return false; + } + + + QString saveDirPath = QFileDialog::getExistingDirectory(nullptr, "Save Model", + "", QFileDialog::ShowDirsOnly); + if (saveDirPath.isEmpty()) { + qDebug() << "Invalid directory" << saveDirPath; + return false; + } + + QDir saveDir(saveDirPath); + copyDirectoryContent(tempDir, saveDir); + return true; +} + +void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry) { + if (!mapping.contains(NAME_FIELD)) { + mapping.insert(NAME_FIELD, QFileInfo(filename).baseName()); + } + + if (!mapping.contains(FILENAME_FIELD)) { + QDir root(_modelFile.path()); + mapping.insert(FILENAME_FIELD, root.relativeFilePath(filename)); + } + if (!mapping.contains(TEXDIR_FIELD)) { + mapping.insert(TEXDIR_FIELD, "."); + } + + // mixamo/autodesk defaults + if (!mapping.contains(SCALE_FIELD)) { + mapping.insert(SCALE_FIELD, 1.0); + } + QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); + if (!joints.contains("jointEyeLeft")) { + joints.insert("jointEyeLeft", geometry.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" : + (geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye")); + } + if (!joints.contains("jointEyeRight")) { + joints.insert("jointEyeRight", geometry.jointIndices.contains("jointEyeRight") ? "jointEyeRight" : + geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye"); + } + if (!joints.contains("jointNeck")) { + joints.insert("jointNeck", geometry.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck"); + } + if (!joints.contains("jointRoot")) { + joints.insert("jointRoot", "Hips"); + } + if (!joints.contains("jointLean")) { + joints.insert("jointLean", "Spine"); + } + if (!joints.contains("jointHead")) { + const char* topName = (geometry.applicationName == "mixamo.com") ? "HeadTop_End" : "HeadEnd"; + joints.insert("jointHead", geometry.jointIndices.contains(topName) ? topName : "Head"); + } + if (!joints.contains("jointLeftHand")) { + joints.insert("jointLeftHand", "LeftHand"); + } + if (!joints.contains("jointRightHand")) { + joints.insert("jointRightHand", "RightHand"); + } + mapping.insert(JOINT_FIELD, joints); + if (!mapping.contains(FREE_JOINT_FIELD)) { + mapping.insertMulti(FREE_JOINT_FIELD, "LeftArm"); + mapping.insertMulti(FREE_JOINT_FIELD, "LeftForeArm"); + mapping.insertMulti(FREE_JOINT_FIELD, "RightArm"); + mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm"); + } + + // mixamo blendshapes - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will + // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file + bool likelyMixamoFile = geometry.applicationName == "mixamo.com" || + (geometry.blendshapeChannelNames.contains("BrowsDown_Right") && + geometry.blendshapeChannelNames.contains("MouthOpen") && + geometry.blendshapeChannelNames.contains("Blink_Left") && + geometry.blendshapeChannelNames.contains("Blink_Right") && + geometry.blendshapeChannelNames.contains("Squint_Right")); + + if (!mapping.contains(BLENDSHAPE_FIELD) && likelyMixamoFile) { + QVariantHash blendshapes; + blendshapes.insertMulti("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0); + blendshapes.insertMulti("BrowsD_R", QVariantList() << "BrowsDown_Right" << 1.0); + blendshapes.insertMulti("BrowsU_C", QVariantList() << "BrowsUp_Left" << 1.0); + blendshapes.insertMulti("BrowsU_C", QVariantList() << "BrowsUp_Right" << 1.0); + blendshapes.insertMulti("BrowsU_L", QVariantList() << "BrowsUp_Left" << 1.0); + blendshapes.insertMulti("BrowsU_R", QVariantList() << "BrowsUp_Right" << 1.0); + blendshapes.insertMulti("ChinLowerRaise", QVariantList() << "Jaw_Up" << 1.0); + blendshapes.insertMulti("ChinUpperRaise", QVariantList() << "UpperLipUp_Left" << 0.5); + blendshapes.insertMulti("ChinUpperRaise", QVariantList() << "UpperLipUp_Right" << 0.5); + blendshapes.insertMulti("EyeBlink_L", QVariantList() << "Blink_Left" << 1.0); + blendshapes.insertMulti("EyeBlink_R", QVariantList() << "Blink_Right" << 1.0); + blendshapes.insertMulti("EyeOpen_L", QVariantList() << "EyesWide_Left" << 1.0); + blendshapes.insertMulti("EyeOpen_R", QVariantList() << "EyesWide_Right" << 1.0); + blendshapes.insertMulti("EyeSquint_L", QVariantList() << "Squint_Left" << 1.0); + blendshapes.insertMulti("EyeSquint_R", QVariantList() << "Squint_Right" << 1.0); + blendshapes.insertMulti("JawFwd", QVariantList() << "JawForeward" << 1.0); + blendshapes.insertMulti("JawLeft", QVariantList() << "JawRotateY_Left" << 0.5); + blendshapes.insertMulti("JawOpen", QVariantList() << "MouthOpen" << 0.7); + blendshapes.insertMulti("JawRight", QVariantList() << "Jaw_Right" << 1.0); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "JawForeward" << 0.39); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "Jaw_Down" << 0.36); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthNarrow_Left" << 1.0); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthNarrow_Right" << 1.0); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthWhistle_NarrowAdjust_Left" << 0.5); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthWhistle_NarrowAdjust_Right" << 0.5); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "TongueUp" << 1.0); + blendshapes.insertMulti("LipsLowerClose", QVariantList() << "LowerLipIn" << 1.0); + blendshapes.insertMulti("LipsLowerDown", QVariantList() << "LowerLipDown_Left" << 0.7); + blendshapes.insertMulti("LipsLowerDown", QVariantList() << "LowerLipDown_Right" << 0.7); + blendshapes.insertMulti("LipsLowerOpen", QVariantList() << "LowerLipOut" << 1.0); + blendshapes.insertMulti("LipsPucker", QVariantList() << "MouthNarrow_Left" << 1.0); + blendshapes.insertMulti("LipsPucker", QVariantList() << "MouthNarrow_Right" << 1.0); + blendshapes.insertMulti("LipsUpperClose", QVariantList() << "UpperLipIn" << 1.0); + blendshapes.insertMulti("LipsUpperOpen", QVariantList() << "UpperLipOut" << 1.0); + blendshapes.insertMulti("LipsUpperUp", QVariantList() << "UpperLipUp_Left" << 0.7); + blendshapes.insertMulti("LipsUpperUp", QVariantList() << "UpperLipUp_Right" << 0.7); + blendshapes.insertMulti("MouthDimple_L", QVariantList() << "Smile_Left" << 0.25); + blendshapes.insertMulti("MouthDimple_R", QVariantList() << "Smile_Right" << 0.25); + blendshapes.insertMulti("MouthFrown_L", QVariantList() << "Frown_Left" << 1.0); + blendshapes.insertMulti("MouthFrown_R", QVariantList() << "Frown_Right" << 1.0); + blendshapes.insertMulti("MouthLeft", QVariantList() << "Midmouth_Left" << 1.0); + blendshapes.insertMulti("MouthRight", QVariantList() << "Midmouth_Right" << 1.0); + blendshapes.insertMulti("MouthSmile_L", QVariantList() << "Smile_Left" << 1.0); + blendshapes.insertMulti("MouthSmile_R", QVariantList() << "Smile_Right" << 1.0); + blendshapes.insertMulti("Puff", QVariantList() << "CheekPuff_Left" << 1.0); + blendshapes.insertMulti("Puff", QVariantList() << "CheekPuff_Right" << 1.0); + blendshapes.insertMulti("Sneer", QVariantList() << "NoseScrunch_Left" << 0.75); + blendshapes.insertMulti("Sneer", QVariantList() << "NoseScrunch_Right" << 0.75); + blendshapes.insertMulti("Sneer", QVariantList() << "Squint_Left" << 0.5); + blendshapes.insertMulti("Sneer", QVariantList() << "Squint_Right" << 0.5); + mapping.insert(BLENDSHAPE_FIELD, blendshapes); + } +} + +void ModelPackager::listTextures() { + _textures.clear(); + foreach (FBXMesh mesh, _geometry.meshes) { + foreach (FBXMeshPart part, mesh.parts) { + if (!part.diffuseTexture.filename.isEmpty() && part.diffuseTexture.content.isEmpty() && + !_textures.contains(part.diffuseTexture.filename)) { + _textures << part.diffuseTexture.filename; + } + if (!part.normalTexture.filename.isEmpty() && part.normalTexture.content.isEmpty() && + !_textures.contains(part.normalTexture.filename)) { + + _textures << part.normalTexture.filename; + } + if (!part.specularTexture.filename.isEmpty() && part.specularTexture.content.isEmpty() && + !_textures.contains(part.specularTexture.filename)) { + _textures << part.specularTexture.filename; + } + if (!part.emissiveTexture.filename.isEmpty() && part.emissiveTexture.content.isEmpty() && + !_textures.contains(part.emissiveTexture.filename)) { + _textures << part.emissiveTexture.filename; + } + } + } +} + +bool ModelPackager::copyTextures(const QString& oldDir, const QDir& newDir) { + QString errors; + for (auto texture : _textures) { + QString oldPath = oldDir + "/" + texture; + QString newPath = newDir.path() + "/" + texture; + + // Make sure path exists + if (texture.contains("/")) { + QString dirPath = newDir.relativeFilePath(QFileInfo(newPath).path()); + newDir.mkpath(dirPath); + } + + QFile texFile(oldPath); + if (texFile.exists() && texFile.open(QIODevice::ReadOnly)) { + // Check if texture needs to be recoded + QFileInfo fileInfo(oldPath); + QString extension = fileInfo.suffix().toLower(); + bool isJpeg = (extension == "jpg"); + bool mustRecode = !(isJpeg || extension == "png"); + QImage image = QImage::fromData(texFile.readAll()); + + // Recode texture if too big + if (image.width() > MAX_TEXTURE_SIZE || image.height() > MAX_TEXTURE_SIZE) { + image = image.scaled(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, Qt::KeepAspectRatio); + mustRecode = true; + } + + // Copy texture + if (mustRecode) { + QFile newTexFile(newPath); + newTexFile.open(QIODevice::WriteOnly); + image.save(&newTexFile, isJpeg ? "JPG" : "PNG"); + } else { + texFile.copy(newPath); + } + } else { + errors += QString("\n%1").arg(oldPath); + } + } + + if (!errors.isEmpty()) { + QMessageBox::warning(nullptr, "ModelPackager::copyTextures()", + "Missing textures:" + errors); + qDebug() << "ModelPackager::copyTextures():" << errors; + return false; + } + + return true; } diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index c5d94643d9..c62388f196 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -12,14 +12,37 @@ #ifndef hifi_ModelPackager_h #define hifi_ModelPackager_h -class ModelPackager { +#include +#include + +#include + +#include "ui/ModelsBrowser.h" + +class ModelPackager : public QObject { public: - static void package(); + static bool package(); private: bool selectModel(); - void editProperties(); - void zipModel(); + + bool loadModel(); + bool editProperties(); + bool zipModel(); + + void populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry); + + void listTextures(); + bool copyTextures(const QString& oldDir, const QDir& newDir); + + QFileInfo _modelFile; + QFileInfo _fbxInfo; + ModelType _modelType; + QString _texDir; + + QVariantHash _mapping; + FBXGeometry _geometry; + QStringList _textures; }; From 95a6df71027165bc0332a8cf3eeff5a5c3fa6252 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 11 Mar 2015 16:18:39 +0100 Subject: [PATCH 09/34] Remove ModelUploader --- interface/src/ModelUploader.cpp | 883 -------------------------------- interface/src/ModelUploader.h | 126 ----- 2 files changed, 1009 deletions(-) delete mode 100644 interface/src/ModelUploader.cpp delete mode 100644 interface/src/ModelUploader.h diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp deleted file mode 100644 index aca02cb904..0000000000 --- a/interface/src/ModelUploader.cpp +++ /dev/null @@ -1,883 +0,0 @@ -// -// ModelUploader.cpp -// interface/src -// -// Created by Clément Brisset on 3/4/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "ModelUploader.h" - - -static const QString NAME_FIELD = "name"; -static const QString FILENAME_FIELD = "filename"; -static const QString TEXDIR_FIELD = "texdir"; -static const QString LOD_FIELD = "lod"; -static const QString JOINT_INDEX_FIELD = "jointIndex"; -static const QString SCALE_FIELD = "scale"; -static const QString TRANSLATION_X_FIELD = "tx"; -static const QString TRANSLATION_Y_FIELD = "ty"; -static const QString TRANSLATION_Z_FIELD = "tz"; -static const QString JOINT_FIELD = "joint"; -static const QString FREE_JOINT_FIELD = "freeJoint"; -static const QString BLENDSHAPE_FIELD = "bs"; - -static const QString S3_URL = "http://public.highfidelity.io"; -static const QString MODEL_URL = "/api/v1/models"; - -static const unsigned long long MAX_SIZE = 50 * 1024 * BYTES_PER_MEGABYTES; // 50 GB (Virtually remove limit) -static const int MAX_TEXTURE_SIZE = 1024; -static const int TIMEOUT = 1000; -static const int MAX_CHECK = 30; - -static const int QCOMPRESS_HEADER_POSITION = 0; -static const int QCOMPRESS_HEADER_SIZE = 4; - -Setting::Handle ModelUploader::_lastModelUploadLocation("LastModelUploadLocation", - QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); - -void ModelUploader::uploadModel(ModelType modelType) { - ModelUploader* uploader = new ModelUploader(modelType); - QThread* thread = new QThread(); - thread->setObjectName("Model Uploader"); - thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit())); - thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater())); - uploader->connect(thread, SIGNAL(started()), SLOT(send())); - - thread->start(); -} - -void ModelUploader::uploadHead() { - uploadModel(HEAD_MODEL); -} - -void ModelUploader::uploadSkeleton() { - uploadModel(SKELETON_MODEL); -} - -void ModelUploader::uploadAttachment() { - uploadModel(ATTACHMENT_MODEL); -} - -void ModelUploader::uploadEntity() { - uploadModel(ENTITY_MODEL); -} - -ModelUploader::ModelUploader(ModelType modelType) : - _lodCount(-1), - _texturesCount(-1), - _totalSize(0), - _modelType(modelType), - _readyToSend(false), - _dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)), - _numberOfChecks(MAX_CHECK) -{ - connect(&_timer, SIGNAL(timeout()), SLOT(checkS3())); -} - -ModelUploader::~ModelUploader() { - delete _dataMultiPart; -} - -bool ModelUploader::zip() { - // File Dialog - QString lastLocation = _lastModelUploadLocation.get(); - - if (lastLocation.isEmpty()) { - lastLocation = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); - // Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475 -#ifdef __APPLE__ - lastLocation.append("/model.fst"); -#endif - } - - - QString filename = QFileDialog::getOpenFileName(NULL, "Select your model file ...", - lastLocation, "Model files (*.fst *.fbx)"); - if (filename == "") { - // If the user canceled we return. - return false; - } - _lastModelUploadLocation.set(filename); - - // First we check the FST file (if any) - QFile* fst; - QVariantHash mapping; - QString basePath; - QString fbxFile; - if (filename.toLower().endsWith(".fst")) { - fst = new QFile(filename, this); - if (!fst->open(QFile::ReadOnly | QFile::Text)) { - QMessageBox::warning(NULL, - QString("ModelUploader::zip()"), - QString("Could not open FST file."), - QMessageBox::Ok); - qDebug() << "[Warning] " << QString("Could not open FST file."); - return false; - } - qDebug() << "Reading FST file : " << QFileInfo(*fst).filePath(); - mapping = readMapping(fst->readAll()); - basePath = QFileInfo(*fst).path(); - fbxFile = basePath + "/" + mapping.value(FILENAME_FIELD).toString(); - QFileInfo fbxInfo(fbxFile); - if (!fbxInfo.exists() || !fbxInfo.isFile()) { // Check existence - QMessageBox::warning(NULL, - QString("ModelUploader::zip()"), - QString("FBX file %1 could not be found.").arg(fbxInfo.fileName()), - QMessageBox::Ok); - qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(fbxInfo.fileName()); - return false; - } - } else { - fst = new QTemporaryFile(this); - fst->open(QFile::WriteOnly); - fbxFile = filename; - basePath = QFileInfo(filename).path(); - mapping.insert(FILENAME_FIELD, QFileInfo(filename).fileName()); - } - - // open the fbx file - QFile fbx(fbxFile); - if (!fbx.open(QIODevice::ReadOnly)) { - return false; - } - QByteArray fbxContents = fbx.readAll(); - FBXGeometry geometry = readFBX(fbxContents, QVariantHash()); - - #if 0 /// Temporarily remove this check until CtrlAltDavid can come up with a fix. - // Make sure that a skeleton model has a skeleton - if (_modelType == SKELETON_MODEL) { - if (geometry.rootJointIndex == -1) { - - QString message = "Your selected skeleton model has no skeleton.\n\nThe upload will be canceled."; - QMessageBox msgBox; - msgBox.setWindowTitle("Model Upload"); - msgBox.setText(message); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); - - return false; - } - } - #endif - - // make sure we have some basic mappings - populateBasicMapping(mapping, filename, geometry); - - // open the dialog to configure the rest - ModelPropertiesDialog properties(_modelType, mapping, basePath, geometry); - if (properties.exec() == QDialog::Rejected) { - return false; - } - mapping = properties.getMapping(); - - QByteArray nameField = mapping.value(NAME_FIELD).toByteArray(); - QString urlBase; - if (!nameField.isEmpty()) { - QHttpPart textPart; - textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"model_name\""); - textPart.setBody(nameField); - _dataMultiPart->append(textPart); - urlBase = S3_URL + "/models/" + MODEL_TYPE_NAMES[_modelType] + "/" + nameField; - _url = urlBase + ".fst"; - - } else { - QMessageBox::warning(NULL, - QString("ModelUploader::zip()"), - QString("Model name is missing in the .fst file."), - QMessageBox::Ok); - qDebug() << "[Warning] " << QString("Model name is missing in the .fst file."); - return false; - } - - QByteArray texdirField = mapping.value(TEXDIR_FIELD).toByteArray(); - QString texDir; - _textureBase = urlBase + "/textures/"; - if (!texdirField.isEmpty()) { - texDir = basePath + "/" + texdirField; - QFileInfo texInfo(texDir); - if (!texInfo.exists() || !texInfo.isDir()) { - QMessageBox::warning(NULL, - QString("ModelUploader::zip()"), - QString("Texture directory could not be found."), - QMessageBox::Ok); - qDebug() << "[Warning] " << QString("Texture directory could not be found."); - return false; - } - } - - QVariantHash lodField = mapping.value(LOD_FIELD).toHash(); - for (QVariantHash::const_iterator it = lodField.constBegin(); it != lodField.constEnd(); it++) { - QFileInfo lod(basePath + "/" + it.key()); - if (!lod.exists() || !lod.isFile()) { // Check existence - QMessageBox::warning(NULL, - QString("ModelUploader::zip()"), - QString("LOD file %1 could not be found.").arg(lod.fileName()), - QMessageBox::Ok); - qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(lod.fileName()); - } - // Compress and copy - if (!addPart(lod.filePath(), QString("lod%1").arg(++_lodCount))) { - return false; - } - } - - // Write out, compress and copy the fst - if (!addPart(*fst, writeMapping(mapping), QString("fst"))) { - return false; - } - - // Compress and copy the fbx - if (!addPart(fbx, fbxContents, "fbx")) { - return false; - } - - if (!addTextures(texDir, geometry)) { - return false; - } - - QHttpPart textPart; - textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" - " name=\"model_category\""); - textPart.setBody(MODEL_TYPE_NAMES[_modelType]); - _dataMultiPart->append(textPart); - - _readyToSend = true; - return true; -} - -void ModelUploader::populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry) { - if (!mapping.contains(NAME_FIELD)) { - mapping.insert(NAME_FIELD, QFileInfo(filename).baseName()); - } - if (!mapping.contains(TEXDIR_FIELD)) { - mapping.insert(TEXDIR_FIELD, "."); - } - - // mixamo/autodesk defaults - if (!mapping.contains(SCALE_FIELD)) { - mapping.insert(SCALE_FIELD, 1.0); - } - QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); - if (!joints.contains("jointEyeLeft")) { - joints.insert("jointEyeLeft", geometry.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" : - (geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye")); - } - if (!joints.contains("jointEyeRight")) { - joints.insert("jointEyeRight", geometry.jointIndices.contains("jointEyeRight") ? "jointEyeRight" : - geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye"); - } - if (!joints.contains("jointNeck")) { - joints.insert("jointNeck", geometry.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck"); - } - if (!joints.contains("jointRoot")) { - joints.insert("jointRoot", "Hips"); - } - if (!joints.contains("jointLean")) { - joints.insert("jointLean", "Spine"); - } - if (!joints.contains("jointHead")) { - const char* topName = (geometry.applicationName == "mixamo.com") ? "HeadTop_End" : "HeadEnd"; - joints.insert("jointHead", geometry.jointIndices.contains(topName) ? topName : "Head"); - } - if (!joints.contains("jointLeftHand")) { - joints.insert("jointLeftHand", "LeftHand"); - } - if (!joints.contains("jointRightHand")) { - joints.insert("jointRightHand", "RightHand"); - } - mapping.insert(JOINT_FIELD, joints); - if (!mapping.contains(FREE_JOINT_FIELD)) { - mapping.insertMulti(FREE_JOINT_FIELD, "LeftArm"); - mapping.insertMulti(FREE_JOINT_FIELD, "LeftForeArm"); - mapping.insertMulti(FREE_JOINT_FIELD, "RightArm"); - mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm"); - } - - // mixamo blendshapes - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will - // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file - bool likelyMixamoFile = geometry.applicationName == "mixamo.com" || - (geometry.blendshapeChannelNames.contains("BrowsDown_Right") && - geometry.blendshapeChannelNames.contains("MouthOpen") && - geometry.blendshapeChannelNames.contains("Blink_Left") && - geometry.blendshapeChannelNames.contains("Blink_Right") && - geometry.blendshapeChannelNames.contains("Squint_Right")); - - if (!mapping.contains(BLENDSHAPE_FIELD) && likelyMixamoFile) { - QVariantHash blendshapes; - blendshapes.insertMulti("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0); - blendshapes.insertMulti("BrowsD_R", QVariantList() << "BrowsDown_Right" << 1.0); - blendshapes.insertMulti("BrowsU_C", QVariantList() << "BrowsUp_Left" << 1.0); - blendshapes.insertMulti("BrowsU_C", QVariantList() << "BrowsUp_Right" << 1.0); - blendshapes.insertMulti("BrowsU_L", QVariantList() << "BrowsUp_Left" << 1.0); - blendshapes.insertMulti("BrowsU_R", QVariantList() << "BrowsUp_Right" << 1.0); - blendshapes.insertMulti("ChinLowerRaise", QVariantList() << "Jaw_Up" << 1.0); - blendshapes.insertMulti("ChinUpperRaise", QVariantList() << "UpperLipUp_Left" << 0.5); - blendshapes.insertMulti("ChinUpperRaise", QVariantList() << "UpperLipUp_Right" << 0.5); - blendshapes.insertMulti("EyeBlink_L", QVariantList() << "Blink_Left" << 1.0); - blendshapes.insertMulti("EyeBlink_R", QVariantList() << "Blink_Right" << 1.0); - blendshapes.insertMulti("EyeOpen_L", QVariantList() << "EyesWide_Left" << 1.0); - blendshapes.insertMulti("EyeOpen_R", QVariantList() << "EyesWide_Right" << 1.0); - blendshapes.insertMulti("EyeSquint_L", QVariantList() << "Squint_Left" << 1.0); - blendshapes.insertMulti("EyeSquint_R", QVariantList() << "Squint_Right" << 1.0); - blendshapes.insertMulti("JawFwd", QVariantList() << "JawForeward" << 1.0); - blendshapes.insertMulti("JawLeft", QVariantList() << "JawRotateY_Left" << 0.5); - blendshapes.insertMulti("JawOpen", QVariantList() << "MouthOpen" << 0.7); - blendshapes.insertMulti("JawRight", QVariantList() << "Jaw_Right" << 1.0); - blendshapes.insertMulti("LipsFunnel", QVariantList() << "JawForeward" << 0.39); - blendshapes.insertMulti("LipsFunnel", QVariantList() << "Jaw_Down" << 0.36); - blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthNarrow_Left" << 1.0); - blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthNarrow_Right" << 1.0); - blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthWhistle_NarrowAdjust_Left" << 0.5); - blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthWhistle_NarrowAdjust_Right" << 0.5); - blendshapes.insertMulti("LipsFunnel", QVariantList() << "TongueUp" << 1.0); - blendshapes.insertMulti("LipsLowerClose", QVariantList() << "LowerLipIn" << 1.0); - blendshapes.insertMulti("LipsLowerDown", QVariantList() << "LowerLipDown_Left" << 0.7); - blendshapes.insertMulti("LipsLowerDown", QVariantList() << "LowerLipDown_Right" << 0.7); - blendshapes.insertMulti("LipsLowerOpen", QVariantList() << "LowerLipOut" << 1.0); - blendshapes.insertMulti("LipsPucker", QVariantList() << "MouthNarrow_Left" << 1.0); - blendshapes.insertMulti("LipsPucker", QVariantList() << "MouthNarrow_Right" << 1.0); - blendshapes.insertMulti("LipsUpperClose", QVariantList() << "UpperLipIn" << 1.0); - blendshapes.insertMulti("LipsUpperOpen", QVariantList() << "UpperLipOut" << 1.0); - blendshapes.insertMulti("LipsUpperUp", QVariantList() << "UpperLipUp_Left" << 0.7); - blendshapes.insertMulti("LipsUpperUp", QVariantList() << "UpperLipUp_Right" << 0.7); - blendshapes.insertMulti("MouthDimple_L", QVariantList() << "Smile_Left" << 0.25); - blendshapes.insertMulti("MouthDimple_R", QVariantList() << "Smile_Right" << 0.25); - blendshapes.insertMulti("MouthFrown_L", QVariantList() << "Frown_Left" << 1.0); - blendshapes.insertMulti("MouthFrown_R", QVariantList() << "Frown_Right" << 1.0); - blendshapes.insertMulti("MouthLeft", QVariantList() << "Midmouth_Left" << 1.0); - blendshapes.insertMulti("MouthRight", QVariantList() << "Midmouth_Right" << 1.0); - blendshapes.insertMulti("MouthSmile_L", QVariantList() << "Smile_Left" << 1.0); - blendshapes.insertMulti("MouthSmile_R", QVariantList() << "Smile_Right" << 1.0); - blendshapes.insertMulti("Puff", QVariantList() << "CheekPuff_Left" << 1.0); - blendshapes.insertMulti("Puff", QVariantList() << "CheekPuff_Right" << 1.0); - blendshapes.insertMulti("Sneer", QVariantList() << "NoseScrunch_Left" << 0.75); - blendshapes.insertMulti("Sneer", QVariantList() << "NoseScrunch_Right" << 0.75); - blendshapes.insertMulti("Sneer", QVariantList() << "Squint_Left" << 0.5); - blendshapes.insertMulti("Sneer", QVariantList() << "Squint_Right" << 0.5); - mapping.insert(BLENDSHAPE_FIELD, blendshapes); - } -} - -void ModelUploader::send() { - if (!zip()) { - deleteLater(); - return; - } - - JSONCallbackParameters callbackParams; - callbackParams.jsonCallbackReceiver = this; - callbackParams.jsonCallbackMethod = "checkJSON"; - callbackParams.errorCallbackReceiver = this; - callbackParams.errorCallbackMethod = "uploadFailed"; - - AccountManager::getInstance().authenticatedRequest(MODEL_URL + "/" + QFileInfo(_url).baseName(), - QNetworkAccessManager::GetOperation, - callbackParams); - - qDebug() << "Sending model..."; - _progressDialog = new QDialog(); - _progressBar = new QProgressBar(_progressDialog); - _progressBar->setRange(0, 100); - _progressBar->setValue(0); - - _progressDialog->setWindowTitle("Uploading model..."); - _progressDialog->setLayout(new QGridLayout(_progressDialog)); - _progressDialog->layout()->addWidget(_progressBar); - - _progressDialog->exec(); - - delete _progressDialog; - _progressDialog = NULL; - _progressBar = NULL; -} - -void ModelUploader::checkJSON(QNetworkReply& requestReply) { - QJsonObject jsonResponse = QJsonDocument::fromJson(requestReply.readAll()).object(); - - if (jsonResponse.contains("status") && jsonResponse.value("status").toString() == "success") { - qDebug() << "status : success"; - JSONCallbackParameters callbackParams; - callbackParams.jsonCallbackReceiver = this; - callbackParams.jsonCallbackMethod = "uploadSuccess"; - callbackParams.errorCallbackReceiver = this; - callbackParams.errorCallbackMethod = "uploadFailed"; - callbackParams.updateReciever = this; - callbackParams.updateSlot = SLOT(uploadUpdate(qint64, qint64)); - - if (jsonResponse.contains("exists") && jsonResponse.value("exists").toBool()) { - qDebug() << "exists : true"; - if (jsonResponse.contains("can_update") && jsonResponse.value("can_update").toBool()) { - qDebug() << "can_update : true"; - - AccountManager::getInstance().authenticatedRequest(MODEL_URL + "/" + QFileInfo(_url).baseName(), - QNetworkAccessManager::PutOperation, - callbackParams, - QByteArray(), - _dataMultiPart); - _dataMultiPart = NULL; - } else { - qDebug() << "can_update : false"; - if (_progressDialog) { - _progressDialog->reject(); - } - QMessageBox::warning(NULL, - QString("ModelUploader::checkJSON()"), - QString("This model already exist and is own by someone else."), - QMessageBox::Ok); - deleteLater(); - } - } else { - qDebug() << "exists : false"; - AccountManager::getInstance().authenticatedRequest(MODEL_URL, - QNetworkAccessManager::PostOperation, - callbackParams, - QByteArray(), - _dataMultiPart); - _dataMultiPart = NULL; - } - } else { - qDebug() << "status : failed"; - if (_progressDialog) { - _progressDialog->reject(); - } - QMessageBox::warning(NULL, - QString("ModelUploader::checkJSON()"), - QString("Something went wrong with the data-server."), - QMessageBox::Ok); - deleteLater(); - } -} - -void ModelUploader::uploadUpdate(qint64 bytesSent, qint64 bytesTotal) { - if (_progressDialog) { - _progressBar->setRange(0, bytesTotal); - _progressBar->setValue(bytesSent); - } -} - -void ModelUploader::uploadSuccess(QNetworkReply& requestReply) { - if (_progressDialog) { - _progressDialog->accept(); - } - QMessageBox::information(NULL, - QString("ModelUploader::uploadSuccess()"), - QString("We are reading your model information."), - QMessageBox::Ok); - qDebug() << "Model sent with success"; - checkS3(); -} - -void ModelUploader::uploadFailed(QNetworkReply& errorReply) { - if (_progressDialog) { - _progressDialog->reject(); - } - qDebug() << "Model upload failed (" << errorReply.error() << "): " << errorReply.errorString(); - QMessageBox::warning(NULL, - QString("ModelUploader::uploadFailed()"), - QString("There was a problem with your upload, please try again later."), - QMessageBox::Ok); - deleteLater(); -} - -void ModelUploader::checkS3() { - qDebug() << "Checking S3 for " << _url; - QNetworkRequest request(_url); - request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - QNetworkReply* reply = NetworkAccessManager::getInstance().head(request); - connect(reply, SIGNAL(finished()), SLOT(processCheck())); -} - -void ModelUploader::processCheck() { - QNetworkReply* reply = static_cast(sender()); - _timer.stop(); - - switch (reply->error()) { - case QNetworkReply::NoError: { - QMessageBox::information(NULL, - QString("ModelUploader::processCheck()"), - QString("Your model is now available in the browser."), - QMessageBox::Ok); - DependencyManager::get()->refresh(_url); - auto textureCache = DependencyManager::get(); - foreach (const QByteArray& filename, _textureFilenames) { - textureCache->refresh(_textureBase + filename); - } - deleteLater(); - break; - } - case QNetworkReply::ContentNotFoundError: - if (--_numberOfChecks) { - _timer.start(TIMEOUT); - break; - } - default: - QMessageBox::warning(NULL, - QString("ModelUploader::processCheck()"), - QString("We could not verify that your model was sent sucessfully\n" - "but it may have. If you do not see it in the model browser, try to upload again."), - QMessageBox::Ok); - deleteLater(); - break; - } - - delete reply; -} - -bool ModelUploader::addTextures(const QString& texdir, const FBXGeometry& geometry) { - foreach (FBXMesh mesh, geometry.meshes) { - foreach (FBXMeshPart part, mesh.parts) { - if (!part.diffuseTexture.filename.isEmpty() && part.diffuseTexture.content.isEmpty() && - !_textureFilenames.contains(part.diffuseTexture.filename)) { - if (!addPart(texdir + "/" + part.diffuseTexture.filename, - QString("texture%1").arg(++_texturesCount), true)) { - return false; - } - _textureFilenames.insert(part.diffuseTexture.filename); - } - if (!part.normalTexture.filename.isEmpty() && part.normalTexture.content.isEmpty() && - !_textureFilenames.contains(part.normalTexture.filename)) { - if (!addPart(texdir + "/" + part.normalTexture.filename, - QString("texture%1").arg(++_texturesCount), true)) { - return false; - } - _textureFilenames.insert(part.normalTexture.filename); - } - if (!part.specularTexture.filename.isEmpty() && part.specularTexture.content.isEmpty() && - !_textureFilenames.contains(part.specularTexture.filename)) { - if (!addPart(texdir + "/" + part.specularTexture.filename, - QString("texture%1").arg(++_texturesCount), true)) { - return false; - } - _textureFilenames.insert(part.specularTexture.filename); - } - if (!part.emissiveTexture.filename.isEmpty() && part.emissiveTexture.content.isEmpty() && - !_textureFilenames.contains(part.emissiveTexture.filename)) { - if (!addPart(texdir + "/" + part.emissiveTexture.filename, - QString("texture%1").arg(++_texturesCount), true)) { - return false; - } - _textureFilenames.insert(part.emissiveTexture.filename); - } - } - } - - return true; -} - -bool ModelUploader::addPart(const QString &path, const QString& name, bool isTexture) { - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - QMessageBox::warning(NULL, - QString("ModelUploader::addPart()"), - QString("Could not open %1").arg(path), - QMessageBox::Ok); - qDebug() << "[Warning] " << QString("Could not open %1").arg(path); - return false; - } - return addPart(file, file.readAll(), name, isTexture); -} - -bool ModelUploader::addPart(const QFile& file, const QByteArray& contents, const QString& name, bool isTexture) { - QFileInfo fileInfo(file); - QByteArray recodedContents = contents; - if (isTexture) { - QString extension = fileInfo.suffix().toLower(); - bool isJpeg = (extension == "jpg"); - bool mustRecode = !(isJpeg || extension == "png"); - QImage image = QImage::fromData(contents); - if (image.width() > MAX_TEXTURE_SIZE || image.height() > MAX_TEXTURE_SIZE) { - image = image.scaled(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, Qt::KeepAspectRatio); - mustRecode = true; - } - if (mustRecode) { - QBuffer buffer; - buffer.open(QIODevice::WriteOnly); - image.save(&buffer, isJpeg ? "JPG" : "PNG"); - recodedContents = buffer.data(); - } - } - QByteArray buffer = qCompress(recodedContents); - - // Qt's qCompress() default compression level (-1) is the standard zLib compression. - // Here remove Qt's custom header that prevent the data server from uncompressing the files with zLib. - buffer.remove(QCOMPRESS_HEADER_POSITION, QCOMPRESS_HEADER_SIZE); - - QHttpPart part; - part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data;" - " name=\"" + name.toUtf8() + "\";" - " filename=\"" + QFileInfo(file).fileName().toUtf8() + "\"")); - part.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); - part.setBody(buffer); - _dataMultiPart->append(part); - - - qDebug() << "File " << QFileInfo(file).fileName() << " added to model."; - _totalSize += recodedContents.size(); - if (_totalSize > MAX_SIZE) { - QMessageBox::warning(NULL, - QString("ModelUploader::zip()"), - QString("Model too big, over %1 MB.").arg(MAX_SIZE / BYTES_PER_MEGABYTES), - QMessageBox::Ok); - qDebug() << "[Warning] " << QString("Model too big, over %1 MB.").arg(MAX_SIZE / BYTES_PER_MEGABYTES); - return false; - } - qDebug() << "Current model size: " << _totalSize; - - return true; -} - -static QDoubleSpinBox* createTranslationBox() { - QDoubleSpinBox* box = new QDoubleSpinBox(); - const double MAX_TRANSLATION = 1000000.0; - box->setMinimum(-MAX_TRANSLATION); - box->setMaximum(MAX_TRANSLATION); - return box; -} - -ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const FBXGeometry& geometry) : - _modelType(modelType), - _originalMapping(originalMapping), - _basePath(basePath), - _geometry(geometry) -{ - setWindowTitle("Set Model Properties"); - - QFormLayout* form = new QFormLayout(); - setLayout(form); - - form->addRow("Name:", _name = new QLineEdit()); - - form->addRow("Texture Directory:", _textureDirectory = new QPushButton()); - connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory())); - - form->addRow("Scale:", _scale = new QDoubleSpinBox()); - _scale->setMaximum(FLT_MAX); - _scale->setSingleStep(0.01); - - if (_modelType != ENTITY_MODEL) { - if (_modelType == ATTACHMENT_MODEL) { - QHBoxLayout* translation = new QHBoxLayout(); - form->addRow("Translation:", translation); - translation->addWidget(_translationX = createTranslationBox()); - translation->addWidget(_translationY = createTranslationBox()); - translation->addWidget(_translationZ = createTranslationBox()); - form->addRow("Pivot About Center:", _pivotAboutCenter = new QCheckBox()); - form->addRow("Pivot Joint:", _pivotJoint = createJointBox()); - connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint())); - _pivotAboutCenter->setChecked(true); - - } else { - form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox()); - form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox()); - form->addRow("Neck Joint:", _neckJoint = createJointBox()); - } - if (_modelType == SKELETON_MODEL) { - form->addRow("Root Joint:", _rootJoint = createJointBox()); - form->addRow("Lean Joint:", _leanJoint = createJointBox()); - form->addRow("Head Joint:", _headJoint = createJointBox()); - form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox()); - form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox()); - - form->addRow("Free Joints:", _freeJoints = new QVBoxLayout()); - QPushButton* newFreeJoint = new QPushButton("New Free Joint"); - _freeJoints->addWidget(newFreeJoint); - connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint())); - } - } - - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | - QDialogButtonBox::Cancel | QDialogButtonBox::Reset); - connect(buttons, SIGNAL(accepted()), SLOT(accept())); - connect(buttons, SIGNAL(rejected()), SLOT(reject())); - connect(buttons->button(QDialogButtonBox::Reset), SIGNAL(clicked(bool)), SLOT(reset())); - - form->addRow(buttons); - - // reset to initialize the fields - reset(); -} - -QVariantHash ModelPropertiesDialog::getMapping() const { - QVariantHash mapping = _originalMapping; - mapping.insert(NAME_FIELD, _name->text()); - mapping.insert(TEXDIR_FIELD, _textureDirectory->text()); - mapping.insert(SCALE_FIELD, QString::number(_scale->value())); - - // update the joint indices - QVariantHash jointIndices; - for (int i = 0; i < _geometry.joints.size(); i++) { - jointIndices.insert(_geometry.joints.at(i).name, QString::number(i)); - } - mapping.insert(JOINT_INDEX_FIELD, jointIndices); - - if (_modelType != ENTITY_MODEL) { - QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); - if (_modelType == ATTACHMENT_MODEL) { - glm::vec3 pivot; - if (_pivotAboutCenter->isChecked()) { - pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f; - - } else if (_pivotJoint->currentIndex() != 0) { - pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform); - } - mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value()); - mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value()); - mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value()); - - } else { - insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText()); - insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText()); - insertJointMapping(joints, "jointNeck", _neckJoint->currentText()); - } - if (_modelType == SKELETON_MODEL) { - insertJointMapping(joints, "jointRoot", _rootJoint->currentText()); - insertJointMapping(joints, "jointLean", _leanJoint->currentText()); - insertJointMapping(joints, "jointHead", _headJoint->currentText()); - insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText()); - insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText()); - - mapping.remove(FREE_JOINT_FIELD); - for (int i = 0; i < _freeJoints->count() - 1; i++) { - QComboBox* box = static_cast(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget()); - mapping.insertMulti(FREE_JOINT_FIELD, box->currentText()); - } - } - mapping.insert(JOINT_FIELD, joints); - } - - return mapping; -} - -static void setJointText(QComboBox* box, const QString& text) { - box->setCurrentIndex(qMax(box->findText(text), 0)); -} - -void ModelPropertiesDialog::reset() { - _name->setText(_originalMapping.value(NAME_FIELD).toString()); - _textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString()); - _scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble()); - - QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash(); - - if (_modelType != ENTITY_MODEL) { - if (_modelType == ATTACHMENT_MODEL) { - _translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble()); - _translationY->setValue(_originalMapping.value(TRANSLATION_Y_FIELD).toDouble()); - _translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble()); - _pivotAboutCenter->setChecked(true); - _pivotJoint->setCurrentIndex(0); - - } else { - setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString()); - setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString()); - setJointText(_neckJoint, jointHash.value("jointNeck").toString()); - } - if (_modelType == SKELETON_MODEL) { - setJointText(_rootJoint, jointHash.value("jointRoot").toString()); - setJointText(_leanJoint, jointHash.value("jointLean").toString()); - setJointText(_headJoint, jointHash.value("jointHead").toString()); - setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString()); - setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString()); - - while (_freeJoints->count() > 1) { - delete _freeJoints->itemAt(0)->widget(); - } - foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) { - QString jointName = joint.toString(); - if (_geometry.jointIndices.contains(jointName)) { - createNewFreeJoint(jointName); - } - } - } - } -} - -void ModelPropertiesDialog::chooseTextureDirectory() { - QString directory = QFileDialog::getExistingDirectory(this, "Choose Texture Directory", - _basePath + "/" + _textureDirectory->text()); - if (directory.isEmpty()) { - return; - } - if (!directory.startsWith(_basePath)) { - QMessageBox::warning(NULL, "Invalid texture directory", "Texture directory must be child of base path."); - return; - } - _textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1)); -} - -void ModelPropertiesDialog::updatePivotJoint() { - _pivotJoint->setEnabled(!_pivotAboutCenter->isChecked()); -} - -void ModelPropertiesDialog::createNewFreeJoint(const QString& joint) { - QWidget* freeJoint = new QWidget(); - QHBoxLayout* freeJointLayout = new QHBoxLayout(); - freeJointLayout->setContentsMargins(QMargins()); - freeJoint->setLayout(freeJointLayout); - QComboBox* jointBox = createJointBox(false); - jointBox->setCurrentText(joint); - freeJointLayout->addWidget(jointBox, 1); - QPushButton* deleteJoint = new QPushButton("Delete"); - freeJointLayout->addWidget(deleteJoint); - freeJoint->connect(deleteJoint, SIGNAL(clicked(bool)), SLOT(deleteLater())); - _freeJoints->insertWidget(_freeJoints->count() - 1, freeJoint); -} - -QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const { - QComboBox* box = new QComboBox(); - if (withNone) { - box->addItem("(none)"); - } - foreach (const FBXJoint& joint, _geometry.joints) { - if (joint.isSkeletonJoint || !_geometry.hasSkeletonJoints) { - box->addItem(joint.name); - } - } - return box; -} - -void ModelPropertiesDialog::insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const { - if (_geometry.jointIndices.contains(name)) { - joints.insert(joint, name); - } else { - joints.remove(joint); - } -} - diff --git a/interface/src/ModelUploader.h b/interface/src/ModelUploader.h deleted file mode 100644 index 59a424b346..0000000000 --- a/interface/src/ModelUploader.h +++ /dev/null @@ -1,126 +0,0 @@ -// -// ModelUploader.h -// interface/src -// -// Created by Clément Brisset on 3/4/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ModelUploader_h -#define hifi_ModelUploader_h - -#include -#include - -#include -#include - -#include "ui/ModelsBrowser.h" - -class QCheckBox; -class QComboBox; -class QDoubleSpinBox; -class QFileInfo; -class QHttpMultiPart; -class QLineEdit; -class QProgressBar; -class QPushButton; -class QVBoxLayout; - -class ModelUploader : public QObject { - Q_OBJECT - -public: - static void uploadModel(ModelType modelType); - - static void uploadHead(); - static void uploadSkeleton(); - static void uploadAttachment(); - static void uploadEntity(); - -private slots: - void send(); - void checkJSON(QNetworkReply& requestReply); - void uploadUpdate(qint64 bytesSent, qint64 bytesTotal); - void uploadSuccess(QNetworkReply& requestReply); - void uploadFailed(QNetworkReply& errorReply); - void checkS3(); - void processCheck(); - -private: - ModelUploader(ModelType type); - ~ModelUploader(); - - void populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry); - bool zip(); - bool addTextures(const QString& texdir, const FBXGeometry& geometry); - bool addPart(const QString& path, const QString& name, bool isTexture = false); - bool addPart(const QFile& file, const QByteArray& contents, const QString& name, bool isTexture = false); - - QString _url; - QString _textureBase; - QSet _textureFilenames; - int _lodCount; - int _texturesCount; - unsigned long _totalSize; - ModelType _modelType; - bool _readyToSend; - - QHttpMultiPart* _dataMultiPart = nullptr; - - int _numberOfChecks; - QTimer _timer; - - QDialog* _progressDialog = nullptr; - QProgressBar* _progressBar = nullptr; - - static Setting::Handle _lastModelUploadLocation; -}; - -/// A dialog that allows customization of various model properties. -class ModelPropertiesDialog : public QDialog { - Q_OBJECT - -public: - ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const FBXGeometry& geometry); - - QVariantHash getMapping() const; - -private slots: - void reset(); - void chooseTextureDirectory(); - void updatePivotJoint(); - void createNewFreeJoint(const QString& joint = QString()); - -private: - QComboBox* createJointBox(bool withNone = true) const; - void insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const; - - ModelType _modelType; - QVariantHash _originalMapping; - QString _basePath; - FBXGeometry _geometry; - QLineEdit* _name = nullptr; - QPushButton* _textureDirectory = nullptr; - QDoubleSpinBox* _scale = nullptr; - QDoubleSpinBox* _translationX = nullptr; - QDoubleSpinBox* _translationY = nullptr; - QDoubleSpinBox* _translationZ = nullptr; - QCheckBox* _pivotAboutCenter = nullptr; - QComboBox* _pivotJoint = nullptr; - QComboBox* _leftEyeJoint = nullptr; - QComboBox* _rightEyeJoint = nullptr; - QComboBox* _neckJoint = nullptr; - QComboBox* _rootJoint = nullptr; - QComboBox* _leanJoint = nullptr; - QComboBox* _headJoint = nullptr; - QComboBox* _leftHandJoint = nullptr; - QComboBox* _rightHandJoint = nullptr; - QVBoxLayout* _freeJoints = nullptr; -}; - -#endif // hifi_ModelUploader_h From 9b80c013572c0283a658decec11d1ecdd66a2fc4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 11 Mar 2015 16:21:36 +0100 Subject: [PATCH 10/34] Remove ModelUploader from the menu --- interface/src/Application.cpp | 18 +++--------------- interface/src/Application.h | 7 ++----- interface/src/Menu.cpp | 11 ++--------- interface/src/Menu.h | 5 +---- 4 files changed, 8 insertions(+), 33 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a7a6c8c094..144da3aed6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -91,7 +91,7 @@ #include "InterfaceVersion.h" #include "LODManager.h" #include "Menu.h" -#include "ModelUploader.h" +#include "ModelPackager.h" #include "Util.h" #include "avatar/AvatarManager.h" @@ -3715,20 +3715,8 @@ void Application::toggleRunningScriptsWidget() { } } -void Application::uploadHead() { - ModelUploader::uploadHead(); -} - -void Application::uploadSkeleton() { - ModelUploader::uploadSkeleton(); -} - -void Application::uploadAttachment() { - ModelUploader::uploadAttachment(); -} - -void Application::uploadEntity() { - ModelUploader::uploadEntity(); +void Application::packageModel() { + ModelPackager::package(); } void Application::openUrl(const QUrl& url) { diff --git a/interface/src/Application.h b/interface/src/Application.h index d8d9132de9..99799aff95 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -340,11 +340,8 @@ public slots: void loadDefaultScripts(); void toggleRunningScriptsWidget(); void saveScripts(); - - void uploadHead(); - void uploadSkeleton(); - void uploadAttachment(); - void uploadEntity(); + + void packageModel(); void openUrl(const QUrl& url); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ea63b9879f..abbe96826d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -105,15 +105,8 @@ Menu::Menu() { addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0, addressManager.data(), SLOT(copyPath())); - addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model"); - addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, - qApp, SLOT(uploadHead())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0, - qApp, SLOT(uploadSkeleton())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadAttachment, 0, - qApp, SLOT(uploadAttachment())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadEntity, 0, - qApp, SLOT(uploadEntity())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::PackageModel, 0, + qApp, SLOT(packageModel())); addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 3166e1fa37..7fbecc319c 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -255,10 +255,7 @@ namespace MenuOption { const QString ToolWindow = "Tool Window"; const QString TransmitterDrive = "Transmitter Drive"; const QString TurnWithHead = "Turn using Head"; - const QString UploadAttachment = "Upload Attachment Model"; - const QString UploadEntity = "Upload Entity Model"; - const QString UploadHead = "Upload Head Model"; - const QString UploadSkeleton = "Upload Skeleton Model"; + const QString PackageModel = "Package Model"; const QString UserInterface = "User Interface"; const QString Visage = "Visage"; const QString Wireframe = "Wireframe"; From a727669a15af6dc1a504613744348e19b486b0db Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 11 Mar 2015 16:29:24 +0100 Subject: [PATCH 11/34] Move menu option to tools --- interface/src/Menu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index abbe96826d..7b4a1105c8 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -105,9 +105,6 @@ Menu::Menu() { addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0, addressManager.data(), SLOT(copyPath())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::PackageModel, 0, - qApp, SLOT(packageModel())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, @@ -173,6 +170,9 @@ Menu::Menu() { Qt::Key_Apostrophe, qApp, SLOT(resetSensors())); + + addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0, + qApp, SLOT(packageModel())); QMenu* avatarMenu = addMenu("Avatar"); QObject* avatar = DependencyManager::get()->getMyAvatar(); From 1609823e2b2c7900749edf87059d7e12ef8b0eda Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 11 Mar 2015 16:32:45 +0100 Subject: [PATCH 12/34] Move helper function to top --- interface/src/ModelPackager.cpp | 45 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index bbd78e274c..4f44502737 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include "ModelSelector.h" #include "ModelPropertiesDialog.h" @@ -22,6 +21,28 @@ static const int MAX_TEXTURE_SIZE = 1024; +void copyDirectoryContent(QDir& from, QDir& to) { + for (auto entry : from.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | + QDir::NoSymLinks | QDir::Readable)) { + if (entry.isDir()) { + to.mkdir(entry.fileName()); + from.cd(entry.fileName()); + to.cd(entry.fileName()); + copyDirectoryContent(from, to); + from.cdUp(); + to.cdUp(); + } else { // Files + QFile file(entry.absoluteFilePath()); + QString newPath = to.absolutePath() + "/" + entry.fileName(); + if (to.exists(entry.fileName())) { + QFile overridenFile(newPath); + overridenFile.remove(); + } + file.copy(newPath); + } + } +} + bool ModelPackager::package() { ModelPackager packager; if (!packager.selectModel()) { @@ -117,28 +138,6 @@ bool ModelPackager::editProperties() { return true; } -void copyDirectoryContent(QDir& from, QDir& to) { - for (auto entry : from.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | - QDir::NoSymLinks | QDir::Readable)) { - if (entry.isDir()) { - to.mkdir(entry.fileName()); - from.cd(entry.fileName()); - to.cd(entry.fileName()); - copyDirectoryContent(from, to); - from.cdUp(); - to.cdUp(); - } else { // Files - QFile file(entry.absoluteFilePath()); - QString newPath = to.absolutePath() + "/" + entry.fileName(); - if (to.exists(entry.fileName())) { - QFile overridenFile(newPath); - overridenFile.remove(); - } - file.copy(newPath); - } - } -} - bool ModelPackager::zipModel() { QTemporaryDir dir; dir.setAutoRemove(true); From bfbc8ffb4707240d8ee6bab7bb384d692a4e8eb2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 11 Mar 2015 18:35:15 +0100 Subject: [PATCH 13/34] File extension is case insensitive --- interface/src/ModelSelector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp index 97c7f780e2..c55d77dc00 100644 --- a/interface/src/ModelSelector.cpp +++ b/interface/src/ModelSelector.cpp @@ -81,7 +81,7 @@ void ModelSelector::browse() { "Model files (*.fst *.fbx)"); QFileInfo fileInfo(filename); - if (fileInfo.isFile() && fileInfo.completeSuffix().contains(QRegExp("fst|fbx"))) { + if (fileInfo.isFile() && fileInfo.completeSuffix().contains(QRegExp("fst|fbx|FST|FBX"))) { _modelFile = fileInfo; _browseButton->setText(fileInfo.fileName()); lastModelBrowseLocation.set(fileInfo.path()); From 2f885bb7f21231c9d351e9a919102e061f866dda Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 11 Mar 2015 15:34:29 -0700 Subject: [PATCH 14/34] Move WindowScriptingInterface to DependencyManager --- interface/src/Application.cpp | 3 ++- interface/src/scripting/WebWindowClass.cpp | 2 +- interface/src/scripting/WindowScriptingInterface.cpp | 5 ----- interface/src/scripting/WindowScriptingInterface.h | 5 ++--- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a7a6c8c094..1dde90aeaa 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -244,6 +244,7 @@ bool setupEssentials(int& argc, char** argv) { auto bandwidthRecorder = DependencyManager::set(); auto resouceCacheSharedItems = DependencyManager::set(); auto entityScriptingInterface = DependencyManager::set(); + auto windowScriptingInterface = DependencyManager::set(); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) auto speechRecognizer = DependencyManager::set(); #endif @@ -3527,7 +3528,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue, RayToOverlayIntersectionResultFromScriptValue); - QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", WindowScriptingInterface::getInstance()); + QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", DependencyManager::get().data()); scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, LocationScriptingInterface::locationSetter, windowValue); // register `location` on the global object. diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index be87870f26..2e0f88c776 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -73,7 +73,7 @@ void WebWindowClass::setVisible(bool visible) { QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { WebWindowClass* retVal; QString file = context->argument(0).toString(); - QMetaObject::invokeMethod(WindowScriptingInterface::getInstance(), "doCreateWebWindow", Qt::BlockingQueuedConnection, + QMetaObject::invokeMethod(DependencyManager::get().data(), "doCreateWebWindow", Qt::BlockingQueuedConnection, Q_RETURN_ARG(WebWindowClass*, retVal), Q_ARG(const QString&, file), Q_ARG(QString, context->argument(1).toString()), diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 8ec9fbbb82..d64c75c345 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -25,11 +25,6 @@ #include "WindowScriptingInterface.h" -WindowScriptingInterface* WindowScriptingInterface::getInstance() { - static WindowScriptingInterface sharedInstance; - return &sharedInstance; -} - WindowScriptingInterface::WindowScriptingInterface() : _editDialog(NULL), _nonBlockingFormActive(false), diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 5c0aa4f0a8..3bf3c5fb0c 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -21,7 +21,7 @@ #include "WebWindowClass.h" -class WindowScriptingInterface : public QObject { +class WindowScriptingInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(int innerWidth READ getInnerWidth) Q_PROPERTY(int innerHeight READ getInnerHeight) @@ -29,7 +29,7 @@ class WindowScriptingInterface : public QObject { Q_PROPERTY(int y READ getY) Q_PROPERTY(bool cursorVisible READ isCursorVisible WRITE setCursorVisible) public: - static WindowScriptingInterface* getInstance(); + WindowScriptingInterface(); int getInnerWidth(); int getInnerHeight(); int getX(); @@ -85,7 +85,6 @@ private slots: WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height); private: - WindowScriptingInterface(); QString jsRegExp2QtRegExp(QString string); QDialog* createForm(const QString& title, QScriptValue form); From c3c2a75f48184ee3011940fda6b8d86c19f95086 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 11 Mar 2015 15:38:15 -0700 Subject: [PATCH 15/34] Handle .svo drop events and emit svoImportRequested --- interface/src/Application.cpp | 10 ++++++++-- interface/src/Application.h | 3 +++ interface/src/GLCanvas.cpp | 3 ++- interface/src/scripting/WindowScriptingInterface.cpp | 1 + interface/src/scripting/WindowScriptingInterface.h | 1 + 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1dde90aeaa..91b4cc5923 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -882,9 +882,15 @@ bool Application::event(QEvent* event) { if (event->type() == QEvent::FileOpen) { QFileOpenEvent* fileEvent = static_cast(event); + + QUrl url = fileEvent->url(); - if (!fileEvent->url().isEmpty()) { - DependencyManager::get()->handleLookupString(fileEvent->url().toString()); + if (!url.isEmpty()) { + if (url.scheme() == HIFI_URL_SCHEME) { + DependencyManager::get()->handleLookupString(fileEvent->url().toString()); + } else if (url.url().toLower().endsWith(SVO_EXTENSION)) { + emit svoImportRequested(url.url()); + } } return false; diff --git a/interface/src/Application.h b/interface/src/Application.h index d8d9132de9..262afac39a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -94,6 +94,7 @@ static const float NODE_KILLED_GREEN = 0.0f; static const float NODE_KILLED_BLUE = 0.0f; static const QString SNAPSHOT_EXTENSION = ".jpg"; +static const QString SVO_EXTENSION = ".svo"; static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees static const float BILLBOARD_DISTANCE = 5.56f; // meters @@ -315,6 +316,8 @@ signals: void scriptLocationChanged(const QString& newPath); + void svoImportRequested(const QString& url); + public slots: void domainChanged(const QString& domainHostname); void updateWindowTitle(); diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp index b72c00c779..4ece8f0857 100644 --- a/interface/src/GLCanvas.cpp +++ b/interface/src/GLCanvas.cpp @@ -170,7 +170,8 @@ void GLCanvas::wheelEvent(QWheelEvent* event) { void GLCanvas::dragEnterEvent(QDragEnterEvent* event) { const QMimeData *mimeData = event->mimeData(); foreach (QUrl url, mimeData->urls()) { - if (url.url().toLower().endsWith(SNAPSHOT_EXTENSION)) { + auto lower = url.url().toLower(); + if (lower.endsWith(SNAPSHOT_EXTENSION) || lower.endsWith(SVO_EXTENSION)) { event->acceptProposedAction(); break; } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index d64c75c345..52de31df3c 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -32,6 +32,7 @@ WindowScriptingInterface::WindowScriptingInterface() : { const DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); connect(&domainHandler, &DomainHandler::hostnameChanged, this, &WindowScriptingInterface::domainChanged); + connect(Application::getInstance(), &Application::svoImportRequested, this, &WindowScriptingInterface::svoImportRequested); } WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height) { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 3bf3c5fb0c..34942366eb 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -60,6 +60,7 @@ signals: void domainChanged(const QString& domainHostname); void inlineButtonClicked(const QString& name); void nonBlockingFormClosed(); + void svoImportRequested(const QString& url); private slots: QScriptValue showAlert(const QString& message); From 0048b913113159f366dfcadade5ee6160b75a42c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 11 Mar 2015 15:42:16 -0700 Subject: [PATCH 16/34] Add handling for opening an .svo using interface --- interface/src/Application.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 91b4cc5923..48e1c4b266 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1457,6 +1457,10 @@ void Application::dropEvent(QDropEvent *event) { if (url.url().toLower().endsWith(SNAPSHOT_EXTENSION)) { snapshotPath = url.toLocalFile(); break; + } else if (url.url().toLower().endsWith(SVO_EXTENSION)) { + emit svoImportRequested(url.url()); + event->acceptProposedAction(); + return; } } From 76ebac118579d8a4aea348112d9dcef02bf2dbb8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 11 Mar 2015 15:44:26 -0700 Subject: [PATCH 17/34] Add import svo requests in editEntities --- examples/editEntities.js | 64 +++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/examples/editEntities.js b/examples/editEntities.js index faef875d9b..c236336266 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -95,6 +95,21 @@ var isActive = false; var placingEntityID = null; +IMPORTING_SVO_OVERLAY_WIDTH = 130; +IMPORTING_SVO_OVERLAY_HEIGHT = 30; +IMPORTING_SVO_OVERLAY_MARGIN = 6; +var importingSVOOverlay = Overlays.addOverlay("text", { + font: { size: 14 }, + text: "Importing SVO...", + x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH - IMPORTING_SVO_OVERLAY_MARGIN, + y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT - IMPORTING_SVO_OVERLAY_MARGIN, + width: IMPORTING_SVO_OVERLAY_WIDTH, + height: IMPORTING_SVO_OVERLAY_HEIGHT, + backgroundColor: { red: 80, green: 80, blue: 80 }, + backgroundAlpha: 0.7, + visible: false, +}); + var toolBar = (function () { var that = {}, toolBar, @@ -753,6 +768,8 @@ Script.scriptEnding.connect(function() { tooltip.cleanup(); selectionDisplay.cleanup(); Entities.setLightsArePickable(originalLightsArePickable); + + Overlays.deleteOverlay(importingSVOOverlay); }); // Do some stuff regularly, like check for placement of various overlays @@ -816,24 +833,7 @@ function handeMenuEvent(menuItem) { } if (importURL) { - var success = Clipboard.importEntities(importURL); - - if (success) { - var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE; - var direction = Quat.getFront(Camera.orientation); - var offset = Vec3.multiply(distance, direction); - var position = Vec3.sum(Camera.position, offset); - - position.x = Math.max(0, position.x); - position.y = Math.max(0, position.y); - position.z = Math.max(0, position.z); - - var pastedEntityIDs = Clipboard.pasteEntities(position); - - selectionManager.setSelections(pastedEntityIDs); - } else { - Window.alert("There was an error importing the entity file."); - } + importSVO(importURL); } } else if (menuItem == "Entity List...") { entityListTool.toggleVisible(); @@ -841,6 +841,34 @@ function handeMenuEvent(menuItem) { tooltip.show(false); } +function importSVO(importURL) { + Overlays.editOverlay(importingSVOOverlay, { visible: true }); + + var success = Clipboard.importEntities(importURL); + + if (success) { + var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE; + var direction = Quat.getFront(Camera.orientation); + var offset = Vec3.multiply(distance, direction); + var position = Vec3.sum(Camera.position, offset); + + position.x = Math.max(0, position.x); + position.y = Math.max(0, position.y); + position.z = Math.max(0, position.z); + + var pastedEntityIDs = Clipboard.pasteEntities(position); + + if (isActive) { + selectionManager.setSelections(pastedEntityIDs); + } + } else { + Window.alert("There was an error importing the entity file."); + } + + Overlays.editOverlay(importingSVOOverlay, { visible: false }); +} +Window.svoImportRequested.connect(importSVO); + Menu.menuItemEvent.connect(handeMenuEvent); Controller.keyPressEvent.connect(function(event) { From 1904e5e209346aa1d4918f3a8d73d63f8756149d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 11 Mar 2015 16:17:44 -0700 Subject: [PATCH 18/34] Make look.js the default look script and remove the old scripts --- examples/defaultScripts.js | 3 +- examples/lookWithMouse.js | 88 ---------------------------------- examples/lookWithTouch.js | 96 -------------------------------------- 3 files changed, 1 insertion(+), 186 deletions(-) delete mode 100644 examples/lookWithMouse.js delete mode 100644 examples/lookWithTouch.js diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index d4efad9ee5..6b57bf18dd 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -9,7 +9,6 @@ // Script.load("progress.js"); -Script.load("lookWithTouch.js"); Script.load("editEntities.js"); Script.load("selectAudioDevice.js"); Script.load("controllers/hydra/hydraMove.js"); @@ -17,5 +16,5 @@ Script.load("headMove.js"); Script.load("inspect.js"); Script.load("lobby.js"); Script.load("notifications.js"); -Script.load("lookWithMouse.js"); +Script.load("look.js"); Script.load("users.js"); diff --git a/examples/lookWithMouse.js b/examples/lookWithMouse.js deleted file mode 100644 index 470a56bd63..0000000000 --- a/examples/lookWithMouse.js +++ /dev/null @@ -1,88 +0,0 @@ -// -// lookWithMouse.js -// examples -// -// Created by Brad Hefta-Gaub on 1/28/14. -// Copyright 2014 High Fidelity, Inc. -// -// This is an example script that demonstrates use of the Controller class -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var alwaysLook = false; // if you want the mouse look to happen only when you click, change this to false -var isMouseDown = false; -var lastX = 0; -var lastY = 0; -var yawFromMouse = 0; -var pitchFromMouse = 0; -var wantDebugging = false; - -function mousePressEvent(event) { - if (wantDebugging) { - print("mousePressEvent event.x,y=" + event.x + ", " + event.y); - } - isMouseDown = true; - lastX = event.x; - lastY = event.y; -} - -function mouseReleaseEvent(event) { - if (wantDebugging) { - print("mouseReleaseEvent event.x,y=" + event.x + ", " + event.y); - } - isMouseDown = false; -} - -function mouseMoveEvent(event) { - if (wantDebugging) { - print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y); - } - - if (alwaysLook || isMouseDown) { - var MOUSE_YAW_SCALE = -0.25; - var MOUSE_PITCH_SCALE = -12.5; - var FIXED_MOUSE_TIMESTEP = 0.016; - yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP); - pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP); - lastX = event.x; - lastY = event.y; - } -} - -function update(deltaTime) { - if (wantDebugging) { - print("update()..."); - } - // rotate body yaw for yaw received from mouse - var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Radians( { x: 0, y: yawFromMouse, z: 0 } )); - if (wantDebugging) { - print("changing orientation" - + " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + "," - + MyAvatar.orientation.z + "," + MyAvatar.orientation.w - + " newOrientation="+newOrientation.x + "," + newOrientation.y + "," + newOrientation.z + "," + newOrientation.w); - } - MyAvatar.orientation = newOrientation; - yawFromMouse = 0; - - // apply pitch from mouse - var newPitch = MyAvatar.headPitch + pitchFromMouse; - if (wantDebugging) { - print("changing pitch [old]MyAvatar.headPitch="+MyAvatar.headPitch+ " newPitch="+newPitch); - } - MyAvatar.headPitch = newPitch; - pitchFromMouse = 0; -} - -// Map the mouse events to our functions -Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseMoveEvent.connect(mouseMoveEvent); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); - -MyAvatar.bodyYaw = 0; -MyAvatar.bodyPitch = 0; -MyAvatar.bodyRoll = 0; - -// would be nice to change to update -Script.update.connect(update); diff --git a/examples/lookWithTouch.js b/examples/lookWithTouch.js deleted file mode 100644 index e9e7b0735a..0000000000 --- a/examples/lookWithTouch.js +++ /dev/null @@ -1,96 +0,0 @@ -// -// lookWithTouch.js -// examples -// -// Created by Brad Hefta-Gaub on 1/28/14. -// Copyright 2014 High Fidelity, Inc. -// -// This is an example script that demonstrates use of the Controller class -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var startedTouching = false; -var lastX = 0; -var lastY = 0; -var yawFromMouse = 0; -var pitchFromMouse = 0; -var wantDebugging = false; - -function touchBeginEvent(event) { - if (wantDebugging) { - print("touchBeginEvent event.x,y=" + event.x + ", " + event.y); - } - lastX = event.x; - lastY = event.y; - startedTouching = true; -} - -function touchEndEvent(event) { - if (wantDebugging) { - print("touchEndEvent event.x,y=" + event.x + ", " + event.y); - } - startedTouching = false; -} - -function touchUpdateEvent(event) { - if (wantDebugging) { - print("touchUpdateEvent event.x,y=" + event.x + ", " + event.y); - } - - if (!startedTouching) { - // handle Qt 5.4.x bug where we get touch update without a touch begin event - startedTouching = true; - lastX = event.x; - lastY = event.y; - } - - var MOUSE_YAW_SCALE = -0.25; - var MOUSE_PITCH_SCALE = -12.5; - var FIXED_MOUSE_TIMESTEP = 0.016; - yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP); - pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP); - lastX = event.x; - lastY = event.y; -} - -function update(deltaTime) { - if (startedTouching) { - // rotate body yaw for yaw received from mouse - var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollRadians(0, yawFromMouse, 0)); - if (wantDebugging) { - print("changing orientation" - + " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + "," - + MyAvatar.orientation.z + "," + MyAvatar.orientation.w - + " newOrientation="+newOrientation.x + "," + newOrientation.y + "," + newOrientation.z + "," + newOrientation.w); - } - MyAvatar.orientation = newOrientation; - yawFromMouse = 0; - - // apply pitch from mouse - var newPitch = MyAvatar.headPitch + pitchFromMouse; - if (wantDebugging) { - print("changing pitch [old]MyAvatar.headPitch="+MyAvatar.headPitch+ " newPitch="+newPitch); - } - MyAvatar.headPitch = newPitch; - pitchFromMouse = 0; - } -} - -// Map the mouse events to our functions -Controller.touchBeginEvent.connect(touchBeginEvent); -Controller.touchUpdateEvent.connect(touchUpdateEvent); -Controller.touchEndEvent.connect(touchEndEvent); - -// disable the standard application for mouse events -Controller.captureTouchEvents(); - -function scriptEnding() { - // re-enabled the standard application for mouse events - Controller.releaseTouchEvents(); -} - -// would be nice to change to update -Script.update.connect(update); -Script.scriptEnding.connect(scriptEnding); From e87e0a1e0b35fefe09807acc525f5e1097333b6f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 11 Mar 2015 12:10:37 -0700 Subject: [PATCH 19/34] some cleanup of dead code related to timers --- assignment-client/src/Agent.cpp | 1 - assignment-client/src/audio/AudioMixer.cpp | 1 - interface/src/ui/BandwidthDialog.h | 1 + libraries/networking/src/BandwidthRecorder.h | 1 - libraries/networking/src/DomainHandler.cpp | 7 ------- libraries/networking/src/DomainHandler.h | 1 - 6 files changed, 1 insertion(+), 11 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index d56db7aace..ed1f293c06 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index e865ab0035..dd566bc40b 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include #include diff --git a/interface/src/ui/BandwidthDialog.h b/interface/src/ui/BandwidthDialog.h index a504a5964f..1fc8627191 100644 --- a/interface/src/ui/BandwidthDialog.h +++ b/interface/src/ui/BandwidthDialog.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "Node.h" #include "BandwidthRecorder.h" diff --git a/libraries/networking/src/BandwidthRecorder.h b/libraries/networking/src/BandwidthRecorder.h index a7f51fbb45..c22665d2cc 100644 --- a/libraries/networking/src/BandwidthRecorder.h +++ b/libraries/networking/src/BandwidthRecorder.h @@ -16,7 +16,6 @@ #include #include -#include #include "DependencyManager.h" #include "Node.h" #include "SimpleMovingAverage.h" diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index fe50647c20..78ec64832b 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -32,7 +32,6 @@ DomainHandler::DomainHandler(QObject* parent) : _iceServerSockAddr(), _icePeer(), _isConnected(false), - _handshakeTimer(NULL), _settingsObject(), _failedSettingsRequests(0) { @@ -50,12 +49,6 @@ void DomainHandler::clearConnectionInfo() { } setIsConnected(false); - - if (_handshakeTimer) { - _handshakeTimer->stop(); - delete _handshakeTimer; - _handshakeTimer = NULL; - } } void DomainHandler::clearSettings() { diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 295e6eac01..0877f657e1 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -100,7 +100,6 @@ private: HifiSockAddr _iceServerSockAddr; NetworkPeer _icePeer; bool _isConnected; - QTimer* _handshakeTimer; QJsonObject _settingsObject; int _failedSettingsRequests; }; From cab6cc8c75ab62f94cb0a99c5b4a3cfeceb4975c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 11 Mar 2015 16:30:21 -0700 Subject: [PATCH 20/34] fix crash on exit related to AvatarManager using TextureCach resources --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a7a6c8c094..1e21144546 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -612,7 +612,8 @@ Application::~Application() { // stop the glWidget frame timer so it doesn't call paintGL _glWidget->stopFrameTimer(); - + + DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); From d3bc54091937240832cb31d3ed7e6a1c84f9f3d9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Mar 2015 16:42:29 -0700 Subject: [PATCH 21/34] use glm from hifi S3 now that fedora is down --- cmake/externals/glm/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/externals/glm/CMakeLists.txt b/cmake/externals/glm/CMakeLists.txt index 6e5b1ef870..7825e2c117 100644 --- a/cmake/externals/glm/CMakeLists.txt +++ b/cmake/externals/glm/CMakeLists.txt @@ -3,7 +3,7 @@ set(EXTERNAL_NAME glm) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://pkgs.fedoraproject.org/repo/pkgs/glm/glm-0.9.5.4.zip/fab76fc982b256b46208e5c750ed456a/glm-0.9.5.4.zip + URL http://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.5.4.zip URL_MD5 fab76fc982b256b46208e5c750ed456a BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= From 9f754e40d0234a69bc988eae009bc186a4fcdd3b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Mar 2015 16:59:06 -0700 Subject: [PATCH 22/34] move all silent node timers into LimitedNodeList --- domain-server/src/DomainServer.cpp | 4 ---- interface/src/Application.cpp | 9 --------- libraries/networking/src/LimitedNodeList.cpp | 5 +++++ libraries/networking/src/LimitedNodeList.h | 2 ++ libraries/networking/src/ThreadedAssignment.cpp | 4 ---- 5 files changed, 7 insertions(+), 17 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d02ad73b47..7af9ffd85c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -267,10 +267,6 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded); connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled); - QTimer* silentNodeTimer = new QTimer(this); - connect(silentNodeTimer, SIGNAL(timeout()), nodeList.data(), SLOT(removeSilentNodes())); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); - connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readAvailableDatagrams())); // add whatever static assignments that have been parsed to the queue diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c53f387518..1e2b06ec27 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -146,7 +146,6 @@ const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB static QTimer* locationUpdateTimer = NULL; static QTimer* balanceUpdateTimer = NULL; -static QTimer* silentNodeTimer = NULL; static QTimer* identityPacketTimer = NULL; static QTimer* billboardPacketTimer = NULL; static QTimer* checkFPStimer = NULL; @@ -426,12 +425,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // connect to the packet sent signal of the _entityEditSender connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); - // move the silentNodeTimer to the _nodeThread - silentNodeTimer = new QTimer(); - connect(silentNodeTimer, SIGNAL(timeout()), nodeList.data(), SLOT(removeSilentNodes())); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); - silentNodeTimer->moveToThread(_nodeThread); - // send the identity packet for our avatar each second to our avatar mixer identityPacketTimer = new QTimer(); connect(identityPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendIdentityPacket); @@ -548,7 +541,6 @@ void Application::cleanupBeforeQuit() { // depending on what thread they run in locationUpdateTimer->stop(); balanceUpdateTimer->stop(); - QMetaObject::invokeMethod(silentNodeTimer, "stop", Qt::BlockingQueuedConnection); identityPacketTimer->stop(); billboardPacketTimer->stop(); checkFPStimer->stop(); @@ -558,7 +550,6 @@ void Application::cleanupBeforeQuit() { // and then delete those that got created by "new" delete locationUpdateTimer; delete balanceUpdateTimer; - delete silentNodeTimer; delete identityPacketTimer; delete billboardPacketTimer; delete checkFPStimer; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 279d958082..520dc650ed 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -80,6 +80,10 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short connect(localSocketUpdate, &QTimer::timeout, this, &LimitedNodeList::updateLocalSockAddr); localSocketUpdate->start(LOCAL_SOCKET_UPDATE_INTERVAL_MSECS); + QTimer* silentNodeTimer = new QTimer(this); + connect(silentNodeTimer, &QTimer::timeout, this, &LimitedNodeList::removeSilentNodes); + silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); + // check the local socket right now updateLocalSockAddr(); @@ -500,6 +504,7 @@ void LimitedNodeList::resetPacketStats() { } void LimitedNodeList::removeSilentNodes() { + QSet killedNodes; eachNodeHashIterator([&](NodeHash::iterator& it){ diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index afbdf23fba..a071eced31 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -223,6 +223,8 @@ protected: HifiSockAddr _localSockAddr; HifiSockAddr _publicSockAddr; HifiSockAddr _stunSockAddr; + + QTimer* _silentNodeTimer; // XXX can BandwidthRecorder be used for this? int _numCollectedPackets; diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index ea94a8e22c..79b4e7f437 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -67,10 +67,6 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); - QTimer* silentNodeRemovalTimer = new QTimer(this); - connect(silentNodeRemovalTimer, SIGNAL(timeout()), nodeList.data(), SLOT(removeSilentNodes())); - silentNodeRemovalTimer->start(NODE_SILENCE_THRESHOLD_MSECS); - if (shouldSendStats) { // send a stats packet every 1 second QTimer* statsTimer = new QTimer(this); From 5d9a3811ca2b06d0baf0222875d4f7f3b0f9710f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Mar 2015 17:14:20 -0700 Subject: [PATCH 23/34] more graceful cleanup for Application NodeList and DatagramProcessor --- interface/src/Application.cpp | 31 ++++++++++++++++----------- interface/src/Application.h | 6 ++---- interface/src/DatagramProcessor.cpp | 4 ++++ interface/src/DatagramProcessor.h | 1 + libraries/networking/src/NodeList.cpp | 5 +++++ libraries/networking/src/NodeList.h | 2 ++ 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1e2b06ec27..c0c7bca214 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -256,7 +256,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _dependencyManagerIsSetup(setupEssentials(argc, argv)), _window(new MainWindow(desktop())), _toolWindow(NULL), - _nodeThread(new QThread(this)), _datagramProcessor(), _undoStack(), _undoStackScriptingInterface(&_undoStack), @@ -327,18 +326,20 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _runningScriptsWidget = new RunningScriptsWidget(_window); // start the nodeThread so its event loop is running - _nodeThread->setObjectName("Datagram Processor Thread"); - _nodeThread->start(); + QThread* nodeThread = new QThread(); + nodeThread->setObjectName("Datagram Processor Thread"); + nodeThread->start(); // make sure the node thread is given highest priority - _nodeThread->setPriority(QThread::TimeCriticalPriority); + nodeThread->setPriority(QThread::TimeCriticalPriority); + + _datagramProcessor = new DatagramProcessor(nodeList.data()); // put the NodeList and datagram processing on the node thread - nodeList->moveToThread(_nodeThread); - _datagramProcessor.moveToThread(_nodeThread); + nodeList->moveToThread(nodeThread); // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal - connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams())); + connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _datagramProcessor, &DatagramProcessor::processDatagrams); // put the audio processing on a separate thread QThread* audioThread = new QThread(); @@ -533,7 +534,7 @@ void Application::aboutToQuit() { } void Application::cleanupBeforeQuit() { - _datagramProcessor.shutdown(); // tell the datagram processor we're shutting down, so it can short circuit + _datagramProcessor->shutdown(); // tell the datagram processor we're shutting down, so it can short circuit _entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts ScriptEngine::stopAllScripts(this); // stop all currently running global scripts @@ -581,10 +582,6 @@ Application::~Application() { tree->lockForWrite(); _entities.getTree()->setSimulation(NULL); tree->unlock(); - - // ask the datagram processing thread to quit and wait until it is done - _nodeThread->quit(); - _nodeThread->wait(); _octreeProcessor.terminate(); _entityEditSender.terminate(); @@ -603,6 +600,14 @@ Application::~Application() { DependencyManager::destroy(); //DependencyManager::destroy(); DependencyManager::destroy(); + + auto nodeList = DependencyManager::get(); + QThread* nodeThread = nodeList->thread(); + nodeList->deleteLater(); + + // ask the node thread to quit and wait until it is done + nodeThread->quit(); + nodeThread->wait(); qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages } @@ -1459,7 +1464,7 @@ void Application::checkFPS() { _fps = (float)_frameCount / diffTime; _frameCount = 0; - _datagramProcessor.resetCounters(); + _datagramProcessor->resetCounters(); _timerStart.start(); // ask the node list to check in with the domain server diff --git a/interface/src/Application.h b/interface/src/Application.h index 1da5f430b6..2841a39707 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -445,10 +445,8 @@ private: MainWindow* _window; ToolWindow* _toolWindow; - - - QThread* _nodeThread; - DatagramProcessor _datagramProcessor; + + DatagramProcessor* _datagramProcessor; QUndoStack _undoStack; UndoStackScriptingInterface _undoStackScriptingInterface; diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 1e63ce6655..b22eb96030 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -27,6 +27,10 @@ DatagramProcessor::DatagramProcessor(QObject* parent) : } +DatagramProcessor::~DatagramProcessor() { + qDebug() << "DP dtor called from" << QThread::currentThread() << "and the DP thread is" << thread(); +} + void DatagramProcessor::processDatagrams() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "DatagramProcessor::processDatagrams()"); diff --git a/interface/src/DatagramProcessor.h b/interface/src/DatagramProcessor.h index 7fc192ee2d..0a6fa3689a 100644 --- a/interface/src/DatagramProcessor.h +++ b/interface/src/DatagramProcessor.h @@ -18,6 +18,7 @@ class DatagramProcessor : public QObject { Q_OBJECT public: DatagramProcessor(QObject* parent = 0); + ~DatagramProcessor(); int getInPacketCount() const { return _inPacketCount; } int getOutPacketCount() const { return _outPacketCount; } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index e63f230f6e..9ca229a245 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -62,6 +63,10 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset); } +NodeList::~NodeList() { + qDebug() << "NL dtor called from" << QThread::currentThread() << "and the NL thread is" << thread(); +} + qint64 NodeList::sendStats(const QJsonObject& statsObject, HifiSockAddr destination) { QByteArray statsPacket = byteArrayWithPopulatedHeader(PacketTypeNodeJsonStats); QDataStream statsPacketStream(&statsPacket, QIODevice::Append); diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 1c6de4bb6c..0624ae5810 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -78,6 +78,8 @@ private: NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton + ~NodeList(); + void sendSTUNRequest(); bool processSTUNResponse(const QByteArray& packet); From 7a8e94f1e572fc927b4c3ecc6b5453384ddc1572 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Mar 2015 17:18:49 -0700 Subject: [PATCH 24/34] remove extra debug information for node thread cleanup --- interface/src/DatagramProcessor.cpp | 4 ---- interface/src/DatagramProcessor.h | 1 - libraries/networking/src/NodeList.cpp | 4 ---- libraries/networking/src/NodeList.h | 2 -- 4 files changed, 11 deletions(-) diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index b22eb96030..1e63ce6655 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -27,10 +27,6 @@ DatagramProcessor::DatagramProcessor(QObject* parent) : } -DatagramProcessor::~DatagramProcessor() { - qDebug() << "DP dtor called from" << QThread::currentThread() << "and the DP thread is" << thread(); -} - void DatagramProcessor::processDatagrams() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "DatagramProcessor::processDatagrams()"); diff --git a/interface/src/DatagramProcessor.h b/interface/src/DatagramProcessor.h index 0a6fa3689a..7fc192ee2d 100644 --- a/interface/src/DatagramProcessor.h +++ b/interface/src/DatagramProcessor.h @@ -18,7 +18,6 @@ class DatagramProcessor : public QObject { Q_OBJECT public: DatagramProcessor(QObject* parent = 0); - ~DatagramProcessor(); int getInPacketCount() const { return _inPacketCount; } int getOutPacketCount() const { return _outPacketCount; } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 9ca229a245..7e1854fb13 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -63,10 +63,6 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset); } -NodeList::~NodeList() { - qDebug() << "NL dtor called from" << QThread::currentThread() << "and the NL thread is" << thread(); -} - qint64 NodeList::sendStats(const QJsonObject& statsObject, HifiSockAddr destination) { QByteArray statsPacket = byteArrayWithPopulatedHeader(PacketTypeNodeJsonStats); QDataStream statsPacketStream(&statsPacket, QIODevice::Append); diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 0624ae5810..1c6de4bb6c 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -78,8 +78,6 @@ private: NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton - ~NodeList(); - void sendSTUNRequest(); bool processSTUNResponse(const QByteArray& packet); From 851d020390f302ccf4ad22bc700f386b123c6deb Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Mar 2015 17:21:49 -0700 Subject: [PATCH 25/34] remove QThread include required for debug --- libraries/networking/src/NodeList.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 7e1854fb13..e63f230f6e 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include From d7e99594aeaab9d984b5f13f27da7abbbd031e8e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Mar 2015 18:04:32 -0700 Subject: [PATCH 26/34] make the application the parent of the node thread --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c0c7bca214..4061ad97f6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -326,7 +326,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _runningScriptsWidget = new RunningScriptsWidget(_window); // start the nodeThread so its event loop is running - QThread* nodeThread = new QThread(); + QThread* nodeThread = new QThread(this); nodeThread->setObjectName("Datagram Processor Thread"); nodeThread->start(); From decd240f60413b269df24e4a048058423cc73e34 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 12 Mar 2015 14:26:12 +0100 Subject: [PATCH 27/34] Update Login menu item on startup after creation --- interface/src/Application.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a7a6c8c094..1e8db8619d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1806,6 +1806,9 @@ void Application::initDisplay() { } void Application::init() { + // Make sure Login state is up to date + DependencyManager::get()->toggleLoginDialog(); + _environment.init(); DependencyManager::get()->init(this); From 52c0ba817a158c564626fdc54f67a46b794da5b9 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 12 Mar 2015 16:32:09 +0100 Subject: [PATCH 28/34] Remove dead code --- interface/src/ModelPackager.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index dec1306b28..49d4ae566f 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -105,24 +105,6 @@ bool ModelPackager::loadModel() { QByteArray fbxContents = fbx.readAll(); _geometry = readFBX(fbxContents, QVariantHash()); -#if 0 /// Temporarily remove this check until CtrlAltDavid can come up with a fix. - // Make sure that a skeleton model has a skeleton - if (_modelType == SKELETON_MODEL) { - if (_geometry.rootJointIndex == -1) { - - QString message = "Your selected skeleton model has no skeleton.\n\nThe upload will be canceled."; - QMessageBox msgBox; - msgBox.setWindowTitle("Model Upload"); - msgBox.setText(message); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setIcon(QMessageBox::Warning); - msgBox.exec(); - - return false; - } - } -#endif - // make sure we have some basic mappings populateBasicMapping(_mapping, _fbxInfo.filePath(), _geometry); return true; From 0bd97ce40c8659cbc6a164ccea0bcbbae990bfb8 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 12 Mar 2015 18:53:29 +0100 Subject: [PATCH 29/34] Dependency::customDeleter uses a lambda --- libraries/shared/src/DependencyManager.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index 3868bf14da..a6abe77c3f 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -17,18 +17,25 @@ #include #include +#include #include #define SINGLETON_DEPENDENCY \ friend class DependencyManager; class Dependency { +public: + typedef std::function DeleterFct; + protected: virtual ~Dependency() {} virtual void customDeleter() { - delete this; + _customDeleterFct(this); } - + + void setCustomDeleterFct(DeleterFct customDeleterFct) { _customDeleterFct = customDeleterFct; } + DeleterFct _customDeleterFct = [](Dependency* pointer) { delete pointer; }; + friend class DependencyManager; }; From eb5206f8e61cf1c261df8d9096e30abc220ab357 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 12 Mar 2015 19:05:06 +0100 Subject: [PATCH 30/34] Naming --- libraries/shared/src/DependencyManager.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index a6abe77c3f..01b755fdd0 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -25,16 +25,16 @@ class Dependency { public: - typedef std::function DeleterFct; + typedef std::function DeleterFunction; protected: virtual ~Dependency() {} virtual void customDeleter() { - _customDeleterFct(this); + _customDeleter(this); } - void setCustomDeleterFct(DeleterFct customDeleterFct) { _customDeleterFct = customDeleterFct; } - DeleterFct _customDeleterFct = [](Dependency* pointer) { delete pointer; }; + void setCustomDeleter(DeleterFunction customDeleter) { _customDeleter = customDeleter; } + DeleterFunction _customDeleter = [](Dependency* pointer) { delete pointer; }; friend class DependencyManager; }; From 9269b2a0b2ae9d05abf721f32b3f3f91ad844b16 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Mar 2015 11:28:07 -0700 Subject: [PATCH 31/34] Revert "NOT MERGEABLE: graceful cleanup on Application dtor for NodeList" --- domain-server/src/DomainServer.cpp | 4 ++ interface/src/Application.cpp | 40 ++++++++++--------- interface/src/Application.h | 6 ++- libraries/networking/src/LimitedNodeList.cpp | 5 --- libraries/networking/src/LimitedNodeList.h | 2 - .../networking/src/ThreadedAssignment.cpp | 4 ++ 6 files changed, 34 insertions(+), 27 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 7af9ffd85c..d02ad73b47 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -267,6 +267,10 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded); connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled); + QTimer* silentNodeTimer = new QTimer(this); + connect(silentNodeTimer, SIGNAL(timeout()), nodeList.data(), SLOT(removeSilentNodes())); + silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); + connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readAvailableDatagrams())); // add whatever static assignments that have been parsed to the queue diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5a84c7895a..141a58b317 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -146,6 +146,7 @@ const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB static QTimer* locationUpdateTimer = NULL; static QTimer* balanceUpdateTimer = NULL; +static QTimer* silentNodeTimer = NULL; static QTimer* identityPacketTimer = NULL; static QTimer* billboardPacketTimer = NULL; static QTimer* checkFPStimer = NULL; @@ -257,6 +258,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _dependencyManagerIsSetup(setupEssentials(argc, argv)), _window(new MainWindow(desktop())), _toolWindow(NULL), + _nodeThread(new QThread(this)), _datagramProcessor(), _undoStack(), _undoStackScriptingInterface(&_undoStack), @@ -327,20 +329,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _runningScriptsWidget = new RunningScriptsWidget(_window); // start the nodeThread so its event loop is running - QThread* nodeThread = new QThread(this); - nodeThread->setObjectName("Datagram Processor Thread"); - nodeThread->start(); + _nodeThread->setObjectName("Datagram Processor Thread"); + _nodeThread->start(); // make sure the node thread is given highest priority - nodeThread->setPriority(QThread::TimeCriticalPriority); - - _datagramProcessor = new DatagramProcessor(nodeList.data()); + _nodeThread->setPriority(QThread::TimeCriticalPriority); // put the NodeList and datagram processing on the node thread - nodeList->moveToThread(nodeThread); + nodeList->moveToThread(_nodeThread); + _datagramProcessor.moveToThread(_nodeThread); // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal - connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _datagramProcessor, &DatagramProcessor::processDatagrams); + connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams())); // put the audio processing on a separate thread QThread* audioThread = new QThread(); @@ -427,6 +427,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // connect to the packet sent signal of the _entityEditSender connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); + // move the silentNodeTimer to the _nodeThread + silentNodeTimer = new QTimer(); + connect(silentNodeTimer, SIGNAL(timeout()), nodeList.data(), SLOT(removeSilentNodes())); + silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); + silentNodeTimer->moveToThread(_nodeThread); + // send the identity packet for our avatar each second to our avatar mixer identityPacketTimer = new QTimer(); connect(identityPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendIdentityPacket); @@ -541,7 +547,7 @@ void Application::aboutToQuit() { } void Application::cleanupBeforeQuit() { - _datagramProcessor->shutdown(); // tell the datagram processor we're shutting down, so it can short circuit + _datagramProcessor.shutdown(); // tell the datagram processor we're shutting down, so it can short circuit _entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts ScriptEngine::stopAllScripts(this); // stop all currently running global scripts @@ -549,6 +555,7 @@ void Application::cleanupBeforeQuit() { // depending on what thread they run in locationUpdateTimer->stop(); balanceUpdateTimer->stop(); + QMetaObject::invokeMethod(silentNodeTimer, "stop", Qt::BlockingQueuedConnection); identityPacketTimer->stop(); billboardPacketTimer->stop(); checkFPStimer->stop(); @@ -558,6 +565,7 @@ void Application::cleanupBeforeQuit() { // and then delete those that got created by "new" delete locationUpdateTimer; delete balanceUpdateTimer; + delete silentNodeTimer; delete identityPacketTimer; delete billboardPacketTimer; delete checkFPStimer; @@ -589,6 +597,10 @@ Application::~Application() { tree->lockForWrite(); _entities.getTree()->setSimulation(NULL); tree->unlock(); + + // ask the datagram processing thread to quit and wait until it is done + _nodeThread->quit(); + _nodeThread->wait(); _octreeProcessor.terminate(); _entityEditSender.terminate(); @@ -608,14 +620,6 @@ Application::~Application() { DependencyManager::destroy(); //DependencyManager::destroy(); DependencyManager::destroy(); - - auto nodeList = DependencyManager::get(); - QThread* nodeThread = nodeList->thread(); - nodeList->deleteLater(); - - // ask the node thread to quit and wait until it is done - nodeThread->quit(); - nodeThread->wait(); qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages } @@ -1494,7 +1498,7 @@ void Application::checkFPS() { _fps = (float)_frameCount / diffTime; _frameCount = 0; - _datagramProcessor->resetCounters(); + _datagramProcessor.resetCounters(); _timerStart.start(); // ask the node list to check in with the domain server diff --git a/interface/src/Application.h b/interface/src/Application.h index 248aaa0f6a..bcd31fcd51 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -445,8 +445,10 @@ private: MainWindow* _window; ToolWindow* _toolWindow; - - DatagramProcessor* _datagramProcessor; + + + QThread* _nodeThread; + DatagramProcessor _datagramProcessor; QUndoStack _undoStack; UndoStackScriptingInterface _undoStackScriptingInterface; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 520dc650ed..279d958082 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -80,10 +80,6 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short connect(localSocketUpdate, &QTimer::timeout, this, &LimitedNodeList::updateLocalSockAddr); localSocketUpdate->start(LOCAL_SOCKET_UPDATE_INTERVAL_MSECS); - QTimer* silentNodeTimer = new QTimer(this); - connect(silentNodeTimer, &QTimer::timeout, this, &LimitedNodeList::removeSilentNodes); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); - // check the local socket right now updateLocalSockAddr(); @@ -504,7 +500,6 @@ void LimitedNodeList::resetPacketStats() { } void LimitedNodeList::removeSilentNodes() { - QSet killedNodes; eachNodeHashIterator([&](NodeHash::iterator& it){ diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index a071eced31..afbdf23fba 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -223,8 +223,6 @@ protected: HifiSockAddr _localSockAddr; HifiSockAddr _publicSockAddr; HifiSockAddr _stunSockAddr; - - QTimer* _silentNodeTimer; // XXX can BandwidthRecorder be used for this? int _numCollectedPackets; diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 79b4e7f437..ea94a8e22c 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -67,6 +67,10 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); + QTimer* silentNodeRemovalTimer = new QTimer(this); + connect(silentNodeRemovalTimer, SIGNAL(timeout()), nodeList.data(), SLOT(removeSilentNodes())); + silentNodeRemovalTimer->start(NODE_SILENCE_THRESHOLD_MSECS); + if (shouldSendStats) { // send a stats packet every 1 second QTimer* statsTimer = new QTimer(this); From 7bfc7477486caf05fbeb29d812541e467e96fde7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Mar 2015 12:13:32 -0700 Subject: [PATCH 32/34] Revert "Revert "NOT MERGEABLE: graceful cleanup on Application dtor for NodeList"" This reverts commit 9269b2a0b2ae9d05abf721f32b3f3f91ad844b16. --- domain-server/src/DomainServer.cpp | 4 -- interface/src/Application.cpp | 40 +++++++++---------- interface/src/Application.h | 6 +-- libraries/networking/src/LimitedNodeList.cpp | 5 +++ libraries/networking/src/LimitedNodeList.h | 2 + .../networking/src/ThreadedAssignment.cpp | 4 -- 6 files changed, 27 insertions(+), 34 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d02ad73b47..7af9ffd85c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -267,10 +267,6 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded); connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled); - QTimer* silentNodeTimer = new QTimer(this); - connect(silentNodeTimer, SIGNAL(timeout()), nodeList.data(), SLOT(removeSilentNodes())); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); - connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readAvailableDatagrams())); // add whatever static assignments that have been parsed to the queue diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 141a58b317..5a84c7895a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -146,7 +146,6 @@ const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB static QTimer* locationUpdateTimer = NULL; static QTimer* balanceUpdateTimer = NULL; -static QTimer* silentNodeTimer = NULL; static QTimer* identityPacketTimer = NULL; static QTimer* billboardPacketTimer = NULL; static QTimer* checkFPStimer = NULL; @@ -258,7 +257,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _dependencyManagerIsSetup(setupEssentials(argc, argv)), _window(new MainWindow(desktop())), _toolWindow(NULL), - _nodeThread(new QThread(this)), _datagramProcessor(), _undoStack(), _undoStackScriptingInterface(&_undoStack), @@ -329,18 +327,20 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _runningScriptsWidget = new RunningScriptsWidget(_window); // start the nodeThread so its event loop is running - _nodeThread->setObjectName("Datagram Processor Thread"); - _nodeThread->start(); + QThread* nodeThread = new QThread(this); + nodeThread->setObjectName("Datagram Processor Thread"); + nodeThread->start(); // make sure the node thread is given highest priority - _nodeThread->setPriority(QThread::TimeCriticalPriority); + nodeThread->setPriority(QThread::TimeCriticalPriority); + + _datagramProcessor = new DatagramProcessor(nodeList.data()); // put the NodeList and datagram processing on the node thread - nodeList->moveToThread(_nodeThread); - _datagramProcessor.moveToThread(_nodeThread); + nodeList->moveToThread(nodeThread); // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal - connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams())); + connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _datagramProcessor, &DatagramProcessor::processDatagrams); // put the audio processing on a separate thread QThread* audioThread = new QThread(); @@ -427,12 +427,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // connect to the packet sent signal of the _entityEditSender connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); - // move the silentNodeTimer to the _nodeThread - silentNodeTimer = new QTimer(); - connect(silentNodeTimer, SIGNAL(timeout()), nodeList.data(), SLOT(removeSilentNodes())); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); - silentNodeTimer->moveToThread(_nodeThread); - // send the identity packet for our avatar each second to our avatar mixer identityPacketTimer = new QTimer(); connect(identityPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendIdentityPacket); @@ -547,7 +541,7 @@ void Application::aboutToQuit() { } void Application::cleanupBeforeQuit() { - _datagramProcessor.shutdown(); // tell the datagram processor we're shutting down, so it can short circuit + _datagramProcessor->shutdown(); // tell the datagram processor we're shutting down, so it can short circuit _entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts ScriptEngine::stopAllScripts(this); // stop all currently running global scripts @@ -555,7 +549,6 @@ void Application::cleanupBeforeQuit() { // depending on what thread they run in locationUpdateTimer->stop(); balanceUpdateTimer->stop(); - QMetaObject::invokeMethod(silentNodeTimer, "stop", Qt::BlockingQueuedConnection); identityPacketTimer->stop(); billboardPacketTimer->stop(); checkFPStimer->stop(); @@ -565,7 +558,6 @@ void Application::cleanupBeforeQuit() { // and then delete those that got created by "new" delete locationUpdateTimer; delete balanceUpdateTimer; - delete silentNodeTimer; delete identityPacketTimer; delete billboardPacketTimer; delete checkFPStimer; @@ -597,10 +589,6 @@ Application::~Application() { tree->lockForWrite(); _entities.getTree()->setSimulation(NULL); tree->unlock(); - - // ask the datagram processing thread to quit and wait until it is done - _nodeThread->quit(); - _nodeThread->wait(); _octreeProcessor.terminate(); _entityEditSender.terminate(); @@ -620,6 +608,14 @@ Application::~Application() { DependencyManager::destroy(); //DependencyManager::destroy(); DependencyManager::destroy(); + + auto nodeList = DependencyManager::get(); + QThread* nodeThread = nodeList->thread(); + nodeList->deleteLater(); + + // ask the node thread to quit and wait until it is done + nodeThread->quit(); + nodeThread->wait(); qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages } @@ -1498,7 +1494,7 @@ void Application::checkFPS() { _fps = (float)_frameCount / diffTime; _frameCount = 0; - _datagramProcessor.resetCounters(); + _datagramProcessor->resetCounters(); _timerStart.start(); // ask the node list to check in with the domain server diff --git a/interface/src/Application.h b/interface/src/Application.h index bcd31fcd51..248aaa0f6a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -445,10 +445,8 @@ private: MainWindow* _window; ToolWindow* _toolWindow; - - - QThread* _nodeThread; - DatagramProcessor _datagramProcessor; + + DatagramProcessor* _datagramProcessor; QUndoStack _undoStack; UndoStackScriptingInterface _undoStackScriptingInterface; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 279d958082..520dc650ed 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -80,6 +80,10 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short connect(localSocketUpdate, &QTimer::timeout, this, &LimitedNodeList::updateLocalSockAddr); localSocketUpdate->start(LOCAL_SOCKET_UPDATE_INTERVAL_MSECS); + QTimer* silentNodeTimer = new QTimer(this); + connect(silentNodeTimer, &QTimer::timeout, this, &LimitedNodeList::removeSilentNodes); + silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); + // check the local socket right now updateLocalSockAddr(); @@ -500,6 +504,7 @@ void LimitedNodeList::resetPacketStats() { } void LimitedNodeList::removeSilentNodes() { + QSet killedNodes; eachNodeHashIterator([&](NodeHash::iterator& it){ diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index afbdf23fba..a071eced31 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -223,6 +223,8 @@ protected: HifiSockAddr _localSockAddr; HifiSockAddr _publicSockAddr; HifiSockAddr _stunSockAddr; + + QTimer* _silentNodeTimer; // XXX can BandwidthRecorder be used for this? int _numCollectedPackets; diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index ea94a8e22c..79b4e7f437 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -67,10 +67,6 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); - QTimer* silentNodeRemovalTimer = new QTimer(this); - connect(silentNodeRemovalTimer, SIGNAL(timeout()), nodeList.data(), SLOT(removeSilentNodes())); - silentNodeRemovalTimer->start(NODE_SILENCE_THRESHOLD_MSECS); - if (shouldSendStats) { // send a stats packet every 1 second QTimer* statsTimer = new QTimer(this); From 09e2c0987e6af3155b0b668b76ffc5ab2b4b0820 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Mar 2015 12:27:48 -0700 Subject: [PATCH 33/34] use Dependency customDeleter for Application NL --- interface/src/Application.cpp | 10 +++++++--- libraries/networking/src/NodeList.h | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5a84c7895a..baa7dd633d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -336,6 +336,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _datagramProcessor = new DatagramProcessor(nodeList.data()); + // have the NodeList use deleteLater from DM customDeleter + nodeList->setCustomDeleter([](Dependency* dependency) { + static_cast(dependency)->deleteLater(); + }); + // put the NodeList and datagram processing on the node thread nodeList->moveToThread(nodeThread); @@ -609,9 +614,8 @@ Application::~Application() { //DependencyManager::destroy(); DependencyManager::destroy(); - auto nodeList = DependencyManager::get(); - QThread* nodeThread = nodeList->thread(); - nodeList->deleteLater(); + QThread* nodeThread = DependencyManager::get()->thread(); + DependencyManager::destroy(); // ask the node thread to quit and wait until it is done nodeThread->quit(); diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 1c6de4bb6c..ccfaa7a4cf 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -37,6 +37,7 @@ const quint64 DOMAIN_SERVER_CHECK_IN_MSECS = 1 * 1000; const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5; +class Application; class Assignment; class NodeList : public LimitedNodeList { @@ -95,6 +96,8 @@ private: HifiSockAddr _assignmentServerSocket; bool _hasCompletedInitialSTUNFailure; unsigned int _stunRequestsSinceSuccess; + + friend class Application; }; #endif // hifi_NodeList_h From 471e55c8cedf7ab321aaed72b84dfe55c741c3a9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Mar 2015 12:36:39 -0700 Subject: [PATCH 34/34] fix some lingering warnings --- interface/src/Application.cpp | 1 - interface/src/avatar/MyAvatar.cpp | 2 - interface/src/avatar/SkeletonModel.cpp | 2 - libraries/entities/src/LightEntityItem.cpp | 1 - libraries/model/src/model/Stage.cpp | 86 +++++++++++----------- libraries/physics/src/MeshInfo.cpp | 8 +- 6 files changed, 48 insertions(+), 52 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index baa7dd633d..72c17ed09b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2739,7 +2739,6 @@ const GLfloat WORLD_DIFFUSE_COLOR[] = { 0.6f, 0.525f, 0.525f }; const GLfloat WORLD_SPECULAR_COLOR[] = { 0.94f, 0.94f, 0.737f, 1.0f }; const glm::vec3 GLOBAL_LIGHT_COLOR = { 0.6f, 0.525f, 0.525f }; -const float GLOBAL_LIGHT_INTENSITY = 1.0f; void Application::setupWorldLight() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9ecc0a3798..d9c9ff3ad1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -49,8 +49,6 @@ using namespace std; const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); const float YAW_SPEED = 500.0f; // degrees/sec const float PITCH_SPEED = 100.0f; // degrees/sec -const float COLLISION_RADIUS_SCALAR = 1.2f; // pertains to avatar-to-avatar collisions -const float COLLISION_RADIUS_SCALE = 0.125f; const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; const float MAX_WALKING_SPEED = 2.5f; // human walking speed diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index d083116ecd..52dd424b71 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -791,8 +791,6 @@ void SkeletonModel::renderBoundingCollisionShapes(float alpha) { glPopMatrix(); } -const int BALL_SUBDIVISIONS = 10; - bool SkeletonModel::hasSkeleton() { return isActive() ? _geometry->getFBXGeometry().rootJointIndex != -1 : false; } diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp index 3265891b18..62a44c7e21 100644 --- a/libraries/entities/src/LightEntityItem.cpp +++ b/libraries/entities/src/LightEntityItem.cpp @@ -32,7 +32,6 @@ LightEntityItem::LightEntityItem(const EntityItemID& entityItemID, const EntityI _type = EntityTypes::Light; // default property values - const quint8 MAX_COLOR = 255; _color[RED_INDEX] = _color[GREEN_INDEX] = _color[BLUE_INDEX] = 0; _intensity = 1.0f; _exponent = 0.0f; diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 970539a908..1c171eee76 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -134,55 +134,55 @@ void EarthSunModel::setSunLongitude(float lon) { _sunLongitude = validateLongitude(lon); invalidate(); } - -Atmosphere::Atmosphere() { - // only if created from nothing shall we create the Buffer to store the properties - Data data; - _dataBuffer = gpu::BufferView(new gpu::Buffer(sizeof(Data), (const gpu::Buffer::Byte*) &data)); - + +Atmosphere::Atmosphere() { + // only if created from nothing shall we create the Buffer to store the properties + Data data; + _dataBuffer = gpu::BufferView(new gpu::Buffer(sizeof(Data), (const gpu::Buffer::Byte*) &data)); + setScatteringWavelength(_scatteringWavelength); setRayleighScattering(_rayleighScattering); setInnerOuterRadiuses(getInnerRadius(), getOuterRadius()); } -void Atmosphere::setScatteringWavelength(Vec3 wavelength) { - _scatteringWavelength = wavelength; - Data& data = editData(); - data._invWaveLength = Vec4(1.0f / powf(wavelength.x, 4.0f), 1.0f / powf(wavelength.y, 4.0f), 1.0f / powf(wavelength.z, 4.0f), 0.0f); -} - -void Atmosphere::setRayleighScattering(float scattering) { - _rayleighScattering = scattering; - updateScattering(); -} - -void Atmosphere::setMieScattering(float scattering) { - _mieScattering = scattering; - updateScattering(); -} - -void Atmosphere::setSunBrightness(float brightness) { - _sunBrightness = brightness; - updateScattering(); -} +void Atmosphere::setScatteringWavelength(Vec3 wavelength) { + _scatteringWavelength = wavelength; + Data& data = editData(); + data._invWaveLength = Vec4(1.0f / powf(wavelength.x, 4.0f), 1.0f / powf(wavelength.y, 4.0f), 1.0f / powf(wavelength.z, 4.0f), 0.0f); +} -void Atmosphere::updateScattering() { - Data& data = editData(); - - data._scatterings.x = getRayleighScattering() * getSunBrightness(); - data._scatterings.y = getMieScattering() * getSunBrightness(); - - data._scatterings.z = getRayleighScattering() * 4.0f * glm::pi(); - data._scatterings.w = getMieScattering() * 4.0f * glm::pi(); -} +void Atmosphere::setRayleighScattering(float scattering) { + _rayleighScattering = scattering; + updateScattering(); +} -void Atmosphere::setInnerOuterRadiuses(float inner, float outer) { - Data& data = editData(); - data._radiuses.x = inner; - data._radiuses.y = outer; - data._scales.x = 1.0f / (outer - inner); - data._scales.z = data._scales.x / data._scales.y; -} +void Atmosphere::setMieScattering(float scattering) { + _mieScattering = scattering; + updateScattering(); +} + +void Atmosphere::setSunBrightness(float brightness) { + _sunBrightness = brightness; + updateScattering(); +} + +void Atmosphere::updateScattering() { + Data& data = editData(); + + data._scatterings.x = getRayleighScattering() * getSunBrightness(); + data._scatterings.y = getMieScattering() * getSunBrightness(); + + data._scatterings.z = getRayleighScattering() * 4.0f * glm::pi(); + data._scatterings.w = getMieScattering() * 4.0f * glm::pi(); +} + +void Atmosphere::setInnerOuterRadiuses(float inner, float outer) { + Data& data = editData(); + data._radiuses.x = inner; + data._radiuses.y = outer; + data._scales.x = 1.0f / (outer - inner); + data._scales.z = data._scales.x / data._scales.y; +} const int NUM_DAYS_PER_YEAR = 365; @@ -267,7 +267,7 @@ void SunSkyStage::updateGraphicsObject() const { static int firstTime = 0; if (firstTime == 0) { firstTime++; - bool result = gpu::Shader::makeProgram(*(_skyPipeline->getProgram())); + gpu::Shader::makeProgram(*(_skyPipeline->getProgram())); } diff --git a/libraries/physics/src/MeshInfo.cpp b/libraries/physics/src/MeshInfo.cpp index 8df5ff914d..29ddc74a98 100644 --- a/libraries/physics/src/MeshInfo.cpp +++ b/libraries/physics/src/MeshInfo.cpp @@ -17,9 +17,11 @@ using namespace meshinfo; //origin is the default reference point for generating the tetrahedron from each triangle of the mesh. MeshInfo::MeshInfo(vector *vertices, vector *triangles) :\ -_vertices(vertices), -_triangles(triangles), -_centerOfMass(Vertex(0.0, 0.0, 0.0)){ + _vertices(vertices), + _centerOfMass(Vertex(0.0, 0.0, 0.0)), + _triangles(triangles) +{ + } MeshInfo::~MeshInfo(){