diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index d4d4f847ee..bc08c6f24a 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -624,8 +624,8 @@ AudioMixerClientData::IgnoreZone& AudioMixerClientData::IgnoreZoneMemo::get(unsi scale = MIN_IGNORE_BOX_SCALE; } - // quadruple the scale (this is arbitrary number chosen for comfort) - const float IGNORE_BOX_SCALE_FACTOR = 4.0f; + // (this is arbitrary number determined empirically for comfort) + const float IGNORE_BOX_SCALE_FACTOR = 2.4f; scale *= IGNORE_BOX_SCALE_FACTOR; // create the box (we use a box for the zone for convenience) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 984884adb2..b6fe765287 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -271,8 +271,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) { otherNodeBox.setScaleStayCentered(minBubbleSize); } - // Quadruple the scale of both bounding boxes - otherNodeBox.embiggen(4.0f); + // Change the scale of both bounding boxes + // (This is an arbitrary number determined empirically) + otherNodeBox.embiggen(2.4f); // Perform the collision check between the two bounding boxes if (nodeBox.touches(otherNodeBox)) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c5857dac53..fc82198076 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2607,10 +2607,55 @@ void Application::initializeGL() { _isGLInitialized = true; } - _glWidget->makeCurrent(); - glClearColor(0.2f, 0.2f, 0.2f, 1); - glClear(GL_COLOR_BUFFER_BIT); - _glWidget->swapBuffers(); + if (!_glWidget->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make window context current"); + } + +#if !defined(DISABLE_QML) + // Build a shared canvas / context for the Chromium processes + { + // Disable signed distance field font rendering on ATI/AMD GPUs, due to + // https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app + std::string vendor{ (const char*)glGetString(GL_VENDOR) }; + if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) { + qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text")); + } + + // Chromium rendering uses some GL functions that prevent nSight from capturing + // frames, so we only create the shared context if nsight is NOT active. + if (!nsightActive()) { + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->setObjectName("ChromiumShareContext"); + _chromiumShareContext->create(_glWidget->qglContext()); + if (!_chromiumShareContext->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make chromium shared context current"); + } + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + _chromiumShareContext->doneCurrent(); + // Restore the GL widget context + if (!_glWidget->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make window context current"); + } + } else { + qCWarning(interfaceapp) << "nSight detected, disabling chrome rendering"; + } + } +#endif + + // Build a shared canvas / context for the QML rendering + { + _qmlShareContext = new OffscreenGLCanvas(); + _qmlShareContext->setObjectName("QmlShareContext"); + _qmlShareContext->create(_glWidget->qglContext()); + if (!_qmlShareContext->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make QML shared context current"); + } + OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext()); + _qmlShareContext->doneCurrent(); + if (!_glWidget->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make window context current"); + } + } // Build an offscreen GL context for the main thread. _offscreenContext = new OffscreenGLCanvas(); @@ -2622,6 +2667,11 @@ void Application::initializeGL() { _offscreenContext->doneCurrent(); _offscreenContext->setThreadContext(); + _glWidget->makeCurrent(); + glClearColor(0.2f, 0.2f, 0.2f, 1); + glClear(GL_COLOR_BUFFER_BIT); + _glWidget->swapBuffers(); + // Move the GL widget context to the render event handler thread _renderEventHandler = new RenderEventHandler(_glWidget->qglContext()); if (!_offscreenContext->makeCurrent()) { @@ -2744,40 +2794,6 @@ extern void setupPreferences(); static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active = false); void Application::initializeUi() { - // Build a shared canvas / context for the Chromium processes -#if !defined(DISABLE_QML) - // Chromium rendering uses some GL functions that prevent nSight from capturing - // frames, so we only create the shared context if nsight is NOT active. - if (!nsightActive()) { - _chromiumShareContext = new OffscreenGLCanvas(); - _chromiumShareContext->setObjectName("ChromiumShareContext"); - _chromiumShareContext->create(_offscreenContext->getContext()); - if (!_chromiumShareContext->makeCurrent()) { - qCWarning(interfaceapp, "Unable to make chromium shared context current"); - } - qt_gl_set_global_share_context(_chromiumShareContext->getContext()); - _chromiumShareContext->doneCurrent(); - // Restore the GL widget context - _offscreenContext->makeCurrent(); - } else { - qCWarning(interfaceapp) << "nSIGHT detected, disabling chrome rendering"; - } -#endif - - // Build a shared canvas / context for the QML rendering - _qmlShareContext = new OffscreenGLCanvas(); - _qmlShareContext->setObjectName("QmlShareContext"); - _qmlShareContext->create(_offscreenContext->getContext()); - if (!_qmlShareContext->makeCurrent()) { - qCWarning(interfaceapp, "Unable to make QML shared context current"); - } - OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext()); - _qmlShareContext->doneCurrent(); - // Restore the GL widget context - _offscreenContext->makeCurrent(); - // Make sure all QML surfaces share the main thread GL context - OffscreenQmlSurface::setSharedContext(_offscreenContext->getContext()); - AddressBarDialog::registerType(); ErrorDialog::registerType(); LoginDialog::registerType(); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 834de2150d..b960c0b703 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -227,10 +227,13 @@ QString QmlCommerce::getInstalledApps() { QString scriptURL = appFileJsonObject["scriptURL"].toString(); // If the script .app.json is on the user's local disk but the associated script isn't running - // for some reason, start that script again. + // for some reason (i.e. the user stopped it from Running Scripts), + // delete the .app.json from the user's local disk. if (!runningScripts.contains(scriptURL)) { - if ((DependencyManager::get()->loadScript(scriptURL.trimmed())).isNull()) { - qCDebug(commerce) << "Couldn't start script while checking installed apps."; + if (!appFile.remove()) { + qCWarning(commerce) + << "Couldn't delete local .app.json file (app's script isn't running). App filename is:" + << appFileName; } } } else { diff --git a/interface/src/ui/OverlayConductor.cpp b/interface/src/ui/OverlayConductor.cpp index e7e3c91d13..d131bb3467 100644 --- a/interface/src/ui/OverlayConductor.cpp +++ b/interface/src/ui/OverlayConductor.cpp @@ -18,7 +18,6 @@ #include "InterfaceLogging.h" OverlayConductor::OverlayConductor() { - } OverlayConductor::~OverlayConductor() { @@ -33,8 +32,8 @@ bool OverlayConductor::headOutsideOverlay() const { glm::vec3 uiPos = uiTransform.getTranslation(); glm::vec3 uiForward = uiTransform.getRotation() * glm::vec3(0.0f, 0.0f, -1.0f); - const float MAX_COMPOSITOR_DISTANCE = 0.99f; // If you're 1m from center of ui sphere, you're at the surface. - const float MAX_COMPOSITOR_ANGLE = 180.0f; // rotation check is effectively disabled + const float MAX_COMPOSITOR_DISTANCE = 0.99f; // If you're 1m from center of ui sphere, you're at the surface. + const float MAX_COMPOSITOR_ANGLE = 180.0f; // rotation check is effectively disabled if (glm::distance(uiPos, hmdPos) > MAX_COMPOSITOR_DISTANCE || glm::dot(uiForward, hmdForward) < cosf(glm::radians(MAX_COMPOSITOR_ANGLE))) { return true; @@ -43,10 +42,9 @@ bool OverlayConductor::headOutsideOverlay() const { } bool OverlayConductor::updateAvatarIsAtRest() { - auto myAvatar = DependencyManager::get()->getMyAvatar(); - const quint64 REST_ENABLE_TIME_USECS = 1000 * 1000; // 1 s + const quint64 REST_ENABLE_TIME_USECS = 1000 * 1000; // 1 s const quint64 REST_DISABLE_TIME_USECS = 200 * 1000; // 200 ms const float AT_REST_THRESHOLD = 0.01f; @@ -69,31 +67,6 @@ bool OverlayConductor::updateAvatarIsAtRest() { return _currentAtRest; } -bool OverlayConductor::updateAvatarHasDriveInput() { - auto myAvatar = DependencyManager::get()->getMyAvatar(); - - const quint64 DRIVE_ENABLE_TIME_USECS = 200 * 1000; // 200 ms - const quint64 DRIVE_DISABLE_TIME_USECS = 1000 * 1000; // 1 s - - bool desiredDriving = myAvatar->hasDriveInput(); - if (desiredDriving != _desiredDriving) { - // start timer - _desiredDrivingTimer = usecTimestampNow() + (desiredDriving ? DRIVE_ENABLE_TIME_USECS : DRIVE_DISABLE_TIME_USECS); - } - - _desiredDriving = desiredDriving; - - if (_desiredDrivingTimer != 0 && usecTimestampNow() > _desiredDrivingTimer) { - // timer expired - // change state! - _currentDriving = _desiredDriving; - // disable timer - _desiredDrivingTimer = 0; - } - - return _currentDriving; -} - void OverlayConductor::centerUI() { // place the overlay at the current hmd position in sensor space auto camMat = cancelOutRollAndPitch(qApp->getHMDSensorPose()); @@ -115,20 +88,19 @@ void OverlayConductor::update(float dt) { _hmdMode = false; } - bool prevDriving = _currentDriving; - bool isDriving = updateAvatarHasDriveInput(); - bool drivingChanged = prevDriving != isDriving; bool isAtRest = updateAvatarIsAtRest(); + bool isMoving = !isAtRest; + bool shouldRecenter = false; - if (_flags & SuppressedByDrive) { - if (!isDriving) { - _flags &= ~SuppressedByDrive; - shouldRecenter = true; + if (_flags & SuppressedByMove) { + if (!isMoving) { + _flags &= ~SuppressedByMove; + shouldRecenter = true; } } else { - if (myAvatar->getClearOverlayWhenMoving() && drivingChanged && isDriving) { - _flags |= SuppressedByDrive; + if (myAvatar->getClearOverlayWhenMoving() && isMoving) { + _flags |= SuppressedByMove; } } @@ -143,7 +115,6 @@ void OverlayConductor::update(float dt) { } } - bool targetVisible = Menu::getInstance()->isOptionChecked(MenuOption::Overlays) && (0 == (_flags & SuppressMask)); if (targetVisible != currentVisible) { offscreenUi->setPinned(!targetVisible); diff --git a/interface/src/ui/OverlayConductor.h b/interface/src/ui/OverlayConductor.h index cdd596a7bc..cf69c32fc5 100644 --- a/interface/src/ui/OverlayConductor.h +++ b/interface/src/ui/OverlayConductor.h @@ -23,23 +23,17 @@ public: private: bool headOutsideOverlay() const; - bool updateAvatarHasDriveInput(); bool updateAvatarIsAtRest(); enum SupressionFlags { - SuppressedByDrive = 0x01, + SuppressedByMove = 0x01, SuppressedByHead = 0x02, SuppressMask = 0x03, }; - uint8_t _flags { SuppressedByDrive }; + uint8_t _flags { SuppressedByMove }; bool _hmdMode { false }; - // used by updateAvatarHasDriveInput - uint64_t _desiredDrivingTimer { 0 }; - bool _desiredDriving { false }; - bool _currentDriving { false }; - // used by updateAvatarIsAtRest uint64_t _desiredAtRestTimer { 0 }; bool _desiredAtRest { true }; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index c8056c11c8..875352ebb4 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -131,6 +131,9 @@ void ModelOverlay::update(float deltatime) { if (!_texturesLoaded && _model->getGeometry() && _model->getGeometry()->areTexturesLoaded()) { _texturesLoaded = true; + if (!_modelTextures.isEmpty()) { + _model->setTextures(_modelTextures); + } _model->updateRenderItems(); } } @@ -229,8 +232,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { if (texturesValue.isValid() && texturesValue.canConvert(QVariant::Map)) { _texturesLoaded = false; QVariantMap textureMap = texturesValue.toMap(); - QMetaObject::invokeMethod(_model.get(), "setTextures", Qt::AutoConnection, - Q_ARG(const QVariantMap&, textureMap)); + _modelTextures = textureMap; } auto groupCulledValue = properties["isGroupCulled"]; diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index b6957a2712..2b50f6be97 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -157,18 +157,19 @@ void TextureBaker::processTexture() { return; } - const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat()); - if (name == nullptr) { - handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString()); - return; - } // attempt to write the baked texture to the destination file path - { + if (memKTX->_header.isCompressed()) { + const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat()); + if (name == nullptr) { + handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString()); + return; + } + const char* data = reinterpret_cast(memKTX->_storage->data()); const size_t length = memKTX->_storage->size(); - auto fileName = _baseFilename + BAKED_TEXTURE_BCN_SUFFIX; + auto fileName = _baseFilename + "_" + name + ".ktx"; auto filePath = _outputDirectory.absoluteFilePath(fileName); QFile bakedTextureFile { filePath }; if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) { @@ -177,6 +178,18 @@ void TextureBaker::processTexture() { } _outputFiles.push_back(filePath); meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName; + } else { + const char* data = reinterpret_cast(memKTX->_storage->data()); + const size_t length = memKTX->_storage->size(); + + auto fileName = _baseFilename + ".ktx"; + auto filePath = _outputDirectory.absoluteFilePath(fileName); + QFile ktxTextureFile { filePath }; + if (!ktxTextureFile.open(QIODevice::WriteOnly) || ktxTextureFile.write(data, length) == -1) { + handleError("Could not write ktx texture for " + _textureURL.toString()); + return; + } + _outputFiles.push_back(filePath); } diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv index cab76227c4..1d4261b1cc 100644 --- a/libraries/entities-renderer/src/textured_particle.slv +++ b/libraries/entities-renderer/src/textured_particle.slv @@ -37,8 +37,8 @@ layout(std140) uniform particleBuffer { ParticleUniforms particle; }; -in vec3 inPosition; -in vec2 inColor; // This is actual Lifetime + Seed +layout(location=0) in vec3 inPosition; +layout(location=2) in vec2 inColor; // This is actual Lifetime + Seed out vec4 varColor; out vec2 varTexcoord; diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index d06b74b724..6df15129a2 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -38,6 +38,11 @@ ResourceManager::ResourceManager(bool atpSupportEnabled) : _atpSupportEnabled(at _thread.start(); } +ResourceManager::~ResourceManager() { + _thread.terminate(); + _thread.wait(); +} + void ResourceManager::setUrlPrefixOverride(const QString& prefix, const QString& replacement) { QMutexLocker locker(&_prefixMapLock); if (replacement.isEmpty()) { diff --git a/libraries/networking/src/ResourceManager.h b/libraries/networking/src/ResourceManager.h index 9fc636f5fe..a79222d2d8 100644 --- a/libraries/networking/src/ResourceManager.h +++ b/libraries/networking/src/ResourceManager.h @@ -28,6 +28,7 @@ class ResourceManager: public QObject, public Dependency { public: ResourceManager(bool atpSupportEnabled = true); + ~ResourceManager(); void setUrlPrefixOverride(const QString& prefix, const QString& replacement); QString normalizeURL(const QString& urlString); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7cfb1f6bc8..9bf6c31784 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1074,15 +1074,11 @@ int Model::getLastFreeJointIndex(int jointIndex) const { void Model::setTextures(const QVariantMap& textures) { if (isLoaded()) { - _needsUpdateTextures = true; + _pendingTextures.clear(); _needsFixupInScene = true; _renderGeometry->setTextures(textures); } else { - // FIXME(Huffman): Disconnect previously connected lambdas so we don't set textures multiple - // after the geometry has finished loading. - connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, [this, textures]() { - _renderGeometry->setTextures(textures); - }); + _pendingTextures = textures; } } @@ -1106,7 +1102,8 @@ void Model::setURL(const QUrl& url) { } _needsReload = true; - _needsUpdateTextures = true; + // One might be tempted to _pendingTextures.clear(), thinking that a new URL means an old texture doesn't apply. + // But sometimes, particularly when first setting the values, the texture might be set first. So let's not clear here. _visualGeometryRequestFailed = false; _needsFixupInScene = true; invalidCalculatedMeshBoxes(); @@ -1123,6 +1120,8 @@ void Model::setURL(const QUrl& url) { void Model::loadURLFinished(bool success) { if (!success) { _visualGeometryRequestFailed = true; + } else if (!_pendingTextures.empty()) { + setTextures(_pendingTextures); } emit setURLFinished(success); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 4180288106..67e6d178ea 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -457,7 +457,7 @@ protected: bool _needsFixupInScene { true }; // needs to be removed/re-added to scene bool _needsReload { true }; bool _needsUpdateClusterMatrices { true }; - mutable bool _needsUpdateTextures { true }; + QVariantMap _pendingTextures { }; friend class ModelMeshPartPayload; Rig _rig; diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 08b88fe74d..91c8d89daf 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -273,7 +273,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.shouldSendStart = false; this.equipedWithSecondary = false; this.handHasBeenRightsideUp = false; - this.mouseEquip = false; this.parameters = makeDispatcherModuleParameters( 300, @@ -283,11 +282,10 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var equipHotspotBuddy = new EquipHotspotBuddy(); - this.setMessageGrabData = function(entityProperties, mouseEquip) { + this.setMessageGrabData = function(entityProperties) { if (entityProperties) { this.messageGrabEntity = true; this.grabEntityProps = entityProperties; - this.mouseEquip = mouseEquip; } }; @@ -584,7 +582,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.targetEntityID = null; this.messageGrabEntity = false; this.grabEntityProps = null; - this.mouseEquip = false; }; this.updateInputs = function (controllerData) { @@ -630,14 +627,12 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa // if the potentialHotspot is cloneable, clone it and return it // if the potentialHotspot os not cloneable and locked return null - if (potentialEquipHotspot && (((this.triggerSmoothedSqueezed() || this.secondarySmoothedSqueezed()) && !this.waitForTriggerRelease) || this.messageGrabEntity)) { this.grabbedHotspot = potentialEquipHotspot; this.targetEntityID = this.grabbedHotspot.entityID; this.startEquipEntity(controllerData); - this.messageGrabEntity = false; this.equipedWithSecondary = this.secondarySmoothedSqueezed(); return makeRunningValues(true, [potentialEquipHotspot.entityID], []); } else { @@ -661,7 +656,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var timestamp = Date.now(); this.updateInputs(controllerData); - if (!this.mouseEquip && !this.isTargetIDValid(controllerData)) { + if (!this.messageGrabEntity && !this.isTargetIDValid(controllerData)) { this.endEquipEntity(); return makeRunningValues(false, [], []); } @@ -762,9 +757,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var equipModule = (data.hand === "left") ? leftEquipEntity : rightEquipEntity; var entityProperties = Entities.getEntityProperties(data.entityID, DISPATCHER_PROPERTIES); entityProperties.id = data.entityID; - var mouseEquip = false; - equipModule.setMessageGrabData(entityProperties, mouseEquip); - + equipModule.setMessageGrabData(entityProperties); } catch (e) { print("WARNING: equipEntity.js -- error parsing Hifi-Hand-Grab message: " + message); } @@ -812,15 +805,14 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var distanceToLeftHand = Vec3.distance(entityProperties.position, leftHandPosition); var leftHandAvailable = leftEquipEntity.targetEntityID === null; var rightHandAvailable = rightEquipEntity.targetEntityID === null; - var mouseEquip = true; if (rightHandAvailable && (distanceToRightHand < distanceToLeftHand || !leftHandAvailable)) { // clear any existing grab actions on the entity now (their later removal could affect bootstrapping flags) clearGrabActions(entityID); - rightEquipEntity.setMessageGrabData(entityProperties, mouseEquip); + rightEquipEntity.setMessageGrabData(entityProperties); } else if (leftHandAvailable && (distanceToLeftHand < distanceToRightHand || !rightHandAvailable)) { // clear any existing grab actions on the entity now (their later removal could affect bootstrapping flags) clearGrabActions(entityID); - leftEquipEntity.setMessageGrabData(entityProperties, mouseEquip); + leftEquipEntity.setMessageGrabData(entityProperties); } } } diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 799a974fd6..9b91d06d41 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -306,13 +306,21 @@ // change pricing to GET/BUY on button hover $('body').on('mouseenter', '#price-or-edit .price', function () { var $this = $(this); + var buyString = "BUY"; + var getString = "GET"; + // Protection against the button getting stuck in the "BUY"/"GET" state. + // That happens when the browser gets two MOUSEENTER events before getting a + // MOUSELEAVE event. + if ($this.text() === buyString || $this.text() === getString) { + return; + } $this.data('initialHtml', $this.html()); var cost = $(this).parent().siblings().text(); if (parseInt(cost) > 0) { - $this.text('BUY'); + $this.text(buyString); } else { - $this.text('GET'); + $this.text(getString); } }); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 41774629e7..7175685b4f 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -40,7 +40,6 @@ var HOVER_TEXTURES = { var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6}; var SELECTED_COLOR = {red: 0xF3, green: 0x91, blue: 0x29}; var HOVER_COLOR = {red: 0xD0, green: 0xD0, blue: 0xD0}; // almost white for now -var conserveResources = true; Script.include("/~/system/libraries/controllers.js"); @@ -431,7 +430,7 @@ function addAvatarNode(id) { alpha: 0.8, color: color(selected, false, 0.0), ignoreRayIntersection: false - }, selected, !conserveResources); + }, selected, true); } // Each open/refresh will capture a stable set of avatarsOfInterest, within the specified filter. var avatarsOfInterest = {}; @@ -496,7 +495,6 @@ function populateNearbyUserList(selectData, oldAudioData) { print('PAL data:', JSON.stringify(avatarPalDatum)); }); getConnectionData(false, location.domainID); // Even admins don't get relationship data in requestUsernameFromID (which is still needed for admin status, which comes from domain). - conserveResources = Object.keys(avatarsOfInterest).length > 20; sendToQml({ method: 'nearbyUsers', params: data }); if (selectData) { selectData[2] = true; @@ -719,7 +717,7 @@ function onTabletScreenChanged(type, url) { ContextOverlay.enabled = false; Users.requestsDomainListData = true; - audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS); + audioTimer = createAudioInterval(AUDIO_LEVEL_UPDATE_INTERVAL_MS); tablet.tabletShownChanged.connect(tabletVisibilityChanged); Script.update.connect(updateOverlays); @@ -874,7 +872,6 @@ startup(); var isWired = false; var audioTimer; var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) -var AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS = 300; function off() { if (isWired) { Script.update.disconnect(updateOverlays); diff --git a/server-console/src/main.js b/server-console/src/main.js index 8a92fc8a5d..d4447c432c 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -75,10 +75,14 @@ function getBuildInfo() { } const buildInfo = getBuildInfo(); -function getRootHifiDataDirectory() { +function getRootHifiDataDirectory(local) { var organization = buildInfo.organization; if (osType == 'Windows_NT') { - return path.resolve(osHomeDir(), 'AppData/Roaming', organization); + if (local) { + return path.resolve(osHomeDir(), 'AppData/Local', organization); + } else { + return path.resolve(osHomeDir(), 'AppData/Roaming', organization); + } } else if (osType == 'Darwin') { return path.resolve(osHomeDir(), 'Library/Application Support', organization); } else { @@ -94,8 +98,8 @@ function getAssignmentClientResourcesDirectory() { return path.join(getRootHifiDataDirectory(), '/assignment-client'); } -function getApplicationDataDirectory() { - return path.join(getRootHifiDataDirectory(), '/Server Console'); +function getApplicationDataDirectory(local) { + return path.join(getRootHifiDataDirectory(local), '/Server Console'); } // Update lock filepath @@ -104,7 +108,7 @@ const UPDATER_LOCK_FULL_PATH = getRootHifiDataDirectory() + "/" + UPDATER_LOCK_F // Configure log global.log = require('electron-log'); -const logFile = getApplicationDataDirectory() + '/log.txt'; +const logFile = getApplicationDataDirectory(true) + '/log.txt'; fs.ensureFileSync(logFile); // Ensure file exists log.transports.file.maxSize = 5 * 1024 * 1024; log.transports.file.file = logFile; @@ -221,7 +225,19 @@ function deleteOldFiles(directoryPath, maxAgeInSeconds, filenameRegex) { } } -var logPath = path.join(getApplicationDataDirectory(), '/logs'); +var oldLogPath = path.join(getApplicationDataDirectory(), '/logs'); +var logPath = path.join(getApplicationDataDirectory(true), '/logs'); + +if (oldLogPath != logPath) { + console.log("Migrating old logs from " + oldLogPath + " to " + logPath); + fs.copy(oldLogPath, logPath, err => { + if (err) { + console.error(err); + } else { + console.log('success!'); + } + }) +} log.debug("Log directory:", logPath); log.debug("Data directory:", getRootHifiDataDirectory()); @@ -433,13 +449,6 @@ var labels = { logWindow.open(); } }, - restoreBackup: { - label: 'Restore Backup Instructions', - click: function() { - var folder = getRootHifiDataDirectory() + "/Server Backup"; - openBackupInstructions(folder); - } - }, share: { label: 'Share', click: function() { @@ -475,7 +484,6 @@ function buildMenuArray(serverState) { menuArray.push(labels.stopServer); menuArray.push(labels.settings); menuArray.push(labels.viewLogs); - menuArray.push(labels.restoreBackup); menuArray.push(separator); menuArray.push(labels.share); menuArray.push(separator); @@ -542,103 +550,6 @@ function backupResourceDirectories(folder) { } } -function openBackupInstructions(folder) { - // Explain user how to restore server - var window = new BrowserWindow({ - icon: appIcon, - width: 800, - height: 520, - }); - window.loadURL('file://' + __dirname + '/content-update.html'); - if (!debug) { - window.setMenu(null); - } - window.show(); - - electron.ipcMain.on('setSize', function(event, obj) { - window.setSize(obj.width, obj.height); - }); - electron.ipcMain.on('ready', function() { - log.debug("got ready"); - window.webContents.send('update', folder); - }); -} -function backupResourceDirectoriesAndRestart() { - homeServer.stop(); - - var folder = getRootHifiDataDirectory() + "/Server Backup - " + Date.now(); - if (backupResourceDirectories(folder)) { - maybeInstallDefaultContentSet(onContentLoaded); - openBackupInstructions(folder); - } else { - dialog.showMessageBox({ - type: 'warning', - buttons: ['Ok'], - title: 'Update Error', - message: 'There was an error updating the content, aborting.' - }, function() {}); - } -} - -function checkNewContent() { - if (argv.noUpdater) { - return; - } - - // Start downloading content set - var req = request.head({ - url: HOME_CONTENT_URL - }, function (error, response, body) { - if (error === null) { - var localContent = Date.parse(userConfig.get('homeContentLastModified')); - var remoteContent = Date.parse(response.headers['last-modified']); - - var shouldUpdate = isNaN(localContent) || (!isNaN(remoteContent) && (remoteContent > localContent)); - - var wantDebug = false; - if (wantDebug) { - log.debug('Last Modified: ' + response.headers['last-modified']); - log.debug(localContent + " " + remoteContent + " " + shouldUpdate + " " + new Date()); - log.debug("Remote content is " + (shouldUpdate ? "newer" : "older") + " that local content."); - } - - if (shouldUpdate) { - dialog.showMessageBox({ - type: 'question', - buttons: ['Yes', 'No'], - defaultId: 1, - cancelId: 1, - title: 'High Fidelity Sandbox', - message: 'A newer version of the home content set is available.\nDo you wish to update?', - noLink: true, - }, function(idx) { - if (idx === 0) { - dialog.showMessageBox({ - type: 'warning', - buttons: ['Yes', 'No'], - defaultId: 1, - cancelId: 1, - title: 'Are you sure?', - message: 'Updating with the new content will remove all your current content and settings and place them in a backup folder.\nAre you sure?', - noLink: true, - }, function(idx) { - if (idx === 0) { - backupResourceDirectoriesAndRestart(); - } - }); - } else { - // They don't want to update, mark content set as current - userConfig.set('homeContentLastModified', new Date()); - userConfig.save(configPath); - } - }); - } else if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) { - backupResourceDirectoriesAndRestart(); - } - } - }); -} - function removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory) { if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) { log.debug('Removing incomplete content update files before copying new update'); @@ -681,7 +592,6 @@ function maybeInstallDefaultContentSet(onComplete) { log.debug("User has existing data, suppressing downloader"); onComplete(); - checkNewContent(); return; } diff --git a/tools/oven/src/BakerCLI.cpp b/tools/oven/src/BakerCLI.cpp index a7b8401269..0db70f6fe4 100644 --- a/tools/oven/src/BakerCLI.cpp +++ b/tools/oven/src/BakerCLI.cpp @@ -16,6 +16,8 @@ #include #include +#include + #include "OvenCLIApplication.h" #include "ModelBakingLoggingCategory.h" #include "FBXBaker.h" @@ -38,17 +40,15 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& static const QString MODEL_EXTENSION { "fbx" }; static const QString SCRIPT_EXTENSION { "js" }; - QString extension = type; - - if (extension.isNull()) { - auto url = inputUrl.toDisplayString(); - extension = url.mid(url.lastIndexOf('.')); - } - // check what kind of baker we should be creating - bool isFBX = extension == MODEL_EXTENSION; - bool isScript = extension == SCRIPT_EXTENSION; + bool isFBX = type == MODEL_EXTENSION; + bool isScript = type == SCRIPT_EXTENSION; + // If the type doesn't match the above, we assume we have a texture, and the type specified is the + // texture usage type (albedo, cubemap, normals, etc.) + auto url = inputUrl.toDisplayString(); + auto idx = url.lastIndexOf('.'); + auto extension = idx >= 0 ? url.mid(idx + 1).toLower() : ""; bool isSupportedImage = QImageReader::supportedImageFormats().contains(extension.toLatin1()); _outputPath = outputPath; @@ -65,7 +65,29 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& _baker = std::unique_ptr { new JSBaker(inputUrl, outputPath) }; _baker->moveToThread(Oven::instance().getNextWorkerThread()); } else if (isSupportedImage) { - _baker = std::unique_ptr { new TextureBaker(inputUrl, image::TextureUsage::CUBE_TEXTURE, outputPath) }; + static const std::unordered_map STRING_TO_TEXTURE_USAGE_TYPE_MAP { + { "default", image::TextureUsage::DEFAULT_TEXTURE }, + { "strict", image::TextureUsage::STRICT_TEXTURE }, + { "albedo", image::TextureUsage::ALBEDO_TEXTURE }, + { "normal", image::TextureUsage::NORMAL_TEXTURE }, + { "bump", image::TextureUsage::BUMP_TEXTURE }, + { "specular", image::TextureUsage::SPECULAR_TEXTURE }, + { "metallic", image::TextureUsage::METALLIC_TEXTURE }, + { "roughness", image::TextureUsage::ROUGHNESS_TEXTURE }, + { "gloss", image::TextureUsage::GLOSS_TEXTURE }, + { "emissive", image::TextureUsage::EMISSIVE_TEXTURE }, + { "cube", image::TextureUsage::CUBE_TEXTURE }, + { "occlusion", image::TextureUsage::OCCLUSION_TEXTURE }, + { "scattering", image::TextureUsage::SCATTERING_TEXTURE }, + { "lightmap", image::TextureUsage::LIGHTMAP_TEXTURE }, + }; + + auto it = STRING_TO_TEXTURE_USAGE_TYPE_MAP.find(type); + if (it == STRING_TO_TEXTURE_USAGE_TYPE_MAP.end()) { + qCDebug(model_baking) << "Unknown texture usage type:" << type; + QCoreApplication::exit(OVEN_STATUS_CODE_FAIL); + } + _baker = std::unique_ptr { new TextureBaker(inputUrl, it->second, outputPath) }; _baker->moveToThread(Oven::instance().getNextWorkerThread()); } else { qCDebug(model_baking) << "Failed to determine baker type for file" << inputUrl; diff --git a/tools/oven/src/OvenCLIApplication.cpp b/tools/oven/src/OvenCLIApplication.cpp index ab3178db01..6f87359134 100644 --- a/tools/oven/src/OvenCLIApplication.cpp +++ b/tools/oven/src/OvenCLIApplication.cpp @@ -14,11 +14,14 @@ #include #include +#include + #include "BakerCLI.h" static const QString CLI_INPUT_PARAMETER = "i"; static const QString CLI_OUTPUT_PARAMETER = "o"; static const QString CLI_TYPE_PARAMETER = "t"; +static const QString CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER = "disable-texture-compression"; OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) : QCoreApplication(argc, argv) @@ -29,7 +32,8 @@ OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) : parser.addOptions({ { CLI_INPUT_PARAMETER, "Path to file that you would like to bake.", "input" }, { CLI_OUTPUT_PARAMETER, "Path to folder that will be used as output.", "output" }, - { CLI_TYPE_PARAMETER, "Type of asset.", "type" } + { CLI_TYPE_PARAMETER, "Type of asset.", "type" }, + { CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER, "Disable texture compression." } }); parser.addHelpOption(); @@ -40,6 +44,15 @@ OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) : QUrl inputUrl(QDir::fromNativeSeparators(parser.value(CLI_INPUT_PARAMETER))); QUrl outputUrl(QDir::fromNativeSeparators(parser.value(CLI_OUTPUT_PARAMETER))); QString type = parser.isSet(CLI_TYPE_PARAMETER) ? parser.value(CLI_TYPE_PARAMETER) : QString::null; + + if (parser.isSet(CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER)) { + qDebug() << "Disabling texture compression"; + image::setColorTexturesCompressionEnabled(false); + image::setGrayscaleTexturesCompressionEnabled(false); + image::setNormalTexturesCompressionEnabled(false); + image::setCubeTexturesCompressionEnabled(false); + } + QMetaObject::invokeMethod(cli, "bakeFile", Qt::QueuedConnection, Q_ARG(QUrl, inputUrl), Q_ARG(QString, outputUrl.toString()), Q_ARG(QString, type)); } else {