This commit is contained in:
Philip Rosedale 2014-02-28 16:43:28 -08:00
commit 299d847036
16 changed files with 264 additions and 79 deletions

View file

@ -309,6 +309,9 @@ void Avatar::renderBody() {
renderBillboard(); renderBillboard();
return; return;
} }
if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
return; // wait until both models are loaded
}
_skeletonModel.render(1.0f); _skeletonModel.render(1.0f);
getHead()->render(1.0f); getHead()->render(1.0f);
getHand()->render(false); getHand()->render(false);
@ -564,13 +567,13 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti
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.fst"); const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_head.fst");
getHead()->getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL, !isMyAvatar()); getHead()->getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL, true, !isMyAvatar());
} }
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.fst"); const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_body.fst");
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, !isMyAvatar()); _skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
} }
void Avatar::setDisplayName(const QString& displayName) { void Avatar::setDisplayName(const QString& displayName) {

View file

@ -677,6 +677,10 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
} }
void MyAvatar::renderBody(bool forceRenderHead) { void MyAvatar::renderBody(bool forceRenderHead) {
if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
return; // wait until both models are loaded
}
// Render the body's voxels and head // Render the body's voxels and head
_skeletonModel.render(1.0f); _skeletonModel.render(1.0f);

View file

@ -28,6 +28,8 @@
using namespace std; using namespace std;
static int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>();
template<class T> QVariant readBinaryArray(QDataStream& in) { template<class T> QVariant readBinaryArray(QDataStream& in) {
quint32 arrayLength; quint32 arrayLength;
quint32 encoding; quint32 encoding;

View file

@ -9,6 +9,7 @@
#ifndef __interface__FBXReader__ #ifndef __interface__FBXReader__
#define __interface__FBXReader__ #define __interface__FBXReader__
#include <QMetaType>
#include <QUrl> #include <QUrl>
#include <QVarLengthArray> #include <QVarLengthArray>
#include <QVariant> #include <QVariant>
@ -167,6 +168,8 @@ public:
QVector<FBXAttachment> attachments; QVector<FBXAttachment> attachments;
}; };
Q_DECLARE_METATYPE(FBXGeometry)
/// Reads an FST mapping from the supplied data. /// Reads an FST mapping from the supplied data.
QVariantHash readMapping(const QByteArray& data); QVariantHash readMapping(const QByteArray& data);

View file

@ -8,6 +8,8 @@
#include <cmath> #include <cmath>
#include <QNetworkReply> #include <QNetworkReply>
#include <QRunnable>
#include <QThreadPool>
#include "Application.h" #include "Application.h"
#include "GeometryCache.h" #include "GeometryCache.h"
@ -307,6 +309,21 @@ NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGe
_fallback(fallback) { _fallback(fallback) {
} }
bool NetworkGeometry::isLoadedWithTextures() const {
if (!isLoaded()) {
return false;
}
foreach (const NetworkMesh& mesh, _meshes) {
foreach (const NetworkMeshPart& part, mesh.parts) {
if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) ||
(part.normalTexture && !part.normalTexture->isLoaded())) {
return false;
}
}
}
return true;
}
QSharedPointer<NetworkGeometry> NetworkGeometry::getLODOrFallback(float distance, float& hysteresis, bool delayLoad) const { QSharedPointer<NetworkGeometry> NetworkGeometry::getLODOrFallback(float distance, float& hysteresis, bool delayLoad) const {
if (_lodParent.data() != this) { if (_lodParent.data() != this) {
return _lodParent.data()->getLODOrFallback(distance, hysteresis, delayLoad); return _lodParent.data()->getLODOrFallback(distance, hysteresis, delayLoad);
@ -428,6 +445,46 @@ void NetworkGeometry::clearLoadPriority(const QPointer<QObject>& owner) {
} }
} }
/// Reads geometry in a worker thread.
class GeometryReader : public QRunnable {
public:
GeometryReader(const QWeakPointer<Resource>& geometry, const QUrl& url,
const QByteArray& data, const QVariantHash& mapping);
virtual void run();
private:
QWeakPointer<Resource> _geometry;
QUrl _url;
QByteArray _data;
QVariantHash _mapping;
};
GeometryReader::GeometryReader(const QWeakPointer<Resource>& geometry, const QUrl& url,
const QByteArray& data, const QVariantHash& mapping) :
_geometry(geometry),
_url(url),
_data(data),
_mapping(mapping) {
}
void GeometryReader::run() {
QSharedPointer<Resource> geometry = _geometry.toStrongRef();
if (geometry.isNull()) {
return;
}
try {
QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&,
_url.path().toLower().endsWith(".svo") ? readSVO(_data) : readFBX(_data, _mapping)));
} catch (const QString& error) {
qDebug() << "Error reading " << _url << ": " << error;
QMetaObject::invokeMethod(geometry.data(), "finishedLoading", Q_ARG(bool, false));
}
}
void NetworkGeometry::downloadFinished(QNetworkReply* reply) { void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
QUrl url = reply->url(); QUrl url = reply->url();
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
@ -438,7 +495,7 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
QString filename = _mapping.value("filename").toString(); QString filename = _mapping.value("filename").toString();
if (filename.isNull()) { if (filename.isNull()) {
qDebug() << "Mapping file " << url << " has no filename."; qDebug() << "Mapping file " << url << " has no filename.";
_failedToLoad = true; finishedLoading(false);
} else { } else {
QString texdir = _mapping.value("texdir").toString(); QString texdir = _mapping.value("texdir").toString();
@ -452,6 +509,7 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
for (QVariantHash::const_iterator it = lods.begin(); it != lods.end(); it++) { for (QVariantHash::const_iterator it = lods.begin(); it != lods.end(); it++) {
QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url.resolved(it.key()), QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url.resolved(it.key()),
QSharedPointer<NetworkGeometry>(), true, _mapping, _textureBase)); QSharedPointer<NetworkGeometry>(), true, _mapping, _textureBase));
geometry->setSelf(geometry.staticCast<Resource>());
geometry->setLODParent(_lodParent); geometry->setLODParent(_lodParent);
_lods.insert(it.value().toFloat(), geometry); _lods.insert(it.value().toFloat(), geometry);
} }
@ -466,15 +524,13 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
return; return;
} }
try { // send the reader off to the thread pool
_geometry = url.path().toLower().endsWith(".svo") ? readSVO(data) : readFBX(data, _mapping); QThreadPool::globalInstance()->start(new GeometryReader(_self, url, data, _mapping));
} catch (const QString& error) {
qDebug() << "Error reading " << url << ": " << error;
_failedToLoad = true;
return;
} }
void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
_geometry = geometry;
foreach (const FBXMesh& mesh, _geometry.meshes) { foreach (const FBXMesh& mesh, _geometry.meshes) {
NetworkMesh networkMesh = { QOpenGLBuffer(QOpenGLBuffer::IndexBuffer), QOpenGLBuffer(QOpenGLBuffer::VertexBuffer) }; NetworkMesh networkMesh = { QOpenGLBuffer(QOpenGLBuffer::IndexBuffer), QOpenGLBuffer(QOpenGLBuffer::VertexBuffer) };
@ -567,6 +623,8 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
_meshes.append(networkMesh); _meshes.append(networkMesh);
} }
finishedLoading(true);
} }
bool NetworkMeshPart::isTranslucent() const { bool NetworkMeshPart::isTranslucent() const {

View file

@ -69,8 +69,8 @@ public:
NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad, NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad,
const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl()); const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl());
/// Checks whether the geometry is fulled loaded. /// Checks whether the geometry and its textures are loaded.
bool isLoaded() const { return !_geometry.joints.isEmpty(); } bool isLoadedWithTextures() const;
/// Returns a pointer to the geometry appropriate for the specified distance. /// Returns a pointer to the geometry appropriate for the specified distance.
/// \param hysteresis a hysteresis parameter that prevents rapid model switching /// \param hysteresis a hysteresis parameter that prevents rapid model switching
@ -90,6 +90,8 @@ protected:
virtual void downloadFinished(QNetworkReply* reply); virtual void downloadFinished(QNetworkReply* reply);
Q_INVOKABLE void setGeometry(const FBXGeometry& geometry);
private: private:
friend class GeometryCache; friend class GeometryCache;

