From 30b77d420022334a5031c544fb9d1963bd79e3ad Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 3 Mar 2015 22:07:12 -0800 Subject: [PATCH 1/3] Don't crash if avatar model at start-up or loaded is skeletonless --- interface/src/avatar/MyAvatar.cpp | 6 ++++++ interface/src/avatar/SkeletonModel.cpp | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9c83942439..9213d7416f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -195,6 +195,12 @@ void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("skeleton"); _skeletonModel.simulate(deltaTime); } + + if (!_skeletonModel.hasSkeleton()) { + // All the simulation that can be done has been done + return; + } + { PerformanceTimer perfTimer("attachments"); simulateAttachments(deltaTime); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index fa9846fd7d..03baa334e5 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -721,7 +721,8 @@ void SkeletonModel::buildShapes() { } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (geometry.joints.isEmpty()) { + if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) { + // rootJointIndex == -1 if the avatar model has no skeleton return; } From aad3f1dfd9c71fb8feb3bb0fd7f5e2a451d97578 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 3 Mar 2015 22:07:45 -0800 Subject: [PATCH 2/3] Automatically switch to default model if avatar model is skeletonless --- interface/src/Application.cpp | 19 +++++++++++++++++++ interface/src/Application.h | 2 ++ interface/src/avatar/MyAvatar.cpp | 1 - interface/src/avatar/SkeletonModel.cpp | 5 +++++ interface/src/avatar/SkeletonModel.h | 8 +++++++- 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6484e364bc..f1d9dcbef2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -490,6 +490,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(nodeList.data(), SIGNAL(dataReceived(const quint8, const int)), bandwidthRecorder.data(), SLOT(updateInboundData(const quint8, const int))); + connect(&_myAvatar->getSkeletonModel(), &SkeletonModel::skeletonLoaded, + this, &Application::checkSkeleton, Qt::QueuedConnection); + // check first run... if (_firstRun.get()) { qDebug() << "This is a first run..."; @@ -4006,3 +4009,19 @@ void Application::notifyPacketVersionMismatch() { msgBox.exec(); } } + +void Application::checkSkeleton() { + if (_myAvatar->getSkeletonModel().isActive() && !_myAvatar->getSkeletonModel().hasSkeleton()) { + qDebug() << "MyAvatar model has no skeleton"; + + QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded..."; + QMessageBox msgBox; + msgBox.setText(message); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + + _myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL); + _myAvatar->sendIdentityPacket(); + } +} diff --git a/interface/src/Application.h b/interface/src/Application.h index 91a5f7547b..81025245fa 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -584,6 +584,8 @@ private: QTimer _settingsTimer; GLCanvas* _glWidget = new GLCanvas(); // our GLCanvas has a couple extra features + + void checkSkeleton(); }; #endif // hifi_Application_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9213d7416f..c2a957dc72 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 03baa334e5..fbb622b30a 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -81,6 +81,8 @@ void SkeletonModel::setJointStates(QVector states) { if (_enableShapes) { buildShapes(); } + + emit skeletonLoaded(); } const float PALM_PRIORITY = DEFAULT_PRIORITY; @@ -1007,3 +1009,6 @@ void SkeletonModel::renderJointCollisionShapes(float alpha) { glPopMatrix(); } +bool SkeletonModel::hasSkeleton() { + return isActive() ? _geometry->getFBXGeometry().rootJointIndex != -1 : false; +} diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index d28d1a8aef..74d0ed0324 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -123,7 +123,13 @@ public: void resetShapePositionsToDefaultPose(); // DEBUG method void renderRagdoll(); - + + bool hasSkeleton(); + +signals: + + void skeletonLoaded(); + protected: void buildShapes(); From 8686e090c9fd32a3aeec629fe7677017012a4989 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 3 Mar 2015 22:30:45 -0800 Subject: [PATCH 3/3] Don't upload skeletonless avatar model to HiFi servers --- interface/src/ModelUploader.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index ad73d119a3..32973b001d 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -177,6 +177,22 @@ bool ModelUploader::zip() { } QByteArray fbxContents = fbx.readAll(); FBXGeometry geometry = readFBX(fbxContents, QVariantHash()); + + // Make sure that a skeleton model has a skeleton + if (_modelType == SKELETON_MODEL) { + if (geometry.rootJointIndex == -1) { + + QString message = "Your selected skeleton model has no skeleton.\n\nThe upload will be canceled."; + QMessageBox msgBox; + msgBox.setWindowTitle("Model Upload"); + msgBox.setText(message); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + + return false; + } + } // make sure we have some basic mappings populateBasicMapping(mapping, filename, geometry);