Basic geometry sharing/texture loading.

This commit is contained in:
Andrzej Kapolka 2013-10-01 10:50:09 -07:00
parent 19e20f64b4
commit 3c3cf0b002
8 changed files with 338 additions and 148 deletions

View file

@ -16,8 +16,7 @@ using namespace fs;
using namespace std;
BlendFace::BlendFace(Head* owningHead) :
_owningHead(owningHead),
_modelReply(NULL)
_owningHead(owningHead)
{
// we may have been created in the network thread, but we live in the main thread
moveToThread(Application::getInstance()->thread());
@ -47,10 +46,27 @@ const glm::vec3 MODEL_TRANSLATION(0.0f, -0.07f, -0.025f); // temporary fudge fac
const float MODEL_SCALE = 0.0006f;
bool BlendFace::render(float alpha) {
if (_meshIDs.isEmpty()) {
if (!isActive()) {
return false;
}
// set up blended buffer ids on first render after load
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
if (_blendedVertexBufferIDs.isEmpty()) {
foreach (const FBXMesh& mesh, geometry.meshes) {
GLuint id = 0;
if (!mesh.blendshapes.isEmpty()) {
glGenBuffers(1, &id);
glBindBuffer(GL_ARRAY_BUFFER, id);
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3),
NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
_blendedVertexBufferIDs.append(id);
}
}
glPushMatrix();
glTranslatef(_owningHead->getPosition().x, _owningHead->getPosition().y, _owningHead->getPosition().z);
glm::quat orientation = _owningHead->getOrientation();
@ -61,22 +77,23 @@ bool BlendFace::render(float alpha) {
-_owningHead->getScale() * MODEL_SCALE);
glScalef(scale.x, scale.y, scale.z);
glTranslatef(-_geometry.neckPivot.x, -_geometry.neckPivot.y, -_geometry.neckPivot.z);
glTranslatef(-geometry.neckPivot.x, -geometry.neckPivot.y, -geometry.neckPivot.z);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// enable normalization under the expectation that the GPU can do it faster
glEnable(GL_NORMALIZE);
glEnable(GL_TEXTURE_2D);
glColor4f(_owningHead->getSkinColor().r, _owningHead->getSkinColor().g, _owningHead->getSkinColor().b, alpha);
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);
for (int i = 0; i < networkMeshes.size(); i++) {
const NetworkMesh& networkMesh = networkMeshes.at(i);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID);
const FBXMesh& mesh = _geometry.meshes.at(i);
const FBXMesh& mesh = geometry.meshes.at(i);
int vertexCount = mesh.vertices.size();
glPushMatrix();
@ -90,14 +107,6 @@ bool BlendFace::render(float alpha) {
glRotatef(glm::angle(rotation), -rotationAxis.x, rotationAxis.y, -rotationAxis.z);
glTranslatef(-mesh.pivot.x, -mesh.pivot.y, -mesh.pivot.z);
// use texture coordinates only for the eye, for now
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
_eyeTexture = _eyeTextureCache.getTexture(_owningHead->getPupilDilation());
glBindTexture(GL_TEXTURE_2D, _eyeTexture->getID());
glEnable(GL_TEXTURE_2D);
_eyeProgram.bind();
}
@ -108,7 +117,15 @@ bool BlendFace::render(float alpha) {
glColor4f(1.0f, 1.0f, 1.0f, alpha);
}
if (!mesh.blendshapes.isEmpty()) {
glBindTexture(GL_TEXTURE_2D, networkMesh.diffuseTexture ? networkMesh.diffuseTexture->getID() : 0);
glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID);
if (mesh.blendshapes.isEmpty()) {
glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3)));
} else {
glTexCoordPointer(2, GL_FLOAT, 0, 0);
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
_blendedNormals.resize(_blendedVertices.size());
memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3));
@ -116,68 +133,73 @@ bool BlendFace::render(float alpha) {
// 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()) {
for (int j = 0; j < coefficients.size(); j++) {
float coefficient = coefficients[j];
if (coefficient == 0.0f || i >= mesh.blendshapes.size() || mesh.blendshapes[j].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++) {
const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData();
const glm::vec3* normal = mesh.blendshapes[j].normals.constData();
for (const int* index = mesh.blendshapes[j].indices.constData(),
*end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++, normal++) {
_blendedVertices[*index] += *vertex * coefficient;
_blendedNormals[*index] += *normal * normalCoefficient;
}
}
glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i));
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)));
glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * 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) {
_eyeProgram.release();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
glPopMatrix();
}
glDisable(GL_NORMALIZE);
glDisable(GL_TEXTURE_2D);
// deactivate vertex arrays after drawing
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
// bind with 0 to switch back to normal operation
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glPopMatrix();
return true;
}
void BlendFace::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
if (!isActive()) {
return;
}
glm::quat orientation = _owningHead->getOrientation();
glm::vec3 scale(-_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE,
-_owningHead->getScale() * MODEL_SCALE);
bool foundFirst = false;
foreach (const FBXMesh& mesh, _geometry.meshes) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
foreach (const FBXMesh& mesh, geometry.meshes) {
if (mesh.isEye) {
glm::vec3 position = orientation * ((mesh.pivot - _geometry.neckPivot) * scale + MODEL_TRANSLATION) +
glm::vec3 position = orientation * ((mesh.pivot - geometry.neckPivot) * scale + MODEL_TRANSLATION) +
_owningHead->getPosition();
if (foundFirst) {
secondEyePosition = position;
@ -190,112 +212,21 @@ void BlendFace::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEy
}
void BlendFace::setModelURL(const QUrl& url) {
// don't restart the download if it's the same URL
// don't recreate the geometry if it's the same URL
if (_modelURL == url) {
return;
}
// cancel any current download
if (_modelReply != 0) {
delete _modelReply;
_modelReply = 0;
}
// clear the current geometry, if any
setGeometry(FBXGeometry());
// remember the URL
_modelURL = url;
// load the URL data asynchronously
if (!url.isValid()) {
return;
}
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
_modelReply = Application::getInstance()->getNetworkAccessManager()->get(request);
connect(_modelReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleModelDownloadProgress(qint64,qint64)));
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::handleModelDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
if (bytesReceived < bytesTotal && !_modelReply->isFinished()) {
return;
}
QByteArray entirety = _modelReply->readAll();
_modelReply->disconnect(this);
_modelReply->deleteLater();
_modelReply = 0;
try {
setGeometry(extractFBXGeometry(parseFBX(entirety)));
} catch (const QString& error) {
qDebug() << error << "\n";
return;
}
}
void BlendFace::handleModelReplyError() {
qDebug("%s\n", _modelReply->errorString().toLocal8Bit().constData());
_modelReply->disconnect(this);
_modelReply->deleteLater();
_modelReply = 0;
}
void BlendFace::setGeometry(const FBXGeometry& geometry) {
// clear any existing geometry
// delete our local 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);
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) +
mesh.texCoords.size() * sizeof(glm::vec2), 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());
glBufferSubData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3),
mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.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 = Application::getInstance()->getGeometryCache()->getGeometry(url);
}
void BlendFace::deleteGeometry() {
foreach (const VerticesIndices& meshIDs, _meshIDs) {
glDeleteBuffers(1, &meshIDs.first);
glDeleteBuffers(1, &meshIDs.second);
foreach (GLuint id, _blendedVertexBufferIDs) {
glDeleteBuffers(1, &id);
}
_meshIDs.clear();
_blendedVertexBufferIDs.clear();
}

View file

@ -13,7 +13,7 @@
#include <QUrl>
#include "InterfaceConfig.h"
#include "renderer/FBXReader.h"
#include "renderer/GeometryCache.h"
#include "renderer/ProgramObject.h"
#include "renderer/TextureCache.h"
@ -30,7 +30,7 @@ public:
BlendFace(Head* owningHead);
~BlendFace();
bool isActive() const { return !_meshIDs.isEmpty(); }
bool isActive() const { return _geometry && _geometry->isLoaded(); }
void init();
bool render(float alpha);
@ -39,27 +39,19 @@ public:
const QUrl& getModelURL() const { return _modelURL; }
void getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
private slots:
void handleModelDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void handleModelReplyError();
private:
void setGeometry(const FBXGeometry& geometry);
void deleteGeometry();
Head* _owningHead;
QUrl _modelURL;
QNetworkReply* _modelReply;
typedef QPair<GLuint, GLuint> VerticesIndices;
QVector<VerticesIndices> _meshIDs;
QSharedPointer<NetworkGeometry> _geometry;
QVector<GLuint> _blendedVertexBufferIDs;
FBXGeometry _geometry;
QVector<glm::vec3> _blendedVertices;
QVector<glm::vec3> _blendedNormals;

View file

@ -205,7 +205,7 @@ QVector<glm::vec2> createVec2Vector(const QVector<double>& doubleVector) {
for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) {
float s = *it++;
float t = *it++;
values.append(glm::vec2(s, t));
values.append(glm::vec2(s, -t));
}
return values;
}
@ -316,6 +316,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
QMultiHash<qint64, qint64> childMap;
QHash<qint64, glm::mat4> localTransforms;
QHash<qint64, glm::mat4> transformLinkMatrices;
QHash<qint64, QByteArray> textureFilenames;
QHash<qint64, qint64> diffuseTextures;
QHash<qint64, qint64> bumpTextures;
qint64 jointEyeLeftID = 0;
qint64 jointEyeRightID = 0;
qint64 jointNeckID = 0;
@ -500,7 +503,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
glm::mat4_cast(glm::quat(glm::radians(rotation))) *
glm::mat4_cast(glm::quat(glm::radians(postRotation))) * glm::translate(-rotationPivot) *
glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot));
} else if (object.name == "Texture") {
foreach (const FBXNode& subobject, object.children) {
if (subobject.name == "RelativeFilename") {
textureFilenames.insert(object.properties.at(0).value<qint64>(),
subobject.properties.at(0).toByteArray());
}
}
} else if (object.name == "Deformer" && object.properties.at(2) == "Cluster") {
foreach (const FBXNode& subobject, object.children) {
if (subobject.name == "TransformLink") {
@ -513,6 +523,16 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
} else if (child.name == "Connections") {
foreach (const FBXNode& connection, child.children) {
if (connection.name == "C") {
if (connection.properties.at(0) == "OP") {
if (connection.properties.at(3) == "DiffuseColor") {
diffuseTextures.insert(connection.properties.at(2).value<qint64>(),
connection.properties.at(1).value<qint64>());
} else if (connection.properties.at(3) == "Bump") {
bumpTextures.insert(connection.properties.at(2).value<qint64>(),
connection.properties.at(1).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>());
}
@ -537,10 +557,24 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
FBXMesh& mesh = it.value();
// accumulate local transforms
qint64 modelID = parentMap.value(it.key());
for (qint64 parentID = parentMap.value(it.key()); parentID != 0; parentID = parentMap.value(parentID)) {
mesh.transform = localTransforms.value(parentID) * mesh.transform;
}
// look for textures
foreach (qint64 childID, childMap.values(modelID)) {
qint64 diffuseTextureID = diffuseTextures.value(childID);
if (diffuseTextureID != 0) {
mesh.diffuseFilename = textureFilenames.value(diffuseTextureID);
}
qint64 bumpTextureID = bumpTextures.value(childID);
if (bumpTextureID != 0) {
mesh.normalFilename = textureFilenames.value(bumpTextureID);
}
}
// look for a limb pivot
mesh.isEye = false;
foreach (qint64 childID, childMap.values(it.key())) {

View file

@ -53,6 +53,9 @@ public:
bool isEye;
QByteArray diffuseFilename;
QByteArray normalFilename;
QVector<FBXBlendshape> blendshapes;
};

View file

@ -7,6 +7,9 @@
#include <cmath>
#include <QNetworkReply>
#include "Application.h"
#include "GeometryCache.h"
#include "world.h"
@ -236,4 +239,104 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) {
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url) {
QSharedPointer<NetworkGeometry> geometry = _networkGeometry.value(url);
if (geometry.isNull()) {
geometry = QSharedPointer<NetworkGeometry>(new NetworkGeometry(url));
_networkGeometry.insert(url, geometry);
}
return geometry;
}
NetworkGeometry::NetworkGeometry(const QUrl& url) {
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
_reply = Application::getInstance()->getNetworkAccessManager()->get(request);
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
}
NetworkGeometry::~NetworkGeometry() {
foreach (const NetworkMesh& mesh, _meshes) {
glDeleteBuffers(1, &mesh.indexBufferID);
glDeleteBuffers(1, &mesh.vertexBufferID);
}
}
void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
if (bytesReceived < bytesTotal && !_reply->isFinished()) {
return;
}
QUrl url = _reply->url();
QByteArray entirety = _reply->readAll();
_reply->disconnect(this);
_reply->deleteLater();
_reply = NULL;
try {
_geometry = extractFBXGeometry(parseFBX(entirety));
} catch (const QString& error) {
qDebug() << "Error reading " << url << ": " << error << "\n";
return;
}
foreach (const FBXMesh& mesh, _geometry.meshes) {
NetworkMesh networkMesh;
glGenBuffers(1, &networkMesh.indexBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID);
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);
glGenBuffers(1, &networkMesh.vertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID);
if (mesh.blendshapes.isEmpty()) {
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) +
mesh.texCoords.size() * sizeof(glm::vec2), 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());
glBufferSubData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3),
mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData());
} else {
glBufferData(GL_ARRAY_BUFFER, mesh.texCoords.size() * sizeof(glm::vec2),
mesh.texCoords.constData(), GL_STATIC_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
QString basePath = url.path();
int idx = basePath.lastIndexOf('/');
if (idx != -1) {
basePath = basePath.left(idx);
}
if (!mesh.diffuseFilename.isEmpty()) {
url.setPath(basePath + mesh.diffuseFilename);
networkMesh.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(url);
}
if (!mesh.normalFilename.isEmpty()) {
url.setPath(basePath + mesh.normalFilename);
networkMesh.normalTexture = Application::getInstance()->getTextureCache()->getTexture(url);
}
_meshes.append(networkMesh);
}
}
void NetworkGeometry::handleReplyError() {
qDebug() << _reply->errorString() << "\n";
_reply->disconnect(this);
_reply->deleteLater();
_reply = NULL;
}

View file

@ -10,9 +10,20 @@
#define __interface__GeometryCache__
#include <QHash>
#include <QObject>
#include <QSharedPointer>
#include <QWeakPointer>
#include "FBXReader.h"
#include "InterfaceConfig.h"
class QNetworkReply;
class NetworkGeometry;
class NetworkMesh;
class NetworkTexture;
/// Stores cached geometry.
class GeometryCache {
public:
@ -22,6 +33,9 @@ public:
void renderSquare(int xDivisions, int yDivisions);
void renderHalfCylinder(int slices, int stacks);
/// Loads geometry from the specified URL.
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url);
private:
typedef QPair<int, int> IntPair;
@ -30,6 +44,46 @@ private:
QHash<IntPair, VerticesIndices> _hemisphereVBOs;
QHash<IntPair, VerticesIndices> _squareVBOs;
QHash<IntPair, VerticesIndices> _halfCylinderVBOs;
QHash<QUrl, QWeakPointer<NetworkGeometry> > _networkGeometry;
};
/// Geometry loaded from the network.
class NetworkGeometry : public QObject {
Q_OBJECT
public:
NetworkGeometry(const QUrl& url);
~NetworkGeometry();
bool isLoaded() const { return !_meshes.isEmpty(); }
const FBXGeometry& getFBXGeometry() const { return _geometry; }
const QVector<NetworkMesh>& getMeshes() const { return _meshes; }
private slots:
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void handleReplyError();
private:
QNetworkReply* _reply;
FBXGeometry _geometry;
QVector<NetworkMesh> _meshes;
};
/// The state associated with a single mesh.
class NetworkMesh {
public:
GLuint indexBufferID;
GLuint vertexBufferID;
QSharedPointer<NetworkTexture> diffuseTexture;
QSharedPointer<NetworkTexture> normalTexture;
};
#endif /* defined(__interface__GeometryCache__) */