View file

@ -57,21 +57,6 @@ QVector<Model::JointState> Model::createJointStates(const FBXGeometry& geometry)
return jointStates; return jointStates;
} }
bool Model::isLoadedWithTextures() const {
if (!isActive()) {
return false;
}
foreach (const NetworkMesh& mesh, _geometry->getMeshes()) {
foreach (const NetworkMeshPart& part, mesh.parts) {
if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) ||
(part.normalTexture && !part.normalTexture->isLoaded())) {
return false;
}
}
}
return true;
}
void Model::init() { void Model::init() {
if (!_program.isLinked()) { if (!_program.isLinked()) {
switchToResourcesParentIfRequired(); switchToResourcesParentIfRequired();
@ -117,32 +102,7 @@ void Model::reset() {
void Model::simulate(float deltaTime, bool delayLoad) { void Model::simulate(float deltaTime, bool delayLoad) {
// update our LOD // update our LOD
QVector<JointState> newJointStates; QVector<JointState> newJointStates = updateGeometry(delayLoad);
if (_geometry) {
QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad);
if (_geometry != geometry) {
if (!_jointStates.isEmpty()) {
// copy the existing joint states
const FBXGeometry& oldGeometry = _geometry->getFBXGeometry();
const FBXGeometry& newGeometry = geometry->getFBXGeometry();
newJointStates = createJointStates(newGeometry);
for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin();
it != oldGeometry.jointIndices.constEnd(); it++) {
int newIndex = newGeometry.jointIndices.value(it.key());
if (newIndex != 0) {
newJointStates[newIndex - 1] = _jointStates.at(it.value() - 1);
}
}
}
deleteGeometry();
_dilatedTextures.clear();
_geometry = geometry;
}
if (!delayLoad) {
_geometry->setLoadPriority(this, -_lodDistance);
_geometry->ensureLoading();
}
}
if (!isActive()) { if (!isActive()) {
return; return;
} }
@ -447,20 +407,18 @@ float Model::getRightArmLength() const {
return getLimbLength(getRightHandJointIndex()); return getLimbLength(getRightHandJointIndex());
} }
void Model::setURL(const QUrl& url, const QUrl& fallback, bool delayLoad) { void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bool delayLoad) {
// don't recreate the geometry if it's the same URL // don't recreate the geometry if it's the same URL
if (_url == url) { if (_url == url) {
return; return;
} }
_url = url; _url = url;
// delete our local geometry and custom textures // if so instructed, keep the current geometry until the new one is loaded
deleteGeometry(); _nextBaseGeometry = _nextGeometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback, delayLoad);
_dilatedTextures.clear(); if (!retainCurrent || !isActive() || _nextGeometry->isLoaded()) {
_lodHysteresis = NetworkGeometry::NO_HYSTERESIS; applyNextGeometry();
}
// we retain a reference to the base geometry so that its reference count doesn't fall to zero
_baseGeometry = _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback, delayLoad);
} }
glm::vec4 Model::computeAverageColor() const { glm::vec4 Model::computeAverageColor() const {
@ -823,6 +781,61 @@ void Model::applyCollision(CollisionInfo& collision) {
} }
} }
QVector<Model::JointState> Model::updateGeometry(bool delayLoad) {
QVector<JointState> newJointStates;
if (_nextGeometry) {
_nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad);
if (!delayLoad) {
_nextGeometry->setLoadPriority(this, -_lodDistance);
_nextGeometry->ensureLoading();
}
if (_nextGeometry->isLoaded()) {
applyNextGeometry();
return newJointStates;
}
}
if (!_geometry) {
return newJointStates;
}
QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad);
if (_geometry != geometry) {
if (!_jointStates.isEmpty()) {
// copy the existing joint states
const FBXGeometry& oldGeometry = _geometry->getFBXGeometry();
const FBXGeometry& newGeometry = geometry->getFBXGeometry();
newJointStates = createJointStates(newGeometry);
for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin();
it != oldGeometry.jointIndices.constEnd(); it++) {
int newIndex = newGeometry.jointIndices.value(it.key());
if (newIndex != 0) {
newJointStates[newIndex - 1] = _jointStates.at(it.value() - 1);
}
}
}
deleteGeometry();
_dilatedTextures.clear();
_geometry = geometry;
}
if (!delayLoad) {
_geometry->setLoadPriority(this, -_lodDistance);
_geometry->ensureLoading();
}
return newJointStates;
}
void Model::applyNextGeometry() {
// delete our local geometry and custom textures
deleteGeometry();
_dilatedTextures.clear();
_lodHysteresis = NetworkGeometry::NO_HYSTERESIS;
// we retain a reference to the base geometry so that its reference count doesn't fall to zero
_baseGeometry = _nextBaseGeometry;
_geometry = _nextGeometry;
_nextBaseGeometry.reset();
_nextGeometry.reset();
}
void Model::deleteGeometry() { void Model::deleteGeometry() {
foreach (Model* attachment, _attachments) { foreach (Model* attachment, _attachments) {
delete attachment; delete attachment;

View file

@ -46,14 +46,22 @@ public:
bool isActive() const { return _geometry && _geometry->isLoaded(); } bool isActive() const { return _geometry && _geometry->isLoaded(); }
bool isLoadedWithTextures() const; bool isRenderable() const { return !_meshStates.isEmpty(); }
bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); }
void init(); void init();
void reset(); void reset();
void simulate(float deltaTime, bool delayLoad = false); void simulate(float deltaTime, bool delayLoad = false);
bool render(float alpha); bool render(float alpha);
Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); /// Sets the URL of the model to render.
/// \param fallback the URL of a fallback model to render if the requested model fails to load
/// \param retainCurrent if true, keep rendering the current model until the new one is loaded
/// \param delayLoad if true, don't load the model immediately; wait until actually requested
Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(),
bool retainCurrent = false, bool delayLoad = false);
const QUrl& getURL() const { return _url; } const QUrl& getURL() const { return _url; }
/// Sets the distance parameter used for LOD computations. /// Sets the distance parameter used for LOD computations.
@ -229,10 +237,14 @@ protected:
private: private:
QVector<JointState> updateGeometry(bool delayLoad);
void applyNextGeometry();
void deleteGeometry(); void deleteGeometry();
void renderMeshes(float alpha, bool translucent); void renderMeshes(float alpha, bool translucent);
QSharedPointer<NetworkGeometry> _baseGeometry; ///< reference required to prevent collection of base QSharedPointer<NetworkGeometry> _baseGeometry; ///< reference required to prevent collection of base
QSharedPointer<NetworkGeometry> _nextBaseGeometry;
QSharedPointer<NetworkGeometry> _nextGeometry;
float _lodDistance; float _lodDistance;
float _lodHysteresis; float _lodHysteresis;

