mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-07 10:02:24 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into 21507-replaceQNetworkAccessManagerWithPython
This commit is contained in:
commit
4e7f558af8
109 changed files with 1521 additions and 794 deletions
|
@ -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
BIN
android/apps/keystore.jks
Normal file
Binary file not shown.
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
BIN
interface/resources/fonts/rawline-500.ttf
Normal file
BIN
interface/resources/fonts/rawline-500.ttf
Normal file
Binary file not shown.
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 + "..."
|
||||
}
|
||||
}
|
||||
|
||||
|
|
20
interface/resources/qml/styles-uit/Rawline.qml
Normal file
20
interface/resources/qml/styles-uit/Rawline.qml
Normal 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"
|
||||
}
|
20
interface/resources/qml/stylesUit/Rawline.qml
Normal file
20
interface/resources/qml/stylesUit/Rawline.qml
Normal 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"
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -45,6 +45,10 @@ void AndroidHelper::notifyBeforeEnterBackground() {
|
|||
emit beforeEnterBackground();
|
||||
}
|
||||
|
||||
void AndroidHelper::notifyToggleAwayMode() {
|
||||
emit toggleAwayMode();
|
||||
}
|
||||
|
||||
void AndroidHelper::notifyEnterBackground() {
|
||||
emit enterBackground();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -338,7 +338,8 @@ public:
|
|||
void beforeEnterBackground();
|
||||
void enterBackground();
|
||||
void enterForeground();
|
||||
#endif
|
||||
void toggleAwayMode();
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void svoImportRequested(const QString& url);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1537,6 +1537,7 @@ void Avatar::rigReady() {
|
|||
buildUnscaledEyeHeightCache();
|
||||
buildSpine2SplineRatioCache();
|
||||
computeMultiSphereShapes();
|
||||
buildSpine2SplineRatioCache();
|
||||
}
|
||||
|
||||
// rig has been reset.
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
VERTEX gpu::vertex::DrawUnitQuadTexcoord
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
VERTEX paintStroke
|
35
libraries/entities-renderer/src/paintStroke_forward.slf
Normal file
35
libraries/entities-renderer/src/paintStroke_forward.slf
Normal 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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
26
libraries/gpu/src/gpu/DrawTextureGammaLinearToSRGB.slf
Normal file
26
libraries/gpu/src/gpu/DrawTextureGammaLinearToSRGB.slf
Normal 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));
|
||||
}
|
1
libraries/gpu/src/gpu/DrawTextureGammaLinearToSRGB.slp
Normal file
1
libraries/gpu/src/gpu/DrawTextureGammaLinearToSRGB.slp
Normal file
|
@ -0,0 +1 @@
|
|||
VERTEX DrawUnitQuadTexcoord
|
26
libraries/gpu/src/gpu/DrawTextureGammaSRGBToLinear.slf
Normal file
26
libraries/gpu/src/gpu/DrawTextureGammaSRGBToLinear.slf
Normal 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));
|
||||
}
|
1
libraries/gpu/src/gpu/DrawTextureGammaSRGBToLinear.slp
Normal file
1
libraries/gpu/src/gpu/DrawTextureGammaSRGBToLinear.slp
Normal file
|
@ -0,0 +1 @@
|
|||
VERTEX DrawUnitQuadTexcoord
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -198,10 +198,10 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
|
|||
}
|
||||
|
||||
|
||||
void OctreeProcessor::clearNonLocalEntities() {
|
||||
void OctreeProcessor::clearDomainAndNonOwnedEntities() {
|
||||
if (_tree) {
|
||||
_tree->withWriteLock([&] {
|
||||
_tree->eraseNonLocalEntities();
|
||||
_tree->eraseDomainAndNonOwnedEntities();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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, [], []);
|
||||
|
|
|
@ -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, [], []);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 }
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue