+ Subject to the terms and conditions of this license, the Copyright Holder grants a worldwide, non-exclusive, non-sublicensable, non-transferable (except by transfer of the Certificate or beneficial ownership thereof) license (i) to the Certificate Holder to display ONE COPY of the Item at a time across any and all virtual worlds WITHOUT MODIFICATION; (ii) to any party to view and interact with the Item as displayed by the Certificate Holder. Redistributions of source code must retain the all copyright notices. Notwithstanding the foregoing, modification of the Item may be permitted pursuant to terms provided in the Certificate.
+
+
+ THE ITEM IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR A CONTRIBUTOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ITEM, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Reference to the “Certificate” means the Proof of Provenance Certificate containing a hash of the code used to generate the Item; ‘Item’ means the visual representation produced by the execution of the code hashed in the Certificate (which term includes the code itself); and “Certificate Holder” means a single holder of the private key for the Certificate.
+
diff --git a/interface/src/AboutUtil.h b/interface/src/AboutUtil.h
index 1495045b85..c06255aaa5 100644
--- a/interface/src/AboutUtil.h
+++ b/interface/src/AboutUtil.h
@@ -16,15 +16,23 @@
#include
/**jsdoc
+ * The HifiAbout API provides information about the version of Interface that is currently running. It also
+ * provides the ability to open a Web page in an Interface browser window.
+ *
* @namespace HifiAbout
*
* @hifi-interface
* @hifi-client-entity
* @hifi-avatar
*
- * @property {string} buildDate
- * @property {string} buildVersion
- * @property {string} qtVersion
+ * @property {string} buildDate - The build date of Interface that is currently running. Read-only.
+ * @property {string} buildVersion - The build version of Interface that is currently running. Read-only.
+ * @property {string} qtVersion - The Qt version used in Interface that is currently running. Read-only.
+ *
+ * @example
Report build information for the version of Interface currently running.
+ * print("HiFi build date: " + HifiAbout.buildDate); // 11 Feb 2019
+ * print("HiFi version: " + HifiAbout.buildVersion); // 0.78.0
+ * print("Qt version: " + HifiAbout.qtVersion); // 5.10.1
*/
class AboutUtil : public QObject {
@@ -44,8 +52,9 @@ public:
public slots:
/**jsdoc
+ * Display a Web page in an Interface browser window.
* @function HifiAbout.openUrl
- * @param {string} url
+ * @param {string} url - The URL of the Web page to display.
*/
void openUrl(const QString &url) const;
private:
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index bbe4d70ab6..b65f39ffd7 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -102,7 +102,7 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -154,7 +154,7 @@
#include
#include
#include
-#include
+#include
#include "recording/ClipCache.h"
#include "AudioClient.h"
@@ -6988,17 +6988,19 @@ void Application::nodeActivated(SharedNodePointer node) {
#if !defined(DISABLE_QML)
auto offscreenUi = getOffscreenUI();
- auto assetDialog = offscreenUi ? offscreenUi->getRootItem()->findChild("AssetServer") : nullptr;
- if (assetDialog) {
+ if (offscreenUi) {
auto nodeList = DependencyManager::get();
if (nodeList->getThisNodeCanWriteAssets()) {
// call reload on the shown asset browser dialog to get the mappings (if permissions allow)
- QMetaObject::invokeMethod(assetDialog, "reload");
+ auto assetDialog = offscreenUi ? offscreenUi->getRootItem()->findChild("AssetServer") : nullptr;
+ if (assetDialog) {
+ QMetaObject::invokeMethod(assetDialog, "reload");
+ }
} else {
// we switched to an Asset Server that we can't modify, hide the Asset Browser
- assetDialog->setVisible(false);
+ offscreenUi->hide("AssetServer");
}
}
#endif
diff --git a/interface/src/commerce/QmlMarketplace.cpp b/interface/src/commerce/QmlMarketplace.cpp
index 23ba418a2d..8197b20275 100644
--- a/interface/src/commerce/QmlMarketplace.cpp
+++ b/interface/src/commerce/QmlMarketplace.cpp
@@ -50,9 +50,10 @@ void QmlMarketplace::getMarketplaceItems(
const QString& adminFilter,
const QString& adminFilterCost,
const QString& sort,
- const bool isFree,
- const int& page,
- const int& perPage) {
+ bool isAscending,
+ bool isFree,
+ int page,
+ int perPage) {
QString endpoint = "items";
QUrlQuery request;
@@ -62,6 +63,7 @@ void QmlMarketplace::getMarketplaceItems(
request.addQueryItem("adminFilter", adminFilter);
request.addQueryItem("adminFilterCost", adminFilterCost);
request.addQueryItem("sort", sort);
+ request.addQueryItem("sort_dir", isAscending ? "asc" : "desc");
if (isFree) {
request.addQueryItem("isFree", "true");
}
diff --git a/interface/src/commerce/QmlMarketplace.h b/interface/src/commerce/QmlMarketplace.h
index 5794d4f53c..76b3d41449 100644
--- a/interface/src/commerce/QmlMarketplace.h
+++ b/interface/src/commerce/QmlMarketplace.h
@@ -46,9 +46,10 @@ protected:
const QString& adminFilter = QString("published"),
const QString& adminFilterCost = QString(),
const QString& sort = QString(),
- const bool isFree = false,
- const int& page = 1,
- const int& perPage = 20);
+ bool isAscending = false,
+ bool isFree = false,
+ int page = 1,
+ int perPage = 20);
Q_INVOKABLE void getMarketplaceItem(const QString& marketplaceItemId);
Q_INVOKABLE void marketplaceItemLike(const QString& marketplaceItemId, const bool like = true);
Q_INVOKABLE void getMarketplaceCategories();
diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h
index c096713058..5e5e6c9b4f 100644
--- a/interface/src/commerce/Wallet.h
+++ b/interface/src/commerce/Wallet.h
@@ -54,6 +54,29 @@ public:
void clear();
void getWalletStatus();
+
+ /**jsdoc
+ *
A WalletStatus may have one of the following values:
+ *
+ *
+ *
Value
Meaning
Description
+ *
+ *
+ *
0
Not logged in
The user isn't logged in.
+ *
1
Not set up
The user's wallet isn't set up.
+ *
2
Pre-existing
There is a wallet present on the server but not one
+ * locally.
+ *
3
Conflicting
There is a wallet present on the server plus one present locally,
+ * and they don't match.
+ *
4
Not authenticated
There is a wallet present locally but the user hasn't
+ * logged into it.
+ *
5
Ready
The wallet is ready for use.
+ *
+ *
+ *
Wallets used to be stored locally but now they're stored on the server, unless the computer once had a wallet stored
+ * locally in which case the wallet may be present in both places.
+ * @typedef {number} WalletScriptingInterface.WalletStatus
+ */
enum WalletStatus {
WALLET_STATUS_NOT_LOGGED_IN = 0,
WALLET_STATUS_NOT_SET_UP,
diff --git a/interface/src/scripting/AccountServicesScriptingInterface.cpp b/interface/src/scripting/AccountServicesScriptingInterface.cpp
index 3939fce91d..a3597886e9 100644
--- a/interface/src/scripting/AccountServicesScriptingInterface.cpp
+++ b/interface/src/scripting/AccountServicesScriptingInterface.cpp
@@ -112,6 +112,12 @@ DownloadInfoResult::DownloadInfoResult() :
{
}
+/**jsdoc
+ * Information on the assets currently being downloaded and pending download.
+ * @typedef {object} AccountServices.DownloadInfoResult
+ * @property {number[]} downloading - The percentage complete for each asset currently being downloaded.
+ * @property {number} pending - The number of assets waiting to be download.
+ */
QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result) {
QScriptValue object = engine->newObject();
diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h
index 55445a864d..c08181d7c9 100644
--- a/interface/src/scripting/AccountServicesScriptingInterface.h
+++ b/interface/src/scripting/AccountServicesScriptingInterface.h
@@ -38,17 +38,25 @@ class AccountServicesScriptingInterface : public QObject {
Q_OBJECT
/**jsdoc
- * The AccountServices API contains helper functions related to user connectivity
+ * The AccountServices API provides functions related to user connectivity, visibility, and asset download
+ * progress.
*
* @hifi-interface
* @hifi-client-entity
* @hifi-avatar
*
* @namespace AccountServices
- * @property {string} username Read-only.
- * @property {boolean} loggedIn Read-only.
- * @property {string} findableBy
- * @property {string} metaverseServerURL Read-only.
+ * @property {string} username - The user name if the user is logged in, otherwise "Unknown user".
+ * Read-only.
+ * @property {boolean} loggedIn - true if the user is logged in, otherwise false.
+ * Read-only.
+ * @property {string} findableBy - The user's visibility to other people:
+ * "none" - user appears offline.
+ * "friends" - user is visible only to friends.
+ * "connections" - user is visible to friends and connections.
+ * "all" - user is visible to everyone.
+ * @property {string} metaverseServerURL - The metaverse server that the user is authenticated against when logged in
+ * — typically "https://metaverse.highfidelity.com". Read-only.
*/
Q_PROPERTY(QString username READ getUsername NOTIFY myUsernameChanged)
@@ -66,29 +74,38 @@ public:
public slots:
/**jsdoc
+ * Get information on the progress of downloading assets in the domain.
* @function AccountServices.getDownloadInfo
- * @returns {DownloadInfoResult}
+ * @returns {AccountServices.DownloadInfoResult} Information on the progress of assets download.
*/
DownloadInfoResult getDownloadInfo();
/**jsdoc
+ * Cause a {@link AccountServices.downloadInfoChanged|downloadInfoChanged} signal to be triggered with information on the
+ * current progress of the download of assets in the domain.
* @function AccountServices.updateDownloadInfo
*/
void updateDownloadInfo();
/**jsdoc
+ * Check whether the user is logged in.
* @function AccountServices.isLoggedIn
- * @returns {boolean}
+ * @returns {boolean} true if the user is logged in, false otherwise.
+ * @example
Report whether you are logged in.
+ * var isLoggedIn = AccountServices.isLoggedIn();
+ * print("You are logged in: " + isLoggedIn); // true or false
*/
bool isLoggedIn();
/**jsdoc
+ * Prompts the user to log in (the login dialog is displayed) if they're not already logged in.
* @function AccountServices.checkAndSignalForAccessToken
- * @returns {boolean}
+ * @returns {boolean} true if the user is already logged in, false otherwise.
*/
bool checkAndSignalForAccessToken();
/**jsdoc
+ * Logs the user out.
* @function AccountServices.logOut
*/
void logOut();
@@ -106,43 +123,75 @@ private slots:
signals:
/**jsdoc
+ * Not currently used.
* @function AccountServices.connected
* @returns {Signal}
*/
void connected();
/**jsdoc
+ * Triggered when the user logs out.
* @function AccountServices.disconnected
- * @param {string} reason
+ * @param {string} reason - Has the value, "logout".
* @returns {Signal}
*/
void disconnected(const QString& reason);
/**jsdoc
+ * Triggered when the username logged in with changes, i.e., when the user logs in or out.
* @function AccountServices.myUsernameChanged
- * @param {string} username
+ * @param {string} username - The username logged in with if the user is logged in, otherwise "".
* @returns {Signal}
+ * @example
Report when your username changes.
+ * AccountServices.myUsernameChanged.connect(function (username) {
+ * print("Username changed: " + username);
+ * });
*/
void myUsernameChanged(const QString& username);
/**jsdoc
+ * Triggered when the progress of the download of assets for the domain changes.
* @function AccountServices.downloadInfoChanged
- * @param {} info
+ * @param {AccountServices.DownloadInfoResult} downloadInfo - Information on the progress of assets download.
* @returns {Signal}
*/
void downloadInfoChanged(DownloadInfoResult info);
/**jsdoc
+ * Triggered when the user's visibility to others changes.
* @function AccountServices.findableByChanged
- * @param {string} discoverabilityMode
+ * @param {string} findableBy - The user's visibility to other people:
+ * "none" - user appears offline.
+ * "friends" - user is visible only to friends.
+ * "connections" - user is visible to friends and connections.
+ * "all" - user is visible to everyone.
* @returns {Signal}
+ * @example
Report when your visiblity changes.
+ * AccountServices.findableByChanged.connect(function (findableBy) {
+ * print("Findable by changed: " + findableBy);
+ * });
+ *
+ * var originalFindableBy = AccountServices.findableBy;
+ * Script.setTimeout(function () {
+ * // Change visiblity.
+ * AccountServices.findableBy = originalFindableBy === "none" ? "all" : "none";
+ * }, 2000);
+ * Script.setTimeout(function () {
+ * // Restore original visibility.
+ * AccountServices.findableBy = originalFindableBy;
+ * }, 4000);
*/
void findableByChanged(const QString& discoverabilityMode);
/**jsdoc
+ * Triggered when the login status of the user changes.
* @function AccountServices.loggedInChanged
- * @param {boolean} loggedIn
+ * @param {boolean} loggedIn - true if the user is logged in, otherwise false.
* @returns {Signal}
+ * @example
Report when your login status changes.
+ * AccountServices.loggedInChanged.connect(function(loggedIn) {
+ * print("Logged in: " + loggedIn);
+ * });
*/
void loggedInChanged(bool loggedIn);
diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h
index 005af7f558..7482b8be00 100644
--- a/interface/src/scripting/WalletScriptingInterface.h
+++ b/interface/src/scripting/WalletScriptingInterface.h
@@ -1,4 +1,4 @@
-
+//
// WalletScriptingInterface.h
// interface/src/scripting
//
@@ -30,14 +30,19 @@ public:
};
/**jsdoc
- * @namespace Wallet
+ * The WalletScriptingInterface API provides functions related to the user's wallet and verification of certified
+ * avatar entities.
+ *
+ * @namespace WalletScriptingInterface
*
* @hifi-interface
* @hifi-client-entity
* @hifi-avatar
*
- * @property {number} walletStatus
- * @property {bool} limitedCommerce
+ * @property {WalletScriptingInterface.WalletStatus} walletStatus - The status of the user's wallet. Read-only.
+ * @property {boolean} limitedCommerce - true if Interface is running in limited commerce mode. In limited commerce
+ * mode, certain Interface functionality is disabled, e.g., users can't buy non-free items from the Marketplace. The Oculus
+ * Store version of Interface runs in limited commerce mode. Read-only.
*/
class WalletScriptingInterface : public QObject, public Dependency {
Q_OBJECT
@@ -50,19 +55,56 @@ public:
WalletScriptingInterface();
/**jsdoc
+ * Check and update the user's wallet status.
* @function WalletScriptingInterface.refreshWalletStatus
*/
Q_INVOKABLE void refreshWalletStatus();
/**jsdoc
+ * Get the current status of the user's wallet.
* @function WalletScriptingInterface.getWalletStatus
- * @returns {number}
+ * @returns {WalletScriptingInterface.WalletStatus}
+ * @example
Two ways to report your wallet status.
+ * print("Wallet status: " + WalletScriptingInterface.walletStatus); // Same value as next line.
+ * print("Wallet status: " + WalletScriptingInterface.getWalletStatus());
*/
Q_INVOKABLE uint getWalletStatus() { return _walletStatus; }
/**jsdoc
+ * Check that a certified avatar entity is owned by the avatar whose entity it is. The result of the check is provided via
+ * the {@link WalletScriptingInterface.ownershipVerificationSuccess|ownershipVerificationSuccess} and
+ * {@link WalletScriptingInterface.ownershipVerificationFailed|ownershipVerificationFailed} signals.
+ * Warning: Neither of these signals fire if the entity is not an avatar entity or it's not a certified
+ * entity.
* @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification
- * @param {Uuid} entityID
+ * @param {Uuid} entityID - The ID of the avatar entity to check.
+ * @example
Check ownership of all nearby certified avatar entities.
+ * // Set up response handling.
+ * function ownershipSuccess(entityID) {
+ * print("Ownership test succeeded for: " + entityID);
+ * }
+ * function ownershipFailed(entityID) {
+ * print("Ownership test failed for: " + entityID);
+ * }
+ * WalletScriptingInterface.ownershipVerificationSuccess.connect(ownershipSuccess);
+ * WalletScriptingInterface.ownershipVerificationFailed.connect(ownershipFailed);
+ *
+ * // Check ownership of all nearby certified avatar entities.
+ * var entityIDs = Entities.findEntities(MyAvatar.position, 10);
+ * var i, length;
+ * for (i = 0, length = entityIDs.length; i < length; i++) {
+ * var properties = Entities.getEntityProperties(entityIDs[i], ["entityHostType", "certificateID"]);
+ * if (properties.entityHostType === "avatar" && properties.certificateID !== "") {
+ * print("Prove ownership of: " + entityIDs[i]);
+ * WalletScriptingInterface.proveAvatarEntityOwnershipVerification(entityIDs[i]);
+ * }
+ * }
+ *
+ * // Tidy up.
+ * Script.scriptEnding.connect(function () {
+ * WalletScriptingInterface.ownershipVerificationFailed.disconnect(ownershipFailed);
+ * WalletScriptingInterface.ownershipVerificationSuccess.disconnect(ownershipSuccess);
+ * });
*/
Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID);
@@ -76,33 +118,45 @@ public:
signals:
/**jsdoc
+ * Triggered when the status of the user's wallet changes.
* @function WalletScriptingInterface.walletStatusChanged
* @returns {Signal}
+ * @example
Report when your wallet status changes, e.g., when you log in and out.
+ * WalletScriptingInterface.walletStatusChanged.connect(function () {
+ * print("Wallet status changed to: " + WalletScriptingInterface.walletStatus");
+ * });
*/
void walletStatusChanged();
/**jsdoc
+ * Triggered when the user's limited commerce status changes.
* @function WalletScriptingInterface.limitedCommerceChanged
* @returns {Signal}
*/
void limitedCommerceChanged();
/**jsdoc
+ * Triggered when the user rezzes a certified entity but the user's wallet is not ready and so the certified location of the
+ * entity cannot be updated in the metaverse.
* @function WalletScriptingInterface.walletNotSetup
* @returns {Signal}
*/
void walletNotSetup();
/**jsdoc
+ * Triggered when a certified avatar entity's ownership check requested via
+ * {@link WalletScriptingInterface.proveAvatarEntityOwnershipVerification|proveAvatarEntityOwnershipVerification} succeeds.
* @function WalletScriptingInterface.ownershipVerificationSuccess
- * @param {Uuid} entityID
+ * @param {Uuid} entityID - The ID of the avatar entity checked.
* @returns {Signal}
*/
void ownershipVerificationSuccess(const QUuid& entityID);
/**jsdoc
+ * Triggered when a certified avatar entity's ownership check requested via
+ * {@link WalletScriptingInterface.proveAvatarEntityOwnershipVerification|proveAvatarEntityOwnershipVerification} fails.
* @function WalletScriptingInterface.ownershipVerificationFailed
- * @param {Uuid} entityID
+ * @param {Uuid} entityID - The ID of the avatar entity checked.
* @returns {Signal}
*/
void ownershipVerificationFailed(const QUuid& entityID);
diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp
index 4e988334f9..237fd3da02 100644
--- a/libraries/animation/src/AnimationCache.cpp
+++ b/libraries/animation/src/AnimationCache.cpp
@@ -41,7 +41,7 @@ QSharedPointer AnimationCache::createResource(const QUrl& url) {
}
QSharedPointer AnimationCache::createResourceCopy(const QSharedPointer& resource) {
- return QSharedPointer(new Animation(*resource.staticCast().data()), &Resource::deleter);
+ return QSharedPointer(new Animation(*resource.staticCast()), &Resource::deleter);
}
AnimationReader::AnimationReader(const QUrl& url, const QByteArray& data) :
diff --git a/libraries/audio/src/SoundCache.cpp b/libraries/audio/src/SoundCache.cpp
index 343de46e9a..c36897c766 100644
--- a/libraries/audio/src/SoundCache.cpp
+++ b/libraries/audio/src/SoundCache.cpp
@@ -40,5 +40,5 @@ QSharedPointer SoundCache::createResource(const QUrl& url) {
}
QSharedPointer SoundCache::createResourceCopy(const QSharedPointer& resource) {
- return QSharedPointer(new Sound(*resource.staticCast().data()), &Resource::deleter);
+ return QSharedPointer(new Sound(*resource.staticCast()), &Resource::deleter);
}
\ No newline at end of file
diff --git a/libraries/avatars-renderer/CMakeLists.txt b/libraries/avatars-renderer/CMakeLists.txt
index 06a3804ece..de1ac1a7c2 100644
--- a/libraries/avatars-renderer/CMakeLists.txt
+++ b/libraries/avatars-renderer/CMakeLists.txt
@@ -1,6 +1,6 @@
set(TARGET_NAME avatars-renderer)
setup_hifi_library(Network Script)
-link_hifi_libraries(shared shaders gpu graphics animation model-networking script-engine render render-utils image trackers entities-renderer)
+link_hifi_libraries(shared shaders gpu graphics animation material-networking model-networking script-engine render render-utils image trackers entities-renderer)
include_hifi_library_headers(avatars)
include_hifi_library_headers(networking)
include_hifi_library_headers(hfm)
diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt
index 8e966ed9ea..ad6503b22d 100644
--- a/libraries/display-plugins/CMakeLists.txt
+++ b/libraries/display-plugins/CMakeLists.txt
@@ -2,6 +2,7 @@ set(TARGET_NAME display-plugins)
setup_hifi_library(Gui)
link_hifi_libraries(shared shaders plugins ui-plugins gl ui render-utils ${PLATFORM_GL_BACKEND})
include_hifi_library_headers(gpu)
+include_hifi_library_headers(material-networking)
include_hifi_library_headers(model-networking)
include_hifi_library_headers(networking)
include_hifi_library_headers(graphics)
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
index e4deaf8f4b..20fc9a2290 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
@@ -720,7 +720,7 @@ void OpenGLDisplayPlugin::present() {
}
gpu::Backend::freeGPUMemSize.set(gpu::gl::getFreeDedicatedMemory());
- } else {
+ } else if (alwaysPresent()) {
internalPresent();
}
_movingAveragePresent.addSample((float)(usecTimestampNow() - startPresent));
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
index 5c653f8a0a..49a38ecb4c 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
@@ -88,6 +88,11 @@ protected:
glm::uvec2 getSurfaceSize() const;
glm::uvec2 getSurfacePixels() const;
+ // Some display plugins require us to always execute some present logic,
+ // whether we have a frame or not (Oculus Mobile plugin)
+ // Such plugins must be prepared to do the right thing if the `_currentFrame`
+ // is not populated
+ virtual bool alwaysPresent() const { return false; }
void updateCompositeFramebuffer();
diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt
index b08856e8a8..e1896cf674 100644
--- a/libraries/entities-renderer/CMakeLists.txt
+++ b/libraries/entities-renderer/CMakeLists.txt
@@ -1,6 +1,6 @@
set(TARGET_NAME entities-renderer)
setup_hifi_library(Network Script)
-link_hifi_libraries(shared workload gpu shaders procedural graphics model-networking script-engine render render-utils image qml ui pointers)
+link_hifi_libraries(shared workload gpu shaders procedural graphics material-networking model-networking script-engine render render-utils image qml ui pointers)
include_hifi_library_headers(networking)
include_hifi_library_headers(gl)
include_hifi_library_headers(ktx)
diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt
index e359c7132f..56adb59914 100644
--- a/libraries/entities/CMakeLists.txt
+++ b/libraries/entities/CMakeLists.txt
@@ -6,4 +6,4 @@ include_hifi_library_headers(fbx)
include_hifi_library_headers(gpu)
include_hifi_library_headers(image)
include_hifi_library_headers(ktx)
-link_hifi_libraries(shared shaders networking octree avatars graphics model-networking)
+link_hifi_libraries(shared shaders networking octree avatars graphics material-networking model-networking)
diff --git a/libraries/entities/src/MaterialEntityItem.h b/libraries/entities/src/MaterialEntityItem.h
index ba142d7719..069c71c1d6 100644
--- a/libraries/entities/src/MaterialEntityItem.h
+++ b/libraries/entities/src/MaterialEntityItem.h
@@ -13,7 +13,7 @@
#include "MaterialMappingMode.h"
#include
-#include
+#include
class MaterialEntityItem : public EntityItem {
using Pointer = std::shared_ptr;
diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp
index 207ee2982d..9e7f422b40 100644
--- a/libraries/fbx/src/FBXSerializer.cpp
+++ b/libraries/fbx/src/FBXSerializer.cpp
@@ -1326,7 +1326,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
hfmModel.meshExtents.reset();
// Create the Material Library
- consolidateHFMMaterials(mapping);
+ consolidateHFMMaterials();
// We can't allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image
// Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key
diff --git a/libraries/fbx/src/FBXSerializer.h b/libraries/fbx/src/FBXSerializer.h
index b95bb729e7..379b1ac743 100644
--- a/libraries/fbx/src/FBXSerializer.h
+++ b/libraries/fbx/src/FBXSerializer.h
@@ -153,7 +153,7 @@ public:
QHash _hfmMaterials;
QHash _materialParams;
- void consolidateHFMMaterials(const QVariantHash& mapping);
+ void consolidateHFMMaterials();
bool _loadLightmaps { true };
float _lightmapOffset { 0.0f };
diff --git a/libraries/fbx/src/FBXSerializer_Material.cpp b/libraries/fbx/src/FBXSerializer_Material.cpp
index 9caf713e75..b47329e483 100644
--- a/libraries/fbx/src/FBXSerializer_Material.cpp
+++ b/libraries/fbx/src/FBXSerializer_Material.cpp
@@ -75,15 +75,7 @@ HFMTexture FBXSerializer::getTexture(const QString& textureID, const QString& ma
return texture;
}
-void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
- QJsonObject materialMap;
- if (mapping.contains("materialMap")) {
- QByteArray materialMapValue = mapping.value("materialMap").toByteArray();
- materialMap = QJsonDocument::fromJson(materialMapValue).object();
- if (materialMap.isEmpty()) {
- qCDebug(modelformat) << "fbx Material Map found but did not produce valid JSON:" << materialMapValue;
- }
- }
+void FBXSerializer::consolidateHFMMaterials() {
for (QHash::iterator it = _hfmMaterials.begin(); it != _hfmMaterials.end(); it++) {
HFMMaterial& material = (*it);
@@ -266,23 +258,6 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
}
qCDebug(modelformat) << " fbx material Name:" << material.name;
- if (materialMap.contains(material.name)) {
- QJsonObject materialOptions = materialMap.value(material.name).toObject();
- qCDebug(modelformat) << "Mapping fbx material:" << material.name << " with HifiMaterial: " << materialOptions;
-
- if (materialOptions.contains("scattering")) {
- float scattering = (float) materialOptions.value("scattering").toDouble();
- material._material->setScattering(scattering);
- }
-
- if (materialOptions.contains("scatteringMap")) {
- QByteArray scatteringMap = materialOptions.value("scatteringMap").toVariant().toByteArray();
- material.scatteringTexture = HFMTexture();
- material.scatteringTexture.name = material.name + ".scatteringMap";
- material.scatteringTexture.filename = scatteringMap;
- }
- }
-
if (material.opacity <= 0.0f) {
material._material->setOpacity(1.0f);
} else {
diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 82a4361723..736e7831c1 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -892,6 +892,23 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
for (int n = 0; n < colors.size() - 3; n += stride) {
mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2]));
}
+ } else if (key == "TANGENT") {
+ QVector tangents;
+ success = addArrayOfType(buffer.blob,
+ bufferview.byteOffset + accBoffset,
+ accessor.count,
+ tangents,
+ accessor.type,
+ accessor.componentType);
+ if (!success) {
+ qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url;
+ continue;
+ }
+ int stride = (accessor.type == GLTFAccessorType::VEC4) ? 4 : 3;
+ for (int n = 0; n < tangents.size() - 3; n += stride) {
+ float tanW = stride == 4 ? tangents[n + 3] : 1;
+ mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tangents[n + 2]));
+ }
} else if (key == "TEXCOORD_0") {
QVector texcoords;
success = addArrayOfType(buffer.blob,
@@ -931,7 +948,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
}
mesh.parts.push_back(part);
- // populate the texture coordenates if they don't exist
+ // populate the texture coordinates if they don't exist
if (mesh.texCoords.size() == 0) {
for (int i = 0; i < part.triangleIndices.size(); i++) mesh.texCoords.push_back(glm::vec2(0.0, 1.0));
}
diff --git a/libraries/gpu/src/gpu/State.h b/libraries/gpu/src/gpu/State.h
index abe0cd7731..2e8a3f2cab 100755
--- a/libraries/gpu/src/gpu/State.h
+++ b/libraries/gpu/src/gpu/State.h
@@ -246,7 +246,7 @@ public:
struct Flags {
Flags() :
- frontFaceClockwise(false), depthClampEnable(false), scissorEnable(false), multisampleEnable(false),
+ frontFaceClockwise(false), depthClampEnable(false), scissorEnable(false), multisampleEnable(true),
antialisedLineEnable(true), alphaToCoverageEnable(false), _spare1(0) {}
bool frontFaceClockwise : 1;
bool depthClampEnable : 1;
diff --git a/libraries/graphics-scripting/CMakeLists.txt b/libraries/graphics-scripting/CMakeLists.txt
index 0f59fb41f8..9bb34adda1 100644
--- a/libraries/graphics-scripting/CMakeLists.txt
+++ b/libraries/graphics-scripting/CMakeLists.txt
@@ -1,4 +1,4 @@
set(TARGET_NAME graphics-scripting)
setup_hifi_library()
-link_hifi_libraries(shared networking graphics fbx image model-networking script-engine)
+link_hifi_libraries(shared networking graphics fbx image material-networking model-networking script-engine)
include_hifi_library_headers(gpu)
diff --git a/libraries/material-networking/CMakeLists.txt b/libraries/material-networking/CMakeLists.txt
new file mode 100644
index 0000000000..2bf8ea213d
--- /dev/null
+++ b/libraries/material-networking/CMakeLists.txt
@@ -0,0 +1,5 @@
+set(TARGET_NAME material-networking)
+setup_hifi_library()
+link_hifi_libraries(shared shaders networking graphics ktx image gl)
+include_hifi_library_headers(gpu)
+include_hifi_library_headers(hfm)
\ No newline at end of file
diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/material-networking/src/material-networking/KTXCache.cpp
similarity index 100%
rename from libraries/model-networking/src/model-networking/KTXCache.cpp
rename to libraries/material-networking/src/material-networking/KTXCache.cpp
diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/material-networking/src/material-networking/KTXCache.h
similarity index 100%
rename from libraries/model-networking/src/model-networking/KTXCache.h
rename to libraries/material-networking/src/material-networking/KTXCache.h
diff --git a/libraries/model-networking/src/model-networking/MaterialCache.cpp b/libraries/material-networking/src/material-networking/MaterialCache.cpp
similarity index 63%
rename from libraries/model-networking/src/model-networking/MaterialCache.cpp
rename to libraries/material-networking/src/material-networking/MaterialCache.cpp
index aaa9767397..6561fc697e 100644
--- a/libraries/model-networking/src/model-networking/MaterialCache.cpp
+++ b/libraries/material-networking/src/material-networking/MaterialCache.cpp
@@ -425,5 +425,311 @@ QSharedPointer MaterialCache::createResource(const QUrl& url) {
}
QSharedPointer MaterialCache::createResourceCopy(const QSharedPointer& resource) {
- return QSharedPointer(new NetworkMaterialResource(*resource.staticCast().data()), &Resource::deleter);
+ return QSharedPointer(new NetworkMaterialResource(*resource.staticCast()), &Resource::deleter);
+}
+
+NetworkMaterial::NetworkMaterial(const NetworkMaterial& m) :
+ Material(m),
+ _textures(m._textures),
+ _albedoTransform(m._albedoTransform),
+ _lightmapTransform(m._lightmapTransform),
+ _lightmapParams(m._lightmapParams),
+ _isOriginal(m._isOriginal)
+{}
+
+const QString NetworkMaterial::NO_TEXTURE = QString();
+
+const QString& NetworkMaterial::getTextureName(MapChannel channel) {
+ if (_textures[channel].texture) {
+ return _textures[channel].name;
+ }
+ return NO_TEXTURE;
+}
+
+QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const HFMTexture& texture) {
+ if (texture.content.isEmpty()) {
+ // External file: search relative to the baseUrl, in case filename is relative
+ return baseUrl.resolved(QUrl(texture.filename));
+ } else {
+ // Inlined file: cache under the fbx file to avoid namespace clashes
+ // NOTE: We cannot resolve the path because filename may be an absolute path
+ assert(texture.filename.size() > 0);
+ auto baseUrlStripped = baseUrl.toDisplayString(QUrl::RemoveFragment | QUrl::RemoveQuery | QUrl::RemoveUserInfo);
+ if (texture.filename.at(0) == '/') {
+ return baseUrlStripped + texture.filename;
+ } else {
+ return baseUrlStripped + '/' + texture.filename;
+ }
+ }
+}
+
+graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture,
+ image::TextureUsage::Type type, MapChannel channel) {
+
+ if (baseUrl.isEmpty()) {
+ return nullptr;
+ }
+
+ const auto url = getTextureUrl(baseUrl, hfmTexture);
+ const auto texture = DependencyManager::get()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels, hfmTexture.sourceChannel);
+ _textures[channel] = Texture { hfmTexture.name, texture };
+
+ auto map = std::make_shared();
+ if (texture) {
+ map->setTextureSource(texture->_textureSource);
+ }
+ map->setTextureTransform(hfmTexture.transform);
+
+ return map;
+}
+
+graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel) {
+ auto textureCache = DependencyManager::get();
+ if (textureCache && !url.isEmpty()) {
+ auto texture = textureCache->getTexture(url, type);
+ _textures[channel].texture = texture;
+
+ auto map = std::make_shared();
+ if (texture) {
+ map->setTextureSource(texture->_textureSource);
+ }
+
+ return map;
+ }
+ return nullptr;
+}
+
+void NetworkMaterial::setAlbedoMap(const QUrl& url, bool useAlphaChannel) {
+ auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
+ if (map) {
+ map->setUseAlphaChannel(useAlphaChannel);
+ setTextureMap(MapChannel::ALBEDO_MAP, map);
+ }
+}
+
+void NetworkMaterial::setNormalMap(const QUrl& url, bool isBumpmap) {
+ auto map = fetchTextureMap(url, isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
+ if (map) {
+ setTextureMap(MapChannel::NORMAL_MAP, map);
+ }
+}
+
+void NetworkMaterial::setRoughnessMap(const QUrl& url, bool isGloss) {
+ auto map = fetchTextureMap(url, isGloss ? image::TextureUsage::GLOSS_TEXTURE : image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
+ if (map) {
+ setTextureMap(MapChannel::ROUGHNESS_MAP, map);
+ }
+}
+
+void NetworkMaterial::setMetallicMap(const QUrl& url, bool isSpecular) {
+ auto map = fetchTextureMap(url, isSpecular ? image::TextureUsage::SPECULAR_TEXTURE : image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
+ if (map) {
+ setTextureMap(MapChannel::METALLIC_MAP, map);
+ }
+}
+
+void NetworkMaterial::setOcclusionMap(const QUrl& url) {
+ auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
+ if (map) {
+ setTextureMap(MapChannel::OCCLUSION_MAP, map);
+ }
+}
+
+void NetworkMaterial::setEmissiveMap(const QUrl& url) {
+ auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
+ if (map) {
+ setTextureMap(MapChannel::EMISSIVE_MAP, map);
+ }
+}
+
+void NetworkMaterial::setScatteringMap(const QUrl& url) {
+ auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
+ if (map) {
+ setTextureMap(MapChannel::SCATTERING_MAP, map);
+ }
+}
+
+void NetworkMaterial::setLightmapMap(const QUrl& url) {
+ auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
+ if (map) {
+ //map->setTextureTransform(_lightmapTransform);
+ //map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
+ setTextureMap(MapChannel::LIGHTMAP_MAP, map);
+ }
+}
+
+NetworkMaterial::NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl) :
+ graphics::Material(*material._material),
+ _textures(MapChannel::NUM_MAP_CHANNELS)
+{
+ _name = material.name.toStdString();
+ if (!material.albedoTexture.filename.isEmpty()) {
+ auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
+ if (map) {
+ _albedoTransform = material.albedoTexture.transform;
+ map->setTextureTransform(_albedoTransform);
+
+ if (!material.opacityTexture.filename.isEmpty()) {
+ if (material.albedoTexture.filename == material.opacityTexture.filename) {
+ // Best case scenario, just indicating that the albedo map contains transparency
+ // TODO: Different albedo/opacity maps are not currently supported
+ map->setUseAlphaChannel(true);
+ }
+ }
+ }
+
+ setTextureMap(MapChannel::ALBEDO_MAP, map);
+ }
+
+
+ if (!material.normalTexture.filename.isEmpty()) {
+ auto type = (material.normalTexture.isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE);
+ auto map = fetchTextureMap(textureBaseUrl, material.normalTexture, type, MapChannel::NORMAL_MAP);
+ setTextureMap(MapChannel::NORMAL_MAP, map);
+ }
+
+ if (!material.roughnessTexture.filename.isEmpty()) {
+ auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
+ setTextureMap(MapChannel::ROUGHNESS_MAP, map);
+ } else if (!material.glossTexture.filename.isEmpty()) {
+ auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, image::TextureUsage::GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP);
+ setTextureMap(MapChannel::ROUGHNESS_MAP, map);
+ }
+
+ if (!material.metallicTexture.filename.isEmpty()) {
+ auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
+ setTextureMap(MapChannel::METALLIC_MAP, map);
+ } else if (!material.specularTexture.filename.isEmpty()) {
+ auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, image::TextureUsage::SPECULAR_TEXTURE, MapChannel::METALLIC_MAP);
+ setTextureMap(MapChannel::METALLIC_MAP, map);
+ }
+
+ if (!material.occlusionTexture.filename.isEmpty()) {
+ auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
+ if (map) {
+ map->setTextureTransform(material.occlusionTexture.transform);
+ }
+ setTextureMap(MapChannel::OCCLUSION_MAP, map);
+ }
+
+ if (!material.emissiveTexture.filename.isEmpty()) {
+ auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
+ setTextureMap(MapChannel::EMISSIVE_MAP, map);
+ }
+
+ if (!material.scatteringTexture.filename.isEmpty()) {
+ auto map = fetchTextureMap(textureBaseUrl, material.scatteringTexture, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
+ setTextureMap(MapChannel::SCATTERING_MAP, map);
+ }
+
+ if (!material.lightmapTexture.filename.isEmpty()) {
+ auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
+ if (map) {
+ _lightmapTransform = material.lightmapTexture.transform;
+ _lightmapParams = material.lightmapParams;
+ map->setTextureTransform(_lightmapTransform);
+ map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
+ }
+ setTextureMap(MapChannel::LIGHTMAP_MAP, map);
+ }
+}
+
+void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
+ _isOriginal = false;
+
+ const auto& albedoName = getTextureName(MapChannel::ALBEDO_MAP);
+ const auto& normalName = getTextureName(MapChannel::NORMAL_MAP);
+ const auto& roughnessName = getTextureName(MapChannel::ROUGHNESS_MAP);
+ const auto& metallicName = getTextureName(MapChannel::METALLIC_MAP);
+ const auto& occlusionName = getTextureName(MapChannel::OCCLUSION_MAP);
+ const auto& emissiveName = getTextureName(MapChannel::EMISSIVE_MAP);
+ const auto& lightmapName = getTextureName(MapChannel::LIGHTMAP_MAP);
+ const auto& scatteringName = getTextureName(MapChannel::SCATTERING_MAP);
+
+ if (!albedoName.isEmpty()) {
+ auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl();
+ auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
+ if (map) {
+ map->setTextureTransform(_albedoTransform);
+ // when reassigning the albedo texture we also check for the alpha channel used as opacity
+ map->setUseAlphaChannel(true);
+ }
+ setTextureMap(MapChannel::ALBEDO_MAP, map);
+ }
+
+ if (!normalName.isEmpty()) {
+ auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl();
+ auto map = fetchTextureMap(url, image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
+ setTextureMap(MapChannel::NORMAL_MAP, map);
+ }
+
+ if (!roughnessName.isEmpty()) {
+ auto url = textureMap.contains(roughnessName) ? textureMap[roughnessName].toUrl() : QUrl();
+ // FIXME: If passing a gloss map instead of a roughmap how do we know?
+ auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
+ setTextureMap(MapChannel::ROUGHNESS_MAP, map);
+ }
+
+ if (!metallicName.isEmpty()) {
+ auto url = textureMap.contains(metallicName) ? textureMap[metallicName].toUrl() : QUrl();
+ // FIXME: If passing a specular map instead of a metallic how do we know?
+ auto map = fetchTextureMap(url, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
+ setTextureMap(MapChannel::METALLIC_MAP, map);
+ }
+
+ if (!occlusionName.isEmpty()) {
+ auto url = textureMap.contains(occlusionName) ? textureMap[occlusionName].toUrl() : QUrl();
+ // FIXME: we need to handle the occlusion map transform here
+ auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
+ setTextureMap(MapChannel::OCCLUSION_MAP, map);
+ }
+
+ if (!emissiveName.isEmpty()) {
+ auto url = textureMap.contains(emissiveName) ? textureMap[emissiveName].toUrl() : QUrl();
+ auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
+ setTextureMap(MapChannel::EMISSIVE_MAP, map);
+ }
+
+ if (!scatteringName.isEmpty()) {
+ auto url = textureMap.contains(scatteringName) ? textureMap[scatteringName].toUrl() : QUrl();
+ auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
+ setTextureMap(MapChannel::SCATTERING_MAP, map);
+ }
+
+ if (!lightmapName.isEmpty()) {
+ auto url = textureMap.contains(lightmapName) ? textureMap[lightmapName].toUrl() : QUrl();
+ auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
+ if (map) {
+ map->setTextureTransform(_lightmapTransform);
+ map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
+ }
+ setTextureMap(MapChannel::LIGHTMAP_MAP, map);
+ }
+}
+
+bool NetworkMaterial::isMissingTexture() {
+ for (auto& networkTexture : _textures) {
+ auto& texture = networkTexture.texture;
+ if (!texture) {
+ continue;
+ }
+ // Failed texture downloads need to be considered as 'loaded'
+ // or the object will never fade in
+ bool finished = texture->isFailed() || (texture->isLoaded() && texture->getGPUTexture() && texture->getGPUTexture()->isDefined());
+ if (!finished) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void NetworkMaterial::checkResetOpacityMap() {
+ // If material textures are loaded, check the material translucency
+ // FIXME: This should not be done here. The opacity map should already be reset in Material::setTextureMap.
+ // However, currently that code can be called before the albedo map is defined, so resetOpacityMap will fail.
+ // Geometry::areTexturesLoaded() is called repeatedly until it returns true, so we do the check here for now
+ const auto& albedoTexture = _textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
+ if (albedoTexture.texture) {
+ resetOpacityMap();
+ }
}
\ No newline at end of file
diff --git a/libraries/material-networking/src/material-networking/MaterialCache.h b/libraries/material-networking/src/material-networking/MaterialCache.h
new file mode 100644
index 0000000000..d327aedb22
--- /dev/null
+++ b/libraries/material-networking/src/material-networking/MaterialCache.h
@@ -0,0 +1,117 @@
+//
+// Created by Sam Gondelman on 2/9/2018
+// Copyright 2018 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+#ifndef hifi_MaterialCache_h
+#define hifi_MaterialCache_h
+
+#include "glm/glm.hpp"
+
+#include
+#include
+#include
+
+#include "TextureCache.h"
+
+class NetworkMaterial : public graphics::Material {
+public:
+ using MapChannel = graphics::Material::MapChannel;
+
+ NetworkMaterial() : _textures(MapChannel::NUM_MAP_CHANNELS) {}
+ NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl);
+ NetworkMaterial(const NetworkMaterial& material);
+
+ void setAlbedoMap(const QUrl& url, bool useAlphaChannel);
+ void setNormalMap(const QUrl& url, bool isBumpmap);
+ void setRoughnessMap(const QUrl& url, bool isGloss);
+ void setMetallicMap(const QUrl& url, bool isSpecular);
+ void setOcclusionMap(const QUrl& url);
+ void setEmissiveMap(const QUrl& url);
+ void setScatteringMap(const QUrl& url);
+ void setLightmapMap(const QUrl& url);
+
+ bool isMissingTexture();
+ void checkResetOpacityMap();
+
+protected:
+ friend class Geometry;
+
+ class Texture {
+ public:
+ QString name;
+ NetworkTexturePointer texture;
+ };
+ using Textures = std::vector;
+
+ Textures _textures;
+
+ static const QString NO_TEXTURE;
+ const QString& getTextureName(MapChannel channel);
+
+ void setTextures(const QVariantMap& textureMap);
+
+ const bool& isOriginal() const { return _isOriginal; }
+
+private:
+ // Helpers for the ctors
+ QUrl getTextureUrl(const QUrl& baseUrl, const HFMTexture& hfmTexture);
+ graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture,
+ image::TextureUsage::Type type, MapChannel channel);
+ graphics::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel);
+
+ Transform _albedoTransform;
+ Transform _lightmapTransform;
+ vec2 _lightmapParams;
+
+ bool _isOriginal { true };
+};
+
+class NetworkMaterialResource : public Resource {
+public:
+ NetworkMaterialResource() : Resource() {}
+ NetworkMaterialResource(const QUrl& url);
+
+ QString getType() const override { return "NetworkMaterial"; }
+
+ virtual void downloadFinished(const QByteArray& data) override;
+
+ typedef struct ParsedMaterials {
+ uint version { 1 };
+ std::vector names;
+ std::unordered_map> networkMaterials;
+
+ void reset() {
+ version = 1;
+ names.clear();
+ networkMaterials.clear();
+ }
+
+ } ParsedMaterials;
+
+ ParsedMaterials parsedMaterials;
+
+ static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl);
+ static std::pair> parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl);
+
+private:
+ static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB);
+};
+
+using NetworkMaterialResourcePointer = QSharedPointer;
+using MaterialMapping = std::vector>;
+
+class MaterialCache : public ResourceCache {
+public:
+ static MaterialCache& instance();
+
+ NetworkMaterialResourcePointer getMaterial(const QUrl& url);
+
+protected:
+ virtual QSharedPointer createResource(const QUrl& url) override;
+ QSharedPointer createResourceCopy(const QSharedPointer& resource) override;
+};
+
+#endif
diff --git a/libraries/material-networking/src/material-networking/MaterialNetworkingLogging.cpp b/libraries/material-networking/src/material-networking/MaterialNetworkingLogging.cpp
new file mode 100644
index 0000000000..9a99c21240
--- /dev/null
+++ b/libraries/material-networking/src/material-networking/MaterialNetworkingLogging.cpp
@@ -0,0 +1,11 @@
+//
+// Created by Sam Gondelman on 2/7/2019
+// Copyright 2019 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "MaterialNetworkingLogging.h"
+
+Q_LOGGING_CATEGORY(materialnetworking, "hifi.gpu-material-network")
diff --git a/libraries/material-networking/src/material-networking/MaterialNetworkingLogging.h b/libraries/material-networking/src/material-networking/MaterialNetworkingLogging.h
new file mode 100644
index 0000000000..a3f220d027
--- /dev/null
+++ b/libraries/material-networking/src/material-networking/MaterialNetworkingLogging.h
@@ -0,0 +1,11 @@
+//
+// Created by Sam Gondelman on 2/7/2019
+// Copyright 2019 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include
+
+Q_DECLARE_LOGGING_CATEGORY(materialnetworking)
diff --git a/libraries/model-networking/src/model-networking/ShaderCache.cpp b/libraries/material-networking/src/material-networking/ShaderCache.cpp
similarity index 94%
rename from libraries/model-networking/src/model-networking/ShaderCache.cpp
rename to libraries/material-networking/src/material-networking/ShaderCache.cpp
index 8d060c42f2..4c8d659315 100644
--- a/libraries/model-networking/src/model-networking/ShaderCache.cpp
+++ b/libraries/material-networking/src/material-networking/ShaderCache.cpp
@@ -29,5 +29,5 @@ QSharedPointer ShaderCache::createResource(const QUrl& url) {
}
QSharedPointer ShaderCache::createResourceCopy(const QSharedPointer& resource) {
- return QSharedPointer(new NetworkShader(*resource.staticCast().data()), &Resource::deleter);
+ return QSharedPointer(new NetworkShader(*resource.staticCast()), &Resource::deleter);
}
diff --git a/libraries/model-networking/src/model-networking/ShaderCache.h b/libraries/material-networking/src/material-networking/ShaderCache.h
similarity index 100%
rename from libraries/model-networking/src/model-networking/ShaderCache.h
rename to libraries/material-networking/src/material-networking/ShaderCache.h
diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/material-networking/src/material-networking/TextureCache.cpp
similarity index 98%
rename from libraries/model-networking/src/model-networking/TextureCache.cpp
rename to libraries/material-networking/src/material-networking/TextureCache.cpp
index a268f4ad0a..ee3c88f02c 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.cpp
+++ b/libraries/material-networking/src/material-networking/TextureCache.cpp
@@ -44,7 +44,7 @@
#include
#include "NetworkLogging.h"
-#include "ModelNetworkingLogging.h"
+#include "MaterialNetworkingLogging.h"
#include "NetworkingConstants.h"
#include
#include
@@ -198,16 +198,16 @@ public:
namespace std {
template <>
struct hash {
- size_t operator()(const QByteArray& a) const {
- return qHash(a);
+ size_t operator()(const QByteArray& byteArray) const {
+ return qHash(byteArray);
}
};
template <>
struct hash {
- size_t operator()(const TextureExtra& a) const {
+ size_t operator()(const TextureExtra& textureExtra) const {
size_t result = 0;
- hash_combine(result, (int)a.type, a.content, a.maxNumPixels, (int)a.sourceChannel);
+ hash_combine(result, (int)textureExtra.type, textureExtra.content, textureExtra.maxNumPixels, (int)textureExtra.sourceChannel);
return result;
}
};
@@ -329,15 +329,14 @@ QSharedPointer TextureCache::createResource(const QUrl& url) {
}
QSharedPointer TextureCache::createResourceCopy(const QSharedPointer& resource) {
- return QSharedPointer(new NetworkTexture(*resource.staticCast().data()), &Resource::deleter);
+ return QSharedPointer(new NetworkTexture(*resource.staticCast()), &Resource::deleter);
}
int networkTexturePointerMetaTypeId = qRegisterMetaType>();
NetworkTexture::NetworkTexture(const QUrl& url, bool resourceTexture) :
Resource(url),
- Texture(),
- _maxNumPixels(100)
+ Texture()
{
if (resourceTexture) {
_textureSource = std::make_shared(url);
@@ -955,7 +954,7 @@ void NetworkTexture::handleFinishedInitialLoad() {
cache::FilePointer file;
auto& ktxCache = textureCache->_ktxCache;
if (!memKtx || !(file = ktxCache->writeFile(data, KTXCache::Metadata(filename, length)))) {
- qCWarning(modelnetworking) << url << " failed to write cache file";
+ qCWarning(materialnetworking) << url << " failed to write cache file";
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0),
@@ -1145,7 +1144,7 @@ void ImageReader::listSupportedImageFormats() {
static std::once_flag once;
std::call_once(once, []{
auto supportedFormats = QImageReader::supportedImageFormats();
- qCDebug(modelnetworking) << "List of supported Image formats:" << supportedFormats.join(", ");
+ qCDebug(materialnetworking) << "List of supported Image formats:" << supportedFormats.join(", ");
});
}
@@ -1194,7 +1193,7 @@ void ImageReader::read() {
if (texture) {
texture = textureCache->cacheTextureByHash(hash, texture);
} else {
- qCWarning(modelnetworking) << "Invalid cached KTX " << _url << " under hash " << hash.c_str() << ", recreating...";
+ qCWarning(materialnetworking) << "Invalid cached KTX " << _url << " under hash " << hash.c_str() << ", recreating...";
}
}
}
diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/material-networking/src/material-networking/TextureCache.h
similarity index 100%
rename from libraries/model-networking/src/model-networking/TextureCache.h
rename to libraries/material-networking/src/material-networking/TextureCache.h
diff --git a/libraries/model-networking/src/model-networking/TextureCacheScriptingInterface.cpp b/libraries/material-networking/src/material-networking/TextureCacheScriptingInterface.cpp
similarity index 100%
rename from libraries/model-networking/src/model-networking/TextureCacheScriptingInterface.cpp
rename to libraries/material-networking/src/material-networking/TextureCacheScriptingInterface.cpp
diff --git a/libraries/model-networking/src/model-networking/TextureCacheScriptingInterface.h b/libraries/material-networking/src/material-networking/TextureCacheScriptingInterface.h
similarity index 100%
rename from libraries/model-networking/src/model-networking/TextureCacheScriptingInterface.h
rename to libraries/material-networking/src/material-networking/TextureCacheScriptingInterface.h
diff --git a/libraries/model-baker/CMakeLists.txt b/libraries/model-baker/CMakeLists.txt
index aabd6eba3a..22c240b487 100644
--- a/libraries/model-baker/CMakeLists.txt
+++ b/libraries/model-baker/CMakeLists.txt
@@ -1,6 +1,7 @@
set(TARGET_NAME model-baker)
setup_hifi_library()
-link_hifi_libraries(shared task gpu graphics hfm)
-
+link_hifi_libraries(shared shaders task gpu graphics hfm material-networking)
+include_hifi_library_headers(networking)
include_hifi_library_headers(image)
+include_hifi_library_headers(ktx)
\ No newline at end of file
diff --git a/libraries/model-baker/src/model-baker/Baker.cpp b/libraries/model-baker/src/model-baker/Baker.cpp
index 1c2a2f5c63..dfb18eef86 100644
--- a/libraries/model-baker/src/model-baker/Baker.cpp
+++ b/libraries/model-baker/src/model-baker/Baker.cpp
@@ -101,7 +101,7 @@ namespace baker {
class BuildModelTask {
public:
- using Input = VaryingSet5, std::vector, QMap /*jointRotationOffsets*/, QHash /*jointIndices*/>;
+ using Input = VaryingSet5, std::vector, QMap, QHash>;
using Output = hfm::Model::Pointer;
using JobModel = Job::ModelIO;
@@ -118,9 +118,9 @@ namespace baker {
class BakerEngineBuilder {
public:
using Input = VaryingSet2;
- using Output = hfm::Model::Pointer;
+ using Output = VaryingSet2;
using JobModel = Task::ModelIO;
- void build(JobModel& model, const Varying& input, Varying& hfmModelOut) {
+ void build(JobModel& model, const Varying& input, Varying& output) {
const auto& hfmModelIn = input.getN(0);
const auto& mapping = input.getN(1);
@@ -154,13 +154,18 @@ namespace baker {
const auto jointRotationOffsets = jointInfoOut.getN(1);
const auto jointIndices = jointInfoOut.getN(2);
+ // Parse material mapping
+ const auto materialMapping = model.addJob("ParseMaterialMapping", mapping);
+
// Combine the outputs into a new hfm::Model
const auto buildBlendshapesInputs = BuildBlendshapesTask::Input(blendshapesPerMeshIn, normalsPerBlendshapePerMesh, tangentsPerBlendshapePerMesh).asVarying();
const auto blendshapesPerMeshOut = model.addJob("BuildBlendshapes", buildBlendshapesInputs);
const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, normalsPerMesh, tangentsPerMesh, blendshapesPerMeshOut).asVarying();
const auto meshesOut = model.addJob("BuildMeshes", buildMeshesInputs);
const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices).asVarying();
- hfmModelOut = model.addJob("BuildModel", buildModelInputs);
+ const auto hfmModelOut = model.addJob("BuildModel", buildModelInputs);
+
+ output = Output(hfmModelOut, materialMapping);
}
};
@@ -172,7 +177,8 @@ namespace baker {
void Baker::run() {
_engine->run();
- hfmModel = _engine->getOutput().get();
+ hfmModel = _engine->getOutput().get().get0();
+ materialMapping = _engine->getOutput().get().get1();
}
};
diff --git a/libraries/model-baker/src/model-baker/Baker.h b/libraries/model-baker/src/model-baker/Baker.h
index 41989d73df..542be0b559 100644
--- a/libraries/model-baker/src/model-baker/Baker.h
+++ b/libraries/model-baker/src/model-baker/Baker.h
@@ -18,6 +18,8 @@
#include "Engine.h"
+#include "ParseMaterialMappingTask.h"
+
namespace baker {
class Baker {
public:
@@ -27,6 +29,7 @@ namespace baker {
// Outputs, available after run() is called
hfm::Model::Pointer hfmModel;
+ MaterialMapping materialMapping;
protected:
EnginePointer _engine;
diff --git a/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.cpp b/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.cpp
new file mode 100644
index 0000000000..7a923a3702
--- /dev/null
+++ b/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.cpp
@@ -0,0 +1,76 @@
+//
+// Created by Sam Gondelman on 2/7/2019
+// Copyright 2019 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "ParseMaterialMappingTask.h"
+
+#include "ModelBakerLogging.h"
+
+void ParseMaterialMappingTask::run(const baker::BakeContextPointer& context, const Input& mapping, Output& output) {
+ MaterialMapping materialMapping;
+
+ auto mappingIter = mapping.find("materialMap");
+ if (mappingIter != mapping.end()) {
+ QByteArray materialMapValue = mappingIter.value().toByteArray();
+ QJsonObject materialMap = QJsonDocument::fromJson(materialMapValue).object();
+ if (materialMap.isEmpty()) {
+ qCDebug(model_baker) << "Material Map found but did not produce valid JSON:" << materialMapValue;
+ } else {
+ auto mappingKeys = materialMap.keys();
+ for (auto mapping : mappingKeys) {
+ auto mappingJSON = materialMap[mapping];
+ if (mappingJSON.isObject()) {
+ auto mappingValue = mappingJSON.toObject();
+
+ // Old subsurface scattering mapping
+ {
+ auto scatteringIter = mappingValue.find("scattering");
+ auto scatteringMapIter = mappingValue.find("scatteringMap");
+ if (scatteringIter != mappingValue.end() || scatteringMapIter != mappingValue.end()) {
+ std::shared_ptr material = std::make_shared();
+
+ if (scatteringIter != mappingValue.end()) {
+ float scattering = (float)scatteringIter.value().toDouble();
+ material->setScattering(scattering);
+ }
+
+ if (scatteringMapIter != mappingValue.end()) {
+ QString scatteringMap = scatteringMapIter.value().toString();
+ material->setScatteringMap(scatteringMap);
+ }
+
+ material->setDefaultFallthrough(true);
+
+ NetworkMaterialResourcePointer materialResource = NetworkMaterialResourcePointer(new NetworkMaterialResource(), [](NetworkMaterialResource* ptr) { ptr->deleteLater(); });
+ materialResource->moveToThread(qApp->thread());
+ materialResource->parsedMaterials.names.push_back("scattering");
+ materialResource->parsedMaterials.networkMaterials["scattering"] = material;
+
+ materialMapping.push_back(std::pair("mat::" + mapping.toStdString(), materialResource));
+ continue;
+ }
+ }
+
+ // Material JSON description
+ {
+ NetworkMaterialResourcePointer materialResource = NetworkMaterialResourcePointer(new NetworkMaterialResource(), [](NetworkMaterialResource* ptr) { ptr->deleteLater(); });
+ materialResource->moveToThread(qApp->thread());
+ // TODO: add baseURL to allow FSTs to reference relative files next to them
+ materialResource->parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument(mappingValue), QUrl());
+ materialMapping.push_back(std::pair(mapping.toStdString(), materialResource));
+ }
+
+ } else if (mappingJSON.isString()) {
+ auto mappingValue = mappingJSON.toString();
+ materialMapping.push_back(std::pair(mapping.toStdString(), MaterialCache::instance().getMaterial(mappingValue)));
+ }
+ }
+ }
+ }
+
+ output = materialMapping;
+}
diff --git a/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.h b/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.h
new file mode 100644
index 0000000000..69e00b0324
--- /dev/null
+++ b/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.h
@@ -0,0 +1,29 @@
+//
+// Created by Sam Gondelman on 2/7/2019
+// Copyright 2019 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_ParseMaterialMappingTask_h
+#define hifi_ParseMaterialMappingTask_h
+
+#include
+
+#include
+
+#include "Engine.h"
+
+#include
+
+class ParseMaterialMappingTask {
+public:
+ using Input = QVariantHash;
+ using Output = MaterialMapping;
+ using JobModel = baker::Job::ModelIO;
+
+ void run(const baker::BakeContextPointer& context, const Input& input, Output& output);
+};
+
+#endif // hifi_ParseMaterialMappingTask_h
\ No newline at end of file
diff --git a/libraries/model-networking/CMakeLists.txt b/libraries/model-networking/CMakeLists.txt
index 6a7182cc33..e79d18f779 100644
--- a/libraries/model-networking/CMakeLists.txt
+++ b/libraries/model-networking/CMakeLists.txt
@@ -1,6 +1,8 @@
set(TARGET_NAME model-networking)
setup_hifi_library()
-link_hifi_libraries(shared shaders networking graphics fbx ktx image gl model-baker)
-include_hifi_library_headers(gpu)
+link_hifi_libraries(shared shaders networking graphics fbx material-networking model-baker)
include_hifi_library_headers(hfm)
include_hifi_library_headers(task)
+include_hifi_library_headers(gpu)
+include_hifi_library_headers(image)
+include_hifi_library_headers(ktx)
\ No newline at end of file
diff --git a/libraries/model-networking/src/model-networking/MaterialCache.h b/libraries/model-networking/src/model-networking/MaterialCache.h
deleted file mode 100644
index 6abadfc030..0000000000
--- a/libraries/model-networking/src/model-networking/MaterialCache.h
+++ /dev/null
@@ -1,60 +0,0 @@
-//
-// Created by Sam Gondelman on 2/9/2018
-// Copyright 2018 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-#ifndef hifi_MaterialCache_h
-#define hifi_MaterialCache_h
-
-#include
-
-#include "glm/glm.hpp"
-
-#include "ModelCache.h"
-
-class NetworkMaterialResource : public Resource {
-public:
- NetworkMaterialResource(const QUrl& url);
-
- QString getType() const override { return "NetworkMaterial"; }
-
- virtual void downloadFinished(const QByteArray& data) override;
-
- typedef struct ParsedMaterials {
- uint version { 1 };
- std::vector names;
- std::unordered_map> networkMaterials;
-
- void reset() {
- version = 1;
- names.clear();
- networkMaterials.clear();
- }
-
- } ParsedMaterials;
-
- ParsedMaterials parsedMaterials;
-
- static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl);
- static std::pair> parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl);
-
-private:
- static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB);
-};
-
-using NetworkMaterialResourcePointer = QSharedPointer;
-
-class MaterialCache : public ResourceCache {
-public:
- static MaterialCache& instance();
-
- NetworkMaterialResourcePointer getMaterial(const QUrl& url);
-
-protected:
- virtual QSharedPointer createResource(const QUrl& url) override;
- QSharedPointer createResourceCopy(const QSharedPointer& resource) override;
-};
-
-#endif
diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp
index 90925d17c3..581196b2cc 100644
--- a/libraries/model-networking/src/model-networking/ModelCache.cpp
+++ b/libraries/model-networking/src/model-networking/ModelCache.cpp
@@ -76,9 +76,9 @@ namespace std {
template <>
struct hash {
- size_t operator()(const GeometryExtra& a) const {
+ size_t operator()(const GeometryExtra& geometryExtra) const {
size_t result = 0;
- hash_combine(result, a.mapping, a.textureBaseUrl, a.combineParts);
+ hash_combine(result, geometryExtra.mapping, geometryExtra.textureBaseUrl, geometryExtra.combineParts);
return result;
}
};
@@ -174,6 +174,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
void GeometryMappingResource::onGeometryMappingLoaded(bool success) {
if (success && _geometryResource) {
_hfmModel = _geometryResource->_hfmModel;
+ _materialMapping = _geometryResource->_materialMapping;
_meshParts = _geometryResource->_meshParts;
_meshes = _geometryResource->_meshes;
_materials = _geometryResource->_materials;
@@ -341,6 +342,7 @@ void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmMode
// Assume ownership of the processed HFMModel
_hfmModel = modelBaker.hfmModel;
+ _materialMapping = modelBaker.materialMapping;
// Copy materials
QHash materialIDAtlas;
@@ -392,7 +394,7 @@ QSharedPointer ModelCache::createResource(const QUrl& url) {
}
QSharedPointer ModelCache::createResourceCopy(const QSharedPointer& resource) {
- return QSharedPointer(new GeometryDefinitionResource(*resource.staticCast().data()), &Resource::deleter);
+ return QSharedPointer(new GeometryDefinitionResource(*resource.staticCast()), &Resource::deleter);
}
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url,
@@ -437,6 +439,7 @@ const QVariantMap Geometry::getTextures() const {
// FIXME: The materials should only be copied when modified, but the Model currently caches the original
Geometry::Geometry(const Geometry& geometry) {
_hfmModel = geometry._hfmModel;
+ _materialMapping = geometry._materialMapping;
_meshes = geometry._meshes;
_meshParts = geometry._meshParts;
@@ -556,310 +559,4 @@ void GeometryResourceWatcher::resourceRefreshed() {
// _instance.reset();
}
-NetworkMaterial::NetworkMaterial(const NetworkMaterial& m) :
- Material(m),
- _textures(m._textures),
- _albedoTransform(m._albedoTransform),
- _lightmapTransform(m._lightmapTransform),
- _lightmapParams(m._lightmapParams),
- _isOriginal(m._isOriginal)
-{}
-
-const QString NetworkMaterial::NO_TEXTURE = QString();
-
-const QString& NetworkMaterial::getTextureName(MapChannel channel) {
- if (_textures[channel].texture) {
- return _textures[channel].name;
- }
- return NO_TEXTURE;
-}
-
-QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const HFMTexture& texture) {
- if (texture.content.isEmpty()) {
- // External file: search relative to the baseUrl, in case filename is relative
- return baseUrl.resolved(QUrl(texture.filename));
- } else {
- // Inlined file: cache under the fbx file to avoid namespace clashes
- // NOTE: We cannot resolve the path because filename may be an absolute path
- assert(texture.filename.size() > 0);
- auto baseUrlStripped = baseUrl.toDisplayString(QUrl::RemoveFragment | QUrl::RemoveQuery | QUrl::RemoveUserInfo);
- if (texture.filename.at(0) == '/') {
- return baseUrlStripped + texture.filename;
- } else {
- return baseUrlStripped + '/' + texture.filename;
- }
- }
-}
-
-graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture,
- image::TextureUsage::Type type, MapChannel channel) {
-
- if (baseUrl.isEmpty()) {
- return nullptr;
- }
-
- const auto url = getTextureUrl(baseUrl, hfmTexture);
- const auto texture = DependencyManager::get()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels, hfmTexture.sourceChannel);
- _textures[channel] = Texture { hfmTexture.name, texture };
-
- auto map = std::make_shared();
- if (texture) {
- map->setTextureSource(texture->_textureSource);
- }
- map->setTextureTransform(hfmTexture.transform);
-
- return map;
-}
-
-graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel) {
- auto textureCache = DependencyManager::get();
- if (textureCache && !url.isEmpty()) {
- auto texture = textureCache->getTexture(url, type);
- _textures[channel].texture = texture;
-
- auto map = std::make_shared();
- if (texture) {
- map->setTextureSource(texture->_textureSource);
- }
-
- return map;
- }
- return nullptr;
-}
-
-void NetworkMaterial::setAlbedoMap(const QUrl& url, bool useAlphaChannel) {
- auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
- if (map) {
- map->setUseAlphaChannel(useAlphaChannel);
- setTextureMap(MapChannel::ALBEDO_MAP, map);
- }
-}
-
-void NetworkMaterial::setNormalMap(const QUrl& url, bool isBumpmap) {
- auto map = fetchTextureMap(url, isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
- if (map) {
- setTextureMap(MapChannel::NORMAL_MAP, map);
- }
-}
-
-void NetworkMaterial::setRoughnessMap(const QUrl& url, bool isGloss) {
- auto map = fetchTextureMap(url, isGloss ? image::TextureUsage::GLOSS_TEXTURE : image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
- if (map) {
- setTextureMap(MapChannel::ROUGHNESS_MAP, map);
- }
-}
-
-void NetworkMaterial::setMetallicMap(const QUrl& url, bool isSpecular) {
- auto map = fetchTextureMap(url, isSpecular ? image::TextureUsage::SPECULAR_TEXTURE : image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
- if (map) {
- setTextureMap(MapChannel::METALLIC_MAP, map);
- }
-}
-
-void NetworkMaterial::setOcclusionMap(const QUrl& url) {
- auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
- if (map) {
- setTextureMap(MapChannel::OCCLUSION_MAP, map);
- }
-}
-
-void NetworkMaterial::setEmissiveMap(const QUrl& url) {
- auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
- if (map) {
- setTextureMap(MapChannel::EMISSIVE_MAP, map);
- }
-}
-
-void NetworkMaterial::setScatteringMap(const QUrl& url) {
- auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
- if (map) {
- setTextureMap(MapChannel::SCATTERING_MAP, map);
- }
-}
-
-void NetworkMaterial::setLightmapMap(const QUrl& url) {
- auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
- if (map) {
- //map->setTextureTransform(_lightmapTransform);
- //map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
- setTextureMap(MapChannel::LIGHTMAP_MAP, map);
- }
-}
-
-NetworkMaterial::NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl) :
- graphics::Material(*material._material),
- _textures(MapChannel::NUM_MAP_CHANNELS)
-{
- _name = material.name.toStdString();
- if (!material.albedoTexture.filename.isEmpty()) {
- auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
- if (map) {
- _albedoTransform = material.albedoTexture.transform;
- map->setTextureTransform(_albedoTransform);
-
- if (!material.opacityTexture.filename.isEmpty()) {
- if (material.albedoTexture.filename == material.opacityTexture.filename) {
- // Best case scenario, just indicating that the albedo map contains transparency
- // TODO: Different albedo/opacity maps are not currently supported
- map->setUseAlphaChannel(true);
- }
- }
- }
-
- setTextureMap(MapChannel::ALBEDO_MAP, map);
- }
-
-
- if (!material.normalTexture.filename.isEmpty()) {
- auto type = (material.normalTexture.isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE);
- auto map = fetchTextureMap(textureBaseUrl, material.normalTexture, type, MapChannel::NORMAL_MAP);
- setTextureMap(MapChannel::NORMAL_MAP, map);
- }
-
- if (!material.roughnessTexture.filename.isEmpty()) {
- auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
- setTextureMap(MapChannel::ROUGHNESS_MAP, map);
- } else if (!material.glossTexture.filename.isEmpty()) {
- auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, image::TextureUsage::GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP);
- setTextureMap(MapChannel::ROUGHNESS_MAP, map);
- }
-
- if (!material.metallicTexture.filename.isEmpty()) {
- auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
- setTextureMap(MapChannel::METALLIC_MAP, map);
- } else if (!material.specularTexture.filename.isEmpty()) {
- auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, image::TextureUsage::SPECULAR_TEXTURE, MapChannel::METALLIC_MAP);
- setTextureMap(MapChannel::METALLIC_MAP, map);
- }
-
- if (!material.occlusionTexture.filename.isEmpty()) {
- auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
- if (map) {
- map->setTextureTransform(material.occlusionTexture.transform);
- }
- setTextureMap(MapChannel::OCCLUSION_MAP, map);
- }
-
- if (!material.emissiveTexture.filename.isEmpty()) {
- auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
- setTextureMap(MapChannel::EMISSIVE_MAP, map);
- }
-
- if (!material.scatteringTexture.filename.isEmpty()) {
- auto map = fetchTextureMap(textureBaseUrl, material.scatteringTexture, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
- setTextureMap(MapChannel::SCATTERING_MAP, map);
- }
-
- if (!material.lightmapTexture.filename.isEmpty()) {
- auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
- if (map) {
- _lightmapTransform = material.lightmapTexture.transform;
- _lightmapParams = material.lightmapParams;
- map->setTextureTransform(_lightmapTransform);
- map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
- }
- setTextureMap(MapChannel::LIGHTMAP_MAP, map);
- }
-}
-
-void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
- _isOriginal = false;
-
- const auto& albedoName = getTextureName(MapChannel::ALBEDO_MAP);
- const auto& normalName = getTextureName(MapChannel::NORMAL_MAP);
- const auto& roughnessName = getTextureName(MapChannel::ROUGHNESS_MAP);
- const auto& metallicName = getTextureName(MapChannel::METALLIC_MAP);
- const auto& occlusionName = getTextureName(MapChannel::OCCLUSION_MAP);
- const auto& emissiveName = getTextureName(MapChannel::EMISSIVE_MAP);
- const auto& lightmapName = getTextureName(MapChannel::LIGHTMAP_MAP);
- const auto& scatteringName = getTextureName(MapChannel::SCATTERING_MAP);
-
- if (!albedoName.isEmpty()) {
- auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl();
- auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
- if (map) {
- map->setTextureTransform(_albedoTransform);
- // when reassigning the albedo texture we also check for the alpha channel used as opacity
- map->setUseAlphaChannel(true);
- }
- setTextureMap(MapChannel::ALBEDO_MAP, map);
- }
-
- if (!normalName.isEmpty()) {
- auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl();
- auto map = fetchTextureMap(url, image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
- setTextureMap(MapChannel::NORMAL_MAP, map);
- }
-
- if (!roughnessName.isEmpty()) {
- auto url = textureMap.contains(roughnessName) ? textureMap[roughnessName].toUrl() : QUrl();
- // FIXME: If passing a gloss map instead of a roughmap how do we know?
- auto map = fetchTextureMap(url, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
- setTextureMap(MapChannel::ROUGHNESS_MAP, map);
- }
-
- if (!metallicName.isEmpty()) {
- auto url = textureMap.contains(metallicName) ? textureMap[metallicName].toUrl() : QUrl();
- // FIXME: If passing a specular map instead of a metallic how do we know?
- auto map = fetchTextureMap(url, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
- setTextureMap(MapChannel::METALLIC_MAP, map);
- }
-
- if (!occlusionName.isEmpty()) {
- auto url = textureMap.contains(occlusionName) ? textureMap[occlusionName].toUrl() : QUrl();
- // FIXME: we need to handle the occlusion map transform here
- auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
- setTextureMap(MapChannel::OCCLUSION_MAP, map);
- }
-
- if (!emissiveName.isEmpty()) {
- auto url = textureMap.contains(emissiveName) ? textureMap[emissiveName].toUrl() : QUrl();
- auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
- setTextureMap(MapChannel::EMISSIVE_MAP, map);
- }
-
- if (!scatteringName.isEmpty()) {
- auto url = textureMap.contains(scatteringName) ? textureMap[scatteringName].toUrl() : QUrl();
- auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
- setTextureMap(MapChannel::SCATTERING_MAP, map);
- }
-
- if (!lightmapName.isEmpty()) {
- auto url = textureMap.contains(lightmapName) ? textureMap[lightmapName].toUrl() : QUrl();
- auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
- if (map) {
- map->setTextureTransform(_lightmapTransform);
- map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);
- }
- setTextureMap(MapChannel::LIGHTMAP_MAP, map);
- }
-}
-
-bool NetworkMaterial::isMissingTexture() {
- for (auto& networkTexture : _textures) {
- auto& texture = networkTexture.texture;
- if (!texture) {
- continue;
- }
- // Failed texture downloads need to be considered as 'loaded'
- // or the object will never fade in
- bool finished = texture->isFailed() || (texture->isLoaded() && texture->getGPUTexture() && texture->getGPUTexture()->isDefined());
- if (!finished) {
- return true;
- }
- }
- return false;
-}
-
-void NetworkMaterial::checkResetOpacityMap() {
- // If material textures are loaded, check the material translucency
- // FIXME: This should not be done here. The opacity map should already be reset in Material::setTextureMap.
- // However, currently that code can be called before the albedo map is defined, so resetOpacityMap will fail.
- // Geometry::areTexturesLoaded() is called repeatedly until it returns true, so we do the check here for now
- const auto& albedoTexture = _textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
- if (albedoTexture.texture) {
- resetOpacityMap();
- }
-}
-
#include "ModelCache.moc"
diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h
index 497cae86a3..4cd7048dca 100644
--- a/libraries/model-networking/src/model-networking/ModelCache.h
+++ b/libraries/model-networking/src/model-networking/ModelCache.h
@@ -15,17 +15,13 @@
#include
#include
-#include
#include
#include "FBXSerializer.h"
-#include "TextureCache.h"
+#include
+#include
#include "ModelLoader.h"
-// Alias instead of derive to avoid copying
-
-class NetworkTexture;
-class NetworkMaterial;
class MeshPart;
class GeometryMappingResource;
@@ -49,6 +45,7 @@ public:
bool isHFMModelLoaded() const { return (bool)_hfmModel; }
const HFMModel& getHFMModel() const { return *_hfmModel; }
+ const MaterialMapping& getMaterialMapping() const { return _materialMapping; }
const GeometryMeshes& getMeshes() const { return *_meshes; }
const std::shared_ptr getShapeMaterial(int shapeID) const;
@@ -64,6 +61,7 @@ protected:
// Shared across all geometries, constant throughout lifetime
std::shared_ptr _hfmModel;
+ MaterialMapping _materialMapping;
std::shared_ptr _meshes;
std::shared_ptr _meshParts;
@@ -166,59 +164,6 @@ private:
ModelLoader _modelLoader;
};
-class NetworkMaterial : public graphics::Material {
-public:
- using MapChannel = graphics::Material::MapChannel;
-
- NetworkMaterial() : _textures(MapChannel::NUM_MAP_CHANNELS) {}
- NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl);
- NetworkMaterial(const NetworkMaterial& material);
-
- void setAlbedoMap(const QUrl& url, bool useAlphaChannel);
- void setNormalMap(const QUrl& url, bool isBumpmap);
- void setRoughnessMap(const QUrl& url, bool isGloss);
- void setMetallicMap(const QUrl& url, bool isSpecular);
- void setOcclusionMap(const QUrl& url);
- void setEmissiveMap(const QUrl& url);
- void setScatteringMap(const QUrl& url);
- void setLightmapMap(const QUrl& url);
-
- bool isMissingTexture();
- void checkResetOpacityMap();
-
-protected:
- friend class Geometry;
-
- class Texture {
- public:
- QString name;
- NetworkTexturePointer texture;
- };
- using Textures = std::vector;
-
- Textures _textures;
-
- static const QString NO_TEXTURE;
- const QString& getTextureName(MapChannel channel);
-
- void setTextures(const QVariantMap& textureMap);
-
- const bool& isOriginal() const { return _isOriginal; }
-
-private:
- // Helpers for the ctors
- QUrl getTextureUrl(const QUrl& baseUrl, const HFMTexture& hfmTexture);
- graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture,
- image::TextureUsage::Type type, MapChannel channel);
- graphics::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel);
-
- Transform _albedoTransform;
- Transform _lightmapTransform;
- vec2 _lightmapParams;
-
- bool _isOriginal { true };
-};
-
class MeshPart {
public:
MeshPart(int mesh, int part, int material) : meshID { mesh }, partID { part }, materialID { material } {}
diff --git a/libraries/model-networking/src/model-networking/ModelNetworkingLogging.cpp b/libraries/model-networking/src/model-networking/ModelNetworkingLogging.cpp
index 0c44fa33eb..d76efec31a 100644
--- a/libraries/model-networking/src/model-networking/ModelNetworkingLogging.cpp
+++ b/libraries/model-networking/src/model-networking/ModelNetworkingLogging.cpp
@@ -8,4 +8,4 @@
#include "ModelNetworkingLogging.h"
-Q_LOGGING_CATEGORY(modelnetworking, "hifi.gpu-network")
+Q_LOGGING_CATEGORY(modelnetworking, "hifi.gpu-model-network")
diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h
index f3967d8ffc..2096213273 100644
--- a/libraries/networking/src/ResourceCache.h
+++ b/libraries/networking/src/ResourceCache.h
@@ -360,13 +360,13 @@ class Resource : public QObject {
Q_OBJECT
public:
-
+ Resource() : QObject(), _loaded(true) {}
Resource(const Resource& other);
Resource(const QUrl& url);
virtual ~Resource();
virtual QString getType() const { return "Resource"; }
-
+
/// Returns the key last used to identify this resource in the unused map.
int getLRUKey() const { return _lruKey; }
@@ -375,13 +375,13 @@ public:
/// Sets the load priority for one owner.
virtual void setLoadPriority(const QPointer& owner, float priority);
-
+
/// Sets a set of priorities at once.
virtual void setLoadPriorities(const QHash, float>& priorities);
-
+
/// Clears the load priority for one owner.
virtual void clearLoadPriority(const QPointer& owner);
-
+
/// Returns the highest load priority across all owners.
float getLoadPriority();
@@ -491,14 +491,14 @@ protected:
QWeakPointer _self;
QPointer _cache;
- qint64 _bytesReceived{ 0 };
- qint64 _bytesTotal{ 0 };
- qint64 _bytes{ 0 };
+ qint64 _bytesReceived { 0 };
+ qint64 _bytesTotal { 0 };
+ qint64 _bytes { 0 };
int _requestID;
- ResourceRequest* _request{ nullptr };
+ ResourceRequest* _request { nullptr };
- size_t _extraHash;
+ size_t _extraHash { std::numeric_limits::max() };
public slots:
void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal);
diff --git a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h
index 4a0a21e995..a98989655e 100644
--- a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h
+++ b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h
@@ -57,6 +57,7 @@ protected:
void internalPresent() override;
void hmdPresent() override { throw std::runtime_error("Unused"); }
bool isHmdMounted() const override;
+ bool alwaysPresent() const override { return true; }
static const char* NAME;
mutable gl::Context* _mainContext{ nullptr };
diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt
index 5249ed2950..d7ce40641d 100644
--- a/libraries/physics/CMakeLists.txt
+++ b/libraries/physics/CMakeLists.txt
@@ -7,6 +7,7 @@ include_hifi_library_headers(avatars)
include_hifi_library_headers(audio)
include_hifi_library_headers(octree)
include_hifi_library_headers(animation)
+include_hifi_library_headers(material-networking)
include_hifi_library_headers(model-networking)
include_hifi_library_headers(image)
include_hifi_library_headers(ktx)
diff --git a/libraries/procedural/CMakeLists.txt b/libraries/procedural/CMakeLists.txt
index f3c3be687a..d08a7aef02 100644
--- a/libraries/procedural/CMakeLists.txt
+++ b/libraries/procedural/CMakeLists.txt
@@ -1,3 +1,4 @@
set(TARGET_NAME procedural)
setup_hifi_library()
-link_hifi_libraries(shared gpu shaders networking graphics model-networking ktx image)
+
+link_hifi_libraries(shared gpu shaders networking graphics material-networking ktx image)
diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h
index 3e10678ba7..8477e69afc 100644
--- a/libraries/procedural/src/procedural/Procedural.h
+++ b/libraries/procedural/src/procedural/Procedural.h
@@ -19,8 +19,8 @@
#include
#include
#include
-#include
-#include
+#include
+#include
using UniformLambdas = std::list>;
const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
diff --git a/libraries/recording/src/recording/ClipCache.cpp b/libraries/recording/src/recording/ClipCache.cpp
index c08dd40ad8..bc20e4d8eb 100644
--- a/libraries/recording/src/recording/ClipCache.cpp
+++ b/libraries/recording/src/recording/ClipCache.cpp
@@ -54,5 +54,5 @@ QSharedPointer ClipCache::createResource(const QUrl& url) {
}
QSharedPointer ClipCache::createResourceCopy(const QSharedPointer& resource) {
- return QSharedPointer(new NetworkClipLoader(*resource.staticCast().data()), &Resource::deleter);
+ return QSharedPointer(new NetworkClipLoader(*resource.staticCast()), &Resource::deleter);
}
\ No newline at end of file
diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt
index 2b092bff2a..833d78bb74 100644
--- a/libraries/render-utils/CMakeLists.txt
+++ b/libraries/render-utils/CMakeLists.txt
@@ -3,7 +3,7 @@ set(TARGET_NAME render-utils)
# pull in the resources.qrc file
qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
setup_hifi_library(Gui Network Qml Quick Script)
-link_hifi_libraries(shared task ktx gpu shaders graphics graphics-scripting model-networking render animation fbx image procedural)
+link_hifi_libraries(shared task ktx gpu shaders graphics graphics-scripting material-networking model-networking render animation fbx image procedural)
include_hifi_library_headers(audio)
include_hifi_library_headers(networking)
include_hifi_library_headers(octree)
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 0206bd6963..b9b294d0e3 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -756,7 +756,16 @@ scriptable::ScriptableModelBase Model::getScriptableModel() {
int numParts = (int)mesh->getNumParts();
for (int partIndex = 0; partIndex < numParts; partIndex++) {
- result.appendMaterial(graphics::MaterialLayer(getGeometry()->getShapeMaterial(shapeID), 0), shapeID, _modelMeshMaterialNames[shapeID]);
+ auto& materialName = _modelMeshMaterialNames[shapeID];
+ result.appendMaterial(graphics::MaterialLayer(getGeometry()->getShapeMaterial(shapeID), 0), shapeID, materialName);
+
+ auto mappedMaterialIter = _materialMapping.find(shapeID);
+ if (mappedMaterialIter != _materialMapping.end()) {
+ auto mappedMaterials = mappedMaterialIter->second;
+ for (auto& mappedMaterial : mappedMaterials) {
+ result.appendMaterial(mappedMaterial, shapeID, materialName);
+ }
+ }
shapeID++;
}
}
@@ -956,6 +965,7 @@ bool Model::addToScene(const render::ScenePointer& scene,
}
if (somethingAdded) {
+ applyMaterialMapping();
_addedToScene = true;
updateRenderItems();
_needsFixupInScene = false;
@@ -973,6 +983,7 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti
_modelMeshRenderItems.clear();
_modelMeshMaterialNames.clear();
_modelMeshRenderItemShapes.clear();
+ _priorityMap.clear();
_blendshapeOffsets.clear();
_blendshapeOffsetsInitialized = false;
@@ -1519,17 +1530,65 @@ std::set Model::getMeshIDsFromMaterialID(QString parentMaterialNam
return toReturn;
}
+void Model::applyMaterialMapping() {
+ auto renderItemsKey = _renderItemKeyGlobalFlags;
+ PrimitiveMode primitiveMode = getPrimitiveMode();
+ bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
+
+ auto& materialMapping = getMaterialMapping();
+ for (auto& mapping : materialMapping) {
+ std::set shapeIDs = getMeshIDsFromMaterialID(QString(mapping.first.c_str()));
+ auto networkMaterialResource = mapping.second;
+ if (!networkMaterialResource || shapeIDs.size() == 0) {
+ continue;
+ }
+
+ auto materialLoaded = [this, networkMaterialResource, shapeIDs, renderItemsKey, primitiveMode, useDualQuaternionSkinning]() {
+ if (networkMaterialResource->isFailed() || networkMaterialResource->parsedMaterials.names.size() == 0) {
+ return;
+ }
+ render::Transaction transaction;
+ auto networkMaterial = networkMaterialResource->parsedMaterials.networkMaterials[networkMaterialResource->parsedMaterials.names[0]];
+ for (auto shapeID : shapeIDs) {
+ if (shapeID < _modelMeshRenderItemIDs.size()) {
+ auto itemID = _modelMeshRenderItemIDs[shapeID];
+ auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
+ bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
+ graphics::MaterialLayer material = graphics::MaterialLayer(networkMaterial, ++_priorityMap[shapeID]);
+ _materialMapping[shapeID].push_back(material);
+ transaction.updateItem(itemID, [material, renderItemsKey,
+ invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
+ data.addMaterial(material);
+ // if the material changed, we might need to update our item key or shape key
+ data.updateKey(renderItemsKey);
+ data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning);
+ });
+ }
+ }
+ AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
+ };
+
+ if (networkMaterialResource->isLoaded()) {
+ materialLoaded();
+ } else {
+ connect(networkMaterialResource.data(), &Resource::finished, materialLoaded);
+ }
+ }
+}
+
void Model::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {
std::set shapeIDs = getMeshIDsFromMaterialID(QString(parentMaterialName.c_str()));
+
+ auto renderItemsKey = _renderItemKeyGlobalFlags;
+ PrimitiveMode primitiveMode = getPrimitiveMode();
+ bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
+
render::Transaction transaction;
for (auto shapeID : shapeIDs) {
if (shapeID < _modelMeshRenderItemIDs.size()) {
auto itemID = _modelMeshRenderItemIDs[shapeID];
- auto renderItemsKey = _renderItemKeyGlobalFlags;
- PrimitiveMode primitiveMode = getPrimitiveMode();
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
- bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
transaction.updateItem(itemID, [material, renderItemsKey,
invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
data.addMaterial(material);
diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h
index aadfca78ba..d1d4a7d3d1 100644
--- a/libraries/render-utils/src/Model.h
+++ b/libraries/render-utils/src/Model.h
@@ -183,6 +183,7 @@ public:
/// Provided as a convenience, will crash if !isLoaded()
// And so that getHFMModel() isn't chained everywhere
const HFMModel& getHFMModel() const { assert(isLoaded()); return _renderGeometry->getHFMModel(); }
+ const MaterialMapping& getMaterialMapping() const { assert(isLoaded()); return _renderGeometry->getMaterialMapping(); }
bool isActive() const { return isLoaded(); }
@@ -373,6 +374,10 @@ signals:
protected:
+ std::unordered_map _priorityMap; // only used for materialMapping
+ std::unordered_map> _materialMapping; // generated during applyMaterialMapping
+ void applyMaterialMapping();
+
void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; }
const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp
index 64037d64b6..385e384efe 100644
--- a/libraries/render-utils/src/RenderCommonTask.cpp
+++ b/libraries/render-utils/src/RenderCommonTask.cpp
@@ -101,7 +101,7 @@ void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs&
}
}
-void CompositeHUD::run(const RenderContextPointer& renderContext) {
+void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& inputs) {
assert(renderContext->args);
assert(renderContext->args->_context);
@@ -119,6 +119,9 @@ void CompositeHUD::run(const RenderContextPointer& renderContext) {
renderContext->args->getViewFrustum().evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat, true);
+ if (inputs) {
+ batch.setFramebuffer(inputs);
+ }
if (renderContext->args->_hudOperator) {
renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE);
}
@@ -197,7 +200,73 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer
});
}
-void ExtractFrustums::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) {
+
+void ResolveFramebuffer::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
+ RenderArgs* args = renderContext->args;
+ auto srcFbo = inputs.get0();
+ auto destFbo = inputs.get1();
+
+ if (!destFbo) {
+ destFbo = args->_blitFramebuffer;
+ }
+ outputs = destFbo;
+
+ // Check valid src and dest
+ if (!srcFbo || !destFbo) {
+ return;
+ }
+
+ // Check valid size for sr and dest
+ auto frameSize(srcFbo->getSize());
+ if (destFbo->getSize() != frameSize) {
+ return;
+ }
+
+ gpu::Vec4i rectSrc;
+ rectSrc.z = frameSize.x;
+ rectSrc.w = frameSize.y;
+ gpu::doInBatch("Resolve", args->_context, [&](gpu::Batch& batch) {
+ batch.blit(srcFbo, rectSrc, destFbo, rectSrc);
+ });
+}
+
+void ResolveNewFramebuffer::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
+ RenderArgs* args = renderContext->args;
+ auto srcFbo = inputs;
+ outputs.reset();
+
+ // Check valid src
+ if (!srcFbo) {
+ return;
+ }
+
+ // Check valid size for sr and dest
+ auto frameSize(srcFbo->getSize());
+
+ // Resizing framebuffers instead of re-building them seems to cause issues with threaded rendering
+ if (_outputFramebuffer && _outputFramebuffer->getSize() != frameSize) {
+ _outputFramebuffer.reset();
+ }
+
+ if (!_outputFramebuffer) {
+ _outputFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("resolvedNew.out"));
+ auto colorFormat = gpu::Element::COLOR_SRGBA_32;
+ auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
+ auto colorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
+ _outputFramebuffer->setRenderBuffer(0, colorTexture);
+ }
+
+ gpu::Vec4i rectSrc;
+ rectSrc.z = frameSize.x;
+ rectSrc.w = frameSize.y;
+ gpu::doInBatch("ResolveNew", args->_context, [&](gpu::Batch& batch) { batch.blit(srcFbo, rectSrc, _outputFramebuffer, rectSrc); });
+
+ outputs = _outputFramebuffer;
+}
+
+
+
+ void ExtractFrustums::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) {
assert(renderContext->args);
assert(renderContext->args->_context);
diff --git a/libraries/render-utils/src/RenderCommonTask.h b/libraries/render-utils/src/RenderCommonTask.h
index 4f8f53257d..b43a10aa7b 100644
--- a/libraries/render-utils/src/RenderCommonTask.h
+++ b/libraries/render-utils/src/RenderCommonTask.h
@@ -77,10 +77,12 @@ protected:
class CompositeHUD {
public:
- using JobModel = render::Job::Model;
+ // IF specified the input Framebuffer is actively set by the batch of this job before calling the HUDOperator.
+ // If not, the current Framebuffer is left unchanged.
+ //using Inputs = gpu::FramebufferPointer;
+ using JobModel = render::Job::ModelI;
- CompositeHUD() {}
- void run(const render::RenderContextPointer& renderContext);
+ void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& inputs);
};
class Blit {
@@ -90,6 +92,28 @@ public:
void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer);
};
+
+class ResolveFramebuffer {
+public:
+ using Inputs = render::VaryingSet2;
+ using Outputs = gpu::FramebufferPointer;
+ using JobModel = render::Job::ModelIO;
+
+ void run(const render::RenderContextPointer& renderContext, const Inputs& source, Outputs& dest);
+};
+
+class ResolveNewFramebuffer {
+public:
+ using Inputs = gpu::FramebufferPointer;
+ using Outputs = gpu::FramebufferPointer;
+ using JobModel = render::Job::ModelIO;
+
+ void run(const render::RenderContextPointer& renderContext, const Inputs& source, Outputs& dest);
+private:
+ gpu::FramebufferPointer _outputFramebuffer;
+};
+
+
class ExtractFrustums {
public:
diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp
index 673a165105..089d267711 100644
--- a/libraries/render-utils/src/RenderDeferredTask.cpp
+++ b/libraries/render-utils/src/RenderDeferredTask.cpp
@@ -257,8 +257,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
// Upscale to finale resolution
const auto primaryFramebuffer = task.addJob("PrimaryBufferUpscale", scaledPrimaryFramebuffer);
- // Composite the HUD and HUD layered objects
- task.addJob("HUD");
+ // Composite the HUD and HUD overlays
+ task.addJob("HUD", primaryFramebuffer);
const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f));
const auto hudOpaquesInputs = DrawLayered3D::Inputs(hudOpaque, lightingModel, nullJitter).asVarying();
diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp
index b7078a00a7..df82d4b56d 100755
--- a/libraries/render-utils/src/RenderForwardTask.cpp
+++ b/libraries/render-utils/src/RenderForwardTask.cpp
@@ -126,14 +126,24 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
task.addJob("DrawZoneStack", debugZoneInputs);
}
+ // Just resolve the msaa
+ const auto resolveInputs =
+ ResolveFramebuffer::Inputs(framebuffer, static_cast(nullptr)).asVarying();
+ const auto resolvedFramebuffer = task.addJob("Resolve", resolveInputs);
+ //auto resolvedFramebuffer = task.addJob("Resolve", framebuffer);
+
+#if defined(Q_OS_ANDROID)
+#else
// Lighting Buffer ready for tone mapping
// Forward rendering on GLES doesn't support tonemapping to and from the same FBO, so we specify
// the output FBO as null, which causes the tonemapping to target the blit framebuffer
- const auto toneMappingInputs = ToneMappingDeferred::Inputs(framebuffer, static_cast(nullptr) ).asVarying();
+ const auto toneMappingInputs = ToneMappingDeferred::Inputs(resolvedFramebuffer, static_cast(nullptr)).asVarying();
task.addJob("ToneMapping", toneMappingInputs);
+#endif
- // Composite the HUD and HUD layered objects
- task.addJob("HUD");
+ // Layered Overlays
+ // Composite the HUD and HUD overlays
+ task.addJob("HUD", resolvedFramebuffer);
const auto hudOpaquesInputs = DrawLayered3D::Inputs(hudOpaque, lightingModel, nullJitter).asVarying();
const auto hudTransparentsInputs = DrawLayered3D::Inputs(hudTransparent, lightingModel, nullJitter).asVarying();
@@ -145,26 +155,32 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
// task.addJob("Blit", framebuffer);
}
+void PrepareFramebuffer::configure(const Config& config) {
+ _numSamples = config.getNumSamples();
+}
+
void PrepareFramebuffer::run(const RenderContextPointer& renderContext, gpu::FramebufferPointer& framebuffer) {
glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w);
// Resizing framebuffers instead of re-building them seems to cause issues with threaded rendering
- if (_framebuffer && _framebuffer->getSize() != frameSize) {
+ if (_framebuffer && (_framebuffer->getSize() != frameSize || _framebuffer->getNumSamples() != _numSamples)) {
_framebuffer.reset();
}
if (!_framebuffer) {
_framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("forward"));
+ int numSamples = _numSamples;
+
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
- auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
+ auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
auto colorTexture =
- gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
+ gpu::Texture::createRenderBufferMultisample(colorFormat, frameSize.x, frameSize.y, numSamples, defaultSampler);
_framebuffer->setRenderBuffer(0, colorTexture);
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
auto depthTexture =
- gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
+ gpu::Texture::createRenderBufferMultisample(depthFormat, frameSize.x, frameSize.y, numSamples, defaultSampler);
_framebuffer->setDepthStencilBuffer(depthTexture, depthFormat);
}
diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h
index e6a6008319..85b51ad5fa 100755
--- a/libraries/render-utils/src/RenderForwardTask.h
+++ b/libraries/render-utils/src/RenderForwardTask.h
@@ -27,16 +27,37 @@ public:
void build(JobModel& task, const render::Varying& input, render::Varying& output);
};
+
+class PrepareFramebufferConfig : public render::Job::Config {
+ Q_OBJECT
+ Q_PROPERTY(int numSamples WRITE setNumSamples READ getNumSamples NOTIFY dirty)
+public:
+ int getNumSamples() const { return numSamples; }
+ void setNumSamples(int num) {
+ numSamples = std::max(1, std::min(32, num));
+ emit dirty();
+ }
+
+signals:
+ void dirty();
+
+protected:
+ int numSamples{ 4 };
+};
+
class PrepareFramebuffer {
public:
using Inputs = gpu::FramebufferPointer;
- using JobModel = render::Job::ModelO;
+ using Config = PrepareFramebufferConfig;
+ using JobModel = render::Job::ModelO;
+ void configure(const Config& config);
void run(const render::RenderContextPointer& renderContext,
gpu::FramebufferPointer& framebuffer);
private:
gpu::FramebufferPointer _framebuffer;
+ int _numSamples;
};
class PrepareForward {
diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp
index 5f3763ac2a..6f6f2ab856 100644
--- a/libraries/render-utils/src/RenderPipelines.cpp
+++ b/libraries/render-utils/src/RenderPipelines.cpp
@@ -15,7 +15,7 @@
#include
#include
-#include
+#include
#include
#include
#include
diff --git a/libraries/render-utils/src/TextureCache.h b/libraries/render-utils/src/TextureCache.h
index d6c1e419b9..f010666e19 100644
--- a/libraries/render-utils/src/TextureCache.h
+++ b/libraries/render-utils/src/TextureCache.h
@@ -1,2 +1,2 @@
// Compatibility
-#include
+#include
diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt
index 346c6e50f6..82c408f386 100644
--- a/libraries/script-engine/CMakeLists.txt
+++ b/libraries/script-engine/CMakeLists.txt
@@ -17,6 +17,6 @@ if (NOT ANDROID)
endif ()
-link_hifi_libraries(shared networking octree shaders gpu procedural graphics model-networking ktx recording avatars fbx hfm entities controllers animation audio physics image midi)
+link_hifi_libraries(shared networking octree shaders gpu procedural graphics material-networking model-networking ktx recording avatars fbx hfm entities controllers animation audio physics image midi)
# ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit
include_hifi_library_headers(gl)
diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt
index 8e43397c19..dcb2e39e1b 100644
--- a/plugins/openvr/CMakeLists.txt
+++ b/plugins/openvr/CMakeLists.txt
@@ -11,7 +11,7 @@ if (WIN32 AND (NOT USE_GLES))
setup_hifi_plugin(Gui Qml Multimedia)
link_hifi_libraries(shared task gl qml networking controllers ui
plugins display-plugins ui-plugins input-plugins script-engine
- audio-client render-utils graphics shaders gpu render model-networking model-baker hfm fbx ktx image procedural ${PLATFORM_GL_BACKEND})
+ audio-client render-utils graphics shaders gpu render material-networking model-networking model-baker hfm fbx ktx image procedural ${PLATFORM_GL_BACKEND})
include_hifi_library_headers(octree)
target_openvr()
diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css
index 6a0e7c8343..926eeaeeb3 100644
--- a/scripts/system/html/css/edit-style.css
+++ b/scripts/system/html/css/edit-style.css
@@ -1210,6 +1210,26 @@ div#grid-section, body#entity-list-body {
width: 200px;
padding-top: 1px;
}
+#filter-type-options-buttons {
+ top: -22px;
+ width: 224px;
+ z-index: 2;
+ background-color: #afafaf;
+ padding-bottom: 6px;
+}
+#filter-type-options input[type=button] {
+ position: relative;
+ left: 16px;
+ z-index: 3;
+ height: 23px;
+ min-width: 60px;
+ font-size: 10px;
+ color: #000;
+ background: linear-gradient(#afafaf 20%, #808080 100%);
+}
+#filter-type-options input[type=button]:enabled:hover {
+ background: linear-gradient(#afafaf 20%, #575757 100%);
+}
#filter-search-and-icon {
position: relative;
diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html
index f525c84cbc..986e5c09b0 100644
--- a/scripts/system/html/entityList.html
+++ b/scripts/system/html/entityList.html
@@ -44,6 +44,10 @@
+
+
+
+
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js
index b19873a049..8482591771 100644
--- a/scripts/system/html/js/entityList.js
+++ b/scripts/system/html/js/entityList.js
@@ -188,6 +188,7 @@ let renameTimeout = null;
let renameLastBlur = null;
let renameLastEntityID = null;
let isRenameFieldBeingMoved = false;
+let elFilterTypeInputs = {};
let elEntityTable,
elEntityTableHeader,
@@ -201,6 +202,9 @@ let elEntityTable,
elFilterTypeMultiselectBox,
elFilterTypeText,
elFilterTypeOptions,
+ elFilterTypeOptionsButtons,
+ elFilterTypeSelectAll,
+ elFilterTypeClearAll,
elFilterSearch,
elFilterInView,
elFilterRadius,
@@ -243,6 +247,9 @@ function loaded() {
elFilterTypeMultiselectBox = document.getElementById("filter-type-multiselect-box");
elFilterTypeText = document.getElementById("filter-type-text");
elFilterTypeOptions = document.getElementById("filter-type-options");
+ elFilterTypeOptionsButtons = document.getElementById("filter-type-options-buttons");
+ elFilterTypeSelectAll = document.getElementById('filter-type-select-all');
+ elFilterTypeClearAll = document.getElementById('filter-type-clear-all');
elFilterSearch = document.getElementById("filter-search");
elFilterInView = document.getElementById("filter-in-view");
elFilterRadius = document.getElementById("filter-radius");
@@ -276,6 +283,8 @@ function loaded() {
};
elRefresh.onclick = refreshEntities;
elFilterTypeMultiselectBox.onclick = onToggleTypeDropdown;
+ elFilterTypeSelectAll.onclick = onSelectAllTypes;
+ elFilterTypeClearAll.onclick = onClearAllTypes;
elFilterSearch.onkeyup = refreshEntityList;
elFilterSearch.onsearch = refreshEntityList;
elFilterInView.onclick = onToggleFilterInView;
@@ -290,13 +299,14 @@ function loaded() {
let elDiv = document.createElement('div');
elDiv.onclick = onToggleTypeFilter;
- elFilterTypeOptions.appendChild(elDiv);
+ elFilterTypeOptions.insertBefore(elDiv, elFilterTypeOptionsButtons);
let elInput = document.createElement('input');
elInput.setAttribute("type", "checkbox");
elInput.setAttribute("id", typeFilterID);
elInput.setAttribute("filterType", type);
elInput.checked = true; // all types are checked initially
+ elFilterTypeInputs[type] = elInput;
elDiv.appendChild(elInput);
let elLabel = document.createElement('label');
@@ -1065,7 +1075,21 @@ function loaded() {
event.stopPropagation();
}
- function toggleTypeFilter(elInput, refresh) {
+ function refreshTypeFilter(refreshList) {
+ if (typeFilters.length === 0) {
+ elFilterTypeText.innerText = "No Types";
+ } else if (typeFilters.length === FILTER_TYPES.length) {
+ elFilterTypeText.innerText = "All Types";
+ } else {
+ elFilterTypeText.innerText = "Types...";
+ }
+
+ if (refreshList) {
+ refreshEntityList();
+ }
+ }
+
+ function toggleTypeFilter(elInput, refreshList) {
let type = elInput.getAttribute("filterType");
let typeChecked = elInput.checked;
@@ -1076,17 +1100,7 @@ function loaded() {
typeFilters.push(type);
}
- if (typeFilters.length === 0) {
- elFilterTypeText.innerText = "No Types";
- } else if (typeFilters.length === FILTER_TYPES.length) {
- elFilterTypeText.innerText = "All Types";
- } else {
- elFilterTypeText.innerText = "Types...";
- }
-
- if (refresh) {
- refreshEntityList();
- }
+ refreshTypeFilter(refreshList);
}
function onToggleTypeFilter(event) {
@@ -1097,6 +1111,24 @@ function loaded() {
event.stopPropagation();
}
+ function onSelectAllTypes(event) {
+ for (let type in elFilterTypeInputs) {
+ elFilterTypeInputs[type].checked = true;
+ }
+ typeFilters = FILTER_TYPES;
+ refreshTypeFilter(true);
+ event.stopPropagation();
+ }
+
+ function onClearAllTypes(event) {
+ for (let type in elFilterTypeInputs) {
+ elFilterTypeInputs[type].checked = false;
+ }
+ typeFilters = [];
+ refreshTypeFilter(true);
+ event.stopPropagation();
+ }
+
function onBodyClick(event) {
// if clicking anywhere outside of the multiselect dropdowns (since click event bubbled up to onBodyClick and
// propagation wasn't stopped in the toggle type/column callbacks) and the dropdown is open then close it
diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js
deleted file mode 100644
index 938477fb5b..0000000000
--- a/scripts/system/html/js/marketplacesInject.js
+++ /dev/null
@@ -1,749 +0,0 @@
-/* global $, window, MutationObserver */
-
-//
-// marketplacesInject.js
-//
-// Created by David Rowe on 12 Nov 2016.
-// Copyright 2016 High Fidelity, Inc.
-//
-// Injected into marketplace Web pages.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-(function () {
- // Event bridge messages.
- var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD";
- var CLARA_IO_STATUS = "CLARA.IO STATUS";
- var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD";
- var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD";
- var GOTO_DIRECTORY = "GOTO_DIRECTORY";
- var GOTO_MARKETPLACE = "GOTO_MARKETPLACE";
- var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS";
- var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS";
- var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS";
-
- var canWriteAssets = false;
- var xmlHttpRequest = null;
- var isPreparing = false; // Explicitly track download request status.
-
- var limitedCommerce = false;
- var commerceMode = false;
- var userIsLoggedIn = false;
- var walletNeedsSetup = false;
- var marketplaceBaseURL = "https://highfidelity.com";
- var messagesWaiting = false;
-
- function injectCommonCode(isDirectoryPage) {
- // Supporting styles from marketplaces.css.
- // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain.
- $("head").append(
- ''
- );
-
- // Supporting styles from edit-style.css.
- // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain.
- $("head").append(
- ''
- );
-
- // Footer.
- var isInitialHiFiPage = location.href === (marketplaceBaseURL + "/marketplace?");
- $("body").append(
- '