Merge pull request #1085 from HifiExperiments/loadPriority

Improve model load priority
This commit is contained in:
HifiExperiments 2024-09-07 12:31:39 -07:00 committed by GitHub
commit 3c4c860ef9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 257 additions and 128 deletions

View file

@ -241,9 +241,9 @@ Item {
model: root.downloadUrls model: root.downloadUrls
delegate: StatText { delegate: StatText {
visible: root.expanded; visible: root.expanded;
text: modelData.length > 30 text: (modelData.length > 30
? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22) ? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22)
: modelData : modelData) + "\n\t" + (!isNaN(root.downloadPriorities[index]) ? ("Priority: " + root.downloadPriorities[index] + ", ") : "") + "Progress: " + root.downloadProgresses[index] + "%"
} }
} }
} }

View file

@ -304,16 +304,16 @@ Item {
} }
ListView { ListView {
width: geoCol.width width: geoCol.width
height: root.downloadUrls.length * 15 height: root.downloadUrls.length * 30
visible: root.expanded && root.downloadUrls.length > 0; visible: root.expanded && root.downloadUrls.length > 0;
model: root.downloadUrls model: root.downloadUrls
delegate: StatText { delegate: StatText {
visible: root.expanded; visible: root.expanded;
text: modelData.length > 30 text: (modelData.length > 30
? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22) ? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22)
: modelData : modelData) + "\n\t" + (!isNaN(root.downloadPriorities[index]) ? ("Priority: " + root.downloadPriorities[index] + ", ") : "") + "Progress: " + root.downloadProgresses[index] + "%"
} }
} }
} }

View file

@ -2277,12 +2277,12 @@ void Application::initialize(const QCommandLineParser &parser) {
auto loadingRequests = ResourceCache::getLoadingRequests(); auto loadingRequests = ResourceCache::getLoadingRequests();
QJsonArray loadingRequestsStats; QJsonArray loadingRequestsStats;
for (const auto& request : loadingRequests) { for (const auto& requestPair : loadingRequests) {
QJsonObject requestStats; QJsonObject requestStats;
requestStats["filename"] = request->getURL().fileName(); requestStats["filename"] = requestPair.first->getURL().fileName();
requestStats["received"] = request->getBytesReceived(); requestStats["received"] = requestPair.first->getBytesReceived();
requestStats["total"] = request->getBytesTotal(); requestStats["total"] = requestPair.first->getBytesTotal();
requestStats["attempts"] = (int)request->getDownloadAttempts(); requestStats["attempts"] = (int)requestPair.first->getDownloadAttempts();
loadingRequestsStats.append(requestStats); loadingRequestsStats.append(requestStats);
} }
@ -5731,15 +5731,30 @@ void Application::init() {
getEntities()->init(); getEntities()->init();
getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) { getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) {
auto dims = item.getScaledDimensions(); if (item.getEntityHostType() == entity::HostType::AVATAR) {
auto maxSize = glm::compMax(dims); return item.isMyAvatarEntity() ? Avatar::MYAVATAR_ENTITY_LOADING_PRIORITY : Avatar::OTHERAVATAR_ENTITY_LOADING_PRIORITY;
}
const float maxSize = glm::compMax(item.getScaledDimensions());
if (maxSize <= 0.0f) { if (maxSize <= 0.0f) {
return 0.0f; return 0.0f;
} }
auto distance = glm::distance(getMyAvatar()->getWorldPosition(), item.getWorldPosition()); const glm::vec3 itemPosition = item.getWorldPosition();
return atan2(maxSize, distance); const float distance = glm::distance(getMyAvatar()->getWorldPosition(), itemPosition);
float result = atan2(maxSize, distance);
bool isInView = true;
{
QMutexLocker viewLocker(&_viewMutex);
isInView = _viewFrustum.sphereIntersectsKeyhole(itemPosition, maxSize);
}
if (!isInView) {
const float OUT_OF_VIEW_PENALTY = -M_PI_2;
result += OUT_OF_VIEW_PENALTY;
}
return result;
}); });
ObjectMotionState::setShapeManager(&_shapeManager); ObjectMotionState::setShapeManager(&_shapeManager);

View file

@ -259,7 +259,7 @@ MyAvatar::MyAvatar(QThread* thread) :
_headData = new MyHead(this); _headData = new MyHead(this);
_skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr); _skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr);
_skeletonModel->setLoadingPriority(MYAVATAR_LOADING_PRIORITY); _skeletonModel->setLoadingPriorityOperator([]() { return MYAVATAR_LOADING_PRIORITY; });
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
connect(_skeletonModel.get(), &Model::setURLFinished, this, [this](bool success) { connect(_skeletonModel.get(), &Model::setURLFinished, this, [this](bool success) {
if (success) { if (success) {
@ -1841,6 +1841,8 @@ void MyAvatar::handleChangedAvatarEntityData() {
} }
}); });
} }
_hasCheckedForAvatarEntities = true;
} }
bool MyAvatar::updateStaleAvatarEntityBlobs() const { bool MyAvatar::updateStaleAvatarEntityBlobs() const {
@ -1896,6 +1898,7 @@ void MyAvatar::prepareAvatarEntityDataForReload() {
}); });
_reloadAvatarEntityDataFromSettings = true; _reloadAvatarEntityDataFromSettings = true;
_hasCheckedForAvatarEntities = false;
} }
AvatarEntityMap MyAvatar::getAvatarEntityData() const { AvatarEntityMap MyAvatar::getAvatarEntityData() const {

View file

@ -44,7 +44,7 @@ OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) {
// give the pointer to our head to inherited _headData variable from AvatarData // give the pointer to our head to inherited _headData variable from AvatarData
_headData = new Head(this); _headData = new Head(this);
_skeletonModel = std::make_shared<SkeletonModel>(this, nullptr); _skeletonModel = std::make_shared<SkeletonModel>(this, nullptr);
_skeletonModel->setLoadingPriority(OTHERAVATAR_LOADING_PRIORITY); _skeletonModel->setLoadingPriorityOperator([]() { return OTHERAVATAR_LOADING_PRIORITY; });
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady);
connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset); connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset);
@ -595,7 +595,8 @@ void OtherAvatar::handleChangedAvatarEntityData() {
} }
}); });
setAvatarEntityDataChanged(false); _avatarEntityDataChanged = false;
_hasCheckedForAvatarEntities = true;
} }
void OtherAvatar::onAddAttachedAvatarEntity(const QUuid& id) { void OtherAvatar::onAddAttachedAvatarEntity(const QUuid& id) {
@ -630,3 +631,11 @@ void OtherAvatar::updateAttachedAvatarEntities() {
} }
} }
} }
void OtherAvatar::onIdentityRecieved() {
if (_avatarEntityIdentityCountdown > 0) {
_avatarEntityIdentityCountdown--;
} else {
_hasCheckedForAvatarEntities = true;
}
}

View file

@ -73,6 +73,8 @@ protected:
void onAddAttachedAvatarEntity(const QUuid& id); void onAddAttachedAvatarEntity(const QUuid& id);
void onRemoveAttachedAvatarEntity(const QUuid& id); void onRemoveAttachedAvatarEntity(const QUuid& id);
void onIdentityRecieved() override;
class AvatarEntityDataHash { class AvatarEntityDataHash {
public: public:
AvatarEntityDataHash(uint32_t h) : hash(h) {}; AvatarEntityDataHash(uint32_t h) : hash(h) {};
@ -91,6 +93,14 @@ protected:
uint8_t _workloadRegion { workload::Region::INVALID }; uint8_t _workloadRegion { workload::Region::INVALID };
BodyLOD _bodyLOD { BodyLOD::Sphere }; BodyLOD _bodyLOD { BodyLOD::Sphere };
bool _needsDetailedRebuild { false }; bool _needsDetailedRebuild { false };
private:
// When determining _hasCheckedForAvatarEntities for OtherAvatars, we can set it to true in
// handleChangedAvatarEntityData if we have avatar entities. But we never receive explicit
// confirmation from the avatar mixer if we don't have any. So instead, we wait to receive
// a few identity packets, and assume that if we haven't gotten any avatar entities by then,
// that we're safe to say there aren't any.
uint8_t _avatarEntityIdentityCountdown { 2 };
}; };
using OtherAvatarPointer = std::shared_ptr<OtherAvatar>; using OtherAvatarPointer = std::shared_ptr<OtherAvatar>;

View file

@ -159,8 +159,8 @@ bool DownloadInfoResultFromScriptValue(const ScriptValue& object, DownloadInfoRe
DownloadInfoResult AccountServicesScriptingInterface::getDownloadInfo() { DownloadInfoResult AccountServicesScriptingInterface::getDownloadInfo() {
DownloadInfoResult result; DownloadInfoResult result;
foreach(const auto& resource, ResourceCache::getLoadingRequests()) { foreach(const auto& resourcePair, ResourceCache::getLoadingRequests()) {
result.downloading.append(resource->getProgress() * 100.0f); result.downloading.append(resourcePair.first->getProgress() * 100.0f);
} }
result.pending = ResourceCache::getPendingRequestCount(); result.pending = ResourceCache::getPendingRequestCount();
return result; return result;

View file

@ -289,8 +289,8 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(entityPacketsInKbps, octreeServerCount ? totalEntityKbps / octreeServerCount : -1); STAT_UPDATE(entityPacketsInKbps, octreeServerCount ? totalEntityKbps / octreeServerCount : -1);
auto loadingRequests = ResourceCache::getLoadingRequests(); auto loadingRequestPairs = ResourceCache::getLoadingRequests();
STAT_UPDATE(downloads, loadingRequests.size()); STAT_UPDATE(downloads, loadingRequestPairs.size());
STAT_UPDATE(downloadLimit, (int)ResourceCache::getRequestLimit()) STAT_UPDATE(downloadLimit, (int)ResourceCache::getRequestLimit())
STAT_UPDATE(downloadsPending, (int)ResourceCache::getPendingRequestCount()); STAT_UPDATE(downloadsPending, (int)ResourceCache::getPendingRequestCount());
STAT_UPDATE(processing, DependencyManager::get<StatTracker>()->getStat("Processing").toInt()); STAT_UPDATE(processing, DependencyManager::get<StatTracker>()->getStat("Processing").toInt());
@ -298,29 +298,37 @@ void Stats::updateStats(bool force) {
// See if the active download urls have changed // See if the active download urls have changed
bool shouldUpdateUrls = _downloads != _downloadUrls.size(); bool shouldUpdateUrls = _downloads != _downloadUrls.size();
bool shouldUpdateProgresses = false;
if (!shouldUpdateUrls) { if (!shouldUpdateUrls) {
for (int i = 0; i < _downloads; i++) { for (int i = 0; i < _downloads; i++) {
if (loadingRequests[i]->getURL().toString() != _downloadUrls[i]) { if (loadingRequestPairs[i].first->getURL().toString() != _downloadUrls[i]) {
shouldUpdateUrls = true; shouldUpdateUrls = true;
break; break;
} else if (loadingRequestPairs[i].first->getProgress() != _downloadProgresses[i]) {
shouldUpdateProgresses = true;
} }
} }
} }
// If the urls have changed, update the list // If the urls have changed, update the list
if (shouldUpdateUrls) { if (shouldUpdateUrls) {
_downloadUrls.clear(); _downloadUrls.clear();
foreach (const auto& resource, loadingRequests) { _downloadPriorities.clear();
_downloadUrls << resource->getURL().toString(); foreach (const auto& resourcePair, loadingRequestPairs) {
_downloadUrls << resourcePair.first->getURL().toString();
_downloadPriorities << resourcePair.second;
} }
emit downloadUrlsChanged(); emit downloadUrlsChanged();
emit downloadPrioritiesChanged();
shouldUpdateProgresses = true;
}
if (shouldUpdateProgresses) {
_downloadProgresses.clear();
foreach (const auto& resourcePair, loadingRequestPairs) {
_downloadProgresses << (int)(100.0f * resourcePair.first->getProgress());
}
emit downloadProgressesChanged();
} }
// TODO fix to match original behavior
//stringstream downloads;
//downloads << "Downloads: ";
//foreach(Resource* resource, ) {
// downloads << (int)(resource->getProgress() * 100.0f) << "% ";
//}
//downloads << "(" << << " pending)";
} }
// Fourth column, octree stats // Fourth column, octree stats

View file

@ -211,7 +211,10 @@ private: \
* <em>Read-only.</em> * <em>Read-only.</em>
* @property {string[]} downloadUrls - The download URLs. * @property {string[]} downloadUrls - The download URLs.
* <em>Read-only.</em> * <em>Read-only.</em>
* <p><strong>Note:</strong> Property not available in the API.</p> * @property {number[]} downloadProgresses - The download progresses.
* <em>Read-only.</em>
* @property {number[]} downloadPriorities - The download priorities.
* <em>Read-only.</em>
* @property {number} processing - The number of completed downloads being processed. * @property {number} processing - The number of completed downloads being processed.
* <em>Read-only.</em> * <em>Read-only.</em>
* @property {number} processingPending - The number of completed downloads waiting to be processed. * @property {number} processingPending - The number of completed downloads waiting to be processed.
@ -529,6 +532,8 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, downloadLimit, 0) STATS_PROPERTY(int, downloadLimit, 0)
STATS_PROPERTY(int, downloadsPending, 0) STATS_PROPERTY(int, downloadsPending, 0)
Q_PROPERTY(QStringList downloadUrls READ downloadUrls NOTIFY downloadUrlsChanged) Q_PROPERTY(QStringList downloadUrls READ downloadUrls NOTIFY downloadUrlsChanged)
Q_PROPERTY(QList<int> downloadProgresses READ downloadProgresses NOTIFY downloadProgressesChanged)
Q_PROPERTY(QList<float> downloadPriorities READ downloadPriorities NOTIFY downloadPrioritiesChanged)
STATS_PROPERTY(int, processing, 0) STATS_PROPERTY(int, processing, 0)
STATS_PROPERTY(int, processingPending, 0) STATS_PROPERTY(int, processingPending, 0)
STATS_PROPERTY(int, triangles, 0) STATS_PROPERTY(int, triangles, 0)
@ -622,7 +627,9 @@ public:
} }
} }
QStringList downloadUrls () { return _downloadUrls; } QStringList downloadUrls() { return _downloadUrls; }
QList<int> downloadProgresses() { return _downloadProgresses; }
QList<float> downloadPriorities() { return _downloadPriorities; }
public slots: public slots:
@ -1091,6 +1098,20 @@ signals:
*/ */
void downloadUrlsChanged(); void downloadUrlsChanged();
/*@jsdoc
* Triggered when the value of the <code>downloadProgresses</code> property changes.
* @function Stats.downloadProgressesChanged
* @returns {Signal}
*/
void downloadProgressesChanged();
/*@jsdoc
* Triggered when the value of the <code>downloadPriorities</code> property changes.
* @function Stats.downloadPrioritiesChanged
* @returns {Signal}
*/
void downloadPrioritiesChanged();
/*@jsdoc /*@jsdoc
* Triggered when the value of the <code>processing</code> property changes. * Triggered when the value of the <code>processing</code> property changes.
* @function Stats.processingChanged * @function Stats.processingChanged
@ -1809,14 +1830,16 @@ signals:
*/ */
private: private:
int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process int _recentMaxPackets { 0 } ; // recent max incoming voxel packets to process
bool _resetRecentMaxPacketsSoon{ true }; bool _resetRecentMaxPacketsSoon { true };
bool _expanded{ false }; bool _expanded { false };
bool _showTimingDetails{ false }; bool _showTimingDetails { false };
bool _showGameUpdateStats{ false }; bool _showGameUpdateStats { false };
QString _monospaceFont; QString _monospaceFont;
const AudioIOStats* _audioStats; const AudioIOStats* _audioStats;
QStringList _downloadUrls = QStringList(); QStringList _downloadUrls { QStringList() };
QList<int> _downloadProgresses { QList<int>() };
QList<float> _downloadPriorities { QList<float>() };
}; };
#endif // hifi_Stats_h #endif // hifi_Stats_h

