Switched from loading FBX and FST simultaneously to loading FST first, then

using its filename and texdir properties (as Faceshift does) to locate the
corresponding model.  Also fixed a bug with the fallback models (they were
invisible after second failure).
This commit is contained in:
Andrzej Kapolka 2014-02-11 14:20:52 -08:00
parent 803d0fd7c7
commit b6d77ec637
13 changed files with 191 additions and 197 deletions

View file

Before

Width:  |  Height:  |  Size: 330 KiB

After

Width:  |  Height:  |  Size: 330 KiB

View file

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 142 KiB

View file

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -1,3 +1,5 @@
filename=defaultAvatar/body.fbx
texdir=defaultAvatar
scale=130 scale=130
joint = jointRoot = jointRoot joint = jointRoot = jointRoot
joint = jointLean = jointSpine joint = jointLean = jointSpine

View file

@ -1,7 +1,7 @@
# faceshift target mapping file # faceshift target mapping file
name= defaultAvatar_head name= defaultAvatar_head
filename=../../../Avatars/Jelly/jellyrob_blue.fbx filename=defaultAvatar/head.fbx
texdir=../../../Avatars/Jelly texdir=defaultAvatar
scale=80 scale=80
rx=0 rx=0
ry=0 ry=0

View file

@ -345,13 +345,13 @@ bool Avatar::findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, floa
void Avatar::setFaceModelURL(const QUrl &faceModelURL) { void Avatar::setFaceModelURL(const QUrl &faceModelURL) {
AvatarData::setFaceModelURL(faceModelURL); AvatarData::setFaceModelURL(faceModelURL);
const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_head.fbx"); const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_head.fst");
_head.getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL); _head.getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL);
} }
void Avatar::setSkeletonModelURL(const QUrl &skeletonModelURL) { void Avatar::setSkeletonModelURL(const QUrl &skeletonModelURL) {
AvatarData::setSkeletonModelURL(skeletonModelURL); AvatarData::setSkeletonModelURL(skeletonModelURL);
const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_body.fbx"); const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_body.fst");
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL); _skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL);
} }

View file

@ -1568,14 +1568,16 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
return geometry; return geometry;
} }
FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping) { QVariantHash readMapping(const QByteArray& data) {
QBuffer modelBuffer(const_cast<QByteArray*>(&model)); QBuffer buffer(const_cast<QByteArray*>(&data));
modelBuffer.open(QIODevice::ReadOnly); buffer.open(QIODevice::ReadOnly);
return parseMapping(&buffer);
}
QBuffer mappingBuffer(const_cast<QByteArray*>(&mapping)); FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping) {
mappingBuffer.open(QIODevice::ReadOnly); QBuffer buffer(const_cast<QByteArray*>(&model));
buffer.open(QIODevice::ReadOnly);
return extractFBXGeometry(parseFBX(&modelBuffer), parseMapping(&mappingBuffer)); return extractFBXGeometry(parseFBX(&buffer), mapping);
} }
bool addMeshVoxelsOperation(OctreeElement* element, void* extraData) { bool addMeshVoxelsOperation(OctreeElement* element, void* extraData) {

View file

@ -163,9 +163,12 @@ public:
QVector<FBXAttachment> attachments; QVector<FBXAttachment> attachments;
}; };
/// Reads an FST mapping from the supplied data.
QVariantHash readMapping(const QByteArray& data);
/// Reads FBX geometry from the supplied model and mapping data. /// Reads FBX geometry from the supplied model and mapping data.
/// \exception QString if an error occurs in parsing /// \exception QString if an error occurs in parsing
FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping); FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping);
/// Reads SVO geometry from the supplied model data. /// Reads SVO geometry from the supplied model data.
FBXGeometry readSVO(const QByteArray& model); FBXGeometry readSVO(const QByteArray& model);

View file

@ -7,11 +7,7 @@
#include <cmath> #include <cmath>
// include this before QOpenGLBuffer, which includes an earlier version of OpenGL
#include "InterfaceConfig.h"
#include <QNetworkReply> #include <QNetworkReply>
#include <QOpenGLBuffer>
#include <QTimer> #include <QTimer>
#include "Application.h" #include "Application.h"
@ -304,40 +300,23 @@ QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, cons
} }
NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback) : NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback) :
_modelRequest(url), _request(url),
_modelReply(NULL), _reply(NULL),
_mappingReply(NULL), _textureBase(url),
_fallback(fallback), _fallback(fallback),
_attempts(0) _attempts(0) {
{
if (!url.isValid()) { if (!url.isValid()) {
return; return;
} }
_modelRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); _request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
makeModelRequest(); makeRequest();
QUrl mappingURL = url;
QString path = url.path();
mappingURL.setPath(path.left(path.lastIndexOf('.')) + ".fst");
QNetworkRequest mappingRequest(mappingURL);
mappingRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
_mappingReply = Application::getInstance()->getNetworkAccessManager()->get(mappingRequest);
connect(_mappingReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(maybeReadModelWithMapping()));
connect(_mappingReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleMappingReplyError()));
} }
NetworkGeometry::~NetworkGeometry() { NetworkGeometry::~NetworkGeometry() {
if (_modelReply != NULL) { if (_reply != NULL) {
delete _modelReply; delete _reply;
} }
if (_mappingReply != NULL) {
delete _mappingReply;
}
foreach (const NetworkMesh& mesh, _meshes) {
glDeleteBuffers(1, &mesh.indexBufferID);
glDeleteBuffers(1, &mesh.vertexBufferID);
}
} }
glm::vec4 NetworkGeometry::computeAverageColor() const { glm::vec4 NetworkGeometry::computeAverageColor() const {
@ -364,20 +343,155 @@ glm::vec4 NetworkGeometry::computeAverageColor() const {
return (totalTriangles == 0) ? glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) : totalColor / totalTriangles; return (totalTriangles == 0) ? glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) : totalColor / totalTriangles;
} }
void NetworkGeometry::makeModelRequest() { void NetworkGeometry::makeRequest() {
_modelReply = Application::getInstance()->getNetworkAccessManager()->get(_modelRequest); _reply = Application::getInstance()->getNetworkAccessManager()->get(_request);
connect(_modelReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(maybeReadModelWithMapping())); connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
connect(_modelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleModelReplyError())); connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
} }
void NetworkGeometry::handleModelReplyError() { void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
QDebug debug = qDebug() << _modelReply->errorString(); if (!_reply->isFinished()) {
return;
}
QNetworkReply::NetworkError error = _modelReply->error(); QUrl url = _reply->url();
_modelReply->disconnect(this); QByteArray data = _reply->readAll();
_modelReply->deleteLater(); _reply->disconnect(this);
_modelReply = NULL; _reply->deleteLater();
_reply = NULL;
if (url.path().toLower().endsWith(".fst")) {
// it's a mapping file; parse it and get the mesh filename
_mapping = readMapping(data);
QString filename = _mapping.value("filename").toString();
if (filename.isNull()) {
qDebug() << "Mapping file " << url << " has no filename.";
maybeLoadFallback();
} else {
QString texdir = _mapping.value("texdir").toString();
if (!texdir.isNull()) {
if (!texdir.endsWith('/')) {
texdir += '/';
}
_textureBase = url.resolved(texdir);
}
_request.setUrl(url.resolved(filename));
makeRequest();
}
return;
}
try {
_geometry = url.path().toLower().endsWith(".svo") ? readSVO(data) : readFBX(data, _mapping);
} catch (const QString& error) {
qDebug() << "Error reading " << url << ": " << error;
maybeLoadFallback();
return;
}
foreach (const FBXMesh& mesh, _geometry.meshes) {
NetworkMesh networkMesh = { QOpenGLBuffer(QOpenGLBuffer::IndexBuffer), QOpenGLBuffer(QOpenGLBuffer::VertexBuffer) };
int totalIndices = 0;
foreach (const FBXMeshPart& part, mesh.parts) {
NetworkMeshPart networkPart;
if (!part.diffuseFilename.isEmpty()) {
networkPart.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(
_textureBase.resolved(QUrl(part.diffuseFilename)), false, mesh.isEye);
}
if (!part.normalFilename.isEmpty()) {
networkPart.normalTexture = Application::getInstance()->getTextureCache()->getTexture(
_textureBase.resolved(QUrl(part.normalFilename)), true);
}
networkMesh.parts.append(networkPart);
totalIndices += (part.quadIndices.size() + part.triangleIndices.size());
}
networkMesh.indexBuffer.create();
networkMesh.indexBuffer.bind();
networkMesh.indexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
networkMesh.indexBuffer.allocate(totalIndices * sizeof(int));
int offset = 0;
foreach (const FBXMeshPart& part, mesh.parts) {
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.quadIndices.size() * sizeof(int),
part.quadIndices.constData());
offset += part.quadIndices.size() * sizeof(int);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.triangleIndices.size() * sizeof(int),
part.triangleIndices.constData());
offset += part.triangleIndices.size() * sizeof(int);
}
networkMesh.indexBuffer.release();
networkMesh.vertexBuffer.create();
networkMesh.vertexBuffer.bind();
networkMesh.vertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
// if we don't need to do any blending or springing, then the positions/normals can be static
if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) {
int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3);
int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3);
int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3);
int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4);
networkMesh.vertexBuffer.allocate(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4));
networkMesh.vertexBuffer.write(0, mesh.vertices.constData(), mesh.vertices.size() * sizeof(glm::vec3));
networkMesh.vertexBuffer.write(normalsOffset, mesh.normals.constData(), mesh.normals.size() * sizeof(glm::vec3));
networkMesh.vertexBuffer.write(tangentsOffset, mesh.tangents.constData(),
mesh.tangents.size() * sizeof(glm::vec3));
networkMesh.vertexBuffer.write(colorsOffset, mesh.colors.constData(), mesh.colors.size() * sizeof(glm::vec3));
networkMesh.vertexBuffer.write(texCoordsOffset, mesh.texCoords.constData(),
mesh.texCoords.size() * sizeof(glm::vec2));
networkMesh.vertexBuffer.write(clusterIndicesOffset, mesh.clusterIndices.constData(),
mesh.clusterIndices.size() * sizeof(glm::vec4));
networkMesh.vertexBuffer.write(clusterWeightsOffset, mesh.clusterWeights.constData(),
mesh.clusterWeights.size() * sizeof(glm::vec4));
// if there's no springiness, then the cluster indices/weights can be static
} else if (mesh.springiness == 0.0f) {
int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3);
int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4);
networkMesh.vertexBuffer.allocate(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4));
networkMesh.vertexBuffer.write(0, mesh.tangents.constData(), mesh.tangents.size() * sizeof(glm::vec3));
networkMesh.vertexBuffer.write(colorsOffset, mesh.colors.constData(), mesh.colors.size() * sizeof(glm::vec3));
networkMesh.vertexBuffer.write(texCoordsOffset, mesh.texCoords.constData(),
mesh.texCoords.size() * sizeof(glm::vec2));
networkMesh.vertexBuffer.write(clusterIndicesOffset, mesh.clusterIndices.constData(),
mesh.clusterIndices.size() * sizeof(glm::vec4));
networkMesh.vertexBuffer.write(clusterWeightsOffset, mesh.clusterWeights.constData(),
mesh.clusterWeights.size() * sizeof(glm::vec4));
} else {
int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3);
int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
networkMesh.vertexBuffer.allocate(texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2));
networkMesh.vertexBuffer.write(0, mesh.tangents.constData(), mesh.tangents.size() * sizeof(glm::vec3));
networkMesh.vertexBuffer.write(colorsOffset, mesh.colors.constData(), mesh.colors.size() * sizeof(glm::vec3));
networkMesh.vertexBuffer.write(texCoordsOffset, mesh.texCoords.constData(),
mesh.texCoords.size() * sizeof(glm::vec2));
}
networkMesh.vertexBuffer.release();
_meshes.append(networkMesh);
}
emit loaded();
}
void NetworkGeometry::handleReplyError() {
QDebug debug = qDebug() << _reply->errorString();
QNetworkReply::NetworkError error = _reply->error();
_reply->disconnect(this);
_reply->deleteLater();
_reply = NULL;
// retry for certain types of failures // retry for certain types of failures
switch (error) { switch (error) {
@ -394,7 +508,7 @@ void NetworkGeometry::handleModelReplyError() {
const int MAX_ATTEMPTS = 8; const int MAX_ATTEMPTS = 8;
const int BASE_DELAY_MS = 1000; const int BASE_DELAY_MS = 1000;
if (++_attempts < MAX_ATTEMPTS) { if (++_attempts < MAX_ATTEMPTS) {
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeModelRequest())); QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeRequest()));
debug << " -- retrying..."; debug << " -- retrying...";
return; return;
} }
@ -407,135 +521,6 @@ void NetworkGeometry::handleModelReplyError() {
} }
void NetworkGeometry::handleMappingReplyError() {
_mappingReply->disconnect(this);
_mappingReply->deleteLater();
_mappingReply = NULL;
maybeReadModelWithMapping();
}
void NetworkGeometry::maybeReadModelWithMapping() {
if (_modelReply == NULL || !_modelReply->isFinished() || (_mappingReply != NULL && !_mappingReply->isFinished())) {
return;
}
QUrl url = _modelReply->url();
QByteArray model = _modelReply->readAll();
_modelReply->disconnect(this);
_modelReply->deleteLater();
_modelReply = NULL;
QByteArray mapping;
if (_mappingReply != NULL) {
mapping = _mappingReply->readAll();
_mappingReply->disconnect(this);
_mappingReply->deleteLater();
_mappingReply = NULL;
}
try {
_geometry = url.path().toLower().endsWith(".svo") ? readSVO(model) : readFBX(model, mapping);
} catch (const QString& error) {
qDebug() << "Error reading " << url << ": " << error;
maybeLoadFallback();
return;
}
foreach (const FBXMesh& mesh, _geometry.meshes) {
NetworkMesh networkMesh;
int totalIndices = 0;
foreach (const FBXMeshPart& part, mesh.parts) {
NetworkMeshPart networkPart;
QString basePath = url.path();
basePath = basePath.left(basePath.lastIndexOf('/') + 1);
if (!part.diffuseFilename.isEmpty()) {
url.setPath(basePath + part.diffuseFilename);
networkPart.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(url, false, mesh.isEye);
}
if (!part.normalFilename.isEmpty()) {
url.setPath(basePath + part.normalFilename);
networkPart.normalTexture = Application::getInstance()->getTextureCache()->getTexture(url, true);
}
networkMesh.parts.append(networkPart);
totalIndices += (part.quadIndices.size() + part.triangleIndices.size());
}
glGenBuffers(1, &networkMesh.indexBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndices * sizeof(int), NULL, GL_STATIC_DRAW);
int offset = 0;
foreach (const FBXMeshPart& part, mesh.parts) {
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.quadIndices.size() * sizeof(int),
part.quadIndices.constData());
offset += part.quadIndices.size() * sizeof(int);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.triangleIndices.size() * sizeof(int),
part.triangleIndices.constData());
offset += part.triangleIndices.size() * sizeof(int);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glGenBuffers(1, &networkMesh.vertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID);
// if we don't need to do any blending or springing, then the positions/normals can be static
if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) {
int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3);
int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3);
int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3);
int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4);
glBufferData(GL_ARRAY_BUFFER, clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4),
NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.vertices.size() * sizeof(glm::vec3), mesh.vertices.constData());
glBufferSubData(GL_ARRAY_BUFFER, normalsOffset, mesh.normals.size() * sizeof(glm::vec3), mesh.normals.constData());
glBufferSubData(GL_ARRAY_BUFFER, tangentsOffset, mesh.tangents.size() * sizeof(glm::vec3), mesh.tangents.constData());
glBufferSubData(GL_ARRAY_BUFFER, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData());
glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2),
mesh.texCoords.constData());
glBufferSubData(GL_ARRAY_BUFFER, clusterIndicesOffset, mesh.clusterIndices.size() * sizeof(glm::vec4),
mesh.clusterIndices.constData());
glBufferSubData(GL_ARRAY_BUFFER, clusterWeightsOffset, mesh.clusterWeights.size() * sizeof(glm::vec4),
mesh.clusterWeights.constData());
// if there's no springiness, then the cluster indices/weights can be static
} else if (mesh.springiness == 0.0f) {
int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3);
int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2);
int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4);
glBufferData(GL_ARRAY_BUFFER, clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4),
NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.tangents.size() * sizeof(glm::vec3), mesh.tangents.constData());
glBufferSubData(GL_ARRAY_BUFFER, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData());
glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData());
glBufferSubData(GL_ARRAY_BUFFER, clusterIndicesOffset, mesh.clusterIndices.size() * sizeof(glm::vec4),
mesh.clusterIndices.constData());
glBufferSubData(GL_ARRAY_BUFFER, clusterWeightsOffset, mesh.clusterWeights.size() * sizeof(glm::vec4),
mesh.clusterWeights.constData());
} else {
int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3);
int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3);
glBufferData(GL_ARRAY_BUFFER, texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2), NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.tangents.size() * sizeof(glm::vec3), mesh.tangents.constData());
glBufferSubData(GL_ARRAY_BUFFER, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData());
glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2),
mesh.texCoords.constData());
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
_meshes.append(networkMesh);
}
emit loaded();
}
void NetworkGeometry::loadFallback() { void NetworkGeometry::loadFallback() {
_geometry = _fallback->_geometry; _geometry = _fallback->_geometry;
_meshes = _fallback->_meshes; _meshes = _fallback->_meshes;

View file

@ -9,17 +9,19 @@
#ifndef __interface__GeometryCache__ #ifndef __interface__GeometryCache__
#define __interface__GeometryCache__ #define __interface__GeometryCache__
// include this before QOpenGLBuffer, which includes an earlier version of OpenGL
#include "InterfaceConfig.h"
#include <QHash> #include <QHash>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QObject> #include <QObject>
#include <QOpenGLBuffer>
#include <QSharedPointer> #include <QSharedPointer>
#include <QWeakPointer> #include <QWeakPointer>
#include "FBXReader.h" #include "FBXReader.h"
#include "InterfaceConfig.h"
class QNetworkReply; class QNetworkReply;
class QOpenGLBuffer;
class NetworkGeometry; class NetworkGeometry;
class NetworkMesh; class NetworkMesh;
@ -76,19 +78,19 @@ signals:
private slots: private slots:
void makeModelRequest(); void makeRequest();
void handleModelReplyError(); void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void handleMappingReplyError(); void handleReplyError();
void maybeReadModelWithMapping();
void loadFallback(); void loadFallback();
private: private:
void maybeLoadFallback(); void maybeLoadFallback();
QNetworkRequest _modelRequest; QNetworkRequest _request;
QNetworkReply* _modelReply; QNetworkReply* _reply;
QNetworkReply* _mappingReply; QVariantHash _mapping;
QUrl _textureBase;
QSharedPointer<NetworkGeometry> _fallback; QSharedPointer<NetworkGeometry> _fallback;
int _attempts; int _attempts;
@ -110,8 +112,8 @@ public:
class NetworkMesh { class NetworkMesh {
public: public:
GLuint indexBufferID; QOpenGLBuffer indexBuffer;
GLuint vertexBufferID; QOpenGLBuffer vertexBuffer;
QVector<NetworkMeshPart> parts; QVector<NetworkMeshPart> parts;

View file

@ -768,13 +768,13 @@ void Model::renderMeshes(float alpha, bool translucent) {
(networkMesh.getTranslucentPartCount() == networkMesh.parts.size())) { (networkMesh.getTranslucentPartCount() == networkMesh.parts.size())) {
continue; continue;
} }
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID); const_cast<QOpenGLBuffer&>(networkMesh.indexBuffer).bind();
const FBXMesh& mesh = geometry.meshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i);
int vertexCount = mesh.vertices.size(); int vertexCount = mesh.vertices.size();
glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); const_cast<QOpenGLBuffer&>(networkMesh.vertexBuffer).bind();
ProgramObject* program = &_program; ProgramObject* program = &_program;
ProgramObject* skinProgram = &_skinProgram; ProgramObject* skinProgram = &_skinProgram;
SkinLocations* skinLocations = &_skinLocations; SkinLocations* skinLocations = &_skinLocations;