diff --git a/BUILD.md b/BUILD.md index eea91e118b..2d94d1b5b1 100644 --- a/BUILD.md +++ b/BUILD.md @@ -80,6 +80,13 @@ Where /path/to/directory is the path to a directory where you wish the build fil // The type of release. RELEASE_TYPE=PRODUCTION|PR|DEV + // The Interface will have a custom default home and startup location. + INITIAL_STARTUP_LOCATION=Location/IP/URL + + // Code-signing environment variables must be set during runtime of CMake AND globally when the signing takes place. + HF_PFX_FILE=Path to certificate + HF_PFX_PASSPHRASE=Passphrase for certificate + // Determine the build type PRODUCTION_BUILD=0|1 PR_BUILD=0|1 diff --git a/INSTALL.md b/INSTALL.md index af398ee6b3..994725ac28 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,5 +1,7 @@ # Creating an Installer +*Last Updated on August 24, 2020* + Follow the [build guide](BUILD.md) to figure out how to build Vircadia for your platform. During generation, CMake should produce an `install` target and a `package` target. @@ -13,6 +15,8 @@ To produce an installer, run the `package` target. However you will want to foll #### Windows +##### Prerequisites + To produce an executable installer on Windows, the following are required: 1. [7-zip]() @@ -59,6 +63,12 @@ To produce an executable installer on Windows, the following are required: 1. [Node.JS and NPM]() 1. Install version 10.15.0 LTS +##### Code Signing (optional) + +For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PASSPHRASE` environment variables to be present during CMake runtime and globally as we proceed to package the installer. + +##### Creating the Installer + 1. Perform a clean cmake from a new terminal. 1. Open the `vircadia.sln` solution with elevated (administrator) permissions on Visual Studio and select the **Release** configuration. 1. Build the solution. diff --git a/assignment-client/src/AssignmentClientApp.cpp b/assignment-client/src/AssignmentClientApp.cpp index c5b228bd16..1dd050fcb9 100644 --- a/assignment-client/src/AssignmentClientApp.cpp +++ b/assignment-client/src/AssignmentClientApp.cpp @@ -183,7 +183,7 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : } QString assignmentServerHostname; - if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) { + if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION)) { assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString(); } if (parser.isSet(assignmentServerHostnameOption)) { @@ -192,7 +192,7 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : // check for an overriden assignment server port quint16 assignmentServerPort = DEFAULT_DOMAIN_SERVER_PORT; - if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) { + if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION)) { assignmentServerPort = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toUInt(); } diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 1b7b3dbe8e..0f47a2fcb3 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -23,6 +23,7 @@ macro(SET_PACKAGING_PARAMETERS) set_from_env(RELEASE_TYPE RELEASE_TYPE "DEV") set_from_env(RELEASE_NUMBER RELEASE_NUMBER "") set_from_env(STABLE_BUILD STABLE_BUILD 0) + set_from_env(INITIAL_STARTUP_LOCATION INITIAL_STARTUP_LOCATION "") message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}") diff --git a/cmake/templates/BuildInfo.h.in b/cmake/templates/BuildInfo.h.in index 9fc9d9be81..02f6a50919 100644 --- a/cmake/templates/BuildInfo.h.in +++ b/cmake/templates/BuildInfo.h.in @@ -22,11 +22,12 @@ namespace BuildInfo { const QString DOMAIN_SERVER_NAME = "domain-server"; const QString AC_CLIENT_SERVER_NAME = "ac-client"; const QString MODIFIED_ORGANIZATION = "@BUILD_ORGANIZATION@"; - const QString ORGANIZATION_DOMAIN = "highfidelity.io"; + const QString ORGANIZATION_DOMAIN = "vircadia.com"; const QString VERSION = "@BUILD_VERSION@"; const QString BUILD_NUMBER = "@BUILD_NUMBER@"; const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@"; const QString BUILD_TIME = "@BUILD_TIME@"; + const QString INITIAL_STARTUP_LOCATION = "@INITIAL_STARTUP_LOCATION@"; enum BuildType { Dev, diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 496209a2a8..2fefb6bff4 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -49,6 +49,12 @@ ScrollingWindow { desktop.setAutoAdd(auto); } + function openExternalBrowser() { + Qt.openUrlExternally(addressBar.text); + root.shown = false; + root.windowClosed(); + } + Item { id:item width: pane.contentWidth @@ -58,34 +64,49 @@ ScrollingWindow { id: buttons spacing: 4 anchors.top: parent.top - anchors.topMargin: 8 + anchors.topMargin: 4 anchors.left: parent.left anchors.leftMargin: 8 HiFiGlyphs { - id: back; - enabled: webview.canGoBack; + id: back + enabled: webview.canGoBack text: hifi.glyphs.backward - color: enabled ? hifi.colors.text : hifi.colors.disabledText + color: enabled ? (backMouseArea.containsMouse ? hifi.colors.blueHighlight : hifi.colors.faintGray) : hifi.colors.lightGray size: 48 - MouseArea { anchors.fill: parent; onClicked: webview.goBack() } + MouseArea { + id: backMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: webview.goBack(); + } } HiFiGlyphs { - id: forward; - enabled: webview.canGoForward; + id: forward + enabled: webview.canGoForward text: hifi.glyphs.forward - color: enabled ? hifi.colors.text : hifi.colors.disabledText + color: enabled ? (forwardMouseArea.containsMouse ? hifi.colors.blueHighlight : hifi.colors.faintGray) : hifi.colors.lightGray size: 48 - MouseArea { anchors.fill: parent; onClicked: webview.goForward() } + MouseArea { + id: forwardMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: webview.goForward(); + } } HiFiGlyphs { - id: reload; - enabled: webview.canGoForward; + id: reload + enabled: url !== "" text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload - color: enabled ? hifi.colors.text : hifi.colors.disabledText + color: enabled ? (reloadMouseArea.containsMouse ? hifi.colors.blueHighlight : hifi.colors.faintGray) : hifi.colors.lightGray size: 48 - MouseArea { anchors.fill: parent; onClicked: webview.goForward() } + MouseArea { + id: reloadMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: webview.loading ? webview.stop() : webview.reload(); + } } } @@ -94,52 +115,82 @@ ScrollingWindow { id: border height: 48 anchors.top: parent.top - anchors.topMargin: 8 + anchors.topMargin: 4 anchors.right: parent.right anchors.rightMargin: 8 anchors.left: buttons.right anchors.leftMargin: 8 + HiFiGlyphs { + id: externalBrowser + anchors.right: parent.right + anchors.top: parent.top + anchors.topMargin: 4 + enabled: !HMD.active && url !== "" + font.family: "FontAwesome" + text: "\uf24d" + rotation: -90 + color: enabled ? (externalBrowserMouseArea.containsMouse ? hifi.colors.blueHighlight : hifi.colors.faintGray) : hifi.colors.lightGray + size: 32 + MouseArea { + id: externalBrowserMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: openExternalBrowser(); + } + } + Item { id: barIcon width: parent.height height: parent.height Image { - source: webview.icon; + source: webview.loading ? "" : webview.icon x: (parent.height - height) / 2 y: (parent.width - width) / 2 - sourceSize: Qt.size(width, height); - verticalAlignment: Image.AlignVCenter; + width: 28 + height: 28 + verticalAlignment: Image.AlignVCenter horizontalAlignment: Image.AlignHCenter } } TextField { id: addressBar - anchors.right: parent.right - anchors.rightMargin: 8 + anchors.right: externalBrowser.left + anchors.rightMargin: 32 anchors.left: barIcon.right anchors.leftMargin: 0 anchors.verticalCenter: parent.verticalCenter focus: true colorScheme: hifi.colorSchemes.dark placeholderText: "Enter URL" + inputMethodHints: Qt.ImhUrlCharactersOnly Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i") Keys.onPressed: { switch(event.key) { case Qt.Key_Enter: case Qt.Key_Return: event.accepted = true - if (text.indexOf("http") != 0) { + if (text.indexOf("http") !== 0) { text = "http://" + text; } + + // Setting webiew.url directly doesn't add the URL to the navigation history. + //webview.url = text; + // The following works around this bug. + text = encodeURI(text); + text = text.replace(/;/g, "%3b"); // Prevent script injection. + text = text.replace(/'/g, "%25"); // "" + webview.runJavaScript("window.location='" + text + "'"); + root.hidePermissionsBar(); root.keyboardRaised = false; - webview.url = text; break; } } } + } Rectangle { @@ -204,7 +255,7 @@ ScrollingWindow { parentRoot: root anchors.top: buttons.bottom - anchors.topMargin: 8 + anchors.topMargin: 4 anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right @@ -216,7 +267,7 @@ ScrollingWindow { Keys.onPressed: { switch(event.key) { case Qt.Key_L: - if (event.modifiers == Qt.ControlModifier) { + if (event.modifiers === Qt.ControlModifier) { event.accepted = true addressBar.selectAll() addressBar.forceActiveFocus() @@ -224,4 +275,5 @@ ScrollingWindow { break; } } + } // dialog diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fe2077f752..c02ac4ceb3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3976,6 +3976,11 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { // If this is a first run we short-circuit the address passed in if (_firstRun.get()) { + if (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) { + DependencyManager::get()->setHomeLocationToAddress(NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS); + Menu::getInstance()->triggerOption(MenuOption::HomeLocation); + } + if (!_overrideEntry) { DependencyManager::get()->goToEntry(); sentTo = SENT_TO_ENTRY; @@ -7105,9 +7110,9 @@ void Application::updateWindowTitle() const { QString currentPlaceName; if (isServerlessMode()) { if (isInErrorState) { - currentPlaceName = "serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString(); + currentPlaceName = "Serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString(); } else { - currentPlaceName = "serverless: " + DependencyManager::get()->getDomainURL().toString(); + currentPlaceName = "Serverless: " + DependencyManager::get()->getDomainURL().toString(); } } else { currentPlaceName = DependencyManager::get()->getDomainURL().host(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e460b4b56b..e175309f06 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -238,11 +238,11 @@ Menu::Menu() { // Navigate > Start-up Location MenuWrapper* startupLocationMenu = navigateMenu->addMenu(MenuOption::StartUpLocation); - QActionGroup* startupLocatiopnGroup = new QActionGroup(startupLocationMenu); - startupLocatiopnGroup->setExclusive(true); - startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::HomeLocation, 0, + QActionGroup* startupLocationGroup = new QActionGroup(startupLocationMenu); + startupLocationGroup->setExclusive(true); + startupLocationGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::HomeLocation, 0, false)); - startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::LastLocation, 0, + startupLocationGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::LastLocation, 0, true)); // Settings menu ---------------------------------- diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 4e8c88560b..8d96a2e6b5 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -2072,7 +2072,6 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi // NOTE: device start() uses the Qt internal device list Lock lock(_deviceMutex); - Lock localAudioLock(_localAudioMutex); _localSamplesAvailable.exchange(0, std::memory_order_release); //wait on local injectors prep to finish running @@ -2080,6 +2079,8 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi _localPrepInjectorFuture.waitForFinished(); } + Lock localAudioLock(_localAudioMutex); + // cleanup any previously initialized device if (_audioOutput) { _audioOutputIODevice.close(); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index eb7856b7ed..8c766d0ab8 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1460,6 +1460,31 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce emit requestRenderUpdate(); } + if (!_allProceduralMaterialsLoaded) { + std::lock_guard lock(_materialsLock); + bool allProceduralMaterialsLoaded = true; + for (auto& shapeMaterialPair : _materials) { + auto material = shapeMaterialPair.second; + while (!material.empty()) { + auto mat = material.top(); + if (mat.material && mat.material->isProcedural() && !mat.material->isReady()) { + allProceduralMaterialsLoaded = false; + break; + } + material.pop(); + } + if (!allProceduralMaterialsLoaded) { + break; + } + } + if (!allProceduralMaterialsLoaded) { + emit requestRenderUpdate(); + } else { + _allProceduralMaterialsLoaded = true; + model->setRenderItemsNeedUpdate(); + } + } + // When the individual mesh parts of a model finish fading, they will mark their Model as needing updating // we will watch for that and ask the model to update it's render items if (model->getRenderItemsNeedUpdate()) { @@ -1584,6 +1609,10 @@ void ModelEntityRenderer::addMaterial(graphics::MaterialLayer material, const st if (_model && _model->fetchRenderItemIDs().size() > 0) { _model->addMaterial(material, parentMaterialName); } + if (material.material && material.material->isProcedural()) { + _allProceduralMaterialsLoaded = false; + emit requestRenderUpdate(); + } } void ModelEntityRenderer::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index f42f0aec94..850617d1af 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -202,10 +202,10 @@ private: bool _prevModelLoaded { false }; void processMaterials(); + bool _allProceduralMaterialsLoaded { false }; static void metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector& blendshapeOffsets, const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs); - }; } } // namespace diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 683cc1359a..4c6f5adc13 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -32,7 +32,7 @@ bool ShapeEntityRenderer::needsRenderUpdate() const { if (resultWithReadLock([&] { auto mat = _materials.find("0"); if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() && - mat->second.top().material->isEnabled()) { + mat->second.top().material->isReady()) { auto procedural = std::static_pointer_cast(mat->second.top().material); if (procedural->isFading()) { return true; @@ -88,7 +88,7 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { withReadLock([&] { auto mat = _materials.find("0"); - if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() && mat->second.top().material->isEnabled()) { + if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() && mat->second.top().material->isReady()) { auto procedural = std::static_pointer_cast(mat->second.top().material); if (procedural->isFading()) { procedural->setIsFading(Interpolate::calculateFadeRatio(procedural->getFadeStartTime()) < 1.0f); @@ -140,7 +140,7 @@ bool ShapeEntityRenderer::isTransparent() const { auto mat = _materials.find("0"); if (mat != _materials.end() && mat->second.top().material) { - if (mat->second.top().material->isProcedural() && mat->second.top().material->isEnabled()) { + if (mat->second.top().material->isProcedural() && mat->second.top().material->isReady()) { auto procedural = std::static_pointer_cast(mat->second.top().material); if (procedural->isFading()) { return true; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 38e7ad9c3a..cd27c1cf36 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -746,6 +746,10 @@ QString ModelEntityItem::getBlendshapeCoefficients() const { } void ModelEntityItem::setBlendshapeCoefficients(const QString& blendshapeCoefficients) { + if (blendshapeCoefficients.isEmpty()) { + return; + } + QJsonParseError error; QJsonDocument newCoefficientsJSON = QJsonDocument::fromJson(blendshapeCoefficients.toUtf8(), &error); if (error.error != QJsonParseError::NoError) { diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 3d2a7edca8..3a0271945b 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -1019,7 +1019,9 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& hfmModel.meshes.append(HFMMesh()); HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; - if (!hfmModel.hasSkeletonJoints) { + mesh.modelTransform = globalTransforms[nodeIndex]; + + if (!hfmModel.hasSkeletonJoints) { HFMCluster cluster; cluster.jointIndex = nodecount; cluster.inverseBindMatrix = glm::mat4(); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 5f08f19d2b..94bcb718d3 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -27,16 +27,22 @@ #include "NodeList.h" #include "NetworkLogging.h" +#include "NetworkingConstants.h" #include "UserActivityLogger.h" #include "udt/PacketHeaders.h" -const QString DEFAULT_HIFI_ADDRESS = "file:///~/serverless/tutorial.json"; -const QString DEFAULT_HOME_ADDRESS = "file:///~/serverless/tutorial.json"; -const QString REDIRECT_HIFI_ADDRESS = "file:///~/serverless/redirect.json"; +const QString REDIRECT_HIFI_ADDRESS = NetworkingConstants::REDIRECT_HIFI_ADDRESS; const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; -Setting::Handle currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_HIFI_ADDRESS); +const QString DEFAULT_VIRCADIA_ADDRESS = (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) + ? BuildInfo::INITIAL_STARTUP_LOCATION + : NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS; +const QString DEFAULT_HOME_ADDRESS = (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) + ? BuildInfo::INITIAL_STARTUP_LOCATION + : NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS; + +Setting::Handle currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_VIRCADIA_ADDRESS); bool AddressManager::isConnected() { return DependencyManager::get()->getDomainHandler().isConnected(); diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 148c4f8580..daca6d3392 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -22,8 +22,8 @@ #include "AccountManager.h" -extern const QString DEFAULT_HIFI_ADDRESS; extern const QString REDIRECT_HIFI_ADDRESS; +extern const QString DEFAULT_VIRCADIA_ADDRESS; extern const QString DEFAULT_HOME_ADDRESS; const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost"; @@ -292,7 +292,8 @@ public slots: * location history is correctly maintained. */ void goToLocalSandbox(QString path = "", LookupTrigger trigger = LookupTrigger::StartupFromSettings) { - handleUrl(SANDBOX_HIFI_ADDRESS + path, trigger); } + handleUrl(SANDBOX_HIFI_ADDRESS + path, trigger); + } /**jsdoc * Takes you to the default "welcome" metaverse address. @@ -300,7 +301,9 @@ public slots: * @param {location.LookupTrigger} trigger=StartupFromSettings - The reason for the function call. Helps ensure that user's * location history is correctly maintained. */ - void goToEntry(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(DEFAULT_HIFI_ADDRESS, trigger); } + void goToEntry(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { + handleUrl(DEFAULT_VIRCADIA_ADDRESS, trigger); + } /**jsdoc * Takes you to the specified user's location. diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index edc1c1a1ef..c20753f8b7 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -56,7 +56,10 @@ namespace NetworkingConstants { const QUrl HELP_SCRIPTING_REFERENCE_URL{ "https://apidocs.vircadia.dev/" }; const QUrl HELP_RELEASE_NOTES_URL{ "https://docs.vircadia.dev/release-notes.html" }; const QUrl HELP_BUG_REPORT_URL{ "https://github.com/kasenvr/project-athena/issues" }; - + + const QString DEFAULT_VIRCADIA_ADDRESS = "file:///~/serverless/tutorial.json"; + const QString DEFAULT_HOME_ADDRESS = "file:///~/serverless/tutorial.json"; + const QString REDIRECT_HIFI_ADDRESS = "file:///~/serverless/redirect.json"; } const QString HIFI_URL_SCHEME_ABOUT = "about"; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index d150e18406..84aed55d72 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -520,8 +520,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { batch.setDrawcallUniform(drawcallInfo); } - if (!_drawMaterials.empty() && _drawMaterials.top().material && _drawMaterials.top().material->isProcedural() && - _drawMaterials.top().material->isReady()) { + if (_shapeKey.hasOwnPipeline()) { if (!(enableMaterialProceduralShaders)) { return; } diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.cpp b/libraries/ui/src/ui/types/ContextAwareProfile.cpp index f74e8754c9..210e1f36b1 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.cpp +++ b/libraries/ui/src/ui/types/ContextAwareProfile.cpp @@ -12,7 +12,9 @@ #include "ContextAwareProfile.h" #include +#include #include +#include #include #include @@ -20,20 +22,63 @@ static const QString RESTRICTED_FLAG_PROPERTY = "RestrictFileAccess"; -ContextAwareProfile::ContextAwareProfile(QQmlContext* context) : - ContextAwareProfileParent(context), _context(context) { +QReadWriteLock ContextAwareProfile::_contextMapProtect; +ContextAwareProfile::ContextMap ContextAwareProfile::_contextMap; + +ContextAwareProfile::ContextAwareProfile(QQmlContext* context) : ContextAwareProfileParent(context), _context(context) { assert(context); + + { // register our object for future updates + QWriteLocker guard(&_contextMapProtect); + ContextMap::iterator setLookup = _contextMap.find(_context); + if (setLookup == _contextMap.end()) { + setLookup = _contextMap.insert(_context, ContextAwareProfileSet()); + } + assert(setLookup != _contextMap.end()); + ContextAwareProfileSet& profileSet = setLookup.value(); + assert(profileSet.find(this) == profileSet.end()); + profileSet.insert(this); + } + + _isRestricted.store(isRestrictedGetProperty()); } +ContextAwareProfile::~ContextAwareProfile() { + // deregister our object + QWriteLocker guard(&_contextMapProtect); + ContextMap::iterator setLookup = _contextMap.find(_context); + assert(setLookup != _contextMap.end()); + if (setLookup != _contextMap.end()) { + ContextAwareProfileSet& profileSet = setLookup.value(); + assert(profileSet.find(this) != profileSet.end()); + profileSet.remove(this); + if (profileSet.isEmpty()) { + _contextMap.erase(setLookup); + } + } +} void ContextAwareProfile::restrictContext(QQmlContext* context, bool restrict) { + + // set the QML property context->setContextProperty(RESTRICTED_FLAG_PROPERTY, restrict); + + // broadcast the new value to any registered ContextAwareProfile objects + QReadLocker guard(&_contextMapProtect); + ContextMap::const_iterator setLookup = _contextMap.find(context); + if (setLookup != _contextMap.end()) { + const ContextAwareProfileSet& profileSet = setLookup.value(); + for (ContextAwareProfileSet::const_iterator profileIterator = profileSet.begin(); + profileIterator != profileSet.end(); profileIterator++) { + (*profileIterator)->_isRestricted.store(restrict); + } + } } -bool ContextAwareProfile::isRestrictedInternal() { +bool ContextAwareProfile::isRestrictedGetProperty() { if (QThread::currentThread() != thread()) { bool restrictedResult = false; - BLOCKING_INVOKE_METHOD(this, "isRestrictedInternal", Q_RETURN_ARG(bool, restrictedResult)); + BLOCKING_INVOKE_METHOD(this, "isRestrictedGetProperty", Q_RETURN_ARG(bool, restrictedResult)); return restrictedResult; } @@ -48,10 +93,5 @@ bool ContextAwareProfile::isRestrictedInternal() { } bool ContextAwareProfile::isRestricted() { - auto now = usecTimestampNow(); - if (now > _cacheExpiry) { - _cachedValue = isRestrictedInternal(); - _cacheExpiry = now + MAX_CACHE_AGE; - } - return _cachedValue; + return _isRestricted.load(); } diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.h b/libraries/ui/src/ui/types/ContextAwareProfile.h index 3192d2be54..486ac5481a 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.h +++ b/libraries/ui/src/ui/types/ContextAwareProfile.h @@ -11,7 +11,11 @@ #ifndef hifi_ContextAwareProfile_h #define hifi_ContextAwareProfile_h -#include +#include +#include +#include +#include +#include #if !defined(Q_OS_ANDROID) #include @@ -26,8 +30,6 @@ using ContextAwareProfileParent = QObject; using RequestInterceptorParent = QObject; #endif -#include - class QQmlContext; class ContextAwareProfile : public ContextAwareProfileParent { @@ -35,7 +37,7 @@ class ContextAwareProfile : public ContextAwareProfileParent { public: static void restrictContext(QQmlContext* context, bool restrict = true); bool isRestricted(); - Q_INVOKABLE bool isRestrictedInternal(); + Q_INVOKABLE bool isRestrictedGetProperty(); protected: class RequestInterceptor : public RequestInterceptorParent { @@ -47,10 +49,17 @@ protected: }; ContextAwareProfile(QQmlContext* parent); + ~ContextAwareProfile(); + +private: + typedef QSet ContextAwareProfileSet; + typedef QHash ContextMap; + QQmlContext* _context{ nullptr }; - bool _cachedValue{ false }; - quint64 _cacheExpiry{ 0 }; - constexpr static quint64 MAX_CACHE_AGE = MSECS_PER_SECOND; + std::atomic _isRestricted{ false }; + + static QReadWriteLock _contextMapProtect; + static ContextMap _contextMap; }; #endif // hifi_FileTypeProfile_h diff --git a/plugins/hifiSdl2/CMakeLists.txt b/plugins/hifiSdl2/CMakeLists.txt index c68723a10a..e1f0ee28d8 100644 --- a/plugins/hifiSdl2/CMakeLists.txt +++ b/plugins/hifiSdl2/CMakeLists.txt @@ -8,7 +8,14 @@ if (NOT APPLE) set(TARGET_NAME hifiSdl2) + if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 10.0) + # GCC 10 and above sets -fno-common by default, and causes a linking problem here: + # multiple definition of `WAYLAND_wl_proxy_marshal' + # + # Work around it per https://medium.com/@clentfort/using-esy-sdl2-with-gcc-10-91b4fa0c5aa9 + link_libraries("-Wl,--allow-multiple-definition") + endif() setup_hifi_plugin(Qml) link_hifi_libraries(shared controllers ui plugins input-plugins script-engine) target_sdl2() -endif() \ No newline at end of file +endif() diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index c247bc7cae..8e53823cd8 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -402,7 +402,7 @@ "tooltip": "Affects the size of the spotlight beam; the higher the value, the larger the beam." }, "materialURL": { - "tooltip": "The URL to an external JSON file or \"materialData\", \"materialData? to use Material Data." + "tooltip": "The URL to an external JSON file or \"materialData\". Append \"?\" to select a single material if multiple are defined." }, "materialData": { "tooltip": "Can be used instead of a JSON file when material set to materialData." diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index 0de87e563c..e01e761cb4 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -736,6 +736,9 @@ var toolBar = (function () { function handleNewMaterialDialogResult(result) { if (result) { var materialURL = result.textInput; + if (materialURL === "") { + materialURL = "materialData"; + } //var materialMappingMode; //switch (result.comboBox) { // case MATERIAL_MODE_PROJECTED: @@ -2443,8 +2446,8 @@ var PropertiesTool = function (opts) { Entities.editEntity(entityID, properties); }); - if (properties.name !== undefined || properties.modelURL !== undefined || properties.materialURL !== undefined || - properties.visible !== undefined || properties.locked !== undefined) { + if (properties.name !== undefined || properties.modelURL !== undefined || properties.imageURL !== undefined || + properties.materialURL !== undefined || properties.visible !== undefined || properties.locked !== undefined) { sendListUpdate = true; } diff --git a/scripts/system/create/qml/NewMaterialDialog.qml b/scripts/system/create/qml/NewMaterialDialog.qml index 1631632fb4..3cc619e176 100644 --- a/scripts/system/create/qml/NewMaterialDialog.qml +++ b/scripts/system/create/qml/NewMaterialDialog.qml @@ -4,6 +4,7 @@ // // Created by Sam Gondelman on 1/17/18 // Copyright 2018 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -54,7 +55,7 @@ Rectangle { Text { id: text1 - text: qsTr("Material URL") + text: qsTr("Material URL (Optional)") color: "#ffffff" font.pixelSize: 12 } @@ -139,7 +140,7 @@ Rectangle { Button { id: button1 - text: qsTr("Add") + text: qsTr("Create") z: -1 onClicked: { newMaterialDialog.sendToScript({ diff --git a/scripts/system/create/qml/NewModelDialog.qml b/scripts/system/create/qml/NewModelDialog.qml index 741902fa7f..dd4ef3c8ad 100644 --- a/scripts/system/create/qml/NewModelDialog.qml +++ b/scripts/system/create/qml/NewModelDialog.qml @@ -4,6 +4,7 @@ // // Created by Seth Alves on 2017-2-10 // Copyright 2017 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -208,7 +209,7 @@ Rectangle { Button { id: button1 - text: qsTr("Add") + text: qsTr("Create") z: -1 enabled: false onClicked: {