View file

@ -1085,7 +1085,7 @@ AnimNodeLoader::AnimNodeLoader(const QUrl& url) :
{ {
_resource = QSharedPointer<Resource>::create(url); _resource = QSharedPointer<Resource>::create(url);
_resource->setSelf(_resource); _resource->setSelf(_resource);
_resource->setLoadPriority(this, ANIM_GRAPH_LOAD_PRIORITY); _resource->setLoadPriorityOperator(this, []() { return ANIM_GRAPH_LOAD_PRIORITY; });
connect(_resource.data(), &Resource::loaded, this, &AnimNodeLoader::onRequestDone); connect(_resource.data(), &Resource::loaded, this, &AnimNodeLoader::onRequestDone);
connect(_resource.data(), &Resource::failed, this, &AnimNodeLoader::onRequestError); connect(_resource.data(), &Resource::failed, this, &AnimNodeLoader::onRequestError);
_resource->ensureLoading(); _resource->ensureLoading();

View file

@ -35,7 +35,7 @@ SharedSoundPointer SoundCache::getSound(const QUrl& url) {
QSharedPointer<Resource> SoundCache::createResource(const QUrl& url) { QSharedPointer<Resource> SoundCache::createResource(const QUrl& url) {
auto resource = QSharedPointer<Sound>(new Sound(url), &Resource::deleter); auto resource = QSharedPointer<Sound>(new Sound(url), &Resource::deleter);
resource->setLoadPriority(this, SOUNDS_LOADING_PRIORITY); resource->setLoadPriorityOperator(this, []() { return SOUNDS_LOADING_PRIORITY; });
return resource; return resource;
} }

View file

@ -51,12 +51,14 @@ const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;
const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f);
const float Avatar::MYAVATAR_LOADING_PRIORITY = (float)M_PI; // Entity priority is computed as atan2(maxDim, distance) which is <= PI / 2 const float Avatar::MYAVATAR_LOADING_PRIORITY = (float)M_PI; // Entity priority is computed as atan2(maxDim, distance) which is <= PI / 2
const float Avatar::OTHERAVATAR_LOADING_PRIORITY = MYAVATAR_LOADING_PRIORITY - EPSILON; const float Avatar::OTHERAVATAR_LOADING_PRIORITY = MYAVATAR_LOADING_PRIORITY - EPSILON;
const float Avatar::MYAVATAR_ENTITY_LOADING_PRIORITY = MYAVATAR_LOADING_PRIORITY - EPSILON;
const float Avatar::OTHERAVATAR_ENTITY_LOADING_PRIORITY = OTHERAVATAR_LOADING_PRIORITY - EPSILON;
namespace render { namespace render {
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) {
ItemKey::Builder keyBuilder = ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::hifi::TAG_ALL_VIEWS).withMetaCullGroup(); ItemKey::Builder keyBuilder = ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::hifi::TAG_ALL_VIEWS).withMetaCullGroup();
auto avatarPtr = static_pointer_cast<Avatar>(avatar); auto avatarPtr = static_pointer_cast<Avatar>(avatar);
if (!avatarPtr->getEnableMeshVisible()) { if (!avatarPtr->shouldRender()) {
keyBuilder.withInvisible(); keyBuilder.withInvisible();
} }
return keyBuilder.build(); return keyBuilder.build();
@ -646,7 +648,7 @@ void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& sc
_skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS); _skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
_skeletonModel->setGroupCulled(true); _skeletonModel->setGroupCulled(true);
_skeletonModel->setCanCastShadow(true); _skeletonModel->setCanCastShadow(true);
_skeletonModel->setVisibleInScene(_isMeshVisible, scene); _skeletonModel->setVisibleInScene(shouldRender(), scene);
processMaterials(); processMaterials();
@ -841,8 +843,26 @@ bool Avatar::getEnableMeshVisible() const {
} }
void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
bool canTryFade{ false }; if (_needsWearablesLoadedCheck && _hasCheckedForAvatarEntities) {
bool wearablesAreLoaded = true;
// Technically, we should be checking for descendant avatar entities that are owned by this avatar.
// But it's sufficient to just check all children entities here.
forEachChild([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
auto entity = std::dynamic_pointer_cast<EntityItem>(child);
if (entity && !entity->isVisuallyReady()) {
wearablesAreLoaded = false;
}
}
});
_isReadyToDraw = wearablesAreLoaded;
if (_isReadyToDraw) {
_needMeshVisibleSwitch = true;
}
_needsWearablesLoadedCheck = !wearablesAreLoaded;
}
bool canTryFade = false;
// check to see if when we added our models to the scene they were ready, if they were not ready, then // check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene // fix them up in the scene
render::Transaction transaction; render::Transaction transaction;
@ -854,7 +874,7 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
_skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS); _skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
_skeletonModel->setGroupCulled(true); _skeletonModel->setGroupCulled(true);
_skeletonModel->setCanCastShadow(true); _skeletonModel->setCanCastShadow(true);
_skeletonModel->setVisibleInScene(_isMeshVisible, scene); _skeletonModel->setVisibleInScene(shouldRender(), scene);
processMaterials(); processMaterials();
canTryFade = true; canTryFade = true;
@ -862,7 +882,7 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
} }
if (_needMeshVisibleSwitch) { if (_needMeshVisibleSwitch) {
_skeletonModel->setVisibleInScene(_isMeshVisible, scene); _skeletonModel->setVisibleInScene(shouldRender(), scene);
updateRenderItem(transaction); updateRenderItem(transaction);
_needMeshVisibleSwitch = false; _needMeshVisibleSwitch = false;
} }
@ -1484,6 +1504,10 @@ void Avatar::rigReady() {
buildSpine2SplineRatioCache(); buildSpine2SplineRatioCache();
setSkeletonData(getSkeletonDefaultData()); setSkeletonData(getSkeletonDefaultData());
sendSkeletonData(); sendSkeletonData();
_needsWearablesLoadedCheck = _skeletonModel && _skeletonModel->isLoaded() && _skeletonModel->getGeometry()->shouldWaitForWearables();
_needMeshVisibleSwitch = (_isReadyToDraw != !_needsWearablesLoadedCheck);
_isReadyToDraw = !_needsWearablesLoadedCheck;
} }
// rig has been reset. // rig has been reset.

View file

@ -554,6 +554,11 @@ public:
uint32_t appendSubMetaItems(render::ItemIDs& subItems); uint32_t appendSubMetaItems(render::ItemIDs& subItems);
virtual bool shouldRender() const { return _isMeshVisible && _isReadyToDraw; }
static const float MYAVATAR_ENTITY_LOADING_PRIORITY;
static const float OTHERAVATAR_ENTITY_LOADING_PRIORITY;
signals: signals:
/*@jsdoc /*@jsdoc
* Triggered when the avatar's target scale is changed. The target scale is the desired scale of the avatar without any * Triggered when the avatar's target scale is changed. The target scale is the desired scale of the avatar without any
@ -742,8 +747,11 @@ protected:
void processMaterials(); void processMaterials();
AABox _renderBound; AABox _renderBound;
bool _isMeshVisible{ true }; bool _isMeshVisible { true };
bool _needMeshVisibleSwitch{ true }; bool _needMeshVisibleSwitch { true };
bool _isReadyToDraw { false };
bool _needsWearablesLoadedCheck { false };
bool _hasCheckedForAvatarEntities { false };
static const float MYAVATAR_LOADING_PRIORITY; static const float MYAVATAR_LOADING_PRIORITY;
static const float OTHERAVATAR_LOADING_PRIORITY; static const float OTHERAVATAR_LOADING_PRIORITY;

View file

@ -2072,6 +2072,8 @@ void AvatarData::processAvatarIdentity(QDataStream& packetStream, bool& identity
<< "is >=" << (udt::SequenceNumber::Type) incomingSequenceNumber; << "is >=" << (udt::SequenceNumber::Type) incomingSequenceNumber;
#endif #endif
} }
onIdentityRecieved();
} }
QUrl AvatarData::getWireSafeSkeletonModelURL() const { QUrl AvatarData::getWireSafeSkeletonModelURL() const {

View file

@ -1276,7 +1276,6 @@ public:
*/ */
Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData); Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData);
void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; }
AvatarEntityIDs getAndClearRecentlyRemovedIDs(); AvatarEntityIDs getAndClearRecentlyRemovedIDs();
/*@jsdoc /*@jsdoc
@ -1583,6 +1582,8 @@ protected:
virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; } virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; }
virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) { } // No-op in AvatarMixer virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) { } // No-op in AvatarMixer
virtual void onIdentityRecieved() {}
// Body scale // Body scale
float _targetScale; float _targetScale;
float _domainMinimumHeight { MIN_AVATAR_HEIGHT }; float _domainMinimumHeight { MIN_AVATAR_HEIGHT };

View file

@ -1310,6 +1310,10 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
scene->enqueueTransaction(transaction); scene->enqueueTransaction(transaction);
}); });
entity->setModel(model); entity->setModel(model);
model->setLoadingPriorityOperator([entity]() {
float loadPriority = entity->getLoadPriority();
return fabs(loadPriority) > EPSILON ? loadPriority : EntityTreeRenderer::getEntityLoadingPriority(*entity);
});
withWriteLock([&] { _model = model; }); withWriteLock([&] { _model = model; });
} }
@ -1317,7 +1321,6 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
if (_parsedModelURL != model->getURL()) { if (_parsedModelURL != model->getURL()) {
_texturesLoaded = false; _texturesLoaded = false;
_jointMappingCompleted = false; _jointMappingCompleted = false;
model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity));
model->setURL(_parsedModelURL); model->setURL(_parsedModelURL);
} }

View file

@ -612,6 +612,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_GROUP_CULLED, groupCulled); CHECK_PROPERTY_CHANGE(PROP_GROUP_CULLED, groupCulled);
CHECK_PROPERTY_CHANGE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients); CHECK_PROPERTY_CHANGE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients);
CHECK_PROPERTY_CHANGE(PROP_USE_ORIGINAL_PIVOT, useOriginalPivot); CHECK_PROPERTY_CHANGE(PROP_USE_ORIGINAL_PIVOT, useOriginalPivot);
CHECK_PROPERTY_CHANGE(PROP_LOAD_PRIORITY, loadPriority);
changedProperties += _animation.getChangedProperties(); changedProperties += _animation.getChangedProperties();
// Light // Light
@ -1090,6 +1091,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* @property {boolean} useOriginalPivot=false - If <code>false</code>, the model will be centered based on its content, * @property {boolean} useOriginalPivot=false - If <code>false</code>, the model will be centered based on its content,
* ignoring any offset in the model itself. If <code>true</code>, the model will respect its original offset. Currently, * ignoring any offset in the model itself. If <code>true</code>, the model will respect its original offset. Currently,
* only pivots relative to <code>{x: 0, y: 0, z: 0}</code> are supported. * only pivots relative to <code>{x: 0, y: 0, z: 0}</code> are supported.
* @property {number} loadPriority=0.0 - If <code>0</code>, the model download will be prioritized based on distance, size, and
* other factors, and assigned a priority automatically between <code>0</code> and <code>PI / 2</code>. Otherwise, the
* download will be ordered based on the set <code>loadPriority</code>.
* @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the * @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the
* model's original textures. Use a texture name from the <code>originalTextures</code> property to override that texture. * model's original textures. Use a texture name from the <code>originalTextures</code> property to override that texture.
* Only the texture names and URLs to be overridden need be specified; original textures are used where there are no * Only the texture names and URLs to be overridden need be specified; original textures are used where there are no
@ -1919,6 +1923,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GROUP_CULLED, groupCulled); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GROUP_CULLED, groupCulled);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USE_ORIGINAL_PIVOT, useOriginalPivot); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USE_ORIGINAL_PIVOT, useOriginalPivot);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOAD_PRIORITY, loadPriority);
_animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); _animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity);
} }
@ -2350,6 +2355,7 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h
COPY_PROPERTY_FROM_QSCRIPTVALUE(groupCulled, bool, setGroupCulled); COPY_PROPERTY_FROM_QSCRIPTVALUE(groupCulled, bool, setGroupCulled);
COPY_PROPERTY_FROM_QSCRIPTVALUE(blendshapeCoefficients, QString, setBlendshapeCoefficients); COPY_PROPERTY_FROM_QSCRIPTVALUE(blendshapeCoefficients, QString, setBlendshapeCoefficients);
COPY_PROPERTY_FROM_QSCRIPTVALUE(useOriginalPivot, bool, setUseOriginalPivot); COPY_PROPERTY_FROM_QSCRIPTVALUE(useOriginalPivot, bool, setUseOriginalPivot);
COPY_PROPERTY_FROM_QSCRIPTVALUE(loadPriority, float, setLoadPriority);
_animation.copyFromScriptValue(object, namesSet, _defaultSettings); _animation.copyFromScriptValue(object, namesSet, _defaultSettings);
// Light // Light
@ -2658,6 +2664,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(groupCulled); COPY_PROPERTY_IF_CHANGED(groupCulled);
COPY_PROPERTY_IF_CHANGED(blendshapeCoefficients); COPY_PROPERTY_IF_CHANGED(blendshapeCoefficients);
COPY_PROPERTY_IF_CHANGED(useOriginalPivot); COPY_PROPERTY_IF_CHANGED(useOriginalPivot);
COPY_PROPERTY_IF_CHANGED(loadPriority);
_animation.merge(other._animation); _animation.merge(other._animation);
// Light // Light
@ -3036,6 +3043,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
ADD_PROPERTY_TO_MAP(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool); ADD_PROPERTY_TO_MAP(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool);
ADD_PROPERTY_TO_MAP(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString); ADD_PROPERTY_TO_MAP(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString);
ADD_PROPERTY_TO_MAP(PROP_USE_ORIGINAL_PIVOT, UseOriginalPivot, useOriginalPivot, bool); ADD_PROPERTY_TO_MAP(PROP_USE_ORIGINAL_PIVOT, UseOriginalPivot, useOriginalPivot, bool);
ADD_PROPERTY_TO_MAP(PROP_LOAD_PRIORITY, LoadPriority, loadPriority, float);
{ // Animation { // Animation
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_URL, Animation, animation, URL, url); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_URL, Animation, animation, URL, url);
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation);
@ -3517,6 +3525,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, properties.getGroupCulled()); APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, properties.getGroupCulled());
APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, properties.getBlendshapeCoefficients()); APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, properties.getBlendshapeCoefficients());
APPEND_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, properties.getUseOriginalPivot()); APPEND_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, properties.getUseOriginalPivot());
APPEND_ENTITY_PROPERTY(PROP_LOAD_PRIORITY, properties.getLoadPriority());
_staticAnimation.setProperties(properties); _staticAnimation.setProperties(properties);
_staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); _staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);
@ -4029,6 +4038,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GROUP_CULLED, bool, setGroupCulled); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GROUP_CULLED, bool, setGroupCulled);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USE_ORIGINAL_PIVOT, bool, setUseOriginalPivot); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USE_ORIGINAL_PIVOT, bool, setUseOriginalPivot);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOAD_PRIORITY, float, setLoadPriority);
properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
} }
@ -4438,6 +4448,7 @@ void EntityItemProperties::markAllChanged() {
_groupCulledChanged = true; _groupCulledChanged = true;
_blendshapeCoefficientsChanged = true; _blendshapeCoefficientsChanged = true;
_useOriginalPivotChanged = true; _useOriginalPivotChanged = true;
_loadPriorityChanged = true;
_animation.markAllChanged(); _animation.markAllChanged();
// Light // Light
@ -5024,6 +5035,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (useOriginalPivotChanged()) { if (useOriginalPivotChanged()) {
out += "useOriginalPivot"; out += "useOriginalPivot";
} }
if (loadPriorityChanged()) {
out += "loadPriority";
}
getAnimation().listChangedProperties(out); getAnimation().listChangedProperties(out);
// Light // Light

View file

@ -315,6 +315,7 @@ public:
DEFINE_PROPERTY_REF(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool, false); DEFINE_PROPERTY_REF(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool, false);
DEFINE_PROPERTY_REF(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString, ""); DEFINE_PROPERTY_REF(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString, "");
DEFINE_PROPERTY_REF(PROP_USE_ORIGINAL_PIVOT, UseOriginalPivot, useOriginalPivot, bool, false); DEFINE_PROPERTY_REF(PROP_USE_ORIGINAL_PIVOT, UseOriginalPivot, useOriginalPivot, bool, false);
DEFINE_PROPERTY_REF(PROP_LOAD_PRIORITY, LoadPriority, loadPriority, float, 0.0f);
DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup);
// Light // Light

View file

@ -255,17 +255,18 @@ enum EntityPropertyList {
PROP_GROUP_CULLED = PROP_DERIVED_7, PROP_GROUP_CULLED = PROP_DERIVED_7,
PROP_BLENDSHAPE_COEFFICIENTS = PROP_DERIVED_8, PROP_BLENDSHAPE_COEFFICIENTS = PROP_DERIVED_8,
PROP_USE_ORIGINAL_PIVOT = PROP_DERIVED_9, PROP_USE_ORIGINAL_PIVOT = PROP_DERIVED_9,
PROP_LOAD_PRIORITY = PROP_DERIVED_10,
// Animation // Animation
PROP_ANIMATION_URL = PROP_DERIVED_10, PROP_ANIMATION_URL = PROP_DERIVED_11,
PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_11, PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_12,
PROP_ANIMATION_FPS = PROP_DERIVED_12, PROP_ANIMATION_FPS = PROP_DERIVED_13,
PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_13, PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_14,
PROP_ANIMATION_PLAYING = PROP_DERIVED_14, PROP_ANIMATION_PLAYING = PROP_DERIVED_15,
PROP_ANIMATION_LOOP = PROP_DERIVED_15, PROP_ANIMATION_LOOP = PROP_DERIVED_16,
PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_16, PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_17,
PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_17, PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_18,
PROP_ANIMATION_HOLD = PROP_DERIVED_18, PROP_ANIMATION_HOLD = PROP_DERIVED_19,
PROP_ANIMATION_SMOOTH_FRAMES = PROP_DERIVED_19, PROP_ANIMATION_SMOOTH_FRAMES = PROP_DERIVED_20,
// Light // Light
PROP_IS_SPOTLIGHT = PROP_DERIVED_0, PROP_IS_SPOTLIGHT = PROP_DERIVED_0,

View file

@ -368,7 +368,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
} }
} }
} else { } else {
if (getIsServer()) { if (isEntityServer()) {
bool simulationBlocked = !entity->getSimulatorID().isNull(); bool simulationBlocked = !entity->getSimulatorID().isNull();
if (properties.simulationOwnerChanged()) { if (properties.simulationOwnerChanged()) {
QUuid submittedID = properties.getSimulationOwner().getID(); QUuid submittedID = properties.getSimulationOwner().getID();
@ -674,7 +674,7 @@ void EntityTree::deleteEntitiesByID(const std::vector<EntityItemID>& ids, bool f
// this method has two paths: // this method has two paths:
// (a) entity-server: applies delete filter // (a) entity-server: applies delete filter
// (b) interface-client: deletes local- and my-avatar-entities immediately, submits domainEntity deletes to the entity-server // (b) interface-client: deletes local- and my-avatar-entities immediately, submits domainEntity deletes to the entity-server
if (getIsServer()) { if (isEntityServer()) {
withWriteLock([&] { withWriteLock([&] {
std::vector<EntityItemPointer> entitiesToDelete; std::vector<EntityItemPointer> entitiesToDelete;
entitiesToDelete.reserve(ids.size()); entitiesToDelete.reserve(ids.size());
@ -1468,7 +1468,7 @@ bool EntityTree::isScriptInAllowlist(const QString& scriptProperty) {
// NOTE: Caller must lock the tree before calling this. // NOTE: Caller must lock the tree before calling this.
int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,
const SharedNodePointer& senderNode) { const SharedNodePointer& senderNode) {
if (!getIsServer()) { if (!isEntityServer()) {
qCWarning(entities) << "EntityTree::processEditPacketData() should only be called on a server tree."; qCWarning(entities) << "EntityTree::processEditPacketData() should only be called on a server tree.";
return 0; return 0;
} }
@ -2071,7 +2071,7 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo
// Which means this is a state synchronization message from the the entity-server. It is saying // Which means this is a state synchronization message from the the entity-server. It is saying
// "The following domain-entities have already been deleted". While need to perform sanity checking // "The following domain-entities have already been deleted". While need to perform sanity checking
// (e.g. verify these are domain entities) permissions need NOT checked for the domain-entities. // (e.g. verify these are domain entities) permissions need NOT checked for the domain-entities.
assert(!getIsServer()); assert(!isEntityServer());
// TODO: remove this stuff out of EntityTree:: and into interface-client code. // TODO: remove this stuff out of EntityTree:: and into interface-client code.
#ifdef EXTRA_ERASE_DEBUGGING #ifdef EXTRA_ERASE_DEBUGGING
qCDebug(entities) << "EntityTree::processEraseMessage()"; qCDebug(entities) << "EntityTree::processEraseMessage()";
@ -2141,7 +2141,7 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo
int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) {
// NOTE: this is called on entity-server when receiving a delete request from an interface-client or agent // NOTE: this is called on entity-server when receiving a delete request from an interface-client or agent
//TODO: assert(treeIsLocked); //TODO: assert(treeIsLocked);
assert(getIsServer()); assert(isEntityServer());
#ifdef EXTRA_ERASE_DEBUGGING #ifdef EXTRA_ERASE_DEBUGGING
qCDebug(entities) << "EntityTree::processEraseMessageDetails()"; qCDebug(entities) << "EntityTree::processEraseMessageDetails()";
#endif #endif

View file

@ -72,6 +72,7 @@ EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& d
COPY_ENTITY_PROPERTY_TO_PROPERTIES(groupCulled, getGroupCulled); COPY_ENTITY_PROPERTY_TO_PROPERTIES(groupCulled, getGroupCulled);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(blendshapeCoefficients, getBlendshapeCoefficients); COPY_ENTITY_PROPERTY_TO_PROPERTIES(blendshapeCoefficients, getBlendshapeCoefficients);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(useOriginalPivot, getUseOriginalPivot); COPY_ENTITY_PROPERTY_TO_PROPERTIES(useOriginalPivot, getUseOriginalPivot);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(loadPriority, getLoadPriority);
withReadLock([&] { withReadLock([&] {
_animationProperties.getProperties(properties); _animationProperties.getProperties(properties);
}); });
@ -96,6 +97,7 @@ bool ModelEntityItem::setSubClassProperties(const EntityItemProperties& properti
SET_ENTITY_PROPERTY_FROM_PROPERTIES(groupCulled, setGroupCulled); SET_ENTITY_PROPERTY_FROM_PROPERTIES(groupCulled, setGroupCulled);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(blendshapeCoefficients, setBlendshapeCoefficients); SET_ENTITY_PROPERTY_FROM_PROPERTIES(blendshapeCoefficients, setBlendshapeCoefficients);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(useOriginalPivot, setUseOriginalPivot); SET_ENTITY_PROPERTY_FROM_PROPERTIES(useOriginalPivot, setUseOriginalPivot);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(loadPriority, setLoadPriority);
withWriteLock([&] { withWriteLock([&] {
AnimationPropertyGroup animationProperties = _animationProperties; AnimationPropertyGroup animationProperties = _animationProperties;
@ -131,6 +133,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY(PROP_GROUP_CULLED, bool, setGroupCulled); READ_ENTITY_PROPERTY(PROP_GROUP_CULLED, bool, setGroupCulled);
READ_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients); READ_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients);
READ_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, bool, setUseOriginalPivot); READ_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, bool, setUseOriginalPivot);
READ_ENTITY_PROPERTY(PROP_LOAD_PRIORITY, float, setLoadPriority);
// grab a local copy of _animationProperties to avoid multiple locks // grab a local copy of _animationProperties to avoid multiple locks
int bytesFromAnimation; int bytesFromAnimation;
@ -171,6 +174,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams&
requestedProperties += PROP_GROUP_CULLED; requestedProperties += PROP_GROUP_CULLED;
requestedProperties += PROP_BLENDSHAPE_COEFFICIENTS; requestedProperties += PROP_BLENDSHAPE_COEFFICIENTS;
requestedProperties += PROP_USE_ORIGINAL_PIVOT; requestedProperties += PROP_USE_ORIGINAL_PIVOT;
requestedProperties += PROP_LOAD_PRIORITY;
requestedProperties += _animationProperties.getEntityProperties(params); requestedProperties += _animationProperties.getEntityProperties(params);
return requestedProperties; return requestedProperties;
@ -201,6 +205,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, getGroupCulled()); APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, getGroupCulled());
APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, getBlendshapeCoefficients()); APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, getBlendshapeCoefficients());
APPEND_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, getUseOriginalPivot()); APPEND_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, getUseOriginalPivot());
APPEND_ENTITY_PROPERTY(PROP_LOAD_PRIORITY, getLoadPriority());
withReadLock([&] { withReadLock([&] {
_animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, _animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
@ -255,6 +260,7 @@ void ModelEntityItem::debugDump() const {
qCDebug(entities) << " compound shape URL:" << getCompoundShapeURL(); qCDebug(entities) << " compound shape URL:" << getCompoundShapeURL();
qCDebug(entities) << " blendshapeCoefficients:" << getBlendshapeCoefficients(); qCDebug(entities) << " blendshapeCoefficients:" << getBlendshapeCoefficients();
qCDebug(entities) << " useOrigialPivot:" << getUseOriginalPivot(); qCDebug(entities) << " useOrigialPivot:" << getUseOriginalPivot();
qCDebug(entities) << " loadPriority:" << getLoadPriority();
} }
void ModelEntityItem::setShapeType(ShapeType type) { void ModelEntityItem::setShapeType(ShapeType type) {
@ -768,3 +774,15 @@ bool ModelEntityItem::getUseOriginalPivot() const {
return _useOriginalPivot; return _useOriginalPivot;
}); });
} }
float ModelEntityItem::getLoadPriority() const {
return resultWithReadLock<float>([&] {
return _loadPriority;
});
}
void ModelEntityItem::setLoadPriority(float loadPriority) {
withWriteLock([&] {
_loadPriority = loadPriority;
});
}

View file

@ -123,6 +123,9 @@ public:
bool getUseOriginalPivot() const; bool getUseOriginalPivot() const;
void setUseOriginalPivot(bool useOriginalPivot); void setUseOriginalPivot(bool useOriginalPivot);
float getLoadPriority() const;
void setLoadPriority(float loadPriority);
private: private:
void setAnimationSettings(const QString& value); // only called for old bitstream format void setAnimationSettings(const QString& value); // only called for old bitstream format
bool applyNewAnimationProperties(AnimationPropertyGroup newProperties); bool applyNewAnimationProperties(AnimationPropertyGroup newProperties);
@ -152,6 +155,7 @@ protected:
glm::u8vec3 _color; glm::u8vec3 _color;
glm::vec3 _modelScale { 1.0f }; glm::vec3 _modelScale { 1.0f };
QString _modelURL; QString _modelURL;
float _loadPriority { 0.0f };
bool _relayParentJoints; bool _relayParentJoints;
bool _groupCulled { false }; bool _groupCulled { false };
QVariantMap _blendshapeCoefficientsMap; QVariantMap _blendshapeCoefficientsMap;

View file

@ -448,9 +448,9 @@ void NetworkTexture::setExtra(void* extra) {
_shouldFailOnRedirect = _currentlyLoadingResourceType != ResourceType::KTX; _shouldFailOnRedirect = _currentlyLoadingResourceType != ResourceType::KTX;
if (_type == image::TextureUsage::SKY_TEXTURE) { if (_type == image::TextureUsage::SKY_TEXTURE) {
setLoadPriority(this, SKYBOX_LOAD_PRIORITY); setLoadPriorityOperator(this, []() { return SKYBOX_LOAD_PRIORITY; });
} else if (_currentlyLoadingResourceType == ResourceType::KTX) { } else if (_currentlyLoadingResourceType == ResourceType::KTX) {
setLoadPriority(this, HIGH_MIPS_LOAD_PRIORITY); setLoadPriorityOperator(this, []() { return HIGH_MIPS_LOAD_PRIORITY; });
} }
if (!_url.isValid()) { if (!_url.isValid()) {
@ -704,7 +704,7 @@ void NetworkTexture::startRequestForNextMipLevel() {
init(false); init(false);
float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip; float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip;
setLoadPriority(this, priority); setLoadPriorityOperator(this, [priority]() { return priority; });
_url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1));
TextureCache::attemptRequest(self); TextureCache::attemptRequest(self);
} }

View file

@ -252,7 +252,6 @@ void GeometryResource::downloadFinished(const QByteArray& data) {
} }
auto animGraphVariant = _mapping.value("animGraphUrl"); auto animGraphVariant = _mapping.value("animGraphUrl");
if (animGraphVariant.isValid()) { if (animGraphVariant.isValid()) {
QUrl fstUrl(animGraphVariant.toString()); QUrl fstUrl(animGraphVariant.toString());
if (fstUrl.isValid()) { if (fstUrl.isValid()) {
@ -264,6 +263,8 @@ void GeometryResource::downloadFinished(const QByteArray& data) {
_animGraphOverrideUrl = QUrl(); _animGraphOverrideUrl = QUrl();
} }
_waitForWearables = _mapping.value(WAIT_FOR_WEARABLES_FIELD).toBool();
auto modelCache = DependencyManager::get<ModelCache>(); auto modelCache = DependencyManager::get<ModelCache>();
GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseURL, false }; GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseURL, false };
@ -452,6 +453,7 @@ Geometry::Geometry(const Geometry& geometry) {
} }
_animGraphOverrideUrl = geometry._animGraphOverrideUrl; _animGraphOverrideUrl = geometry._animGraphOverrideUrl;
_waitForWearables = geometry._waitForWearables;
_mapping = geometry._mapping; _mapping = geometry._mapping;
} }

View file

@ -58,6 +58,7 @@ public:
virtual bool areTexturesLoaded() const; virtual bool areTexturesLoaded() const;
const QUrl& getAnimGraphOverrideUrl() const { return _animGraphOverrideUrl; } const QUrl& getAnimGraphOverrideUrl() const { return _animGraphOverrideUrl; }
bool shouldWaitForWearables() const { return _waitForWearables; }
const QVariantHash& getMapping() const { return _mapping; } const QVariantHash& getMapping() const { return _mapping; }
protected: protected:
@ -72,6 +73,7 @@ protected:
QUrl _animGraphOverrideUrl; QUrl _animGraphOverrideUrl;
QVariantHash _mapping; // parsed contents of FST file. QVariantHash _mapping; // parsed contents of FST file.
bool _waitForWearables { false };
private: private:
mutable bool _areTexturesLoaded { false }; mutable bool _areTexturesLoaded { false };

View file

@ -22,7 +22,7 @@
#include <SharedUtil.h> #include <SharedUtil.h>
const QStringList SINGLE_VALUE_PROPERTIES{"name", "filename", "texdir", "script", "comment"}; const QStringList SINGLE_VALUE_PROPERTIES { NAME_FIELD, FILENAME_FIELD, TEXDIR_FIELD, SCRIPT_FIELD, WAIT_FOR_WEARABLES_FIELD, COMMENT_FIELD };
hifi::VariantMultiHash FSTReader::parseMapping(QIODevice* device) { hifi::VariantMultiHash FSTReader::parseMapping(QIODevice* device) {
hifi::VariantMultiHash properties; hifi::VariantMultiHash properties;

View file

@ -33,6 +33,7 @@ static const QString BLENDSHAPE_FIELD = "bs";
static const QString SCRIPT_FIELD = "script"; static const QString SCRIPT_FIELD = "script";
static const QString JOINT_NAME_MAPPING_FIELD = "jointMap"; static const QString JOINT_NAME_MAPPING_FIELD = "jointMap";
static const QString MATERIAL_MAPPING_FIELD = "materialMap"; static const QString MATERIAL_MAPPING_FIELD = "materialMap";
static const QString WAIT_FOR_WEARABLES_FIELD = "waitForWearables";
static const QString COMMENT_FIELD = "comment"; static const QString COMMENT_FIELD = "comment";
class FSTReader { class FSTReader {

View file

@ -30,10 +30,10 @@
#include "NetworkLogging.h" #include "NetworkLogging.h"
#include "NodeList.h" #include "NodeList.h"
bool ResourceCacheSharedItems::appendRequest(QWeakPointer<Resource> resource) { bool ResourceCacheSharedItems::appendRequest(QWeakPointer<Resource> resource, float priority) {
Lock lock(_mutex); Lock lock(_mutex);
if ((uint32_t)_loadingRequests.size() < _requestLimit) { if ((uint32_t)_loadingRequests.size() < _requestLimit) {
_loadingRequests.append(resource); _loadingRequests.append({ resource, priority });
return true; return true;
} else { } else {
_pendingRequests.append(resource); _pendingRequests.append(resource);
@ -70,14 +70,14 @@ uint32_t ResourceCacheSharedItems::getPendingRequestsCount() const {
return _pendingRequests.size(); return _pendingRequests.size();
} }
QList<QSharedPointer<Resource>> ResourceCacheSharedItems::getLoadingRequests() const { QList<std::pair<QSharedPointer<Resource>, float>> ResourceCacheSharedItems::getLoadingRequests() const {
QList<QSharedPointer<Resource>> result; QList<std::pair<QSharedPointer<Resource>, float>> result;
Lock lock(_mutex); Lock lock(_mutex);
foreach(QWeakPointer<Resource> resource, _loadingRequests) { foreach(auto resourcePair, _loadingRequests) {
auto locked = resource.lock(); auto locked = resourcePair.first.lock();
if (locked) { if (locked) {
result.append(locked); result.append({ locked, resourcePair.second });
} }
} }
@ -96,7 +96,7 @@ void ResourceCacheSharedItems::removeRequest(QWeakPointer<Resource> resource) {
// QWeakPointer has no operator== implementation for two weak ptrs, so // QWeakPointer has no operator== implementation for two weak ptrs, so
// manually loop in case resource has been freed. // manually loop in case resource has been freed.
for (int i = 0; i < _loadingRequests.size();) { for (int i = 0; i < _loadingRequests.size();) {
auto request = _loadingRequests.at(i); auto request = _loadingRequests.at(i).first;
// Clear our resource and any freed resources // Clear our resource and any freed resources
if (!request || request.toStrongRef().data() == resource.toStrongRef().data()) { if (!request || request.toStrongRef().data() == resource.toStrongRef().data()) {
_loadingRequests.removeAt(i); _loadingRequests.removeAt(i);
@ -106,7 +106,7 @@ void ResourceCacheSharedItems::removeRequest(QWeakPointer<Resource> resource) {
} }
} }
QSharedPointer<Resource> ResourceCacheSharedItems::getHighestPendingRequest() { std::pair<QSharedPointer<Resource>, float> ResourceCacheSharedItems::getHighestPendingRequest() {
// look for the highest priority pending request // look for the highest priority pending request
int highestIndex = -1; int highestIndex = -1;
float highestPriority = -FLT_MAX; float highestPriority = -FLT_MAX;
@ -139,7 +139,7 @@ QSharedPointer<Resource> ResourceCacheSharedItems::getHighestPendingRequest() {
_pendingRequests.takeAt(highestIndex); _pendingRequests.takeAt(highestIndex);
} }
return highestResource; return { highestResource, highestPriority };
} }
void ResourceCacheSharedItems::clear() { void ResourceCacheSharedItems::clear() {
@ -519,7 +519,7 @@ void ResourceCache::updateTotalSize(const qint64& deltaSize) {
emit dirty(); emit dirty();
} }
QList<QSharedPointer<Resource>> ResourceCache::getLoadingRequests() { QList<std::pair<QSharedPointer<Resource>, float>> ResourceCache::getLoadingRequests() {
return DependencyManager::get<ResourceCacheSharedItems>()->getLoadingRequests(); return DependencyManager::get<ResourceCacheSharedItems>()->getLoadingRequests();
} }
@ -531,11 +531,11 @@ uint32_t ResourceCache::getLoadingRequestCount() {
return DependencyManager::get<ResourceCacheSharedItems>()->getLoadingRequestsCount(); return DependencyManager::get<ResourceCacheSharedItems>()->getLoadingRequestsCount();
} }
bool ResourceCache::attemptRequest(QSharedPointer<Resource> resource) { bool ResourceCache::attemptRequest(QSharedPointer<Resource> resource, float priority) {
Q_ASSERT(!resource.isNull()); Q_ASSERT(!resource.isNull());
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>(); auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
if (sharedItems->appendRequest(resource)) { if (sharedItems->appendRequest(resource, priority)) {
resource->makeRequest(); resource->makeRequest();
return true; return true;
} }
@ -555,8 +555,8 @@ void ResourceCache::requestCompleted(QWeakPointer<Resource> resource) {
bool ResourceCache::attemptHighestPriorityRequest() { bool ResourceCache::attemptHighestPriorityRequest() {
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>(); auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
auto resource = sharedItems->getHighestPendingRequest(); auto resourcePair = sharedItems->getHighestPendingRequest();
return (resource && attemptRequest(resource)); return (resourcePair.first && attemptRequest(resourcePair.first, resourcePair.second));
} }
static int requestID = 0; static int requestID = 0;
@ -571,7 +571,7 @@ Resource::Resource(const Resource& other) :
_startedLoading(other._startedLoading), _startedLoading(other._startedLoading),
_failedToLoad(other._failedToLoad), _failedToLoad(other._failedToLoad),
_loaded(other._loaded), _loaded(other._loaded),
_loadPriorities(other._loadPriorities), _loadPriorityOperators(other._loadPriorityOperators),
_bytesReceived(other._bytesReceived), _bytesReceived(other._bytesReceived),
_bytesTotal(other._bytesTotal), _bytesTotal(other._bytesTotal),
_bytes(other._bytes), _bytes(other._bytes),
@ -605,40 +605,24 @@ void Resource::ensureLoading() {
} }
} }
void Resource::setLoadPriority(const QPointer<QObject>& owner, float priority) { void Resource::setLoadPriorityOperator(const QPointer<QObject>& owner, std::function<float()> priorityOperator) {
if (!_failedToLoad) { if (!_failedToLoad) {
_loadPriorities.insert(owner, priority); _loadPriorityOperators.insert(owner, priorityOperator);
}
}
void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) {
if (_failedToLoad) {
return;
}
for (QHash<QPointer<QObject>, float>::const_iterator it = priorities.constBegin();
it != priorities.constEnd(); it++) {
_loadPriorities.insert(it.key(), it.value());
}
}
void Resource::clearLoadPriority(const QPointer<QObject>& owner) {
if (!_failedToLoad) {
_loadPriorities.remove(owner);
} }
} }
float Resource::getLoadPriority() { float Resource::getLoadPriority() {
if (_loadPriorities.size() == 0) { if (_loadPriorityOperators.size() == 0) {
return 0; return 0;
} }
float highestPriority = -FLT_MAX; float highestPriority = -FLT_MAX;
for (QHash<QPointer<QObject>, float>::iterator it = _loadPriorities.begin(); it != _loadPriorities.end(); ) { for (QHash<QPointer<QObject>, std::function<float()>>::iterator it = _loadPriorityOperators.begin(); it != _loadPriorityOperators.end();) {
if (it.key().isNull()) { if (it.key().isNull() || !it.value()) {
it = _loadPriorities.erase(it); it = _loadPriorityOperators.erase(it);
continue; continue;
} }
highestPriority = qMax(highestPriority, it.value()); highestPriority = qMax(highestPriority, it.value()());
it++; it++;
} }
return highestPriority; return highestPriority;
@ -742,7 +726,7 @@ void Resource::attemptRequest() {
void Resource::finishedLoading(bool success) { void Resource::finishedLoading(bool success) {
if (success) { if (success) {
_loadPriorities.clear(); _loadPriorityOperators.clear();
_loaded = true; _loaded = true;
} else { } else {
_failedToLoad = true; _failedToLoad = true;

View file

@ -16,6 +16,7 @@
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
#include <math.h>
#include <QtCore/QHash> #include <QtCore/QHash>
#include <QtCore/QList> #include <QtCore/QList>
@ -66,14 +67,14 @@ class ResourceCacheSharedItems : public Dependency {
using Lock = std::unique_lock<Mutex>; using Lock = std::unique_lock<Mutex>;
public: public:
bool appendRequest(QWeakPointer<Resource> newRequest); bool appendRequest(QWeakPointer<Resource> newRequest, float priority);
void removeRequest(QWeakPointer<Resource> doneRequest); void removeRequest(QWeakPointer<Resource> doneRequest);
void setRequestLimit(uint32_t limit); void setRequestLimit(uint32_t limit);
uint32_t getRequestLimit() const; uint32_t getRequestLimit() const;
QList<QSharedPointer<Resource>> getPendingRequests() const; QList<QSharedPointer<Resource>> getPendingRequests() const;
QSharedPointer<Resource> getHighestPendingRequest(); std::pair<QSharedPointer<Resource>, float> getHighestPendingRequest();
uint32_t getPendingRequestsCount() const; uint32_t getPendingRequestsCount() const;
QList<QSharedPointer<Resource>> getLoadingRequests() const; QList<std::pair<QSharedPointer<Resource>, float>> getLoadingRequests() const;
uint32_t getLoadingRequestsCount() const; uint32_t getLoadingRequestsCount() const;
void clear(); void clear();
@ -82,7 +83,7 @@ private:
mutable Mutex _mutex; mutable Mutex _mutex;
QList<QWeakPointer<Resource>> _pendingRequests; QList<QWeakPointer<Resource>> _pendingRequests;
QList<QWeakPointer<Resource>> _loadingRequests; QList<std::pair<QWeakPointer<Resource>, float>> _loadingRequests;
const uint32_t DEFAULT_REQUEST_LIMIT = 10; const uint32_t DEFAULT_REQUEST_LIMIT = 10;
uint32_t _requestLimit { DEFAULT_REQUEST_LIMIT }; uint32_t _requestLimit { DEFAULT_REQUEST_LIMIT };
}; };
@ -216,7 +217,7 @@ public:
void setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize); void setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize);
qint64 getUnusedResourceCacheSize() const { return _unusedResourcesMaxSize; } qint64 getUnusedResourceCacheSize() const { return _unusedResourcesMaxSize; }
static QList<QSharedPointer<Resource>> getLoadingRequests(); static QList<std::pair<QSharedPointer<Resource>, float>> getLoadingRequests();
static uint32_t getPendingRequestCount(); static uint32_t getPendingRequestCount();
static uint32_t getLoadingRequestCount(); static uint32_t getLoadingRequestCount();
@ -268,7 +269,7 @@ protected:
/// Attempt to load a resource if requests are below the limit, otherwise queue the resource for loading /// Attempt to load a resource if requests are below the limit, otherwise queue the resource for loading
/// \return true if the resource began loading, otherwise false if the resource is in the pending queue /// \return true if the resource began loading, otherwise false if the resource is in the pending queue
static bool attemptRequest(QSharedPointer<Resource> resource); static bool attemptRequest(QSharedPointer<Resource> resource, float priority = NAN);
static void requestCompleted(QWeakPointer<Resource> resource); static void requestCompleted(QWeakPointer<Resource> resource);
static bool attemptHighestPriorityRequest(); static bool attemptHighestPriorityRequest();
@ -424,13 +425,7 @@ public:
void ensureLoading(); void ensureLoading();
/// Sets the load priority for one owner. /// Sets the load priority for one owner.
virtual void setLoadPriority(const QPointer<QObject>& owner, float priority); virtual void setLoadPriorityOperator(const QPointer<QObject>& owner, std::function<float()> priorityOperator);
/// Sets a set of priorities at once.
virtual void setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities);
/// Clears the load priority for one owner.
virtual void clearLoadPriority(const QPointer<QObject>& owner);
/// Returns the highest load priority across all owners. /// Returns the highest load priority across all owners.
float getLoadPriority(); float getLoadPriority();
@ -451,7 +446,7 @@ public:
qint64 getBytes() const { return _bytes; } qint64 getBytes() const { return _bytes; }
/// For loading resources, returns the load progress. /// For loading resources, returns the load progress.
float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : ((float)_bytesReceived / _bytesTotal); }
/// Refreshes the resource. /// Refreshes the resource.
virtual void refresh(); virtual void refresh();
@ -537,7 +532,7 @@ protected:
bool _failedToLoad = false; bool _failedToLoad = false;
bool _loaded = false; bool _loaded = false;
QHash<QPointer<QObject>, float> _loadPriorities; QHash<QPointer<QObject>, std::function<float()>> _loadPriorityOperators;
QWeakPointer<Resource> _self; QWeakPointer<Resource> _self;
QPointer<ResourceCache> _cache; QPointer<ResourceCache> _cache;

View file

@ -359,6 +359,7 @@ enum class EntityVersion : PacketVersion {
AmbientColor, AmbientColor,
SoundEntities, SoundEntities,
TonemappingAndAmbientOcclusion, TonemappingAndAmbientOcclusion,
ModelLoadPriority,
// Add new versions above here // Add new versions above here
NUM_PACKET_TYPE, NUM_PACKET_TYPE,

View file

@ -1343,7 +1343,7 @@ void Model::setURL(const QUrl& url) {
auto resource = DependencyManager::get<ModelCache>()->getGeometryResource(url); auto resource = DependencyManager::get<ModelCache>()->getGeometryResource(url);
if (resource) { if (resource) {
resource->setLoadPriority(this, _loadingPriority); resource->setLoadPriorityOperator(this, _loadingPriorityOperator);
_renderWatcher.setResource(resource); _renderWatcher.setResource(resource);
} }
_rig.initFlow(false); _rig.initFlow(false);

View file

@ -295,7 +295,7 @@ public:
// returns 'true' if needs fullUpdate after geometry change // returns 'true' if needs fullUpdate after geometry change
virtual bool updateGeometry(); virtual bool updateGeometry();
void setLoadingPriority(float priority) { _loadingPriority = priority; } void setLoadingPriorityOperator(std::function<float()> priorityOperator) { _loadingPriorityOperator = priorityOperator; }
size_t getRenderInfoVertexCount() const { return _renderInfoVertexCount; } size_t getRenderInfoVertexCount() const { return _renderInfoVertexCount; }
size_t getRenderInfoTextureSize(); size_t getRenderInfoTextureSize();
@ -518,7 +518,7 @@ protected:
uint64_t _created; uint64_t _created;
private: private:
float _loadingPriority { 0.0f }; std::function<float()> _loadingPriorityOperator { []() { return 0.0f; } };
void calculateTextureInfo(); void calculateTextureInfo();