mirror of
https://github.com/lubosz/overte.git
synced 2025-04-14 03:06:20 +02:00
More work on FBX reading.
This commit is contained in:
parent
1bd8ea947a
commit
09fa782c61
6 changed files with 226 additions and 65 deletions
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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';
|
||||
|
|
Loading…
Reference in a new issue