View file

@ -9,6 +9,7 @@
#include "InterfaceConfig.h"
#include <QGLWidget>
#include <QNetworkReply>
#include <QOpenGLFramebufferObject>
#include <glm/gtc/random.hpp>
@ -83,6 +84,15 @@ GLuint TextureCache::getFileTextureID(const QString& filename) {
return id;
}
QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url) {
QSharedPointer<NetworkTexture> texture = _networkTextures.value(url);
if (texture.isNull()) {
texture = QSharedPointer<NetworkTexture>(new NetworkTexture(url));
_networkTextures.insert(url, texture);
}
return texture;
}
QOpenGLFramebufferObject* TextureCache::getPrimaryFramebufferObject() {
if (_primaryFramebufferObject == NULL) {
_primaryFramebufferObject = createFramebufferObject();
@ -163,6 +173,41 @@ Texture::~Texture() {
glDeleteTextures(1, &_id);
}
NetworkTexture::NetworkTexture(const QUrl& url) {
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
_reply = Application::getInstance()->getNetworkAccessManager()->get(request);
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
}
void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
if (bytesReceived < bytesTotal && !_reply->isFinished()) {
return;
}
QByteArray entirety = _reply->readAll();
_reply->disconnect(this);
_reply->deleteLater();
_reply = NULL;
QImage image = QImage::fromData(entirety).convertToFormat(QImage::Format_ARGB32);
glBindTexture(GL_TEXTURE_2D, getID());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1,
GL_BGRA, GL_UNSIGNED_BYTE, image.constBits());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
}
void NetworkTexture::handleReplyError() {
qDebug() << _reply->errorString() << "\n";
_reply->disconnect(this);
_reply->deleteLater();
_reply = NULL;
}
DilatedTextureCache::DilatedTextureCache(const QString& filename, int innerRadius, int outerRadius) :
_innerRadius(innerRadius),
_outerRadius(outerRadius)

