mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 02:52:57 +02:00
Merge remote-tracking branch 'hifi/master' into android_places_goto
This commit is contained in:
commit
c460f8bf1e
58 changed files with 853 additions and 184 deletions
|
@ -63,7 +63,7 @@ struct AssetMeta {
|
||||||
AssetMeta() {
|
AssetMeta() {
|
||||||
}
|
}
|
||||||
|
|
||||||
BakeVersion bakeVersion;
|
BakeVersion bakeVersion { INITIAL_BAKE_VERSION };
|
||||||
bool failedLastBake { false };
|
bool failedLastBake { false };
|
||||||
QString lastBakeErrors;
|
QString lastBakeErrors;
|
||||||
};
|
};
|
||||||
|
|
3
cmake/externals/quazip/CMakeLists.txt
vendored
3
cmake/externals/quazip/CMakeLists.txt
vendored
|
@ -41,6 +41,9 @@ if (APPLE)
|
||||||
elseif (WIN32)
|
elseif (WIN32)
|
||||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip5.lib CACHE FILEPATH "Location of QuaZip release library")
|
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip5.lib CACHE FILEPATH "Location of QuaZip release library")
|
||||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/quazip5d.lib CACHE FILEPATH "Location of QuaZip release library")
|
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/quazip5d.lib CACHE FILEPATH "Location of QuaZip release library")
|
||||||
|
elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
|
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library")
|
||||||
|
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.so CACHE FILEPATH "Location of QuaZip release library")
|
||||||
else ()
|
else ()
|
||||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library")
|
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library")
|
||||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library")
|
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library")
|
||||||
|
|
|
@ -21,6 +21,7 @@ Item {
|
||||||
signal newViewRequestedCallback(var request)
|
signal newViewRequestedCallback(var request)
|
||||||
signal loadingChangedCallback(var loadRequest)
|
signal loadingChangedCallback(var loadRequest)
|
||||||
|
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
property bool interactive: false
|
property bool interactive: false
|
||||||
|
@ -29,6 +30,10 @@ Item {
|
||||||
id: hifi
|
id: hifi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stop() {
|
||||||
|
webViewCore.stop();
|
||||||
|
}
|
||||||
|
|
||||||
function unfocus() {
|
function unfocus() {
|
||||||
webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
|
webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
|
||||||
console.log('unfocus completed: ', result);
|
console.log('unfocus completed: ', result);
|
||||||
|
|
|
@ -21,6 +21,10 @@ Item {
|
||||||
property bool passwordField: false
|
property bool passwordField: false
|
||||||
property alias flickable: webroot.interactive
|
property alias flickable: webroot.interactive
|
||||||
|
|
||||||
|
function stop() {
|
||||||
|
webroot.stop();
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface
|
// FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface
|
||||||
// or provide HMDinfo object to QML in RenderableWebEntityItem and do the following.
|
// or provide HMDinfo object to QML in RenderableWebEntityItem and do the following.
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -254,7 +254,7 @@ ModalWindow {
|
||||||
text: root.warning;
|
text: root.warning;
|
||||||
wrapMode: Text.WordWrap;
|
wrapMode: Text.WordWrap;
|
||||||
font.italic: true;
|
font.italic: true;
|
||||||
maximumLineCount: 2;
|
maximumLineCount: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
|
|
|
@ -254,7 +254,7 @@ ModalWindow {
|
||||||
text: root.warning;
|
text: root.warning;
|
||||||
wrapMode: Text.WordWrap;
|
wrapMode: Text.WordWrap;
|
||||||
font.italic: true;
|
font.italic: true;
|
||||||
maximumLineCount: 2;
|
maximumLineCount: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
|
|
|
@ -282,7 +282,7 @@ TabletModalWindow {
|
||||||
text: root.warning;
|
text: root.warning;
|
||||||
wrapMode: Text.WordWrap;
|
wrapMode: Text.WordWrap;
|
||||||
font.italic: true;
|
font.italic: true;
|
||||||
maximumLineCount: 2;
|
maximumLineCount: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
|
|
|
@ -124,6 +124,14 @@ Rectangle {
|
||||||
root.numUpdatesAvailable = result.data.updates.length;
|
root.numUpdatesAvailable = result.data.updates.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAppInstalled: {
|
||||||
|
root.installedApps = Commerce.getInstalledApps();
|
||||||
|
}
|
||||||
|
|
||||||
|
onAppUninstalled: {
|
||||||
|
root.installedApps = Commerce.getInstalledApps();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
|
@ -249,6 +257,145 @@ Rectangle {
|
||||||
Commerce.getWalletStatus();
|
Commerce.getWalletStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: installedAppsContainer;
|
||||||
|
z: 998;
|
||||||
|
visible: false;
|
||||||
|
anchors.top: titleBarContainer.bottom;
|
||||||
|
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
|
||||||
|
anchors.left: parent.left;
|
||||||
|
anchors.bottom: parent.bottom;
|
||||||
|
width: parent.width;
|
||||||
|
|
||||||
|
RalewayRegular {
|
||||||
|
id: installedAppsHeader;
|
||||||
|
anchors.top: parent.top;
|
||||||
|
anchors.topMargin: 10;
|
||||||
|
anchors.left: parent.left;
|
||||||
|
anchors.leftMargin: 12;
|
||||||
|
height: 80;
|
||||||
|
width: paintedWidth;
|
||||||
|
text: "All Installed Marketplace Apps";
|
||||||
|
color: hifi.colors.black;
|
||||||
|
size: 22;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: installedAppsList;
|
||||||
|
clip: true;
|
||||||
|
model: installedAppsModel;
|
||||||
|
snapMode: ListView.SnapToItem;
|
||||||
|
// Anchors
|
||||||
|
anchors.top: installedAppsHeader.bottom;
|
||||||
|
anchors.left: parent.left;
|
||||||
|
anchors.bottom: sideloadAppButton.top;
|
||||||
|
width: parent.width;
|
||||||
|
delegate: Item {
|
||||||
|
width: parent.width;
|
||||||
|
height: 40;
|
||||||
|
|
||||||
|
RalewayRegular {
|
||||||
|
text: model.appUrl;
|
||||||
|
// Text size
|
||||||
|
size: 16;
|
||||||
|
// Anchors
|
||||||
|
anchors.left: parent.left;
|
||||||
|
anchors.leftMargin: 12;
|
||||||
|
height: parent.height;
|
||||||
|
anchors.right: sideloadAppOpenButton.left;
|
||||||
|
anchors.rightMargin: 8;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
// Style
|
||||||
|
color: hifi.colors.black;
|
||||||
|
// Alignment
|
||||||
|
verticalAlignment: Text.AlignVCenter;
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent;
|
||||||
|
onClicked: {
|
||||||
|
Window.copyToClipboard((model.appUrl).slice(0, -9));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControlsUit.Button {
|
||||||
|
id: sideloadAppOpenButton;
|
||||||
|
text: "OPEN";
|
||||||
|
color: hifi.buttons.blue;
|
||||||
|
colorScheme: hifi.colorSchemes.dark;
|
||||||
|
anchors.top: parent.top;
|
||||||
|
anchors.topMargin: 2;
|
||||||
|
anchors.bottom: parent.bottom;
|
||||||
|
anchors.bottomMargin: 2;
|
||||||
|
anchors.right: uninstallGlyph.left;
|
||||||
|
anchors.rightMargin: 8;
|
||||||
|
width: 80;
|
||||||
|
onClicked: {
|
||||||
|
Commerce.openApp(model.appUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
id: uninstallGlyph;
|
||||||
|
text: hifi.glyphs.close;
|
||||||
|
color: hifi.colors.black;
|
||||||
|
size: 22;
|
||||||
|
anchors.top: parent.top;
|
||||||
|
anchors.right: parent.right;
|
||||||
|
anchors.rightMargin: 6;
|
||||||
|
width: 35;
|
||||||
|
height: parent.height;
|
||||||
|
horizontalAlignment: Text.AlignHCenter;
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent;
|
||||||
|
hoverEnabled: true;
|
||||||
|
onEntered: {
|
||||||
|
parent.text = hifi.glyphs.closeInverted;
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
parent.text = hifi.glyphs.close;
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
Commerce.uninstallApp(model.appUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HifiControlsUit.Button {
|
||||||
|
id: sideloadAppButton;
|
||||||
|
color: hifi.buttons.blue;
|
||||||
|
colorScheme: hifi.colorSchemes.dark;
|
||||||
|
anchors.bottom: parent.bottom;
|
||||||
|
anchors.bottomMargin: 8;
|
||||||
|
anchors.left: parent.left;
|
||||||
|
anchors.leftMargin: 8;
|
||||||
|
anchors.right: closeAppListButton.left;
|
||||||
|
anchors.rightMargin: 8;
|
||||||
|
height: 40;
|
||||||
|
text: "SIDELOAD APP FROM LOCAL DISK";
|
||||||
|
onClicked: {
|
||||||
|
Window.browseChanged.connect(onFileOpenChanged);
|
||||||
|
Window.browseAsync("Locate your app's .app.json file", "", "*.app.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HifiControlsUit.Button {
|
||||||
|
id: closeAppListButton;
|
||||||
|
color: hifi.buttons.white;
|
||||||
|
colorScheme: hifi.colorSchemes.dark;
|
||||||
|
anchors.bottom: parent.bottom;
|
||||||
|
anchors.bottomMargin: 8;
|
||||||
|
anchors.right: parent.right;
|
||||||
|
anchors.rightMargin: 8;
|
||||||
|
width: 100;
|
||||||
|
height: 40;
|
||||||
|
text: "BACK";
|
||||||
|
onClicked: {
|
||||||
|
installedAppsContainer.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HifiWallet.NeedsLogIn {
|
HifiWallet.NeedsLogIn {
|
||||||
id: needsLogIn;
|
id: needsLogIn;
|
||||||
|
@ -317,7 +464,7 @@ Rectangle {
|
||||||
//
|
//
|
||||||
Item {
|
Item {
|
||||||
id: purchasesContentsContainer;
|
id: purchasesContentsContainer;
|
||||||
visible: root.activeView === "purchasesMain";
|
visible: root.activeView === "purchasesMain" && !installedAppsList.visible;
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.left: parent.left;
|
anchors.left: parent.left;
|
||||||
anchors.right: parent.right;
|
anchors.right: parent.right;
|
||||||
|
@ -959,6 +1106,39 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: {
|
||||||
|
if ((event.key == Qt.Key_F) && (event.modifiers & Qt.ControlModifier)) {
|
||||||
|
installedAppsContainer.visible = !installedAppsContainer.visible;
|
||||||
|
console.log("User changed visibility of installedAppsContainer to " + installedAppsContainer.visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onFileOpenChanged(filename) {
|
||||||
|
// disconnect the event, otherwise the requests will stack up
|
||||||
|
try { // Not all calls to onFileOpenChanged() connect an event.
|
||||||
|
Window.browseChanged.disconnect(onFileOpenChanged);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Purchases.qml ignoring', e);
|
||||||
|
}
|
||||||
|
if (filename) {
|
||||||
|
Commerce.installApp(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListModel {
|
||||||
|
id: installedAppsModel;
|
||||||
|
}
|
||||||
|
onInstalledAppsChanged: {
|
||||||
|
installedAppsModel.clear();
|
||||||
|
var installedAppsArray = root.installedApps.split(",");
|
||||||
|
var installedAppsObject = [];
|
||||||
|
// "- 1" because the last app string ends with ","
|
||||||
|
for (var i = 0; i < installedAppsArray.length - 1; i++) {
|
||||||
|
installedAppsObject[i] = {
|
||||||
|
"appUrl": installedAppsArray[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
installedAppsModel.append(installedAppsObject);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function Name: fromScript()
|
// Function Name: fromScript()
|
||||||
//
|
//
|
||||||
|
|
|
@ -1071,6 +1071,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
if (steamClient) {
|
if (steamClient) {
|
||||||
qCDebug(interfaceapp) << "[VERSION] SteamVR buildID:" << steamClient->getSteamVRBuildID();
|
qCDebug(interfaceapp) << "[VERSION] SteamVR buildID:" << steamClient->getSteamVRBuildID();
|
||||||
}
|
}
|
||||||
|
setCrashAnnotation("steam", property(hifi::properties::STEAM).toBool() ? "1" : "0");
|
||||||
|
|
||||||
qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion());
|
qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion());
|
||||||
qCDebug(interfaceapp) << "[VERSION] MODIFIED_ORGANIZATION:" << BuildInfo::MODIFIED_ORGANIZATION;
|
qCDebug(interfaceapp) << "[VERSION] MODIFIED_ORGANIZATION:" << BuildInfo::MODIFIED_ORGANIZATION;
|
||||||
qCDebug(interfaceapp) << "[VERSION] VERSION:" << BuildInfo::VERSION;
|
qCDebug(interfaceapp) << "[VERSION] VERSION:" << BuildInfo::VERSION;
|
||||||
|
@ -1156,6 +1158,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||||
|
|
||||||
connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainURLChanged(QUrl)));
|
connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainURLChanged(QUrl)));
|
||||||
|
connect(&domainHandler, &DomainHandler::domainURLChanged, [](QUrl domainURL){
|
||||||
|
setCrashAnnotation("domain", domainURL.toString().toStdString());
|
||||||
|
});
|
||||||
connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
|
connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
|
||||||
connect(&domainHandler, SIGNAL(connectedToDomain(QUrl)), SLOT(updateWindowTitle()));
|
connect(&domainHandler, SIGNAL(connectedToDomain(QUrl)), SLOT(updateWindowTitle()));
|
||||||
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
|
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
|
||||||
|
@ -1201,6 +1206,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||||
connect(accountManager.data(), &AccountManager::authRequired, dialogsManager.data(), &DialogsManager::showLoginDialog);
|
connect(accountManager.data(), &AccountManager::authRequired, dialogsManager.data(), &DialogsManager::showLoginDialog);
|
||||||
connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
|
connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
|
||||||
|
connect(accountManager.data(), &AccountManager::usernameChanged, [](QString username){
|
||||||
|
setCrashAnnotation("username", username.toStdString());
|
||||||
|
});
|
||||||
|
|
||||||
// set the account manager's root URL and trigger a login request if we don't have the access token
|
// set the account manager's root URL and trigger a login request if we don't have the access token
|
||||||
accountManager->setIsAgent(true);
|
accountManager->setIsAgent(true);
|
||||||
|
@ -1218,6 +1226,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateThreadPoolCount);
|
connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateThreadPoolCount);
|
||||||
connect(this, &Application::activeDisplayPluginChanged, this, [](){
|
connect(this, &Application::activeDisplayPluginChanged, this, [](){
|
||||||
qApp->setProperty(hifi::properties::HMD, qApp->isHMDMode());
|
qApp->setProperty(hifi::properties::HMD, qApp->isHMDMode());
|
||||||
|
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||||
|
setCrashAnnotation("display_plugin", displayPlugin->getName().toStdString());
|
||||||
|
setCrashAnnotation("hmd", displayPlugin->isHmd() ? "1" : "0");
|
||||||
});
|
});
|
||||||
connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateSystemTabletMode);
|
connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateSystemTabletMode);
|
||||||
|
|
||||||
|
@ -1225,6 +1236,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
connect(myAvatar.get(), &MyAvatar::positionGoneTo,
|
connect(myAvatar.get(), &MyAvatar::positionGoneTo,
|
||||||
DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
|
DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
|
||||||
|
|
||||||
|
connect(myAvatar.get(), &MyAvatar::skeletonModelURLChanged, [](){
|
||||||
|
QUrl avatarURL = qApp->getMyAvatar()->getSkeletonModelURL();
|
||||||
|
setCrashAnnotation("avatar", avatarURL.toString().toStdString());
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Inititalize sample before registering
|
// Inititalize sample before registering
|
||||||
_sampleSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/sample.wav"));
|
_sampleSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/sample.wav"));
|
||||||
|
|
||||||
|
@ -1242,6 +1259,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
|
|
||||||
connect(scriptEngines, &ScriptEngines::scriptsReloading, scriptEngines, [this] {
|
connect(scriptEngines, &ScriptEngines::scriptsReloading, scriptEngines, [this] {
|
||||||
getEntities()->reloadEntityScripts();
|
getEntities()->reloadEntityScripts();
|
||||||
|
loadAvatarScripts(getMyAvatar()->getScriptUrls());
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
connect(scriptEngines, &ScriptEngines::scriptLoadError,
|
connect(scriptEngines, &ScriptEngines::scriptLoadError,
|
||||||
|
@ -1389,6 +1407,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
userActivityLogger.disable(false);
|
userActivityLogger.disable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
||||||
|
|
||||||
if (userActivityLogger.isEnabled()) {
|
if (userActivityLogger.isEnabled()) {
|
||||||
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
|
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
|
||||||
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
|
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
|
||||||
|
@ -1438,11 +1458,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
properties["first_run"] = firstRun.get();
|
properties["first_run"] = firstRun.get();
|
||||||
|
|
||||||
// add the user's machine ID to the launch event
|
// add the user's machine ID to the launch event
|
||||||
properties["machine_fingerprint"] = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
properties["machine_fingerprint"] = machineFingerPrint;
|
||||||
|
|
||||||
userActivityLogger.logAction("launch", properties);
|
userActivityLogger.logAction("launch", properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCrashAnnotation("machine_fingerprint", machineFingerPrint.toStdString());
|
||||||
|
|
||||||
_entityEditSender.setMyAvatar(myAvatar.get());
|
_entityEditSender.setMyAvatar(myAvatar.get());
|
||||||
|
|
||||||
// The entity octree will have to know about MyAvatar for the parentJointName import
|
// The entity octree will have to know about MyAvatar for the parentJointName import
|
||||||
|
@ -4803,6 +4825,31 @@ void Application::init() {
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::loadAvatarScripts(const QVector<QString>& urls) {
|
||||||
|
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||||
|
auto runningScripts = scriptEngines->getRunningScripts();
|
||||||
|
for (auto url : urls) {
|
||||||
|
int index = runningScripts.indexOf(url);
|
||||||
|
if (index < 0) {
|
||||||
|
auto scriptEnginePointer = scriptEngines->loadScript(url, false);
|
||||||
|
if (scriptEnginePointer) {
|
||||||
|
scriptEnginePointer->setType(ScriptEngine::Type::AVATAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::unloadAvatarScripts() {
|
||||||
|
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||||
|
auto urls = scriptEngines->getRunningScripts();
|
||||||
|
for (auto url : urls) {
|
||||||
|
auto scriptEngine = scriptEngines->getScriptEngine(url);
|
||||||
|
if (scriptEngine->getType() == ScriptEngine::Type::AVATAR) {
|
||||||
|
scriptEngines->stopScript(url, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Application::updateLOD(float deltaTime) const {
|
void Application::updateLOD(float deltaTime) const {
|
||||||
PerformanceTimer perfTimer("LOD");
|
PerformanceTimer perfTimer("LOD");
|
||||||
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
|
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
|
||||||
|
|
|
@ -290,6 +290,9 @@ public:
|
||||||
|
|
||||||
void replaceDomainContent(const QString& url);
|
void replaceDomainContent(const QString& url);
|
||||||
|
|
||||||
|
void loadAvatarScripts(const QVector<QString>& urls);
|
||||||
|
void unloadAvatarScripts();
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
void enterBackground();
|
void enterBackground();
|
||||||
void enterForeground();
|
void enterForeground();
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
#if HAS_CRASHPAD
|
#if HAS_CRASHPAD
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
|
@ -23,8 +25,8 @@
|
||||||
#include <client/crashpad_client.h>
|
#include <client/crashpad_client.h>
|
||||||
#include <client/crash_report_database.h>
|
#include <client/crash_report_database.h>
|
||||||
#include <client/settings.h>
|
#include <client/settings.h>
|
||||||
// #include <client/annotation_list.h>
|
#include <client/annotation_list.h>
|
||||||
// #include <client/crashpad_info.h>
|
#include <client/crashpad_info.h>
|
||||||
|
|
||||||
using namespace crashpad;
|
using namespace crashpad;
|
||||||
|
|
||||||
|
@ -35,7 +37,8 @@ static std::wstring gIPCPipe;
|
||||||
|
|
||||||
extern QString qAppFileName();
|
extern QString qAppFileName();
|
||||||
|
|
||||||
// crashpad::AnnotationList* crashpadAnnotations { nullptr };
|
std::mutex annotationMutex;
|
||||||
|
crashpad::SimpleStringDictionary* crashpadAnnotations { nullptr };
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
|
@ -102,12 +105,14 @@ bool startCrashHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCrashAnnotation(std::string name, std::string value) {
|
void setCrashAnnotation(std::string name, std::string value) {
|
||||||
// if (!crashpadAnnotations) {
|
std::lock_guard<std::mutex> guard(annotationMutex);
|
||||||
// crashpadAnnotations = new crashpad::AnnotationList(); // don't free this, let it leak
|
if (!crashpadAnnotations) {
|
||||||
// crashpad::CrashpadInfo* crashpad_info = crashpad::GetCrashpadInfo();
|
crashpadAnnotations = new crashpad::SimpleStringDictionary(); // don't free this, let it leak
|
||||||
// crashpad_info->set_simple_annotations(crashpadAnnotations);
|
crashpad::CrashpadInfo* crashpad_info = crashpad::CrashpadInfo::GetCrashpadInfo();
|
||||||
// }
|
crashpad_info->set_simple_annotations(crashpadAnnotations);
|
||||||
// crashpadAnnotations->SetKeyValue(name, value);
|
}
|
||||||
|
std::replace(value.begin(), value.end(), ',', ';');
|
||||||
|
crashpadAnnotations->SetKeyValue(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <UserActivityLogger.h>
|
#include <UserActivityLogger.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
|
|
||||||
|
#include "Crashpad.h"
|
||||||
#include "DiscoverabilityManager.h"
|
#include "DiscoverabilityManager.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
|
||||||
|
@ -127,10 +128,12 @@ void DiscoverabilityManager::updateLocation() {
|
||||||
QNetworkAccessManager::PutOperation, callbackParameters);
|
QNetworkAccessManager::PutOperation, callbackParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update Steam
|
// Update Steam and crash logger
|
||||||
|
QUrl currentAddress = addressManager->currentFacingPublicAddress();
|
||||||
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
||||||
steamClient->updateLocation(domainHandler.getHostname(), addressManager->currentFacingPublicAddress());
|
steamClient->updateLocation(domainHandler.getHostname(), currentAddress);
|
||||||
}
|
}
|
||||||
|
setCrashAnnotation("address", currentAddress.toString().toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) {
|
void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) {
|
||||||
|
|
|
@ -9,11 +9,6 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* The LOD class manages your Level of Detail functions within interface
|
|
||||||
* @namespace LODManager
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef hifi_LODManager_h
|
#ifndef hifi_LODManager_h
|
||||||
#define hifi_LODManager_h
|
#define hifi_LODManager_h
|
||||||
|
|
||||||
|
@ -39,10 +34,32 @@ const float ADJUST_LOD_MIN_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE * 0.04f;
|
||||||
|
|
||||||
class AABox;
|
class AABox;
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* The LOD class manages your Level of Detail functions within Interface.
|
||||||
|
* @namespace LODManager
|
||||||
|
* @property {number} presentTime <em>Read-only.</em>
|
||||||
|
* @property {number} engineRunTime <em>Read-only.</em>
|
||||||
|
* @property {number} gpuTime <em>Read-only.</em>
|
||||||
|
* @property {number} avgRenderTime <em>Read-only.</em>
|
||||||
|
* @property {number} fps <em>Read-only.</em>
|
||||||
|
* @property {number} lodLevel <em>Read-only.</em>
|
||||||
|
* @property {number} lodDecreaseFPS <em>Read-only.</em>
|
||||||
|
* @property {number} lodIncreaseFPS <em>Read-only.</em>
|
||||||
|
*/
|
||||||
|
|
||||||
class LODManager : public QObject, public Dependency {
|
class LODManager : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
|
Q_PROPERTY(float presentTime READ getPresentTime)
|
||||||
|
Q_PROPERTY(float engineRunTime READ getEngineRunTime)
|
||||||
|
Q_PROPERTY(float gpuTime READ getGPUTime)
|
||||||
|
Q_PROPERTY(float avgRenderTime READ getAverageRenderTime)
|
||||||
|
Q_PROPERTY(float fps READ getMaxTheoreticalFPS)
|
||||||
|
Q_PROPERTY(float lodLevel READ getLODLevel)
|
||||||
|
Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS)
|
||||||
|
Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
@ -138,28 +155,6 @@ public:
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE float getLODIncreaseFPS() const;
|
Q_INVOKABLE float getLODIncreaseFPS() const;
|
||||||
|
|
||||||
/**jsdoc
|
|
||||||
* @namespace LODManager
|
|
||||||
* @property {number} presentTime <em>Read-only.</em>
|
|
||||||
* @property {number} engineRunTime <em>Read-only.</em>
|
|
||||||
* @property {number} gpuTime <em>Read-only.</em>
|
|
||||||
* @property {number} avgRenderTime <em>Read-only.</em>
|
|
||||||
* @property {number} fps <em>Read-only.</em>
|
|
||||||
* @property {number} lodLevel <em>Read-only.</em>
|
|
||||||
* @property {number} lodDecreaseFPS <em>Read-only.</em>
|
|
||||||
* @property {number} lodIncreaseFPS <em>Read-only.</em>
|
|
||||||
*/
|
|
||||||
|
|
||||||
Q_PROPERTY(float presentTime READ getPresentTime)
|
|
||||||
Q_PROPERTY(float engineRunTime READ getEngineRunTime)
|
|
||||||
Q_PROPERTY(float gpuTime READ getGPUTime)
|
|
||||||
Q_PROPERTY(float avgRenderTime READ getAverageRenderTime)
|
|
||||||
Q_PROPERTY(float fps READ getMaxTheoreticalFPS)
|
|
||||||
Q_PROPERTY(float lodLevel READ getLODLevel)
|
|
||||||
|
|
||||||
Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS)
|
|
||||||
Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS)
|
|
||||||
|
|
||||||
float getPresentTime() const { return _presentTime; }
|
float getPresentTime() const { return _presentTime; }
|
||||||
float getEngineRunTime() const { return _engineRunTime; }
|
float getEngineRunTime() const { return _engineRunTime; }
|
||||||
float getGPUTime() const { return _gpuTime; }
|
float getGPUTime() const { return _gpuTime; }
|
||||||
|
|
|
@ -156,9 +156,11 @@ bool ModelPackager::zipModel() {
|
||||||
|
|
||||||
QByteArray nameField = _mapping.value(NAME_FIELD).toByteArray();
|
QByteArray nameField = _mapping.value(NAME_FIELD).toByteArray();
|
||||||
tempDir.mkpath(nameField + "/textures");
|
tempDir.mkpath(nameField + "/textures");
|
||||||
|
tempDir.mkpath(nameField + "/scripts");
|
||||||
QDir fbxDir(tempDir.path() + "/" + nameField);
|
QDir fbxDir(tempDir.path() + "/" + nameField);
|
||||||
QDir texDir(fbxDir.path() + "/textures");
|
QDir texDir(fbxDir.path() + "/textures");
|
||||||
|
QDir scriptDir(fbxDir.path() + "/scripts");
|
||||||
|
|
||||||
// Copy textures
|
// Copy textures
|
||||||
listTextures();
|
listTextures();
|
||||||
if (!_textures.empty()) {
|
if (!_textures.empty()) {
|
||||||
|
@ -166,6 +168,23 @@ bool ModelPackager::zipModel() {
|
||||||
_texDir = _modelFile.path() + "/" + texdirField;
|
_texDir = _modelFile.path() + "/" + texdirField;
|
||||||
copyTextures(_texDir, texDir);
|
copyTextures(_texDir, texDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy scripts
|
||||||
|
QByteArray scriptField = _mapping.value(SCRIPT_FIELD).toByteArray();
|
||||||
|
_mapping.remove(SCRIPT_FIELD);
|
||||||
|
if (scriptField.size() > 1) {
|
||||||
|
tempDir.mkpath(nameField + "/scripts");
|
||||||
|
_scriptDir = _modelFile.path() + "/" + scriptField;
|
||||||
|
QDir wdir = QDir(_scriptDir);
|
||||||
|
_mapping.remove(SCRIPT_FIELD);
|
||||||
|
wdir.setSorting(QDir::Name | QDir::Reversed);
|
||||||
|
auto list = wdir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries);
|
||||||
|
for (auto script : list) {
|
||||||
|
auto sc = tempDir.relativeFilePath(scriptDir.path()) + "/" + QUrl(script).fileName();
|
||||||
|
_mapping.insertMulti(SCRIPT_FIELD, sc);
|
||||||
|
}
|
||||||
|
copyDirectoryContent(wdir, scriptDir);
|
||||||
|
}
|
||||||
|
|
||||||
// Copy LODs
|
// Copy LODs
|
||||||
QVariantHash lodField = _mapping.value(LOD_FIELD).toHash();
|
QVariantHash lodField = _mapping.value(LOD_FIELD).toHash();
|
||||||
|
@ -189,7 +208,10 @@ bool ModelPackager::zipModel() {
|
||||||
// Correct FST
|
// Correct FST
|
||||||
_mapping[FILENAME_FIELD] = tempDir.relativeFilePath(newPath);
|
_mapping[FILENAME_FIELD] = tempDir.relativeFilePath(newPath);
|
||||||
_mapping[TEXDIR_FIELD] = tempDir.relativeFilePath(texDir.path());
|
_mapping[TEXDIR_FIELD] = tempDir.relativeFilePath(texDir.path());
|
||||||
|
|
||||||
|
for (auto multi : _mapping.values(SCRIPT_FIELD)) {
|
||||||
|
multi.fromValue(tempDir.relativeFilePath(scriptDir.path()) + multi.toString());
|
||||||
|
}
|
||||||
// Copy FST
|
// Copy FST
|
||||||
QFile fst(tempDir.path() + "/" + nameField + ".fst");
|
QFile fst(tempDir.path() + "/" + nameField + ".fst");
|
||||||
if (fst.open(QIODevice::WriteOnly)) {
|
if (fst.open(QIODevice::WriteOnly)) {
|
||||||
|
@ -237,7 +259,9 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename
|
||||||
if (!mapping.contains(TEXDIR_FIELD)) {
|
if (!mapping.contains(TEXDIR_FIELD)) {
|
||||||
mapping.insert(TEXDIR_FIELD, ".");
|
mapping.insert(TEXDIR_FIELD, ".");
|
||||||
}
|
}
|
||||||
|
if (!mapping.contains(SCRIPT_FIELD)) {
|
||||||
|
mapping.insert(SCRIPT_FIELD, ".");
|
||||||
|
}
|
||||||
// mixamo/autodesk defaults
|
// mixamo/autodesk defaults
|
||||||
if (!mapping.contains(SCALE_FIELD)) {
|
if (!mapping.contains(SCALE_FIELD)) {
|
||||||
mapping.insert(SCALE_FIELD, 1.0);
|
mapping.insert(SCALE_FIELD, 1.0);
|
||||||
|
|
|
@ -37,10 +37,12 @@ private:
|
||||||
QFileInfo _fbxInfo;
|
QFileInfo _fbxInfo;
|
||||||
FSTReader::ModelType _modelType;
|
FSTReader::ModelType _modelType;
|
||||||
QString _texDir;
|
QString _texDir;
|
||||||
|
QString _scriptDir;
|
||||||
|
|
||||||
QVariantHash _mapping;
|
QVariantHash _mapping;
|
||||||
std::unique_ptr<FBXGeometry> _geometry;
|
std::unique_ptr<FBXGeometry> _geometry;
|
||||||
QStringList _textures;
|
QStringList _textures;
|
||||||
|
QStringList _scripts;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,9 @@ _geometry(geometry)
|
||||||
form->addRow("Texture Directory:", _textureDirectory = new QPushButton());
|
form->addRow("Texture Directory:", _textureDirectory = new QPushButton());
|
||||||
connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory()));
|
connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory()));
|
||||||
|
|
||||||
|
form->addRow("Script Directory:", _scriptDirectory = new QPushButton());
|
||||||
|
connect(_scriptDirectory, SIGNAL(clicked(bool)), SLOT(chooseScriptDirectory()));
|
||||||
|
|
||||||
form->addRow("Scale:", _scale = new QDoubleSpinBox());
|
form->addRow("Scale:", _scale = new QDoubleSpinBox());
|
||||||
_scale->setMaximum(FLT_MAX);
|
_scale->setMaximum(FLT_MAX);
|
||||||
_scale->setSingleStep(0.01);
|
_scale->setSingleStep(0.01);
|
||||||
|
@ -100,6 +103,7 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
|
||||||
mapping.insert(TYPE_FIELD, getType());
|
mapping.insert(TYPE_FIELD, getType());
|
||||||
mapping.insert(NAME_FIELD, _name->text());
|
mapping.insert(NAME_FIELD, _name->text());
|
||||||
mapping.insert(TEXDIR_FIELD, _textureDirectory->text());
|
mapping.insert(TEXDIR_FIELD, _textureDirectory->text());
|
||||||
|
mapping.insert(SCRIPT_FIELD, _scriptDirectory->text());
|
||||||
mapping.insert(SCALE_FIELD, QString::number(_scale->value()));
|
mapping.insert(SCALE_FIELD, QString::number(_scale->value()));
|
||||||
|
|
||||||
// update the joint indices
|
// update the joint indices
|
||||||
|
@ -157,6 +161,7 @@ void ModelPropertiesDialog::reset() {
|
||||||
_name->setText(_originalMapping.value(NAME_FIELD).toString());
|
_name->setText(_originalMapping.value(NAME_FIELD).toString());
|
||||||
_textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString());
|
_textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString());
|
||||||
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
|
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
|
||||||
|
_scriptDirectory->setText(_originalMapping.value(SCRIPT_FIELD).toString());
|
||||||
|
|
||||||
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
|
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
|
||||||
|
|
||||||
|
@ -207,6 +212,20 @@ void ModelPropertiesDialog::chooseTextureDirectory() {
|
||||||
_textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
|
_textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelPropertiesDialog::chooseScriptDirectory() {
|
||||||
|
QString directory = QFileDialog::getExistingDirectory(this, "Choose Script Directory",
|
||||||
|
_basePath + "/" + _scriptDirectory->text());
|
||||||
|
if (directory.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!directory.startsWith(_basePath)) {
|
||||||
|
OffscreenUi::asyncWarning(NULL, "Invalid script directory", "Script directory must be child of base path.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_scriptDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ModelPropertiesDialog::updatePivotJoint() {
|
void ModelPropertiesDialog::updatePivotJoint() {
|
||||||
_pivotJoint->setEnabled(!_pivotAboutCenter->isChecked());
|
_pivotJoint->setEnabled(!_pivotAboutCenter->isChecked());
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ public:
|
||||||
private slots:
|
private slots:
|
||||||
void reset();
|
void reset();
|
||||||
void chooseTextureDirectory();
|
void chooseTextureDirectory();
|
||||||
|
void chooseScriptDirectory();
|
||||||
void updatePivotJoint();
|
void updatePivotJoint();
|
||||||
void createNewFreeJoint(const QString& joint = QString());
|
void createNewFreeJoint(const QString& joint = QString());
|
||||||
|
|
||||||
|
@ -52,6 +53,7 @@ private:
|
||||||
FBXGeometry _geometry;
|
FBXGeometry _geometry;
|
||||||
QLineEdit* _name = nullptr;
|
QLineEdit* _name = nullptr;
|
||||||
QPushButton* _textureDirectory = nullptr;
|
QPushButton* _textureDirectory = nullptr;
|
||||||
|
QPushButton* _scriptDirectory = nullptr;
|
||||||
QDoubleSpinBox* _scale = nullptr;
|
QDoubleSpinBox* _scale = nullptr;
|
||||||
QDoubleSpinBox* _translationX = nullptr;
|
QDoubleSpinBox* _translationX = nullptr;
|
||||||
QDoubleSpinBox* _translationY = nullptr;
|
QDoubleSpinBox* _translationY = nullptr;
|
||||||
|
|
|
@ -121,6 +121,19 @@ MyAvatar::MyAvatar(QThread* thread) :
|
||||||
|
|
||||||
_skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr);
|
_skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr);
|
||||||
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
|
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
|
||||||
|
connect(_skeletonModel.get(), &Model::setURLFinished, this, [this](bool success) {
|
||||||
|
if (success) {
|
||||||
|
qApp->unloadAvatarScripts();
|
||||||
|
_shouldLoadScripts = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(_skeletonModel.get(), &Model::rigReady, this, [this]() {
|
||||||
|
if (_shouldLoadScripts) {
|
||||||
|
auto geometry = getSkeletonModel()->getFBXGeometry();
|
||||||
|
qApp->loadAvatarScripts(geometry.scripts);
|
||||||
|
_shouldLoadScripts = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady);
|
connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady);
|
||||||
connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset);
|
connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset);
|
||||||
|
|
||||||
|
@ -2839,6 +2852,11 @@ void MyAvatar::setWalkSpeed(float value) {
|
||||||
_walkSpeed.set(value);
|
_walkSpeed.set(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<QString> MyAvatar::getScriptUrls() {
|
||||||
|
QVector<QString> scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector<QString>();
|
||||||
|
return scripts;
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec3 MyAvatar::getPositionForAudio() {
|
glm::vec3 MyAvatar::getPositionForAudio() {
|
||||||
glm::vec3 result;
|
glm::vec3 result;
|
||||||
switch (_audioListenerMode) {
|
switch (_audioListenerMode) {
|
||||||
|
|
|
@ -985,6 +985,8 @@ public:
|
||||||
void setWalkSpeed(float value);
|
void setWalkSpeed(float value);
|
||||||
float getWalkSpeed() const;
|
float getWalkSpeed() const;
|
||||||
|
|
||||||
|
QVector<QString> getScriptUrls();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
@ -1322,7 +1324,6 @@ signals:
|
||||||
private slots:
|
private slots:
|
||||||
void leaveDomain();
|
void leaveDomain();
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void beParentOfChild(SpatiallyNestablePointer newChild) const override;
|
virtual void beParentOfChild(SpatiallyNestablePointer newChild) const override;
|
||||||
virtual void forgetChild(SpatiallyNestablePointer newChild) const override;
|
virtual void forgetChild(SpatiallyNestablePointer newChild) const override;
|
||||||
|
@ -1564,6 +1565,9 @@ private:
|
||||||
// max unscaled forward movement speed
|
// max unscaled forward movement speed
|
||||||
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
|
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
|
||||||
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
|
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
|
||||||
|
|
||||||
|
// load avatar scripts once when rig is ready
|
||||||
|
bool _shouldLoadScripts { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||||
|
|
|
@ -615,9 +615,15 @@ void Wallet::updateImageProvider() {
|
||||||
securityImageProvider->setSecurityImage(_securityImage);
|
securityImageProvider->setSecurityImage(_securityImage);
|
||||||
|
|
||||||
// inform tablet security image provider
|
// inform tablet security image provider
|
||||||
QQmlEngine* tabletEngine = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system")->getTabletSurface()->getSurfaceContext()->engine();
|
TabletProxy* tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
|
||||||
securityImageProvider = reinterpret_cast<SecurityImageProvider*>(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
|
if (tablet) {
|
||||||
securityImageProvider->setSecurityImage(_securityImage);
|
OffscreenQmlSurface* tabletSurface = tablet->getTabletSurface();
|
||||||
|
if (tabletSurface) {
|
||||||
|
QQmlEngine* tabletEngine = tabletSurface->getSurfaceContext()->engine();
|
||||||
|
securityImageProvider = reinterpret_cast<SecurityImageProvider*>(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
|
||||||
|
securityImageProvider->setSecurityImage(_securityImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wallet::chooseSecurityImage(const QString& filename) {
|
void Wallet::chooseSecurityImage(const QString& filename) {
|
||||||
|
|
|
@ -28,7 +28,7 @@ class ScriptEngine;
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* The Controller API provides facilities to interact with computer and controller hardware.
|
* The Controller API provides facilities to interact with computer and controller hardware.
|
||||||
*
|
*
|
||||||
* <h5>Functions:</h5>
|
* <h5>Functions</h5>
|
||||||
*
|
*
|
||||||
* <p>Properties</p>
|
* <p>Properties</p>
|
||||||
* <ul>
|
* <ul>
|
||||||
|
@ -143,6 +143,61 @@ class ScriptEngine;
|
||||||
* <li>{@link Controller.stopInputPlayback|stopInputPlayback}</li>
|
* <li>{@link Controller.stopInputPlayback|stopInputPlayback}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
|
* <h5>Entity Methods:</h5>
|
||||||
|
*
|
||||||
|
* <p>The default scripts implement hand controller actions that use {@link Entities.callEntityMethod} to call entity script
|
||||||
|
* methods, if present in the entity being interacted with.</p>
|
||||||
|
*
|
||||||
|
* <table>
|
||||||
|
* <thead>
|
||||||
|
* <tr><th>Method Name</th><th>Description</th><th>Example</th></tr>
|
||||||
|
* </thead>
|
||||||
|
* <tbody>
|
||||||
|
* <tr>
|
||||||
|
* <td><code>startFarTrigger</code><br /><code>continueFarTrigger</code><br /><code>stopFarTrigger</code></td>
|
||||||
|
* <td>These methods are called when a user is more than 0.3m away from the entity, the entity is triggerable, and the
|
||||||
|
* user starts, continues, or stops squeezing the trigger.</td>
|
||||||
|
* </td>
|
||||||
|
* <td>A light switch that can be toggled on and off from a distance.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td><code>startNearTrigger</code><br /><code>continueNearTrigger</code><br /><code>stopNearTrigger</code></td>
|
||||||
|
* <td>These methods are called when a user is less than 0.3m away from the entity, the entity is triggerable, and the
|
||||||
|
* user starts, continues, or stops squeezing the trigger.</td>
|
||||||
|
* <td>A doorbell that can be rung when a user is near.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td><code>startDistanceGrab</code><br /><code>continueDistanceGrab</code><br /></td>
|
||||||
|
* <td>These methods are called when a user is more than 0.3m away from the entity, the entity is either cloneable, or
|
||||||
|
* grabbable and not locked, and the user starts or continues to squeeze the trigger.</td>
|
||||||
|
* <td>A comet that emits icy particle trails when a user is dragging it through the sky.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td><code>startNearGrab</code><br /><code>continueNearGrab</code><br /></td>
|
||||||
|
* <td>These methods are called when a user is less than 0.3m away from the entity, the entity is either cloneable, or
|
||||||
|
* grabbable and not locked, and the user starts or continues to squeeze the trigger.</td>
|
||||||
|
* <td>A ball that glows when it's being held close.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td><code>releaseGrab</code></td>
|
||||||
|
* <td>This method is called when a user releases the trigger when having been either distance or near grabbing an
|
||||||
|
* entity.</td>
|
||||||
|
* <td>Turn off the ball glow or comet trail with the user finishes grabbing it.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td><code>startEquip</code><br /><code>continueEquip</code><br /><code>releaseEquip</code></td>
|
||||||
|
* <td>These methods are called when a user starts, continues, or stops equipping an entity.</td>
|
||||||
|
* <td>A glass that stays in the user's hand after the trigger is clicked.</td>
|
||||||
|
* </tr>
|
||||||
|
* </tbody>
|
||||||
|
* </table>
|
||||||
|
* <p>All the entity methods are called with the following two arguments:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>The entity ID.</li>
|
||||||
|
* <li>A string, <code>"hand,userID"</code> — where "hand" is <code>"left"</code> or <code>"right"</code>, and "userID"
|
||||||
|
* is the user's {@link MyAvatar|MyAvatar.sessionUUID}.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
* @namespace Controller
|
* @namespace Controller
|
||||||
*
|
*
|
||||||
* @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end
|
* @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end
|
||||||
|
|
|
@ -58,9 +58,8 @@ void AddressBarDialog::loadHome() {
|
||||||
qDebug() << "Called LoadHome";
|
qDebug() << "Called LoadHome";
|
||||||
auto locationBookmarks = DependencyManager::get<LocationBookmarks>();
|
auto locationBookmarks = DependencyManager::get<LocationBookmarks>();
|
||||||
QString homeLocation = locationBookmarks->addressForBookmark(LocationBookmarks::HOME_BOOKMARK);
|
QString homeLocation = locationBookmarks->addressForBookmark(LocationBookmarks::HOME_BOOKMARK);
|
||||||
const QString DEFAULT_HOME_LOCATION = "localhost";
|
|
||||||
if (homeLocation == "") {
|
if (homeLocation == "") {
|
||||||
homeLocation = DEFAULT_HOME_LOCATION;
|
homeLocation = DEFAULT_HIFI_ADDRESS;
|
||||||
}
|
}
|
||||||
DependencyManager::get<AddressManager>()->handleLookupString(homeLocation);
|
DependencyManager::get<AddressManager>()->handleLookupString(homeLocation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -588,6 +588,7 @@ void AnimExpression::evalUnaryMinus(const AnimVariantMap& map, std::stack<OpCode
|
||||||
PUSH(false);
|
PUSH(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Int:
|
case OpCode::Int:
|
||||||
PUSH(-rhs.intVal);
|
PUSH(-rhs.intVal);
|
||||||
|
|
|
@ -150,6 +150,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) {
|
||||||
|
|
||||||
// fall through to OnTime case
|
// fall through to OnTime case
|
||||||
}
|
}
|
||||||
|
// FALLTHRU
|
||||||
case SequenceNumberStats::OnTime: {
|
case SequenceNumberStats::OnTime: {
|
||||||
// Packet is on time; parse its data to the ringbuffer
|
// Packet is on time; parse its data to the ringbuffer
|
||||||
if (message.getType() == PacketType::SilentAudioFrame
|
if (message.getType() == PacketType::SilentAudioFrame
|
||||||
|
|
|
@ -39,7 +39,9 @@ class UserInputMapper;
|
||||||
* methods.</li>
|
* methods.</li>
|
||||||
* <li>Use {@link Controller.parseMapping} or {@link Controller.loadMapping} to load a {@link Controller.MappingJSON}.</li>
|
* <li>Use {@link Controller.parseMapping} or {@link Controller.loadMapping} to load a {@link Controller.MappingJSON}.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>Enable the mapping using {@link MappingObject#enable|enable} or {@link Controller.enableMapping} for it to take effect.
|
*
|
||||||
|
* <p>Enable the mapping using {@link MappingObject#enable|enable} or {@link Controller.enableMapping} for it to take
|
||||||
|
* effect.</p>
|
||||||
*
|
*
|
||||||
* <p>Mappings and their routes are applied according to the following rules:</p>
|
* <p>Mappings and their routes are applied according to the following rules:</p>
|
||||||
* <ul>
|
* <ul>
|
||||||
|
@ -49,7 +51,7 @@ class UserInputMapper;
|
||||||
* output that already has a route the new route is ignored.</li>
|
* output that already has a route the new route is ignored.</li>
|
||||||
* <li>New mappings override previous mappings: each output is processed using the route in the most recently enabled
|
* <li>New mappings override previous mappings: each output is processed using the route in the most recently enabled
|
||||||
* mapping that contains that output.</li>
|
* mapping that contains that output.</li>
|
||||||
* </p>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @class MappingObject
|
* @class MappingObject
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -29,7 +29,8 @@ class ScriptingInterface;
|
||||||
* <p>A route in a {@link MappingObject} used by the {@link Controller} API.</p>
|
* <p>A route in a {@link MappingObject} used by the {@link Controller} API.</p>
|
||||||
*
|
*
|
||||||
* <p>Create a route using {@link MappingObject} methods and apply this object's methods to process it, terminating with
|
* <p>Create a route using {@link MappingObject} methods and apply this object's methods to process it, terminating with
|
||||||
* {@link RouteObject#to} to apply it to a <code>Standard</code> control, action, or script function.</p>
|
* {@link RouteObject#to} to apply it to a <code>Standard</code> control, action, or script function. Note: Loops are not
|
||||||
|
* permitted.</p>
|
||||||
*
|
*
|
||||||
* <p>Some methods apply to routes with number data, some apply routes with {@link Pose} data, and some apply to both route
|
* <p>Some methods apply to routes with number data, some apply routes with {@link Pose} data, and some apply to both route
|
||||||
* types.<p>
|
* types.<p>
|
||||||
|
|
|
@ -308,12 +308,7 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
||||||
item->setProperty(URL_PROPERTY, _lastSourceUrl);
|
item->setProperty(URL_PROPERTY, _lastSourceUrl);
|
||||||
});
|
});
|
||||||
} else if (_contentType == ContentType::QmlContent) {
|
} else if (_contentType == ContentType::QmlContent) {
|
||||||
_webSurface->load(_lastSourceUrl, [this](QQmlContext* context, QObject* item) {
|
_webSurface->load(_lastSourceUrl);
|
||||||
if (item && item->objectName() == "tabletRoot") {
|
|
||||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
|
||||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
_fadeStartTime = usecTimestampNow();
|
_fadeStartTime = usecTimestampNow();
|
||||||
_webSurface->resume();
|
_webSurface->resume();
|
||||||
|
@ -323,32 +318,21 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
||||||
|
|
||||||
void WebEntityRenderer::destroyWebSurface() {
|
void WebEntityRenderer::destroyWebSurface() {
|
||||||
QSharedPointer<OffscreenQmlSurface> webSurface;
|
QSharedPointer<OffscreenQmlSurface> webSurface;
|
||||||
|
ContentType contentType{ ContentType::NoContent };
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
webSurface.swap(_webSurface);
|
webSurface.swap(_webSurface);
|
||||||
|
std::swap(contentType, _contentType);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (webSurface) {
|
if (webSurface) {
|
||||||
--_currentWebCount;
|
--_currentWebCount;
|
||||||
QQuickItem* rootItem = webSurface->getRootItem();
|
QQuickItem* rootItem = webSurface->getRootItem();
|
||||||
// Explicitly set the web URL to an empty string, in an effort to get a
|
|
||||||
// faster shutdown of any chromium processes interacting with audio
|
|
||||||
if (rootItem && _contentType == ContentType::HtmlContent) {
|
|
||||||
rootItem->setProperty(URL_PROPERTY, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rootItem && rootItem->objectName() == "tabletRoot") {
|
|
||||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
|
||||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||||
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
|
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
|
||||||
if (rootItem) {
|
if (rootItem && contentType == ContentType::HtmlContent) {
|
||||||
QObject* obj = rootItem->findChild<QObject*>("webEngineView");
|
// stop loading
|
||||||
if (obj) {
|
QMetaObject::invokeMethod(rootItem, "stop");
|
||||||
// stop loading
|
|
||||||
QMetaObject::invokeMethod(obj, "stop");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
webSurface->pause();
|
webSurface->pause();
|
||||||
|
|
|
@ -390,17 +390,18 @@ public slots:
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Find all entities of a particular name that intersect a sphere defined by a center point and radius.
|
* Find all entities of a particular name that intersect a sphere defined by a center point and radius.
|
||||||
* @function Entities.findEntitiesByName
|
* @function Entities.findEntitiesByName
|
||||||
* @param {Entities.EntityType} entityName - The name of the entity to search for.
|
* @param {string} entityName - The name of the entity to search for.
|
||||||
* @param {Vec3} center - The point about which to search.
|
* @param {Vec3} center - The point about which to search.
|
||||||
* @param {number} radius - The radius within which to search.
|
* @param {number} radius - The radius within which to search.
|
||||||
* @param {boolean} caseSensitiveSearch - Choose whether to to return case sensitive results back.
|
* @param {boolean} [caseSensitive=false] - If <code>true</code> then the search is case-sensitive.
|
||||||
* @returns {Uuid[]} An array of entity IDs of the specified type that intersect the search sphere. The array is empty if
|
* @returns {Uuid[]} An array of entity IDs that have the specified name and intersect the search sphere. The array is empty
|
||||||
* no entities could be found.
|
* if no entities could be found.
|
||||||
* @example <caption>Get back a list of entities</caption>
|
* @example <caption>Report the number of entities with the name, "Light-Target".</caption>
|
||||||
* var entityIDs = Entities.findEntitiesByName("Light-Target", MyAvatar.position, 10, false);
|
* var entityIDs = Entities.findEntitiesByName("Light-Target", MyAvatar.position, 10, false);
|
||||||
* print("Number of Entities with the name Light-Target " + entityIDs.length);
|
* print("Number of entities with the name "Light-Target": " + entityIDs.length);
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE QVector<QUuid> findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch = false ) const;
|
Q_INVOKABLE QVector<QUuid> findEntitiesByName(const QString entityName, const glm::vec3& center, float radius,
|
||||||
|
bool caseSensitiveSearch = false ) const;
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Find the first entity intersected by a {@link PickRay}. <code>Light</code> and <code>Zone</code> entities are not
|
* Find the first entity intersected by a {@link PickRay}. <code>Light</code> and <code>Zone</code> entities are not
|
||||||
|
|
|
@ -1402,6 +1402,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
|
|
||||||
case PacketType::EntityAdd:
|
case PacketType::EntityAdd:
|
||||||
isAdd = true; // fall through to next case
|
isAdd = true; // fall through to next case
|
||||||
|
// FALLTHRU
|
||||||
case PacketType::EntityPhysics:
|
case PacketType::EntityPhysics:
|
||||||
case PacketType::EntityEdit: {
|
case PacketType::EntityEdit: {
|
||||||
quint64 startDecode = 0, endDecode = 0;
|
quint64 startDecode = 0, endDecode = 0;
|
||||||
|
|
|
@ -298,6 +298,7 @@ public:
|
||||||
bool hasSkeletonJoints;
|
bool hasSkeletonJoints;
|
||||||
|
|
||||||
QVector<FBXMesh> meshes;
|
QVector<FBXMesh> meshes;
|
||||||
|
QVector<QString> scripts;
|
||||||
|
|
||||||
QHash<QString, FBXMaterial> materials;
|
QHash<QString, FBXMaterial> materials;
|
||||||
|
|
||||||
|
|
|
@ -249,7 +249,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
||||||
indexToDirect = true;
|
indexToDirect = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (indexToDirect && data.normalIndices.isEmpty()) {
|
if (indexToDirect && data.colorIndices.isEmpty()) {
|
||||||
// hack to work around wacky Makehuman exports
|
// hack to work around wacky Makehuman exports
|
||||||
data.colorsByVertex = true;
|
data.colorsByVertex = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,6 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) {
|
||||||
out << prop.toInt();
|
out << prop.toInt();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
encodeNode(out, FBXNode());
|
|
||||||
case QMetaType::Float:
|
case QMetaType::Float:
|
||||||
out.device()->write("F", 1);
|
out.device()->write("F", 1);
|
||||||
out << prop.toFloat();
|
out << prop.toFloat();
|
||||||
|
|
|
@ -84,7 +84,7 @@ void FSTReader::writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it)
|
||||||
|
|
||||||
QByteArray FSTReader::writeMapping(const QVariantHash& mapping) {
|
QByteArray FSTReader::writeMapping(const QVariantHash& mapping) {
|
||||||
static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << TYPE_FIELD << SCALE_FIELD << FILENAME_FIELD
|
static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << TYPE_FIELD << SCALE_FIELD << FILENAME_FIELD
|
||||||
<< TEXDIR_FIELD << JOINT_FIELD << FREE_JOINT_FIELD
|
<< TEXDIR_FIELD << SCRIPT_FIELD << JOINT_FIELD << FREE_JOINT_FIELD
|
||||||
<< BLENDSHAPE_FIELD << JOINT_INDEX_FIELD;
|
<< BLENDSHAPE_FIELD << JOINT_INDEX_FIELD;
|
||||||
QBuffer buffer;
|
QBuffer buffer;
|
||||||
buffer.open(QIODevice::WriteOnly);
|
buffer.open(QIODevice::WriteOnly);
|
||||||
|
@ -92,7 +92,7 @@ QByteArray FSTReader::writeMapping(const QVariantHash& mapping) {
|
||||||
for (auto key : PREFERED_ORDER) {
|
for (auto key : PREFERED_ORDER) {
|
||||||
auto it = mapping.find(key);
|
auto it = mapping.find(key);
|
||||||
if (it != mapping.constEnd()) {
|
if (it != mapping.constEnd()) {
|
||||||
if (key == FREE_JOINT_FIELD) { // writeVariant does not handle strings added using insertMulti.
|
if (key == FREE_JOINT_FIELD || key == SCRIPT_FIELD) { // writeVariant does not handle strings added using insertMulti.
|
||||||
for (auto multi : mapping.values(key)) {
|
for (auto multi : mapping.values(key)) {
|
||||||
buffer.write(key.toUtf8());
|
buffer.write(key.toUtf8());
|
||||||
buffer.write(" = ");
|
buffer.write(" = ");
|
||||||
|
@ -187,6 +187,26 @@ FSTReader::ModelType FSTReader::predictModelType(const QVariantHash& mapping) {
|
||||||
return ENTITY_MODEL;
|
return ENTITY_MODEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<QString> FSTReader::getScripts(const QUrl& url, const QVariantHash& mapping) {
|
||||||
|
|
||||||
|
auto fstMapping = mapping.isEmpty() ? downloadMapping(url.toString()) : mapping;
|
||||||
|
QVector<QString> scriptPaths;
|
||||||
|
if (!fstMapping.value(SCRIPT_FIELD).isNull()) {
|
||||||
|
auto scripts = fstMapping.values(SCRIPT_FIELD).toVector();
|
||||||
|
for (auto &script : scripts) {
|
||||||
|
QString scriptPath = script.toString();
|
||||||
|
if (QUrl(scriptPath).isRelative()) {
|
||||||
|
if (scriptPath.at(0) == '/') {
|
||||||
|
scriptPath = scriptPath.right(scriptPath.length() - 1);
|
||||||
|
}
|
||||||
|
scriptPath = url.resolved(QUrl(scriptPath)).toString();
|
||||||
|
}
|
||||||
|
scriptPaths.push_back(scriptPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scriptPaths;
|
||||||
|
}
|
||||||
|
|
||||||
QVariantHash FSTReader::downloadMapping(const QString& url) {
|
QVariantHash FSTReader::downloadMapping(const QString& url) {
|
||||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
QNetworkRequest networkRequest = QNetworkRequest(url);
|
QNetworkRequest networkRequest = QNetworkRequest(url);
|
||||||
|
|
|
@ -28,6 +28,7 @@ static const QString TRANSLATION_Z_FIELD = "tz";
|
||||||
static const QString JOINT_FIELD = "joint";
|
static const QString JOINT_FIELD = "joint";
|
||||||
static const QString FREE_JOINT_FIELD = "freeJoint";
|
static const QString FREE_JOINT_FIELD = "freeJoint";
|
||||||
static const QString BLENDSHAPE_FIELD = "bs";
|
static const QString BLENDSHAPE_FIELD = "bs";
|
||||||
|
static const QString SCRIPT_FIELD = "script";
|
||||||
|
|
||||||
class FSTReader {
|
class FSTReader {
|
||||||
public:
|
public:
|
||||||
|
@ -49,6 +50,8 @@ public:
|
||||||
/// Predicts the type of model by examining the mapping
|
/// Predicts the type of model by examining the mapping
|
||||||
static ModelType predictModelType(const QVariantHash& mapping);
|
static ModelType predictModelType(const QVariantHash& mapping);
|
||||||
|
|
||||||
|
static QVector<QString> getScripts(const QUrl& fstUrl, const QVariantHash& mapping = QVariantHash());
|
||||||
|
|
||||||
static QString getNameFromType(ModelType modelType);
|
static QString getNameFromType(ModelType modelType);
|
||||||
static FSTReader::ModelType getTypeFromName(const QString& name);
|
static FSTReader::ModelType getTypeFromName(const QString& name);
|
||||||
static QVariantHash downloadMapping(const QString& url);
|
static QVariantHash downloadMapping(const QString& url);
|
||||||
|
|
|
@ -66,6 +66,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
|
||||||
auto mapping = FSTReader::readMapping(data);
|
auto mapping = FSTReader::readMapping(data);
|
||||||
|
|
||||||
QString filename = mapping.value("filename").toString();
|
QString filename = mapping.value("filename").toString();
|
||||||
|
|
||||||
if (filename.isNull()) {
|
if (filename.isNull()) {
|
||||||
qCDebug(modelnetworking) << "Mapping file" << _url << "has no \"filename\" field";
|
qCDebug(modelnetworking) << "Mapping file" << _url << "has no \"filename\" field";
|
||||||
finishedLoading(false);
|
finishedLoading(false);
|
||||||
|
@ -82,6 +83,14 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
|
||||||
_textureBaseUrl = url.resolved(QUrl("."));
|
_textureBaseUrl = url.resolved(QUrl("."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto scripts = FSTReader::getScripts(_url, mapping);
|
||||||
|
if (scripts.size() > 0) {
|
||||||
|
mapping.remove(SCRIPT_FIELD);
|
||||||
|
for (auto &scriptPath : scripts) {
|
||||||
|
mapping.insertMulti(SCRIPT_FIELD, scriptPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto animGraphVariant = mapping.value("animGraphUrl");
|
auto animGraphVariant = mapping.value("animGraphUrl");
|
||||||
if (animGraphVariant.isValid()) {
|
if (animGraphVariant.isValid()) {
|
||||||
QUrl fstUrl(animGraphVariant.toString());
|
QUrl fstUrl(animGraphVariant.toString());
|
||||||
|
@ -209,6 +218,14 @@ void GeometryReader::run() {
|
||||||
throw QString("unsupported format");
|
throw QString("unsupported format");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add scripts to fbxgeometry
|
||||||
|
if (!_mapping.value(SCRIPT_FIELD).isNull()) {
|
||||||
|
QVariantList scripts = _mapping.values(SCRIPT_FIELD);
|
||||||
|
for (auto &script : scripts) {
|
||||||
|
fbxGeometry->scripts.push_back(script.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the resource has not been deleted
|
// Ensure the resource has not been deleted
|
||||||
auto resource = _resource.toStrongRef();
|
auto resource = _resource.toStrongRef();
|
||||||
if (!resource) {
|
if (!resource) {
|
||||||
|
|
|
@ -331,12 +331,14 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const QString LOCALHOST = "localhost";
|
||||||
|
|
||||||
bool isPossiblePlaceName(QString possiblePlaceName) {
|
bool isPossiblePlaceName(QString possiblePlaceName) {
|
||||||
bool result { false };
|
bool result { false };
|
||||||
int length = possiblePlaceName.length();
|
int length = possiblePlaceName.length();
|
||||||
static const int MINIMUM_PLACENAME_LENGTH = 1;
|
static const int MINIMUM_PLACENAME_LENGTH = 1;
|
||||||
static const int MAXIMUM_PLACENAME_LENGTH = 64;
|
static const int MAXIMUM_PLACENAME_LENGTH = 64;
|
||||||
if (possiblePlaceName.toLower() != "localhost" &&
|
if (possiblePlaceName.toLower() != LOCALHOST &&
|
||||||
length >= MINIMUM_PLACENAME_LENGTH && length <= MAXIMUM_PLACENAME_LENGTH) {
|
length >= MINIMUM_PLACENAME_LENGTH && length <= MAXIMUM_PLACENAME_LENGTH) {
|
||||||
const QRegExp PLACE_NAME_REGEX = QRegExp("^[0-9A-Za-z](([0-9A-Za-z]|-(?!-))*[^\\W_]$|$)");
|
const QRegExp PLACE_NAME_REGEX = QRegExp("^[0-9A-Za-z](([0-9A-Za-z]|-(?!-))*[^\\W_]$|$)");
|
||||||
result = PLACE_NAME_REGEX.indexIn(possiblePlaceName) == 0;
|
result = PLACE_NAME_REGEX.indexIn(possiblePlaceName) == 0;
|
||||||
|
@ -356,7 +358,7 @@ void AddressManager::handleLookupString(const QString& lookupString, bool fromSu
|
||||||
sanitizedString = sanitizedString.remove(HIFI_SCHEME_REGEX);
|
sanitizedString = sanitizedString.remove(HIFI_SCHEME_REGEX);
|
||||||
|
|
||||||
lookupURL = QUrl(sanitizedString);
|
lookupURL = QUrl(sanitizedString);
|
||||||
if (lookupURL.scheme().isEmpty()) {
|
if (lookupURL.scheme().isEmpty() || lookupURL.scheme().toLower() == LOCALHOST) {
|
||||||
lookupURL = QUrl("hifi://" + sanitizedString);
|
lookupURL = QUrl("hifi://" + sanitizedString);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -605,7 +607,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTri
|
||||||
if (ipAddressRegex.indexIn(lookupString) != -1) {
|
if (ipAddressRegex.indexIn(lookupString) != -1) {
|
||||||
QString domainIPString = ipAddressRegex.cap(1);
|
QString domainIPString = ipAddressRegex.cap(1);
|
||||||
|
|
||||||
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
quint16 domainPort = 0;
|
||||||
if (!ipAddressRegex.cap(2).isEmpty()) {
|
if (!ipAddressRegex.cap(2).isEmpty()) {
|
||||||
domainPort = (quint16) ipAddressRegex.cap(2).toInt();
|
domainPort = (quint16) ipAddressRegex.cap(2).toInt();
|
||||||
}
|
}
|
||||||
|
@ -627,7 +629,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTri
|
||||||
if (hostnameRegex.indexIn(lookupString) != -1) {
|
if (hostnameRegex.indexIn(lookupString) != -1) {
|
||||||
QString domainHostname = hostnameRegex.cap(1);
|
QString domainHostname = hostnameRegex.cap(1);
|
||||||
|
|
||||||
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
quint16 domainPort = 0;
|
||||||
|
|
||||||
if (!hostnameRegex.cap(2).isEmpty()) {
|
if (!hostnameRegex.cap(2).isEmpty()) {
|
||||||
domainPort = (quint16)hostnameRegex.cap(2).toInt();
|
domainPort = (quint16)hostnameRegex.cap(2).toInt();
|
||||||
|
|
|
@ -166,7 +166,12 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_domainURL != domainURL || _sockAddr.getPort() != domainURL.port()) {
|
auto domainPort = domainURL.port();
|
||||||
|
if (domainPort == -1) {
|
||||||
|
domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_domainURL != domainURL || _sockAddr.getPort() != domainPort) {
|
||||||
// re-set the domain info so that auth information is reloaded
|
// re-set the domain info so that auth information is reloaded
|
||||||
hardReset();
|
hardReset();
|
||||||
|
|
||||||
|
@ -192,12 +197,10 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) {
|
||||||
|
|
||||||
emit domainURLChanged(_domainURL);
|
emit domainURLChanged(_domainURL);
|
||||||
|
|
||||||
if (_sockAddr.getPort() != domainURL.port()) {
|
if (_sockAddr.getPort() != domainPort) {
|
||||||
qCDebug(networking) << "Updated domain port to" << domainURL.port();
|
qCDebug(networking) << "Updated domain port to" << domainPort;
|
||||||
|
_sockAddr.setPort(domainPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab the port by reading the string after the colon
|
|
||||||
_sockAddr.setPort(domainURL.port());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -302,6 +302,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
NLPacket::LocalID sourceLocalID = Node::NULL_LOCAL_ID;
|
NLPacket::LocalID sourceLocalID = Node::NULL_LOCAL_ID;
|
||||||
|
|
||||||
// check if we were passed a sourceNode hint or if we need to look it up
|
// check if we were passed a sourceNode hint or if we need to look it up
|
||||||
if (!sourceNode) {
|
if (!sourceNode) {
|
||||||
// figure out which node this is from
|
// figure out which node this is from
|
||||||
|
@ -314,6 +315,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
|
||||||
QUuid sourceID = sourceNode ? sourceNode->getUUID() : QUuid();
|
QUuid sourceID = sourceNode ? sourceNode->getUUID() : QUuid();
|
||||||
|
|
||||||
if (!sourceNode &&
|
if (!sourceNode &&
|
||||||
|
!isDomainServer() &&
|
||||||
sourceLocalID == getDomainLocalID() &&
|
sourceLocalID == getDomainLocalID() &&
|
||||||
packet.getSenderSockAddr() == getDomainSockAddr() &&
|
packet.getSenderSockAddr() == getDomainSockAddr() &&
|
||||||
PacketTypeEnum::getDomainSourcedPackets().contains(headerType)) {
|
PacketTypeEnum::getDomainSourcedPackets().contains(headerType)) {
|
||||||
|
@ -608,6 +610,7 @@ bool LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID, ConnectionID newCo
|
||||||
|
|
||||||
{
|
{
|
||||||
QWriteLocker writeLocker(&_nodeMutex);
|
QWriteLocker writeLocker(&_nodeMutex);
|
||||||
|
_localIDMap.unsafe_erase(matchingNode->getLocalID());
|
||||||
_nodeHash.unsafe_erase(it);
|
_nodeHash.unsafe_erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -750,6 +750,7 @@ bool Resource::handleFailedRequest(ResourceRequest::Result result) {
|
||||||
qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal;
|
qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal;
|
||||||
// Fall through to other cases
|
// Fall through to other cases
|
||||||
}
|
}
|
||||||
|
// FALLTHRU
|
||||||
case ResourceRequest::Result::ServerUnavailable: {
|
case ResourceRequest::Result::ServerUnavailable: {
|
||||||
_attempts++;
|
_attempts++;
|
||||||
_attemptsRemaining--;
|
_attemptsRemaining--;
|
||||||
|
@ -768,6 +769,7 @@ bool Resource::handleFailedRequest(ResourceRequest::Result result) {
|
||||||
}
|
}
|
||||||
// fall through to final failure
|
// fall through to final failure
|
||||||
}
|
}
|
||||||
|
// FALLTHRU
|
||||||
default: {
|
default: {
|
||||||
_attemptsRemaining = 0;
|
_attemptsRemaining = 0;
|
||||||
qCDebug(networking) << "Error loading " << _url << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining;
|
qCDebug(networking) << "Error loading " << _url << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining;
|
||||||
|
|
|
@ -447,7 +447,12 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
||||||
// this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor
|
// this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor
|
||||||
assert(!(_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()));
|
assert(!(_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()));
|
||||||
|
|
||||||
if (_entity->dynamicDataNeedsTransmit() || _entity->queryAACubeNeedsUpdate()) {
|
// shouldSendUpdate() sould NOT be triggering updates to maintain the queryAACube of dynamic entities.
|
||||||
|
// The server is supposed to predict the transform of such moving things. The client performs a "double prediction"
|
||||||
|
// where it predicts what the the server is doing, and only sends updates whent the entity's true transform
|
||||||
|
// differs significantly. That is what the remoteSimulationOutOfSync() logic is all about.
|
||||||
|
if (_entity->dynamicDataNeedsTransmit() ||
|
||||||
|
(!_entity->getDynamic() && _entity->queryAACubeNeedsUpdate())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,6 +153,8 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
|
||||||
// remove the objects (aka MotionStates) from physics
|
// remove the objects (aka MotionStates) from physics
|
||||||
_physicsEngine->removeSetOfObjects(_physicalObjects);
|
_physicsEngine->removeSetOfObjects(_physicalObjects);
|
||||||
|
|
||||||
|
clearOwnershipData();
|
||||||
|
|
||||||
// delete the MotionStates
|
// delete the MotionStates
|
||||||
for (auto stateItr : _physicalObjects) {
|
for (auto stateItr : _physicalObjects) {
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(&(*stateItr));
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(&(*stateItr));
|
||||||
|
@ -165,7 +167,6 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
|
||||||
_physicalObjects.clear();
|
_physicalObjects.clear();
|
||||||
|
|
||||||
// clear all other lists specific to this derived class
|
// clear all other lists specific to this derived class
|
||||||
clearOwnershipData();
|
|
||||||
_entitiesToRemoveFromPhysics.clear();
|
_entitiesToRemoveFromPhysics.clear();
|
||||||
_entitiesToAddToPhysics.clear();
|
_entitiesToAddToPhysics.clear();
|
||||||
_incomingChanges.clear();
|
_incomingChanges.clear();
|
||||||
|
|
|
@ -90,19 +90,23 @@ SharedObject::~SharedObject() {
|
||||||
_renderControl = nullptr;
|
_renderControl = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_rootItem) {
|
||||||
|
delete _rootItem;
|
||||||
|
_rootItem = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (_quickWindow) {
|
if (_quickWindow) {
|
||||||
_quickWindow->destroy();
|
_quickWindow->destroy();
|
||||||
delete _quickWindow;
|
delete _quickWindow;
|
||||||
_quickWindow = nullptr;
|
_quickWindow = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// _rootItem is parented to the quickWindow, so needs no explicit destruction
|
if (_qmlContext) {
|
||||||
//if (_rootItem) {
|
auto engine = _qmlContext->engine();
|
||||||
// delete _rootItem;
|
delete _qmlContext;
|
||||||
// _rootItem = nullptr;
|
_qmlContext = nullptr;
|
||||||
//}
|
releaseEngine(engine);
|
||||||
|
}
|
||||||
releaseEngine(_qmlContext->engine());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedObject::create(OffscreenSurface* surface) {
|
void SharedObject::create(OffscreenSurface* surface) {
|
||||||
|
@ -210,9 +214,9 @@ QQmlEngine* SharedObject::acquireEngine(OffscreenSurface* surface) {
|
||||||
if (!globalEngine) {
|
if (!globalEngine) {
|
||||||
Q_ASSERT(0 == globalEngineRefCount);
|
Q_ASSERT(0 == globalEngineRefCount);
|
||||||
globalEngine = new QQmlEngine();
|
globalEngine = new QQmlEngine();
|
||||||
surface->initializeQmlEngine(result);
|
surface->initializeEngine(result);
|
||||||
++globalEngineRefCount;
|
|
||||||
}
|
}
|
||||||
|
++globalEngineRefCount;
|
||||||
result = globalEngine;
|
result = globalEngine;
|
||||||
#else
|
#else
|
||||||
result = new QQmlEngine();
|
result = new QQmlEngine();
|
||||||
|
|
|
@ -180,6 +180,21 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const
|
||||||
// don't delete `ScriptEngines` until all `ScriptEngine`s are gone
|
// don't delete `ScriptEngines` until all `ScriptEngine`s are gone
|
||||||
_scriptEngines(DependencyManager::get<ScriptEngines>())
|
_scriptEngines(DependencyManager::get<ScriptEngines>())
|
||||||
{
|
{
|
||||||
|
switch (_context) {
|
||||||
|
case Context::CLIENT_SCRIPT:
|
||||||
|
_type = Type::CLIENT;
|
||||||
|
break;
|
||||||
|
case Context::ENTITY_CLIENT_SCRIPT:
|
||||||
|
_type = Type::ENTITY_CLIENT;
|
||||||
|
break;
|
||||||
|
case Context::ENTITY_SERVER_SCRIPT:
|
||||||
|
_type = Type::ENTITY_SERVER;
|
||||||
|
break;
|
||||||
|
case Context::AGENT_SCRIPT:
|
||||||
|
_type = Type::AGENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {
|
connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {
|
||||||
if (hasUncaughtException()) {
|
if (hasUncaughtException()) {
|
||||||
// the engine's uncaughtException() seems to produce much better stack traces here
|
// the engine's uncaughtException() seems to produce much better stack traces here
|
||||||
|
@ -2161,6 +2176,32 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString&
|
||||||
}, forceRedownload);
|
}, forceRedownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Triggered when the script starts for a user.
|
||||||
|
* <p>Note: Can only be connected to via <code>this.preload = function (...) { ... }</code> in the entity script.</p>
|
||||||
|
* <table><tr><th>Available in:</th><td>Client Entity Scripts</td><td>Server Entity Scripts</td></tr></table>
|
||||||
|
* @function Entities.preload
|
||||||
|
* @param {Uuid} entityID - The ID of the entity that the script is running in.
|
||||||
|
* @returns {Signal}
|
||||||
|
* @example <caption>Get the ID of the entity that a client entity script is running in.</caption>
|
||||||
|
* var entityScript = (function () {
|
||||||
|
* this.entityID = Uuid.NULL;
|
||||||
|
*
|
||||||
|
* this.preload = function (entityID) {
|
||||||
|
* this.entityID = entityID;
|
||||||
|
* print("Entity ID: " + this.entityID);
|
||||||
|
* };
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* var entityID = Entities.addEntity({
|
||||||
|
* type: "Box",
|
||||||
|
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })),
|
||||||
|
* dimensions: { x: 0.5, y: 0.5, z: 0.5 },
|
||||||
|
* color: { red: 255, green: 0, blue: 0 },
|
||||||
|
* script: "(" + entityScript + ")", // Could host the script on a Web server instead.
|
||||||
|
* lifetime: 300 // Delete after 5 minutes.
|
||||||
|
* });
|
||||||
|
*/
|
||||||
// since all of these operations can be asynch we will always do the actual work in the response handler
|
// since all of these operations can be asynch we will always do the actual work in the response handler
|
||||||
// for the download
|
// for the download
|
||||||
void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success , const QString& status) {
|
void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success , const QString& status) {
|
||||||
|
@ -2345,6 +2386,13 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
||||||
processDeferredEntityLoads(entityScript, entityID);
|
processDeferredEntityLoads(entityScript, entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Triggered when the script terminates for a user.
|
||||||
|
* <p>Note: Can only be connected to via <code>this.unoad = function () { ... }</code> in the entity script.</p>
|
||||||
|
* <table><tr><th>Available in:</th><td>Client Entity Scripts</td><td>Server Entity Scripts</td></tr></table>
|
||||||
|
* @function Entities.unload
|
||||||
|
* @returns {Signal}
|
||||||
|
*/
|
||||||
void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) {
|
void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
#ifdef THREAD_DEBUGGING
|
#ifdef THREAD_DEBUGGING
|
||||||
|
|
|
@ -103,6 +103,14 @@ public:
|
||||||
AGENT_SCRIPT
|
AGENT_SCRIPT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Type {
|
||||||
|
CLIENT,
|
||||||
|
ENTITY_CLIENT,
|
||||||
|
ENTITY_SERVER,
|
||||||
|
AGENT,
|
||||||
|
AVATAR
|
||||||
|
};
|
||||||
|
|
||||||
static int processLevelMaxRetries;
|
static int processLevelMaxRetries;
|
||||||
ScriptEngine(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine"));
|
ScriptEngine(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine"));
|
||||||
~ScriptEngine();
|
~ScriptEngine();
|
||||||
|
@ -493,6 +501,9 @@ public:
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE QUuid generateUUID() { return QUuid::createUuid(); }
|
Q_INVOKABLE QUuid generateUUID() { return QUuid::createUuid(); }
|
||||||
|
|
||||||
|
void setType(Type type) { _type = type; };
|
||||||
|
Type getType() { return _type; };
|
||||||
|
|
||||||
bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget
|
bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget
|
||||||
bool isRunning() const { return _isRunning; } // used by ScriptWidget
|
bool isRunning() const { return _isRunning; } // used by ScriptWidget
|
||||||
|
|
||||||
|
@ -724,6 +735,7 @@ protected:
|
||||||
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
|
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
|
||||||
|
|
||||||
Context _context;
|
Context _context;
|
||||||
|
Type _type;
|
||||||
QString _scriptContents;
|
QString _scriptContents;
|
||||||
QString _parentURL;
|
QString _parentURL;
|
||||||
std::atomic<bool> _isFinished { false };
|
std::atomic<bool> _isFinished { false };
|
||||||
|
|
|
@ -427,11 +427,13 @@ bool ScriptEngines::stopScript(const QString& rawScriptURL, bool restart) {
|
||||||
if (_scriptEnginesHash.contains(scriptURL)) {
|
if (_scriptEnginesHash.contains(scriptURL)) {
|
||||||
ScriptEnginePointer scriptEngine = _scriptEnginesHash[scriptURL];
|
ScriptEnginePointer scriptEngine = _scriptEnginesHash[scriptURL];
|
||||||
if (restart) {
|
if (restart) {
|
||||||
|
bool isUserLoaded = scriptEngine->isUserLoaded();
|
||||||
|
ScriptEngine::Type type = scriptEngine->getType();
|
||||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||||
scriptCache->deleteScript(scriptURL);
|
scriptCache->deleteScript(scriptURL);
|
||||||
connect(scriptEngine.data(), &ScriptEngine::finished,
|
connect(scriptEngine.data(), &ScriptEngine::finished,
|
||||||
this, [this](QString scriptName, ScriptEnginePointer engine) {
|
this, [this, isUserLoaded, type](QString scriptName, ScriptEnginePointer engine) {
|
||||||
reloadScript(scriptName);
|
reloadScript(scriptName, isUserLoaded)->setType(type);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
scriptEngine->stop();
|
scriptEngine->stop();
|
||||||
|
|
|
@ -259,7 +259,7 @@ protected slots:
|
||||||
protected:
|
protected:
|
||||||
friend class ScriptEngine;
|
friend class ScriptEngine;
|
||||||
|
|
||||||
void reloadScript(const QString& scriptName) { loadScript(scriptName, true, false, false, true); }
|
ScriptEnginePointer reloadScript(const QString& scriptName, bool isUserLoaded = true) { return loadScript(scriptName, isUserLoaded, false, false, true); }
|
||||||
void removeScriptEngine(ScriptEnginePointer);
|
void removeScriptEngine(ScriptEnginePointer);
|
||||||
void onScriptEngineLoaded(const QString& scriptFilename);
|
void onScriptEngineLoaded(const QString& scriptFilename);
|
||||||
void onScriptEngineError(const QString& scriptFilename);
|
void onScriptEngineError(const QString& scriptFilename);
|
||||||
|
|
|
@ -60,6 +60,7 @@ bool gunzip(QByteArray source, QByteArray &destination) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case Z_NEED_DICT:
|
case Z_NEED_DICT:
|
||||||
status = Z_DATA_ERROR;
|
status = Z_DATA_ERROR;
|
||||||
|
// FALLTHRU
|
||||||
case Z_DATA_ERROR:
|
case Z_DATA_ERROR:
|
||||||
case Z_MEM_ERROR:
|
case Z_MEM_ERROR:
|
||||||
case Z_STREAM_ERROR:
|
case Z_STREAM_ERROR:
|
||||||
|
|
|
@ -102,6 +102,9 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void traceEvent(const QLoggingCategory& category, const QString& name, EventType type, const QString& id = "", const QVariantMap& args = {}, const QVariantMap& extra = {}) {
|
inline void traceEvent(const QLoggingCategory& category, const QString& name, EventType type, const QString& id = "", const QVariantMap& args = {}, const QVariantMap& extra = {}) {
|
||||||
|
if (!DependencyManager::isSet<Tracer>()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto& tracer = DependencyManager::get<Tracer>();
|
const auto& tracer = DependencyManager::get<Tracer>();
|
||||||
if (tracer) {
|
if (tracer) {
|
||||||
tracer->traceEvent(category, name, type, id, args, extra);
|
tracer->traceEvent(category, name, type, id, args, extra);
|
||||||
|
|
|
@ -115,6 +115,7 @@ private:
|
||||||
class UrlHandler : public QObject {
|
class UrlHandler : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
UrlHandler(QObject* parent = nullptr) : QObject(parent) {}
|
||||||
Q_INVOKABLE bool canHandleUrl(const QString& url) {
|
Q_INVOKABLE bool canHandleUrl(const QString& url) {
|
||||||
static auto handler = dynamic_cast<AbstractUriHandler*>(qApp);
|
static auto handler = dynamic_cast<AbstractUriHandler*>(qApp);
|
||||||
return handler && handler->canAcceptURL(url);
|
return handler && handler->canAcceptURL(url);
|
||||||
|
@ -223,6 +224,17 @@ void AudioHandler::run() {
|
||||||
qDebug() << "QML Audio changed to " << _newTargetDevice;
|
qDebug() << "QML Audio changed to " << _newTargetDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OffscreenQmlSurface::~OffscreenQmlSurface() {
|
||||||
|
clearFocusItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OffscreenQmlSurface::clearFocusItem() {
|
||||||
|
if (_currentFocusItem) {
|
||||||
|
disconnect(_currentFocusItem, &QObject::destroyed, this, &OffscreenQmlSurface::focusDestroyed);
|
||||||
|
}
|
||||||
|
_currentFocusItem = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
|
void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
|
||||||
Parent::initializeEngine(engine);
|
Parent::initializeEngine(engine);
|
||||||
new QQmlFileSelector(engine);
|
new QQmlFileSelector(engine);
|
||||||
|
@ -246,7 +258,7 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
|
||||||
|
|
||||||
auto rootContext = engine->rootContext();
|
auto rootContext = engine->rootContext();
|
||||||
rootContext->setContextProperty("GL", ::getGLContextData());
|
rootContext->setContextProperty("GL", ::getGLContextData());
|
||||||
rootContext->setContextProperty("urlHandler", new UrlHandler());
|
rootContext->setContextProperty("urlHandler", new UrlHandler(rootContext));
|
||||||
rootContext->setContextProperty("resourceDirectoryUrl", QUrl::fromLocalFile(PathUtils::resourcesPath()));
|
rootContext->setContextProperty("resourceDirectoryUrl", QUrl::fromLocalFile(PathUtils::resourcesPath()));
|
||||||
rootContext->setContextProperty("ApplicationInterface", qApp);
|
rootContext->setContextProperty("ApplicationInterface", qApp);
|
||||||
auto javaScriptToInject = getEventBridgeJavascript();
|
auto javaScriptToInject = getEventBridgeJavascript();
|
||||||
|
@ -545,17 +557,15 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::focusDestroyed(QObject* obj) {
|
void OffscreenQmlSurface::focusDestroyed(QObject* obj) {
|
||||||
if (_currentFocusItem) {
|
clearFocusItem();
|
||||||
disconnect(_currentFocusItem, &QObject::destroyed, this, &OffscreenQmlSurface::focusDestroyed);
|
|
||||||
}
|
|
||||||
_currentFocusItem = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::onFocusObjectChanged(QObject* object) {
|
void OffscreenQmlSurface::onFocusObjectChanged(QObject* object) {
|
||||||
|
clearFocusItem();
|
||||||
|
|
||||||
QQuickItem* item = static_cast<QQuickItem*>(object);
|
QQuickItem* item = static_cast<QQuickItem*>(object);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
setFocusText(false);
|
setFocusText(false);
|
||||||
_currentFocusItem = nullptr;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,10 +573,6 @@ void OffscreenQmlSurface::onFocusObjectChanged(QObject* object) {
|
||||||
qApp->sendEvent(object, &query);
|
qApp->sendEvent(object, &query);
|
||||||
setFocusText(query.value(Qt::ImEnabled).toBool());
|
setFocusText(query.value(Qt::ImEnabled).toBool());
|
||||||
|
|
||||||
if (_currentFocusItem) {
|
|
||||||
disconnect(_currentFocusItem, &QObject::destroyed, this, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Raise and lower keyboard for QML text fields.
|
// Raise and lower keyboard for QML text fields.
|
||||||
// HTML text fields are handled in emitWebEvent() methods - testing READ_ONLY_PROPERTY prevents action for HTML files.
|
// HTML text fields are handled in emitWebEvent() methods - testing READ_ONLY_PROPERTY prevents action for HTML files.
|
||||||
const char* READ_ONLY_PROPERTY = "readOnly";
|
const char* READ_ONLY_PROPERTY = "readOnly";
|
||||||
|
|
|
@ -22,7 +22,8 @@ class OffscreenQmlSurface : public hifi::qml::OffscreenSurface {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged)
|
Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged)
|
||||||
public:
|
public:
|
||||||
|
~OffscreenQmlSurface();
|
||||||
|
|
||||||
static void addWhitelistContextHandler(const std::initializer_list<QUrl>& urls, const QmlContextCallback& callback);
|
static void addWhitelistContextHandler(const std::initializer_list<QUrl>& urls, const QmlContextCallback& callback);
|
||||||
static void addWhitelistContextHandler(const QUrl& url, const QmlContextCallback& callback) { addWhitelistContextHandler({ { url } }, callback); };
|
static void addWhitelistContextHandler(const QUrl& url, const QmlContextCallback& callback) { addWhitelistContextHandler({ { url } }, callback); };
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ public slots:
|
||||||
void sendToQml(const QVariant& message);
|
void sendToQml(const QVariant& message);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void clearFocusItem();
|
||||||
void setFocusText(bool newFocusText);
|
void setFocusText(bool newFocusText);
|
||||||
void initializeEngine(QQmlEngine* engine) override;
|
void initializeEngine(QQmlEngine* engine) override;
|
||||||
void onRootContextCreated(QQmlContext* qmlContext) override;
|
void onRootContextCreated(QQmlContext* qmlContext) override;
|
||||||
|
|
|
@ -1291,6 +1291,7 @@ function cleanupModelMenus() {
|
||||||
Menu.removeMenuItem("Edit", MENU_EASE_ON_FOCUS);
|
Menu.removeMenuItem("Edit", MENU_EASE_ON_FOCUS);
|
||||||
Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE);
|
Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE);
|
||||||
Menu.removeMenuItem("Edit", MENU_SHOW_ZONES_IN_EDIT_MODE);
|
Menu.removeMenuItem("Edit", MENU_SHOW_ZONES_IN_EDIT_MODE);
|
||||||
|
Menu.removeMenuItem("Edit", MENU_CREATE_ENTITIES_GRABBABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Script.scriptEnding.connect(function () {
|
Script.scriptEnding.connect(function () {
|
||||||
|
|
|
@ -103,7 +103,7 @@ EntityListTool = function(opts) {
|
||||||
|
|
||||||
var selectedIDs = [];
|
var selectedIDs = [];
|
||||||
for (var j = 0; j < selectionManager.selections.length; j++) {
|
for (var j = 0; j < selectionManager.selections.length; j++) {
|
||||||
selectedIDs.push(selectionManager.selections[j].id);
|
selectedIDs.push(selectionManager.selections[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
|
|
|
@ -321,7 +321,8 @@
|
||||||
{
|
{
|
||||||
id: "alpha",
|
id: "alpha",
|
||||||
name: "Alpha",
|
name: "Alpha",
|
||||||
type: "SliderFloat"
|
type: "SliderFloat",
|
||||||
|
max: 1.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Row"
|
type: "Row"
|
||||||
|
@ -329,7 +330,8 @@
|
||||||
{
|
{
|
||||||
id: "alphaSpread",
|
id: "alphaSpread",
|
||||||
name: "Alpha Spread",
|
name: "Alpha Spread",
|
||||||
type: "SliderFloat"
|
type: "SliderFloat",
|
||||||
|
max: 1.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Row"
|
type: "Row"
|
||||||
|
@ -337,7 +339,8 @@
|
||||||
{
|
{
|
||||||
id: "alphaStart",
|
id: "alphaStart",
|
||||||
name: "Alpha Start",
|
name: "Alpha Start",
|
||||||
type: "SliderFloat"
|
type: "SliderFloat",
|
||||||
|
max: 1.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Row"
|
type: "Row"
|
||||||
|
@ -345,7 +348,8 @@
|
||||||
{
|
{
|
||||||
id: "alphaFinish",
|
id: "alphaFinish",
|
||||||
name: "Alpha Finish",
|
name: "Alpha Finish",
|
||||||
type: "SliderFloat"
|
type: "SliderFloat",
|
||||||
|
max: 1.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Row"
|
type: "Row"
|
||||||
|
|
|
@ -23,5 +23,9 @@ if (WIN32)
|
||||||
add_dependency_external_projects(wasapi)
|
add_dependency_external_projects(wasapi)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
|
target_link_libraries(${TARGET_NAME} atomic)
|
||||||
|
endif()
|
||||||
|
|
||||||
package_libraries_for_deployment()
|
package_libraries_for_deployment()
|
||||||
endif()
|
endif()
|
||||||
|
|
47
tests/qml/qml/controls/WebEntityView.qml
Normal file
47
tests/qml/qml/controls/WebEntityView.qml
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// WebEntityView.qml
|
||||||
|
//
|
||||||
|
// Created by Kunal Gosar on 16 March 2017
|
||||||
|
// Copyright 2017 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
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtWebEngine 1.5
|
||||||
|
import Hifi 1.0
|
||||||
|
|
||||||
|
/*
|
||||||
|
TestItem {
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 10
|
||||||
|
color: "blue"
|
||||||
|
property string url: ""
|
||||||
|
ColorAnimation on color {
|
||||||
|
loops: Animation.Infinite; from: "blue"; to: "yellow"; duration: 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
WebEngineView {
|
||||||
|
id: webViewCore
|
||||||
|
objectName: "webEngineView"
|
||||||
|
width: parent !== null ? parent.width : undefined
|
||||||
|
height: parent !== null ? parent.height : undefined
|
||||||
|
|
||||||
|
onFeaturePermissionRequested: {
|
||||||
|
grantFeaturePermission(securityOrigin, feature, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//disable popup
|
||||||
|
onContextMenuRequested: {
|
||||||
|
request.accepted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onNewViewRequested: {
|
||||||
|
newViewRequestedCallback(request)
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,52 +28,87 @@
|
||||||
#include <QtGui/QImage>
|
#include <QtGui/QImage>
|
||||||
#include <QtGui/QOpenGLFunctions_4_5_Core>
|
#include <QtGui/QOpenGLFunctions_4_5_Core>
|
||||||
#include <QtGui/QOpenGLContext>
|
#include <QtGui/QOpenGLContext>
|
||||||
|
#include <QtQuick/QQuickItem>
|
||||||
#include <QtQml/QQmlContext>
|
#include <QtQml/QQmlContext>
|
||||||
#include <QtQml/QQmlComponent>
|
#include <QtQml/QQmlComponent>
|
||||||
|
|
||||||
|
#include <SettingInterface.h>
|
||||||
|
|
||||||
#include <gl/OffscreenGLCanvas.h>
|
#include <gl/OffscreenGLCanvas.h>
|
||||||
#include <GLMHelpers.h>
|
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <ViewFrustum.h>
|
#include <ViewFrustum.h>
|
||||||
#include <qml/OffscreenSurface.h>
|
#include <qml/OffscreenSurface.h>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace gl {
|
||||||
|
extern void initModuleGl();
|
||||||
|
}
|
||||||
|
|
||||||
class OffscreenQmlSurface : public hifi::qml::OffscreenSurface {
|
class QTestItem : public QQuickItem {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
QTestItem(QQuickItem* parent = nullptr) : QQuickItem(parent) { qDebug() << __FUNCTION__; }
|
||||||
|
|
||||||
|
~QTestItem() { qDebug() << __FUNCTION__; }
|
||||||
|
};
|
||||||
|
|
||||||
|
QUrl getTestResource(const QString& relativePath) {
|
||||||
|
static QString dir;
|
||||||
|
if (dir.isEmpty()) {
|
||||||
|
QDir path(__FILE__);
|
||||||
|
path.cdUp();
|
||||||
|
dir = path.cleanPath(path.absoluteFilePath("../")) + "/";
|
||||||
|
qDebug() << "Resources Path: " << dir;
|
||||||
|
}
|
||||||
|
return QUrl::fromLocalFile(dir + relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DIVISIONS_X 5
|
||||||
|
#define DIVISIONS_Y 5
|
||||||
|
|
||||||
|
using QmlPtr = QSharedPointer<hifi::qml::OffscreenSurface>;
|
||||||
|
using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence;
|
||||||
|
|
||||||
|
struct QmlInfo {
|
||||||
|
QmlPtr surface;
|
||||||
|
GLuint texture{ 0 };
|
||||||
|
uint64_t lifetime{ 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class TestWindow : public QWindow {
|
class TestWindow : public QWindow {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TestWindow();
|
TestWindow();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence;
|
|
||||||
QOpenGLContext _glContext;
|
QOpenGLContext _glContext;
|
||||||
OffscreenGLCanvas _sharedContext;
|
OffscreenGLCanvas _sharedContext;
|
||||||
OffscreenQmlSurface _offscreenQml;
|
std::array<std::array<QmlInfo, DIVISIONS_Y>, DIVISIONS_X> _surfaces;
|
||||||
|
|
||||||
QOpenGLFunctions_4_5_Core _glf;
|
QOpenGLFunctions_4_5_Core _glf;
|
||||||
uint32_t _currentTexture{ 0 };
|
|
||||||
GLsync _readFence{ 0 };
|
|
||||||
std::function<void(uint32_t, void*)> _discardLamdba;
|
std::function<void(uint32_t, void*)> _discardLamdba;
|
||||||
QSize _size;
|
QSize _size;
|
||||||
|
size_t _surfaceCount{ 0 };
|
||||||
GLuint _fbo{ 0 };
|
GLuint _fbo{ 0 };
|
||||||
const QSize _qmlSize{ 640, 480 };
|
const QSize _qmlSize{ 640, 480 };
|
||||||
bool _aboutToQuit{ false };
|
bool _aboutToQuit{ false };
|
||||||
|
uint64_t _createStopTime;
|
||||||
void initGl();
|
void initGl();
|
||||||
|
void updateSurfaces();
|
||||||
|
void buildSurface(QmlInfo& qmlInfo, bool allowVideo);
|
||||||
|
void destroySurface(QmlInfo& qmlInfo);
|
||||||
void resizeWindow(const QSize& size);
|
void resizeWindow(const QSize& size);
|
||||||
void draw();
|
void draw();
|
||||||
void resizeEvent(QResizeEvent* ev) override;
|
void resizeEvent(QResizeEvent* ev) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
TestWindow::TestWindow() {
|
TestWindow::TestWindow() {
|
||||||
setSurfaceType(QSurface::OpenGLSurface);
|
Setting::init();
|
||||||
|
|
||||||
|
setSurfaceType(QSurface::OpenGLSurface);
|
||||||
QSurfaceFormat format;
|
QSurfaceFormat format;
|
||||||
format.setDepthBufferSize(24);
|
format.setDepthBufferSize(24);
|
||||||
format.setStencilBufferSize(8);
|
format.setStencilBufferSize(8);
|
||||||
|
@ -83,13 +118,16 @@ TestWindow::TestWindow() {
|
||||||
QSurfaceFormat::setDefaultFormat(format);
|
QSurfaceFormat::setDefaultFormat(format);
|
||||||
setFormat(format);
|
setFormat(format);
|
||||||
|
|
||||||
|
qmlRegisterType<QTestItem>("Hifi", 1, 0, "TestItem");
|
||||||
|
|
||||||
show();
|
show();
|
||||||
|
_createStopTime = usecTimestampNow() + (3000u * USECS_PER_SECOND);
|
||||||
|
|
||||||
resize(QSize(800, 600));
|
resize(QSize(800, 600));
|
||||||
|
|
||||||
auto timer = new QTimer(this);
|
auto timer = new QTimer(this);
|
||||||
timer->setTimerType(Qt::PreciseTimer);
|
timer->setTimerType(Qt::PreciseTimer);
|
||||||
timer->setInterval(5);
|
timer->setInterval(30);
|
||||||
connect(timer, &QTimer::timeout, [&] { draw(); });
|
connect(timer, &QTimer::timeout, [&] { draw(); });
|
||||||
timer->start();
|
timer->start();
|
||||||
|
|
||||||
|
@ -97,7 +135,6 @@ TestWindow::TestWindow() {
|
||||||
timer->stop();
|
timer->stop();
|
||||||
_aboutToQuit = true;
|
_aboutToQuit = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestWindow::initGl() {
|
void TestWindow::initGl() {
|
||||||
|
@ -105,6 +142,7 @@ void TestWindow::initGl() {
|
||||||
if (!_glContext.create() || !_glContext.makeCurrent(this)) {
|
if (!_glContext.create() || !_glContext.makeCurrent(this)) {
|
||||||
qFatal("Unable to intialize Window GL context");
|
qFatal("Unable to intialize Window GL context");
|
||||||
}
|
}
|
||||||
|
gl::initModuleGl();
|
||||||
|
|
||||||
_glf.initializeOpenGLFunctions();
|
_glf.initializeOpenGLFunctions();
|
||||||
_glf.glCreateFramebuffers(1, &_fbo);
|
_glf.glCreateFramebuffers(1, &_fbo);
|
||||||
|
@ -113,15 +151,97 @@ void TestWindow::initGl() {
|
||||||
qFatal("Unable to intialize Shared GL context");
|
qFatal("Unable to intialize Shared GL context");
|
||||||
}
|
}
|
||||||
hifi::qml::OffscreenSurface::setSharedContext(_sharedContext.getContext());
|
hifi::qml::OffscreenSurface::setSharedContext(_sharedContext.getContext());
|
||||||
_discardLamdba = _offscreenQml.getDiscardLambda();
|
_discardLamdba = hifi::qml::OffscreenSurface::getDiscardLambda();
|
||||||
_offscreenQml.resize({ 640, 480 });
|
|
||||||
_offscreenQml.load(QUrl::fromLocalFile("C:/Users/bdavi/Git/hifi/tests/qml/qml/main.qml"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestWindow::resizeWindow(const QSize& size) {
|
void TestWindow::resizeWindow(const QSize& size) {
|
||||||
_size = size;
|
_size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const int DEFAULT_MAX_FPS = 10;
|
||||||
|
static const QString CONTROL_URL{ "/qml/controls/WebEntityView.qml" };
|
||||||
|
static const char* URL_PROPERTY{ "url" };
|
||||||
|
|
||||||
|
QString getSourceUrl(bool video) {
|
||||||
|
static const std::vector<QString> SOURCE_URLS{
|
||||||
|
"https://www.reddit.com/wiki/random",
|
||||||
|
"https://en.wikipedia.org/wiki/Wikipedia:Random",
|
||||||
|
"https://slashdot.org/",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::vector<QString> VIDEO_SOURCE_URLS{
|
||||||
|
"https://www.youtube.com/watch?v=gDXwhHm4GhM",
|
||||||
|
"https://www.youtube.com/watch?v=Ch_hoYPPeGc",
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto& sourceUrls = video ? VIDEO_SOURCE_URLS : SOURCE_URLS;
|
||||||
|
auto index = rand() % sourceUrls.size();
|
||||||
|
return sourceUrls[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::buildSurface(QmlInfo& qmlInfo, bool video) {
|
||||||
|
++_surfaceCount;
|
||||||
|
auto lifetimeSecs = (uint32_t)(5.0f + (randFloat() * 10.0f));
|
||||||
|
auto lifetimeUsecs = (USECS_PER_SECOND * lifetimeSecs);
|
||||||
|
qmlInfo.lifetime = lifetimeUsecs + usecTimestampNow();
|
||||||
|
qmlInfo.texture = 0;
|
||||||
|
qmlInfo.surface.reset(new hifi::qml::OffscreenSurface());
|
||||||
|
qmlInfo.surface->load(getTestResource(CONTROL_URL), [video](QQmlContext* context, QQuickItem* item) {
|
||||||
|
item->setProperty(URL_PROPERTY, getSourceUrl(video));
|
||||||
|
});
|
||||||
|
qmlInfo.surface->setMaxFps(DEFAULT_MAX_FPS);
|
||||||
|
qmlInfo.surface->resize(_qmlSize);
|
||||||
|
qmlInfo.surface->resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::destroySurface(QmlInfo& qmlInfo) {
|
||||||
|
auto& surface = qmlInfo.surface;
|
||||||
|
auto webView = surface->getRootItem();
|
||||||
|
if (webView) {
|
||||||
|
// stop loading
|
||||||
|
QMetaObject::invokeMethod(webView, "stop");
|
||||||
|
webView->setProperty(URL_PROPERTY, "about:blank");
|
||||||
|
}
|
||||||
|
surface->pause();
|
||||||
|
surface.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::updateSurfaces() {
|
||||||
|
auto now = usecTimestampNow();
|
||||||
|
// Fetch any new textures
|
||||||
|
for (size_t x = 0; x < DIVISIONS_X; ++x) {
|
||||||
|
for (size_t y = 0; y < DIVISIONS_Y; ++y) {
|
||||||
|
auto& qmlInfo = _surfaces[x][y];
|
||||||
|
if (!qmlInfo.surface) {
|
||||||
|
if (now < _createStopTime && randFloat() > 0.99f) {
|
||||||
|
buildSurface(qmlInfo, x == 0 && y == 0);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now > qmlInfo.lifetime) {
|
||||||
|
destroySurface(qmlInfo);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& surface = qmlInfo.surface;
|
||||||
|
auto& currentTexture = qmlInfo.texture;
|
||||||
|
|
||||||
|
TextureAndFence newTextureAndFence;
|
||||||
|
if (surface->fetchTexture(newTextureAndFence)) {
|
||||||
|
if (currentTexture != 0) {
|
||||||
|
auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
|
glFlush();
|
||||||
|
_discardLamdba(currentTexture, readFence);
|
||||||
|
}
|
||||||
|
currentTexture = newTextureAndFence.first;
|
||||||
|
_glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TestWindow::draw() {
|
void TestWindow::draw() {
|
||||||
if (_aboutToQuit) {
|
if (_aboutToQuit) {
|
||||||
return;
|
return;
|
||||||
|
@ -140,38 +260,30 @@ void TestWindow::draw() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSurfaces();
|
||||||
|
|
||||||
|
auto size = this->geometry().size();
|
||||||
|
auto incrementX = size.width() / DIVISIONS_X;
|
||||||
|
auto incrementY = size.height() / DIVISIONS_Y;
|
||||||
|
_glf.glViewport(0, 0, size.width(), size.height());
|
||||||
_glf.glClearColor(1, 0, 0, 1);
|
_glf.glClearColor(1, 0, 0, 1);
|
||||||
_glf.glClear(GL_COLOR_BUFFER_BIT);
|
_glf.glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
for (uint32_t x = 0; x < DIVISIONS_X; ++x) {
|
||||||
TextureAndFence newTextureAndFence;
|
for (uint32_t y = 0; y < DIVISIONS_Y; ++y) {
|
||||||
if (_offscreenQml.fetchTexture(newTextureAndFence)) {
|
auto& qmlInfo = _surfaces[x][y];
|
||||||
if (_currentTexture) {
|
if (!qmlInfo.surface || !qmlInfo.texture) {
|
||||||
_discardLamdba(_currentTexture, _readFence);
|
continue;
|
||||||
_readFence = 0;
|
}
|
||||||
|
_glf.glNamedFramebufferTexture(_fbo, GL_COLOR_ATTACHMENT0, qmlInfo.texture, 0);
|
||||||
|
_glf.glBlitNamedFramebuffer(_fbo, 0,
|
||||||
|
// src coordinates
|
||||||
|
0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1,
|
||||||
|
// dst coordinates
|
||||||
|
incrementX * x, incrementY * y, incrementX * (x + 1), incrementY * (y + 1),
|
||||||
|
// blit mask and filter
|
||||||
|
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentTexture = newTextureAndFence.first;
|
|
||||||
_glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
|
||||||
_glf.glNamedFramebufferTexture(_fbo, GL_COLOR_ATTACHMENT0, _currentTexture, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto diff = _size - _qmlSize;
|
|
||||||
diff /= 2;
|
|
||||||
auto qmlExtent = diff + _qmlSize;
|
|
||||||
|
|
||||||
if (_currentTexture) {
|
|
||||||
_glf.glBlitNamedFramebuffer(_fbo, 0,
|
|
||||||
0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1,
|
|
||||||
diff.width(), diff.height(), qmlExtent.width() - 1, qmlExtent.height() - 2,
|
|
||||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_readFence) {
|
|
||||||
_glf.glDeleteSync(_readFence);
|
|
||||||
}
|
|
||||||
_readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
||||||
_glf.glFlush();
|
|
||||||
|
|
||||||
_glContext.swapBuffers(this);
|
_glContext.swapBuffers(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,11 +292,9 @@ void TestWindow::resizeEvent(QResizeEvent* ev) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
setupHifiApplication("QML Test");
|
|
||||||
|
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
TestWindow window;
|
TestWindow window;
|
||||||
app.exec();
|
return app.exec();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
||||||
|
|
|
@ -27,6 +27,10 @@ if (WIN32)
|
||||||
add_dependency_external_projects(wasapi)
|
add_dependency_external_projects(wasapi)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
|
target_link_libraries(${TARGET_NAME} atomic)
|
||||||
|
endif()
|
||||||
|
|
||||||
package_libraries_for_deployment()
|
package_libraries_for_deployment()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -94,20 +94,20 @@ exports.handlers = {
|
||||||
if (e.doclet.hifiInterface) {
|
if (e.doclet.hifiInterface) {
|
||||||
rows.push("Interface Scripts");
|
rows.push("Interface Scripts");
|
||||||
}
|
}
|
||||||
if (e.doclet.hifiAssignmentClient) {
|
|
||||||
rows.push("Assignment Client Scripts");
|
|
||||||
}
|
|
||||||
if (e.doclet.hifiClientEntity) {
|
if (e.doclet.hifiClientEntity) {
|
||||||
rows.push("Client Entity Scripts");
|
rows.push("Client Entity Scripts");
|
||||||
}
|
}
|
||||||
if (e.doclet.hifiServerEntity) {
|
if (e.doclet.hifiServerEntity) {
|
||||||
rows.push("Server Entity Scripts");
|
rows.push("Server Entity Scripts");
|
||||||
}
|
}
|
||||||
|
if (e.doclet.hifiAssignmentClient) {
|
||||||
|
rows.push("Assignment Client Scripts");
|
||||||
|
}
|
||||||
|
|
||||||
// Append an Available In: table at the end of the namespace description.
|
// Append an Available In: table at the end of the namespace description.
|
||||||
if (rows.length > 0) {
|
if (rows.length > 0) {
|
||||||
var table = "<br><br><table><td>Available In:<td>" + rows.join("<td>") + "</table>";
|
var table = "<br><br><table><tr><th>Available in:</th><td>" + rows.join("</td><td>") + "</td></tr></table>";
|
||||||
e.doclet.description = e.doclet.description + table;
|
e.doclet.description = (e.doclet.description ? e.doclet.description : "") + table;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ exports.defineTags = function (dictionary) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// @hifi-assignment-client
|
// @hifi-assignment-client
|
||||||
dictionary.defineTag("hifi-assigment-client", {
|
dictionary.defineTag("hifi-assignment-client", {
|
||||||
onTagged: function (doclet, tag) {
|
onTagged: function (doclet, tag) {
|
||||||
doclet.hifiAssignmentClient = true;
|
doclet.hifiAssignmentClient = true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue