diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index ece667422c..d9f1234674 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -52,7 +52,8 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf _viewState(viewState), _scriptingServices(scriptingServices), _displayModelBounds(false), - _dontDoPrecisionPicking(false) + _dontDoPrecisionPicking(false), + _layeredZones(this) { REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory) @@ -135,8 +136,8 @@ void EntityTreeRenderer::clear() { _entitiesInScene.clear(); // reset the zone to the default (while we load the next scene) - _bestZone = nullptr; - applyZonePropertiesToScene(_bestZone); + _layeredZones.clear(); + applyZoneAndHasSkybox(nullptr); OctreeRenderer::clear(); } @@ -192,10 +193,10 @@ void EntityTreeRenderer::update() { // If we haven't already updated and previously attempted to load a texture, // check if the texture loaded and apply it - if (!updated && ( - (_pendingSkyboxTexture && (!_skyboxTexture || _skyboxTexture->isLoaded())) || - (_pendingAmbientTexture && (!_ambientTexture || _ambientTexture->isLoaded())))) { - applyZonePropertiesToScene(_bestZone); + if (!updated && + ((_pendingAmbientTexture && (!_ambientTexture || _ambientTexture->isLoaded())) || + (_pendingSkyboxTexture && (!_skyboxTexture || _skyboxTexture->isLoaded())))) { + applySkyboxAndHasAmbient(); } // Even if we're not moving the mouse, if we started clicking on an entity and we have @@ -210,7 +211,7 @@ void EntityTreeRenderer::update() { deleteReleasedModels(); } -bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3& avatarPosition, QVector* entitiesContainingAvatar) { +bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar) { bool didUpdate = false; float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later QVector foundEntities; @@ -220,12 +221,10 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3& _tree->withReadLock([&] { // FIXME - if EntityTree had a findEntitiesContainingPoint() this could theoretically be a little faster - std::static_pointer_cast(_tree)->findEntities(avatarPosition, radius, foundEntities); + std::static_pointer_cast(_tree)->findEntities(_avatarPosition, radius, foundEntities); - // Whenever you're in an intersection between zones, we will always choose the smallest zone. - auto oldBestZone = _bestZone; - _bestZone = nullptr; // NOTE: Is this what we want? - _bestZoneVolume = std::numeric_limits::max(); + LayeredZones oldLayeredZones(std::move(_layeredZones)); + _layeredZones.clear(); // create a list of entities that actually contain the avatar's position for (auto& entity : foundEntities) { @@ -239,38 +238,34 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3& if (isZone || hasScript) { // now check to see if the point contains our entity, this can be expensive if // the entity has a collision hull - if (entity->contains(avatarPosition)) { + if (entity->contains(_avatarPosition)) { if (entitiesContainingAvatar) { *entitiesContainingAvatar << entity->getEntityItemID(); } // if this entity is a zone and visible, determine if it is the bestZone if (isZone && entity->getVisible()) { - float entityVolumeEstimate = entity->getVolumeEstimate(); - if (entityVolumeEstimate < _bestZoneVolume) { - _bestZoneVolume = entityVolumeEstimate; - _bestZone = std::dynamic_pointer_cast(entity); - } else if (entityVolumeEstimate == _bestZoneVolume) { - // in the case of the volume being equal, we will use the - // EntityItemID to deterministically pick one entity over the other - if (!_bestZone) { - _bestZoneVolume = entityVolumeEstimate; - _bestZone = std::dynamic_pointer_cast(entity); - } else if (entity->getEntityItemID() < _bestZone->getEntityItemID()) { - _bestZoneVolume = entityVolumeEstimate; - _bestZone = std::dynamic_pointer_cast(entity); - } - } + auto zone = std::dynamic_pointer_cast(entity); + _layeredZones.insert(zone); } } } } - if (_bestZone != oldBestZone) { - applyZonePropertiesToScene(_bestZone); - didUpdate = true; + // check if our layered zones have changed + if (_layeredZones.empty()) { + if (oldLayeredZones.empty()) { + return; + } + } else if (!oldLayeredZones.empty()) { + if (_layeredZones.contains(oldLayeredZones)) { + return; + } } + _layeredZones.apply(); + didUpdate = true; }); + return didUpdate; } @@ -286,13 +281,14 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() { // if some amount of time has elapsed since we last checked. We check the time // elapsed because zones or entities might have been created "around us" while we've // been stationary - auto movedEnough = glm::distance(avatarPosition, _lastAvatarPosition) > ZONE_CHECK_DISTANCE; + auto movedEnough = glm::distance(avatarPosition, _avatarPosition) > ZONE_CHECK_DISTANCE; auto enoughTimeElapsed = (now - _lastZoneCheck) > ZONE_CHECK_INTERVAL; if (movedEnough || enoughTimeElapsed) { + _avatarPosition = avatarPosition; _lastZoneCheck = now; QVector entitiesContainingAvatar; - didUpdate = findBestZoneAndMaybeContainingEntities(avatarPosition, &entitiesContainingAvatar); + didUpdate = findBestZoneAndMaybeContainingEntities(&entitiesContainingAvatar); // Note: at this point we don't need to worry about the tree being locked, because we only deal with // EntityItemIDs from here. The callEntityScriptMethod() method is robust against attempting to call scripts @@ -318,7 +314,6 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() { } } _currentEntitiesInside = entitiesContainingAvatar; - _lastAvatarPosition = avatarPosition; } } return didUpdate; @@ -342,24 +337,20 @@ void EntityTreeRenderer::leaveAllEntities() { void EntityTreeRenderer::forceRecheckEntities() { // make sure our "last avatar position" is something other than our current position, // so that on our next chance, we'll check for enter/leave entity events. - _lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE); + _avatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE); } - -void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr zone) { +bool EntityTreeRenderer::applyZoneAndHasSkybox(const std::shared_ptr& zone) { auto textureCache = DependencyManager::get(); auto scene = DependencyManager::get(); auto sceneStage = scene->getStage(); auto skyStage = scene->getSkyStage(); auto sceneKeyLight = sceneStage->getKeyLight(); - // Skybox and procedural skybox data - auto skybox = std::dynamic_pointer_cast(skyStage->getSkybox()); - // If there is no zone, use the default background if (!zone) { _zoneUserData = QString(); - skybox->clear(); + skyStage->getSkybox()->clear(); _pendingSkyboxTexture = false; _skyboxTexture.clear(); @@ -371,7 +362,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrsetAmbientMap(nullptr); skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT); - return; + return false; } // Set the keylight @@ -394,90 +385,127 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrgetKeyLightProperties().getAmbientURL().isEmpty()) { + _ambientTextureURL = zone->getKeyLightProperties().getAmbientURL(); + if (_ambientTextureURL.isEmpty()) { _pendingAmbientTexture = false; _ambientTexture.clear(); } else { - _ambientTexture = textureCache->getTexture(zone->getKeyLightProperties().getAmbientURL(), NetworkTexture::CUBE_TEXTURE); _pendingAmbientTexture = true; - - if (_ambientTexture && _ambientTexture->isLoaded()) { - _pendingAmbientTexture = false; - - auto texture = _ambientTexture->getGPUTexture(); - if (texture) { - sceneKeyLight->setAmbientSphere(texture->getIrradiance()); - sceneKeyLight->setAmbientMap(texture); - isAmbientTextureSet = true; - } else { - qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << zone->getKeyLightProperties().getAmbientURL(); - } - } } // Set the skybox texture + return layerZoneAndHasSkybox(zone); +} + +bool EntityTreeRenderer::layerZoneAndHasSkybox(const std::shared_ptr& zone) { + assert(zone); + + auto textureCache = DependencyManager::get(); + auto scene = DependencyManager::get(); + auto skyStage = scene->getSkyStage(); + auto skybox = skyStage->getSkybox(); + + bool hasSkybox = false; + switch (zone->getBackgroundMode()) { - case BACKGROUND_MODE_SKYBOX: { + case BACKGROUND_MODE_SKYBOX: + hasSkybox = true; + skybox->setColor(zone->getSkyboxProperties().getColorVec3()); + if (_zoneUserData != zone->getUserData()) { _zoneUserData = zone->getUserData(); - skybox->parse(_zoneUserData); + std::dynamic_pointer_cast(skybox)->parse(_zoneUserData); } - if (zone->getSkyboxProperties().getURL().isEmpty()) { - skybox->setCubemap(nullptr); + + _skyboxTextureURL = zone->getSkyboxProperties().getURL(); + if (_skyboxTextureURL.isEmpty()) { _pendingSkyboxTexture = false; _skyboxTexture.clear(); } else { - // Update the Texture of the Skybox with the one pointed by this zone - _skyboxTexture = textureCache->getTexture(zone->getSkyboxProperties().getURL(), NetworkTexture::CUBE_TEXTURE); _pendingSkyboxTexture = true; - - if (_skyboxTexture && _skyboxTexture->isLoaded()) { - _pendingSkyboxTexture = false; - - auto texture = _skyboxTexture->getGPUTexture(); - if (texture) { - skybox->setCubemap(texture); - if (!isAmbientTextureSet) { - sceneKeyLight->setAmbientSphere(texture->getIrradiance()); - sceneKeyLight->setAmbientMap(texture); - isAmbientTextureSet = true; - } - } else { - qCDebug(entitiesrenderer) << "Failed to load skybox texture:" << zone->getSkyboxProperties().getURL(); - skybox->setCubemap(nullptr); - } - } else { - skybox->setCubemap(nullptr); - } } + + applySkyboxAndHasAmbient(); skyStage->setBackgroundMode(model::SunSkyStage::SKY_BOX); + break; - } case BACKGROUND_MODE_INHERIT: default: // Clear the skybox to release its textures - _zoneUserData = QString(); skybox->clear(); + _zoneUserData = QString(); - _skyboxTexture.clear(); _pendingSkyboxTexture = false; + _skyboxTexture.clear(); // Let the application background through - if (isAmbientTextureSet) { + if (applySkyboxAndHasAmbient()) { skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_TEXTURE); } else { skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE); } + break; } - if (!isAmbientTextureSet) { + return hasSkybox; +} + +bool EntityTreeRenderer::applySkyboxAndHasAmbient() { + auto textureCache = DependencyManager::get(); + auto scene = DependencyManager::get(); + auto sceneStage = scene->getStage(); + auto skyStage = scene->getSkyStage(); + auto sceneKeyLight = sceneStage->getKeyLight(); + auto skybox = skyStage->getSkybox(); + + bool isAmbientSet = false; + if (_pendingAmbientTexture && !_ambientTexture) { + _ambientTexture = textureCache->getTexture(_ambientTextureURL, NetworkTexture::CUBE_TEXTURE); + } + if (_ambientTexture && _ambientTexture->isLoaded()) { + _pendingAmbientTexture = false; + + auto texture = _ambientTexture->getGPUTexture(); + if (texture) { + isAmbientSet = true; + sceneKeyLight->setAmbientSphere(texture->getIrradiance()); + sceneKeyLight->setAmbientMap(texture); + } else { + qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL(); + } + } + + if (_pendingSkyboxTexture && !_skyboxTexture) { + _skyboxTexture = textureCache->getTexture(_skyboxTextureURL, NetworkTexture::CUBE_TEXTURE); + } + if (_skyboxTexture && _skyboxTexture->isLoaded()) { + _pendingSkyboxTexture = false; + + auto texture = _skyboxTexture->getGPUTexture(); + if (texture) { + skybox->setCubemap(texture); + if (!isAmbientSet) { + sceneKeyLight->setAmbientSphere(texture->getIrradiance()); + sceneKeyLight->setAmbientMap(texture); + isAmbientSet = true; + } + } else { + qCDebug(entitiesrenderer) << "Failed to load skybox texture:" << _skyboxTexture->getURL(); + skybox->setCubemap(nullptr); + } + } else { + skybox->setCubemap(nullptr); + } + + if (!isAmbientSet) { sceneKeyLight->resetAmbientSphere(); sceneKeyLight->setAmbientMap(nullptr); } + + return isAmbientSet; } const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer entityItem) { @@ -1046,21 +1074,128 @@ void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) { } void EntityTreeRenderer::updateZone(const EntityItemID& id) { - if (!_bestZone) { - // Get in the zone! - auto zone = getTree()->findEntityByEntityItemID(id); - if (zone && zone->contains(_lastAvatarPosition)) { - _currentEntitiesInside << id; - emit enterEntity(id); - if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(id, "enterEntity"); - } - if (zone->getVisible()) { - _bestZone = std::dynamic_pointer_cast(zone); - } - } - } - if (_bestZone && _bestZone->getID() == id) { - applyZonePropertiesToScene(_bestZone); + // Get in the zone! + auto zone = std::dynamic_pointer_cast(getTree()->findEntityByEntityItemID(id)); + if (zone && zone->contains(_avatarPosition)) { + _layeredZones.update(zone); } } + +EntityTreeRenderer::LayeredZones::LayeredZones(LayeredZones&& other) { + // In a swap: + // > All iterators and references remain valid. The past-the-end iterator is invalidated. + bool isSkyboxLayerValid = (other._skyboxLayer != other.end()); + + swap(other); + _map.swap(other._map); + _skyboxLayer = other._skyboxLayer; + + if (!isSkyboxLayerValid) { + _skyboxLayer = end(); + } +} + +void EntityTreeRenderer::LayeredZones::clear() { + std::set::clear(); + _map.clear(); + _skyboxLayer = end(); +} + +std::pair EntityTreeRenderer::LayeredZones::insert(const LayeredZone& layer) { + iterator it; + bool success; + std::tie(it, success) = std::set::insert(layer); + + if (success) { + _map.emplace(it->id, it); + } + + return { it, success }; +} + +void EntityTreeRenderer::LayeredZones::apply() { + assert(_entityTreeRenderer); + + applyPartial(begin()); +} + +void EntityTreeRenderer::LayeredZones::update(std::shared_ptr zone) { + assert(_entityTreeRenderer); + bool isVisible = zone->isVisible(); + + if (empty() && isVisible) { + // there are no zones: set this one + insert(zone); + apply(); + return; + } else { + LayeredZone zoneLayer(zone); + + // should we update? only if this zone is tighter than the current skybox zone + bool shouldUpdate = false; + if (_skyboxLayer == end() || zoneLayer <= *_skyboxLayer) { + shouldUpdate = true; + } + + // find this zone's layer, if it exists + iterator layer = end(); + auto it = _map.find(zoneLayer.id); + if (it != _map.end()) { + layer = it->second; + // if the volume changed, we need to resort the layer (reinsertion) + // if the visibility changed, we need to erase the layer + if (zoneLayer.volume != layer->volume || !isVisible) { + erase(layer); + _map.erase(it); + layer = end(); + } + } + + // (re)insert this zone's layer if necessary + if (layer == end() && isVisible) { + std::tie(layer, std::ignore) = insert(zoneLayer); + _map.emplace(layer->id, layer); + } + + if (shouldUpdate) { + applyPartial(layer); + } + } +} + +void EntityTreeRenderer::LayeredZones::applyPartial(iterator layer) { + bool hasSkybox = false; + _skyboxLayer = end(); + + if (layer == end()) { + if (empty()) { + _entityTreeRenderer->applyZoneAndHasSkybox(nullptr); + return; + } else { // a layer was removed - reapply from beginning + layer = begin(); + } + } + + if (layer == begin()) { + hasSkybox = _entityTreeRenderer->applyZoneAndHasSkybox(layer->zone); + } else { + hasSkybox = _entityTreeRenderer->layerZoneAndHasSkybox(layer->zone); + } + + if (layer != end()) { + while (!hasSkybox && ++layer != end()) { + hasSkybox = _entityTreeRenderer->layerZoneAndHasSkybox(layer->zone); + } + } + + _skyboxLayer = layer; +} + +bool EntityTreeRenderer::LayeredZones::contains(const LayeredZones& other) { + bool result = std::equal(other.begin(), other._skyboxLayer, begin()); + if (result) { + // if valid, set the _skyboxLayer from the other LayeredZones + _skyboxLayer = std::next(begin(), std::distance(other.begin(), other._skyboxLayer)); + } + return result; +} diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 7ca11ccdbb..5c4bc04510 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -95,7 +95,7 @@ public: // For Scene.shouldRenderEntities QList& getEntitiesLastInScene() { return _entityIDsLastInScene; } - std::shared_ptr myAvatarZone() { return _bestZone; } + std::shared_ptr myAvatarZone() { return _layeredZones.getZone(); } signals: void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); @@ -138,9 +138,12 @@ private: void resetEntitiesScriptEngine(); void addEntityToScene(EntityItemPointer entity); - bool findBestZoneAndMaybeContainingEntities(const glm::vec3& avatarPosition, QVector* entitiesContainingAvatar); + bool findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar = nullptr); + + bool applyZoneAndHasSkybox(const std::shared_ptr& zone); + bool layerZoneAndHasSkybox(const std::shared_ptr& zone); + bool applySkyboxAndHasAmbient(); - void applyZonePropertiesToScene(std::shared_ptr zone); void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false); QList _releasedModels; @@ -156,15 +159,9 @@ private: void leaveAllEntities(); void forceRecheckEntities(); - glm::vec3 _lastAvatarPosition { 0.0f }; + glm::vec3 _avatarPosition { 0.0f }; QVector _currentEntitiesInside; - bool _pendingSkyboxTexture { false }; - NetworkTexturePointer _skyboxTexture; - - bool _pendingAmbientTexture { false }; - NetworkTexturePointer _ambientTexture; - bool _wantScripts; QSharedPointer _entitiesScriptEngine; @@ -185,26 +182,62 @@ private: QMultiMap _waitingOnPreload; - std::shared_ptr _bestZone; - float _bestZoneVolume; + class LayeredZone { + public: + LayeredZone(std::shared_ptr zone, QUuid id, float volume) : zone(zone), id(id), volume(volume) {} + LayeredZone(std::shared_ptr zone) : LayeredZone(zone, zone->getID(), zone->getVolumeEstimate()) {} + bool operator<(const LayeredZone& r) const { return std::tie(volume, id) < std::tie(r.volume, r.id); } + bool operator==(const LayeredZone& r) const { return id == r.id; } + bool operator<=(const LayeredZone& r) const { return (*this < r) || (*this == r); } + + std::shared_ptr zone; + QUuid id; + float volume; + }; + + class LayeredZones : public std::set { + public: + LayeredZones(EntityTreeRenderer* parent) : _entityTreeRenderer(parent) {} + LayeredZones(LayeredZones&& other); + + // avoid accidental misconstruction + LayeredZones() = delete; + LayeredZones(const LayeredZones&) = delete; + LayeredZones& operator=(const LayeredZones&) = delete; + LayeredZones& operator=(LayeredZones&&) = delete; + + void clear(); + std::pair insert(const LayeredZone& layer); + + void apply(); + void update(std::shared_ptr zone); + + bool contains(const LayeredZones& other); + + std::shared_ptr getZone() { return empty() ? nullptr : begin()->zone; } + + private: + void applyPartial(iterator layer); + + std::map _map; + iterator _skyboxLayer{ end() }; + EntityTreeRenderer* _entityTreeRenderer; + }; + + LayeredZones _layeredZones; QString _zoneUserData; + NetworkTexturePointer _ambientTexture; + NetworkTexturePointer _skyboxTexture; + QString _ambientTextureURL; + QString _skyboxTextureURL; + bool _pendingAmbientTexture { false }; + bool _pendingSkyboxTexture { false }; quint64 _lastZoneCheck { 0 }; const quint64 ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz const float ZONE_CHECK_DISTANCE = 0.001f; - glm::vec3 _previousKeyLightColor; - float _previousKeyLightIntensity; - float _previousKeyLightAmbientIntensity; - glm::vec3 _previousKeyLightDirection; - bool _previousStageSunModelEnabled; - float _previousStageLongitude; - float _previousStageLatitude; - float _previousStageAltitude; - float _previousStageHour; - int _previousStageDay; - QHash _entitiesInScene; // For Scene.shouldRenderEntities QList _entityIDsLastInScene; diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index bfbc883978..4901a3c61b 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -26,13 +26,15 @@ Skybox::Skybox() { } void Skybox::setColor(const Color& color) { - _empty = false; _schemaBuffer.edit().color = color; + _empty = false; } void Skybox::setCubemap(const gpu::TexturePointer& cubemap) { - _empty = false; _cubemap = cubemap; + if (cubemap) { + _empty = false; + } } void Skybox::updateSchemaBuffer() const { @@ -52,9 +54,9 @@ void Skybox::updateSchemaBuffer() const { } void Skybox::clear() { - _empty = true; _schemaBuffer.edit().color = vec3(0); - setCubemap(nullptr); + _cubemap = nullptr; + _empty = true; } void Skybox::prepare(gpu::Batch& batch, int textureSlot, int bufferSlot) const { diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 97bfcf4fe5..1c5ba40891 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -100,7 +100,9 @@ bool Procedural::parseVersion(const QJsonValue& version) { return (_version == 1 || _version == 2); } -bool Procedural::parseUrl(const QUrl& shaderUrl) { +bool Procedural::parseShader(const QUrl& shaderPath) { + auto shaderUrl = ResourceManager::normalizeURL(shaderPath); + if (!shaderUrl.isValid()) { if (!shaderUrl.isEmpty()) { qWarning() << "Invalid shader URL: " << shaderUrl; @@ -168,7 +170,6 @@ void Procedural::parse(const QJsonObject& proceduralData) { auto version = proceduralData[VERSION_KEY]; auto shaderUrl = proceduralData[URL_KEY].toString(); - shaderUrl = ResourceManager::normalizeURL(shaderUrl); auto uniforms = proceduralData[UNIFORMS_KEY].toObject(); auto channels = proceduralData[CHANNELS_KEY].toArray(); @@ -176,7 +177,7 @@ void Procedural::parse(const QJsonObject& proceduralData) { // Run through parsing regardless of validity to clear old cached resources isValid = parseVersion(version) && isValid; - isValid = parseUrl(shaderUrl) && isValid; + isValid = parseShader(shaderUrl) && isValid; isValid = parseUniforms(uniforms) && isValid; isValid = parseTextures(channels) && isValid; @@ -221,6 +222,7 @@ bool Procedural::ready() { _hasStartedFade = true; _isFading = true; } + return true; } diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index c128da0be0..ac71174727 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -107,7 +107,7 @@ private: // This should only be called from the render thread, as it shares data with Procedural::prepare void parse(const QJsonObject&); bool parseVersion(const QJsonValue& version); - bool parseUrl(const QUrl& url); + bool parseShader(const QUrl& shaderPath); bool parseUniforms(const QJsonObject& uniforms); bool parseTextures(const QJsonArray& channels); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 958d15d9bf..d90566c619 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -164,9 +164,10 @@ function toggleMarketplace() { } } +var TOOLS_PATH = Script.resolvePath("assets/images/tools/"); + var toolBar = (function () { var EDIT_SETTING = "io.highfidelity.isEditting"; // for communication with other scripts - var TOOL_ICON_URL = Script.resolvePath("assets/images/tools/"); var that = {}, toolBar, systemToolbar, @@ -199,7 +200,7 @@ var toolBar = (function () { } function addButton(name, image, handler) { - var imageUrl = TOOL_ICON_URL + image; + var imageUrl = TOOLS_PATH + image; var button = toolBar.addButton({ objectName: name, imageURL: imageUrl, @@ -232,7 +233,7 @@ var toolBar = (function () { systemToolbar = Toolbars.getToolbar(SYSTEM_TOOLBAR); activeButton = systemToolbar.addButton({ objectName: EDIT_TOGGLE_BUTTON, - imageURL: TOOL_ICON_URL + "edit.svg", + imageURL: TOOLS_PATH + "edit.svg", visible: true, alpha: 0.9, buttonState: 1, @@ -1326,13 +1327,14 @@ function pushCommandForSelections(createdEntityData, deletedEntityData) { UndoStack.pushCommand(applyEntityProperties, undoData, applyEntityProperties, redoData); } +var ENTITY_PROPERTIES_URL = Script.resolvePath('html/entityProperties.html'); + var PropertiesTool = function (opts) { var that = {}; - var url = Script.resolvePath('html/entityProperties.html'); var webView = new OverlayWebWindow({ title: 'Entity Properties', - source: url, + source: ENTITY_PROPERTIES_URL, toolWindow: true }); diff --git a/scripts/system/mod.js b/scripts/system/mod.js index e69c2ce2f6..b2c9785539 100644 --- a/scripts/system/mod.js +++ b/scripts/system/mod.js @@ -16,8 +16,11 @@ // grab the toolbar var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); +var ASSETS_PATH = Script.resolvePath("assets"); +var TOOLS_PATH = Script.resolvePath("assets/images/tools/"); + function buttonImageURL() { - return Script.resolvePath("assets/images/tools/" + (Users.canKick ? 'kick.svg' : 'ignore.svg')); + return TOOLS_PATH + (Users.canKick ? 'kick.svg' : 'ignore.svg'); } // setup the mod button and add it to the toolbar @@ -68,7 +71,7 @@ function buttonClicked(){ button.clicked.connect(buttonClicked); function overlayURL() { - return Script.resolvePath("assets") + "/images/" + (Users.canKick ? "kick-target.svg" : "ignore-target.svg"); + return ASSETS_PATH + "/images/" + (Users.canKick ? "kick-target.svg" : "ignore-target.svg"); } function updateOverlays() { diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 673b48961d..f41b0502c8 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -124,7 +124,6 @@ var NotificationType = { var randomSounds = new SoundArray({ localOnly: true }, true); var numberOfSounds = 2; for (var i = 1; i <= numberOfSounds; i++) { - randomSounds.addSound(Script.resolvePath("assets/sounds/notification-general"+ i + ".raw")); } @@ -317,6 +316,8 @@ function notify(notice, button, height, imageProperties, image) { return notificationText; } +var CLOSE_NOTIFICATION_ICON = Script.resolvePath("assets/images/close-small-light.svg"); + // This function creates and sizes the overlays function createNotification(text, notificationType, imageProperties) { var count = (text.match(/\n/g) || []).length, @@ -363,7 +364,7 @@ function createNotification(text, notificationType, imageProperties) { width: 10.0, height: 10.0, subImage: { x: 0, y: 0, width: 10, height: 10 }, - imageURL: Script.resolvePath("assets/images/close-small-light.svg"), + imageURL: CLOSE_NOTIFICATION_ICON, color: { red: 255, green: 255, blue: 255}, visible: true, alpha: backgroundAlpha @@ -534,7 +535,7 @@ function onDomainConnectionRefused(reason) { function onSnapshotTaken(path, notify) { if (notify) { var imageProperties = { - path: Script.resolvePath("file:///" + path), + path: "file:///" + path, aspectRatio: Window.innerWidth / Window.innerHeight } createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties); diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index c9876af039..5eebadd02f 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -32,9 +32,11 @@ function showFeedWindow() { DialogsManager.showFeed(); } +var SNAPSHOT_REVIEW_URL = Script.resolvePath("html/SnapshotReview.html"); + var outstanding; function confirmShare(data) { - var dialog = new OverlayWebWindow('Snapshot Review', Script.resolvePath("html/SnapshotReview.html"), 800, 320); + var dialog = new OverlayWebWindow('Snapshot Review', SNAPSHOT_REVIEW_URL, 800, 320); function onMessage(message) { // Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following: // 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.) diff --git a/scripts/system/users.js b/scripts/system/users.js index a56656a074..76722bd58d 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -13,6 +13,10 @@ (function() { // BEGIN LOCAL_SCOPE +// resolve these paths immediately +var MIN_MAX_BUTTON_SVG = Script.resolvePath("assets/images/tools/min-max-toggle.svg"); +var BASE_URL = Script.resolvePath("assets/images/tools/"); + var PopUpMenu = function (properties) { var value = properties.value, promptOverlay, @@ -25,8 +29,7 @@ var PopUpMenu = function (properties) { MIN_MAX_BUTTON_SVG_WIDTH = 17.1, MIN_MAX_BUTTON_SVG_HEIGHT = 32.5, MIN_MAX_BUTTON_WIDTH = 14, - MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH, - MIN_MAX_BUTTON_SVG = Script.resolvePath("assets/images/tools/min-max-toggle.svg"); + MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH; function positionDisplayOptions() { var y, @@ -223,8 +226,7 @@ var PopUpMenu = function (properties) { var usersWindow = (function () { - var baseURL = Script.resolvePath("assets/images/tools/"), - WINDOW_WIDTH = 260, + var WINDOW_WIDTH = 260, WINDOW_MARGIN = 12, WINDOW_BASE_MARGIN = 6, // A little less is needed in order look correct WINDOW_FONT = { @@ -261,7 +263,7 @@ var usersWindow = (function () { WINDOW_BORDER_ALPHA = 0.5, windowBorder, - MIN_MAX_BUTTON_SVG = baseURL + "min-max-toggle.svg", + MIN_MAX_BUTTON_SVG = BASE_URL + "min-max-toggle.svg", MIN_MAX_BUTTON_SVG_WIDTH = 17.1, MIN_MAX_BUTTON_SVG_HEIGHT = 32.5, MIN_MAX_BUTTON_WIDTH = 14, @@ -293,7 +295,7 @@ var usersWindow = (function () { scrollbarBackgroundHeight, scrollbarBarHeight, FRIENDS_BUTTON_SPACER = 6, // Space before add/remove friends button - FRIENDS_BUTTON_SVG = baseURL + "add-remove-friends.svg", + FRIENDS_BUTTON_SVG = BASE_URL + "add-remove-friends.svg", FRIENDS_BUTTON_SVG_WIDTH = 107, FRIENDS_BUTTON_SVG_HEIGHT = 27, FRIENDS_BUTTON_WIDTH = FRIENDS_BUTTON_SVG_WIDTH,