mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 03:50:40 +02:00
When switching models, retain old one until new one is loaded. Closes #2146.
This commit is contained in:
parent
2639796402
commit
21a149348b
11 changed files with 83 additions and 38 deletions
|
@ -567,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) {
|
||||||
|
|
|
@ -307,6 +307,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);
|
||||||
|
@ -438,7 +453,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();
|
||||||
|
@ -471,7 +486,7 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
|
||||||
|
|
||||||
} catch (const QString& error) {
|
} catch (const QString& error) {
|
||||||
qDebug() << "Error reading " << url << ": " << error;
|
qDebug() << "Error reading " << url << ": " << error;
|
||||||
_failedToLoad = true;
|
finishedLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,6 +582,8 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
|
||||||
|
|
||||||
_meshes.append(networkMesh);
|
_meshes.append(networkMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finishedLoading(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkMeshPart::isTranslucent() const {
|
bool NetworkMeshPart::isTranslucent() const {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
@ -119,7 +104,18 @@ void Model::simulate(float deltaTime, bool delayLoad) {
|
||||||
// update our LOD
|
// update our LOD
|
||||||
QVector<JointState> newJointStates;
|
QVector<JointState> newJointStates;
|
||||||
if (_geometry) {
|
if (_geometry) {
|
||||||
QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad);
|
QSharedPointer<NetworkGeometry> geometry = _geometry;
|
||||||
|
if (_nextGeometry) {
|
||||||
|
if (!delayLoad) {
|
||||||
|
_nextGeometry->setLoadPriority(this, -_lodDistance);
|
||||||
|
_nextGeometry->ensureLoading();
|
||||||
|
}
|
||||||
|
if (_nextGeometry->isLoaded()) {
|
||||||
|
geometry = _nextGeometry;
|
||||||
|
_nextGeometry.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
geometry = geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad);
|
||||||
if (_geometry != geometry) {
|
if (_geometry != geometry) {
|
||||||
if (!_jointStates.isEmpty()) {
|
if (!_jointStates.isEmpty()) {
|
||||||
// copy the existing joint states
|
// copy the existing joint states
|
||||||
|
@ -447,20 +443,27 @@ 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;
|
||||||
|
|
||||||
|
// if so instructed, keep the current geometry until the new one is loaded
|
||||||
|
_nextGeometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback, delayLoad);
|
||||||
|
if (retainCurrent && isActive() && !_nextGeometry->isLoaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// delete our local geometry and custom textures
|
// delete our local geometry and custom textures
|
||||||
deleteGeometry();
|
deleteGeometry();
|
||||||
_dilatedTextures.clear();
|
_dilatedTextures.clear();
|
||||||
_lodHysteresis = NetworkGeometry::NO_HYSTERESIS;
|
_lodHysteresis = NetworkGeometry::NO_HYSTERESIS;
|
||||||
|
|
||||||
// we retain a reference to the base geometry so that its reference count doesn't fall to zero
|
// 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);
|
_baseGeometry = _geometry = _nextGeometry;
|
||||||
|
_nextGeometry.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec4 Model::computeAverageColor() const {
|
glm::vec4 Model::computeAverageColor() const {
|
||||||
|
|
|
@ -48,14 +48,20 @@ public:
|
||||||
|
|
||||||
bool isRenderable() const { return !_meshStates.isEmpty(); }
|
bool isRenderable() const { return !_meshStates.isEmpty(); }
|
||||||
|
|
||||||
bool isLoadedWithTextures() const;
|
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.
|
||||||
|
@ -235,6 +241,7 @@ private:
|
||||||
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> _nextGeometry;
|
||||||
float _lodDistance;
|
float _lodDistance;
|
||||||
float _lodHysteresis;
|
float _lodHysteresis;
|
||||||
|
|
||||||
|
|
|
@ -254,8 +254,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;
|
||||||
|
@ -269,8 +268,6 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkTexture::downloadFinished(QNetworkReply* reply) {
|
void NetworkTexture::downloadFinished(QNetworkReply* reply) {
|
||||||
_loaded = true;
|
|
||||||
|
|
||||||
QImage image = QImage::fromData(reply->readAll());
|
QImage image = QImage::fromData(reply->readAll());
|
||||||
if (image.format() != QImage::Format_ARGB32) {
|
if (image.format() != QImage::Format_ARGB32) {
|
||||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||||
|
@ -298,6 +295,7 @@ void NetworkTexture::downloadFinished(QNetworkReply* reply) {
|
||||||
_averageColor = accumulated / (imageArea * EIGHT_BIT_MAXIMUM);
|
_averageColor = accumulated / (imageArea * EIGHT_BIT_MAXIMUM);
|
||||||
_translucent = (translucentPixels >= imageArea / 2);
|
_translucent = (translucentPixels >= imageArea / 2);
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
@ -135,7 +133,6 @@ private:
|
||||||
|
|
||||||
glm::vec4 _averageColor;
|
glm::vec4 _averageColor;
|
||||||
bool _translucent;
|
bool _translucent;
|
||||||
bool _loaded;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Caches derived, dilated textures.
|
/// Caches derived, dilated textures.
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -77,6 +77,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 +107,15 @@ void Resource::ensureLoading() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Resource::setLoadPriority(const QPointer<QObject>& owner, float priority) {
|
void Resource::setLoadPriority(const QPointer<QObject>& owner, float priority) {
|
||||||
_loadPriorities.insert(owner, priority);
|
if (!(_failedToLoad || _loaded)) {
|
||||||
|
_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,7 +123,9 @@ void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& prioriti
|
||||||
}
|
}
|
||||||
|
|
||||||
void Resource::clearLoadPriority(const QPointer<QObject>& owner) {
|
void Resource::clearLoadPriority(const QPointer<QObject>& owner) {
|
||||||
_loadPriorities.remove(owner);
|
if (!(_failedToLoad || _loaded)) {
|
||||||
|
_loadPriorities.remove(owner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float Resource::getLoadPriority() {
|
float Resource::getLoadPriority() {
|
||||||
|
@ -138,6 +146,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 +199,7 @@ void Resource::handleReplyError() {
|
||||||
// fall through to final failure
|
// fall through to final failure
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
_failedToLoad = true;
|
finishedLoading(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,9 @@ 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; }
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
|
||||||
void attemptRequest();
|
void attemptRequest();
|
||||||
|
@ -96,9 +99,13 @@ 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.
|
||||||
|
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;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
Loading…
Reference in a new issue