Merge pull request #1971 from ey6es/master

Ignore extra "=1"'s that Faceshift appends to FST files, fall back to default head/body on failure to load.
This commit is contained in:
ZappoMan 2014-02-10 19:50:26 -08:00
commit 91740f89f8
11 changed files with 148 additions and 25 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

@ -345,12 +345,14 @@ bool Avatar::findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, floa
void Avatar::setFaceModelURL(const QUrl &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) {
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) {

View file

@ -834,6 +834,12 @@ QString getTopModelID(const QMultiHash<QString, QString>& parentMap,
}
}
QString getString(const QVariant& value) {
// if it's a list, return the first entry
QVariantList list = value.toList();
return list.isEmpty() ? value.toString() : list.at(0).toString();
}
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
QHash<QString, ExtractedMesh> meshes;
QVector<ExtractedBlendshape> blendshapes;
@ -847,14 +853,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
QHash<QString, QString> bumpTextures;
QVariantHash joints = mapping.value("joint").toHash();
QString jointEyeLeftName = processID(joints.value("jointEyeLeft", "jointEyeLeft").toString());
QString jointEyeRightName = processID(joints.value("jointEyeRight", "jointEyeRight").toString());
QString jointNeckName = processID(joints.value("jointNeck", "jointNeck").toString());
QString jointRootName = processID(joints.value("jointRoot", "jointRoot").toString());
QString jointLeanName = processID(joints.value("jointLean", "jointLean").toString());
QString jointHeadName = processID(joints.value("jointHead", "jointHead").toString());
QString jointLeftHandName = processID(joints.value("jointLeftHand", "jointLeftHand").toString());
QString jointRightHandName = processID(joints.value("jointRightHand", "jointRightHand").toString());
QString jointEyeLeftName = processID(getString(joints.value("jointEyeLeft", "jointEyeLeft")));
QString jointEyeRightName = processID(getString(joints.value("jointEyeRight", "jointEyeRight")));
QString jointNeckName = processID(getString(joints.value("jointNeck", "jointNeck")));
QString jointRootName = processID(getString(joints.value("jointRoot", "jointRoot")));
QString jointLeanName = processID(getString(joints.value("jointLean", "jointLean")));
QString jointHeadName = processID(getString(joints.value("jointHead", "jointHead")));
QString jointLeftHandName = processID(getString(joints.value("jointLeftHand", "jointLeftHand")));
QString jointRightHandName = processID(getString(joints.value("jointRightHand", "jointRightHand")));
QVariantList jointLeftFingerNames = joints.values("jointLeftFinger");
QVariantList jointRightFingerNames = joints.values("jointRightFinger");
QVariantList jointLeftFingertipNames = joints.values("jointLeftFingertip");

View file

@ -290,19 +290,24 @@ void GeometryCache::renderGrid(int xDivisions, int yDivisions) {
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);
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);
}
return geometry;
}
NetworkGeometry::NetworkGeometry(const QUrl& url) :
NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback) :
_modelRequest(url),
_modelReply(NULL),
_mappingReply(NULL),
_fallback(fallback),
_attempts(0)
{
if (!url.isValid()) {
@ -369,18 +374,37 @@ void NetworkGeometry::makeModelRequest() {
void NetworkGeometry::handleModelReplyError() {
QDebug debug = qDebug() << _modelReply->errorString();
QNetworkReply::NetworkError error = _modelReply->error();
_modelReply->disconnect(this);
_modelReply->deleteLater();
_modelReply = NULL;
// retry with increasing delays
const int MAX_ATTEMPTS = 8;
const int BASE_DELAY_MS = 1000;
if (++_attempts < MAX_ATTEMPTS) {
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeModelRequest()));
debug << " -- retrying...";
// 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
const int MAX_ATTEMPTS = 8;
const int BASE_DELAY_MS = 1000;
if (++_attempts < MAX_ATTEMPTS) {
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeModelRequest()));
debug << " -- retrying...";
return;
}
// fall through to final failure
}
default:
maybeLoadFallback();
break;
}
}
void NetworkGeometry::handleMappingReplyError() {
@ -415,6 +439,7 @@ void NetworkGeometry::maybeReadModelWithMapping() {
} catch (const QString& error) {
qDebug() << "Error reading " << url << ": " << error;
maybeLoadFallback();
return;
}
@ -507,6 +532,24 @@ void NetworkGeometry::maybeReadModelWithMapping() {
_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 {

View file

@ -37,7 +37,8 @@ public:
void renderGrid(int xDivisions, int yDivisions);
/// 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:
@ -58,7 +59,7 @@ class NetworkGeometry : public QObject {
public:
NetworkGeometry(const QUrl& url);
NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback);
~NetworkGeometry();
bool isLoaded() const { return !_geometry.joints.isEmpty(); }
@ -69,18 +70,26 @@ public:
/// Returns the average color of all meshes in the geometry.
glm::vec4 computeAverageColor() const;
signals:
void loaded();
private slots:
void makeModelRequest();
void handleModelReplyError();
void handleMappingReplyError();
void maybeReadModelWithMapping();
void loadFallback();
private:
void maybeLoadFallback();
QNetworkRequest _modelRequest;
QNetworkReply* _modelReply;
QNetworkReply* _mappingReply;
QSharedPointer<NetworkGeometry> _fallback;
int _attempts;
FBXGeometry _geometry;

View file

@ -390,7 +390,7 @@ float Model::getRightArmLength() const {
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
if (_url == url) {
return;
@ -401,7 +401,7 @@ void Model::setURL(const QUrl& url) {
deleteGeometry();
_dilatedTextures.clear();
_geometry = Application::getInstance()->getGeometryCache()->getGeometry(url);
_geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback);
}
glm::vec4 Model::computeAverageColor() const {

View file

@ -61,7 +61,7 @@ public:
void simulate(float deltaTime);
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; }
/// Returns the extents of the model in its bind pose.