View file

@ -18,10 +18,15 @@
#include "InterfaceConfig.h"
class QNetworkReply;
class QOpenGLFramebufferObject;
/// Stored cached textures, including render-to-texture targets.
class NetworkTexture;
/// Stores cached textures, including render-to-texture targets.
class TextureCache : public QObject {
Q_OBJECT
public:
TextureCache();
@ -35,6 +40,9 @@ public:
/// Returns the ID of a texture containing the contents of the specified file, loading it if necessary.
GLuint getFileTextureID(const QString& filename);
/// Loads a texture from the specified URL.
QSharedPointer<NetworkTexture> getTexture(const QUrl& url);
/// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is
/// used for scene rendering.
QOpenGLFramebufferObject* getPrimaryFramebufferObject();
@ -60,6 +68,8 @@ private:
QHash<QString, GLuint> _fileTextureIDs;
QHash<QUrl, QWeakPointer<NetworkTexture> > _networkTextures;
GLuint _primaryDepthTextureID;
QOpenGLFramebufferObject* _primaryFramebufferObject;
QOpenGLFramebufferObject* _secondaryFramebufferObject;
@ -80,6 +90,24 @@ private:
GLuint _id;
};
/// A texture loaded from the network.
class NetworkTexture : public QObject, public Texture {
Q_OBJECT
public:
NetworkTexture(const QUrl& url);
private slots:
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void handleReplyError();
private:
QNetworkReply* _reply;
};
/// Caches textures according to pupillary dilation.
class DilatedTextureCache {
public: