mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 08:04:01 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into 19597
This commit is contained in:
commit
aedc7eda3e
22 changed files with 513 additions and 85 deletions
|
@ -63,11 +63,11 @@
|
|||
#include <UUID.h>
|
||||
#include <OctreeSceneStats.h>
|
||||
#include <LocalVoxelsList.h>
|
||||
#include <ModelUploader.h>
|
||||
|
||||
#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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
@ -157,6 +158,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()));
|
||||
|
@ -187,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,
|
||||
|
@ -835,6 +840,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
|
||||
|
|
|
@ -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> _preferencesDialog;
|
||||
QPointer<AttachmentsDialog> _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";
|
||||
|
@ -362,6 +366,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";
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include <FBXReader.h>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -126,6 +126,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
_skeletonModel.simulate(deltaTime);
|
||||
}
|
||||
_skeletonModel.simulate(deltaTime, _hasNewJointRotations);
|
||||
simulateAttachments(deltaTime);
|
||||
_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,29 @@ bool Avatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode render
|
|||
return true;
|
||||
}
|
||||
|
||||
void Avatar::simulateAttachments(float deltaTime) {
|
||||
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 +692,25 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
|
||||
}
|
||||
|
||||
void Avatar::setAttachmentData(const QVector<AttachmentData>& 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);
|
||||
|
|
|
@ -131,6 +131,7 @@ public:
|
|||
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||
virtual void setDisplayName(const QString& displayName);
|
||||
virtual void setBillboard(const QByteArray& billboard);
|
||||
|
||||
|
@ -160,6 +161,7 @@ signals:
|
|||
|
||||
protected:
|
||||
SkeletonModel _skeletonModel;
|
||||
QVector<Model*> _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);
|
||||
void renderAttachments(Model::RenderMode renderMode);
|
||||
|
||||
virtual void updateJointMappings();
|
||||
|
||||
private:
|
||||
|
|
|
@ -178,6 +178,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());
|
||||
|
@ -422,6 +423,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();
|
||||
|
@ -450,6 +469,28 @@ void MyAvatar::loadData(QSettings* settings) {
|
|||
|
||||
setFaceModelURL(settings->value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl());
|
||||
setSkeletonModelURL(settings->value("skeletonModelURL").toUrl());
|
||||
|
||||
QVector<AttachmentData> 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();
|
||||
|
@ -548,7 +589,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);
|
||||
|
@ -732,7 +774,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
@ -949,24 +967,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()) {
|
||||
|
|
|
@ -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));
|
||||
|
|
154
interface/src/ui/AttachmentsDialog.cpp
Normal file
154
interface/src/ui/AttachmentsDialog.cpp
Normal file
|
@ -0,0 +1,154 @@
|
|||
//
|
||||
// AttachmentsDialog.cpp
|
||||
// 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
|
||||
//
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFormLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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()));
|
||||
|
||||
setMinimumSize(600, 600);
|
||||
}
|
||||
|
||||
void AttachmentsDialog::updateAttachmentData() {
|
||||
QVector<AttachmentData> data;
|
||||
for (int i = 0; i < _attachments->count(); i++) {
|
||||
data.append(static_cast<AttachmentPanel*>(_attachments->itemAt(i)->widget())->getAttachmentData());
|
||||
}
|
||||
Application::getInstance()->getAvatar()->setAttachmentData(data);
|
||||
}
|
||||
|
||||
void AttachmentsDialog::addAttachment(const AttachmentData& data) {
|
||||
_attachments->addWidget(new AttachmentPanel(this, 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<NetworkGeometry> 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 = 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();
|
||||
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&)));
|
||||
modelBrowser.browse();
|
||||
}
|
||||
|
||||
void AttachmentPanel::setModelURL(const QString& url) {
|
||||
_modelURL->setText(url);
|
||||
emit _modelURL->returnPressed();
|
||||
}
|
73
interface/src/ui/AttachmentsDialog.h
Normal file
73
interface/src/ui/AttachmentsDialog.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// 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 <QDialog>
|
||||
|
||||
#include <AvatarData.h>
|
||||
|
||||
class QComboBox;
|
||||
class QDoubleSpinner;
|
||||
class QLineEdit;
|
||||
class QVBoxLayout;
|
||||
|
||||
/// Allows users to edit the avatar attachments.
|
||||
class AttachmentsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AttachmentsDialog();
|
||||
|
||||
public slots:
|
||||
|
||||
void updateAttachmentData();
|
||||
|
||||
private slots:
|
||||
|
||||
void addAttachment(const AttachmentData& data = AttachmentData());
|
||||
|
||||
private:
|
||||
|
||||
QVBoxLayout* _attachments;
|
||||
};
|
||||
|
||||
/// A panel controlling a single attachment.
|
||||
class AttachmentPanel : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data = AttachmentData());
|
||||
|
||||
AttachmentData getAttachmentData() const;
|
||||
|
||||
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
|
|
@ -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);
|
||||
|
|
|
@ -16,12 +16,14 @@
|
|||
#include <QStandardItemModel>
|
||||
#include <QTreeView>
|
||||
|
||||
|
||||
enum ModelType {
|
||||
Head,
|
||||
Skeleton
|
||||
HEAD_MODEL,
|
||||
SKELETON_MODEL,
|
||||
ATTACHMENT_MODEL
|
||||
};
|
||||
|
||||
extern const char* MODEL_TYPE_NAMES[];
|
||||
|
||||
class ModelHandler : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -599,8 +599,9 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray &packet) {
|
|||
|
||||
QUuid avatarUUID;
|
||||
QUrl faceModelURL, skeletonModelURL;
|
||||
QVector<AttachmentData> 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,10 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
updateJointMappings();
|
||||
}
|
||||
|
||||
void AvatarData::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||
_attachmentData = attachmentData;
|
||||
}
|
||||
|
||||
void AvatarData::setDisplayName(const QString& displayName) {
|
||||
_displayName = displayName;
|
||||
|
||||
|
@ -762,3 +772,23 @@ void AvatarData::updateJointMappings() {
|
|||
connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply()));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@ typedef unsigned long long quint64;
|
|||
|
||||
#include <CollisionInfo.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <StreamUtils.h>
|
||||
|
||||
#include <Node.h>
|
||||
|
||||
#include "HeadData.h"
|
||||
|
@ -95,8 +97,10 @@ enum KeyState {
|
|||
|
||||
const glm::vec3 vec3Zero(0.0f);
|
||||
|
||||
class QDataStream;
|
||||
class QNetworkAccessManager;
|
||||
|
||||
class AttachmentData;
|
||||
class JointData;
|
||||
|
||||
class AvatarData : public QObject {
|
||||
|
@ -226,9 +230,11 @@ public:
|
|||
const QUrl& getFaceModelURL() const { return _faceModelURL; }
|
||||
QString getFaceModelURLString() const { return _faceModelURL.toString(); }
|
||||
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
|
||||
const QVector<AttachmentData>& 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>& attachmentData);
|
||||
virtual void setDisplayName(const QString& displayName);
|
||||
|
||||
virtual void setBillboard(const QByteArray& billboard);
|
||||
|
@ -291,6 +297,7 @@ protected:
|
|||
|
||||
QUrl _faceModelURL;
|
||||
QUrl _skeletonModelURL;
|
||||
QVector<AttachmentData> _attachmentData;
|
||||
QString _displayName;
|
||||
|
||||
QRect _displayNameBoundingRect;
|
||||
|
@ -325,4 +332,20 @@ public:
|
|||
glm::quat rotation;
|
||||
};
|
||||
|
||||
class AttachmentData {
|
||||
public:
|
||||
QUrl modelURL;
|
||||
QString jointName;
|
||||
glm::vec3 translation;
|
||||
glm::quat rotation;
|
||||
float scale;
|
||||
|
||||
AttachmentData();
|
||||
|
||||
bool operator==(const AttachmentData& other) const;
|
||||
};
|
||||
|
||||
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment);
|
||||
QDataStream& operator>>(QDataStream& in, AttachmentData& attachment);
|
||||
|
||||
#endif // hifi_AvatarData_h
|
||||
|
|
|
@ -127,8 +127,9 @@ void AvatarHashMap::processAvatarIdentityPacket(const QByteArray &packet, const
|
|||
while (!identityStream.atEnd()) {
|
||||
|
||||
QUrl faceMeshURL, skeletonURL;
|
||||
QVector<AttachmentData> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
switch (type) {
|
||||
case PacketTypeAvatarData:
|
||||
return 3;
|
||||
case PacketTypeAvatarIdentity:
|
||||
return 1;
|
||||
case PacketTypeEnvironmentData:
|
||||
return 1;
|
||||
case PacketTypeParticleData:
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QDataStream>
|
||||
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#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
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
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"
|
||||
|
|
Loading…
Reference in a new issue