mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 01:44:21 +02:00
Added support for multiple blended meshes.
This commit is contained in:
parent
ad69a9547f
commit
fd41a075ed
4 changed files with 120 additions and 130 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue