Merge branch 'master' of https://github.com/worklist/hifi into voxel_render_pipeline_improvement

This commit is contained in:
ZappoMan 2013-10-03 15:09:28 -07:00
commit 2450d64016
4 changed files with 173 additions and 88 deletions

View file

@ -19,12 +19,6 @@
using namespace std;
FBXNode parseFBX(const QByteArray& data) {
QBuffer buffer(const_cast<QByteArray*>(&data));
buffer.open(QIODevice::ReadOnly);
return parseFBX(&buffer);
}
template<class T> QVariant readArray(QDataStream& in) {
quint32 arrayLength;
quint32 encoding;
@ -189,6 +183,41 @@ FBXNode parseFBX(QIODevice* device) {
return top;
}
QVariantHash parseMapping(QIODevice* device) {
QVariantHash properties;
QByteArray line;
while (!(line = device->readLine()).isEmpty()) {
if ((line = line.trimmed()).startsWith('#')) {
continue; // comment
}
QList<QByteArray> sections = line.split('=');
if (sections.size() < 2) {
continue;
}
QByteArray name = sections.at(0).trimmed();
if (sections.size() == 2) {
properties.insert(name, sections.at(1).trimmed());
} else if (sections.size() == 3) {
QVariantHash heading = properties.value(name).toHash();
heading.insert(sections.at(1).trimmed(), sections.at(2).trimmed());
properties.insert(name, heading);
} else if (sections.size() >= 4) {
QVariantHash heading = properties.value(name).toHash();
QVariantList contents;
for (int i = 2; i < sections.size(); i++) {
contents.append(sections.at(i).trimmed());
}
heading.insertMulti(sections.at(1).trimmed(), contents);
properties.insert(name, heading);
}
}
return properties;
}
QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
QVector<glm::vec3> values;
for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) {
@ -269,19 +298,6 @@ const char* FACESHIFT_BLENDSHAPES[] = {
""
};
QHash<QByteArray, int> createBlendshapeMap() {
QHash<QByteArray, int> map;
for (int i = 0;; i++) {
QByteArray name = FACESHIFT_BLENDSHAPES[i];
if (name != "") {
map.insert(name, i);
} else {
return map;
}
}
}
class Transform {
public:
bool inheritScale;
@ -315,11 +331,10 @@ glm::mat4 getGlobalTransform(const QMultiHash<qint64, qint64>& parentMap, const
class ExtractedBlendshape {
public:
qint64 id;
int index;
FBXBlendshape blendshape;
};
FBXGeometry extractFBXGeometry(const FBXNode& node) {
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
QHash<qint64, FBXMesh> meshes;
QVector<ExtractedBlendshape> blendshapes;
QMultiHash<qint64, qint64> parentMap;
@ -329,10 +344,35 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
QHash<qint64, QByteArray> textureFilenames;
QHash<qint64, qint64> diffuseTextures;
QHash<qint64, qint64> bumpTextures;
QVariantHash joints = mapping.value("joint").toHash();
QByteArray jointEyeLeftName = joints.value("jointEyeLeft", "jointEyeLeft").toByteArray();
QByteArray jointEyeRightName = joints.value("jointEyeRight", "jointEyeRight").toByteArray();
QByteArray jointNeckName = joints.value("jointNeck", "jointNeck").toByteArray();
qint64 jointEyeLeftID = 0;
qint64 jointEyeRightID = 0;
qint64 jointNeckID = 0;
QVariantHash blendshapeMappings = mapping.value("bs").toHash();
QHash<QByteArray, QPair<int, float> > blendshapeIndices;
for (int i = 0;; i++) {
QByteArray blendshapeName = FACESHIFT_BLENDSHAPES[i];
if (blendshapeName.isEmpty()) {
break;
}
QList<QVariant> mappings = blendshapeMappings.values(blendshapeName);
if (mappings.isEmpty()) {
blendshapeIndices.insert("ExpressionBlendshapes." + blendshapeName, QPair<int, float>(i, 1.0f));
} else {
foreach (const QVariant& mapping, mappings) {
QVariantList blendshapeMapping = mapping.toList();
blendshapeIndices.insert(blendshapeMapping.at(0).toByteArray(),
QPair<int, float>(i, blendshapeMapping.at(1).toFloat()));
}
}
}
QHash<qint64, QPair<int, float> > blendshapeChannelIndices;
foreach (const FBXNode& child, node.children) {
if (child.name == "Objects") {
foreach (const FBXNode& object, child.children) {
@ -387,14 +427,16 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
}
// same with the tex coords
mesh.texCoords.resize(mesh.vertices.size());
for (int i = 0, n = polygonIndices.size(); i < n; i++) {
int index = polygonIndices.at(i);
int texCoordIndex = texCoordIndices.at(i);
if (texCoordIndex >= 0) {
mesh.texCoords[index < 0 ? (-index - 1) : index] = texCoords.at(texCoordIndex);
}
}
if (!texCoordIndices.isEmpty()) {
mesh.texCoords.resize(mesh.vertices.size());
for (int i = 0, n = polygonIndices.size(); i < n; i++) {
int index = polygonIndices.at(i);
int texCoordIndex = texCoordIndices.at(i);
if (texCoordIndex >= 0) {
mesh.texCoords[index < 0 ? (-index - 1) : index] = texCoords.at(texCoordIndex);
}
}
}
// convert the polygons to quads and triangles
for (const int* beginIndex = polygonIndices.constData(), *end = beginIndex + polygonIndices.size();
@ -441,22 +483,18 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
}
}
// the name is followed by a null and some type info
QByteArray name = object.properties.at(1).toByteArray();
static QHash<QByteArray, int> blendshapeMap = createBlendshapeMap();
extracted.index = blendshapeMap.value(name.left(name.indexOf('\0')));
blendshapes.append(extracted);
}
} else if (object.name == "Model") {
QByteArray name = object.properties.at(1).toByteArray();
if (name.startsWith("jointEyeLeft") || name.startsWith("EyeL") || name.startsWith("joint_Leye")) {
name = name.left(name.indexOf('\0'));
if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") {
jointEyeLeftID = object.properties.at(0).value<qint64>();
} else if (name.startsWith("jointEyeRight") || name.startsWith("EyeR") || name.startsWith("joint_Reye")) {
} else if (name == jointEyeRightName || name == "EyeR" || name == "joint_Reye") {
jointEyeRightID = object.properties.at(0).value<qint64>();
} else if (name.startsWith("jointNeck") || name.startsWith("NeckRot") || name.startsWith("joint_neck")) {
} else if (name == jointNeckName || name == "NeckRot" || name == "joint_neck") {
jointNeckID = object.properties.at(0).value<qint64>();
}
glm::vec3 translation;
@ -526,12 +564,18 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
subobject.properties.at(0).toByteArray());
}
}
} else if (object.name == "Deformer" && object.properties.at(2) == "Cluster") {
foreach (const FBXNode& subobject, object.children) {
if (subobject.name == "TransformLink") {
QVector<double> values = subobject.properties.at(0).value<QVector<double> >();
transformLinkMatrices.insert(object.properties.at(0).value<qint64>(), createMat4(values));
} else if (object.name == "Deformer") {
if (object.properties.at(2) == "Cluster") {
foreach (const FBXNode& subobject, object.children) {
if (subobject.name == "TransformLink") {
QVector<double> values = subobject.properties.at(0).value<QVector<double> >();
transformLinkMatrices.insert(object.properties.at(0).value<qint64>(), createMat4(values));
}
}
} else if (object.properties.at(2) == "BlendShapeChannel") {
QByteArray name = object.properties.at(1).toByteArray();
blendshapeChannelIndices.insert(object.properties.at(0).value<qint64>(),
blendshapeIndices.value(name.left(name.indexOf('\0'))));
}
}
}
@ -558,13 +602,21 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
// assign the blendshapes to their corresponding meshes
foreach (const ExtractedBlendshape& extracted, blendshapes) {
qint64 blendshapeChannelID = parentMap.value(extracted.id);
QPair<int, float> index = blendshapeChannelIndices.value(blendshapeChannelID);
qint64 blendshapeID = parentMap.value(blendshapeChannelID);
qint64 meshID = parentMap.value(blendshapeID);
FBXMesh& mesh = meshes[meshID];
mesh.blendshapes.resize(max(mesh.blendshapes.size(), extracted.index + 1));
mesh.blendshapes[extracted.index] = extracted.blendshape;
mesh.blendshapes.resize(max(mesh.blendshapes.size(), index.first + 1));
mesh.blendshapes[index.first] = extracted.blendshape;
}
// get offset transform from mapping
float offsetScale = mapping.value("scale", 1.0f).toFloat();
glm::mat4 offset = glm::translate(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(),
mapping.value("tz").toFloat()) * glm::mat4_cast(glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(),
mapping.value("ry").toFloat(), mapping.value("rz").toFloat())))) *
glm::scale(offsetScale, offsetScale, offsetScale);
// as a temporary hack, put the mesh with the most blendshapes on top; assume it to be the face
FBXGeometry geometry;
int mostBlendshapes = 0;
@ -601,11 +653,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
// see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion
// of skinning information in FBX
glm::mat4 jointTransform = getGlobalTransform(parentMap, localTransforms, jointID);
glm::mat4 jointTransform = offset * getGlobalTransform(parentMap, localTransforms, jointID);
mesh.transform = jointTransform * glm::inverse(transformLinkMatrices.value(clusterID)) * modelTransform;
// extract translation component for pivot
glm::mat4 jointTransformScaled = getGlobalTransform(parentMap, localTransforms, jointID, true);
glm::mat4 jointTransformScaled = offset * getGlobalTransform(parentMap, localTransforms, jointID, true);
mesh.pivot = glm::vec3(jointTransformScaled[3][0], jointTransformScaled[3][1], jointTransformScaled[3][2]);
}
}
@ -620,7 +672,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
}
// extract translation component for neck pivot
glm::mat4 neckTransform = getGlobalTransform(parentMap, localTransforms, jointNeckID, true);
glm::mat4 neckTransform = offset * getGlobalTransform(parentMap, localTransforms, jointNeckID, true);
geometry.neckPivot = glm::vec3(neckTransform[3][0], neckTransform[3][1], neckTransform[3][2]);
return geometry;
@ -638,3 +690,13 @@ void printNode(const FBXNode& node, int indent) {
}
}
FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping) {
QBuffer modelBuffer(const_cast<QByteArray*>(&model));
modelBuffer.open(QIODevice::ReadOnly);
QBuffer mappingBuffer(const_cast<QByteArray*>(&mapping));
mappingBuffer.open(QIODevice::ReadOnly);
return extractFBXGeometry(parseFBX(&modelBuffer), parseMapping(&mappingBuffer));
}

View file

@ -14,8 +14,6 @@
#include <glm/glm.hpp>
class QIODevice;
class FBXNode;
typedef QList<FBXNode> FBXNodeList;
@ -68,17 +66,8 @@ public:
glm::vec3 neckPivot;
};
/// Parses the input from the supplied data as an FBX file.
/// Reads FBX geometry from the supplied model and mapping data.
/// \exception QString if an error occurs in parsing
FBXNode parseFBX(const QByteArray& data);
/// Parses the input from the supplied device as an FBX file.
/// \exception QString if an error occurs in parsing
FBXNode parseFBX(QIODevice* device);
/// Extracts the geometry from a parsed FBX node.
FBXGeometry extractFBXGeometry(const FBXNode& node);
void printNode(const FBXNode& node, int indent = 0);
FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping);
#endif /* defined(__interface__FBXReader__) */

View file

@ -250,21 +250,37 @@ QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url) {
return geometry;
}
NetworkGeometry::NetworkGeometry(const QUrl& url) : _reply(NULL) {
NetworkGeometry::NetworkGeometry(const QUrl& url) :
_modelReply(NULL),
_mappingReply(NULL)
{
if (!url.isValid()) {
return;
}
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
_reply = Application::getInstance()->getNetworkAccessManager()->get(request);
QNetworkRequest modelRequest(url);
modelRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
_modelReply = Application::getInstance()->getNetworkAccessManager()->get(modelRequest);
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
connect(_modelReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(maybeReadModelWithMapping()));
connect(_modelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleModelReplyError()));
QUrl mappingURL = url;
QString path = url.path();
mappingURL.setPath(path.left(path.lastIndexOf('.')) + ".fst");
QNetworkRequest mappingRequest(mappingURL);
mappingRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
_mappingReply = Application::getInstance()->getNetworkAccessManager()->get(mappingRequest);
connect(_mappingReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(maybeReadModelWithMapping()));
connect(_mappingReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleMappingReplyError()));
}
NetworkGeometry::~NetworkGeometry() {
if (_reply != NULL) {
delete _reply;
if (_modelReply != NULL) {
delete _modelReply;
}
if (_mappingReply != NULL) {
delete _mappingReply;
}
foreach (const NetworkMesh& mesh, _meshes) {
glDeleteBuffers(1, &mesh.indexBufferID);
@ -272,19 +288,43 @@ NetworkGeometry::~NetworkGeometry() {
}
}
void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
if (bytesReceived < bytesTotal && !_reply->isFinished()) {
void NetworkGeometry::handleModelReplyError() {
qDebug() << _modelReply->errorString() << "\n";
_modelReply->disconnect(this);
_modelReply->deleteLater();
_modelReply = NULL;
}
void NetworkGeometry::handleMappingReplyError() {
_mappingReply->disconnect(this);
_mappingReply->deleteLater();
_mappingReply = NULL;
maybeReadModelWithMapping();
}
void NetworkGeometry::maybeReadModelWithMapping() {
if (_modelReply == NULL || !_modelReply->isFinished() || (_mappingReply != NULL && !_mappingReply->isFinished())) {
return;
}
QUrl url = _reply->url();
QByteArray entirety = _reply->readAll();
_reply->disconnect(this);
_reply->deleteLater();
_reply = NULL;
QUrl url = _modelReply->url();
QByteArray model = _modelReply->readAll();
_modelReply->disconnect(this);
_modelReply->deleteLater();
_modelReply = NULL;
QByteArray mapping;
if (_mappingReply != NULL) {
mapping = _mappingReply->readAll();
_mappingReply->disconnect(this);
_mappingReply->deleteLater();
_mappingReply = NULL;
}
try {
_geometry = extractFBXGeometry(parseFBX(entirety));
_geometry = readFBX(model, mapping);
} catch (const QString& error) {
qDebug() << "Error reading " << url << ": " << error << "\n";
@ -338,11 +378,3 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT
_meshes.append(networkMesh);
}
}
void NetworkGeometry::handleReplyError() {
qDebug() << _reply->errorString() << "\n";
_reply->disconnect(this);
_reply->deleteLater();
_reply = NULL;
}

View file

@ -64,12 +64,14 @@ public:
private slots:
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void handleReplyError();
void handleModelReplyError();
void handleMappingReplyError();
void maybeReadModelWithMapping();
private:
QNetworkReply* _reply;
QNetworkReply* _modelReply;
QNetworkReply* _mappingReply;
FBXGeometry _geometry;
QVector<NetworkMesh> _meshes;