Provide a means to specify a fallback for when we fail to load a model. Also

added missing associated resources for default model.  Closes #1960.
This commit is contained in:
Andrzej Kapolka 2014-02-10 18:50:36 -08:00
parent bbb8e2a45f
commit 2071da2c8a
10 changed files with 134 additions and 17 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

View file

@ -0,0 +1,18 @@
scale=130
joint = jointRoot = jointRoot
joint = jointLean = jointSpine
joint = jointNeck = jointNeck
joint = jointHead = jointHeadtop
joint = joint_L_shoulder = joint_L_shoulder
freeJoint = joint_L_arm
freeJoint = joint_L_elbow
joint = jointLeftHand = joint_L_hand
joint = joint_R_shoulder = joint_R_shoulder
freeJoint = joint_R_arm
freeJoint = joint_R_elbow
joint = jointRightHand = joint_R_hand

View file

@ -0,0 +1,45 @@
# faceshift target mapping file
name= defaultAvatar_head
filename=../../../Avatars/Jelly/jellyrob_blue.fbx
texdir=../../../Avatars/Jelly
scale=80
rx=0
ry=0
rz=0
tx=0
ty=0
tz=0
joint = jointNeck = jointNeck
bs = BrowsD_L = Leye1.BrowsD_L = 0.97
bs = BrowsD_R = Reye1.BrowsD_R = 1
bs = CheekSquint_L = Leye1.CheekSquint_L = 1
bs = CheekSquint_R = Reye1.CheekSquint_R = 1
bs = EyeBlink_L = Leye1.EyeBlink_L = 1
bs = EyeBlink_R = Reye1.EyeBlink_R = 1
bs = EyeDown_L = Leye1.EyeDown_L = 1
bs = EyeDown_R = Reye1.EyeDown_R = 0.99
bs = EyeIn_L = Leye1.EyeIn_L = 0.92
bs = EyeIn_R = Reye1.EyeIn_R = 1
bs = EyeOpen_L = Leye1.EyeOpen_L = 1
bs = EyeOpen_R = Reye1.EyeOpen_R = 1
bs = EyeOut_L = Leye1.EyeOut_L = 0.99
bs = EyeOut_R = Reye1.EyeOut_R = 1
bs = EyeUp_L = Leye1.EyeUp_L = 0.93
bs = EyeUp_R = Reye1.EyeUp_R = 1
bs = JawOpen = Mouth.JawOpen = 1
bs = LipsFunnel = Mouth.LipsFunnel = 1
bs = LipsLowerDown = Mouth.LipsLowerDown = 1
bs = LipsPucker = Mouth.LipsPucker = 1
bs = LipsStretch_L = Mouth.LipsStretch_L = 0.96
bs = LipsStretch_R = Mouth.LipsStretch_R = 1
bs = LipsUpperUp = Mouth.LipsUpperUp = 1
bs = MouthDimple_L = Mouth.MouthDimple_L = 1
bs = MouthDimple_R = Mouth.MouthDimple_R = 1
bs = MouthFrown_L = Mouth.MouthFrown_L = 1
bs = MouthFrown_R = Mouth.MouthFrown_R = 1
bs = MouthLeft = Mouth.MouthLeft = 1
bs = MouthRight = Mouth.MouthRight = 1
bs = MouthSmile_L = Mouth.MouthSmile_L = 1
bs = MouthSmile_R = Mouth.MouthSmile_R = 1
bs = Puff = Mouth.Puff = 1
bs = Sneer = Mouth.Sneer = 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -338,12 +338,14 @@ bool Avatar::findSphereCollision(const glm::vec3& sphereCenter, float sphereRadi
void Avatar::setFaceModelURL(const QUrl &faceModelURL) { void Avatar::setFaceModelURL(const QUrl &faceModelURL) {
AvatarData::setFaceModelURL(faceModelURL); AvatarData::setFaceModelURL(faceModelURL);
_head.getFaceModel().setURL(_faceModelURL); const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_head.fbx");
_head.getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL);
} }
void Avatar::setSkeletonModelURL(const QUrl &skeletonModelURL) { void Avatar::setSkeletonModelURL(const QUrl &skeletonModelURL) {
AvatarData::setSkeletonModelURL(skeletonModelURL); AvatarData::setSkeletonModelURL(skeletonModelURL);
_skeletonModel.setURL(_skeletonModelURL); const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_body.fbx");
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL);
} }
int Avatar::parseData(const QByteArray& packet) { int Avatar::parseData(const QByteArray& packet) {

View file

@ -290,19 +290,24 @@ void GeometryCache::renderGrid(int xDivisions, int yDivisions) {
buffer.release(); buffer.release();
} }
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url) { QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback) {
if (!url.isValid() && fallback.isValid()) {
return getGeometry(fallback);
}
QSharedPointer<NetworkGeometry> geometry = _networkGeometry.value(url); QSharedPointer<NetworkGeometry> geometry = _networkGeometry.value(url);
if (geometry.isNull()) { if (geometry.isNull()) {
geometry = QSharedPointer<NetworkGeometry>(new NetworkGeometry(url)); geometry = QSharedPointer<NetworkGeometry>(new NetworkGeometry(url, fallback.isValid() ?
getGeometry(fallback) : QSharedPointer<NetworkGeometry>()));
_networkGeometry.insert(url, geometry); _networkGeometry.insert(url, geometry);
} }
return geometry; return geometry;
} }
NetworkGeometry::NetworkGeometry(const QUrl& url) : NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback) :
_modelRequest(url), _modelRequest(url),
_modelReply(NULL), _modelReply(NULL),
_mappingReply(NULL), _mappingReply(NULL),
_fallback(fallback),
_attempts(0) _attempts(0)
{ {
if (!url.isValid()) { if (!url.isValid()) {
@ -369,18 +374,37 @@ void NetworkGeometry::makeModelRequest() {
void NetworkGeometry::handleModelReplyError() { void NetworkGeometry::handleModelReplyError() {
QDebug debug = qDebug() << _modelReply->errorString(); QDebug debug = qDebug() << _modelReply->errorString();
QNetworkReply::NetworkError error = _modelReply->error();
_modelReply->disconnect(this); _modelReply->disconnect(this);
_modelReply->deleteLater(); _modelReply->deleteLater();
_modelReply = NULL; _modelReply = NULL;
// retry for certain types of failures
switch (error) {
case QNetworkReply::RemoteHostClosedError:
case QNetworkReply::TimeoutError:
case QNetworkReply::TemporaryNetworkFailureError:
case QNetworkReply::ProxyConnectionClosedError:
case QNetworkReply::ProxyTimeoutError:
case QNetworkReply::UnknownNetworkError:
case QNetworkReply::UnknownProxyError:
case QNetworkReply::UnknownContentError:
case QNetworkReply::ProtocolFailure: {
// retry with increasing delays // retry with increasing delays
const int MAX_ATTEMPTS = 8; const int MAX_ATTEMPTS = 8;
const int BASE_DELAY_MS = 1000; const int BASE_DELAY_MS = 1000;
if (++_attempts < MAX_ATTEMPTS) { if (++_attempts < MAX_ATTEMPTS) {
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeModelRequest())); QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeModelRequest()));
debug << " -- retrying..."; debug << " -- retrying...";
return;
} }
// fall through to final failure
}
default:
maybeLoadFallback();
break;
}
} }
void NetworkGeometry::handleMappingReplyError() { void NetworkGeometry::handleMappingReplyError() {
@ -415,6 +439,7 @@ void NetworkGeometry::maybeReadModelWithMapping() {
} catch (const QString& error) { } catch (const QString& error) {
qDebug() << "Error reading " << url << ": " << error; qDebug() << "Error reading " << url << ": " << error;
maybeLoadFallback();
return; return;
} }
@ -507,6 +532,24 @@ void NetworkGeometry::maybeReadModelWithMapping() {
_meshes.append(networkMesh); _meshes.append(networkMesh);
} }
emit loaded();
}
void NetworkGeometry::loadFallback() {
_geometry = _fallback->_geometry;
_meshes = _fallback->_meshes;
emit loaded();
}
void NetworkGeometry::maybeLoadFallback() {
if (_fallback) {
if (_fallback->isLoaded()) {
loadFallback();
} else {
connect(_fallback.data(), SIGNAL(loaded()), SLOT(loadFallback()));
}
}
} }
bool NetworkMeshPart::isTranslucent() const { bool NetworkMeshPart::isTranslucent() const {

View file

@ -37,7 +37,8 @@ public:
void renderGrid(int xDivisions, int yDivisions); void renderGrid(int xDivisions, int yDivisions);
/// Loads geometry from the specified URL. /// Loads geometry from the specified URL.
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url); /// \param fallback a fallback URL to load if the desired one is unavailable
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl());
private: private:
@ -58,7 +59,7 @@ class NetworkGeometry : public QObject {
public: public:
NetworkGeometry(const QUrl& url); NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback);
~NetworkGeometry(); ~NetworkGeometry();
bool isLoaded() const { return !_geometry.joints.isEmpty(); } bool isLoaded() const { return !_geometry.joints.isEmpty(); }
@ -69,18 +70,26 @@ public:
/// Returns the average color of all meshes in the geometry. /// Returns the average color of all meshes in the geometry.
glm::vec4 computeAverageColor() const; glm::vec4 computeAverageColor() const;
signals:
void loaded();
private slots: private slots:
void makeModelRequest(); void makeModelRequest();
void handleModelReplyError(); void handleModelReplyError();
void handleMappingReplyError(); void handleMappingReplyError();
void maybeReadModelWithMapping(); void maybeReadModelWithMapping();
void loadFallback();
private: private:
void maybeLoadFallback();
QNetworkRequest _modelRequest; QNetworkRequest _modelRequest;
QNetworkReply* _modelReply; QNetworkReply* _modelReply;
QNetworkReply* _mappingReply; QNetworkReply* _mappingReply;
QSharedPointer<NetworkGeometry> _fallback;
int _attempts; int _attempts;
FBXGeometry _geometry; FBXGeometry _geometry;

View file

@ -390,7 +390,7 @@ float Model::getRightArmLength() const {
return getLimbLength(getRightHandJointIndex()); return getLimbLength(getRightHandJointIndex());
} }
void Model::setURL(const QUrl& url) { void Model::setURL(const QUrl& url, const QUrl& fallback) {
// don't recreate the geometry if it's the same URL // don't recreate the geometry if it's the same URL
if (_url == url) { if (_url == url) {
return; return;
@ -401,7 +401,7 @@ void Model::setURL(const QUrl& url) {
deleteGeometry(); deleteGeometry();
_dilatedTextures.clear(); _dilatedTextures.clear();
_geometry = Application::getInstance()->getGeometryCache()->getGeometry(url); _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback);
} }
glm::vec4 Model::computeAverageColor() const { glm::vec4 Model::computeAverageColor() const {

View file

@ -51,7 +51,7 @@ public:
void simulate(float deltaTime); void simulate(float deltaTime);
bool render(float alpha); bool render(float alpha);
Q_INVOKABLE void setURL(const QUrl& url); Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl());
const QUrl& getURL() const { return _url; } const QUrl& getURL() const { return _url; }
/// Returns the extents of the model in its bind pose. /// Returns the extents of the model in its bind pose.