mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 15:30:38 +02:00
Merge pull request #976 from ey6es/blendface
Include other meshes (eyes, teeth) from the FBX files.
This commit is contained in:
commit
2db8985c63
6 changed files with 221 additions and 109 deletions
|
@ -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,59 +37,84 @@ 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,
|
for (int i = 0; i < _meshIDs.size(); i++) {
|
||||||
(void*)(_geometry.quadIndices.size() * sizeof(int)));
|
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);
|
glDisable(GL_NORMALIZE);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID);
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (mesh.quadIndices.size() + mesh.triangleIndices.size()) * sizeof(int),
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (geometry.quadIndices.size() + geometry.triangleIndices.size()) * sizeof(int),
|
NULL, GL_STATIC_DRAW);
|
||||||
NULL, GL_STATIC_DRAW);
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mesh.quadIndices.size() * sizeof(int), mesh.quadIndices.constData());
|
||||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, geometry.quadIndices.size() * sizeof(int), geometry.quadIndices.constData());
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, mesh.quadIndices.size() * sizeof(int),
|
||||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, geometry.quadIndices.size() * sizeof(int),
|
mesh.triangleIndices.size() * sizeof(int), mesh.triangleIndices.constData());
|
||||||
geometry.triangleIndices.size() * sizeof(int), geometry.triangleIndices.constData());
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
|
glGenBuffers(1, &ids.second);
|
||||||
glBufferData(GL_ARRAY_BUFFER, (geometry.vertices.size() + geometry.normals.size()) * sizeof(glm::vec3),
|
glBindBuffer(GL_ARRAY_BUFFER, ids.second);
|
||||||
NULL, GL_DYNAMIC_DRAW);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
_geometry = geometry;
|
_geometry = geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlendFace::deleteGeometry() {
|
||||||
|
foreach (const VerticesIndices& meshIDs, _meshIDs) {
|
||||||
|
glDeleteBuffers(1, &meshIDs.first);
|
||||||
|
glDeleteBuffers(1, &meshIDs.second);
|
||||||
|
}
|
||||||
|
_meshIDs.clear();
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue