Merge branch 'master' of github.com:highfidelity/hifi into 21507-replaceQNetworkAccessManagerWithPython

This commit is contained in:
NissimHadar 2019-03-06 13:02:36 -08:00
commit 4e7f558af8
109 changed files with 1521 additions and 794 deletions

View file

@ -66,10 +66,10 @@ android {
}
signingConfigs {
release {
storeFile project.hasProperty("HIFI_ANDROID_KEYSTORE") ? file(HIFI_ANDROID_KEYSTORE) : null
storePassword project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") ? HIFI_ANDROID_KEYSTORE_PASSWORD : ''
keyAlias project.hasProperty("HIFI_ANDROID_KEY_ALIAS") ? HIFI_ANDROID_KEY_ALIAS : ''
keyPassword project.hasProperty("HIFI_ANDROID_KEY_PASSWORD") ? HIFI_ANDROID_KEY_PASSWORD : ''
storeFile project.hasProperty("HIFI_ANDROID_KEYSTORE") ? file(HIFI_ANDROID_KEYSTORE) : file('../keystore.jks')
storePassword project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") ? HIFI_ANDROID_KEYSTORE_PASSWORD : 'password'
keyAlias project.hasProperty("HIFI_ANDROID_KEY_ALIAS") ? HIFI_ANDROID_KEY_ALIAS : 'key0'
keyPassword project.hasProperty("HIFI_ANDROID_KEY_PASSWORD") ? HIFI_ANDROID_KEY_PASSWORD : 'password'
}
}
}
@ -90,10 +90,7 @@ android {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig project.hasProperty("HIFI_ANDROID_KEYSTORE") &&
project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") &&
project.hasProperty("HIFI_ANDROID_KEY_ALIAS") &&
project.hasProperty("HIFI_ANDROID_KEY_PASSWORD")? signingConfigs.release : null
signingConfig signingConfigs.release
buildConfigField "String", "BACKTRACE_URL", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\""
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\""
buildConfigField "String", "OAUTH_CLIENT_ID", "\"" + (System.getenv("OAUTH_CLIENT_ID") ? System.getenv("OAUTH_CLIENT_ID") : '') + "\""

BIN
android/apps/keystore.jks Normal file

Binary file not shown.

View file

@ -44,10 +44,10 @@ android {
}
signingConfigs {
release {
storeFile project.hasProperty("HIFI_ANDROID_KEYSTORE") ? file(HIFI_ANDROID_KEYSTORE) : null
storePassword project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") ? HIFI_ANDROID_KEYSTORE_PASSWORD : ''
keyAlias project.hasProperty("HIFI_ANDROID_KEY_ALIAS") ? HIFI_ANDROID_KEY_ALIAS : ''
keyPassword project.hasProperty("HIFI_ANDROID_KEY_PASSWORD") ? HIFI_ANDROID_KEY_PASSWORD : ''
storeFile project.hasProperty("HIFI_ANDROID_KEYSTORE") ? file(HIFI_ANDROID_KEYSTORE) : file('../keystore.jks')
storePassword project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") ? HIFI_ANDROID_KEYSTORE_PASSWORD : 'password'
keyAlias project.hasProperty("HIFI_ANDROID_KEY_ALIAS") ? HIFI_ANDROID_KEY_ALIAS : 'key0'
keyPassword project.hasProperty("HIFI_ANDROID_KEY_PASSWORD") ? HIFI_ANDROID_KEY_PASSWORD : 'password'
v2SigningEnabled false
}
}
@ -133,12 +133,6 @@ android {
assetList.each { file -> out.println(file) }
}
}
variant.outputs.all {
if (RELEASE_NUMBER != '0') {
outputFileName = "app_" + RELEASE_NUMBER + "_" + RELEASE_TYPE + ".apk"
}
}
}
}

View file

@ -61,7 +61,7 @@ extern "C" {
Java_io_highfidelity_oculus_OculusMobileActivity_nativeInitOculusPlatform(JNIEnv *env, jobject obj){
initOculusPlatform(env, obj);
}
QAndroidJniObject __interfaceActivity;
QAndroidJniObject __interfaceActivity;
JNIEXPORT void JNICALL
Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnCreate(JNIEnv *env, jobject obj) {
@ -80,6 +80,10 @@ QAndroidJniObject __interfaceActivity;
});
}
JNIEXPORT void JNICALL
Java_io_highfidelity_oculus_OculusMobileActivity_questNativeAwayMode(JNIEnv *env, jobject obj) {
AndroidHelper::instance().toggleAwayMode();
}
JNIEXPORT void Java_io_highfidelity_oculus_OculusMobileActivity_questOnAppAfterLoad(JNIEnv* env, jobject obj) {

View file

@ -5,6 +5,7 @@ import io.highfidelity.oculus.OculusMobileActivity;
import io.highfidelity.utils.HifiUtils;
public class InterfaceActivity extends OculusMobileActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
HifiUtils.upackAssets(getAssets(), getCacheDir().getAbsolutePath());

View file

@ -5,6 +5,7 @@ import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import io.highfidelity.oculus.OculusMobileActivity;
import io.highfidelity.utils.HifiUtils;
@ -19,9 +20,18 @@ public class PermissionsChecker extends Activity {
Manifest.permission.CAMERA
};
private static final String EXTRA_ARGS = "args";
private String mArgs;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mArgs =(getIntent().getStringExtra(EXTRA_ARGS));
if(!TextUtils.isEmpty(mArgs)) {
System.out.println("Application launched with following args: " + mArgs);
}
requestAppPermissions(REQUIRED_PERMISSIONS,REQUEST_PERMISSIONS);
}
@ -47,7 +57,13 @@ public class PermissionsChecker extends Activity {
}
private void launchActivityWithPermissions() {
startActivity(new Intent(this, InterfaceActivity.class));
Intent intent= new Intent(this, InterfaceActivity.class);
if(!TextUtils.isEmpty(mArgs)) {
intent.putExtra("applicationArguments", mArgs);
}
startActivity(intent);
finish();
}

View file

@ -1,11 +1,35 @@
#!/usr/bin/env bash
set -xeuo pipefail
ANDROID_BUILD_TYPE=release
ANDROID_BUILD_TARGET=assembleRelease
if [[ "$RELEASE_TYPE" == "PR" ]]; then
ANDROID_APK_SUFFIX=PR${RELEASE_NUMBER}-${SHA7}.apk ;
elif [[ "${STABLE_BUILD}" == "1" ]]; then
ANDROID_APK_SUFFIX=${RELEASE_NUMBER}.apk ;
else
ANDROID_APK_SUFFIX=${RELEASE_NUMBER}-${SHA7}.apk ;
fi
# Interface build
ANDROID_APP=interface
ANDROID_OUTPUT_DIR=./apps/${ANDROID_APP}/build/outputs/apk/${ANDROID_BUILD_TYPE}
ANDROID_OUTPUT_FILE=${ANDROID_APP}-${ANDROID_BUILD_TYPE}.apk
ANDROID_APK_NAME=HighFidelity-Beta-${ANDROID_APK_SUFFIX}
./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PVERSION_CODE=${VERSION_CODE} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} ${ANDROID_APP}:${ANDROID_BUILD_TARGET}
cp ${ANDROID_OUTPUT_DIR}/${ANDROID_OUTPUT_FILE} ./${ANDROID_APK_NAME}
# Quest Interface build
ANDROID_APP=questInterface
ANDROID_OUTPUT_DIR=./apps/${ANDROID_APP}/build/outputs/apk/${ANDROID_BUILD_TYPE}
ANDROID_OUTPUT_FILE=${ANDROID_APP}-${ANDROID_BUILD_TYPE}.apk
ANDROID_APK_NAME=HighFidelity-Quest-Beta-${ANDROID_APK_SUFFIX}
./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PVERSION_CODE=${VERSION_CODE} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} ${ANDROID_APP}:${ANDROID_BUILD_TARGET} || true
cp ${ANDROID_OUTPUT_DIR}/${ANDROID_OUTPUT_FILE} ./${ANDROID_APK_NAME} || true
# This is the actual output from gradle, which no longer attempts to muck with the naming of the APK
OUTPUT_APK=./apps/${ANDROID_APP}/build/outputs/apk/${ANDROID_BUILD_DIR}/${ANDROID_BUILT_APK_NAME}
# This is the APK name requested by Jenkins
TARGET_APK=./${ANDROID_APK_NAME}
# Make sure this matches up with the new ARTIFACT_EXPRESSION for jenkins builds, which should be "android/*.apk"
cp ${OUTPUT_APK} ${TARGET_APK}

View file

@ -9,6 +9,11 @@ docker build --build-arg BUILD_UID=`id -u` -t "${DOCKER_IMAGE_NAME}" -f docker/D
# So make sure we use VERSION_CODE consistently
test -z "$VERSION_CODE" && export VERSION_CODE=$VERSION
# PR builds don't populate STABLE_BUILD, but the release builds do, and the build
# bash script requires it, so we need to populate it if it's not present
test -z "$STABLE_BUILD" && export STABLE_BUILD=0
# FIXME figure out which of these actually need to be forwarded and which can be eliminated
docker run \
--rm \
--security-opt seccomp:unconfined \
@ -27,6 +32,8 @@ docker run \
-e OAUTH_CLIENT_SECRET \
-e OAUTH_CLIENT_ID \
-e OAUTH_REDIRECT_URI \
-e SHA7 \
-e STABLE_BUILD \
-e VERSION_CODE \
"${DOCKER_IMAGE_NAME}" \
sh -c "./build_android.sh"

View file

@ -73,7 +73,7 @@ RUN mkdir "$HIFI_BASE" && \
RUN git clone https://github.com/jherico/hifi.git && \
cd ~/hifi && \
git checkout feature/quest_frame_player
git checkout quest/build
WORKDIR /home/jenkins/hifi

View file

@ -34,11 +34,16 @@ public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Ca
private native void questNativeOnResume();
private native void questOnAppAfterLoad();
private native void questNativeAwayMode();
private SurfaceView mView;
private SurfaceHolder mSurfaceHolder;
public void onCreate(Bundle savedInstanceState) {
if(getIntent().hasExtra("applicationArguments")){
super.APPLICATION_PARAMETERS=getIntent().getStringExtra("applicationArguments");
}
super.onCreate(savedInstanceState);
Log.w(TAG, "QQQ onCreate");
@ -51,6 +56,7 @@ public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Ca
nativeOnCreate();
questNativeOnCreate();
}
public void onAppLoadedComplete() {
Log.w(TAG, "QQQ Load Completed");
runOnUiThread(() -> {
@ -62,7 +68,8 @@ public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Ca
@Override
protected void onDestroy() {
Log.w(TAG, "QQQ onDestroy");
isPausing=false;
super.onStop();
nativeOnSurfaceChanged(null);
Log.w(TAG, "QQQ onDestroy -- SUPER onDestroy");
@ -78,6 +85,7 @@ public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Ca
questNativeOnResume();
nativeOnResume();
isPausing=false;
}
@Override
@ -87,40 +95,42 @@ public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Ca
questNativeOnPause();
nativeOnPause();
isPausing=true;
}
@Override
protected void onStop(){
super.onStop();
Log.w(TAG, "QQQ Onstop called");
Log.w(TAG, "QQQ_ Onstop called");
questNativeAwayMode();
}
@Override
protected void onRestart(){
protected void onRestart() {
super.onRestart();
Log.w(TAG, "QQQ onRestart called ****");
Log.w(TAG, "QQQ_ onRestart called");
questOnAppAfterLoad();
questNativeAwayMode();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.w(TAG, "QQQ surfaceCreated ************************************");
Log.w(TAG, "QQQ_ surfaceCreated");
nativeOnSurfaceChanged(holder.getSurface());
mSurfaceHolder = holder;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.w(TAG, "QQQ surfaceChanged");
Log.w(TAG, "QQQ_ surfaceChanged");
nativeOnSurfaceChanged(holder.getSurface());
mSurfaceHolder = holder;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.w(TAG, "QQQ surfaceDestroyed ***************************************************");
Log.w(TAG, "QQQ_ surfaceDestroyed");
nativeOnSurfaceChanged(null);
mSurfaceHolder = null;
}
}

View file

@ -70,6 +70,7 @@ public class QtActivity extends Activity {
public final String QT_ANDROID_DEFAULT_THEME = QT_ANDROID_THEMES[0]; // sets the default theme.
private QtActivityLoader m_loader = new QtActivityLoader(this);
public boolean isPausing=false;
public QtActivity() {
}
@ -650,9 +651,13 @@ public class QtActivity extends Activity {
@Override
protected void onStop() {
super.onStop();
QtApplication.invokeDelegate();
if(!isPausing){
QtApplication.invokeDelegate();
}
}
//---------------------------------------------------------------------------
@Override

Binary file not shown.

View file

@ -213,6 +213,63 @@ Item {
popup.open();
}
HiFiGlyphs {
id: errorsGlyph
visible: !AvatarPackagerCore.currentAvatarProject || AvatarPackagerCore.currentAvatarProject.hasErrors
text: hifi.glyphs.alert
size: 315
color: "#EA4C5F"
anchors {
top: parent.top
topMargin: -30
horizontalCenter: parent.horizontalCenter
}
}
Image {
id: successGlyph
visible: AvatarPackagerCore.currentAvatarProject && !AvatarPackagerCore.currentAvatarProject.hasErrors
anchors {
top: parent.top
topMargin: 52
horizontalCenter: parent.horizontalCenter
}
width: 149.6
height: 149
source: "../../../icons/checkmark-stroke.svg"
}
RalewayRegular {
id: doctorStatusMessage
states: [
State {
when: AvatarPackagerCore.currentAvatarProject && !AvatarPackagerCore.currentAvatarProject.hasErrors
name: "noErrors"
PropertyChanges {
target: doctorStatusMessage
text: "Your avatar looks fine."
}
},
State {
when: !AvatarPackagerCore.currentAvatarProject || AvatarPackagerCore.currentAvatarProject.hasErrors
name: "errors"
PropertyChanges {
target: doctorStatusMessage
text: "Your avatar has a few issues."
}
}
]
color: 'white'
size: 20
anchors.left: parent.left
anchors.right: parent.right
anchors.top: errorsGlyph.bottom
wrapMode: Text.Wrap
}
RalewayRegular {
id: infoMessage
@ -240,7 +297,7 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.top: doctorStatusMessage.bottom
anchors.bottomMargin: 24
@ -249,6 +306,53 @@ Item {
text: "You can upload your files to our servers to always access them, and to make your avatar visible to other users."
}
RalewayRegular {
id: notForSaleMessage
visible: root.hasSuccessfullyUploaded
color: 'white'
linkColor: '#00B4EF'
size: 20
anchors.left: parent.left
anchors.right: parent.right
anchors.top: infoMessage.bottom
anchors.topMargin: 10
anchors.bottomMargin: 24
wrapMode: Text.Wrap
text: "This item is not for sale yet, <a href='#'>learn more</a>."
onLinkActivated: {
Qt.openUrlExternally("https://docs.highfidelity.com/sell/add-item/upload-avatar.html");
}
}
RalewayRegular {
id: showErrorsLink
color: 'white'
linkColor: '#00B4EF'
visible: AvatarPackagerCore.currentAvatarProject && AvatarPackagerCore.currentAvatarProject.hasErrors
anchors {
top: notForSaleMessage.bottom
topMargin: 16
horizontalCenter: parent.horizontalCenter
}
size: 28
text: "<a href='toggle'>View all errors</a>"
onLinkActivated: {
avatarPackager.state = AvatarPackagerState.avatarDoctorErrorReport;
}
}
HifiControls.Button {
id: openFolderButton

View file

@ -6,7 +6,7 @@ import QtQuick.Layouts 1.3
import TabletScriptingInterface 1.0
import "."
import stylesUit 1.0
import stylesUit 1.0 as HifiStylesUit
import "../audio" as HifiAudio
Item {
@ -49,44 +49,116 @@ Item {
}
Item {
width: 150
height: 50
id: rightContainer
width: clockItem.width > loginItem.width ? clockItem.width + clockAmPmTextMetrics.width :
loginItem.width + clockAmPmTextMetrics.width
height: parent.height
anchors.top: parent.top
anchors.topMargin: 15
anchors.right: parent.right
anchors.rightMargin: 30
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: 20
anchors.bottom: parent.bottom
ColumnLayout {
anchors.fill: parent
function timeChanged() {
var date = new Date();
clockTime.text = date.toLocaleTimeString(Qt.locale("en_US"), "h:mm ap");
var regex = /[\sa-zA-z]+/;
clockTime.text = clockTime.text.replace(regex, "");
clockAmPm.text = date.toLocaleTimeString(Qt.locale("en_US"), "ap");
}
RalewaySemiBold {
text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in")
horizontalAlignment: Text.AlignRight
Layout.alignment: Qt.AlignRight
font.pixelSize: 20
color: "#afafaf"
Timer {
interval: 1000; running: true; repeat: true;
onTriggered: rightContainer.timeChanged();
}
Item {
id: clockAmPmItem
width: clockAmPmTextMetrics.width
height: clockAmPmTextMetrics.height
anchors.top: parent.top
anchors.right: parent.right
TextMetrics {
id: clockAmPmTextMetrics
text: clockAmPm.text
font: clockAmPm.font
}
RalewaySemiBold {
visible: Account.loggedIn
height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0
text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : ""
horizontalAlignment: Text.AlignRight
Layout.alignment: Qt.AlignRight
font.pixelSize: 20
Text {
anchors.left: parent.left
id: clockAmPm
anchors.right: parent.right
font.capitalization: Font.AllUppercase
font.pixelSize: 12
font.family: "Rawline"
color: "#afafaf"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if (!Account.loggedIn) {
DialogsManager.showLoginDialog()
} else {
Account.logOut()
Item {
id: clockItem
width: clockTimeTextMetrics.width
height: clockTimeTextMetrics.height
anchors {
top: parent.top
topMargin: -10
right: clockAmPmItem.left
rightMargin: 5
}
TextMetrics {
id: clockTimeTextMetrics
text: clockTime.text
font: clockTime.font
}
Text {
anchors.top: parent.top
anchors.right: parent.right
id: clockTime
font.bold: false
font.pixelSize: 36
font.family: "Rawline"
color: "#afafaf"
}
}
Item {
id: loginItem
width: loginTextMetrics.width
height: loginTextMetrics.height
anchors {
bottom: parent.bottom
bottomMargin: 10
right: clockAmPmItem.left
rightMargin: 5
}
Text {
id: loginText
anchors.right: parent.right
text: Account.loggedIn ? tabletRoot.usernameShort : qsTr("Log in")
horizontalAlignment: Text.AlignRight
Layout.alignment: Qt.AlignRight
font.pixelSize: 18
font.family: "Rawline"
color: "#afafaf"
}
TextMetrics {
id: loginTextMetrics
text: loginText.text
font: loginText.font
}
MouseArea {
anchors.fill: parent
onClicked: {
if (!Account.loggedIn) {
DialogsManager.showLoginDialog();
}
}
}
}
Component.onCompleted: {
rightContainer.timeChanged();
}
}
}

View file

@ -178,10 +178,10 @@ Rectangle {
function setUsername(newUsername) {
username = newUsername;
usernameShort = newUsername.substring(0, 8);
usernameShort = newUsername.substring(0, 14);
if (newUsername.length > 8) {
usernameShort = usernameShort + "..."
if (newUsername.length > 14) {
usernameShort = usernameShort + "..."
}
}

View file

@ -0,0 +1,20 @@
//
// Rawline.qml
//
// Created by Wayne Chen on 25 Feb 2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
Text {
id: root
property real size: 32
font.pixelSize: size
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.family: "Rawline"
}

View file

@ -0,0 +1,20 @@
//
// Rawline.qml
//
// Created by Wayne Chen on 25 Feb 2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
Text {
id: root
property real size: 32
font.pixelSize: size
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.family: "Rawline"
}

View file

@ -16,8 +16,8 @@
#include <QObject>
/**jsdoc
* The <code>HifiAbout</code> API provides information about the version of Interface that is currently running. It also
* provides the ability to open a Web page in an Interface browser window.
* The <code>HifiAbout</code> API provides information about the version of Interface that is currently running. It also
* has the functionality to open a web page in an Interface browser window.
*
* @namespace HifiAbout
*
@ -30,9 +30,9 @@
* @property {string} qtVersion - The Qt version used in Interface that is currently running. <em>Read-only.</em>
*
* @example <caption>Report build information for the version of Interface currently running.</caption>
* print("HiFi build date: " + HifiAbout.buildDate); // 11 Feb 2019
* print("HiFi version: " + HifiAbout.buildVersion); // 0.78.0
* print("Qt version: " + HifiAbout.qtVersion); // 5.10.1
* print("HiFi build date: " + HifiAbout.buildDate); // Returns the build date of the version of Interface currently running on your machine.
* print("HiFi version: " + HifiAbout.buildVersion); // Returns the build version of Interface currently running on your machine.
* print("Qt version: " + HifiAbout.qtVersion); // Returns the Qt version details of the version of Interface currently running on your machine.
*/
class AboutUtil : public QObject {
@ -52,9 +52,9 @@ public:
public slots:
/**jsdoc
* Display a Web page in an Interface browser window.
* Display a web page in an Interface browser window.
* @function HifiAbout.openUrl
* @param {string} url - The URL of the Web page to display.
* @param {string} url - The URL of the web page you want to view in Interface.
*/
void openUrl(const QString &url) const;
private:

View file

@ -45,6 +45,10 @@ void AndroidHelper::notifyBeforeEnterBackground() {
emit beforeEnterBackground();
}
void AndroidHelper::notifyToggleAwayMode() {
emit toggleAwayMode();
}
void AndroidHelper::notifyEnterBackground() {
emit enterBackground();
}

View file

@ -31,6 +31,7 @@ public:
void notifyEnterForeground();
void notifyBeforeEnterBackground();
void notifyEnterBackground();
void notifyToggleAwayMode();
void performHapticFeedback(int duration);
void processURL(const QString &url);
@ -55,7 +56,7 @@ signals:
void enterForeground();
void beforeEnterBackground();
void enterBackground();
void toggleAwayMode();
void hapticFeedbackRequested(int duration);
void handleSignupCompleted();

View file

@ -1061,6 +1061,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(PluginManager::getInstance().data(), &PluginManager::inputDeviceRunningChanged,
controllerScriptingInterface, &controller::ScriptingInterface::updateRunningInputDevices);
EntityTree::setEntityClicksCapturedOperator([this] {
return _controllerScriptingInterface->areEntityClicksCaptured();
});
_entityClipboard->createRootElement();
#ifdef Q_OS_WIN
@ -1075,6 +1079,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/FiraSans-SemiBold.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Light.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Regular.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/rawline-500.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Bold.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-SemiBold.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Cairo-SemiBold.ttf");
@ -1756,6 +1761,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
#endif
});
// Setup the _keyboardMouseDevice, _touchscreenDevice, _touchscreenVirtualPadDevice and the user input mapper with the default bindings
userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice());
// if the _touchscreenDevice is not supported it will not be registered
@ -2411,6 +2417,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(&AndroidHelper::instance(), &AndroidHelper::beforeEnterBackground, this, &Application::beforeEnterBackground);
connect(&AndroidHelper::instance(), &AndroidHelper::enterBackground, this, &Application::enterBackground);
connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground);
connect(&AndroidHelper::instance(), &AndroidHelper::toggleAwayMode, this, &Application::toggleAwayMode);
AndroidHelper::instance().notifyLoadComplete();
#endif
pauseUntilLoginDetermined();
@ -3661,8 +3668,8 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
// If this is a first run we short-circuit the address passed in
if (_firstRun.get()) {
#if !defined(Q_OS_ANDROID)
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
#endif
_firstRun.set(false);
@ -4389,7 +4396,6 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() ||
getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y())) != UNKNOWN_ENTITY_ID) {
getEntities()->mouseMoveEvent(&mappedEvent);
getOverlays().mouseMoveEvent(&mappedEvent);
}
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts
@ -4423,14 +4429,8 @@ void Application::mousePressEvent(QMouseEvent* event) {
#endif
QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), event->buttons(), event->modifiers());
std::pair<float, QUuid> entityResult;
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
entityResult = getEntities()->mousePressEvent(&mappedEvent);
}
std::pair<float, QUuid> overlayResult = getOverlays().mousePressEvent(&mappedEvent);
QUuid focusedEntity = entityResult.first < overlayResult.first ? entityResult.second : overlayResult.second;
setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(focusedEntity) ? focusedEntity : UNKNOWN_ENTITY_ID);
QUuid result = getEntities()->mousePressEvent(&mappedEvent);
setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(result) ? result : UNKNOWN_ENTITY_ID);
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
@ -4469,11 +4469,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) {
transformedPos,
event->screenPos(), event->button(),
event->buttons(), event->modifiers());
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
getEntities()->mouseDoublePressEvent(&mappedEvent);
}
getOverlays().mouseDoublePressEvent(&mappedEvent);
getEntities()->mouseDoublePressEvent(&mappedEvent);
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface->isMouseCaptured()) {
@ -4498,7 +4494,6 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
event->buttons(), event->modifiers());
getEntities()->mouseReleaseEvent(&mappedEvent);
getOverlays().mouseReleaseEvent(&mappedEvent);
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
@ -4984,9 +4979,9 @@ void Application::idle() {
{
if (_keyboardFocusWaitingOnRenderable && getEntities()->renderableForEntityId(_keyboardFocusedEntity.get())) {
_keyboardFocusWaitingOnRenderable = false;
QUuid entityId = _keyboardFocusedEntity.get();
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
_keyboardFocusWaitingOnRenderable = false;
setKeyboardFocusEntity(entityId);
}
}
@ -6948,7 +6943,7 @@ void Application::clearDomainOctreeDetails(bool clearAll) {
});
// reset the model renderer
clearAll ? getEntities()->clear() : getEntities()->clearNonLocalEntities();
clearAll ? getEntities()->clear() : getEntities()->clearDomainAndNonOwnedEntities();
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
@ -9155,6 +9150,8 @@ void Application::beforeEnterBackground() {
clearDomainOctreeDetails();
}
void Application::enterBackground() {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
"stop", Qt::BlockingQueuedConnection);
@ -9178,6 +9175,15 @@ void Application::enterForeground() {
auto nodeList = DependencyManager::get<NodeList>();
nodeList->setSendDomainServerCheckInEnabled(true);
}
void Application::toggleAwayMode(){
QKeyEvent event = QKeyEvent (QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier);
QCoreApplication::sendEvent (this, &event);
}
#endif
#include "Application.moc"

View file

@ -338,7 +338,8 @@ public:
void beforeEnterBackground();
void enterBackground();
void enterForeground();
#endif
void toggleAwayMode();
#endif
signals:
void svoImportRequested(const QString& url);

View file

@ -19,13 +19,19 @@
#include <SimpleMovingAverage.h>
#include <render/Args.h>
#ifdef Q_OS_ANDROID
const float LOD_DEFAULT_QUALITY_LEVEL = 0.75f; // default quality level setting is High (lower framerate)
const float LOD_DEFAULT_QUALITY_LEVEL = 0.2f; // default quality level setting is High (lower framerate)
#else
const float LOD_DEFAULT_QUALITY_LEVEL = 0.5f; // default quality level setting is Mid
#endif
const float LOD_MAX_LIKELY_DESKTOP_FPS = 60.0f; // this is essentially, V-synch fps
#ifdef Q_OS_ANDROID
const float LOD_MAX_LIKELY_HMD_FPS = 36.0f; // this is essentially, V-synch fps
#else
const float LOD_MAX_LIKELY_HMD_FPS = 90.0f; // this is essentially, V-synch fps
#endif
const float LOD_OFFSET_FPS = 5.0f; // offset of FPS to add for computing the target framerate
class AABox;

View file

@ -77,7 +77,10 @@ public:
return QDir::cleanPath(QDir(_projectPath).absoluteFilePath(_fst->getModelPath()));
}
Q_INVOKABLE bool getHasErrors() const { return _hasErrors; }
Q_INVOKABLE void setHasErrors(bool hasErrors) { _hasErrors = hasErrors; }
Q_INVOKABLE void setHasErrors(bool hasErrors) {
_hasErrors = hasErrors;
emit hasErrorsChanged();
}
/**
* returns the AvatarProject or a nullptr on failure.

View file

@ -62,8 +62,8 @@ public:
* <tr><th>Value</th><th>Meaning</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>0</code></td><td>Not logged in</td><td>The user isn't logged in.</td></tr>
* <tr><td><code>1</code></td><td>Not set up</td><td>The user's wallet isn't set up.</td></tr>
* <tr><td><code>0</code></td><td>Not logged in</td><td>The user is not logged in.</td></tr>
* <tr><td><code>1</code></td><td>Not set up</td><td>The user's wallet has not been set up.</td></tr>
* <tr><td><code>2</code></td><td>Pre-existing</td><td>There is a wallet present on the server but not one
* locally.</td></tr>
* <tr><td><code>3</code></td><td>Conflicting</td><td>There is a wallet present on the server plus one present locally,
@ -73,8 +73,8 @@ public:
* <tr><td><code>5</code></td><td>Ready</td><td>The wallet is ready for use.</td></tr>
* </tbody>
* </table>
* <p>Wallets used to be stored locally but now they're stored on the server, unless the computer once had a wallet stored
* locally in which case the wallet may be present in both places.</p>
* <p>Wallets used to be stored locally but now they're only stored on the server. A wallet is present in both places if
* your computer previously stored its information locally.</p>
* @typedef {number} WalletScriptingInterface.WalletStatus
*/
enum WalletStatus {

View file

@ -172,7 +172,10 @@ void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3&
properties.setVisible(true);
properties.setIgnorePickIntersection(doesPathIgnorePicks());
QVector<float> widths;
widths.append(getLineWidth() * parentScale);
float width = getLineWidth() * parentScale;
widths.append(width);
widths.append(width);
properties.setStrokeWidths(widths);
DependencyManager::get<EntityScriptingInterface>()->editEntity(getPathID(), properties);
}
}

View file

@ -115,8 +115,8 @@ DownloadInfoResult::DownloadInfoResult() :
/**jsdoc
* Information on the assets currently being downloaded and pending download.
* @typedef {object} AccountServices.DownloadInfoResult
* @property {number[]} downloading - The percentage complete for each asset currently being downloaded.
* @property {number} pending - The number of assets waiting to be download.
* @property {number[]} downloading - The download percentage remaining of each asset currently downloading.
* @property {number} pending - The number of assets pending download.
*/
QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result) {
QScriptValue object = engine->newObject();

View file

@ -38,19 +38,19 @@ class AccountServicesScriptingInterface : public QObject {
Q_OBJECT
/**jsdoc
* The <code>AccountServices</code> API provides functions related to user connectivity, visibility, and asset download
* progress.
* The <code>AccountServices</code> API provides functions that give information on user connectivity, visibility, and
* asset download progress.
*
* @hifi-interface
* @hifi-client-entity
* @hifi-avatar
*
* @namespace AccountServices
* @property {string} username - The user name if the user is logged in, otherwise <code>"Unknown user"</code>.
* <em>Read-only.</em>
* @property {string} username - The user name of the user logged in. If there is no user logged in, it is
* <code>"Unknown user"</code>. <em>Read-only.</em>
* @property {boolean} loggedIn - <code>true</code> if the user is logged in, otherwise <code>false</code>.
* <em>Read-only.</em>
* @property {string} findableBy - The user's visibility to other people:<br />
* @property {string} findableBy - The user's visibility to other users:<br />
* <code>"none"</code> - user appears offline.<br />
* <code>"friends"</code> - user is visible only to friends.<br />
* <code>"connections"</code> - user is visible to friends and connections.<br />
@ -74,23 +74,23 @@ public:
public slots:
/**jsdoc
* Get information on the progress of downloading assets in the domain.
* Gets information on the download progress of assets in the domain.
* @function AccountServices.getDownloadInfo
* @returns {AccountServices.DownloadInfoResult} Information on the progress of assets download.
* @returns {AccountServices.DownloadInfoResult} Information on the download progress of assets.
*/
DownloadInfoResult getDownloadInfo();
/**jsdoc
* Cause a {@link AccountServices.downloadInfoChanged|downloadInfoChanged} signal to be triggered with information on the
* current progress of the download of assets in the domain.
* Triggers a {@link AccountServices.downloadInfoChanged|downloadInfoChanged} signal with information on the current
* download progress of the assets in the domain.
* @function AccountServices.updateDownloadInfo
*/
void updateDownloadInfo();
/**jsdoc
* Check whether the user is logged in.
* Checks whether the user is logged in.
* @function AccountServices.isLoggedIn
* @returns {boolean} <code>true</code> if the user is logged in, <code>false</code> otherwise.
* @returns {boolean} <code>true</code> if the user is logged in, <code>false</code> if not.
* @example <caption>Report whether you are logged in.</caption>
* var isLoggedIn = AccountServices.isLoggedIn();
* print("You are logged in: " + isLoggedIn); // true or false
@ -98,9 +98,9 @@ public slots:
bool isLoggedIn();
/**jsdoc
* Prompts the user to log in (the login dialog is displayed) if they're not already logged in.
* The function returns the login status of the user and prompts the user to log in (with a login dialog) if they're not already logged in.
* @function AccountServices.checkAndSignalForAccessToken
* @returns {boolean} <code>true</code> if the user is already logged in, <code>false</code> otherwise.
* @returns {boolean} <code>true</code> if the user is logged in, <code>false</code> if not.
*/
bool checkAndSignalForAccessToken();
@ -140,7 +140,7 @@ signals:
/**jsdoc
* Triggered when the username logged in with changes, i.e., when the user logs in or out.
* @function AccountServices.myUsernameChanged
* @param {string} username - The username logged in with if the user is logged in, otherwise <code>""</code>.
* @param {string} username - The user name of the user logged in. If there is no user logged in, it is <code>""</code>.
* @returns {Signal}
* @example <caption>Report when your username changes.</caption>
* AccountServices.myUsernameChanged.connect(function (username) {
@ -150,9 +150,9 @@ signals:
void myUsernameChanged(const QString& username);
/**jsdoc
* Triggered when the progress of the download of assets for the domain changes.
* Triggered when the download progress of the assets in the domain changes.
* @function AccountServices.downloadInfoChanged
* @param {AccountServices.DownloadInfoResult} downloadInfo - Information on the progress of assets download.
* @param {AccountServices.DownloadInfoResult} downloadInfo - Information on the download progress of assets.
* @returns {Signal}
*/
void downloadInfoChanged(DownloadInfoResult info);
@ -186,7 +186,7 @@ signals:
/**jsdoc
* Triggered when the login status of the user changes.
* @function AccountServices.loggedInChanged
* @param {boolean} loggedIn - <code>true</code> if the user is logged in, otherwise <code>false</code>.
* @param {boolean} loggedIn - <code>true</code> if the user is logged in, <code>false</code> if not.
* @returns {Signal}
* @example <caption>Report when your login status changes.</caption>
* AccountServices.loggedInChanged.connect(function(loggedIn) {

View file

@ -41,8 +41,8 @@ public:
*
* @property {WalletScriptingInterface.WalletStatus} walletStatus - The status of the user's wallet. <em>Read-only.</em>
* @property {boolean} limitedCommerce - <code>true</code> if Interface is running in limited commerce mode. In limited commerce
* mode, certain Interface functionality is disabled, e.g., users can't buy non-free items from the Marketplace. The Oculus
* Store version of Interface runs in limited commerce mode. <em>Read-only.</em>
* mode, certain Interface functionalities are disabled, e.g., users can't buy items that are not free from the Marketplace.
* The Oculus Store version of Interface runs in limited commerce mode. <em>Read-only.</em>
*/
class WalletScriptingInterface : public QObject, public Dependency {
Q_OBJECT
@ -55,16 +55,16 @@ public:
WalletScriptingInterface();
/**jsdoc
* Check and update the user's wallet status.
* Checks and updates the user's wallet status.
* @function WalletScriptingInterface.refreshWalletStatus
*/
Q_INVOKABLE void refreshWalletStatus();
/**jsdoc
* Get the current status of the user's wallet.
* Gets the current status of the user's wallet.
* @function WalletScriptingInterface.getWalletStatus
* @returns {WalletScriptingInterface.WalletStatus}
* @example <caption>Two ways to report your wallet status.</caption>
* @example <caption>Use two methods to report your wallet's status.</caption>
* print("Wallet status: " + WalletScriptingInterface.walletStatus); // Same value as next line.
* print("Wallet status: " + WalletScriptingInterface.getWalletStatus());
*/
@ -74,11 +74,11 @@ public:
* Check that a certified avatar entity is owned by the avatar whose entity it is. The result of the check is provided via
* the {@link WalletScriptingInterface.ownershipVerificationSuccess|ownershipVerificationSuccess} and
* {@link WalletScriptingInterface.ownershipVerificationFailed|ownershipVerificationFailed} signals.<br />
* <strong>Warning:</strong> Neither of these signals fire if the entity is not an avatar entity or it's not a certified
* entity.
* <strong>Warning:</strong> Neither of these signals are triggered if the entity is not an avatar entity or is not
* certified.
* @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification
* @param {Uuid} entityID - The ID of the avatar entity to check.
* @example <caption>Check ownership of all nearby certified avatar entities.</caption>
* @param {Uuid} entityID - The avatar entity's ID.
* @example <caption>Check the ownership of all nearby certified avatar entities.</caption>
* // Set up response handling.
* function ownershipSuccess(entityID) {
* print("Ownership test succeeded for: " + entityID);
@ -118,7 +118,7 @@ public:
signals:
/**jsdoc
* Triggered when the status of the user's wallet changes.
* Triggered when the user's wallet status changes.
* @function WalletScriptingInterface.walletStatusChanged
* @returns {Signal}
* @example <caption>Report when your wallet status changes, e.g., when you log in and out.</caption>
@ -136,7 +136,7 @@ signals:
void limitedCommerceChanged();
/**jsdoc
* Triggered when the user rezzes a certified entity but the user's wallet is not ready and so the certified location of the
* Triggered when the user rezzes a certified entity but the user's wallet is not ready. So the certified location of the
* entity cannot be updated in the metaverse.
* @function WalletScriptingInterface.walletNotSetup
* @returns {Signal}

View file

@ -16,8 +16,7 @@
#include "Application.h"
Overlay::Overlay() :
_renderItemID(render::Item::INVALID_ITEM_ID),
_visible(true)
_renderItemID(render::Item::INVALID_ITEM_ID)
{
}
@ -34,20 +33,6 @@ void Overlay::setProperties(const QVariantMap& properties) {
}
}
QVariant Overlay::getProperty(const QString& property) {
if (property == "type") {
return QVariant(getType());
}
if (property == "id") {
return getID();
}
if (property == "visible") {
return _visible;
}
return QVariant();
}
bool Overlay::addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) {
_renderItemID = scene->allocateID();
transaction.resetItem(_renderItemID, std::make_shared<Overlay::Payload>(overlay));
@ -65,7 +50,7 @@ render::ItemKey Overlay::getKey() {
builder.withViewSpace();
builder.withLayer(render::hifi::LAYER_2D);
if (!getVisible()) {
if (!_visible) {
builder.withInvisible();
}

View file

@ -31,7 +31,6 @@ public:
virtual render::ItemKey getKey();
virtual AABox getBounds() const = 0;
virtual bool supportsGetProperty() const { return true; }
virtual bool addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction);
virtual void removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction);
@ -42,17 +41,15 @@ public:
// getters
virtual QString getType() const = 0;
bool isLoaded() { return true; }
bool getVisible() const { return _visible; }
// setters
virtual void setVisible(bool visible) { _visible = visible; }
void setVisible(bool visible) { _visible = visible; }
unsigned int getStackOrder() const { return _stackOrder; }
void setStackOrder(unsigned int stackOrder) { _stackOrder = stackOrder; }
Q_INVOKABLE virtual void setProperties(const QVariantMap& properties);
Q_INVOKABLE virtual void setProperties(const QVariantMap& properties) = 0;
Q_INVOKABLE virtual Overlay* createClone() const = 0;
Q_INVOKABLE virtual QVariant getProperty(const QString& property);
render::ItemID getRenderItemID() const { return _renderItemID; }
void setRenderItemID(render::ItemID renderItemID) { _renderItemID = renderItemID; }
@ -60,7 +57,7 @@ public:
protected:
render::ItemID _renderItemID { render::Item::INVALID_ITEM_ID };
bool _visible;
bool _visible { true };
unsigned int _stackOrder { 0 };
private:

View file

@ -65,24 +65,4 @@ void Overlay2D::setProperties(const QVariantMap& properties) {
}
setBounds(newBounds);
}
}
QVariant Overlay2D::getProperty(const QString& property) {
if (property == "bounds") {
return qRectToVariant(_bounds);
}
if (property == "x") {
return _bounds.x();
}
if (property == "y") {
return _bounds.y();
}
if (property == "width") {
return _bounds.width();
}
if (property == "height") {
return _bounds.height();
}
return Overlay::getProperty(property);
}
}

View file

@ -26,10 +26,6 @@ public:
virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return 1; }
// getters
int getX() const { return _bounds.x(); }
int getY() const { return _bounds.y(); }
int getWidth() const { return _bounds.width(); }
int getHeight() const { return _bounds.height(); }
const QRect& getBoundingRect() const { return _bounds; }
// setters
@ -40,7 +36,6 @@ public:
void setBounds(const QRect& bounds) { _bounds = bounds; }
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
protected:
QRect _bounds; // where on the screen to draw

View file

@ -43,14 +43,6 @@ std::unordered_map<QString, QString> Overlays::_entityToOverlayTypes;
std::unordered_map<QString, QString> Overlays::_overlayToEntityTypes;
Overlays::Overlays() {
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Overlays::hoverEnterPointerEvent);
connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, this, &Overlays::hoverOverPointerEvent);
connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Overlays::hoverLeavePointerEvent);
connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Overlays::mousePressPointerEvent);
connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Overlays::mouseMovePointerEvent);
connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Overlays::mouseReleasePointerEvent);
ADD_TYPE_MAP(Box, cube);
ADD_TYPE_MAP(Sphere, sphere);
_overlayToEntityTypes["rectangle3d"] = "Shape";
@ -81,13 +73,23 @@ void Overlays::cleanupAllOverlays() {
void Overlays::init() {
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>().data();
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, entityScriptingInterface , &EntityScriptingInterface::hoverEnterEntity);
connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity);
connect(pointerManager.data(), &PointerManager::hoverEndOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity);
connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity);
connect(pointerManager.data(), &PointerManager::triggerEndOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity);
auto pointerManager = DependencyManager::get<PointerManager>().data();
connect(pointerManager, &PointerManager::hoverBeginOverlay, entityScriptingInterface , &EntityScriptingInterface::hoverEnterEntity);
connect(pointerManager, &PointerManager::hoverContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity);
connect(pointerManager, &PointerManager::hoverEndOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity);
connect(pointerManager, &PointerManager::triggerBeginOverlay, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
connect(pointerManager, &PointerManager::triggerContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity);
connect(pointerManager, &PointerManager::triggerEndOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity);
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, &Overlays::mousePressOnPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOffEntity, this, &Overlays::mousePressOffPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseDoublePressOnEntity, this, &Overlays::mouseDoublePressOnPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseDoublePressOffEntity, this, &Overlays::mouseDoublePressOffPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, &Overlays::mouseReleasePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity, this, &Overlays::mouseMovePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity , this, &Overlays::hoverEnterPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity, this, &Overlays::hoverOverPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity, this, &Overlays::hoverLeavePointerEvent);
}
void Overlays::update(float deltatime) {
@ -413,32 +415,13 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove
overlayProps["dimensions"] = vec3toVariant(ratio * dimensions);
}
if (add || overlayProps.contains("rotation")) {
glm::quat rotation;
{
auto iter = overlayProps.find("rotation");
if (iter != overlayProps.end()) {
rotation = quatFromVariant(iter.value());
} else if (!add) {
EntityPropertyFlags desiredProperties;
desiredProperties += PROP_ROTATION;
rotation = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(id, desiredProperties).getRotation();
}
}
if (add && !overlayProps.contains("rotation") && !overlayProps.contains("localRotation")) {
overlayProps["rotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, Vectors::RIGHT));
} else if (overlayProps.contains("rotation")) {
glm::quat rotation = quatFromVariant(overlayProps["rotation"]);
overlayProps["rotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation);
}
if (add || overlayProps.contains("localRotation")) {
glm::quat rotation;
{
auto iter = overlayProps.find("localRotation");
if (iter != overlayProps.end()) {
rotation = quatFromVariant(iter.value());
} else if (!add) {
EntityPropertyFlags desiredProperties;
desiredProperties += PROP_LOCAL_ROTATION;
rotation = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(id, desiredProperties).getLocalRotation();
}
}
} else if (overlayProps.contains("localRotation")) {
glm::quat rotation = quatFromVariant(overlayProps["localRotation"]);
overlayProps["localRotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation);
}
@ -772,29 +755,29 @@ QUuid Overlays::addOverlay(const QString& type, const QVariant& properties) {
return UNKNOWN_ENTITY_ID;
}
if (QThread::currentThread() != thread()) {
QUuid result;
PROFILE_RANGE(script, __FUNCTION__);
BLOCKING_INVOKE_METHOD(this, "addOverlay", Q_RETURN_ARG(QUuid, result), Q_ARG(const QString&, type), Q_ARG(const QVariant&, properties));
return result;
}
Overlay::Pointer overlay;
if (type == ImageOverlay::TYPE) {
if (type == ImageOverlay::TYPE || type == TextOverlay::TYPE || type == RectangleOverlay::TYPE) {
#if !defined(DISABLE_QML)
overlay = Overlay::Pointer(new ImageOverlay(), [](Overlay* ptr) { ptr->deleteLater(); });
#endif
} else if (type == TextOverlay::TYPE) {
#if !defined(DISABLE_QML)
overlay = Overlay::Pointer(new TextOverlay(), [](Overlay* ptr) { ptr->deleteLater(); });
#endif
} else if (type == RectangleOverlay::TYPE) {
overlay = Overlay::Pointer(new RectangleOverlay(), [](Overlay* ptr) { ptr->deleteLater(); });
}
if (QThread::currentThread() != thread()) {
QUuid result;
PROFILE_RANGE(script, __FUNCTION__);
BLOCKING_INVOKE_METHOD(this, "addOverlay", Q_RETURN_ARG(QUuid, result), Q_ARG(const QString&, type), Q_ARG(const QVariant&, properties));
return result;
}
if (overlay) {
overlay->setProperties(properties.toMap());
return add2DOverlay(overlay);
Overlay::Pointer overlay;
if (type == ImageOverlay::TYPE) {
overlay = Overlay::Pointer(new ImageOverlay(), [](Overlay* ptr) { ptr->deleteLater(); });
} else if (type == TextOverlay::TYPE) {
overlay = Overlay::Pointer(new TextOverlay(), [](Overlay* ptr) { ptr->deleteLater(); });
} else if (type == RectangleOverlay::TYPE) {
overlay = Overlay::Pointer(new RectangleOverlay(), [](Overlay* ptr) { ptr->deleteLater(); });
}
if (overlay) {
overlay->setProperties(properties.toMap());
return add2DOverlay(overlay);
}
#endif
return QUuid();
}
QString entityType = overlayToEntityType(type);
@ -835,15 +818,14 @@ QUuid Overlays::cloneOverlay(const QUuid& id) {
return UNKNOWN_ENTITY_ID;
}
if (QThread::currentThread() != thread()) {
QUuid result;
PROFILE_RANGE(script, __FUNCTION__);
BLOCKING_INVOKE_METHOD(this, "cloneOverlay", Q_RETURN_ARG(QUuid, result), Q_ARG(const QUuid&, id));
return result;
}
Overlay::Pointer overlay = get2DOverlay(id);
if (overlay) {
if (QThread::currentThread() != thread()) {
QUuid result;
PROFILE_RANGE(script, __FUNCTION__);
BLOCKING_INVOKE_METHOD(this, "cloneOverlay", Q_RETURN_ARG(QUuid, result), Q_ARG(const QUuid&, id));
return result;
}
return add2DOverlay(Overlay::Pointer(overlay->createClone(), [](Overlay* ptr) { ptr->deleteLater(); }));
}
@ -919,6 +901,11 @@ void Overlays::deleteOverlay(const QUuid& id) {
Overlay::Pointer overlay = take2DOverlay(id);
if (overlay) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "deleteOverlay", Q_ARG(const QUuid&, id));
return;
}
_overlaysToDelete.push_back(overlay);
emit overlayDeleted(id);
return;
@ -933,15 +920,14 @@ QString Overlays::getOverlayType(const QUuid& id) {
return "";
}
if (QThread::currentThread() != thread()) {
QString result;
PROFILE_RANGE(script, __FUNCTION__);
BLOCKING_INVOKE_METHOD(this, "getOverlayType", Q_RETURN_ARG(QString, result), Q_ARG(const QUuid&, id));
return result;
}
Overlay::Pointer overlay = get2DOverlay(id);
if (overlay) {
if (QThread::currentThread() != thread()) {
QString result;
PROFILE_RANGE(script, __FUNCTION__);
BLOCKING_INVOKE_METHOD(this, "getOverlayType", Q_RETURN_ARG(QString, result), Q_ARG(const QUuid&, id));
return result;
}
return overlay->getType();
}
@ -949,15 +935,14 @@ QString Overlays::getOverlayType(const QUuid& id) {
}
QObject* Overlays::getOverlayObject(const QUuid& id) {
if (QThread::currentThread() != thread()) {
QObject* result;
PROFILE_RANGE(script, __FUNCTION__);
BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(const QUuid&, id));
return result;
}
Overlay::Pointer overlay = get2DOverlay(id);
if (overlay) {
if (QThread::currentThread() != thread()) {
QObject* result;
PROFILE_RANGE(script, __FUNCTION__);
BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(const QUuid&, id));
return result;
}
return qobject_cast<QObject*>(&(*overlay));
}
@ -969,6 +954,12 @@ QUuid Overlays::getOverlayAtPoint(const glm::vec2& point) {
return UNKNOWN_ENTITY_ID;
}
if (QThread::currentThread() != thread()) {
QUuid result;
BLOCKING_INVOKE_METHOD(this, "getOverlayAtPoint", Q_RETURN_ARG(QUuid, result), Q_ARG(const glm::vec2&, point));
return result;
}
QMutexLocker locker(&_mutex);
QMapIterator<QUuid, Overlay::Pointer> i(_overlays);
unsigned int bestStackOrder = 0;
@ -976,8 +967,7 @@ QUuid Overlays::getOverlayAtPoint(const glm::vec2& point) {
while (i.hasNext()) {
i.next();
auto thisOverlay = std::dynamic_pointer_cast<Overlay2D>(i.value());
if (thisOverlay && thisOverlay->getVisible() && thisOverlay->isLoaded() &&
thisOverlay->getBoundingRect().contains(point.x, point.y, false)) {
if (thisOverlay && thisOverlay->getVisible() && thisOverlay->getBoundingRect().contains(point.x, point.y, false)) {
if (thisOverlay->getStackOrder() > bestStackOrder) {
bestID = i.key();
bestStackOrder = thisOverlay->getStackOrder();
@ -991,9 +981,7 @@ QUuid Overlays::getOverlayAtPoint(const glm::vec2& point) {
QVariant Overlays::getProperty(const QUuid& id, const QString& property) {
Overlay::Pointer overlay = get2DOverlay(id);
if (overlay) {
if (overlay->supportsGetProperty()) {
return overlay->getProperty(property);
}
// We don't support getting properties from QML Overlays right now
return QVariant();
}
@ -1009,12 +997,8 @@ QVariantMap Overlays::getProperties(const QUuid& id, const QStringList& properti
Overlay::Pointer overlay = get2DOverlay(id);
QVariantMap result;
if (overlay) {
if (overlay->supportsGetProperty()) {
for (const auto& property : properties) {
result.insert(property, overlay->getProperty(property));
}
}
return result;
// We don't support getting properties from QML Overlays right now
return QVariantMap();
}
QVariantMap overlayProperties = convertEntityToOverlayProperties(DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(id));
@ -1141,38 +1125,30 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
}
bool Overlays::isLoaded(const QUuid& id) {
if (QThread::currentThread() != thread()) {
bool result;
PROFILE_RANGE(script, __FUNCTION__);
BLOCKING_INVOKE_METHOD(this, "isLoaded", Q_RETURN_ARG(bool, result), Q_ARG(const QUuid&, id));
return result;
}
Overlay::Pointer overlay = get2DOverlay(id);
if (overlay) {
return overlay->isLoaded();
return true;
}
return DependencyManager::get<EntityScriptingInterface>()->isLoaded(id);
}
QSizeF Overlays::textSize(const QUuid& id, const QString& text) {
if (QThread::currentThread() != thread()) {
QSizeF result;
PROFILE_RANGE(script, __FUNCTION__);
BLOCKING_INVOKE_METHOD(this, "textSize", Q_RETURN_ARG(QSizeF, result), Q_ARG(const QUuid&, id), Q_ARG(QString, text));
return result;
}
Overlay::Pointer overlay = get2DOverlay(id);
if (overlay) {
if (QThread::currentThread() != thread()) {
QSizeF result;
PROFILE_RANGE(script, __FUNCTION__);
BLOCKING_INVOKE_METHOD(this, "textSize", Q_RETURN_ARG(QSizeF, result), Q_ARG(const QUuid&, id), Q_ARG(QString, text));
return result;
}
if (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(overlay)) {
return textOverlay->textSize(text);
}
return QSizeF(0.0f, 0.0f);
} else {
return DependencyManager::get<EntityScriptingInterface>()->textSize(id, text);
}
return DependencyManager::get<EntityScriptingInterface>()->textSize(id, text);
}
bool Overlays::isAddedOverlay(const QUuid& id) {
@ -1185,7 +1161,7 @@ bool Overlays::isAddedOverlay(const QUuid& id) {
}
void Overlays::sendMousePressOnOverlay(const QUuid& id, const PointerEvent& event) {
mousePressPointerEvent(id, event);
mousePressOnPointerEvent(id, event);
}
void Overlays::sendMouseReleaseOnOverlay(const QUuid& id, const PointerEvent& event) {
@ -1232,57 +1208,66 @@ float Overlays::height() {
return offscreenUi->getWindow()->size().height();
}
static uint32_t toPointerButtons(const QMouseEvent& event) {
uint32_t buttons = 0;
buttons |= event.buttons().testFlag(Qt::LeftButton) ? PointerEvent::PrimaryButton : 0;
buttons |= event.buttons().testFlag(Qt::RightButton) ? PointerEvent::SecondaryButton : 0;
buttons |= event.buttons().testFlag(Qt::MiddleButton) ? PointerEvent::TertiaryButton : 0;
return buttons;
}
static PointerEvent::Button toPointerButton(const QMouseEvent& event) {
switch (event.button()) {
case Qt::LeftButton:
return PointerEvent::PrimaryButton;
case Qt::RightButton:
return PointerEvent::SecondaryButton;
case Qt::MiddleButton:
return PointerEvent::TertiaryButton;
default:
return PointerEvent::NoButtons;
}
}
RayToOverlayIntersectionResult getPrevPickResult() {
RayToOverlayIntersectionResult overlayResult;
overlayResult.intersects = false;
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResultTyped<RayPickResult>(DependencyManager::get<EntityTreeRenderer>()->getMouseRayPickID());
if (pickResult) {
overlayResult.intersects = pickResult->type == IntersectionType::LOCAL_ENTITY;
if (overlayResult.intersects) {
overlayResult.intersection = pickResult->intersection;
overlayResult.distance = pickResult->distance;
overlayResult.surfaceNormal = pickResult->surfaceNormal;
overlayResult.overlayID = pickResult->objectID;
overlayResult.extraInfo = pickResult->extraInfo;
void Overlays::mousePressOnPointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit mousePressOnOverlay(id, event);
}
}
return overlayResult;
}
PointerEvent Overlays::calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray,
const RayToOverlayIntersectionResult& rayPickResult, QMouseEvent* event,
PointerEvent::EventType eventType) {
glm::vec2 pos2D = RayPick::projectOntoEntityXYPlane(id, rayPickResult.intersection);
return PointerEvent(eventType, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal,
ray.direction, toPointerButton(*event), toPointerButtons(*event), event->modifiers());
void Overlays::mousePressOffPointerEvent() {
emit mousePressOffOverlay();
}
void Overlays::mouseDoublePressOnPointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit mouseDoublePressOnOverlay(id, event);
}
}
}
void Overlays::mouseDoublePressOffPointerEvent() {
emit mouseDoublePressOffOverlay();
}
void Overlays::mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit mouseReleaseOnOverlay(id, event);
}
}
}
void Overlays::mouseMovePointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit mouseMoveOnOverlay(id, event);
}
}
}
void Overlays::hoverEnterPointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit hoverEnterOverlay(id, event);
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit hoverEnterOverlay(id, event);
}
}
}
@ -1290,7 +1275,10 @@ void Overlays::hoverOverPointerEvent(const QUuid& id, const PointerEvent& event)
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit hoverOverOverlay(id, event);
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit hoverOverOverlay(id, event);
}
}
}
@ -1298,113 +1286,10 @@ void Overlays::hoverLeavePointerEvent(const QUuid& id, const PointerEvent& event
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit hoverLeaveOverlay(id, event);
}
}
std::pair<float, QUuid> Overlays::mousePressEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mousePressEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
PointerEvent pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press);
mousePressPointerEvent(_currentClickingOnOverlayID, pointerEvent);
return { rayPickResult.distance, rayPickResult.overlayID };
}
emit mousePressOffOverlay();
return { FLT_MAX, UNKNOWN_ENTITY_ID };
}
void Overlays::mousePressPointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit mousePressOnOverlay(id, event);
}
}
bool Overlays::mouseDoublePressEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
auto pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press);
emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent);
return true;
}
emit mouseDoublePressOffOverlay();
return false;
}
bool Overlays::mouseReleaseEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseReleaseEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release);
mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent);
}
_currentClickingOnOverlayID = UNKNOWN_ENTITY_ID;
return false;
}
void Overlays::mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit mouseReleaseOnOverlay(id, event);
}
}
bool Overlays::mouseMoveEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseMoveEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move);
mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent);
// If previously hovering over a different overlay then leave hover on that overlay.
if (_currentHoverOverOverlayID != UNKNOWN_ENTITY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) {
auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move);
hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent);
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit hoverLeaveOverlay(id, event);
}
// If hovering over a new overlay then enter hover on that overlay.
if (rayPickResult.overlayID != _currentHoverOverOverlayID) {
hoverEnterPointerEvent(rayPickResult.overlayID, pointerEvent);
}
// Hover over current overlay.
hoverOverPointerEvent(rayPickResult.overlayID, pointerEvent);
_currentHoverOverOverlayID = rayPickResult.overlayID;
} else {
// If previously hovering an overlay then leave hover.
if (_currentHoverOverOverlayID != UNKNOWN_ENTITY_ID) {
auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move);
hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent);
_currentHoverOverOverlayID = UNKNOWN_ENTITY_ID;
}
}
return false;
}
void Overlays::mouseMovePointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit mouseMoveOnOverlay(id, event);
}
}

View file

@ -112,11 +112,6 @@ public:
const QVector<EntityItemID>& discard,
bool visibleOnly = false, bool collidableOnly = false);
std::pair<float, QUuid> mousePressEvent(QMouseEvent* event);
bool mouseDoublePressEvent(QMouseEvent* event);
bool mouseReleaseEvent(QMouseEvent* event);
bool mouseMoveEvent(QMouseEvent* event);
void cleanupAllOverlays();
mutable QScriptEngine _scriptEngine;
@ -719,9 +714,6 @@ private:
PointerEvent calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray, const RayToOverlayIntersectionResult& rayPickResult,
QMouseEvent* event, PointerEvent::EventType eventType);
QUuid _currentClickingOnOverlayID;
QUuid _currentHoverOverOverlayID;
static QString entityToOverlayType(const QString& type);
static QString overlayToEntityType(const QString& type);
static std::unordered_map<QString, QString> _entityToOverlayTypes;
@ -732,12 +724,17 @@ private:
EntityItemProperties convertOverlayToEntityProperties(QVariantMap& overlayProps, std::pair<glm::quat, bool>& rotationToSave, const QString& type, bool add, const QUuid& id = QUuid());
private slots:
void mousePressPointerEvent(const QUuid& id, const PointerEvent& event);
void mouseMovePointerEvent(const QUuid& id, const PointerEvent& event);
void mousePressOnPointerEvent(const QUuid& id, const PointerEvent& event);
void mousePressOffPointerEvent();
void mouseDoublePressOnPointerEvent(const QUuid& id, const PointerEvent& event);
void mouseDoublePressOffPointerEvent();
void mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event);
void mouseMovePointerEvent(const QUuid& id, const PointerEvent& event);
void hoverEnterPointerEvent(const QUuid& id, const PointerEvent& event);
void hoverOverPointerEvent(const QUuid& id, const PointerEvent& event);
void hoverLeavePointerEvent(const QUuid& id, const PointerEvent& event);
};
#define ADD_TYPE_MAP(entity, overlay) \

View file

@ -57,29 +57,15 @@ QmlOverlay::~QmlOverlay() {
// QmlOverlay replaces Overlay's properties with those defined in the QML file used but keeps Overlay2D's properties.
void QmlOverlay::setProperties(const QVariantMap& properties) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setProperties", Q_ARG(QVariantMap, properties));
return;
}
Overlay2D::setProperties(properties);
auto bounds = _bounds;
// check to see if qmlElement still exists
if (_qmlElement) {
_qmlElement->setX(bounds.left());
_qmlElement->setY(bounds.top());
_qmlElement->setWidth(bounds.width());
_qmlElement->setHeight(bounds.height());
_qmlElement->setX(_bounds.left());
_qmlElement->setY(_bounds.top());
_qmlElement->setWidth(_bounds.width());
_qmlElement->setHeight(_bounds.height());
_qmlElement->setVisible(_visible);
QMetaObject::invokeMethod(_qmlElement, "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties));
}
}
void QmlOverlay::render(RenderArgs* args) {
if (!_qmlElement) {
return;
}
if (_visible != _qmlElement->isVisible()) {
_qmlElement->setVisible(_visible);
}
}
}

View file

@ -25,10 +25,8 @@ public:
QmlOverlay(const QUrl& url, const QmlOverlay* overlay);
~QmlOverlay();
bool supportsGetProperty() const override { return false; }
void setProperties(const QVariantMap& properties) override;
void render(RenderArgs* args) override;
void render(RenderArgs* args) override {}
private:
Q_INVOKABLE void qmlElementDestroyed();

View file

@ -95,69 +95,132 @@ void AnimClip::setCurrentFrameInternal(float frame) {
_frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame + _startFrame, dt, _loopFlag, _id, triggers);
}
static std::vector<int> buildJointIndexMap(const AnimSkeleton& dstSkeleton, const AnimSkeleton& srcSkeleton) {
std::vector<int> jointIndexMap;
int srcJointCount = srcSkeleton.getNumJoints();
jointIndexMap.reserve(srcJointCount);
for (int srcJointIndex = 0; srcJointIndex < srcJointCount; srcJointIndex++) {
QString srcJointName = srcSkeleton.getJointName(srcJointIndex);
int dstJointIndex = dstSkeleton.nameToJointIndex(srcJointName);
jointIndexMap.push_back(dstJointIndex);
}
return jointIndexMap;
}
void AnimClip::copyFromNetworkAnim() {
assert(_networkAnim && _networkAnim->isLoaded() && _skeleton);
_anim.clear();
auto avatarSkeleton = getSkeleton();
// build a mapping from animation joint indices to avatar joint indices.
// by matching joints with the same name.
const HFMModel& animModel = _networkAnim->getHFMModel();
AnimSkeleton animSkeleton(animModel);
const int animJointCount = animSkeleton.getNumJoints();
const int avatarJointCount = avatarSkeleton->getNumJoints();
std::vector<int> animToAvatarJointIndexMap;
animToAvatarJointIndexMap.reserve(animJointCount);
for (int animJointIndex = 0; animJointIndex < animJointCount; animJointIndex++) {
QString animJointName = animSkeleton.getJointName(animJointIndex);
int avatarJointIndex = avatarSkeleton->nameToJointIndex(animJointName);
animToAvatarJointIndexMap.push_back(avatarJointIndex);
}
// build a mapping from animation joint indices to avatar joint indices by matching joints with the same name.
std::vector<int> avatarToAnimJointIndexMap = buildJointIndexMap(animSkeleton, *avatarSkeleton);
const int animFrameCount = animModel.animationFrames.size();
_anim.resize(animFrameCount);
for (int frame = 0; frame < animFrameCount; frame++) {
// find the size scale factor for translation in the animation.
const int avatarHipsParentIndex = avatarSkeleton->getParentIndex(avatarSkeleton->nameToJointIndex("Hips"));
const int animHipsParentIndex = animSkeleton.getParentIndex(animSkeleton.nameToJointIndex("Hips"));
const AnimPose& avatarHipsAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarSkeleton->nameToJointIndex("Hips"));
const AnimPose& animHipsAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animSkeleton.nameToJointIndex("Hips"));
// the get the units and the heights for the animation and the avatar
const float avatarUnitScale = extractScale(avatarSkeleton->getGeometryOffset()).y;
const float animationUnitScale = extractScale(animModel.offset).y;
const float avatarHeightInMeters = avatarUnitScale * avatarHipsAbsoluteDefaultPose.trans().y;
const float animHeightInMeters = animationUnitScale * animHipsAbsoluteDefaultPose.trans().y;
// get the parent scales for the avatar and the animation
float avatarHipsParentScale = 1.0f;
if (avatarHipsParentIndex >= 0) {
const AnimPose& avatarHipsParentAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarHipsParentIndex);
avatarHipsParentScale = avatarHipsParentAbsoluteDefaultPose.scale().y;
}
float animHipsParentScale = 1.0f;
if (animHipsParentIndex >= 0) {
const AnimPose& animationHipsParentAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animHipsParentIndex);
animHipsParentScale = animationHipsParentAbsoluteDefaultPose.scale().y;
}
const float EPSILON = 0.0001f;
float boneLengthScale = 1.0f;
// compute the ratios for the units, the heights in meters, and the parent scales
if ((fabsf(animHeightInMeters) > EPSILON) && (animationUnitScale > EPSILON) && (animHipsParentScale > EPSILON)) {
const float avatarToAnimationHeightRatio = avatarHeightInMeters / animHeightInMeters;
const float unitsRatio = 1.0f / (avatarUnitScale / animationUnitScale);
const float parentScaleRatio = 1.0f / (avatarHipsParentScale / animHipsParentScale);
boneLengthScale = avatarToAnimationHeightRatio * unitsRatio * parentScaleRatio;
}
for (int frame = 0; frame < animFrameCount; frame++) {
const HFMAnimationFrame& animFrame = animModel.animationFrames[frame];
// init all joints in animation to default pose
// this will give us a resonable result for bones in the avatar skeleton but not in the animation.
_anim[frame].reserve(avatarJointCount);
for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) {
_anim[frame].push_back(avatarSkeleton->getRelativeDefaultPose(avatarJointIndex));
// extract the full rotations from the animFrame (including pre and post rotations from the animModel).
std::vector<glm::quat> animRotations;
animRotations.reserve(animJointCount);
for (int i = 0; i < animJointCount; i++) {
animRotations.push_back(animModel.joints[i].preRotation * animFrame.rotations[i] * animModel.joints[i].postRotation);
}
for (int animJointIndex = 0; animJointIndex < animJointCount; animJointIndex++) {
int avatarJointIndex = animToAvatarJointIndexMap[animJointIndex];
// convert rotations into absolute frame
animSkeleton.convertRelativeRotationsToAbsolute(animRotations);
// skip joints that are in the animation but not in the avatar.
if (avatarJointIndex >= 0 && avatarJointIndex < avatarJointCount) {
// build absolute rotations for the avatar
std::vector<glm::quat> avatarRotations;
avatarRotations.reserve(avatarJointCount);
for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) {
int animJointIndex = avatarToAnimJointIndexMap[avatarJointIndex];
if (animJointIndex >= 0) {
// This joint is in both animation and avatar.
// Set the absolute rotation directly
avatarRotations.push_back(animRotations[animJointIndex]);
} else {
// This joint is NOT in the animation at all.
// Set it so that the default relative rotation remains unchanged.
glm::quat avatarRelativeDefaultRot = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex).rot();
glm::quat avatarParentAbsoluteRot;
int avatarParentJointIndex = avatarSkeleton->getParentIndex(avatarJointIndex);
if (avatarParentJointIndex >= 0) {
avatarParentAbsoluteRot = avatarRotations[avatarParentJointIndex];
}
avatarRotations.push_back(avatarParentAbsoluteRot * avatarRelativeDefaultRot);
}
}
// convert avatar rotations into relative frame
avatarSkeleton->convertAbsoluteRotationsToRelative(avatarRotations);
_anim[frame].reserve(avatarJointCount);
for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) {
const AnimPose& avatarDefaultPose = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex);
// copy scale over from avatar default pose
glm::vec3 relativeScale = avatarDefaultPose.scale();
glm::vec3 relativeTranslation;
int animJointIndex = avatarToAnimJointIndexMap[avatarJointIndex];
if (animJointIndex >= 0) {
// This joint is in both animation and avatar.
const glm::vec3& animTrans = animFrame.translations[animJointIndex];
const glm::quat& animRot = animFrame.rotations[animJointIndex];
const AnimPose& animPreRotPose = animSkeleton.getPreRotationPose(animJointIndex);
AnimPose animPostRotPose = animSkeleton.getPostRotationPose(animJointIndex);
AnimPose animRotPose(glm::vec3(1.0f), animRot, glm::vec3());
// adjust anim scale to equal the scale from the avatar joint.
// we do not support animated scale.
const AnimPose& avatarDefaultPose = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex);
animPostRotPose.scale() = avatarDefaultPose.scale();
// retarget translation from animation to avatar
const glm::vec3& animZeroTrans = animModel.animationFrames[0].translations[animJointIndex];
float boneLengthScale = 1.0f;
const float EPSILON = 0.0001f;
if (fabsf(glm::length(animZeroTrans)) > EPSILON) {
boneLengthScale = glm::length(avatarDefaultPose.trans()) / glm::length(animZeroTrans);
}
AnimPose animTransPose = AnimPose(glm::vec3(1.0f), glm::quat(), avatarDefaultPose.trans() + boneLengthScale * (animTrans - animZeroTrans));
_anim[frame][avatarJointIndex] = animTransPose * animPreRotPose * animRotPose * animPostRotPose;
relativeTranslation = avatarDefaultPose.trans() + boneLengthScale * (animTrans - animZeroTrans);
} else {
// This joint is NOT in the animation at all.
// preserve the default translation.
relativeTranslation = avatarDefaultPose.trans();
}
// build the final pose
_anim[frame].push_back(AnimPose(relativeScale, avatarRotations[avatarJointIndex], relativeTranslation));
}
}

View file

@ -298,10 +298,8 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
}
// harvest accumulated rotations and apply the average
for (int i = 0; i < (int)_relativePoses.size(); ++i) {
if (i == _hipsIndex) {
continue; // don't apply accumulators to hips
}
// don't apply accumulators to hips, or parents of hips
for (int i = (_hipsIndex+1); i < (int)_relativePoses.size(); ++i) {
if (_rotationAccumulators[i].size() > 0) {
_relativePoses[i].rot() = _rotationAccumulators[i].getAverage();
_rotationAccumulators[i].clear();
@ -865,7 +863,6 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
//virtual
const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) {
// allows solutionSource to be overridden by an animVar
auto solutionSource = animVars.lookup(_solutionSourceVar, (int)_solutionSource);

View file

@ -17,6 +17,9 @@
#include "AnimationLogging.h"
AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) {
_geometryOffset = hfmModel.offset;
// convert to std::vector of joints
std::vector<HFMJoint> joints;
joints.reserve(hfmModel.joints.size());
@ -149,8 +152,19 @@ void AnimSkeleton::convertAbsolutePosesToRelative(AnimPoseVec& poses) const {
}
}
void AnimSkeleton::convertRelativeRotationsToAbsolute(std::vector<glm::quat>& rotations) const {
// rotations start off relative and leave in absolute frame
int lastIndex = std::min((int)rotations.size(), _jointsSize);
for (int i = 0; i < lastIndex; ++i) {
int parentIndex = _parentIndices[i];
if (parentIndex != -1) {
rotations[i] = rotations[parentIndex] * rotations[i];
}
}
}
void AnimSkeleton::convertAbsoluteRotationsToRelative(std::vector<glm::quat>& rotations) const {
// poses start off absolute and leave in relative frame
// rotations start off absolute and leave in relative frame
int lastIndex = std::min((int)rotations.size(), _jointsSize);
for (int i = lastIndex - 1; i >= 0; --i) {
int parentIndex = _parentIndices[i];

View file

@ -36,6 +36,7 @@ public:
const AnimPoseVec& getRelativeDefaultPoses() const { return _relativeDefaultPoses; }
const AnimPose& getAbsoluteDefaultPose(int jointIndex) const;
const AnimPoseVec& getAbsoluteDefaultPoses() const { return _absoluteDefaultPoses; }
const glm::mat4& getGeometryOffset() const { return _geometryOffset; }
// get pre transform which should include FBX pre potations
const AnimPose& getPreRotationPose(int jointIndex) const;
@ -54,6 +55,7 @@ public:
void convertRelativePosesToAbsolute(AnimPoseVec& poses) const;
void convertAbsolutePosesToRelative(AnimPoseVec& poses) const;
void convertRelativeRotationsToAbsolute(std::vector<glm::quat>& rotations) const;
void convertAbsoluteRotationsToRelative(std::vector<glm::quat>& rotations) const;
void saveNonMirroredPoses(const AnimPoseVec& poses) const;
@ -83,6 +85,7 @@ protected:
std::vector<int> _mirrorMap;
QHash<QString, int> _jointIndicesByName;
std::vector<std::vector<HFMCluster>> _clusterBindMatrixOriginalValues;
glm::mat4 _geometryOffset;
// no copies
AnimSkeleton(const AnimSkeleton&) = delete;

View file

@ -1066,6 +1066,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
if (_enableInverseKinematics) {
_animVars.set("ikOverlayAlpha", 1.0f);
_animVars.set("splineIKEnabled", true);
_animVars.set("leftHandIKEnabled", true);
_animVars.set("rightHandIKEnabled", true);
_animVars.set("leftFootIKEnabled", true);
_animVars.set("rightFootIKEnabled", true);
_animVars.set("leftFootPoleVectorEnabled", true);
_animVars.set("rightFootPoleVectorEnabled", true);
} else {
_animVars.set("ikOverlayAlpha", 0.0f);
_animVars.set("splineIKEnabled", false);
@ -1878,15 +1885,13 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
};
std::shared_ptr<AnimInverseKinematics> ikNode = getAnimInverseKinematicsNode();
if (ikNode) {
for (int i = 0; i < (int)NumSecondaryControllerTypes; i++) {
int index = indexOfJoint(secondaryControllerJointNames[i]);
if (index >= 0) {
if (params.secondaryControllerFlags[i] & (uint8_t)ControllerFlags::Enabled) {
ikNode->setSecondaryTargetInRigFrame(index, params.secondaryControllerPoses[i]);
} else {
ikNode->clearSecondaryTarget(index);
}
for (int i = 0; i < (int)NumSecondaryControllerTypes; i++) {
int index = indexOfJoint(secondaryControllerJointNames[i]);
if ((index >= 0) && (ikNode)) {
if (params.secondaryControllerFlags[i] & (uint8_t)ControllerFlags::Enabled) {
ikNode->setSecondaryTargetInRigFrame(index, params.secondaryControllerPoses[i]);
} else {
ikNode->clearSecondaryTarget(index);
}
}
}

View file

@ -1537,6 +1537,7 @@ void Avatar::rigReady() {
buildUnscaledEyeHeightCache();
buildSpine2SplineRatioCache();
computeMultiSphereShapes();
buildSpine2SplineRatioCache();
}
// rig has been reset.

View file

@ -380,16 +380,26 @@ void OpenGLDisplayPlugin::customizeContext() {
scissorState->setScissorEnable(true);
{
#ifdef Q_OS_ANDROID
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB);
#else
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture);
_simplePipeline = gpu::Pipeline::create(program, scissorState);
_hudPipeline = gpu::Pipeline::create(program, blendState);
#endif
_simplePipeline = gpu::Pipeline::create(program, scissorState);
}
{
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::display_plugins::program::SrgbToLinear);
#ifdef Q_OS_ANDROID
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB);
#else
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaSRGBToLinear);
#endif
_presentPipeline = gpu::Pipeline::create(program, scissorState);
}
{
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture);
_hudPipeline = gpu::Pipeline::create(program, blendState);
}
{
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX);
_mirrorHUDPipeline = gpu::Pipeline::create(program, blendState);
@ -885,6 +895,7 @@ void OpenGLDisplayPlugin::updateCompositeFramebuffer() {
auto renderSize = glm::uvec2(getRecommendedRenderSize());
if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) {
_compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y));
// _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_SRGBA_32, renderSize.x, renderSize.y));
}
}

View file

@ -1,22 +0,0 @@
// OpenGLDisplayPlugin_present.frag
LAYOUT(binding=0) uniform sampler2D colorMap;
layout(location=0) in vec2 varTexCoord0;
layout(location=0) out vec4 outFragColor;
float sRGBFloatToLinear(float value) {
const float SRGB_ELBOW = 0.04045;
return mix(pow((value + 0.055) / 1.055, 2.4), value / 12.92, float(value <= SRGB_ELBOW));
}
vec3 colorToLinearRGB(vec3 srgb) {
return vec3(sRGBFloatToLinear(srgb.r), sRGBFloatToLinear(srgb.g), sRGBFloatToLinear(srgb.b));
}
void main(void) {
outFragColor.a = 1.0;
outFragColor.rgb = colorToLinearRGB(texture(colorMap, varTexCoord0).rgb);
}

View file

@ -1 +0,0 @@
VERTEX gpu::vertex::DrawUnitQuadTexcoord

View file

@ -73,14 +73,14 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID;
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>().data();
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity);
connect(pointerManager.data(), &PointerManager::hoverContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity);
connect(pointerManager.data(), &PointerManager::hoverEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity);
connect(pointerManager.data(), &PointerManager::triggerBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity);
connect(pointerManager.data(), &PointerManager::triggerContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity);
connect(pointerManager.data(), &PointerManager::triggerEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity);
connect(pointerManager.data(), &PointerManager::hoverBeginEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity);
connect(pointerManager.data(), &PointerManager::hoverContinueEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity);
connect(pointerManager.data(), &PointerManager::hoverEndEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity);
connect(pointerManager.data(), &PointerManager::triggerBeginEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
connect(pointerManager.data(), &PointerManager::triggerContinueEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity);
connect(pointerManager.data(), &PointerManager::triggerEndEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity);
// Forward mouse events to web entities
auto handlePointerEvent = [&](const QUuid& entityID, const PointerEvent& event) {
@ -93,10 +93,10 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
QMetaObject::invokeMethod(thisEntity.get(), "handlePointerEvent", Q_ARG(const PointerEvent&, event));
}
};
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;
auto entity = getEntity(entityID);
if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) {
@ -106,8 +106,8 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
QMetaObject::invokeMethod(thisEntity.get(), "hoverEnterEntity", Q_ARG(const PointerEvent&, event));
}
});
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent);
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
connect(entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;
auto entity = getEntity(entityID);
if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) {
@ -196,8 +196,8 @@ void EntityTreeRenderer::resetEntitiesScriptEngine() {
});
}
void EntityTreeRenderer::stopNonLocalEntityScripts() {
leaveNonLocalEntities();
void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
leaveDomainAndNonOwnedEntities();
// unload and stop the engine
if (_entitiesScriptEngine) {
QList<EntityItemID> entitiesWithEntityScripts = _entitiesScriptEngine->getListOfEntityScriptIDs();
@ -206,7 +206,7 @@ void EntityTreeRenderer::stopNonLocalEntityScripts() {
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
if (entityItem) {
if (!entityItem->isLocalEntity()) {
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
_entitiesScriptEngine->unloadEntityScript(entityID, true);
}
}
@ -214,8 +214,8 @@ void EntityTreeRenderer::stopNonLocalEntityScripts() {
}
}
void EntityTreeRenderer::clearNonLocalEntities() {
stopNonLocalEntityScripts();
void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
stopDomainAndNonOwnedEntities();
std::unordered_map<EntityItemID, EntityRendererPointer> savedEntities;
// remove all entities from the scene
@ -225,7 +225,7 @@ void EntityTreeRenderer::clearNonLocalEntities() {
for (const auto& entry : _entitiesInScene) {
const auto& renderer = entry.second;
const EntityItemPointer& entityItem = renderer->getEntity();
if (!entityItem->isLocalEntity()) {
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
renderer->removeFromScene(scene, transaction);
} else {
savedEntities[entry.first] = entry.second;
@ -239,7 +239,7 @@ void EntityTreeRenderer::clearNonLocalEntities() {
_layeredZones.clearNonLocalLayeredZones();
OctreeProcessor::clearNonLocalEntities();
OctreeProcessor::clearDomainAndNonOwnedEntities();
}
void EntityTreeRenderer::clear() {
@ -655,22 +655,22 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
return didUpdate;
}
void EntityTreeRenderer::leaveNonLocalEntities() {
void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() {
if (_tree && !_shuttingDown) {
QVector<EntityItemID> currentLocalEntitiesInside;
QVector<EntityItemID> currentEntitiesInsideToSave;
foreach (const EntityItemID& entityID, _currentEntitiesInside) {
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
if (!entityItem->isLocalEntity()) {
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
emit leaveEntity(entityID);
if (_entitiesScriptEngine) {
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
}
} else {
currentLocalEntitiesInside.push_back(entityID);
currentEntitiesInsideToSave.push_back(entityID);
}
}
_currentEntitiesInside = currentLocalEntitiesInside;
_currentEntitiesInside = currentEntitiesInsideToSave;
forceRecheckEntities();
}
}
@ -792,11 +792,11 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) {
}
}
std::pair<float, QUuid> EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
QUuid EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
// If we don't have a tree, or we're in the process of shutting down, then don't
// process these events.
if (!_tree || _shuttingDown) {
return { FLT_MAX, UNKNOWN_ENTITY_ID };
return UNKNOWN_ENTITY_ID;
}
PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent");
@ -805,11 +805,13 @@ std::pair<float, QUuid> EntityTreeRenderer::mousePressEvent(QMouseEvent* event)
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
EntityItemPointer entity;
if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) {
auto properties = entity->getProperties();
QString urlString = properties.getHref();
QUrl url = QUrl(urlString, QUrl::StrictMode);
if (url.isValid() && !url.isEmpty()){
DependencyManager::get<AddressManager>()->handleLookupString(urlString);
if (!EntityTree::areEntityClicksCaptured()) {
auto properties = entity->getProperties();
QString urlString = properties.getHref();
QUrl url = QUrl(urlString, QUrl::StrictMode);
if (url.isValid() && !url.isEmpty()) {
DependencyManager::get<AddressManager>()->handleLookupString(urlString);
}
}
glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult);
@ -827,10 +829,10 @@ std::pair<float, QUuid> EntityTreeRenderer::mousePressEvent(QMouseEvent* event)
_lastPointerEvent = pointerEvent;
_lastPointerEventValid = true;
return { rayPickResult.distance, rayPickResult.entityID };
return rayPickResult.entityID;
}
emit entityScriptingInterface->mousePressOffEntity();
return { FLT_MAX, UNKNOWN_ENTITY_ID };
return UNKNOWN_ENTITY_ID;
}
void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) {

View file

@ -87,14 +87,14 @@ public:
virtual void init() override;
/// clears the tree
virtual void clearNonLocalEntities() override;
virtual void clearDomainAndNonOwnedEntities() override;
virtual void clear() override;
/// reloads the entity scripts, calling unload and preload
void reloadEntityScripts();
// event handles which may generate entity related events
std::pair<float, QUuid> mousePressEvent(QMouseEvent* event);
QUuid mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseDoublePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
@ -170,7 +170,7 @@ private:
bool findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar = nullptr);
bool applyLayeredZones();
void stopNonLocalEntityScripts();
void stopDomainAndNonOwnedEntities();
void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false);
@ -179,7 +179,7 @@ private:
QScriptValueList createEntityArgs(const EntityItemID& entityID);
bool checkEnterLeaveEntities();
void leaveNonLocalEntities();
void leaveDomainAndNonOwnedEntities();
void leaveAllEntities();
void forceRecheckEntities();

View file

@ -29,6 +29,13 @@ gpu::PipelinePointer PolyLineEntityRenderer::_glowPipeline = nullptr;
static const QUrl DEFAULT_POLYLINE_TEXTURE = PathUtils::resourcesUrl("images/paintStroke.png");
#if defined(USE_GLES)
static bool DISABLE_DEFERRED = true;
#else
static const QString RENDER_FORWARD{ "HIFI_RENDER_FORWARD" };
static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
#endif
PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {
_texture = DependencyManager::get<TextureCache>()->getTexture(DEFAULT_POLYLINE_TEXTURE);
@ -44,7 +51,13 @@ PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity)
void PolyLineEntityRenderer::buildPipeline() {
// FIXME: opaque pipeline
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::entities_renderer::program::paintStroke);
gpu::ShaderPointer program;
if (DISABLE_DEFERRED) {
program = gpu::Shader::createProgram(shader::entities_renderer::program::paintStroke_forward);
} else {
program = gpu::Shader::createProgram(shader::entities_renderer::program::paintStroke);
}
{
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setCullMode(gpu::State::CullMode::CULL_NONE);
@ -170,18 +183,19 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo
void PolyLineEntityRenderer::updateGeometry() {
int maxNumVertices = std::min(_points.length(), _normals.length());
bool doesStrokeWidthVary = false;
if (_widths.size() >= 0) {
if (_widths.size() > 0) {
float prevWidth = _widths[0];
for (int i = 1; i < maxNumVertices; i++) {
float width = PolyLineEntityItem::DEFAULT_LINE_WIDTH;
if (i < _widths.length()) {
width = _widths[i];
}
if (width != _widths[i - 1]) {
float width = i < _widths.length() ? _widths[i] : PolyLineEntityItem::DEFAULT_LINE_WIDTH;
if (width != prevWidth) {
doesStrokeWidthVary = true;
break;
}
if (i > _widths.length() + 1) {
break;
}
prevWidth = width;
}
}
@ -193,12 +207,13 @@ void PolyLineEntityRenderer::updateGeometry() {
std::vector<PolylineVertex> vertices;
vertices.reserve(maxNumVertices);
for (int i = 0; i < maxNumVertices; i++) {
// Position
glm::vec3 point = _points[i];
// uCoord
float width = i < _widths.size() ? _widths[i] : PolyLineEntityItem::DEFAULT_LINE_WIDTH;
if (i > 0) { // First uCoord is 0.0f
if (!_isUVModeStretch) {
accumulatedDistance += glm::distance(point, _points[i - 1]);

View file

@ -30,6 +30,13 @@ using namespace render::entities;
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
static const float SPHERE_ENTITY_SCALE = 0.5f;
#if defined(USE_GLES)
static bool DISABLE_DEFERRED = true;
#else
static const QString RENDER_FORWARD{ "HIFI_RENDER_FORWARD" };
static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
#endif
static_assert(shader::render_utils::program::simple != 0, "Validate simple program exists");
static_assert(shader::render_utils::program::simple_transparent != 0, "Validate simple transparent program exists");
@ -276,7 +283,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
// FIXME, support instanced multi-shape rendering using multidraw indirect
outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
render::ShapePipelinePointer pipeline;
if (_renderLayer == RenderLayer::WORLD) {
if (_renderLayer == RenderLayer::WORLD && !DISABLE_DEFERRED) {
pipeline = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
} else {
pipeline = outColor.a < 1.0f ? geometryCache->getForwardTransparentShapePipeline() : geometryCache->getForwardOpaqueShapePipeline();

View file

@ -0,0 +1 @@
VERTEX paintStroke

View file

@ -0,0 +1,35 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// paintStroke.frag
// fragment shader
//
// Created by Eric Levin on 8/10/2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include paintStroke.slh@>
<$declarePolyLineBuffers()$>
LAYOUT(binding=0) uniform sampler2D _texture;
layout(location=0) in vec3 _normalWS;
layout(location=1) in vec2 _texCoord;
layout(location=2) in vec4 _color;
layout(location=3) in float _distanceFromCenter;
layout(location=0) out vec4 _fragColor0;
void main(void) {
vec4 texel = texture(_texture, _texCoord);
int frontCondition = 1 - 2 * int(gl_FrontFacing);
vec3 color = _color.rgb * texel.rgb;
float alpha = texel.a * _color.a;
alpha *= mix(1.0, pow(1.0 - abs(_distanceFromCenter), 10.0), _polylineData.faceCameraGlow.y);
_fragColor0 = vec4(color, alpha);
}

View file

@ -78,13 +78,14 @@ OctreeElementPointer EntityTree::createNewElement(unsigned char* octalCode) {
return std::static_pointer_cast<OctreeElement>(newElement);
}
void EntityTree::eraseNonLocalEntities() {
void EntityTree::eraseDomainAndNonOwnedEntities() {
emit clearingEntities();
if (_simulation) {
// local entities are not in the simulation, so we clear ALL
_simulation->clearEntities();
}
this->withWriteLock([&] {
QHash<EntityItemID, EntityItemPointer> savedEntities;
// NOTE: lock the Tree first, then lock the _entityMap.
@ -93,10 +94,10 @@ void EntityTree::eraseNonLocalEntities() {
foreach(EntityItemPointer entity, _entityMap) {
EntityTreeElementPointer element = entity->getElement();
if (element) {
element->cleanupNonLocalEntities();
element->cleanupDomainAndNonOwnedEntities();
}
if (entity->isLocalEntity()) {
if (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID())) {
savedEntities[entity->getEntityItemID()] = entity;
} else {
int32_t spaceIndex = entity->getSpaceIndex();
@ -114,15 +115,16 @@ void EntityTree::eraseNonLocalEntities() {
{
QWriteLocker locker(&_needsParentFixupLock);
QVector<EntityItemWeakPointer> localEntitiesNeedsParentFixup;
QVector<EntityItemWeakPointer> needParentFixup;
foreach (EntityItemWeakPointer entityItem, _needsParentFixup) {
if (!entityItem.expired() && entityItem.lock()->isLocalEntity()) {
localEntitiesNeedsParentFixup.push_back(entityItem);
auto entity = entityItem.lock();
if (entity && (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID()))) {
needParentFixup.push_back(entityItem);
}
}
_needsParentFixup = localEntitiesNeedsParentFixup;
_needsParentFixup = needParentFixup;
}
}
@ -2972,6 +2974,7 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const {
std::function<QObject*(const QUuid&)> EntityTree::_getEntityObjectOperator = nullptr;
std::function<QSizeF(const QUuid&, const QString&)> EntityTree::_textSizeOperator = nullptr;
std::function<bool()> EntityTree::_areEntityClicksCapturedOperator = nullptr;
QObject* EntityTree::getEntityObject(const QUuid& id) {
if (_getEntityObjectOperator) {
@ -2987,6 +2990,13 @@ QSizeF EntityTree::textSize(const QUuid& id, const QString& text) {
return QSizeF(0.0f, 0.0f);
}
bool EntityTree::areEntityClicksCaptured() {
if (_areEntityClicksCapturedOperator) {
return _areEntityClicksCapturedOperator();
}
return false;
}
void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
MovingEntitiesOperator& moveOperator, bool force, bool tellServer) {
// if the queryBox has changed, tell the entity-server

View file

@ -75,7 +75,7 @@ public:
}
virtual void eraseNonLocalEntities() override;
virtual void eraseDomainAndNonOwnedEntities() override;
virtual void eraseAllOctreeElements(bool createNewRoot = true) override;
virtual void readBitstreamToTree(const unsigned char* bitstream,
@ -255,6 +255,7 @@ public:
QByteArray computeNonce(const QString& certID, const QString ownerKey);
bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id);
QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); }
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
void swapStaleProxies(std::vector<int>& proxies) { proxies.swap(_staleProxies); }
@ -268,6 +269,9 @@ public:
static void setTextSizeOperator(std::function<QSizeF(const QUuid&, const QString&)> textSizeOperator) { _textSizeOperator = textSizeOperator; }
static QSizeF textSize(const QUuid& id, const QString& text);
static void setEntityClicksCapturedOperator(std::function<bool()> areEntityClicksCapturedOperator) { _areEntityClicksCapturedOperator = areEntityClicksCapturedOperator; }
static bool areEntityClicksCaptured();
std::map<QString, QString> getNamedPaths() const { return _namedPaths; }
void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
@ -378,6 +382,7 @@ private:
static std::function<QObject*(const QUuid&)> _getEntityObjectOperator;
static std::function<QSizeF(const QUuid&, const QString&)> _textSizeOperator;
static std::function<bool()> _areEntityClicksCapturedOperator;
std::vector<int32_t> _staleProxies;

View file

@ -697,11 +697,11 @@ EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemI
return foundEntity;
}
void EntityTreeElement::cleanupNonLocalEntities() {
void EntityTreeElement::cleanupDomainAndNonOwnedEntities() {
withWriteLock([&] {
EntityItems savedEntities;
foreach(EntityItemPointer entity, _entityItems) {
if (!entity->isLocalEntity()) {
if (!(entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
entity->preDelete();
entity->_element = NULL;
} else {

View file

@ -190,7 +190,7 @@ public:
EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const;
void getEntitiesInside(const AACube& box, QVector<EntityItemPointer>& foundEntities);
void cleanupNonLocalEntities();
void cleanupDomainAndNonOwnedEntities();
void cleanupEntities(); /// called by EntityTree on cleanup this will free all entities
bool removeEntityItem(EntityItemPointer entity, bool deletion = false);

View file

@ -164,7 +164,7 @@ protected:
int _lastKnownCurrentFrame{-1};
glm::u8vec3 _color;
glm::vec3 _modelScale;
glm::vec3 _modelScale { 1.0f };
QString _modelURL;
bool _relayParentJoints;
bool _groupCulled { false };

View file

@ -167,7 +167,6 @@ glm::mat4 getGlobalTransform(const QMultiMap<QString, QString>& _connectionParen
}
}
}
return globalTransform;
}
@ -436,6 +435,8 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
hfmModel.originalURL = url;
float unitScaleFactor = 1.0f;
glm::quat upAxisZRotation;
bool applyUpAxisZRotation = false;
glm::vec3 ambientColor;
QString hifiGlobalNodeID;
unsigned int meshIndex = 0;
@ -473,11 +474,22 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
if (subobject.name == propertyName) {
static const QVariant UNIT_SCALE_FACTOR = QByteArray("UnitScaleFactor");
static const QVariant AMBIENT_COLOR = QByteArray("AmbientColor");
static const QVariant UP_AXIS = QByteArray("UpAxis");
const auto& subpropName = subobject.properties.at(0);
if (subpropName == UNIT_SCALE_FACTOR) {
unitScaleFactor = subobject.properties.at(index).toFloat();
} else if (subpropName == AMBIENT_COLOR) {
ambientColor = getVec3(subobject.properties, index);
} else if (subpropName == UP_AXIS) {
constexpr int UP_AXIS_Y = 1;
constexpr int UP_AXIS_Z = 2;
int upAxis = subobject.properties.at(index).toInt();
if (upAxis == UP_AXIS_Y) {
// No update necessary, y up is the default
} else if (upAxis == UP_AXIS_Z) {
upAxisZRotation = glm::angleAxis(glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
applyUpAxisZRotation = true;
}
}
}
}
@ -1269,9 +1281,11 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
joint.geometricScaling = fbxModel.geometricScaling;
joint.isSkeletonJoint = fbxModel.isLimbNode;
hfmModel.hasSkeletonJoints = (hfmModel.hasSkeletonJoints || joint.isSkeletonJoint);
if (applyUpAxisZRotation && joint.parentIndex == -1) {
joint.rotation *= upAxisZRotation;
joint.translation = upAxisZRotation * joint.translation;
}
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
if (joint.parentIndex == -1) {
joint.transform = hfmModel.offset * glm::translate(joint.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
@ -1664,6 +1678,14 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
}
}
if (applyUpAxisZRotation) {
hfmModelPtr->meshExtents.transform(glm::mat4_cast(upAxisZRotation));
hfmModelPtr->bindExtents.transform(glm::mat4_cast(upAxisZRotation));
for (auto &mesh : hfmModelPtr->meshes) {
mesh.modelTransform *= glm::mat4_cast(upAxisZRotation);
mesh.meshExtents.transform(glm::mat4_cast(upAxisZRotation));
}
}
return hfmModelPtr;
}

View file

@ -739,8 +739,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
//Build dependencies
QVector<QVector<int>> nodeDependencies(_file.nodes.size());
int nodecount = 0;
bool hasChildren = false;
foreach(auto &node, _file.nodes) {
//nodes_transforms.push_back(getModelTransform(node));
hasChildren |= !node.children.isEmpty();
foreach(int child, node.children) nodeDependencies[child].push_back(nodecount);
nodecount++;
}
@ -763,17 +765,25 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
nodecount++;
}
//Build default joints
hfmModel.joints.resize(1);
hfmModel.joints[0].parentIndex = -1;
hfmModel.joints[0].distanceToParent = 0;
hfmModel.joints[0].translation = glm::vec3(0, 0, 0);
hfmModel.joints[0].rotationMin = glm::vec3(0, 0, 0);
hfmModel.joints[0].rotationMax = glm::vec3(0, 0, 0);
hfmModel.joints[0].name = "OBJ";
hfmModel.joints[0].isSkeletonJoint = true;
hfmModel.jointIndices["x"] = 1;
HFMJoint joint;
joint.isSkeletonJoint = true;
joint.bindTransformFoundInCluster = false;
joint.distanceToParent = 0;
joint.parentIndex = -1;
hfmModel.joints.resize(_file.nodes.size());
hfmModel.jointIndices["x"] = _file.nodes.size();
int jointInd = 0;
for (auto& node : _file.nodes) {
int size = node.transforms.size();
if (hasChildren) { size--; }
joint.preTransform = glm::mat4(1);
for (int i = 0; i < size; i++) {
joint.preTransform = node.transforms[i] * joint.preTransform;
}
joint.name = node.name;
hfmModel.joints[jointInd] = joint;
jointInd++;
}
//Build materials
QVector<QString> materialIDs;
@ -804,7 +814,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
hfmModel.meshes.append(HFMMesh());
HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1];
HFMCluster cluster;
cluster.jointIndex = 0;
cluster.jointIndex = nodecount;
cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
@ -907,7 +917,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
int stride = (accessor.type == GLTFAccessorType::VEC4) ? 4 : 3;
for (int n = 0; n < tangents.size() - 3; n += stride) {
float tanW = stride == 4 ? tangents[n + 3] : 1;
mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tangents[n + 2]));
mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tanW * tangents[n + 2]));
}
} else if (key == "TEXCOORD_0") {
QVector<float> texcoords;
@ -957,16 +967,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
mesh.meshExtents.addPoint(vertex);
hfmModel.meshExtents.addPoint(vertex);
}
// since mesh.modelTransform seems to not have any effect I apply the transformation the model
for (int h = 0; h < mesh.vertices.size(); h++) {
glm::vec4 ver = glm::vec4(mesh.vertices[h], 1);
if (node.transforms.size() > 0) {
ver = node.transforms[0] * ver; // for model dependency should multiply also by parents transforms?
mesh.vertices[h] = glm::vec3(ver[0], ver[1], ver[2]);
}
}
mesh.meshIndex = hfmModel.meshes.size();
}

View file

@ -16,20 +16,39 @@
// YCoCg =====> Luma (Y) chrominance green (Cg) and chrominance orange (Co)
// https://software.intel.com/en-us/node/503873
// sRGB ====> Linear
float color_scalar_sRGBToLinear(float value) {
const float SRGB_ELBOW = 0.04045;
return mix(pow((value + 0.055) / 1.055, 2.4), value / 12.92, float(value <= SRGB_ELBOW));
// Same as pow(value, 2.2)
return mix(pow((value + 0.055) / 1.055, 2.4), value / 12.92, float(value <= 0.04045));
}
vec3 color_sRGBToLinear(vec3 srgb) {
return vec3(color_scalar_sRGBToLinear(srgb.r), color_scalar_sRGBToLinear(srgb.g), color_scalar_sRGBToLinear(srgb.b));
// return vec3(color_scalar_sRGBToLinear(srgb.r), color_scalar_sRGBToLinear(srgb.g), color_scalar_sRGBToLinear(srgb.b));
// Same as pow(value, 2.2)
return mix(pow((srgb + vec3(0.055)) / vec3(1.055), vec3(2.4)), srgb / vec3(12.92), vec3(lessThanEqual(srgb, vec3(0.04045))));
}
vec4 color_sRGBAToLinear(vec4 srgba) {
return vec4(color_sRGBToLinear(srgba.xyz), srgba.w);
}
// Linear ====> sRGB
float color_scalar_LinearTosRGB(float value) {
// Same as return pow(value, 1/2.2)
return mix(1.055 * pow(value, 0.41666) - 0.055, value * 12.92, float(value < 0.0031308));
}
vec3 color_LinearTosRGB(vec3 lrgb) {
// Same as return pow(lrgb, 1/2.2)
// return vec3(color_scalar_LinearTosRGB(lrgb.r), color_scalar_LinearTosRGB(lrgb.g), color_scalar_LinearTosRGB(lrgb.b));
return mix(vec3(1.055) * pow(vec3(lrgb), vec3(0.41666)) - vec3(0.055), vec3(lrgb) * vec3(12.92), vec3(lessThan(lrgb, vec3(0.0031308))));
}
vec4 color_LinearTosRGBA(vec4 lrgba) {
return vec4(color_LinearTosRGB(lrgba.xyz), lrgba.w);
}
vec3 color_LinearToYCoCg(vec3 rgb) {
// Y = R/4 + G/2 + B/4
// Co = R/2 - B/2

View file

@ -0,0 +1,26 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// DrawTextureGammaLinearToSRGB.frag
//
// Draw texture 0 fetched at texcoord.xy, and apply linear to sRGB color space conversion
//
// Created by Sam Gateau on 2/24/2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Color.slh@>
LAYOUT(binding=0) uniform sampler2D colorMap;
layout(location=0) in vec2 varTexCoord0;
layout(location=0) out vec4 outFragColor;
void main(void) {
outFragColor = color_LinearTosRGBA(texture(colorMap, varTexCoord0));
}

View file

@ -0,0 +1 @@
VERTEX DrawUnitQuadTexcoord

View file

@ -0,0 +1,26 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// DrawTextureGammaSRGBToLinear.frag
//
// Draw texture 0 fetched at texcoord.xy, and apply sRGB to Linear color space conversion
//
// Created by Sam Gateau on 2/24/2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Color.slh@>
LAYOUT(binding=0) uniform sampler2D colorMap;
layout(location=0) in vec2 varTexCoord0;
layout(location=0) out vec4 outFragColor;
void main(void) {
outFragColor = color_sRGBAToLinear(texture(colorMap, varTexCoord0));
}

View file

@ -0,0 +1 @@
VERTEX DrawUnitQuadTexcoord

View file

@ -117,7 +117,7 @@ namespace baker {
class BakerEngineBuilder {
public:
using Input = VaryingSet2<hfm::Model::Pointer, QVariantHash>;
using Input = VaryingSet2<hfm::Model::Pointer, GeometryMappingPair>;
using Output = VaryingSet2<hfm::Model::Pointer, MaterialMapping>;
using JobModel = Task::ModelIO<BakerEngineBuilder, Input, Output>;
void build(JobModel& model, const Varying& input, Varying& output) {
@ -169,7 +169,7 @@ namespace baker {
}
};
Baker::Baker(const hfm::Model::Pointer& hfmModel, const QVariantHash& mapping) :
Baker::Baker(const hfm::Model::Pointer& hfmModel, const GeometryMappingPair& mapping) :
_engine(std::make_shared<Engine>(BakerEngineBuilder::JobModel::create("Baker"), std::make_shared<BakeContext>())) {
_engine->feedInput<BakerEngineBuilder::Input>(0, hfmModel);
_engine->feedInput<BakerEngineBuilder::Input>(1, mapping);

View file

@ -17,13 +17,14 @@
#include <hfm/HFM.h>
#include "Engine.h"
#include "BakerTypes.h"
#include "ParseMaterialMappingTask.h"
namespace baker {
class Baker {
public:
Baker(const hfm::Model::Pointer& hfmModel, const QVariantHash& mapping);
Baker(const hfm::Model::Pointer& hfmModel, const GeometryMappingPair& mapping);
void run();

View file

@ -12,6 +12,7 @@
#ifndef hifi_BakerTypes_h
#define hifi_BakerTypes_h
#include <QUrl>
#include <hfm/HFM.h>
namespace baker {
@ -35,6 +36,7 @@ namespace baker {
using TangentsPerBlendshape = std::vector<std::vector<glm::vec3>>;
using MeshIndicesToModelNames = QHash<int, QString>;
using GeometryMappingPair = std::pair<QUrl, QVariantHash>;
};
#endif // hifi_BakerTypes_h

View file

@ -10,7 +10,9 @@
#include "ModelBakerLogging.h"
void ParseMaterialMappingTask::run(const baker::BakeContextPointer& context, const Input& mapping, Output& output) {
void ParseMaterialMappingTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
const auto& url = input.first;
const auto& mapping = input.second;
MaterialMapping materialMapping;
auto mappingIter = mapping.find("materialMap");
@ -59,14 +61,13 @@ void ParseMaterialMappingTask::run(const baker::BakeContextPointer& context, con
{
NetworkMaterialResourcePointer materialResource = NetworkMaterialResourcePointer(new NetworkMaterialResource(), [](NetworkMaterialResource* ptr) { ptr->deleteLater(); });
materialResource->moveToThread(qApp->thread());
// TODO: add baseURL to allow FSTs to reference relative files next to them
materialResource->parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument(mappingValue), QUrl());
materialResource->parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument(mappingValue), url);
materialMapping.push_back(std::pair<std::string, NetworkMaterialResourcePointer>(mapping.toStdString(), materialResource));
}
} else if (mappingJSON.isString()) {
auto mappingValue = mappingJSON.toString();
materialMapping.push_back(std::pair<std::string, NetworkMaterialResourcePointer>(mapping.toStdString(), MaterialCache::instance().getMaterial(mappingValue)));
materialMapping.push_back(std::pair<std::string, NetworkMaterialResourcePointer>(mapping.toStdString(), MaterialCache::instance().getMaterial(url.resolved(mappingValue))));
}
}
}

View file

@ -14,12 +14,13 @@
#include <hfm/HFM.h>
#include "Engine.h"
#include "BakerTypes.h"
#include <material-networking/MaterialCache.h>
class ParseMaterialMappingTask {
public:
using Input = QVariantHash;
using Input = baker::GeometryMappingPair;
using Output = MaterialMapping;
using JobModel = baker::Job::ModelIO<ParseMaterialMappingTask, Input, Output>;

View file

@ -58,7 +58,7 @@ void PrepareJointsTask::run(const baker::BakeContextPointer& context, const Inpu
auto& jointIndices = output.edit2();
// Get joint renames
auto jointNameMapping = getJointNameMapping(mapping);
auto jointNameMapping = getJointNameMapping(mapping.second);
// Apply joint metadata from FST file mappings
for (const auto& jointIn : jointsIn) {
jointsOut.push_back(jointIn);
@ -73,7 +73,7 @@ void PrepareJointsTask::run(const baker::BakeContextPointer& context, const Inpu
}
// Get joint rotation offsets from FST file mappings
auto offsets = getJointRotationOffsets(mapping);
auto offsets = getJointRotationOffsets(mapping.second);
for (auto itr = offsets.begin(); itr != offsets.end(); itr++) {
QString jointName = itr.key();
int jointIndex = jointIndices.value(jointName) - 1;

View file

@ -17,10 +17,11 @@
#include <hfm/HFM.h>
#include "Engine.h"
#include "BakerTypes.h"
class PrepareJointsTask {
public:
using Input = baker::VaryingSet2<std::vector<hfm::Joint>, QVariantHash /*mapping*/>;
using Input = baker::VaryingSet2<std::vector<hfm::Joint>, baker::GeometryMappingPair /*mapping*/>;
using Output = baker::VaryingSet3<std::vector<hfm::Joint>, QMap<int, glm::quat> /*jointRotationOffsets*/, QHash<QString, int> /*jointIndices*/>;
using JobModel = baker::Job::ModelIO<PrepareJointsTask, Input, Output>;

View file

@ -35,11 +35,13 @@ class GeometryReader;
class GeometryExtra {
public:
const QVariantHash& mapping;
const GeometryMappingPair& mapping;
const QUrl& textureBaseUrl;
bool combineParts;
};
int geometryMappingPairTypeId = qRegisterMetaType<GeometryMappingPair>("GeometryMappingPair");
// From: https://stackoverflow.com/questions/41145012/how-to-hash-qvariant
class QVariantHasher {
public:
@ -78,7 +80,8 @@ namespace std {
struct hash<GeometryExtra> {
size_t operator()(const GeometryExtra& geometryExtra) const {
size_t result = 0;
hash_combine(result, geometryExtra.mapping, geometryExtra.textureBaseUrl, geometryExtra.combineParts);
hash_combine(result, geometryExtra.mapping.first, geometryExtra.mapping.second, geometryExtra.textureBaseUrl,
geometryExtra.combineParts);
return result;
}
};
@ -151,7 +154,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
}
auto modelCache = DependencyManager::get<ModelCache>();
GeometryExtra extra { _mapping, _textureBaseUrl, false };
GeometryExtra extra { GeometryMappingPair(_url, _mapping), _textureBaseUrl, false };
// Get the raw GeometryResource
_geometryResource = modelCache->getResource(url, QUrl(), &extra, std::hash<GeometryExtra>()(extra)).staticCast<GeometryResource>();
@ -191,7 +194,7 @@ void GeometryMappingResource::onGeometryMappingLoaded(bool success) {
class GeometryReader : public QRunnable {
public:
GeometryReader(const ModelLoader& modelLoader, QWeakPointer<Resource>& resource, const QUrl& url, const QVariantHash& mapping,
GeometryReader(const ModelLoader& modelLoader, QWeakPointer<Resource>& resource, const QUrl& url, const GeometryMappingPair& mapping,
const QByteArray& data, bool combineParts, const QString& webMediaType) :
_modelLoader(modelLoader), _resource(resource), _url(url), _mapping(mapping), _data(data), _combineParts(combineParts), _webMediaType(webMediaType) {
@ -204,7 +207,7 @@ private:
ModelLoader _modelLoader;
QWeakPointer<Resource> _resource;
QUrl _url;
QVariantHash _mapping;
GeometryMappingPair _mapping;
QByteArray _data;
bool _combineParts;
QString _webMediaType;
@ -244,7 +247,7 @@ void GeometryReader::run() {
}
HFMModel::Pointer hfmModel;
QVariantHash serializerMapping = _mapping;
QVariantHash serializerMapping = _mapping.second;
serializerMapping["combineParts"] = _combineParts;
if (_url.path().toLower().endsWith(".gz")) {
@ -270,15 +273,14 @@ void GeometryReader::run() {
}
// Add scripts to hfmModel
if (!_mapping.value(SCRIPT_FIELD).isNull()) {
QVariantList scripts = _mapping.values(SCRIPT_FIELD);
if (!serializerMapping.value(SCRIPT_FIELD).isNull()) {
QVariantList scripts = serializerMapping.values(SCRIPT_FIELD);
for (auto &script : scripts) {
hfmModel->scripts.push_back(script.toString());
}
}
QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition",
Q_ARG(HFMModel::Pointer, hfmModel), Q_ARG(QVariantHash, _mapping));
Q_ARG(HFMModel::Pointer, hfmModel), Q_ARG(GeometryMappingPair, _mapping));
} catch (const std::exception&) {
auto resource = _resource.toStrongRef();
if (resource) {
@ -312,17 +314,17 @@ public:
void setExtra(void* extra) override;
protected:
Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, QVariantHash mapping);
Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, const GeometryMappingPair& mapping);
private:
ModelLoader _modelLoader;
QVariantHash _mapping;
GeometryMappingPair _mapping;
bool _combineParts;
};
void GeometryDefinitionResource::setExtra(void* extra) {
const GeometryExtra* geometryExtra = static_cast<const GeometryExtra*>(extra);
_mapping = geometryExtra ? geometryExtra->mapping : QVariantHash();
_mapping = geometryExtra ? geometryExtra->mapping : GeometryMappingPair(QUrl(), QVariantHash());
_textureBaseUrl = geometryExtra ? resolveTextureBaseUrl(_url, geometryExtra->textureBaseUrl) : QUrl();
_combineParts = geometryExtra ? geometryExtra->combineParts : true;
}
@ -335,7 +337,7 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, _self, _effectiveBaseURL, _mapping, data, _combineParts, _request->getWebMediaType()));
}
void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel, QVariantHash mapping) {
void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const GeometryMappingPair& mapping) {
// Do processing on the model
baker::Baker modelBaker(hfmModel, mapping);
modelBaker.run();
@ -398,7 +400,7 @@ QSharedPointer<Resource> ModelCache::createResourceCopy(const QSharedPointer<Res
}
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url,
const QVariantHash& mapping, const QUrl& textureBaseUrl) {
const GeometryMappingPair& mapping, const QUrl& textureBaseUrl) {
bool combineParts = true;
GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts };
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash<GeometryExtra>()(geometryExtra)).staticCast<GeometryResource>();
@ -411,7 +413,8 @@ GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url,
}
GeometryResource::Pointer ModelCache::getCollisionGeometryResource(const QUrl& url,
const QVariantHash& mapping, const QUrl& textureBaseUrl) {
const GeometryMappingPair& mapping,
const QUrl& textureBaseUrl) {
bool combineParts = false;
GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts };
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash<GeometryExtra>()(geometryExtra)).staticCast<GeometryResource>();

View file

@ -26,6 +26,9 @@ class MeshPart;
class GeometryMappingResource;
using GeometryMappingPair = std::pair<QUrl, QVariantHash>;
Q_DECLARE_METATYPE(GeometryMappingPair)
class Geometry {
public:
using Pointer = std::shared_ptr<Geometry>;
@ -145,11 +148,13 @@ class ModelCache : public ResourceCache, public Dependency {
public:
GeometryResource::Pointer getGeometryResource(const QUrl& url,
const QVariantHash& mapping = QVariantHash(),
const GeometryMappingPair& mapping =
GeometryMappingPair(QUrl(), QVariantHash()),
const QUrl& textureBaseUrl = QUrl());
GeometryResource::Pointer getCollisionGeometryResource(const QUrl& url,
const QVariantHash& mapping = QVariantHash(),
const GeometryMappingPair& mapping =
GeometryMappingPair(QUrl(), QVariantHash()),
const QUrl& textureBaseUrl = QUrl());
protected:

View file

@ -353,16 +353,19 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
// We've seen this extra info before
resource = resourcesWithExtraHashIter.value().lock();
} else if (resourcesWithExtraHash.size() > 0.0f) {
// We haven't seen this extra info before, but we've already downloaded the resource. We need a new copy of this object (with any old hash).
resource = createResourceCopy(resourcesWithExtraHash.begin().value().lock());
resource->setExtra(extra);
resource->setExtraHash(extraHash);
resource->setSelf(resource);
resource->setCache(this);
resource->moveToThread(qApp->thread());
connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize);
resourcesWithExtraHash.insert(extraHash, resource);
resource->ensureLoading();
auto oldResource = resourcesWithExtraHash.begin().value().lock();
if (oldResource) {
// We haven't seen this extra info before, but we've already downloaded the resource. We need a new copy of this object (with any old hash).
resource = createResourceCopy(oldResource);
resource->setExtra(extra);
resource->setExtraHash(extraHash);
resource->setSelf(resource);
resource->setCache(this);
resource->moveToThread(qApp->thread());
connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize);
resourcesWithExtraHash.insert(extraHash, resource);
resource->ensureLoading();
}
}
}
if (resource) {

View file

@ -31,7 +31,6 @@ using namespace udt;
using namespace std::chrono;
Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::unique_ptr<CongestionControl> congestionControl) :
QObject(parentSocket),
_parentSocket(parentSocket),
_destination(destination),
_congestionControl(move(congestionControl))

View file

@ -251,7 +251,10 @@ Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr, bool fi
auto congestionControl = _ccFactory->create();
congestionControl->setMaxBandwidth(_maxBandwidth);
auto connection = std::unique_ptr<Connection>(new Connection(this, sockAddr, std::move(congestionControl)));
if (QThread::currentThread() != thread()) {
qCDebug(networking) << "Moving new Connection to NodeList thread";
connection->moveToThread(thread());
}
// allow higher-level classes to find out when connections have completed a handshake
QObject::connect(connection.get(), &Connection::receiverHandshakeRequestComplete,
this, &Socket::clientHandshakeRequestComplete);

View file

@ -149,7 +149,7 @@ public:
OctreeElementPointer getRoot() { return _rootElement; }
virtual void eraseNonLocalEntities() { _isDirty = true; };
virtual void eraseDomainAndNonOwnedEntities() { _isDirty = true; };
virtual void eraseAllOctreeElements(bool createNewRoot = true);
virtual void readBitstreamToTree(const unsigned char* bitstream, uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args);

View file

@ -198,10 +198,10 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
}
void OctreeProcessor::clearNonLocalEntities() {
void OctreeProcessor::clearDomainAndNonOwnedEntities() {
if (_tree) {
_tree->withWriteLock([&] {
_tree->eraseNonLocalEntities();
_tree->eraseDomainAndNonOwnedEntities();
});
}
}

View file

@ -43,7 +43,7 @@ public:
virtual void init();
/// clears the tree
virtual void clearNonLocalEntities();
virtual void clearDomainAndNonOwnedEntities();
virtual void clear();
float getAverageElementsPerPacket() const { return _elementsPerPacket.getAverage(); }

View file

@ -32,18 +32,19 @@ void Framebuffer::create(const glm::uvec2& size) {
_validTexture = false;
// Depth renderbuffer
glGenRenderbuffers(1, &_depth);
/* glGenRenderbuffers(1, &_depth);
glBindRenderbuffer(GL_RENDERBUFFER, _depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, _size.x, _size.y);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
*/
// Framebuffer
glGenFramebuffers(1, &_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depth);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
// glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depth);
// glBindFramebuffer(GL_FRAMEBUFFER, 0);
_swapChain = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D, GL_RGBA8, _size.x, _size.y, 1, 3);
_length = vrapi_GetTextureSwapChainLength(_swapChain);
if (!_length) {
__android_log_write(ANDROID_LOG_WARN, "QQQ_OVR", "Unable to count swap chain textures");

View file

@ -140,7 +140,11 @@ struct VrSurface : public TaskQueue {
if (vrReady != vrRunning) {
if (vrRunning) {
__android_log_write(ANDROID_LOG_WARN, "QQQ_OVR", "vrapi_LeaveVrMode");
vrapi_SetClockLevels(session, 1, 1);
vrapi_SetExtraLatencyMode(session, VRAPI_EXTRA_LATENCY_MODE_OFF);
vrapi_SetDisplayRefreshRate(session, 60);
vrapi_LeaveVrMode(session);
session = nullptr;
oculusActivity = nullptr;
} else {

View file

@ -121,6 +121,7 @@ QRectF OculusMobileDisplayPlugin::getPlayAreaRect() {
glm::mat4 OculusMobileDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& baseProjection) const {
glm::mat4 result = baseProjection;
VrHandler::withOvrMobile([&](ovrMobile* session){
auto trackingState = vrapi_GetPredictedTracking2(session, 0.0);
result = ovr::Fov{ trackingState.Eye[eye].ProjectionMatrix }.withZ(baseProjection);
@ -130,15 +131,19 @@ glm::mat4 OculusMobileDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4&
glm::mat4 OculusMobileDisplayPlugin::getCullingProjection(const glm::mat4& baseProjection) const {
glm::mat4 result = baseProjection;
VrHandler::withOvrMobile([&](ovrMobile* session){
auto trackingState = vrapi_GetPredictedTracking2(session, 0.0);
ovr::Fov fovs[2];
for (size_t i = 0; i < 2; ++i) {
fovs[i].extract(trackingState.Eye[i].ProjectionMatrix);
}
fovs[0].extend(fovs[1]);
return fovs[0].withZ(baseProjection);
result= glm::scale( fovs[0].withZ(baseProjection),glm::vec3(1.5f));
return result;
});
return result;
}
@ -168,10 +173,8 @@ bool OculusMobileDisplayPlugin::isHmdMounted() const {
static void goToDevMobile() {
auto addressManager = DependencyManager::get<AddressManager>();
auto currentAddress = addressManager->currentAddress().toString().toStdString();
if (std::string::npos == currentAddress.find("dev-mobile")) {
addressManager->handleLookupString("hifi://dev-mobile/495.236,501.017,482.434/0,0.97452,0,-0.224301");
//addressManager->handleLookupString("hifi://dev-mobile/504,498,491/0,0,0,0");
//addressManager->handleLookupString("hifi://dev-mobile/0,-1,1");
if (std::string::npos == currentAddress.find("quest-dev")) {
addressManager->handleLookupString("hifi://quest-dev");
}
}
@ -217,12 +220,12 @@ bool OculusMobileDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
});
}
// static uint32_t count = 0;
// if ((++count % 1000) == 0) {
// AbstractViewStateInterface::instance()->postLambdaEvent([] {
// goToDevMobile();
// });
// }
// static uint32_t count = 0;
// if ((++count % 1000) == 0) {
// AbstractViewStateInterface::instance()->postLambdaEvent([] {
// goToDevMobile();
// });
// }
return result && Parent::beginFrameRender(frameIndex);
}

View file

@ -1346,14 +1346,19 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
}
void Model::computeMeshPartLocalBounds() {
for (auto& part : _modelMeshRenderItems) {
const Model::MeshState& state = _meshStates.at(part->_meshIndex);
if (_useDualQuaternionSkinning) {
part->computeAdjustedLocalBound(state.clusterDualQuaternions);
} else {
part->computeAdjustedLocalBound(state.clusterMatrices);
}
render::Transaction transaction;
auto meshStates = _meshStates;
for (auto renderItem : _modelMeshRenderItemIDs) {
transaction.updateItem<ModelMeshPartPayload>(renderItem, [this, meshStates](ModelMeshPartPayload& data) {
const Model::MeshState& state = meshStates.at(data._meshIndex);
if (_useDualQuaternionSkinning) {
data.computeAdjustedLocalBound(state.clusterDualQuaternions);
} else {
data.computeAdjustedLocalBound(state.clusterMatrices);
}
});
}
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
}
// virtual

View file

@ -96,13 +96,6 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
// draw a stencil mask in hidden regions of the framebuffer.
task.addJob<PrepareStencil>("PrepareStencil", framebuffer);
// Layered
const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f));
const auto inFrontOpaquesInputs = DrawLayered3D::Inputs(inFrontOpaque, lightingModel, nullJitter).asVarying();
const auto inFrontTransparentsInputs = DrawLayered3D::Inputs(inFrontTransparent, lightingModel, nullJitter).asVarying();
task.addJob<DrawLayered3D>("DrawInFrontOpaque", inFrontOpaquesInputs, true);
task.addJob<DrawLayered3D>("DrawInFrontTransparent", inFrontTransparentsInputs, false);
// Draw opaques forward
const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel).asVarying();
task.addJob<DrawForward>("DrawOpaques", opaqueInputs, shapePlumber);
@ -115,6 +108,13 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
const auto transparentInputs = DrawForward::Inputs(transparents, lightingModel).asVarying();
task.addJob<DrawForward>("DrawTransparents", transparentInputs, shapePlumber);
// Layered
const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f));
const auto inFrontOpaquesInputs = DrawLayered3D::Inputs(inFrontOpaque, lightingModel, nullJitter).asVarying();
const auto inFrontTransparentsInputs = DrawLayered3D::Inputs(inFrontTransparent, lightingModel, nullJitter).asVarying();
task.addJob<DrawLayered3D>("DrawInFrontOpaque", inFrontOpaquesInputs, true);
task.addJob<DrawLayered3D>("DrawInFrontTransparent", inFrontTransparentsInputs, false);
{ // Debug the bounds of the rendered items, still look at the zbuffer
task.addJob<DrawBounds>("DrawMetaBounds", metas);

View file

@ -976,7 +976,9 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString&
using PointerHandler = std::function<void(const EntityItemID&, const PointerEvent&)>;
auto makePointerHandler = [this](QString eventName) -> PointerHandler {
return [this, eventName](const EntityItemID& entityItemID, const PointerEvent& event) {
forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) });
if (!EntityTree::areEntityClicksCaptured()) {
forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) });
}
};
};

View file

@ -458,6 +458,11 @@ void TabletProxy::emitWebEvent(const QVariant& msg) {
}
void TabletProxy::onTabletShown() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "onTabletShown");
return;
}
if (_tabletShown) {
Setting::Handle<bool> notificationSounds{ QStringLiteral("play_notification_sounds"), true};
Setting::Handle<bool> notificationSoundTablet{ QStringLiteral("play_notification_sounds_tablet"), true};
@ -485,7 +490,11 @@ bool TabletProxy::isPathLoaded(const QVariant& path) {
}
void TabletProxy::setQmlTabletRoot(OffscreenQmlSurface* qmlOffscreenSurface) {
Q_ASSERT(QThread::currentThread() == qApp->thread());
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setQmlTabletRoot", Q_ARG(OffscreenQmlSurface*, qmlOffscreenSurface));
return;
}
_qmlOffscreenSurface = qmlOffscreenSurface;
_qmlTabletRoot = qmlOffscreenSurface ? qmlOffscreenSurface->getRootItem() : nullptr;
if (_qmlTabletRoot && _qmlOffscreenSurface) {
@ -654,6 +663,11 @@ void TabletProxy::loadQMLSource(const QVariant& path, bool resizable) {
}
void TabletProxy::stopQMLSource() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stopQMLSource");
return;
}
// For desktop toolbar mode dialogs.
if (!_toolbarMode || !_desktopWindow) {
qCDebug(uiLogging) << "tablet cannot clear QML because not desktop toolbar mode";
@ -879,6 +893,12 @@ void TabletProxy::sendToQml(const QVariant& msg) {
OffscreenQmlSurface* TabletProxy::getTabletSurface() {
if (QThread::currentThread() != thread()) {
OffscreenQmlSurface* result = nullptr;
BLOCKING_INVOKE_METHOD(this, "getTabletSurface", Q_RETURN_ARG(OffscreenQmlSurface*, result));
return result;
}
return _qmlOffscreenSurface;
}
@ -888,6 +908,11 @@ void TabletProxy::desktopWindowClosed() {
}
void TabletProxy::unfocus() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "unfocus");
return;
}
if (_qmlOffscreenSurface) {
_qmlOffscreenSurface->lowerKeyboard();
}

View file

@ -14,8 +14,8 @@
var DEFAULT_SCRIPTS_COMBINED = [
"system/request-service.js",
"system/progress.js",
//"system/away.js",
"system/hmd.js",
"system/away.js",
//"system/hmd.js",
"system/menu.js",
"system/bubble.js",
"system/pal.js", // "system/mod.js", // older UX, if you prefer
@ -25,6 +25,7 @@ var DEFAULT_SCRIPTS_COMBINED = [
"system/notifications.js",
"system/commerce/wallet.js",
"system/dialTone.js",
"system/marketplaces/marketplaces.js",
"system/quickGoto.js",
"system/firstPersonHMD.js",
"system/tablet-ui/tabletUI.js",

View file

@ -148,6 +148,27 @@ Rectangle {
}
}
Separator {}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: 5
Repeater {
model: [ "MSAA:PrepareFramebuffer:numSamples:4:1"
]
ConfigSlider {
label: qsTr(modelData.split(":")[0])
integral: true
config: render.mainViewTask.getConfig(modelData.split(":")[1])
property: modelData.split(":")[2]
max: modelData.split(":")[3]
min: modelData.split(":")[4]
anchors.left: parent.left
anchors.right: parent.right
}
}
}
Separator {}
Item {
height: childrenRect.height

View file

@ -65,7 +65,7 @@ var eventMappingName = "io.highfidelity.away"; // goActive on hand controller bu
var eventMapping = Controller.newMapping(eventMappingName);
var avatarPosition = MyAvatar.position;
var wasHmdMounted = HMD.mounted;
var previousBubbleState = Users.getIgnoreRadiusEnabled();
// some intervals we may create/delete
var avatarMovedInterval;
@ -154,7 +154,7 @@ function goAway(fromStartup) {
if (!isEnabled || isAway) {
return;
}
// If we're entering away mode from some other state than startup, then we create our move timer immediately.
// However if we're just stating up, we need to delay this process so that we don't think the initial teleport
// is actually a move.
@ -166,7 +166,12 @@ function goAway(fromStartup) {
avatarMovedInterval = Script.setInterval(ifAvatarMovedGoActive, BASIC_TIMER_INTERVAL);
}, WAIT_FOR_MOVE_ON_STARTUP);
}
previousBubbleState = Users.getIgnoreRadiusEnabled();
if (!previousBubbleState) {
Users.toggleIgnoreRadius();
}
UserActivityLogger.bubbleToggled(Users.getIgnoreRadiusEnabled());
UserActivityLogger.toggledAway(true);
MyAvatar.isAway = true;
}
@ -179,6 +184,11 @@ function goActive() {
UserActivityLogger.toggledAway(false);
MyAvatar.isAway = false;
if (Users.getIgnoreRadiusEnabled() !== previousBubbleState) {
Users.toggleIgnoreRadius();
UserActivityLogger.bubbleToggled(Users.getIgnoreRadiusEnabled());
}
if (!Window.hasFocus()) {
Window.setFocus();
}

View file

@ -60,6 +60,14 @@ Script.include("/~/system/libraries/controllers.js");
this.reticleMaxY = 0;
this.endedGrab = 0;
this.MIN_HAPTIC_PULSE_INTERVAL = 500; // ms
this.disabled = false;
var _this = this;
this.leftTrigger = 0.0;
this.rightTrigger = 0.0;
this.initialControllerRotation = Quat.IDENTITY;
this.currentControllerRotation = Quat.IDENTITY;
this.manipulating = false;
this.wasManipulating = false;
var FAR_GRAB_JOINTS = [65527, 65528]; // FARGRAB_LEFTHAND_INDEX, FARGRAB_RIGHTHAND_INDEX
@ -75,6 +83,45 @@ Script.include("/~/system/libraries/controllers.js");
100,
makeLaserParams(this.hand, false));
this.getOtherModule = function () {
return getEnabledModuleByName(this.hand === RIGHT_HAND ? ("LeftFarGrabEntity") : ("RightFarGrabEntity"));
};
// Get the rotation of the fargrabbed entity.
this.getTargetRotation = function () {
if (this.targetIsNull()) {
return null;
} else {
var props = Entities.getEntityProperties(this.targetEntityID, ["rotation"]);
return props.rotation;
}
};
this.getOffhand = function () {
return (this.hand === RIGHT_HAND ? LEFT_HAND : RIGHT_HAND);
}
this.getOffhandTrigger = function () {
return (_this.hand === RIGHT_HAND ? _this.leftTrigger : _this.rightTrigger);
}
// Activation criteria for rotating a fargrabbed entity. If we're changing the mapping, this is where to do it.
this.shouldManipulateTarget = function () {
return (_this.getOffhandTrigger() > TRIGGER_ON_VALUE) ? true : false;
};
// Get the delta between the current rotation and where the controller was when manipulation started.
this.calculateEntityRotationManipulation = function (controllerRotation) {
return Quat.multiply(controllerRotation, Quat.inverse(this.initialControllerRotation));
};
this.setJointTranslation = function (newTargetPosLocal) {
MyAvatar.setJointTranslation(FAR_GRAB_JOINTS[this.hand], newTargetPosLocal);
};
this.setJointRotation = function (newTargetRotLocal) {
MyAvatar.setJointRotation(FAR_GRAB_JOINTS[this.hand], newTargetRotLocal);
};
this.handToController = function() {
return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
@ -142,8 +189,9 @@ Script.include("/~/system/libraries/controllers.js");
Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message));
var newTargetPosLocal = MyAvatar.worldToJointPoint(targetProps.position);
MyAvatar.setJointTranslation(FAR_GRAB_JOINTS[this.hand], newTargetPosLocal);
MyAvatar.setJointRotation(FAR_GRAB_JOINTS[this.hand], { x: 0, y: 0, z: 0, w: 1 });
var newTargetRotLocal = targetProps.rotation;
this.setJointTranslation(newTargetPosLocal);
this.setJointRotation(newTargetRotLocal);
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(targetProps.id, "startDistanceGrab", args);
@ -227,12 +275,44 @@ Script.include("/~/system/libraries/controllers.js");
newTargetPosition = Vec3.sum(newTargetPosition, worldControllerPosition);
newTargetPosition = Vec3.sum(newTargetPosition, this.offsetPosition);
// MyAvatar.setJointTranslation(FAR_GRAB_JOINTS[this.hand], MyAvatar.worldToJointPoint(newTargetPosition));
// var newTargetPosLocal = Mat4.transformPoint(MyAvatar.getSensorToWorldMatrix(), newTargetPosition);
var newTargetPosLocal = MyAvatar.worldToJointPoint(newTargetPosition);
MyAvatar.setJointTranslation(FAR_GRAB_JOINTS[this.hand], newTargetPosLocal);
MyAvatar.setJointRotation(FAR_GRAB_JOINTS[this.hand], { x: 0, y: 0, z: 0, w: 1 });
// This block handles the user's ability to rotate the object they're FarGrabbing
if (this.shouldManipulateTarget()) {
// Get the pose of the controller that is not grabbing.
var pose = Controller.getPoseValue((this.getOffhand() ? Controller.Standard.RightHand : Controller.Standard.LeftHand));
if (pose.valid) {
// If we weren't manipulating the object yet, initialize the entity's original position.
if (!this.manipulating) {
// This will only be triggered if we've let go of the off-hand trigger and pulled it again without ending a grab.
// Need to poll the entity's rotation again here.
if (!this.wasManipulating) {
this.initialEntityRotation = this.getTargetRotation();
}
// Save the original controller orientation, we only care about the delta between this rotation and wherever
// the controller rotates, so that we can apply it to the entity's rotation.
this.initialControllerRotation = Quat.multiply(pose.rotation, MyAvatar.orientation);
this.manipulating = true;
}
}
var rot = Quat.multiply(pose.rotation, MyAvatar.orientation);
var rotBetween = this.calculateEntityRotationManipulation(rot);
var doubleRot = Quat.multiply(rotBetween, rotBetween);
this.lastJointRotation = Quat.multiply(doubleRot, this.initialEntityRotation);
this.setJointRotation(this.lastJointRotation);
} else {
// If we were manipulating but the user isn't currently expressing this intent, we want to know so we preserve the rotation
// between manipulations without ending the fargrab.
if (this.manipulating) {
this.initialEntityRotation = this.lastJointRotation;
this.wasManipulating = true;
}
this.manipulating = false;
// Reset the inital controller position.
this.initialControllerRotation = Quat.IDENTITY;
}
this.setJointTranslation(newTargetPosLocal);
this.previousRoomControllerPosition = roomControllerPosition;
};
@ -254,9 +334,15 @@ Script.include("/~/system/libraries/controllers.js");
}));
unhighlightTargetEntity(this.targetEntityID);
this.grabbing = false;
this.targetEntityID = null;
this.potentialEntityWithContextOverlay = false;
MyAvatar.clearJointData(FAR_GRAB_JOINTS[this.hand]);
this.initialEntityRotation = Quat.IDENTITY;
this.initialControllerRotation = Quat.IDENTITY;
this.targetEntityID = null;
this.manipulating = false;
this.wasManipulating = false;
var otherModule = this.getOtherModule();
otherModule.disabled = false;
};
this.updateRecommendedArea = function() {
@ -326,7 +412,9 @@ Script.include("/~/system/libraries/controllers.js");
this.distanceHolding = false;
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE && !this.disabled) {
var otherModule = this.getOtherModule();
otherModule.disabled = true;
return makeRunningValues(true, [], []);
} else {
this.destroyContextOverlay();
@ -336,6 +424,8 @@ Script.include("/~/system/libraries/controllers.js");
};
this.run = function (controllerData) {
this.leftTrigger = controllerData.triggerValues[LEFT_HAND];
this.rightTrigger = controllerData.triggerValues[RIGHT_HAND];
if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.targetIsNull()) {
this.endFarGrabEntity(controllerData);
return makeRunningValues(false, [], []);

View file

@ -30,6 +30,20 @@
100,
makeLaserParams((this.hand + HUD_LASER_OFFSET), false));
this.getFarGrab = function () {
return getEnabledModuleByName(this.hand === RIGHT_HAND ? ("RightFarGrabEntity") : ("LeftFarGrabEntity"));
}
this.farGrabActive = function () {
var farGrab = this.getFarGrab();
// farGrab will be null if module isn't loaded.
if (farGrab) {
return farGrab.targetIsNull();
} else {
return false;
}
};
this.getOtherHandController = function() {
return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand;
};
@ -79,7 +93,7 @@
this.isReady = function (controllerData) {
var otherModuleRunning = this.getOtherModule().running;
if (!otherModuleRunning && HMD.active) {
if (!otherModuleRunning && HMD.active && !this.farGrabActive()) {
if (this.processLaser(controllerData)) {
this.running = true;
return ControllerDispatcherUtils.makeRunningValues(true, [], []);

View file

@ -37,6 +37,20 @@ Script.include("/~/system/libraries/controllers.js");
100,
makeLaserParams(hand, true));
this.getFarGrab = function () {
return getEnabledModuleByName(this.hand === RIGHT_HAND ? ("RightFarGrabEntity") : ("LeftFarGrabEntity"));
};
this.farGrabActive = function () {
var farGrab = this.getFarGrab();
// farGrab will be null if module isn't loaded.
if (farGrab) {
return farGrab.targetIsNull();
} else {
return false;
}
};
this.grabModuleWantsNearbyOverlay = function(controllerData) {
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE || controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) {
var nearGrabName = this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay";
@ -184,7 +198,12 @@ Script.include("/~/system/libraries/controllers.js");
this.dominantHandOverride = false;
this.isReady = function(controllerData) {
this.isReady = function (controllerData) {
// Trivial rejection for when FarGrab is active.
if (this.farGrabActive()) {
return makeRunningValues(false, [], []);
}
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE &&
controllerData.triggerValues[this.otherHand] <= TRIGGER_OFF_VALUE;
var type = this.getInteractableType(controllerData, isTriggerPressed, false);

View file

@ -418,13 +418,11 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride)
var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleOffsetOverride;
Entities.editEntity(HMD.homeButtonID, {
localPosition: { x: HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
localRotation: { x: 0, y: 1, z: 0, w: 0 },
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
});
Entities.editEntity(HMD.homeButtonHighlightID, {
localPosition: { x: -HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
localRotation: { x: 0, y: 1, z: 0, w: 0 },
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
});
};
@ -482,13 +480,11 @@ reparentAndScaleTablet = function(width, reparentProps) {
var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleOffsetOverride;
Entities.editEntity(HMD.homeButtonID, {
localPosition: { x: HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
localRotation: { x: 0, y: 1, z: 0, w: 0 },
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
});
Entities.editEntity(HMD.homeButtonHighlightID, {
localPosition: { x: -HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
localRotation: { x: 0, y: 1, z: 0, w: 0 },
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
});
}

View file

@ -125,9 +125,6 @@ module.exports = (function() {
Script.scriptEnding.connect(this, function() {
this.window.close();
// FIXME: temp solution for reload crash (MS18269),
// we should decide on proper object ownership strategy for InteractiveWindow API
this.window = null;
});
},
setVisible: function(visible) {

View file

@ -20,7 +20,7 @@ endfunction()
if (BUILD_TOOLS)
# Allow different tools for stable builds
if (RELEASE_TYPE STREQUAL "PRODUCTION")
if (STABLE_BUILD)
set(ALL_TOOLS
udt-test
vhacd-util

View file

@ -80,7 +80,9 @@ else ()
add_executable(${TARGET_NAME} ${NITPICK_SRCS} ${QM})
endif ()
add_dependencies(${TARGET_NAME} resources)
if (NOT UNIX)
add_dependencies(${TARGET_NAME} resources)
endif()
# disable /OPT:REF and /OPT:ICF for the Debug builds
# This will prevent the following linker warnings

View file

@ -27,7 +27,10 @@ AWSInterface::AWSInterface(QObject* parent) : QObject(parent) {
void AWSInterface::createWebPageFromResults(const QString& testResults,
const QString& workingDirectory,
QCheckBox* updateAWSCheckBox,
QLineEdit* urlLineEdit) {
QRadioButton* diffImageRadioButton,
QRadioButton* ssimImageRadionButton,
QLineEdit* urlLineEdit
) {
_workingDirectory = workingDirectory;
// Verify filename is in correct format
@ -52,6 +55,13 @@ void AWSInterface::createWebPageFromResults(const QString& testResults,
QString zipFilenameWithoutExtension = zipFilename.split('.')[0];
extractTestFailuresFromZippedFolder(_workingDirectory + "/" + zipFilenameWithoutExtension);
if (diffImageRadioButton->isChecked()) {
_comparisonImageFilename = "Difference Image.png";
} else {
_comparisonImageFilename = "SSIM Image.png";
}
createHTMLFile();
if (updateAWSCheckBox->isChecked()) {
@ -353,7 +363,7 @@ void AWSInterface::openTable(QTextStream& stream, const QString& testResult, con
stream << "\t\t\t\t<th><h1>Test</h1></th>\n";
stream << "\t\t\t\t<th><h1>Actual Image</h1></th>\n";
stream << "\t\t\t\t<th><h1>Expected Image</h1></th>\n";
stream << "\t\t\t\t<th><h1>Difference Image</h1></th>\n";
stream << "\t\t\t\t<th><h1>Comparison Image</h1></th>\n";
stream << "\t\t\t</tr>\n";
}
}
@ -378,12 +388,13 @@ void AWSInterface::createEntry(const int index, const QString& testResult, QText
QString folder;
bool differenceFileFound;
if (isFailure) {
folder = FAILURES_FOLDER;
differenceFileFound = QFile::exists(_htmlFailuresFolder + "/" + resultName + "/Difference Image.png");
differenceFileFound = QFile::exists(_htmlFailuresFolder + "/" + resultName + "/" + _comparisonImageFilename);
} else {
folder = SUCCESSES_FOLDER;
differenceFileFound = QFile::exists(_htmlSuccessesFolder + "/" + resultName + "/Difference Image.png");
differenceFileFound = QFile::exists(_htmlSuccessesFolder + "/" + resultName + "/" + _comparisonImageFilename);
}
if (textResultsFileFound) {
@ -450,7 +461,7 @@ void AWSInterface::createEntry(const int index, const QString& testResult, QText
stream << "\t\t\t\t<td><img src=\"./" << folder << "/" << resultName << "/Expected Image.png\" width = \"576\" height = \"324\" ></td>\n";
if (differenceFileFound) {
stream << "\t\t\t\t<td><img src=\"./" << folder << "/" << resultName << "/Difference Image.png\" width = \"576\" height = \"324\" ></td>\n";
stream << "\t\t\t\t<td><img src=\"./" << folder << "/" << resultName << "/" << _comparisonImageFilename << "\" width = \"576\" height = \"324\" ></td>\n";
} else {
stream << "\t\t\t\t<td><h2>No Image Found</h2>\n";
}
@ -512,12 +523,12 @@ void AWSInterface::updateAWS() {
stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Expected Image.png" << "', Body=data)\n\n";
if (QFile::exists(_htmlFailuresFolder + "/" + parts[parts.length() - 1] + "/Difference Image.png")) {
if (QFile::exists(_htmlFailuresFolder + "/" + parts[parts.length() - 1] + "/" + _comparisonImageFilename)) {
stream << "data = open('" << _workingDirectory << "/" << filename << "/"
<< "Difference Image.png"
<< _comparisonImageFilename
<< "', 'rb')\n";
stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Difference Image.png" << "', Body=data)\n\n";
stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << _comparisonImageFilename << "', Body=data)\n\n";
}
}
}
@ -555,12 +566,12 @@ void AWSInterface::updateAWS() {
stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Expected Image.png" << "', Body=data)\n\n";
if (QFile::exists(_htmlSuccessesFolder + "/" + parts[parts.length() - 1] + "/Difference Image.png")) {
if (QFile::exists(_htmlSuccessesFolder + "/" + parts[parts.length() - 1] + "/" + _comparisonImageFilename)) {
stream << "data = open('" << _workingDirectory << "/" << filename << "/"
<< "Difference Image.png"
<< _comparisonImageFilename
<< "', 'rb')\n";
stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Difference Image.png" << "', Body=data)\n\n";
stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << _comparisonImageFilename << "', Body=data)\n\n";
}
}
}

View file

@ -14,6 +14,7 @@
#include <QCheckBox>
#include <QLineEdit>
#include <QObject>
#include <QRadioButton>
#include <QTextStream>
#include "BusyWindow.h"
@ -28,6 +29,8 @@ public:
void createWebPageFromResults(const QString& testResults,
const QString& workingDirectory,
QCheckBox* updateAWSCheckBox,
QRadioButton* diffImageRadioButton,
QRadioButton* ssimImageRadionButton,
QLineEdit* urlLineEdit);
void extractTestFailuresFromZippedFolder(const QString& folderName);
@ -67,6 +70,9 @@ private:
QString AWS_BUCKET{ "hifi-qa" };
QLineEdit* _urlLineEdit;
QString _comparisonImageFilename;
};
#endif // hifi_AWSInterface_h

View file

@ -14,7 +14,7 @@
// Computes SSIM - see https://en.wikipedia.org/wiki/Structural_similarity
// The value is computed for the luminance component and the average value is returned
double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) const {
void ImageComparer::compareImages(const QImage& resultImage, const QImage& expectedImage) {
const int L = 255; // (2^number of bits per pixel) - 1
const double K1 { 0.01 };
@ -39,8 +39,13 @@ double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) co
double p[WIN_SIZE * WIN_SIZE];
double q[WIN_SIZE * WIN_SIZE];
_ssimResults.results.clear();
int windowCounter{ 0 };
double ssim{ 0.0 };
double min { 1.0 };
double max { -1.0 };
while (x < expectedImage.width()) {
int lastX = x + WIN_SIZE - 1;
if (lastX > expectedImage.width() - 1) {
@ -96,7 +101,13 @@ double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) co
double numerator = (2.0 * mP * mQ + c1) * (2.0 * sigPQ + c2);
double denominator = (mP * mP + mQ * mQ + c1) * (sigsqP + sigsqQ + c2);
ssim += numerator / denominator;
double value { numerator / denominator };
_ssimResults.results.push_back(value);
ssim += value;
if (value < min) min = value;
if (value > max) max = value;
++windowCounter;
y += WIN_SIZE;
@ -106,5 +117,17 @@ double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) co
y = 0;
}
return ssim / windowCounter;
};
_ssimResults.width = (int)(expectedImage.width() / WIN_SIZE);
_ssimResults.height = (int)(expectedImage.height() / WIN_SIZE);
_ssimResults.min = min;
_ssimResults.max = max;
_ssimResults.ssim = ssim / windowCounter;
};
double ImageComparer::getSSIMValue() {
return _ssimResults.ssim;
}
SSIMResults ImageComparer::getSSIMResults() {
return _ssimResults;
}

View file

@ -10,12 +10,20 @@
#ifndef hifi_ImageComparer_h
#define hifi_ImageComparer_h
#include "common.h"
#include <QtCore/QString>
#include <QImage>
class ImageComparer {
public:
double compareImages(QImage resultImage, QImage expectedImage) const;
void compareImages(const QImage& resultImage, const QImage& expectedImage);
double getSSIMValue();
SSIMResults getSSIMResults();
private:
SSIMResults _ssimResults;
};
#endif // hifi_ImageComparer_h

Some files were not shown because too many files have changed in this diff Show more