View file

@ -11,6 +11,8 @@
#include <QGLWidget> #include <QGLWidget>
#include <QNetworkReply> #include <QNetworkReply>
#include <QOpenGLFramebufferObject> #include <QOpenGLFramebufferObject>
#include <QRunnable>
#include <QThreadPool>
#include <glm/gtc/random.hpp> #include <glm/gtc/random.hpp>
@ -129,6 +131,7 @@ QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url, bool no
QSharedPointer<NetworkTexture> texture = _dilatableNetworkTextures.value(url); QSharedPointer<NetworkTexture> texture = _dilatableNetworkTextures.value(url);
if (texture.isNull()) { if (texture.isNull()) {
texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url)); texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url));
texture->setSelf(texture);
_dilatableNetworkTextures.insert(url, texture); _dilatableNetworkTextures.insert(url, texture);
} }
return texture; return texture;
@ -254,8 +257,7 @@ Texture::~Texture() {
NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) : NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
Resource(url), Resource(url),
_averageColor(1.0f, 1.0f, 1.0f, 1.0f), _averageColor(1.0f, 1.0f, 1.0f, 1.0f),
_translucent(false), _translucent(false) {
_loaded(false) {
if (!url.isValid()) { if (!url.isValid()) {
_loaded = true; _loaded = true;
@ -268,10 +270,30 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }
void NetworkTexture::downloadFinished(QNetworkReply* reply) { class ImageReader : public QRunnable {
_loaded = true; public:
QImage image = QImage::fromData(reply->readAll()); ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data);
virtual void run();
private:
QWeakPointer<Resource> _texture;
QByteArray _data;
};
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data) :
_texture(texture),
_data(data) {
}
void ImageReader::run() {
QSharedPointer<Resource> texture = _texture.toStrongRef();
if (texture.isNull()) {
return;
}
QImage image = QImage::fromData(_data);
if (image.format() != QImage::Format_ARGB32) { if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32); image = image.convertToFormat(QImage::Format_ARGB32);
} }
@ -295,9 +317,21 @@ void NetworkTexture::downloadFinished(QNetworkReply* reply) {
} }
} }
int imageArea = image.width() * image.height(); int imageArea = image.width() * image.height();
_averageColor = accumulated / (imageArea * EIGHT_BIT_MAXIMUM); QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image),
_translucent = (translucentPixels >= imageArea / 2); Q_ARG(const glm::vec4&, accumulated / (imageArea * EIGHT_BIT_MAXIMUM)),
Q_ARG(bool, translucentPixels >= imageArea / 2));
}
void NetworkTexture::downloadFinished(QNetworkReply* reply) {
// send the reader off to the thread pool
QThreadPool::globalInstance()->start(new ImageReader(_self, reply->readAll()));
}
void NetworkTexture::setImage(const QImage& image, const glm::vec4& averageColor, bool translucent) {
_averageColor = averageColor;
_translucent = translucent;
finishedLoading(true);
imageLoaded(image); imageLoaded(image);
glBindTexture(GL_TEXTURE_2D, getID()); glBindTexture(GL_TEXTURE_2D, getID());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1,

View file

@ -117,8 +117,6 @@ public:
NetworkTexture(const QUrl& url, bool normalMap); NetworkTexture(const QUrl& url, bool normalMap);
bool isLoaded() const { return _loaded; }
/// Returns the average color over the entire texture. /// Returns the average color over the entire texture.
const glm::vec4& getAverageColor() const { return _averageColor; } const glm::vec4& getAverageColor() const { return _averageColor; }
@ -131,11 +129,12 @@ protected:
virtual void downloadFinished(QNetworkReply* reply); virtual void downloadFinished(QNetworkReply* reply);
virtual void imageLoaded(const QImage& image); virtual void imageLoaded(const QImage& image);
Q_INVOKABLE void setImage(const QImage& image, const glm::vec4& averageColor, bool translucent);
private: private:
glm::vec4 _averageColor; glm::vec4 _averageColor;
bool _translucent; bool _translucent;
bool _loaded;
}; };
/// Caches derived, dilated textures. /// Caches derived, dilated textures.

View file

@ -61,6 +61,7 @@ NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) :
void NetworkProgram::downloadFinished(QNetworkReply* reply) { void NetworkProgram::downloadFinished(QNetworkReply* reply) {
_program = QScriptProgram(QTextStream(reply).readAll(), reply->url().toString()); _program = QScriptProgram(QTextStream(reply).readAll(), reply->url().toString());
finishedLoading(true);
emit loaded(); emit loaded();
} }

View file

@ -72,8 +72,6 @@ public:
ScriptCache* getCache() const { return _cache; } ScriptCache* getCache() const { return _cache; }
bool isLoaded() const { return !_program.isNull(); }
const QScriptProgram& getProgram() const { return _program; } const QScriptProgram& getProgram() const { return _program; }
signals: signals:

View file

@ -10,7 +10,15 @@
#include "RegisteredMetaTypes.h" #include "RegisteredMetaTypes.h"
static int vec4MetaTypeId = qRegisterMetaType<glm::vec4>();
static int vec3MetaTypeId = qRegisterMetaType<glm::vec3>();
static int vec2MetaTypeId = qRegisterMetaType<glm::vec2>();
static int quatMetaTypeId = qRegisterMetaType<glm::quat>();
static int xColorMetaTypeId = qRegisterMetaType<xColor>();
static int pickRayMetaTypeId = qRegisterMetaType<PickRay>();
void registerMetaTypes(QScriptEngine* engine) { void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue);
qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue); qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue);
qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue); qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue);
qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue);
@ -18,6 +26,22 @@ void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue); qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue);
} }
QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) {
QScriptValue obj = engine->newObject();
obj.setProperty("x", vec4.x);
obj.setProperty("y", vec4.y);
obj.setProperty("z", vec4.z);
obj.setProperty("w", vec4.w);
return obj;
}
void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4) {
vec4.x = object.property("x").toVariant().toFloat();
vec4.y = object.property("y").toVariant().toFloat();
vec4.z = object.property("z").toVariant().toFloat();
vec4.w = object.property("w").toVariant().toFloat();
}
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3) { QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3) {
QScriptValue obj = engine->newObject(); QScriptValue obj = engine->newObject();
obj.setProperty("x", vec3.x); obj.setProperty("x", vec3.x);

View file

@ -17,6 +17,7 @@
#include "SharedUtil.h" #include "SharedUtil.h"
Q_DECLARE_METATYPE(glm::vec4)
Q_DECLARE_METATYPE(glm::vec3) Q_DECLARE_METATYPE(glm::vec3)
Q_DECLARE_METATYPE(glm::vec2) Q_DECLARE_METATYPE(glm::vec2)
Q_DECLARE_METATYPE(glm::quat) Q_DECLARE_METATYPE(glm::quat)
@ -24,6 +25,9 @@ Q_DECLARE_METATYPE(xColor)
void registerMetaTypes(QScriptEngine* engine); void registerMetaTypes(QScriptEngine* engine);
QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4);
void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4);
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3); QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3);
void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3); void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3);

