Added support for multiple blended meshes.

This commit is contained in:
Andrzej Kapolka 2013-09-24 13:53:19 -07:00
parent ad69a9547f
commit fd41a075ed
4 changed files with 120 additions and 130 deletions

View file

@ -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));
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
// enable normalization under the expectation that the GPU can do it faster
glEnable(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();
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<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()) {
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 = _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++) {
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;
}
}
// 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());
vertexCount * 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)));
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);
// back to white for the other meshes
glColor4f(1.0f, 1.0f, 1.0f, alpha);
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);
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)));
}
// 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);
void BlendFace::setGeometry(const FBXGeometry& geometry) {
// clear any existing geometry
deleteGeometry();
if (geometry.meshes.isEmpty()) {
return;
}
foreach (const FBXMesh& mesh, geometry.meshes) {
VerticesIndices ids;
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);
}
void BlendFace::setGeometry(const FBXGeometry& geometry) {
// clear any existing geometry
deleteGeometry();
if (geometry.blendMesh.vertices.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) {
VerticesIndices ids;
createIndexBuffer(mesh, ids.first);
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);
_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();
}

View file

@ -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<GLuint, GLuint> VerticesIndices;
VerticesIndices _baseMeshIDs;
QVector<VerticesIndices> _otherMeshIDs;
QVector<VerticesIndices> _meshIDs;
FBXGeometry _geometry;
QVector<glm::vec3> _blendedVertices;

View file

@ -259,10 +259,16 @@ QHash<QByteArray, int> createBlendshapeMap() {
}
}
class ExtractedBlendshape {
public:
qint64 id;
int index;
FBXBlendshape blendshape;
};
FBXGeometry extractFBXGeometry(const FBXNode& node) {
QVector<FBXBlendshape> blendshapes;
QHash<qint64, FBXMesh> meshMap;
qint64 blendshapeId = 0;
QHash<qint64, FBXMesh> meshes;
QVector<ExtractedBlendshape> blendshapes;
QHash<qint64, qint64> 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<qint64>(), mesh);
meshes.insert(object.properties.at(0).value<qint64>(), mesh);
} else { // object.properties.at(2) == "Shape"
FBXBlendshape blendshape;
ExtractedBlendshape extracted = { object.properties.at(0).value<qint64>() };
foreach (const FBXNode& data, object.children) {
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") {
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") {
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
QByteArray name = object.properties.at(1).toByteArray();
static QHash<QByteArray, int> 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<qint64>();
}
}
} 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;

View file

@ -29,16 +29,6 @@ public:
FBXNodeList children;
};
/// A single mesh extracted from an FBX document.
class FBXMesh {
public:
QVector<int> quadIndices;
QVector<int> triangleIndices;
QVector<glm::vec3> vertices;
QVector<glm::vec3> normals;
};
/// A single blendshape extracted from an FBX document.
class FBXBlendshape {
public:
@ -48,14 +38,23 @@ public:
QVector<glm::vec3> normals;
};
/// Base geometry with blendshapes mapped by name.
/// A single mesh (with optional blendshapes) extracted from an FBX document.
class FBXMesh {
public:
QVector<int> quadIndices;
QVector<int> triangleIndices;
QVector<glm::vec3> vertices;
QVector<glm::vec3> normals;
QVector<FBXBlendshape> blendshapes;
};
/// A set of meshes extracted from an FBX document.
class FBXGeometry {
public:
FBXMesh blendMesh;
QVector<FBXBlendshape> blendshapes;
QVector<FBXMesh> otherMeshes;
QVector<FBXMesh> meshes;
};
/// Parses the input from the supplied data as an FBX file.