diff --git a/interface/src/avatar/BlendFace.cpp b/interface/src/avatar/BlendFace.cpp index 11882ca000..886184aafc 100644 --- a/interface/src/avatar/BlendFace.cpp +++ b/interface/src/avatar/BlendFace.cpp @@ -28,7 +28,7 @@ BlendFace::~BlendFace() { } bool BlendFace::render(float alpha) { - if (_baseMeshIDs.first == 0) { + if (_meshIDs.isEmpty()) { return false; } @@ -42,74 +42,63 @@ bool BlendFace::render(float alpha) { glScalef(_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE, -_owningHead->getScale() * MODEL_SCALE); - // start with the base - int vertexCount = _geometry.blendMesh.vertices.size(); - int normalCount = _geometry.blendMesh.normals.size(); - _blendedVertices.resize(vertexCount); - _blendedNormals.resize(normalCount); - memcpy(_blendedVertices.data(), _geometry.blendMesh.vertices.constData(), vertexCount * sizeof(glm::vec3)); - memcpy(_blendedNormals.data(), _geometry.blendMesh.normals.constData(), normalCount * sizeof(glm::vec3)); - - // blend in each coefficient - const vector& 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, _baseMeshIDs.second); - 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); - glVertexPointer(3, GL_FLOAT, 0, 0); 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 glEnable(GL_NORMALIZE); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _baseMeshIDs.first); - glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, _geometry.blendMesh.quadIndices.size(), GL_UNSIGNED_INT, 0); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, _geometry.blendMesh.triangleIndices.size(), GL_UNSIGNED_INT, - (void*)(_geometry.blendMesh.quadIndices.size() * sizeof(int))); - - glDisable(GL_NORMALIZE); - - // back to white for the other meshes - glColor4f(1.0f, 1.0f, 1.0f, alpha); + 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(); + + if (mesh.blendshapes.isEmpty()) { + glColor4f(1.0f, 1.0f, 1.0f, alpha); + + } else { + glColor4f(_owningHead->getSkinColor().r, _owningHead->getSkinColor().g, _owningHead->getSkinColor().b, alpha); + + _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& 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; + } + } - for (int i = 0; i < _geometry.otherMeshes.size(); i++) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _otherMeshIDs.at(i).first); - glBindBuffer(GL_ARRAY_BUFFER, _otherMeshIDs.at(i).second); + 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); - int vertexCount = _geometry.otherMeshes.at(i).vertices.size(); glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3))); - - glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, _geometry.otherMeshes.at(i).quadIndices.size(), - GL_UNSIGNED_INT, 0); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, _geometry.otherMeshes.at(i).triangleIndices.size(), - GL_UNSIGNED_INT, (void*)(_geometry.otherMeshes.at(i).quadIndices.size() * sizeof(int))); + 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))); } + glDisable(GL_NORMALIZE); + // deactivate vertex arrays after drawing glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); @@ -183,60 +172,51 @@ void BlendFace::handleModelReplyError() { _modelReply = 0; } -void createIndexBuffer(const FBXMesh& mesh, GLuint& iboID) { - glGenBuffers(1, &iboID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID); - 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); -} - void BlendFace::setGeometry(const FBXGeometry& geometry) { // clear any existing geometry deleteGeometry(); - if (geometry.blendMesh.vertices.isEmpty()) { + if (geometry.meshes.isEmpty()) { return; } - createIndexBuffer(geometry.blendMesh, _baseMeshIDs.first); - - glGenBuffers(1, &_baseMeshIDs.second); - glBindBuffer(GL_ARRAY_BUFFER, _baseMeshIDs.second); - glBufferData(GL_ARRAY_BUFFER, (geometry.blendMesh.vertices.size() + geometry.blendMesh.normals.size()) * sizeof(glm::vec3), - NULL, GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - foreach (const FBXMesh& mesh, geometry.otherMeshes) { + foreach (const FBXMesh& mesh, geometry.meshes) { VerticesIndices ids; - createIndexBuffer(mesh, ids.first); + 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); - 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()); + + 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); - _otherMeshIDs.append(ids); + _meshIDs.append(ids); } _geometry = geometry; } void BlendFace::deleteGeometry() { - if (_baseMeshIDs.first != 0) { - glDeleteBuffers(1, &_baseMeshIDs.first); - glDeleteBuffers(1, &_baseMeshIDs.second); - _baseMeshIDs = VerticesIndices(); - } - foreach (const VerticesIndices& meshIDs, _otherMeshIDs) { + foreach (const VerticesIndices& meshIDs, _meshIDs) { glDeleteBuffers(1, &meshIDs.first); glDeleteBuffers(1, &meshIDs.second); } - _otherMeshIDs.clear(); + _meshIDs.clear(); } diff --git a/interface/src/avatar/BlendFace.h b/interface/src/avatar/BlendFace.h index 821cda93ef..e9ce58e3c4 100644 --- a/interface/src/avatar/BlendFace.h +++ b/interface/src/avatar/BlendFace.h @@ -28,7 +28,7 @@ public: BlendFace(Head* owningHead); ~BlendFace(); - bool isActive() const { return _baseMeshIDs.first != 0; } + bool isActive() const { return !_meshIDs.isEmpty(); } bool render(float alpha); @@ -52,9 +52,7 @@ private: QNetworkReply* _modelReply; typedef QPair VerticesIndices; - - VerticesIndices _baseMeshIDs; - QVector _otherMeshIDs; + QVector _meshIDs; FBXGeometry _geometry; QVector _blendedVertices; diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index e370fae4b2..5219669193 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -259,10 +259,16 @@ QHash createBlendshapeMap() { } } +class ExtractedBlendshape { +public: + qint64 id; + int index; + FBXBlendshape blendshape; +}; + FBXGeometry extractFBXGeometry(const FBXNode& node) { - QVector blendshapes; - QHash meshMap; - qint64 blendshapeId = 0; + QHash meshes; + QVector blendshapes; QHash parentMap; foreach (const FBXNode& child, node.children) { @@ -323,31 +329,32 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) { beginIndex = endIndex; } } - meshMap.insert(object.properties.at(0).value(), mesh); + meshes.insert(object.properties.at(0).value(), mesh); } else { // object.properties.at(2) == "Shape" - FBXBlendshape blendshape; + ExtractedBlendshape extracted = { object.properties.at(0).value() }; + foreach (const FBXNode& data, object.children) { if (data.name == "Indexes") { - blendshape.indices = data.properties.at(0).value >(); + extracted.blendshape.indices = data.properties.at(0).value >(); } else if (data.name == "Vertices") { - blendshape.vertices = createVec3Vector(data.properties.at(0).value >()); + extracted.blendshape.vertices = createVec3Vector( + data.properties.at(0).value >()); } else if (data.name == "Normals") { - blendshape.normals = createVec3Vector(data.properties.at(0).value >()); + extracted.blendshape.normals = createVec3Vector( + data.properties.at(0).value >()); } } // the name is followed by a null and some type info QByteArray name = object.properties.at(1).toByteArray(); static QHash blendshapeMap = createBlendshapeMap(); - int index = blendshapeMap.value(name.left(name.indexOf('\0'))); - blendshapes.resize(qMax(blendshapes.size(), index + 1)); - blendshapes[index] = blendshape; + extracted.index = blendshapeMap.value(name.left(name.indexOf('\0'))); + + blendshapes.append(extracted); } - } else if (object.name == "Deformer" && object.properties.at(2) == "BlendShape") { - blendshapeId = object.properties.at(0).value(); } } } else if (child.name == "Connections") { @@ -359,13 +366,19 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) { } } - // get the mesh that owns the blendshape - FBXGeometry geometry; - geometry.blendMesh = meshMap.take(parentMap.value(blendshapeId)); - geometry.blendshapes = blendshapes; + // assign the blendshapes to their corresponding meshes + foreach (const ExtractedBlendshape& extracted, blendshapes) { + qint64 blendshapeChannelID = parentMap.value(extracted.id); + 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; + } - foreach (const FBXMesh& mesh, meshMap) { - geometry.otherMeshes.append(mesh); + FBXGeometry geometry; + foreach (const FBXMesh& mesh, meshes) { + geometry.meshes.append(mesh); } return geometry; diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index dfa972697f..db67c6b54a 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -29,16 +29,6 @@ public: FBXNodeList children; }; -/// A single mesh extracted from an FBX document. -class FBXMesh { -public: - - QVector quadIndices; - QVector triangleIndices; - QVector vertices; - QVector normals; -}; - /// A single blendshape extracted from an FBX document. class FBXBlendshape { public: @@ -48,14 +38,23 @@ public: QVector normals; }; -/// Base geometry with blendshapes mapped by name. -class FBXGeometry { +/// A single mesh (with optional blendshapes) extracted from an FBX document. +class FBXMesh { public: - FBXMesh blendMesh; - QVector blendshapes; + QVector quadIndices; + QVector triangleIndices; + QVector vertices; + QVector normals; - QVector otherMeshes; + QVector blendshapes; +}; + +/// A set of meshes extracted from an FBX document. +class FBXGeometry { +public: + + QVector meshes; }; /// Parses the input from the supplied data as an FBX file.