View file

@ -27,6 +27,7 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
if (resource.isNull()) { if (resource.isNull()) {
resource = createResource(url, fallback.isValid() ? resource = createResource(url, fallback.isValid() ?
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra); getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);
resource->setSelf(resource);
_resources.insert(url, resource); _resources.insert(url, resource);
} }
return resource; return resource;
@ -77,6 +78,7 @@ Resource::Resource(const QUrl& url, bool delayLoad) :
_request(url), _request(url),
_startedLoading(false), _startedLoading(false),
_failedToLoad(false), _failedToLoad(false),
_loaded(false),
_attempts(0), _attempts(0),
_reply(NULL) { _reply(NULL) {
@ -106,10 +108,15 @@ void Resource::ensureLoading() {
} }
void Resource::setLoadPriority(const QPointer<QObject>& owner, float priority) { void Resource::setLoadPriority(const QPointer<QObject>& owner, float priority) {
if (!(_failedToLoad || _loaded)) {
_loadPriorities.insert(owner, priority); _loadPriorities.insert(owner, priority);
} }
}
void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) { void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) {
if (_failedToLoad || _loaded) {
return;
}
for (QHash<QPointer<QObject>, float>::const_iterator it = priorities.constBegin(); for (QHash<QPointer<QObject>, float>::const_iterator it = priorities.constBegin();
it != priorities.constEnd(); it++) { it != priorities.constEnd(); it++) {
_loadPriorities.insert(it.key(), it.value()); _loadPriorities.insert(it.key(), it.value());
@ -117,8 +124,10 @@ void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& prioriti
} }
void Resource::clearLoadPriority(const QPointer<QObject>& owner) { void Resource::clearLoadPriority(const QPointer<QObject>& owner) {
if (!(_failedToLoad || _loaded)) {
_loadPriorities.remove(owner); _loadPriorities.remove(owner);
} }
}
float Resource::getLoadPriority() { float Resource::getLoadPriority() {
float highestPriority = -FLT_MAX; float highestPriority = -FLT_MAX;
@ -138,6 +147,15 @@ void Resource::attemptRequest() {
ResourceCache::attemptRequest(this); ResourceCache::attemptRequest(this);
} }
void Resource::finishedLoading(bool success) {
if (success) {
_loaded = true;
} else {
_failedToLoad = true;
}
_loadPriorities.clear();
}
void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
if (!_reply->isFinished()) { if (!_reply->isFinished()) {
return; return;
@ -182,7 +200,7 @@ void Resource::handleReplyError() {
// fall through to final failure // fall through to final failure
} }
default: default:
_failedToLoad = true; finishedLoading(false);
break; break;
} }
} }

View file

@ -88,6 +88,11 @@ public:
/// Returns the highest load priority across all owners. /// Returns the highest load priority across all owners.
float getLoadPriority(); float getLoadPriority();
/// Checks whether the resource has loaded.
bool isLoaded() const { return _loaded; }
void setSelf(const QWeakPointer<Resource>& self) { _self = self; }
protected slots: protected slots:
void attemptRequest(); void attemptRequest();
@ -96,10 +101,15 @@ protected:
virtual void downloadFinished(QNetworkReply* reply) = 0; virtual void downloadFinished(QNetworkReply* reply) = 0;
/// Should be called by subclasses when all the loading that will be done has been done.
Q_INVOKABLE void finishedLoading(bool success);
QNetworkRequest _request; QNetworkRequest _request;
bool _startedLoading; bool _startedLoading;
bool _failedToLoad; bool _failedToLoad;
bool _loaded;
QHash<QPointer<QObject>, float> _loadPriorities; QHash<QPointer<QObject>, float> _loadPriorities;
QWeakPointer<Resource> _self;
private slots: private slots: