More work on FBX reading.

This commit is contained in:
Andrzej Kapolka 2013-09-19 15:05:17 -07:00
parent 1bd8ea947a
commit 09fa782c61
6 changed files with 226 additions and 65 deletions

View file

@ -139,6 +139,7 @@ void AvatarMixer::run() {
break;
case PACKET_TYPE_AVATAR_VOXEL_URL:
case PACKET_TYPE_AVATAR_FACE_VIDEO:
case PACKET_TYPE_AVATAR_FACE_URL:
// grab the node ID from the packet
unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID);
@ -158,4 +159,4 @@ void AvatarMixer::run() {
}
nodeList->stopSilentNodeRemovalThread();
}
}

View file

@ -11,7 +11,6 @@
#include "Application.h"
#include "BlendFace.h"
#include "Head.h"
#include "renderer/FBXReader.h"
using namespace fs;
using namespace std;
@ -48,32 +47,27 @@ bool BlendFace::render(float alpha) {
glColor4f(1.0f, 1.0f, 1.0f, alpha);
// start with the base
int vertexCount = _baseVertices.size();
int vertexCount = _geometry.vertices.size();
_blendedVertices.resize(vertexCount);
memcpy(_blendedVertices.data(), _baseVertices.data(), vertexCount * sizeof(fsVector3f));
memcpy(_blendedVertices.data(), _geometry.vertices.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 >= _blendshapes.size()) {
if (coefficient == 0.0f || i >= _geometry.blendshapes.size()) {
continue;
}
const fsVector3f* source = _blendshapes[i].m_vertices.data();
fsVector3f* dest = _blendedVertices.data();
for (int j = 0; j < vertexCount; j++) {
dest->x += source->x * coefficient;
dest->y += source->y * coefficient;
dest->z += source->z * coefficient;
source++;
dest++;
const glm::vec3* source = _geometry.blendshapes[i].vertices.constData();
for (const int* index = _geometry.blendshapes[i].indices.constData(),
*end = index + _geometry.blendshapes[i].indices.size(); index != end; index++, source++) {
_blendedVertices[*index] += *source * coefficient;
}
}
// update the blended vertices
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glBufferSubData(GL_ARRAY_BUFFER, 0, _blendedVertices.size() * sizeof(fsVector3f), _blendedVertices.data());
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData());
// tell OpenGL where to find vertex information
glEnableClientState(GL_VERTEX_ARRAY);
@ -82,9 +76,9 @@ bool BlendFace::render(float alpha) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID);
glDrawRangeElementsEXT(GL_QUADS, 0, _baseVertices.size() - 1, _quadIndexCount, GL_UNSIGNED_INT, 0);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, _baseVertices.size() - 1, _triangleIndexCount, GL_UNSIGNED_INT,
(void*)(_quadIndexCount * sizeof(int)));
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)));
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
@ -124,51 +118,42 @@ void BlendFace::setModelURL(const QUrl& url) {
connect(_modelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleModelReplyError()));
}
glm::vec3 createVec3(const fsVector3f& vector) {
return glm::vec3(vector.x, vector.y, vector.z);
}
void BlendFace::setRig(const fsMsgRig& rig) {
if (rig.mesh().m_tris.empty()) {
// clear any existing geometry
if (_iboID != 0) {
glDeleteBuffers(1, &_iboID);
glDeleteBuffers(1, &_vboID);
_iboID = 0;
// convert to FBX geometry
FBXGeometry geometry;
for (vector<fsVector4i>::const_iterator it = rig.mesh().m_quads.begin(), end = rig.mesh().m_quads.end(); it != end; it++) {
geometry.quadIndices.append(it->x);
geometry.quadIndices.append(it->y);
geometry.quadIndices.append(it->z);
geometry.quadIndices.append(it->w);
}
for (vector<fsVector3i>::const_iterator it = rig.mesh().m_tris.begin(), end = rig.mesh().m_tris.end(); it != end; it++) {
geometry.triangleIndices.append(it->x);
geometry.triangleIndices.append(it->y);
geometry.triangleIndices.append(it->z);
}
for (vector<fsVector3f>::const_iterator it = rig.mesh().m_vertex_data.m_vertices.begin(),
end = rig.mesh().m_vertex_data.m_vertices.end(); it != end; it++) {
geometry.vertices.append(glm::vec3(it->x, it->y, it->z));
}
for (vector<fsVertexData>::const_iterator it = rig.blendshapes().begin(), end = rig.blendshapes().end(); it != end; it++) {
FBXBlendshape blendshape;
for (int i = 0, n = it->m_vertices.size(); i < n; i++) {
blendshape.indices.append(i);
blendshape.vertices.append(createVec3(it->m_vertices[i]));
}
return;
geometry.blendshapes.append(blendshape);
}
if (_iboID == 0) {
glGenBuffers(1, &_iboID);
glGenBuffers(1, &_vboID);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, rig.mesh().m_quads.size() * sizeof(fsVector4i) +
rig.mesh().m_tris.size() * sizeof(fsVector3i), NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, rig.mesh().m_quads.size() * sizeof(fsVector4i), rig.mesh().m_quads.data());
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, rig.mesh().m_quads.size() * sizeof(fsVector4i),
rig.mesh().m_tris.size() * sizeof(fsVector3i), rig.mesh().m_tris.data());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glBufferData(GL_ARRAY_BUFFER, rig.mesh().m_vertex_data.m_vertices.size() * sizeof(fsVector3f), NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
_quadIndexCount = rig.mesh().m_quads.size() * 4;
_triangleIndexCount = rig.mesh().m_tris.size() * 3;
_baseVertices = rig.mesh().m_vertex_data.m_vertices;
_blendshapes = rig.blendshapes();
// subtract the neutral locations from the blend shapes; we want the deltas
int vertexCount = _baseVertices.size();
for (vector<fsVertexData>::iterator it = _blendshapes.begin(); it != _blendshapes.end(); it++) {
const fsVector3f* neutral = _baseVertices.data();
fsVector3f* offset = it->m_vertices.data();
for (int i = 0; i < vertexCount; i++) {
offset->x -= neutral->x;
offset->y -= neutral->y;
offset->z -= neutral->z;
neutral++;
offset++;
}
}
setGeometry(geometry);
}
void BlendFace::handleModelDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
@ -182,10 +167,11 @@ void BlendFace::handleModelDownloadProgress(qint64 bytesReceived, qint64 bytesTo
_modelReply = 0;
try {
printNode(parseFBX(entirety));
setGeometry(extractFBXGeometry(parseFBX(entirety)));
} catch (const QString& error) {
qDebug() << error << "\n";
return;
}
}
@ -197,3 +183,46 @@ void BlendFace::handleModelReplyError() {
_modelReply = 0;
}
void BlendFace::setGeometry(const FBXGeometry& geometry) {
if (geometry.vertices.isEmpty()) {
// clear any existing geometry
if (_iboID != 0) {
glDeleteBuffers(1, &_iboID);
glDeleteBuffers(1, &_vboID);
_iboID = 0;
}
return;
}
if (_iboID == 0) {
glGenBuffers(1, &_iboID);
glGenBuffers(1, &_vboID);
}
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() * sizeof(glm::vec3), NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
_geometry = geometry;
// subtract the neutral locations from the blend shapes; we want the deltas
/*
int vertexCount = geometry.vertices.size();
for (QVector<FBXBlendshape>::iterator it = _geometry.blendshapes.begin(); it != _geometry.blendshapes.end(); it++) {
const glm::vec3* neutral = geometry.vertices.constData();
glm::vec3* offset = it->vertices.data();
for (int i = 0; i < vertexCount; i++) {
*offset -= *neutral;
neutral++;
offset++;
}
}
*/
}

View file

@ -15,6 +15,7 @@
#include <fsbinarystream.h>
#include "InterfaceConfig.h"
#include "renderer/FBXReader.h"
class QNetworkReply;
@ -45,6 +46,8 @@ private slots:
private:
void setGeometry(const FBXGeometry& geometry);
Head* _owningHead;
QUrl _modelURL;
@ -54,11 +57,8 @@ private:
GLuint _iboID;
GLuint _vboID;
GLsizei _quadIndexCount;
GLsizei _triangleIndexCount;
std::vector<fs::fsVector3f> _baseVertices;
std::vector<fs::fsVertexData> _blendshapes;
std::vector<fs::fsVector3f> _blendedVertices;
FBXGeometry _geometry;
QVector<glm::vec3> _blendedVertices;
};
#endif /* defined(__interface__BlendFace__) */

View file

@ -14,6 +14,8 @@
#include "FBXReader.h"
using namespace std;
FBXNode parseFBX(const QByteArray& data) {
QBuffer buffer(const_cast<QByteArray*>(&data));
buffer.open(QIODevice::ReadOnly);
@ -154,6 +156,9 @@ FBXNode parseFBX(QIODevice* device) {
QDataStream in(device);
in.setByteOrder(QDataStream::LittleEndian);
// see http://code.blender.org/index.php/2013/08/fbx-binary-file-format-specification/ for an explanation
// of the FBX format
// verify the prolog
const QByteArray EXPECTED_PROLOG = "Kaydara FBX Binary ";
if (device->read(EXPECTED_PROLOG.size()) != EXPECTED_PROLOG) {
@ -179,6 +184,104 @@ FBXNode parseFBX(QIODevice* device) {
return top;
}
QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
QVector<glm::vec3> values;
for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) {
values.append(glm::vec3(*it++, *it++, *it++));
}
return values;
}
FBXGeometry extractFBXGeometry(const FBXNode& node) {
FBXGeometry geometry;
// "Objects"
// "Geometry" QVariant(qlonglong, 5386006736) QVariant(QByteArray, "QVariant(QByteArray, "Mesh")
// "Vertices" QVector<double>
// "PolygonVertexIndex" QVector<int>
// "LayerElementNormal"
// "Normals" QVector<double>
// "Geometry" QVariant(qlonglong, 5470612480) QVariant(QByteArray, "BrowsD_LQVariant(QByteArray, "Shape")
// "Vertices" QVector<double>
// "Normals" QVector<double>
foreach (const FBXNode& child, node.children) {
if (child.name == "Objects") {
foreach (const FBXNode& object, child.children) {
if (object.name == "Geometry") {
if (object.properties.at(2) == "Mesh") {
QVector<glm::vec3> vertices;
QVector<int> polygonIndices;
foreach (const FBXNode& data, object.children) {
if (data.name == "Vertices") {
geometry.vertices = createVec3Vector(data.properties.at(0).value<QVector<double> >());
} else if (data.name == "PolygonVertexIndex") {
polygonIndices = data.properties.at(0).value<QVector<int> >();
} else if (data.name == "LayerElementNormal") {
foreach (const FBXNode& subdata, data.children) {
if (subdata.name == "Normals") {
geometry.normals = createVec3Vector(
subdata.properties.at(0).value<QVector<double> >());
}
}
}
}
// convert the polygons to quads and triangles
for (const int* beginIndex = polygonIndices.constData(), *end = beginIndex + polygonIndices.size();
beginIndex != end; ) {
const int* endIndex = beginIndex;
while (*endIndex++ >= 0);
if (endIndex - beginIndex == 4) {
geometry.quadIndices.append(*beginIndex++);
geometry.quadIndices.append(*beginIndex++);
geometry.quadIndices.append(*beginIndex++);
geometry.quadIndices.append(-*beginIndex++ - 1);
} else {
for (const int* nextIndex = beginIndex + 1;; ) {
geometry.triangleIndices.append(*beginIndex);
geometry.triangleIndices.append(*nextIndex++);
if (*nextIndex >= 0) {
geometry.triangleIndices.append(*nextIndex);
} else {
geometry.triangleIndices.append(-*nextIndex - 1);
break;
}
}
beginIndex = endIndex;
}
}
} else { // object.properties.at(2) == "Shape"
FBXBlendshape blendshape;
foreach (const FBXNode& data, object.children) {
if (data.name == "Indexes") {
blendshape.indices = data.properties.at(0).value<QVector<int> >();
} else if (data.name == "Vertices") {
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> >());
}
}
// the name is followed by a null and some type info
QByteArray name = object.properties.at(1).toByteArray();
name = name.left(name.indexOf('\0'));
geometry.blendshapes.append(blendshape);
}
}
}
}
}
return geometry;
}
void printNode(const FBXNode& node, int indent) {
QByteArray spaces(indent, ' ');
qDebug("%s%s: ", spaces.data(), node.name.data());
@ -190,3 +293,4 @@ void printNode(const FBXNode& node, int indent) {
printNode(child, indent + 1);
}
}

View file

@ -12,6 +12,8 @@
#include <QVariant>
#include <QVector>
#include <glm/glm.hpp>
class QIODevice;
class FBXNode;
@ -27,6 +29,27 @@ public:
FBXNodeList children;
};
/// A single blendshape extracted from an FBX document.
class FBXBlendshape {
public:
QVector<int> indices;
QVector<glm::vec3> vertices;
QVector<glm::vec3> normals;
};
/// Base geometry with blendshapes mapped by name.
class FBXGeometry {
public:
QVector<int> quadIndices;
QVector<int> triangleIndices;
QVector<glm::vec3> vertices;
QVector<glm::vec3> normals;
QVector<FBXBlendshape> blendshapes;
};
/// Parses the input from the supplied data as an FBX file.
/// \exception QString if an error occurs in parsing
FBXNode parseFBX(const QByteArray& data);
@ -35,6 +58,9 @@ FBXNode parseFBX(const QByteArray& data);
/// \exception QString if an error occurs in parsing
FBXNode parseFBX(QIODevice* device);
/// Extracts the geometry from a parsed FBX node.
FBXGeometry extractFBXGeometry(const FBXNode& node);
void printNode(const FBXNode& node, int indent = 0);
#endif /* defined(__interface__FBXReader__) */

View file

@ -31,6 +31,7 @@ const PACKET_TYPE PACKET_TYPE_VOXEL_DATA_MONOCHROME = 'v';
const PACKET_TYPE PACKET_TYPE_BULK_AVATAR_DATA = 'X';
const PACKET_TYPE PACKET_TYPE_AVATAR_VOXEL_URL = 'U';
const PACKET_TYPE PACKET_TYPE_AVATAR_FACE_VIDEO = 'F';
const PACKET_TYPE PACKET_TYPE_AVATAR_FACE_URL = 'f';
const PACKET_TYPE PACKET_TYPE_TRANSMITTER_DATA_V2 = 'T';
const PACKET_TYPE PACKET_TYPE_ENVIRONMENT_DATA = 'e';
const PACKET_TYPE PACKET_TYPE_DOMAIN_LIST_REQUEST = 'L';