Merge pull request #976 from ey6es/blendface

Include other meshes (eyes, teeth) from the FBX files.
This commit is contained in:
Philip Rosedale 2013-09-25 10:59:27 -07:00
commit 2db8985c63
6 changed files with 221 additions and 109 deletions

View file

@ -17,22 +17,18 @@ using namespace std;
BlendFace::BlendFace(Head* owningHead) : BlendFace::BlendFace(Head* owningHead) :
_owningHead(owningHead), _owningHead(owningHead),
_modelReply(NULL), _modelReply(NULL)
_iboID(0)
{ {
// we may have been created in the network thread, but we live in the main thread // we may have been created in the network thread, but we live in the main thread
moveToThread(Application::getInstance()->thread()); moveToThread(Application::getInstance()->thread());
} }
BlendFace::~BlendFace() { BlendFace::~BlendFace() {
if (_iboID != 0) { deleteGeometry();
glDeleteBuffers(1, &_iboID);
glDeleteBuffers(1, &_vboID);
}
} }
bool BlendFace::render(float alpha) { bool BlendFace::render(float alpha) {
if (_iboID == 0) { if (_meshIDs.isEmpty()) {
return false; return false;
} }
@ -41,62 +37,87 @@ bool BlendFace::render(float alpha) {
glm::quat orientation = _owningHead->getOrientation(); glm::quat orientation = _owningHead->getOrientation();
glm::vec3 axis = glm::axis(orientation); glm::vec3 axis = glm::axis(orientation);
glRotatef(glm::angle(orientation), axis.x, axis.y, axis.z); glRotatef(glm::angle(orientation), axis.x, axis.y, axis.z);
glTranslatef(0.0f, -0.025f, -0.025f); // temporary fudge factor until we have a better method of per-model positioning const glm::vec3 MODEL_TRANSLATION(0.0f, -0.025f, -0.025f); // temporary fudge factor
glTranslatef(MODEL_TRANSLATION.x, MODEL_TRANSLATION.y, MODEL_TRANSLATION.z);
const float MODEL_SCALE = 0.0006f; const float MODEL_SCALE = 0.0006f;
glScalef(_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE, glm::vec3 scale(_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE,
-_owningHead->getScale() * MODEL_SCALE); -_owningHead->getScale() * MODEL_SCALE);
glScalef(scale.x, scale.y, scale.z);
// start with the base
int vertexCount = _geometry.vertices.size();
int normalCount = _geometry.normals.size();
_blendedVertices.resize(vertexCount);
_blendedNormals.resize(normalCount);
memcpy(_blendedVertices.data(), _geometry.vertices.constData(), vertexCount * sizeof(glm::vec3));
memcpy(_blendedNormals.data(), _geometry.normals.constData(), normalCount * sizeof(glm::vec3));
// blend in each coefficient
const vector<float>& coefficients = _owningHead->getBlendshapeCoefficients();
for (int i = 0; i < coefficients.size(); i++) {
float coefficient = coefficients[i];
if (coefficient == 0.0f || i >= _geometry.blendshapes.size() || _geometry.blendshapes[i].vertices.isEmpty()) {
continue;
}
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
float normalCoefficient = coefficient * NORMAL_COEFFICIENT_SCALE;
const glm::vec3* vertex = _geometry.blendshapes[i].vertices.constData();
const glm::vec3* normal = _geometry.blendshapes[i].normals.constData();
for (const int* index = _geometry.blendshapes[i].indices.constData(),
*end = index + _geometry.blendshapes[i].indices.size(); index != end; index++, vertex++, normal++) {
_blendedVertices[*index] += *vertex * coefficient;
_blendedNormals[*index] += *normal * normalCoefficient;
}
}
// use the head skin color
glColor4f(_owningHead->getSkinColor().r, _owningHead->getSkinColor().g, _owningHead->getSkinColor().b, alpha);
// update the blended vertices
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData());
glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3),
normalCount * sizeof(glm::vec3), _blendedNormals.constData());
// tell OpenGL where to find vertex information
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, 0);
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3)));
// enable normalization under the expectation that the GPU can do it faster // enable normalization under the expectation that the GPU can do it faster
glEnable(GL_NORMALIZE); glEnable(GL_NORMALIZE);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID); glColor4f(_owningHead->getSkinColor().r, _owningHead->getSkinColor().g, _owningHead->getSkinColor().b, alpha);
glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, _geometry.quadIndices.size(), GL_UNSIGNED_INT, 0);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, _geometry.triangleIndices.size(), GL_UNSIGNED_INT,
(void*)(_geometry.quadIndices.size() * sizeof(int)));
glDisable(GL_NORMALIZE);
for (int i = 0; i < _meshIDs.size(); i++) {
const VerticesIndices& ids = _meshIDs.at(i);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ids.first);
glBindBuffer(GL_ARRAY_BUFFER, ids.second);
const FBXMesh& mesh = _geometry.meshes.at(i);
int vertexCount = mesh.vertices.size();
// apply eye rotation if appropriate
if (mesh.isEye) {
glPushMatrix();
glTranslatef(mesh.pivot.x, mesh.pivot.y, mesh.pivot.z);
glm::quat rotation = glm::inverse(orientation) * _owningHead->getEyeRotation(orientation *
(mesh.pivot * scale + MODEL_TRANSLATION) + _owningHead->getPosition());
glm::vec3 rotationAxis = glm::axis(rotation);
glRotatef(glm::angle(rotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glTranslatef(-mesh.pivot.x, -mesh.pivot.y, -mesh.pivot.z);
}
// all meshes after the first are white
if (i == 1) {
glColor4f(1.0f, 1.0f, 1.0f, alpha);
}
if (!mesh.blendshapes.isEmpty()) {
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
_blendedNormals.resize(_blendedVertices.size());
memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3));
memcpy(_blendedNormals.data(), mesh.normals.constData(), vertexCount * sizeof(glm::vec3));
// blend in each coefficient
const vector<float>& coefficients = _owningHead->getBlendshapeCoefficients();
for (int i = 0; i < coefficients.size(); i++) {
float coefficient = coefficients[i];
if (coefficient == 0.0f || i >= mesh.blendshapes.size() || mesh.blendshapes[i].vertices.isEmpty()) {
continue;
}
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
float normalCoefficient = coefficient * NORMAL_COEFFICIENT_SCALE;
const glm::vec3* vertex = mesh.blendshapes[i].vertices.constData();
const glm::vec3* normal = mesh.blendshapes[i].normals.constData();
for (const int* index = mesh.blendshapes[i].indices.constData(),
*end = index + mesh.blendshapes[i].indices.size(); index != end; index++, vertex++, normal++) {
_blendedVertices[*index] += *vertex * coefficient;
_blendedNormals[*index] += *normal * normalCoefficient;
}
}
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData());
glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3),
vertexCount * sizeof(glm::vec3), _blendedNormals.constData());
}
glVertexPointer(3, GL_FLOAT, 0, 0);
glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3)));
glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, mesh.quadIndices.size(), GL_UNSIGNED_INT, 0);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, mesh.triangleIndices.size(),
GL_UNSIGNED_INT, (void*)(mesh.quadIndices.size() * sizeof(int)));
if (mesh.isEye) {
glPopMatrix();
}
}
glDisable(GL_NORMALIZE);
// deactivate vertex arrays after drawing // deactivate vertex arrays after drawing
glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_VERTEX_ARRAY);
@ -171,31 +192,50 @@ void BlendFace::handleModelReplyError() {
} }
void BlendFace::setGeometry(const FBXGeometry& geometry) { void BlendFace::setGeometry(const FBXGeometry& geometry) {
if (geometry.vertices.isEmpty()) { // clear any existing geometry
// clear any existing geometry deleteGeometry();
if (_iboID != 0) {
glDeleteBuffers(1, &_iboID); if (geometry.meshes.isEmpty()) {
glDeleteBuffers(1, &_vboID);
_iboID = 0;
}
return; return;
} }
if (_iboID == 0) { foreach (const FBXMesh& mesh, geometry.meshes) {
glGenBuffers(1, &_iboID); VerticesIndices ids;
glGenBuffers(1, &_vboID); glGenBuffers(1, &ids.first);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ids.first);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (mesh.quadIndices.size() + mesh.triangleIndices.size()) * sizeof(int),
NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mesh.quadIndices.size() * sizeof(int), mesh.quadIndices.constData());
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, mesh.quadIndices.size() * sizeof(int),
mesh.triangleIndices.size() * sizeof(int), mesh.triangleIndices.constData());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glGenBuffers(1, &ids.second);
glBindBuffer(GL_ARRAY_BUFFER, ids.second);
if (mesh.blendshapes.isEmpty()) {
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3),
NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.vertices.size() * sizeof(glm::vec3), mesh.vertices.constData());
glBufferSubData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(glm::vec3),
mesh.normals.size() * sizeof(glm::vec3), mesh.normals.constData());
} else {
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3),
NULL, GL_DYNAMIC_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
_meshIDs.append(ids);
} }
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (geometry.quadIndices.size() + geometry.triangleIndices.size()) * sizeof(int),
NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, geometry.quadIndices.size() * sizeof(int), geometry.quadIndices.constData());
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, geometry.quadIndices.size() * sizeof(int),
geometry.triangleIndices.size() * sizeof(int), geometry.triangleIndices.constData());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glBufferData(GL_ARRAY_BUFFER, (geometry.vertices.size() + geometry.normals.size()) * sizeof(glm::vec3),
NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
_geometry = geometry; _geometry = geometry;
} }
void BlendFace::deleteGeometry() {
foreach (const VerticesIndices& meshIDs, _meshIDs) {
glDeleteBuffers(1, &meshIDs.first);
glDeleteBuffers(1, &meshIDs.second);
}
_meshIDs.clear();
}

View file

@ -28,7 +28,7 @@ public:
BlendFace(Head* owningHead); BlendFace(Head* owningHead);
~BlendFace(); ~BlendFace();
bool isActive() const { return _iboID != 0; } bool isActive() const { return !_meshIDs.isEmpty(); }
bool render(float alpha); bool render(float alpha);
@ -43,6 +43,7 @@ private slots:
private: private:
void setGeometry(const FBXGeometry& geometry); void setGeometry(const FBXGeometry& geometry);
void deleteGeometry();
Head* _owningHead; Head* _owningHead;
@ -50,8 +51,8 @@ private:
QNetworkReply* _modelReply; QNetworkReply* _modelReply;
GLuint _iboID; typedef QPair<GLuint, GLuint> VerticesIndices;
GLuint _vboID; QVector<VerticesIndices> _meshIDs;
FBXGeometry _geometry; FBXGeometry _geometry;
QVector<glm::vec3> _blendedVertices; QVector<glm::vec3> _blendedVertices;

View file

@ -459,6 +459,11 @@ glm::quat Head::getCameraOrientation () const {
* glm::quat(glm::radians(glm::vec3(_cameraPitch + _mousePitch, _cameraYaw, 0.0f))); * glm::quat(glm::radians(glm::vec3(_cameraPitch + _mousePitch, _cameraYaw, 0.0f)));
} }
glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {
glm::quat orientation = getOrientation();
return rotationBetween(orientation * IDENTITY_FRONT, _lookAtPosition + _saccade - eyePosition) * orientation;
}
void Head::renderHeadSphere() { void Head::renderHeadSphere() {
glPushMatrix(); glPushMatrix();
glTranslatef(_position.x, _position.y, _position.z); //translate to head position glTranslatef(_position.x, _position.y, _position.z); //translate to head position
@ -663,17 +668,13 @@ void Head::renderEyeBalls() {
glBindTexture(GL_TEXTURE_2D, _irisTextureID); glBindTexture(GL_TEXTURE_2D, _irisTextureID);
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
glm::quat orientation = getOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT;
// render left iris // render left iris
glm::quat leftIrisRotation; glm::quat leftIrisRotation;
glPushMatrix(); { glPushMatrix(); {
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position
//rotate the eyeball to aim towards the lookat position //rotate the eyeball to aim towards the lookat position
glm::vec3 targetLookatVector = _lookAtPosition + _saccade - _leftEyePosition; leftIrisRotation = getEyeRotation(_leftEyePosition);
leftIrisRotation = rotationBetween(front, targetLookatVector) * orientation;
glm::vec3 rotationAxis = glm::axis(leftIrisRotation); glm::vec3 rotationAxis = glm::axis(leftIrisRotation);
glRotatef(glm::angle(leftIrisRotation), rotationAxis.x, rotationAxis.y, rotationAxis.z); glRotatef(glm::angle(leftIrisRotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glTranslatef(0.0f, 0.0f, -_scale * IRIS_PROTRUSION); glTranslatef(0.0f, 0.0f, -_scale * IRIS_PROTRUSION);
@ -697,8 +698,7 @@ void Head::renderEyeBalls() {
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position
//rotate the eyeball to aim towards the lookat position //rotate the eyeball to aim towards the lookat position
glm::vec3 targetLookatVector = _lookAtPosition + _saccade - _rightEyePosition; rightIrisRotation = getEyeRotation(_rightEyePosition);
rightIrisRotation = rotationBetween(front, targetLookatVector) * orientation;
glm::vec3 rotationAxis = glm::axis(rightIrisRotation); glm::vec3 rotationAxis = glm::axis(rightIrisRotation);
glRotatef(glm::angle(rightIrisRotation), rotationAxis.x, rotationAxis.y, rotationAxis.z); glRotatef(glm::angle(rightIrisRotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glTranslatef(0.0f, 0.0f, -_scale * IRIS_PROTRUSION); glTranslatef(0.0f, 0.0f, -_scale * IRIS_PROTRUSION);

View file

@ -72,6 +72,8 @@ public:
glm::vec3 getUpDirection() const { return getOrientation() * IDENTITY_UP; } glm::vec3 getUpDirection() const { return getOrientation() * IDENTITY_UP; }
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; } glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
glm::quat getEyeRotation(const glm::vec3& eyePosition) const;
Face& getFace() { return _face; } Face& getFace() { return _face; }
BlendFace& getBlendFace() { return _blendFace; } BlendFace& getBlendFace() { return _blendFace; }

View file

@ -189,7 +189,10 @@ FBXNode parseFBX(QIODevice* device) {
QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) { QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
QVector<glm::vec3> values; QVector<glm::vec3> values;
for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) { for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) {
values.append(glm::vec3(*it++, *it++, *it++)); float x = *it++;
float y = *it++;
float z = *it++;
values.append(glm::vec3(x, y, z));
} }
return values; return values;
} }
@ -259,18 +262,28 @@ QHash<QByteArray, int> createBlendshapeMap() {
} }
} }
class ExtractedBlendshape {
public:
qint64 id;
int index;
FBXBlendshape blendshape;
};
FBXGeometry extractFBXGeometry(const FBXNode& node) { FBXGeometry extractFBXGeometry(const FBXNode& node) {
QVector<FBXBlendshape> blendshapes; QHash<qint64, FBXMesh> meshes;
QHash<qint64, FBXGeometry> meshMap; QVector<ExtractedBlendshape> blendshapes;
qint64 blendshapeId = 0;
QHash<qint64, qint64> parentMap; QHash<qint64, qint64> parentMap;
QMultiHash<qint64, qint64> childMap;
QHash<qint64, glm::vec3> pivots;
qint64 jointEyeLeftID = 0;
qint64 jointEyeRightID = 0;
foreach (const FBXNode& child, node.children) { foreach (const FBXNode& child, node.children) {
if (child.name == "Objects") { if (child.name == "Objects") {
foreach (const FBXNode& object, child.children) { foreach (const FBXNode& object, child.children) {
if (object.name == "Geometry") { if (object.name == "Geometry") {
if (object.properties.at(2) == "Mesh") { if (object.properties.at(2) == "Mesh") {
FBXGeometry mesh; FBXMesh mesh;
QVector<glm::vec3> normals; QVector<glm::vec3> normals;
QVector<int> polygonIndices; QVector<int> polygonIndices;
@ -323,50 +336,95 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
beginIndex = endIndex; beginIndex = endIndex;
} }
} }
meshMap.insert(object.properties.at(0).value<qint64>(), mesh); meshes.insert(object.properties.at(0).value<qint64>(), mesh);
} else { // object.properties.at(2) == "Shape" } else { // object.properties.at(2) == "Shape"
FBXBlendshape blendshape; ExtractedBlendshape extracted = { object.properties.at(0).value<qint64>() };
foreach (const FBXNode& data, object.children) { foreach (const FBXNode& data, object.children) {
if (data.name == "Indexes") { if (data.name == "Indexes") {
blendshape.indices = data.properties.at(0).value<QVector<int> >(); extracted.blendshape.indices = data.properties.at(0).value<QVector<int> >();
} else if (data.name == "Vertices") { } else if (data.name == "Vertices") {
blendshape.vertices = createVec3Vector(data.properties.at(0).value<QVector<double> >()); extracted.blendshape.vertices = createVec3Vector(
data.properties.at(0).value<QVector<double> >());
} else if (data.name == "Normals") { } else if (data.name == "Normals") {
blendshape.normals = createVec3Vector(data.properties.at(0).value<QVector<double> >()); extracted.blendshape.normals = createVec3Vector(
data.properties.at(0).value<QVector<double> >());
} }
} }
// the name is followed by a null and some type info // the name is followed by a null and some type info
QByteArray name = object.properties.at(1).toByteArray(); QByteArray name = object.properties.at(1).toByteArray();
static QHash<QByteArray, int> blendshapeMap = createBlendshapeMap(); static QHash<QByteArray, int> blendshapeMap = createBlendshapeMap();
int index = blendshapeMap.value(name.left(name.indexOf('\0'))); extracted.index = blendshapeMap.value(name.left(name.indexOf('\0')));
blendshapes.resize(qMax(blendshapes.size(), index + 1));
blendshapes[index] = blendshape; blendshapes.append(extracted);
}
} else if (object.name == "Model" && object.properties.at(2) == "LimbNode") {
if (object.properties.at(1).toByteArray().startsWith("jointEyeLeft")) {
jointEyeLeftID = object.properties.at(0).value<qint64>();
} else if (object.properties.at(1).toByteArray().startsWith("jointEyeRight")) {
jointEyeRightID = object.properties.at(0).value<qint64>();
}
} 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> >();
pivots.insert(object.properties.at(0).value<qint64>(),
glm::vec3(values.at(12), values.at(13), values.at(14))); // matrix translation component
}
} }
} else if (object.name == "Deformer" && object.properties.at(2) == "BlendShape") {
blendshapeId = object.properties.at(0).value<qint64>();
} }
} }
} else if (child.name == "Connections") { } else if (child.name == "Connections") {
foreach (const FBXNode& connection, child.children) { foreach (const FBXNode& connection, child.children) {
if (connection.name == "C") { if (connection.name == "C") {
parentMap.insert(connection.properties.at(1).value<qint64>(), connection.properties.at(2).value<qint64>()); parentMap.insert(connection.properties.at(1).value<qint64>(), connection.properties.at(2).value<qint64>());
childMap.insert(connection.properties.at(2).value<qint64>(), connection.properties.at(1).value<qint64>());
} }
} }
} }
} }
// get the mesh that owns the blendshape // assign the blendshapes to their corresponding meshes
FBXGeometry geometry; foreach (const ExtractedBlendshape& extracted, blendshapes) {
if (meshMap.size() == 1) { qint64 blendshapeChannelID = parentMap.value(extracted.id);
geometry = *meshMap.begin(); qint64 blendshapeID = parentMap.value(blendshapeChannelID);
} else { qint64 meshID = parentMap.value(blendshapeID);
geometry = meshMap.take(parentMap.value(blendshapeId)); FBXMesh& mesh = meshes[meshID];
mesh.blendshapes.resize(max(mesh.blendshapes.size(), extracted.index + 1));
mesh.blendshapes[extracted.index] = extracted.blendshape;
}
// as a temporary hack, put the mesh with the most blendshapes on top; assume it to be the face
FBXGeometry geometry;
int mostBlendshapes = 0;
for (QHash<qint64, FBXMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
FBXMesh& mesh = it.value();
// look for a limb pivot
foreach (qint64 childID, childMap.values(it.key())) {
qint64 clusterID = childMap.value(childID);
if (pivots.contains(clusterID)) {
mesh.pivot = pivots.value(clusterID);
qint64 jointID = childMap.value(clusterID);
if (jointID == jointEyeLeftID || jointID == jointEyeRightID) {
mesh.isEye = true;
}
}
}
if (mesh.blendshapes.size() > mostBlendshapes) {
geometry.meshes.prepend(mesh);
mostBlendshapes = mesh.blendshapes.size();
} else {
geometry.meshes.append(mesh);
}
} }
geometry.blendshapes = blendshapes;
return geometry; return geometry;
} }

View file

@ -38,8 +38,8 @@ public:
QVector<glm::vec3> normals; QVector<glm::vec3> normals;
}; };
/// Base geometry with blendshapes mapped by name. /// A single mesh (with optional blendshapes) extracted from an FBX document.
class FBXGeometry { class FBXMesh {
public: public:
QVector<int> quadIndices; QVector<int> quadIndices;
@ -47,9 +47,20 @@ public:
QVector<glm::vec3> vertices; QVector<glm::vec3> vertices;
QVector<glm::vec3> normals; QVector<glm::vec3> normals;
glm::vec3 pivot;
bool isEye;
QVector<FBXBlendshape> blendshapes; QVector<FBXBlendshape> blendshapes;
}; };
/// A set of meshes extracted from an FBX document.
class FBXGeometry {
public:
QVector<FBXMesh> meshes;
};
/// Parses the input from the supplied data as an FBX file. /// Parses the input from the supplied data as an FBX file.
/// \exception QString if an error occurs in parsing /// \exception QString if an error occurs in parsing
FBXNode parseFBX(const QByteArray& data); FBXNode parseFBX(const QByteArray& data);