From e0400dbd9c892f8331da9ffd317264a64c5775a9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 4 May 2014 15:23:31 -0700 Subject: [PATCH 1/9] Working on allowing attachment models to be uploaded. --- interface/src/Application.cpp | 30 ++++++++------ interface/src/Application.h | 9 ++-- interface/src/Menu.cpp | 2 + interface/src/Menu.h | 1 + interface/src/ModelUploader.cpp | 48 +++++++++++----------- interface/src/ModelUploader.h | 10 +++-- interface/src/ui/ModelsBrowser.cpp | 11 ++--- interface/src/ui/ModelsBrowser.h | 8 ++-- interface/src/ui/PreferencesDialog.cpp | 4 +- libraries/avatars/src/AvatarData.h | 11 +++++ libraries/networking/src/PacketHeaders.cpp | 2 + 11 files changed, 79 insertions(+), 57 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 42f11ee576..5ce9468318 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -63,11 +63,11 @@ #include #include #include -#include #include "Application.h" #include "InterfaceVersion.h" #include "Menu.h" +#include "ModelUploader.h" #include "Util.h" #include "devices/OculusManager.h" #include "devices/TV3DManager.h" @@ -3090,6 +3090,16 @@ void Application::setMenuShortcutsEnabled(bool enabled) { setShortcutsEnabled(_window->menuBar(), enabled); } +void Application::uploadModel(ModelType modelType) { + ModelUploader* uploader = new ModelUploader(modelType); + QThread* thread = new QThread(); + thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit())); + thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater())); + uploader->connect(thread, SIGNAL(started()), SLOT(send())); + + thread->start(); +} + void Application::updateWindowTitle(){ QString buildVersion = " (build " + applicationVersion() + ")"; @@ -3417,22 +3427,16 @@ void Application::toggleRunningScriptsWidget() { } } -void Application::uploadFST(bool isHead) { - ModelUploader* uploader = new ModelUploader(isHead); - QThread* thread = new QThread(); - thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit())); - thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater())); - uploader->connect(thread, SIGNAL(started()), SLOT(send())); - - thread->start(); -} - void Application::uploadHead() { - uploadFST(true); + uploadModel(HEAD_MODEL); } void Application::uploadSkeleton() { - uploadFST(false); + uploadModel(SKELETON_MODEL); +} + +void Application::uploadAttachment() { + uploadModel(ATTACHMENT_MODEL); } ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 96b472e553..a7073ac4e9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -71,6 +71,7 @@ #include "scripting/ControllerScriptingInterface.h" #include "ui/BandwidthDialog.h" #include "ui/BandwidthMeter.h" +#include "ui/ModelsBrowser.h" #include "ui/OctreeStatsDialog.h" #include "ui/RearMirrorTools.h" #include "ui/LodToolsDialog.h" @@ -295,9 +296,9 @@ public slots: void reloadAllScripts(); void toggleRunningScriptsWidget(); - void uploadFST(bool isHead); void uploadHead(); void uploadSkeleton(); + void uploadAttachment(); void bumpSettings() { ++_numChangedSettings; } @@ -375,13 +376,11 @@ private: void setMenuShortcutsEnabled(bool enabled); + void uploadModel(ModelType modelType); + static void attachNewHeadToNode(Node *newNode); static void* networkReceive(void* args); // network receive thread - void findAxisAlignment(); - - void displayRearMirrorTools(); - MainWindow* _window; GLCanvas* _glWidget; // our GLCanvas has a couple extra features diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index baa82a2f6a..b6df2f7269 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -157,6 +157,8 @@ Menu::Menu() : addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model"); addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead())); addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0, Application::getInstance(), SLOT(uploadSkeleton())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadAttachment, 0, + Application::getInstance(), SLOT(uploadAttachment())); addDisabledActionAndSeparator(fileMenu, "Settings"); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings())); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index da2585f81a..e37d256f62 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -363,6 +363,7 @@ namespace MenuOption { const QString TestPing = "Test Ping"; const QString TransmitterDrive = "Transmitter Drive"; const QString TurnWithHead = "Turn using Head"; + const QString UploadAttachment = "Upload Attachment Model"; const QString UploadHead = "Upload Head Model"; const QString UploadSkeleton = "Upload Skeleton Model"; const QString Visage = "Visage"; diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index 0ffd725716..2b86e04829 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -59,11 +59,11 @@ static const int MAX_CHECK = 30; static const int QCOMPRESS_HEADER_POSITION = 0; static const int QCOMPRESS_HEADER_SIZE = 4; -ModelUploader::ModelUploader(bool isHead) : +ModelUploader::ModelUploader(ModelType modelType) : _lodCount(-1), _texturesCount(-1), _totalSize(0), - _isHead(isHead), + _modelType(modelType), _readyToSend(false), _dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)), _numberOfChecks(MAX_CHECK) @@ -190,7 +190,7 @@ bool ModelUploader::zip() { } // open the dialog to configure the rest - ModelPropertiesDialog properties(_isHead, mapping, basePath, geometry); + ModelPropertiesDialog properties(_modelType, mapping, basePath, geometry); if (properties.exec() == QDialog::Rejected) { return false; } @@ -202,7 +202,7 @@ bool ModelUploader::zip() { textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"model_name\""); textPart.setBody(nameField); _dataMultiPart->append(textPart); - _url = S3_URL + ((_isHead)? "/models/heads/" : "/models/skeletons/") + nameField + ".fst"; + _url = S3_URL + "/models/" + MODEL_TYPE_NAMES[_modelType] + "/" + nameField + ".fst"; } else { QMessageBox::warning(NULL, QString("ModelUploader::zip()"), @@ -260,11 +260,7 @@ bool ModelUploader::zip() { QHttpPart textPart; textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" " name=\"model_category\""); - if (_isHead) { - textPart.setBody("heads"); - } else { - textPart.setBody("skeletons"); - } + textPart.setBody(MODEL_TYPE_NAMES[_modelType]); _dataMultiPart->append(textPart); _readyToSend = true; @@ -510,9 +506,9 @@ bool ModelUploader::addPart(const QFile& file, const QByteArray& contents, const return true; } -ModelPropertiesDialog::ModelPropertiesDialog(bool isHead, const QVariantHash& originalMapping, +ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping, const QString& basePath, const FBXGeometry& geometry) : - _isHead(isHead), + _modelType(modelType), _originalMapping(originalMapping), _basePath(basePath), _geometry(geometry) { @@ -531,10 +527,12 @@ ModelPropertiesDialog::ModelPropertiesDialog(bool isHead, const QVariantHash& or _scale->setMaximum(FLT_MAX); _scale->setSingleStep(0.01); - form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox()); - form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox()); - form->addRow("Neck Joint:", _neckJoint = createJointBox()); - if (!isHead) { + if (_modelType != ATTACHMENT_MODEL) { + 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()); @@ -573,10 +571,12 @@ QVariantHash ModelPropertiesDialog::getMapping() const { mapping.insert(JOINT_INDEX_FIELD, jointIndices); QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); - insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText()); - insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText()); - insertJointMapping(joints, "jointNeck", _neckJoint->currentText()); - if (!_isHead) { + if (_modelType != ATTACHMENT_MODEL) { + 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()); @@ -604,10 +604,12 @@ void ModelPropertiesDialog::reset() { _scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble()); QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash(); - setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString()); - setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString()); - setJointText(_neckJoint, jointHash.value("jointNeck").toString()); - if (!_isHead) { + if (_modelType != ATTACHMENT_MODEL) { + 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()); diff --git a/interface/src/ModelUploader.h b/interface/src/ModelUploader.h index 11594b3d95..499bfad03b 100644 --- a/interface/src/ModelUploader.h +++ b/interface/src/ModelUploader.h @@ -17,6 +17,8 @@ #include +#include "ui/ModelsBrowser.h" + class QComboBox; class QDoubleSpinBox; class QFileInfo; @@ -30,7 +32,7 @@ class ModelUploader : public QObject { Q_OBJECT public: - ModelUploader(bool isHead); + ModelUploader(ModelType type); ~ModelUploader(); public slots: @@ -49,7 +51,7 @@ private: int _lodCount; int _texturesCount; int _totalSize; - bool _isHead; + ModelType _modelType; bool _readyToSend; QHttpMultiPart* _dataMultiPart; @@ -73,7 +75,7 @@ class ModelPropertiesDialog : public QDialog { Q_OBJECT public: - ModelPropertiesDialog(bool isHead, const QVariantHash& originalMapping, + ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping, const QString& basePath, const FBXGeometry& geometry); QVariantHash getMapping() const; @@ -87,7 +89,7 @@ private: QComboBox* createJointBox(bool withNone = true) const; void insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const; - bool _isHead; + ModelType _modelType; QVariantHash _originalMapping; QString _basePath; FBXGeometry _geometry; diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 77e056bdd3..f65829a8ac 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -22,10 +22,11 @@ #include "ModelsBrowser.h" +const char* MODEL_TYPE_NAMES[] = { "heads", "skeletons", "attachments" }; + static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com"; static const QString PUBLIC_URL = "http://public.highfidelity.io"; -static const QString HEAD_MODELS_LOCATION = "models/heads"; -static const QString SKELETON_MODELS_LOCATION = "models/skeletons/"; +static const QString MODELS_LOCATION = "models/"; static const QString PREFIX_PARAMETER_NAME = "prefix"; static const QString MARKER_PARAMETER_NAME = "marker"; @@ -243,11 +244,7 @@ void ModelHandler::queryNewFiles(QString marker) { // Build query QUrl url(S3_URL); QUrlQuery query; - if (_type == Head) { - query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION); - } else if (_type == Skeleton) { - query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION); - } + query.addQueryItem(PREFIX_PARAMETER_NAME, MODELS_LOCATION + MODEL_TYPE_NAMES[_type]); if (!marker.isEmpty()) { query.addQueryItem(MARKER_PARAMETER_NAME, marker); diff --git a/interface/src/ui/ModelsBrowser.h b/interface/src/ui/ModelsBrowser.h index 81f64c6730..ff273a45bc 100644 --- a/interface/src/ui/ModelsBrowser.h +++ b/interface/src/ui/ModelsBrowser.h @@ -16,12 +16,14 @@ #include #include - enum ModelType { - Head, - Skeleton + HEAD_MODEL, + SKELETON_MODEL, + ATTACHMENT_MODEL }; +extern const char* MODEL_TYPE_NAMES[]; + class ModelHandler : public QObject { Q_OBJECT public: diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 7a70b743bd..eed33fda23 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -48,7 +48,7 @@ void PreferencesDialog::openHeadModelBrowser() { setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint); show(); - ModelsBrowser modelBrowser(Head); + ModelsBrowser modelBrowser(HEAD_MODEL); connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setHeadUrl); modelBrowser.browse(); @@ -60,7 +60,7 @@ void PreferencesDialog::openBodyModelBrowser() { setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint); show(); - ModelsBrowser modelBrowser(Skeleton); + ModelsBrowser modelBrowser(SKELETON_MODEL); connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setSkeletonUrl); modelBrowser.browse(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index be47aed1ba..78b33571e8 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -81,6 +81,7 @@ const glm::vec3 vec3Zero(0.0f); class QNetworkAccessManager; +class AttachmentData; class JointData; class AvatarData : public QObject { @@ -275,6 +276,7 @@ protected: QUrl _faceModelURL; QUrl _skeletonModelURL; + QVector _attachmentData; QString _displayName; QRect _displayNameBoundingRect; @@ -309,4 +311,13 @@ public: glm::quat rotation; }; +class AttachmentData { +public: + QUrl modelURL; + int jointIndex; + glm::vec3 translation; + glm::quat rotation; + float scale; +}; + #endif // hifi_AvatarData_h diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 0955759097..0785b81581 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -49,6 +49,8 @@ PacketVersion versionForPacketType(PacketType type) { switch (type) { case PacketTypeAvatarData: return 3; + case PacketTypeAvatarIdentity: + return 1; case PacketTypeEnvironmentData: return 1; case PacketTypeParticleData: From 53a276090547f5b49483e06c4fd564a89cd69712 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 4 May 2014 16:24:23 -0700 Subject: [PATCH 2/9] More attachment bits. --- interface/src/Menu.cpp | 12 ++++++++ interface/src/Menu.h | 4 +++ interface/src/avatar/MyAvatar.cpp | 40 ++++++++++++++++++++++++++ interface/src/ui/AttachmentsDialog.cpp | 39 +++++++++++++++++++++++++ interface/src/ui/AttachmentsDialog.h | 30 +++++++++++++++++++ libraries/avatars/src/AvatarData.cpp | 34 ++++++++++++++++++++-- libraries/avatars/src/AvatarData.h | 12 +++++++- libraries/shared/src/StreamUtils.cpp | 18 ++++++++++++ libraries/shared/src/StreamUtils.h | 7 +++++ 9 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 interface/src/ui/AttachmentsDialog.cpp create mode 100644 interface/src/ui/AttachmentsDialog.h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index b6df2f7269..ed03e77021 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -37,6 +37,7 @@ #include "Menu.h" #include "scripting/MenuScriptingInterface.h" #include "Util.h" +#include "ui/AttachmentsDialog.h" #include "ui/InfoView.h" #include "ui/MetavoxelEditor.h" #include "ui/ModelsBrowser.h" @@ -189,6 +190,8 @@ Menu::Menu() : SLOT(editPreferences()), QAction::PreferencesRole); + addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0, this, SLOT(editAttachments())); + addDisabledActionAndSeparator(editMenu, "Physics"); QObject* avatar = appInstance->getAvatar(); addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, true, @@ -834,6 +837,15 @@ void Menu::editPreferences() { } } +void Menu::editAttachments() { + if (!_attachmentsDialog) { + _attachmentsDialog = new AttachmentsDialog(); + _attachmentsDialog->show(); + } else { + _attachmentsDialog->close(); + } +} + void Menu::goToDomain(const QString newDomain) { if (NodeList::getInstance()->getDomainHandler().getHostname() != newDomain) { // send a node kill request, indicating to other clients that they should play the "disappeared" effect diff --git a/interface/src/Menu.h b/interface/src/Menu.h index e37d256f62..348d61bab8 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -64,6 +64,7 @@ struct ViewFrustumOffset { class QSettings; +class AttachmentsDialog; class BandwidthDialog; class LodToolsDialog; class MetavoxelEditor; @@ -171,6 +172,7 @@ public slots: private slots: void aboutApp(); void editPreferences(); + void editAttachments(); void goToDomainDialog(); void goToLocation(); void nameLocation(); @@ -252,6 +254,7 @@ private: SimpleMovingAverage _fastFPSAverage; QAction* _loginAction; QPointer _preferencesDialog; + QPointer _attachmentsDialog; QAction* _chatAction; QString _snapshotsLocation; }; @@ -261,6 +264,7 @@ namespace MenuOption { const QString AlignForearmsWithWrists = "Align Forearms with Wrists"; const QString AmbientOcclusion = "Ambient Occlusion"; const QString Atmosphere = "Atmosphere"; + const QString Attachments = "Attachments..."; const QString AudioNoiseReduction = "Audio Noise Reduction"; const QString AudioScope = "Audio Scope"; const QString AudioScopePause = "Pause Audio Scope"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 652eb56258..6af7235117 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -491,6 +491,24 @@ void MyAvatar::saveData(QSettings* settings) { settings->setValue("faceModelURL", _faceModelURL); settings->setValue("skeletonModelURL", _skeletonModelURL); + + settings->beginWriteArray("attachmentData"); + for (int i = 0; i < _attachmentData.size(); i++) { + settings->setArrayIndex(i); + const AttachmentData& attachment = _attachmentData.at(i); + settings->setValue("modelURL", attachment.modelURL); + settings->setValue("jointName", attachment.jointName); + settings->setValue("translation_x", attachment.translation.x); + settings->setValue("translation_y", attachment.translation.y); + settings->setValue("translation_z", attachment.translation.z); + glm::vec3 eulers = safeEulerAngles(attachment.rotation); + settings->setValue("rotation_x", eulers.x); + settings->setValue("rotation_y", eulers.y); + settings->setValue("rotation_z", eulers.z); + settings->setValue("scale", attachment.scale); + } + settings->endArray(); + settings->setValue("displayName", _displayName); settings->endGroup(); @@ -519,6 +537,28 @@ void MyAvatar::loadData(QSettings* settings) { setFaceModelURL(settings->value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl()); setSkeletonModelURL(settings->value("skeletonModelURL").toUrl()); + + QVector attachmentData; + int attachmentCount = settings->beginReadArray("attachmentData"); + for (int i = 0; i < attachmentCount; i++) { + settings->setArrayIndex(i); + AttachmentData attachment; + attachment.modelURL = settings->value("modelURL").toUrl(); + attachment.jointName = settings->value("jointName").toString(); + attachment.translation.x = loadSetting(settings, "translation_x", 0.0f); + attachment.translation.y = loadSetting(settings, "translation_y", 0.0f); + attachment.translation.z = loadSetting(settings, "translation_z", 0.0f); + glm::vec3 eulers; + eulers.x = loadSetting(settings, "rotation_x", 0.0f); + eulers.y = loadSetting(settings, "rotation_y", 0.0f); + eulers.z = loadSetting(settings, "rotation_z", 0.0f); + attachment.rotation = glm::quat(eulers); + attachment.scale = loadSetting(settings, "scale", 1.0f); + attachmentData.append(attachment); + } + settings->endArray(); + setAttachmentData(attachmentData); + setDisplayName(settings->value("displayName").toString()); settings->endGroup(); diff --git a/interface/src/ui/AttachmentsDialog.cpp b/interface/src/ui/AttachmentsDialog.cpp new file mode 100644 index 0000000000..319927c492 --- /dev/null +++ b/interface/src/ui/AttachmentsDialog.cpp @@ -0,0 +1,39 @@ +// +// MetavoxelEditor.cpp +// interface/src/ui +// +// Created by Andrzej Kapolka on 1/21/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 "Application.h" +#include "AttachmentsDialog.h" + +AttachmentsDialog::AttachmentsDialog() : + QDialog(Application::getInstance()->getWindow()) { + + setWindowTitle("Edit Attachments"); + setAttribute(Qt::WA_DeleteOnClose); + + QVBoxLayout* layout = new QVBoxLayout(); + setLayout(layout); + + QPushButton* newAttachment = new QPushButton("New Attachment"); + connect(newAttachment, SIGNAL(clicked(bool)), SLOT(addAttachment())); + layout->addWidget(newAttachment); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok); + layout->addWidget(buttons); + connect(buttons, SIGNAL(accepted()), SLOT(deleteLater())); +} + +void AttachmentsDialog::addAttachment() { + +} diff --git a/interface/src/ui/AttachmentsDialog.h b/interface/src/ui/AttachmentsDialog.h new file mode 100644 index 0000000000..f6dc3d25b4 --- /dev/null +++ b/interface/src/ui/AttachmentsDialog.h @@ -0,0 +1,30 @@ +// +// AttachmentsDialog.h +// interface/src/ui +// +// Created by Andrzej Kapolka on 5/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_AttachmentsDialog_h +#define hifi_AttachmentsDialog_h + +#include + +/// Allows users to edit the avatar attachments. +class AttachmentsDialog : public QDialog { + Q_OBJECT + +public: + + AttachmentsDialog(); + +private slots: + + void addAttachment(); +}; + +#endif // hifi_AttachmentsDialog_h diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index b57d5406d5..485b5517f0 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -599,8 +599,9 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray &packet) { QUuid avatarUUID; QUrl faceModelURL, skeletonModelURL; + QVector attachmentData; QString displayName; - packetStream >> avatarUUID >> faceModelURL >> skeletonModelURL >> displayName; + packetStream >> avatarUUID >> faceModelURL >> skeletonModelURL >> attachmentData >> displayName; bool hasIdentityChanged = false; @@ -618,7 +619,12 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray &packet) { setDisplayName(displayName); hasIdentityChanged = true; } - + + if (attachmentData != _attachmentData) { + setAttachmentData(attachmentData); + hasIdentityChanged = true; + } + return hasIdentityChanged; } @@ -626,7 +632,7 @@ QByteArray AvatarData::identityByteArray() { QByteArray identityData; QDataStream identityStream(&identityData, QIODevice::Append); - identityStream << QUuid() << _faceModelURL << _skeletonModelURL << _displayName; + identityStream << QUuid() << _faceModelURL << _skeletonModelURL << _attachmentData << _displayName; return identityData; } @@ -654,6 +660,12 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { updateJointMappings(); } +void AvatarData::setAttachmentData(const QVector& attachmentData) { + _attachmentData = attachmentData; + + qDebug() << "Changing attachment data for avatar."; +} + void AvatarData::setDisplayName(const QString& displayName) { _displayName = displayName; @@ -762,3 +774,19 @@ void AvatarData::updateJointMappings() { connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply())); } } + +bool AttachmentData::operator==(const AttachmentData& other) const { + return modelURL == other.modelURL && jointName == other.jointName && translation == other.translation && + rotation == other.rotation && scale == other.scale; +} + +QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment) { + return out << attachment.modelURL << attachment.jointName << + attachment.translation << attachment.rotation << attachment.scale; +} + +QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) { + return in >> attachment.modelURL >> attachment.jointName >> + attachment.translation >> attachment.rotation >> attachment.scale; +} + diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 78b33571e8..a853706005 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -45,6 +45,8 @@ typedef unsigned long long quint64; #include #include +#include + #include #include "HeadData.h" @@ -79,6 +81,7 @@ enum KeyState { const glm::vec3 vec3Zero(0.0f); +class QDataStream; class QNetworkAccessManager; class AttachmentData; @@ -211,9 +214,11 @@ public: const QUrl& getFaceModelURL() const { return _faceModelURL; } QString getFaceModelURLString() const { return _faceModelURL.toString(); } const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; } + const QVector& getAttachmentData() const { return _attachmentData; } const QString& getDisplayName() const { return _displayName; } virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); + virtual void setAttachmentData(const QVector& attachmentData); virtual void setDisplayName(const QString& displayName); virtual void setBillboard(const QByteArray& billboard); @@ -314,10 +319,15 @@ public: class AttachmentData { public: QUrl modelURL; - int jointIndex; + QString jointName; glm::vec3 translation; glm::quat rotation; float scale; + + bool operator==(const AttachmentData& other) const; }; +QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment); +QDataStream& operator>>(QDataStream& in, AttachmentData& attachment); + #endif // hifi_AvatarData_h diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp index d7b0c83c1f..5356c45a51 100644 --- a/libraries/shared/src/StreamUtils.cpp +++ b/libraries/shared/src/StreamUtils.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include "StreamUtils.h" @@ -47,6 +49,22 @@ std::ostream& operator<<(std::ostream& s, const glm::mat4& m) { return s; } +QDataStream& operator<<(QDataStream& out, const glm::vec3& vector) { + return out << vector.x << vector.y << vector.z; +} + +QDataStream& operator>>(QDataStream& in, glm::vec3& vector) { + return in >> vector.x >> vector.y >> vector.z; +} + +QDataStream& operator<<(QDataStream& out, const glm::quat& quaternion) { + return out << quaternion.x << quaternion.y << quaternion.z << quaternion.w; +} + +QDataStream& operator>>(QDataStream& in, glm::quat& quaternion) { + return in >> quaternion.x >> quaternion.y >> quaternion.z >> quaternion.w; +} + // less common utils can be enabled with DEBUG #ifdef DEBUG diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index 2546d49ffc..2a42a3ea7b 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -19,6 +19,7 @@ #include #include +class QDataStream; namespace StreamUtil { // dump the buffer, 32 bytes per row, each byte in hex, separated by whitespace @@ -29,6 +30,12 @@ std::ostream& operator<<(std::ostream& s, const glm::vec3& v); std::ostream& operator<<(std::ostream& s, const glm::quat& q); std::ostream& operator<<(std::ostream& s, const glm::mat4& m); +QDataStream& operator<<(QDataStream& out, const glm::vec3& vector); +QDataStream& operator>>(QDataStream& in, glm::vec3& vector); + +QDataStream& operator<<(QDataStream& out, const glm::quat& quaternion); +QDataStream& operator>>(QDataStream& in, glm::quat& quaternion); + // less common utils can be enabled with DEBUG #ifdef DEBUG #include "CollisionInfo.h" From e898b4a900a1841beeeb78bea6fb398aefd818c2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 4 May 2014 17:24:15 -0700 Subject: [PATCH 3/9] More attachment bits. --- interface/src/ui/AttachmentsDialog.cpp | 57 +++++++++++++++++++++++++- interface/src/ui/AttachmentsDialog.h | 34 ++++++++++++++- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/AttachmentsDialog.cpp b/interface/src/ui/AttachmentsDialog.cpp index 319927c492..34c1f251e1 100644 --- a/interface/src/ui/AttachmentsDialog.cpp +++ b/interface/src/ui/AttachmentsDialog.cpp @@ -9,7 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include +#include +#include +#include #include #include @@ -25,6 +29,10 @@ AttachmentsDialog::AttachmentsDialog() : QVBoxLayout* layout = new QVBoxLayout(); setLayout(layout); + foreach (const AttachmentData& data, Application::getInstance()->getAvatar()->getAttachmentData()) { + addAttachment(data); + } + QPushButton* newAttachment = new QPushButton("New Attachment"); connect(newAttachment, SIGNAL(clicked(bool)), SLOT(addAttachment())); layout->addWidget(newAttachment); @@ -34,6 +42,51 @@ AttachmentsDialog::AttachmentsDialog() : connect(buttons, SIGNAL(accepted()), SLOT(deleteLater())); } -void AttachmentsDialog::addAttachment() { - +void AttachmentsDialog::addAttachment(const AttachmentData& data) { + QVBoxLayout* layout = static_cast(this->layout()); + layout->insertWidget(layout->count() - 2, new AttachmentPanel(data)); +} + +AttachmentPanel::AttachmentPanel(const AttachmentData& data) { + QFormLayout* layout = new QFormLayout(); + setLayout(layout); + + QHBoxLayout* urlBox = new QHBoxLayout(); + layout->addRow("Model URL:", urlBox); + urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1); + QPushButton* chooseURL = new QPushButton("Choose"); + urlBox->addWidget(chooseURL); + connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL())); + + layout->addRow("Joint:", _jointName = new QComboBox()); + + QHBoxLayout* translationBox = new QHBoxLayout(); + translationBox->addWidget(_translationX = new QDoubleSpinBox()); + translationBox->addWidget(_translationY = new QDoubleSpinBox()); + translationBox->addWidget(_translationZ = new QDoubleSpinBox()); + layout->addRow("Translation:", translationBox); + + QHBoxLayout* rotationBox = new QHBoxLayout(); + rotationBox->addWidget(_rotationX = new QDoubleSpinBox()); + rotationBox->addWidget(_rotationY = new QDoubleSpinBox()); + rotationBox->addWidget(_rotationZ = new QDoubleSpinBox()); + layout->addRow("Rotation:", rotationBox); + + layout->addRow("Scale:", _scale = new QDoubleSpinBox()); + _scale->setSingleStep(0.01); + _scale->setMaximum(FLT_MAX); + + QPushButton* remove = new QPushButton("Delete"); + layout->addRow(remove); + connect(remove, SIGNAL(clicked(bool)), SLOT(deleteLater())); +} + +void AttachmentPanel::chooseModelURL() { + ModelsBrowser modelBrowser(ATTACHMENT_MODEL, this); + connect(&modelBrowser, SIGNAL(selected(QString)), SLOT(setModelURL(const QString&))); + modelBrowser.browse(); +} + +void AttachmentPanel::setModelURL(const QString& url) { + _modelURL->setText(url); } diff --git a/interface/src/ui/AttachmentsDialog.h b/interface/src/ui/AttachmentsDialog.h index f6dc3d25b4..e8c173f80a 100644 --- a/interface/src/ui/AttachmentsDialog.h +++ b/interface/src/ui/AttachmentsDialog.h @@ -14,6 +14,12 @@ #include +#include + +class QComboBox; +class QDoubleSpinner; +class QLineEdit; + /// Allows users to edit the avatar attachments. class AttachmentsDialog : public QDialog { Q_OBJECT @@ -24,7 +30,33 @@ public: private slots: - void addAttachment(); + void addAttachment(const AttachmentData& data = AttachmentData()); +}; + +/// A panel controlling a single attachment. +class AttachmentPanel : public QWidget { + Q_OBJECT + +public: + + AttachmentPanel(const AttachmentData& data = AttachmentData()); + +private slots: + + void chooseModelURL(); + void setModelURL(const QString& url); + +private: + + QLineEdit* _modelURL; + QComboBox* _jointName; + QDoubleSpinBox* _translationX; + QDoubleSpinBox* _translationY; + QDoubleSpinBox* _translationZ; + QDoubleSpinBox* _rotationX; + QDoubleSpinBox* _rotationY; + QDoubleSpinBox* _rotationZ; + QDoubleSpinBox* _scale; }; #endif // hifi_AttachmentsDialog_h From 65e34f9697defaef4e1ef854bcd9f1d19e2a453f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 5 May 2014 13:41:50 -0700 Subject: [PATCH 4/9] More work on attachment interface. --- interface/src/ui/AttachmentsDialog.cpp | 80 +++++++++++++++++++++++--- interface/src/ui/AttachmentsDialog.h | 13 ++++- libraries/avatars/src/AvatarData.cpp | 6 +- libraries/avatars/src/AvatarData.h | 2 + 4 files changed, 89 insertions(+), 12 deletions(-) diff --git a/interface/src/ui/AttachmentsDialog.cpp b/interface/src/ui/AttachmentsDialog.cpp index 34c1f251e1..38ef10625f 100644 --- a/interface/src/ui/AttachmentsDialog.cpp +++ b/interface/src/ui/AttachmentsDialog.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "Application.h" @@ -29,6 +30,14 @@ AttachmentsDialog::AttachmentsDialog() : QVBoxLayout* layout = new QVBoxLayout(); setLayout(layout); + QScrollArea* area = new QScrollArea(); + layout->addWidget(area); + area->setWidgetResizable(true); + QWidget* container = new QWidget(); + container->setLayout(_attachments = new QVBoxLayout()); + container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); + area->setWidget(container); + foreach (const AttachmentData& data, Application::getInstance()->getAvatar()->getAttachmentData()) { addAttachment(data); } @@ -40,47 +49,99 @@ AttachmentsDialog::AttachmentsDialog() : QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok); layout->addWidget(buttons); connect(buttons, SIGNAL(accepted()), SLOT(deleteLater())); + + setMinimumSize(600, 600); +} + +void AttachmentsDialog::updateAttachmentData() { + QVector data; + for (int i = 0; i < _attachments->count(); i++) { + data.append(static_cast(_attachments->itemAt(i)->widget())->getAttachmentData()); + } + Application::getInstance()->getAvatar()->setAttachmentData(data); } void AttachmentsDialog::addAttachment(const AttachmentData& data) { - QVBoxLayout* layout = static_cast(this->layout()); - layout->insertWidget(layout->count() - 2, new AttachmentPanel(data)); + _attachments->addWidget(new AttachmentPanel(this, data)); } -AttachmentPanel::AttachmentPanel(const AttachmentData& data) { +static QDoubleSpinBox* createTranslationBox(AttachmentsDialog* dialog, float value) { + QDoubleSpinBox* box = new QDoubleSpinBox(); + box->setSingleStep(0.01); + box->setMinimum(-FLT_MAX); + box->setMaximum(FLT_MAX); + box->setValue(value); + dialog->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); + return box; +} + +static QDoubleSpinBox* createRotationBox(AttachmentsDialog* dialog, float value) { + QDoubleSpinBox* box = new QDoubleSpinBox(); + box->setMinimum(-180.0); + box->setMaximum(180.0); + box->setWrapping(true); + box->setValue(value); + dialog->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); + return box; +} + +AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data) { QFormLayout* layout = new QFormLayout(); setLayout(layout); QHBoxLayout* urlBox = new QHBoxLayout(); layout->addRow("Model URL:", urlBox); urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1); + _modelURL->setText(data.modelURL.toString()); + dialog->connect(_modelURL, SIGNAL(returnPressed()), SLOT(updateAttachmentData())); QPushButton* chooseURL = new QPushButton("Choose"); urlBox->addWidget(chooseURL); connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL())); layout->addRow("Joint:", _jointName = new QComboBox()); + QSharedPointer geometry = Application::getInstance()->getAvatar()->getSkeletonModel().getGeometry(); + if (geometry && geometry->isLoaded()) { + foreach (const FBXJoint& joint, geometry->getFBXGeometry().joints) { + _jointName->addItem(joint.name); + } + } + _jointName->setCurrentText(data.jointName); + dialog->connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(updateAttachmentData())); QHBoxLayout* translationBox = new QHBoxLayout(); - translationBox->addWidget(_translationX = new QDoubleSpinBox()); - translationBox->addWidget(_translationY = new QDoubleSpinBox()); - translationBox->addWidget(_translationZ = new QDoubleSpinBox()); + translationBox->addWidget(_translationX = createTranslationBox(dialog, data.translation.x)); + translationBox->addWidget(_translationY = createTranslationBox(dialog, data.translation.y)); + translationBox->addWidget(_translationZ = createTranslationBox(dialog, data.translation.z)); layout->addRow("Translation:", translationBox); QHBoxLayout* rotationBox = new QHBoxLayout(); - rotationBox->addWidget(_rotationX = new QDoubleSpinBox()); - rotationBox->addWidget(_rotationY = new QDoubleSpinBox()); - rotationBox->addWidget(_rotationZ = new QDoubleSpinBox()); + glm::vec3 eulers = glm::degrees(safeEulerAngles(data.rotation)); + rotationBox->addWidget(_rotationX = createRotationBox(dialog, eulers.x)); + rotationBox->addWidget(_rotationY = createRotationBox(dialog, eulers.y)); + rotationBox->addWidget(_rotationZ = createRotationBox(dialog, eulers.z)); layout->addRow("Rotation:", rotationBox); layout->addRow("Scale:", _scale = new QDoubleSpinBox()); _scale->setSingleStep(0.01); _scale->setMaximum(FLT_MAX); + _scale->setValue(data.scale); + dialog->connect(_scale, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); QPushButton* remove = new QPushButton("Delete"); layout->addRow(remove); connect(remove, SIGNAL(clicked(bool)), SLOT(deleteLater())); } +AttachmentData AttachmentPanel::getAttachmentData() const { + AttachmentData data; + data.modelURL = _modelURL->text(); + data.jointName = _jointName->currentText(); + data.translation = glm::vec3(_translationX->value(), _translationY->value(), _translationZ->value()); + data.rotation = glm::quat(glm::radians(glm::vec3(_rotationX->value(), _rotationY->value(), _rotationZ->value()))); + data.scale = _scale->value(); + return data; +} + void AttachmentPanel::chooseModelURL() { ModelsBrowser modelBrowser(ATTACHMENT_MODEL, this); connect(&modelBrowser, SIGNAL(selected(QString)), SLOT(setModelURL(const QString&))); @@ -89,4 +150,5 @@ void AttachmentPanel::chooseModelURL() { void AttachmentPanel::setModelURL(const QString& url) { _modelURL->setText(url); + emit _modelURL->returnPressed(); } diff --git a/interface/src/ui/AttachmentsDialog.h b/interface/src/ui/AttachmentsDialog.h index e8c173f80a..c23bd2efb8 100644 --- a/interface/src/ui/AttachmentsDialog.h +++ b/interface/src/ui/AttachmentsDialog.h @@ -19,6 +19,7 @@ class QComboBox; class QDoubleSpinner; class QLineEdit; +class QVBoxLayout; /// Allows users to edit the avatar attachments. class AttachmentsDialog : public QDialog { @@ -28,9 +29,17 @@ public: AttachmentsDialog(); +public slots: + + void updateAttachmentData(); + private slots: void addAttachment(const AttachmentData& data = AttachmentData()); + +private: + + QVBoxLayout* _attachments; }; /// A panel controlling a single attachment. @@ -39,7 +48,9 @@ class AttachmentPanel : public QWidget { public: - AttachmentPanel(const AttachmentData& data = AttachmentData()); + AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data = AttachmentData()); + + AttachmentData getAttachmentData() const; private slots: diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 485b5517f0..bbfff8f025 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -662,8 +662,6 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { void AvatarData::setAttachmentData(const QVector& attachmentData) { _attachmentData = attachmentData; - - qDebug() << "Changing attachment data for avatar."; } void AvatarData::setDisplayName(const QString& displayName) { @@ -775,6 +773,10 @@ void AvatarData::updateJointMappings() { } } +AttachmentData::AttachmentData() : + scale(1.0f) { +} + bool AttachmentData::operator==(const AttachmentData& other) const { return modelURL == other.modelURL && jointName == other.jointName && translation == other.translation && rotation == other.rotation && scale == other.scale; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a853706005..97fad639cd 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -324,6 +324,8 @@ public: glm::quat rotation; float scale; + AttachmentData(); + bool operator==(const AttachmentData& other) const; }; From f37460e39a67b4dd6d5ef9b9084fb57d837975b9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 5 May 2014 15:46:09 -0700 Subject: [PATCH 5/9] Basic attachment rendering. --- interface/src/avatar/Avatar.cpp | 47 +++++++++++++++++++++++++++++++ interface/src/avatar/Avatar.h | 5 ++++ interface/src/avatar/MyAvatar.cpp | 4 ++- interface/src/renderer/Model.cpp | 36 +++++++++++------------ interface/src/renderer/Model.h | 6 ++-- 5 files changed, 76 insertions(+), 22 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e8ac93234c..3f670709ff 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -126,6 +126,7 @@ void Avatar::simulate(float deltaTime) { _skeletonModel.simulate(deltaTime); } _skeletonModel.simulate(deltaTime, _hasNewJointRotations); + simulateAttachments(deltaTime, _hasNewJointRotations); _hasNewJointRotations = false; glm::vec3 headPosition = _position; @@ -338,6 +339,7 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) { return; } _skeletonModel.render(1.0f, modelRenderMode); + renderAttachments(modelRenderMode); getHand()->render(false); } getHead()->render(1.0f, modelRenderMode); @@ -347,6 +349,32 @@ bool Avatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode render return true; } +void Avatar::simulateAttachments(float deltaTime, bool fullUpdate) { + if (!fullUpdate) { + return; // only simulate if we have new data + } + for (int i = 0; i < _attachmentModels.size(); i++) { + const AttachmentData& attachment = _attachmentData.at(i); + Model* model = _attachmentModels.at(i); + int jointIndex = getJointIndex(attachment.jointName); + glm::vec3 jointPosition; + glm::quat jointRotation; + if (_skeletonModel.getJointPosition(jointIndex, jointPosition) && + _skeletonModel.getJointRotation(jointIndex, jointRotation)) { + model->setTranslation(jointPosition + jointRotation * attachment.translation * _skeletonModel.getScale()); + model->setRotation(jointRotation * attachment.rotation); + model->setScale(_skeletonModel.getScale() * attachment.scale); + model->simulate(deltaTime); + } + } +} + +void Avatar::renderAttachments(Model::RenderMode renderMode) { + foreach (Model* model, _attachmentModels) { + model->render(1.0f, renderMode); + } +} + void Avatar::updateJointMappings() { // no-op; joint mappings come from skeleton model } @@ -667,6 +695,25 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar()); } +void Avatar::setAttachmentData(const QVector& attachmentData) { + AvatarData::setAttachmentData(attachmentData); + + // make sure we have as many models as attachments + while (_attachmentModels.size() < attachmentData.size()) { + Model* model = new Model(this); + model->init(); + _attachmentModels.append(model); + } + while (_attachmentModels.size() > attachmentData.size()) { + delete _attachmentModels.takeLast(); + } + + // update the urls + for (int i = 0; i < attachmentData.size(); i++) { + _attachmentModels[i]->setURL(attachmentData.at(i).modelURL); + } +} + void Avatar::setDisplayName(const QString& displayName) { AvatarData::setDisplayName(displayName); _displayNameBoundingRect = textRenderer(DISPLAYNAME)->metrics().tightBoundingRect(displayName); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 4263e606a5..4b0bf85ea1 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -131,6 +131,7 @@ public: virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); + virtual void setAttachmentData(const QVector& attachmentData); virtual void setDisplayName(const QString& displayName); virtual void setBillboard(const QByteArray& billboard); @@ -160,6 +161,7 @@ signals: protected: SkeletonModel _skeletonModel; + QVector _attachmentModels; float _bodyYawDelta; glm::vec3 _velocity; float _leanScale; @@ -188,6 +190,9 @@ protected: virtual void renderBody(RenderMode renderMode, float glowLevel = 0.0f); virtual bool shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const; + void simulateAttachments(float deltaTime, bool fullUpdate = true); + void renderAttachments(Model::RenderMode renderMode); + virtual void updateJointMappings(); private: diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6af7235117..b52b77f691 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -244,6 +244,7 @@ void MyAvatar::simulate(float deltaTime) { getHand()->simulate(deltaTime, true); _skeletonModel.simulate(deltaTime); + simulateAttachments(deltaTime); // copy out the skeleton joints from the model _jointData.resize(_skeletonModel.getJointStateCount()); @@ -657,7 +658,8 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; _skeletonModel.render(1.0f, modelRenderMode); - + renderAttachments(modelRenderMode); + // Render head so long as the camera isn't inside it if (shouldRenderHead(Application::getInstance()->getCamera()->getPosition(), renderMode)) { getHead()->render(1.0f, modelRenderMode); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index a177783955..db86b37d0d 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -509,6 +509,24 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo } } +bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return false; + } + position = _translation + extractTranslation(_jointStates[jointIndex].transform); + return true; +} + +bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind) const { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return false; + } + rotation = _jointStates[jointIndex].combinedRotation * + (fromBind ? _geometry->getFBXGeometry().joints[jointIndex].inverseBindRotation : + _geometry->getFBXGeometry().joints[jointIndex].inverseDefaultRotation); + return true; +} + void Model::clearShapes() { for (int i = 0; i < _jointShapes.size(); ++i) { delete _jointShapes[i]; @@ -957,24 +975,6 @@ void Model::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint // nothing by default } -bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } - position = _translation + extractTranslation(_jointStates[jointIndex].transform); - return true; -} - -bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind) const { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } - rotation = _jointStates[jointIndex].combinedRotation * - (fromBind ? _geometry->getFBXGeometry().joints[jointIndex].inverseBindRotation : - _geometry->getFBXGeometry().joints[jointIndex].inverseDefaultRotation); - return true; -} - bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation, bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment) { if (jointIndex == -1 || _jointStates.isEmpty()) { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index ae2bcd79b8..6a79772ca7 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -180,6 +180,9 @@ public: /// Returns the extended length from the right hand to its first free ancestor. float getRightArmLength() const; + bool getJointPosition(int jointIndex, glm::vec3& position) const; + bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const; + void clearShapes(); void rebuildShapes(); void updateShapePositions(); @@ -269,9 +272,6 @@ protected: virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); virtual void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); - bool getJointPosition(int jointIndex, glm::vec3& position) const; - bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const; - bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(), bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f)); From fd0a39df12b4d00d09b163027aa1ac824ebd043b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 5 May 2014 16:19:33 -0700 Subject: [PATCH 6/9] Always update attachments. --- interface/src/avatar/Avatar.cpp | 7 ++----- interface/src/avatar/Avatar.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 3f670709ff..e3700b920b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -126,7 +126,7 @@ void Avatar::simulate(float deltaTime) { _skeletonModel.simulate(deltaTime); } _skeletonModel.simulate(deltaTime, _hasNewJointRotations); - simulateAttachments(deltaTime, _hasNewJointRotations); + simulateAttachments(deltaTime); _hasNewJointRotations = false; glm::vec3 headPosition = _position; @@ -349,10 +349,7 @@ bool Avatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode render return true; } -void Avatar::simulateAttachments(float deltaTime, bool fullUpdate) { - if (!fullUpdate) { - return; // only simulate if we have new data - } +void Avatar::simulateAttachments(float deltaTime) { for (int i = 0; i < _attachmentModels.size(); i++) { const AttachmentData& attachment = _attachmentData.at(i); Model* model = _attachmentModels.at(i); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 4b0bf85ea1..9828e120c1 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -190,7 +190,7 @@ protected: virtual void renderBody(RenderMode renderMode, float glowLevel = 0.0f); virtual bool shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const; - void simulateAttachments(float deltaTime, bool fullUpdate = true); + void simulateAttachments(float deltaTime); void renderAttachments(Model::RenderMode renderMode); virtual void updateJointMappings(); From 0021c77789480a862e7ba13136f62d93005cf744 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 5 May 2014 16:29:11 -0700 Subject: [PATCH 7/9] Missed a spot for decoding avatar identity data. --- libraries/avatars/src/AvatarHashMap.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 6b17a3fab8..8e3797cbc0 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -127,8 +127,9 @@ void AvatarHashMap::processAvatarIdentityPacket(const QByteArray &packet, const while (!identityStream.atEnd()) { QUrl faceMeshURL, skeletonURL; + QVector attachmentData; QString displayName; - identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> displayName; + identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> attachmentData >> displayName; // mesh URL for a UUID, find avatar in our list AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(sessionUUID, mixerWeakPointer); @@ -142,6 +143,10 @@ void AvatarHashMap::processAvatarIdentityPacket(const QByteArray &packet, const matchingAvatar->setSkeletonModelURL(skeletonURL); } + if (matchingAvatar->getAttachmentData() != attachmentData) { + matchingAvatar->setAttachmentData(attachmentData); + } + if (matchingAvatar->getDisplayName() != displayName) { matchingAvatar->setDisplayName(displayName); } @@ -171,4 +176,4 @@ void AvatarHashMap::processKillAvatar(const QByteArray& datagram) { if (matchedAvatar != _avatarHash.end()) { erase(matchedAvatar); } -} \ No newline at end of file +} From 710cc3ec2a25dac7fd57306c469399ee6dfc52fd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 5 May 2014 17:08:10 -0700 Subject: [PATCH 8/9] Comment fix. --- interface/src/ui/AttachmentsDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/AttachmentsDialog.cpp b/interface/src/ui/AttachmentsDialog.cpp index 38ef10625f..edd28d461c 100644 --- a/interface/src/ui/AttachmentsDialog.cpp +++ b/interface/src/ui/AttachmentsDialog.cpp @@ -1,8 +1,8 @@ // -// MetavoxelEditor.cpp +// AttachmentsDialog.cpp // interface/src/ui // -// Created by Andrzej Kapolka on 1/21/14. +// Created by Andrzej Kapolka on 5/4/14. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. From e33ef1a5712953cfc131f2d7e47d9d969faf0731 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 May 2014 17:35:23 -0700 Subject: [PATCH 9/9] fix avatar motor to point in direction of camera --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cd799b7d2e..3fac129bec 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -732,7 +732,7 @@ void MyAvatar::applyMotor(float deltaTime) { glm::vec3 targetVelocity = _motorVelocity; if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) { // rotate _motorVelocity into world frame - glm::quat rotation = getOrientation(); + glm::quat rotation = getHead()->getCameraOrientation(); targetVelocity = rotation * _motorVelocity; }