Merge branch 'master' into vr-edit-a
1
.gitattributes
vendored
|
@ -10,6 +10,7 @@
|
|||
*.json text
|
||||
*.js text
|
||||
*.qml text
|
||||
*.qrc text
|
||||
*.slf text
|
||||
*.slh text
|
||||
*.slv text
|
||||
|
|
5
.gitignore
vendored
|
@ -12,6 +12,11 @@ ext/
|
|||
Makefile
|
||||
*.user
|
||||
|
||||
# Android Studio
|
||||
*.iml
|
||||
local.properties
|
||||
android/libraries
|
||||
|
||||
# Xcode
|
||||
*.xcodeproj
|
||||
*.xcworkspace
|
||||
|
|
|
@ -1,19 +1,56 @@
|
|||
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Android specific instructions are found in this file.
|
||||
|
||||
### Android Dependencies
|
||||
# Android Dependencies
|
||||
|
||||
You will need the following tools to build our Android targets.
|
||||
|
||||
* [cmake](http://www.cmake.org/download/) ~> 3.5.1
|
||||
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.6.2
|
||||
* [ant](http://ant.apache.org/bindownload.cgi) ~> 1.9.4
|
||||
* [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html) ~> r10d
|
||||
* [Android SDK](http://developer.android.com/sdk/installing/index.html) ~> 24.4.1.1
|
||||
* Install the latest Platform-tools
|
||||
* Install the latest Build-tools
|
||||
* Install the SDK Platform for API Level 19
|
||||
* Install Sources for Android SDK for API Level 19
|
||||
* Install the ARM EABI v7a System Image if you want to run an emulator.
|
||||
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.9.1
|
||||
* [Android Studio](https://developer.android.com/studio/index.html)
|
||||
* [Google VR SDK](https://github.com/googlevr/gvr-android-sdk/releases)
|
||||
* [Gradle](https://gradle.org/releases/)
|
||||
|
||||
### Qt
|
||||
|
||||
Download the Qt online installer. Run the installer and select the android_armv7 binaries. Installing to the default path is recommended
|
||||
|
||||
### Android Studio
|
||||
|
||||
Download the Android Studio installer and run it. Once installed, at the welcome screen, click configure in the lower right corner and select SDK manager
|
||||
|
||||
From the SDK Platforms tab, select API level 26.
|
||||
|
||||
* Install the ARM EABI v7a System Image if you want to run an emulator.
|
||||
|
||||
From the SDK Tools tab select the following
|
||||
|
||||
* Android SDK Build-Tools
|
||||
* GPU Debugging Tools
|
||||
* CMake (even if you have a separate CMake installation)
|
||||
* LLDB
|
||||
* Android SDK Platform-Tools
|
||||
* Android SDK Tools
|
||||
* Android SDK Tools
|
||||
* NDK (even if you have the NDK installed separately)
|
||||
|
||||
### Google VR SDK
|
||||
|
||||
Download the 1.8 Google VR SDK [release](https://github.com/googlevr/gvr-android-sdk/archive/v1.80.0.zip). Unzip the archive to a location on your drive.
|
||||
|
||||
### Gradle
|
||||
|
||||
Download [Gradle 4.1](https://services.gradle.org/distributions/gradle-4.1-all.zip) and unzip it on your local drive. You may wish to add the location of the bin directory within the archive to your path
|
||||
|
||||
#### Set up machine specific Gradle properties
|
||||
|
||||
Create a `gradle.properties` file in ~/.gradle. Edit the file to contain the following
|
||||
|
||||
QT5_ROOT=C\:\\Qt\\5.9.1\\android_armv7
|
||||
GVR_ROOT=C\:\\Android\\gvr-android-sdk
|
||||
|
||||
Replace the paths with your local installations of Qt5 and the Google VR SDK
|
||||
|
||||
|
||||
# TODO fix the rest
|
||||
|
||||
You will also need to cross-compile the dependencies required for all platforms for Android, and help CMake find these compiled libraries on your machine.
|
||||
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
if (WIN32)
|
||||
# If we're running under the gradle build, HIFI_ANDROID will be set here, but
|
||||
# ANDROID will not be set until after the `project` statement. This is the *ONLY*
|
||||
# place you need to use `HIFI_ANDROID` instead of `ANDROID`
|
||||
if (WIN32 AND NOT HIFI_ANDROID)
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
endif()
|
||||
|
||||
include("cmake/init.cmake")
|
||||
|
||||
project(hifi)
|
||||
|
||||
include("cmake/init.cmake")
|
||||
|
||||
include("cmake/compiler.cmake")
|
||||
|
||||
if (NOT DEFINED SERVER_ONLY)
|
||||
|
@ -54,11 +57,13 @@ endif()
|
|||
file(GLOB_RECURSE CMAKE_SRC cmake/*.cmake cmake/CMakeLists.txt)
|
||||
add_custom_target(cmake SOURCES ${CMAKE_SRC})
|
||||
GroupSources("cmake")
|
||||
unset(CMAKE_SRC)
|
||||
|
||||
file(GLOB_RECURSE JS_SRC scripts/*.js unpublishedScripts/*.js)
|
||||
add_custom_target(js SOURCES ${JS_SRC})
|
||||
GroupSources("scripts")
|
||||
GroupSources("unpublishedScripts")
|
||||
unset(JS_SRC)
|
||||
|
||||
# Locate the required Qt build on the filesystem
|
||||
setup_qt()
|
||||
|
@ -77,6 +82,12 @@ option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
|
|||
|
||||
set_packaging_parameters()
|
||||
|
||||
# FIXME hack to work on the proper Android toolchain
|
||||
if (ANDROID)
|
||||
add_subdirectory(android/app)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# add subdirectories for all targets
|
||||
if (BUILD_SERVER)
|
||||
add_subdirectory(assignment-client)
|
||||
|
|
8
android/app/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
set(TARGET_NAME native-lib)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared networking gl gpu gpu-gles render-utils)
|
||||
autoscribe_shader_lib(gpu model render render-utils)
|
||||
target_opengl()
|
||||
target_link_libraries(native-lib android log m)
|
||||
target_include_directories(native-lib PRIVATE "${GVR_ROOT}/libraries/headers")
|
||||
target_link_libraries(native-lib "C:/Users/bdavis/Git/hifi/android/libraries/jni/armeabi-v7a/libgvr.so")
|
57
android/app/build.gradle
Normal file
|
@ -0,0 +1,57 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "26.0.1"
|
||||
defaultConfig {
|
||||
applicationId "org.saintandreas.testapp"
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 26
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
ndk { abiFilters 'armeabi-v7a' }
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments '-DHIFI_ANDROID=1',
|
||||
'-DANDROID_PLATFORM=android-24',
|
||||
'-DANDROID_TOOLCHAIN=clang',
|
||||
'-DANDROID_STL=gnustl_shared',
|
||||
'-DGVR_ROOT=' + GVR_ROOT,
|
||||
'-DNATIVE_SCRIBE=c:/bin/scribe.exe',
|
||||
"-DHIFI_ANDROID_PRECOMPILED=${project.rootDir}/libraries/jni/armeabi-v7a"
|
||||
}
|
||||
}
|
||||
jackOptions { enabled true }
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDirs += '../libraries/jni';
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path '../../CMakeLists.txt'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: "${project.rootDir}/libraries/jar", include: 'QtAndroid-bundled.jar')
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.google.vr:sdk-audio:1.80.0'
|
||||
compile 'com.google.vr:sdk-base:1.80.0'
|
||||
}
|
||||
|
||||
build.dependsOn(':extractQt5')
|
25
android/app/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:\Android\SDK/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
37
android/app/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.saintandreas.testapp">
|
||||
|
||||
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="26" />
|
||||
<uses-feature android:glEsVersion="0x00030001" android:required="true" />
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true"/>
|
||||
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="true"/>
|
||||
<uses-feature android:name="android.software.vr.mode" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.vr.high_performance" android:required="false"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:theme="@style/VrActivityTheme"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="landscape"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:enableVrMode="@string/gvr_vr_mode_component"
|
||||
android:resizeableActivity="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="com.google.intent.category.DAYDREAM"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="com.google.intent.category.CARDBOARD" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
50
android/app/src/main/cpp/GoogleVRHelpers.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <vr/gvr/capi/include/gvr.h>
|
||||
|
||||
namespace googlevr {
|
||||
|
||||
// Convert a GVR matrix to GLM matrix
|
||||
glm::mat4 toGlm(const gvr::Mat4f &matrix) {
|
||||
glm::mat4 result;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
result[j][i] = matrix.m[i][j];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Given a field of view in degrees, compute the corresponding projection
|
||||
// matrix.
|
||||
glm::mat4 perspectiveMatrixFromView(const gvr::Rectf& fov, float z_near, float z_far) {
|
||||
const float x_left = -std::tan(fov.left * M_PI / 180.0f) * z_near;
|
||||
const float x_right = std::tan(fov.right * M_PI / 180.0f) * z_near;
|
||||
const float y_bottom = -std::tan(fov.bottom * M_PI / 180.0f) * z_near;
|
||||
const float y_top = std::tan(fov.top * M_PI / 180.0f) * z_near;
|
||||
const float Y = (2 * z_near) / (y_top - y_bottom);
|
||||
const float A = (x_right + x_left) / (x_right - x_left);
|
||||
const float B = (y_top + y_bottom) / (y_top - y_bottom);
|
||||
const float C = (z_near + z_far) / (z_near - z_far);
|
||||
const float D = (2 * z_near * z_far) / (z_near - z_far);
|
||||
|
||||
glm::mat4 result { 0 };
|
||||
result[2][0] = A;
|
||||
result[1][1] = Y;
|
||||
result[2][1] = B;
|
||||
result[2][2] = C;
|
||||
result[3][2] = D;
|
||||
result[2][3] = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::quat toGlm(const gvr::ControllerQuat& q) {
|
||||
glm::quat result;
|
||||
result.w = q.qw;
|
||||
result.x = q.qx;
|
||||
result.y = q.qy;
|
||||
result.z = q.qz;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
78
android/app/src/main/cpp/native-lib.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include <jni.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
int QtMsgTypeToAndroidPriority(QtMsgType type) {
|
||||
int priority = ANDROID_LOG_UNKNOWN;
|
||||
switch (type) {
|
||||
case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break;
|
||||
case QtWarningMsg: priority = ANDROID_LOG_WARN; break;
|
||||
case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break;
|
||||
case QtFatalMsg: priority = ANDROID_LOG_FATAL; break;
|
||||
case QtInfoMsg: priority = ANDROID_LOG_INFO; break;
|
||||
default: break;
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
__android_log_write(QtMsgTypeToAndroidPriority(type), "Interface", message.toStdString().c_str());
|
||||
}
|
||||
|
||||
static jlong toJni(NativeRenderer *renderer) {
|
||||
return reinterpret_cast<intptr_t>(renderer);
|
||||
}
|
||||
|
||||
static NativeRenderer *fromJni(jlong renderer) {
|
||||
return reinterpret_cast<NativeRenderer*>(renderer);
|
||||
}
|
||||
|
||||
#define JNI_METHOD(r, name) JNIEXPORT r JNICALL Java_org_saintandreas_testapp_MainActivity_##name
|
||||
|
||||
extern "C" {
|
||||
|
||||
JNI_METHOD(jlong, nativeCreateRenderer)
|
||||
(JNIEnv *env, jclass clazz, jobject class_loader, jobject android_context, jlong native_gvr_api) {
|
||||
qInstallMessageHandler(messageHandler);
|
||||
#if defined(GVR)
|
||||
auto gvrContext = reinterpret_cast<gvr_context *>(native_gvr_api);
|
||||
return toJni(new NativeRenderer(gvrContext));
|
||||
#else
|
||||
return toJni(new NativeRenderer(nullptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeDestroyRenderer)
|
||||
(JNIEnv *env, jclass clazz, jlong renderer) {
|
||||
delete fromJni(renderer);
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeInitializeGl)
|
||||
(JNIEnv *env, jobject obj, jlong renderer) {
|
||||
fromJni(renderer)->InitializeGl();
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeDrawFrame)
|
||||
(JNIEnv *env, jobject obj, jlong renderer) {
|
||||
fromJni(renderer)->DrawFrame();
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeOnTriggerEvent)
|
||||
(JNIEnv *env, jobject obj, jlong renderer) {
|
||||
fromJni(renderer)->OnTriggerEvent();
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeOnPause)
|
||||
(JNIEnv *env, jobject obj, jlong renderer) {
|
||||
fromJni(renderer)->OnPause();
|
||||
}
|
||||
|
||||
JNI_METHOD(void, nativeOnResume)
|
||||
(JNIEnv *env, jobject obj, jlong renderer) {
|
||||
fromJni(renderer)->OnResume();
|
||||
}
|
||||
|
||||
} // extern "C"
|
636
android/app/src/main/cpp/renderer.cpp
Normal file
|
@ -0,0 +1,636 @@
|
|||
#include "renderer.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <gl/Config.h>
|
||||
|
||||
#include "GoogleVRHelpers.h"
|
||||
|
||||
#include <gl/GLShaders.h>
|
||||
#include <shared/Bilateral.h>
|
||||
|
||||
#include <gpu/DrawTransformUnitQuad_vert.h>
|
||||
#include <gpu/DrawTexcoordRectTransformUnitQuad_vert.h>
|
||||
#include <gpu/DrawViewportQuadTransformTexcoord_vert.h>
|
||||
#include <gpu/DrawTexture_frag.h>
|
||||
#include <gpu/DrawTextureOpaque_frag.h>
|
||||
#include <gpu/DrawColoredTexture_frag.h>
|
||||
|
||||
#include <render-utils/simple_vert.h>
|
||||
#include <render-utils/simple_frag.h>
|
||||
#include <render-utils/simple_textured_frag.h>
|
||||
#include <render-utils/simple_textured_unlit_frag.h>
|
||||
|
||||
#include <render-utils/deferred_light_vert.h>
|
||||
#include <render-utils/deferred_light_point_vert.h>
|
||||
#include <render-utils/deferred_light_spot_vert.h>
|
||||
|
||||
#include <render-utils/directional_ambient_light_frag.h>
|
||||
#include <render-utils/directional_skybox_light_frag.h>
|
||||
|
||||
#include <render-utils/standardTransformPNTC_vert.h>
|
||||
#include <render-utils/standardDrawTexture_frag.h>
|
||||
|
||||
#include <render-utils/model_vert.h>
|
||||
#include <render-utils/model_shadow_vert.h>
|
||||
#include <render-utils/model_normal_map_vert.h>
|
||||
#include <render-utils/model_lightmap_vert.h>
|
||||
#include <render-utils/model_lightmap_normal_map_vert.h>
|
||||
#include <render-utils/skin_model_vert.h>
|
||||
#include <render-utils/skin_model_shadow_vert.h>
|
||||
#include <render-utils/skin_model_normal_map_vert.h>
|
||||
|
||||
#include <render-utils/model_frag.h>
|
||||
#include <render-utils/model_shadow_frag.h>
|
||||
#include <render-utils/model_normal_map_frag.h>
|
||||
#include <render-utils/model_normal_specular_map_frag.h>
|
||||
#include <render-utils/model_specular_map_frag.h>
|
||||
#include <render-utils/model_lightmap_frag.h>
|
||||
#include <render-utils/model_lightmap_normal_map_frag.h>
|
||||
#include <render-utils/model_lightmap_normal_specular_map_frag.h>
|
||||
#include <render-utils/model_lightmap_specular_map_frag.h>
|
||||
#include <render-utils/model_translucent_frag.h>
|
||||
#include <render-utils/overlay3D_vert.h>
|
||||
#include <render-utils/overlay3D_frag.h>
|
||||
|
||||
#include <render-utils/sdf_text3D_vert.h>
|
||||
#include <render-utils/sdf_text3D_frag.h>
|
||||
|
||||
#if 0
|
||||
#include <model/skybox_vert.h>
|
||||
#include <model/skybox_frag.h>
|
||||
#include <entities-renderer/textured_particle_frag.h>
|
||||
#include <entities-renderer/textured_particle_vert.h>
|
||||
#include <entities-renderer/paintStroke_vert.h>
|
||||
#include <entities-renderer/paintStroke_frag.h>
|
||||
#include <entities-renderer/polyvox_vert.h>
|
||||
#include <entities-renderer/polyvox_frag.h>
|
||||
#endif
|
||||
|
||||
|
||||
template <typename F>
|
||||
void withFrameBuffer(gvr::Frame& frame, int32_t index, F f) {
|
||||
frame.BindBuffer(index);
|
||||
f();
|
||||
frame.Unbind();
|
||||
}
|
||||
|
||||
|
||||
static const uint64_t kPredictionTimeWithoutVsyncNanos = 50000000;
|
||||
|
||||
// Each shader has two variants: a single-eye ES 2.0 variant, and a multiview
|
||||
// ES 3.0 variant. The multiview vertex shaders use transforms defined by
|
||||
// arrays of mat4 uniforms, using gl_ViewID_OVR to determine the array index.
|
||||
|
||||
#define UNIFORM_LIGHT_POS 20
|
||||
#define UNIFORM_M 16
|
||||
#define UNIFORM_MV 8
|
||||
#define UNIFORM_MVP 0
|
||||
|
||||
#if 0
|
||||
uniform Transform { // API uses “Transform[2]” to refer to instance 2
|
||||
mat4 u_MVP[2];
|
||||
mat4 u_MVMatrix[2];
|
||||
mat4 u_Model;
|
||||
vec3 u_LightPos[2];
|
||||
};
|
||||
static const char *kDiffuseLightingVertexShader = R"glsl(
|
||||
#version 300 es
|
||||
#extension GL_OVR_multiview2 : enable
|
||||
|
||||
layout(num_views=2) in;
|
||||
|
||||
layout(location = 0) uniform mat4 u_MVP[2];
|
||||
layout(location = 8) uniform mat4 u_MVMatrix[2];
|
||||
layout(location = 16) uniform mat4 u_Model;
|
||||
layout(location = 20) uniform vec3 u_LightPos[2];
|
||||
|
||||
layout(location = 0) in vec4 a_Position;
|
||||
layout(location = 1) in vec4 a_Color;
|
||||
layout(location = 2) in vec3 a_Normal;
|
||||
|
||||
out vec4 v_Color;
|
||||
out vec3 v_Grid;
|
||||
|
||||
void main() {
|
||||
mat4 mvp = u_MVP[gl_ViewID_OVR];
|
||||
mat4 modelview = u_MVMatrix[gl_ViewID_OVR];
|
||||
vec3 lightpos = u_LightPos[gl_ViewID_OVR];
|
||||
v_Grid = vec3(u_Model * a_Position);
|
||||
vec3 modelViewVertex = vec3(modelview * a_Position);
|
||||
vec3 modelViewNormal = vec3(modelview * vec4(a_Normal, 0.0));
|
||||
float distance = length(lightpos - modelViewVertex);
|
||||
vec3 lightVector = normalize(lightpos - modelViewVertex);
|
||||
float diffuse = max(dot(modelViewNormal, lightVector), 0.5);
|
||||
diffuse = diffuse * (1.0 / (1.0 + (0.00001 * distance * distance)));
|
||||
v_Color = vec4(a_Color.rgb * diffuse, a_Color.a);
|
||||
gl_Position = mvp * a_Position;
|
||||
}
|
||||
)glsl";
|
||||
#endif
|
||||
|
||||
|
||||
static const char *kSimepleVertexShader = R"glsl(
|
||||
#version 300 es
|
||||
#extension GL_OVR_multiview2 : enable
|
||||
|
||||
layout(num_views=2) in;
|
||||
|
||||
layout(location = 0) in vec4 a_Position;
|
||||
|
||||
out vec4 v_Color;
|
||||
|
||||
void main() {
|
||||
v_Color = vec4(a_Position.xyz, 1.0);
|
||||
gl_Position = vec4(a_Position.xyz, 1.0);
|
||||
}
|
||||
)glsl";
|
||||
|
||||
|
||||
static const char *kPassthroughFragmentShader = R"glsl(
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
in vec4 v_Color;
|
||||
out vec4 FragColor;
|
||||
|
||||
void main() { FragColor = v_Color; }
|
||||
)glsl";
|
||||
|
||||
static void CheckGLError(const char* label) {
|
||||
int gl_error = glGetError();
|
||||
if (gl_error != GL_NO_ERROR) {
|
||||
qWarning("GL error @ %s: %d", label, gl_error);
|
||||
// Crash immediately to make OpenGL errors obvious.
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
// Contains vertex, normal and other data.
|
||||
namespace cube {
|
||||
const std::array<float, 108> CUBE_COORDS{{
|
||||
// Front face
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
1.0f, -1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
|
||||
// Right face
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, -1.0f, 1.0f,
|
||||
1.0f, 1.0f, -1.0f,
|
||||
1.0f, -1.0f, 1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
1.0f, 1.0f, -1.0f,
|
||||
|
||||
// Back face
|
||||
1.0f, 1.0f, -1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
-1.0f, -1.0f, -1.0f,
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
|
||||
// Left face
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
-1.0f, -1.0f, -1.0f,
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, -1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
|
||||
// Top face
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, -1.0f,
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, -1.0f,
|
||||
|
||||
// Bottom face
|
||||
1.0f, -1.0f, -1.0f,
|
||||
1.0f, -1.0f, 1.0f,
|
||||
-1.0f, -1.0f, -1.0f,
|
||||
1.0f, -1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
-1.0f, -1.0f, -1.0f
|
||||
}};
|
||||
|
||||
const std::array<float, 108> CUBE_COLORS{{
|
||||
// front, green
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
|
||||
// right, blue
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
|
||||
// back, also green
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
0.0f, 0.5273f, 0.2656f,
|
||||
|
||||
// left, also blue
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
0.0f, 0.3398f, 0.9023f,
|
||||
|
||||
// top, red
|
||||
0.8359375f, 0.17578125f, 0.125f,
|
||||
0.8359375f, 0.17578125f, 0.125f,
|
||||
0.8359375f, 0.17578125f, 0.125f,
|
||||
0.8359375f, 0.17578125f, 0.125f,
|
||||
0.8359375f, 0.17578125f, 0.125f,
|
||||
0.8359375f, 0.17578125f, 0.125f,
|
||||
|
||||
// bottom, also red
|
||||
0.8359375f, 0.17578125f, 0.125f,
|
||||
0.8359375f, 0.17578125f, 0.125f,
|
||||
0.8359375f, 0.17578125f, 0.125f,
|
||||
0.8359375f, 0.17578125f, 0.125f,
|
||||
0.8359375f, 0.17578125f, 0.125f,
|
||||
0.8359375f, 0.17578125f, 0.125f
|
||||
}};
|
||||
|
||||
const std::array<float, 108> CUBE_NORMALS{{
|
||||
// Front face
|
||||
0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 1.0f,
|
||||
|
||||
// Right face
|
||||
1.0f, 0.0f, 0.0f,
|
||||
1.0f, 0.0f, 0.0f,
|
||||
1.0f, 0.0f, 0.0f,
|
||||
1.0f, 0.0f, 0.0f,
|
||||
1.0f, 0.0f, 0.0f,
|
||||
1.0f, 0.0f, 0.0f,
|
||||
|
||||
// Back face
|
||||
0.0f, 0.0f, -1.0f,
|
||||
0.0f, 0.0f, -1.0f,
|
||||
0.0f, 0.0f, -1.0f,
|
||||
0.0f, 0.0f, -1.0f,
|
||||
0.0f, 0.0f, -1.0f,
|
||||
0.0f, 0.0f, -1.0f,
|
||||
|
||||
// Left face
|
||||
-1.0f, 0.0f, 0.0f,
|
||||
-1.0f, 0.0f, 0.0f,
|
||||
-1.0f, 0.0f, 0.0f,
|
||||
-1.0f, 0.0f, 0.0f,
|
||||
-1.0f, 0.0f, 0.0f,
|
||||
-1.0f, 0.0f, 0.0f,
|
||||
|
||||
// Top face
|
||||
0.0f, 1.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f,
|
||||
|
||||
// Bottom face
|
||||
0.0f, -1.0f, 0.0f,
|
||||
0.0f, -1.0f, 0.0f,
|
||||
0.0f, -1.0f, 0.0f,
|
||||
0.0f, -1.0f, 0.0f,
|
||||
0.0f, -1.0f, 0.0f,
|
||||
0.0f, -1.0f, 0.0f
|
||||
}};
|
||||
}
|
||||
|
||||
namespace triangle {
|
||||
static std::array<float, 9> TRIANGLE_VERTS {{
|
||||
-0.5f, -0.5f, 0.0f,
|
||||
0.5f, -0.5f, 0.0f,
|
||||
0.0f, 0.5f, 0.0f
|
||||
}};
|
||||
}
|
||||
|
||||
std::array<gvr::BufferViewport, 2> buildViewports(const std::unique_ptr<gvr::GvrApi> &gvrapi) {
|
||||
return { {gvrapi->CreateBufferViewport(), gvrapi->CreateBufferViewport()} };
|
||||
};
|
||||
|
||||
const std::string VERTEX_SHADER_DEFINES{ R"GLSL(
|
||||
#version 300 es
|
||||
#extension GL_EXT_clip_cull_distance : enable
|
||||
#define GPU_VERTEX_SHADER
|
||||
#define GPU_SSBO_TRANSFORM_OBJECT 1
|
||||
#define GPU_TRANSFORM_IS_STEREO
|
||||
#define GPU_TRANSFORM_STEREO_CAMERA
|
||||
#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED
|
||||
#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN
|
||||
)GLSL" };
|
||||
|
||||
const std::string PIXEL_SHADER_DEFINES{ R"GLSL(
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
#define GPU_PIXEL_SHADER
|
||||
#define GPU_TRANSFORM_IS_STEREO
|
||||
#define GPU_TRANSFORM_STEREO_CAMERA
|
||||
#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED
|
||||
#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN
|
||||
)GLSL" };
|
||||
|
||||
|
||||
#if defined(GVR)
|
||||
NativeRenderer::NativeRenderer(gvr_context *vrContext) :
|
||||
_gvrapi(new gvr::GvrApi(vrContext, false)),
|
||||
_viewports(buildViewports(_gvrapi)),
|
||||
_gvr_viewer_type(_gvrapi->GetViewerType())
|
||||
#else
|
||||
NativeRenderer::NativeRenderer(void *vrContext)
|
||||
#endif
|
||||
{
|
||||
start = std::chrono::system_clock::now();
|
||||
qDebug() << "QQQ" << __FUNCTION__;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a raw text file, saved as a resource, into an OpenGL ES shader.
|
||||
*
|
||||
* @param type The type of shader we will be creating.
|
||||
* @param resId The resource ID of the raw text file.
|
||||
* @return The shader object handler.
|
||||
*/
|
||||
int LoadGLShader(int type, const char *shadercode) {
|
||||
GLuint result = 0;
|
||||
std::string shaderError;
|
||||
static const std::string SHADER_DEFINES;
|
||||
if (!gl::compileShader(type, shadercode, SHADER_DEFINES, result, shaderError)) {
|
||||
qWarning() << "QQQ" << __FUNCTION__ << "Shader compile failure" << shaderError.c_str();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Computes a texture size that has approximately half as many pixels. This is
|
||||
// equivalent to scaling each dimension by approximately sqrt(2)/2.
|
||||
static gvr::Sizei HalfPixelCount(const gvr::Sizei &in) {
|
||||
// Scale each dimension by sqrt(2)/2 ~= 7/10ths.
|
||||
gvr::Sizei out;
|
||||
out.width = (7 * in.width) / 10;
|
||||
out.height = (7 * in.height) / 10;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
#if defined(GVR)
|
||||
void NativeRenderer::InitializeVR() {
|
||||
_gvrapi->InitializeGl();
|
||||
bool multiviewEnabled = _gvrapi->IsFeatureSupported(GVR_FEATURE_MULTIVIEW);
|
||||
qWarning() << "QQQ" << __FUNCTION__ << "Multiview enabled " << multiviewEnabled;
|
||||
// Because we are using 2X MSAA, we can render to half as many pixels and
|
||||
// achieve similar quality.
|
||||
_renderSize = HalfPixelCount(_gvrapi->GetMaximumEffectiveRenderTargetSize());
|
||||
|
||||
std::vector<gvr::BufferSpec> specs;
|
||||
specs.push_back(_gvrapi->CreateBufferSpec());
|
||||
specs[0].SetColorFormat(GVR_COLOR_FORMAT_RGBA_8888);
|
||||
specs[0].SetDepthStencilFormat(GVR_DEPTH_STENCIL_FORMAT_DEPTH_16);
|
||||
specs[0].SetSamples(2);
|
||||
gvr::Sizei half_size = {_renderSize.width / 2, _renderSize.height};
|
||||
specs[0].SetMultiviewLayers(2);
|
||||
specs[0].SetSize(half_size);
|
||||
|
||||
_swapchain.reset(new gvr::SwapChain(_gvrapi->CreateSwapChain(specs)));
|
||||
_viewportlist.reset(new gvr::BufferViewportList(_gvrapi->CreateEmptyBufferViewportList()));
|
||||
}
|
||||
void NativeRenderer::PrepareFramebuffer() {
|
||||
const gvr::Sizei recommended_size = HalfPixelCount(
|
||||
_gvrapi->GetMaximumEffectiveRenderTargetSize());
|
||||
if (_renderSize.width != recommended_size.width ||
|
||||
_renderSize.height != recommended_size.height) {
|
||||
// We need to resize the framebuffer. Note that multiview uses two texture
|
||||
// layers, each with half the render width.
|
||||
gvr::Sizei framebuffer_size = recommended_size;
|
||||
framebuffer_size.width /= 2;
|
||||
_swapchain->ResizeBuffer(0, framebuffer_size);
|
||||
_renderSize = recommended_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void testShaderBuild(const char* vs_src, const char * fs_src) {
|
||||
std::string error;
|
||||
GLuint vs, fs;
|
||||
if (!gl::compileShader(GL_VERTEX_SHADER, vs_src, VERTEX_SHADER_DEFINES, vs, error) ||
|
||||
!gl::compileShader(GL_FRAGMENT_SHADER, fs_src, PIXEL_SHADER_DEFINES, fs, error)) {
|
||||
throw std::runtime_error("Failed to compile shader");
|
||||
}
|
||||
auto pr = gl::compileProgram({ vs, fs }, error);
|
||||
if (!pr) {
|
||||
throw std::runtime_error("Failed to link shader");
|
||||
}
|
||||
}
|
||||
|
||||
void NativeRenderer::InitializeGl() {
|
||||
qDebug() << "QQQ" << __FUNCTION__;
|
||||
//gl::initModuleGl();
|
||||
#if defined(GVR)
|
||||
InitializeVR();
|
||||
#endif
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
|
||||
|
||||
const uint32_t vertShader = LoadGLShader(GL_VERTEX_SHADER, kSimepleVertexShader);
|
||||
//const uint32_t vertShader = LoadGLShader(GL_VERTEX_SHADER, kDiffuseLightingVertexShader);
|
||||
const uint32_t fragShader = LoadGLShader(GL_FRAGMENT_SHADER, kPassthroughFragmentShader);
|
||||
std::string error;
|
||||
_cubeProgram = gl::compileProgram({ vertShader, fragShader }, error);
|
||||
CheckGLError("build program");
|
||||
|
||||
glGenBuffers(1, &_cubeBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _cubeBuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 9, triangle::TRIANGLE_VERTS.data(), GL_STATIC_DRAW);
|
||||
/*
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 3, NULL, GL_STATIC_DRAW);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 0, sizeof(float) * 108, cube::CUBE_COORDS.data());
|
||||
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 1, sizeof(float) * 108, cube::CUBE_COLORS.data());
|
||||
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 2, sizeof(float) * 108, cube::CUBE_NORMALS.data());
|
||||
*/
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
CheckGLError("upload vertices");
|
||||
|
||||
glGenVertexArrays(1, &_cubeVao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _cubeBuffer);
|
||||
glBindVertexArray(_cubeVao);
|
||||
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
/*
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, (const void*)(sizeof(float) * 108 * 1) );
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(2, 3, GL_FLOAT, false, 0, (const void*)(sizeof(float) * 108 * 2));
|
||||
glEnableVertexAttribArray(2);
|
||||
*/
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
CheckGLError("build vao ");
|
||||
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&]{
|
||||
testShaderBuild(sdf_text3D_vert, sdf_text3D_frag);
|
||||
|
||||
testShaderBuild(DrawTransformUnitQuad_vert, DrawTexture_frag);
|
||||
testShaderBuild(DrawTexcoordRectTransformUnitQuad_vert, DrawTexture_frag);
|
||||
testShaderBuild(DrawViewportQuadTransformTexcoord_vert, DrawTexture_frag);
|
||||
testShaderBuild(DrawTransformUnitQuad_vert, DrawTextureOpaque_frag);
|
||||
testShaderBuild(DrawTransformUnitQuad_vert, DrawColoredTexture_frag);
|
||||
|
||||
testShaderBuild(simple_vert, simple_frag);
|
||||
testShaderBuild(simple_vert, simple_textured_frag);
|
||||
testShaderBuild(simple_vert, simple_textured_unlit_frag);
|
||||
testShaderBuild(deferred_light_vert, directional_ambient_light_frag);
|
||||
testShaderBuild(deferred_light_vert, directional_skybox_light_frag);
|
||||
testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag);
|
||||
testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag);
|
||||
|
||||
testShaderBuild(model_vert, model_frag);
|
||||
testShaderBuild(model_normal_map_vert, model_normal_map_frag);
|
||||
testShaderBuild(model_vert, model_specular_map_frag);
|
||||
testShaderBuild(model_normal_map_vert, model_normal_specular_map_frag);
|
||||
testShaderBuild(model_vert, model_translucent_frag);
|
||||
testShaderBuild(model_normal_map_vert, model_translucent_frag);
|
||||
testShaderBuild(model_lightmap_vert, model_lightmap_frag);
|
||||
testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_map_frag);
|
||||
testShaderBuild(model_lightmap_vert, model_lightmap_specular_map_frag);
|
||||
testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_specular_map_frag);
|
||||
|
||||
testShaderBuild(skin_model_vert, model_frag);
|
||||
testShaderBuild(skin_model_normal_map_vert, model_normal_map_frag);
|
||||
testShaderBuild(skin_model_vert, model_specular_map_frag);
|
||||
testShaderBuild(skin_model_normal_map_vert, model_normal_specular_map_frag);
|
||||
testShaderBuild(skin_model_vert, model_translucent_frag);
|
||||
testShaderBuild(skin_model_normal_map_vert, model_translucent_frag);
|
||||
|
||||
testShaderBuild(model_shadow_vert, model_shadow_frag);
|
||||
|
||||
testShaderBuild(overlay3D_vert, overlay3D_frag);
|
||||
|
||||
#if 0
|
||||
testShaderBuild(textured_particle_vert, textured_particle_frag);
|
||||
testShaderBuild(skybox_vert, skybox_frag);
|
||||
testShaderBuild(paintStroke_vert,paintStroke_frag);
|
||||
testShaderBuild(polyvox_vert, polyvox_frag);
|
||||
#endif
|
||||
|
||||
});
|
||||
|
||||
qDebug() << "done";
|
||||
}
|
||||
|
||||
static const float kZNear = 1.0f;
|
||||
static const float kZFar = 100.0f;
|
||||
static const gvr_rectf fullscreen = {0, 1, 0, 1};
|
||||
|
||||
void NativeRenderer::DrawFrame() {
|
||||
auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now() - start);
|
||||
glm::vec3 v;
|
||||
v.r = (float) (now.count() % 1000) / 1000.0f;
|
||||
v.g = 1.0f - v.r;
|
||||
v.b = 1.0f;
|
||||
|
||||
PrepareFramebuffer();
|
||||
|
||||
// A client app does its rendering here.
|
||||
gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
|
||||
target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
|
||||
|
||||
using namespace googlevr;
|
||||
using namespace bilateral;
|
||||
const auto gvrHeadPose = _gvrapi->GetHeadSpaceFromStartSpaceRotation(target_time);
|
||||
_head_view = toGlm(gvrHeadPose);
|
||||
_viewportlist->SetToRecommendedBufferViewports();
|
||||
|
||||
glm::mat4 eye_views[2];
|
||||
for_each_side([&](bilateral::Side side) {
|
||||
int eye = index(side);
|
||||
const gvr::Eye gvr_eye = eye == 0 ? GVR_LEFT_EYE : GVR_RIGHT_EYE;
|
||||
const auto& eyeView = eye_views[eye] = toGlm(_gvrapi->GetEyeFromHeadMatrix(gvr_eye)) * _head_view;
|
||||
auto& viewport = _viewports[eye];
|
||||
|
||||
_viewportlist->GetBufferViewport(eye, &viewport);
|
||||
viewport.SetSourceUv(fullscreen);
|
||||
viewport.SetSourceLayer(eye);
|
||||
_viewportlist->SetBufferViewport(eye, viewport);
|
||||
const auto &mvc = _modelview_cube[eye] = eyeView * _model_cube;
|
||||
const auto &mvf = _modelview_floor[eye] = eyeView * _model_floor;
|
||||
const gvr_rectf fov = viewport.GetSourceFov();
|
||||
const glm::mat4 perspective = perspectiveMatrixFromView(fov, kZNear, kZFar);
|
||||
_modelview_projection_cube[eye] = perspective * mvc;
|
||||
_modelview_projection_floor[eye] = perspective * mvf;
|
||||
_light_pos_eye_space[eye] = glm::vec3(eyeView * _light_pos_world_space);
|
||||
});
|
||||
|
||||
|
||||
gvr::Frame frame = _swapchain->AcquireFrame();
|
||||
withFrameBuffer(frame, 0, [&]{
|
||||
glClearColor(v.r, v.g, v.b, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glViewport(0, 0, _renderSize.width / 2, _renderSize.height);
|
||||
glUseProgram(_cubeProgram);
|
||||
glBindVertexArray(_cubeVao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
/*
|
||||
float* fp;
|
||||
fp = (float*)&_light_pos_eye_space[0];
|
||||
glUniform3fv(UNIFORM_LIGHT_POS, 2, fp);
|
||||
fp = (float*)&_modelview_cube[0];
|
||||
glUniformMatrix4fv(UNIFORM_MV, 2, GL_FALSE, fp);
|
||||
fp = (float*)&_modelview_projection_cube[0];
|
||||
glUniformMatrix4fv(UNIFORM_MVP, 2, GL_FALSE, fp);
|
||||
fp = (float*)&_model_cube;
|
||||
glUniformMatrix4fv(UNIFORM_M, 1, GL_FALSE, fp);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
*/
|
||||
glBindVertexArray(0);
|
||||
});
|
||||
|
||||
frame.Submit(*_viewportlist, gvrHeadPose);
|
||||
CheckGLError("onDrawFrame");
|
||||
|
||||
}
|
||||
|
||||
void NativeRenderer::OnTriggerEvent() {
|
||||
qDebug() << "QQQ" << __FUNCTION__;
|
||||
}
|
||||
|
||||
void NativeRenderer::OnPause() {
|
||||
qDebug() << "QQQ" << __FUNCTION__;
|
||||
_gvrapi->PauseTracking();
|
||||
}
|
||||
|
||||
void NativeRenderer::OnResume() {
|
||||
qDebug() << "QQQ" << __FUNCTION__;
|
||||
_gvrapi->ResumeTracking();
|
||||
_gvrapi->RefreshViewerProfile();
|
||||
}
|
60
android/app/src/main/cpp/renderer.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <array>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#define GVR
|
||||
|
||||
#if defined(GVR)
|
||||
#include <vr/gvr/capi/include/gvr.h>
|
||||
#endif
|
||||
|
||||
class NativeRenderer {
|
||||
public:
|
||||
|
||||
#if defined(GVR)
|
||||
NativeRenderer(gvr_context* vrContext);
|
||||
#else
|
||||
NativeRenderer(void* vrContext);
|
||||
#endif
|
||||
|
||||
void InitializeGl();
|
||||
void DrawFrame();
|
||||
void OnTriggerEvent();
|
||||
void OnPause();
|
||||
void OnResume();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> start;
|
||||
#if defined(GVR)
|
||||
void InitializeVR();
|
||||
void PrepareFramebuffer();
|
||||
|
||||
std::unique_ptr<gvr::GvrApi> _gvrapi;
|
||||
gvr::ViewerType _gvr_viewer_type;
|
||||
std::unique_ptr<gvr::BufferViewportList> _viewportlist;
|
||||
std::unique_ptr<gvr::SwapChain> _swapchain;
|
||||
std::array<gvr::BufferViewport, 2> _viewports;
|
||||
gvr::Sizei _renderSize;
|
||||
#endif
|
||||
|
||||
uint32_t _cubeBuffer { 0 };
|
||||
uint32_t _cubeVao { 0 };
|
||||
uint32_t _cubeProgram { 0 };
|
||||
|
||||
glm::mat4 _head_view;
|
||||
glm::mat4 _model_cube;
|
||||
glm::mat4 _camera;
|
||||
glm::mat4 _view;
|
||||
glm::mat4 _model_floor;
|
||||
|
||||
std::array<glm::mat4, 2> _modelview_cube;
|
||||
std::array<glm::mat4, 2> _modelview_floor;
|
||||
std::array<glm::mat4, 2> _modelview_projection_cube;
|
||||
std::array<glm::mat4, 2> _modelview_projection_floor;
|
||||
std::array<glm::vec3, 2> _light_pos_eye_space;
|
||||
const glm::vec4 _light_pos_world_space{ 0, 2, 0, 1};
|
||||
};
|
|
@ -0,0 +1,105 @@
|
|||
package org.saintandreas.testapp;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import com.google.vr.ndk.base.AndroidCompat;
|
||||
import com.google.vr.ndk.base.GvrLayout;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
private final static int IMMERSIVE_STICKY_VIEW_FLAGS = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||
|
||||
static {
|
||||
System.loadLibrary("gvr");
|
||||
System.loadLibrary("native-lib");
|
||||
}
|
||||
|
||||
private long nativeRenderer;
|
||||
private GvrLayout gvrLayout;
|
||||
private GLSurfaceView surfaceView;
|
||||
|
||||
private native long nativeCreateRenderer(ClassLoader appClassLoader, Context context, long nativeGvrContext);
|
||||
private native void nativeDestroyRenderer(long renderer);
|
||||
private native void nativeInitializeGl(long renderer);
|
||||
private native void nativeDrawFrame(long renderer);
|
||||
private native void nativeOnTriggerEvent(long renderer);
|
||||
private native void nativeOnPause(long renderer);
|
||||
private native void nativeOnResume(long renderer);
|
||||
|
||||
class NativeRenderer implements GLSurfaceView.Renderer {
|
||||
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { nativeInitializeGl(nativeRenderer); }
|
||||
@Override public void onSurfaceChanged(GL10 gl, int width, int height) { }
|
||||
@Override public void onDrawFrame(GL10 gl) {
|
||||
nativeDrawFrame(nativeRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setImmersiveSticky();
|
||||
getWindow()
|
||||
.getDecorView()
|
||||
.setOnSystemUiVisibilityChangeListener((int visibility)->{
|
||||
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { setImmersiveSticky(); }
|
||||
});
|
||||
|
||||
gvrLayout = new GvrLayout(this);
|
||||
nativeRenderer = nativeCreateRenderer(
|
||||
getClass().getClassLoader(),
|
||||
getApplicationContext(),
|
||||
gvrLayout.getGvrApi().getNativeGvrContext());
|
||||
|
||||
surfaceView = new GLSurfaceView(this);
|
||||
surfaceView.setEGLContextClientVersion(3);
|
||||
surfaceView.setEGLConfigChooser(8, 8, 8, 0, 0, 0);
|
||||
surfaceView.setPreserveEGLContextOnPause(true);
|
||||
surfaceView.setRenderer(new NativeRenderer());
|
||||
|
||||
gvrLayout.setPresentationView(surfaceView);
|
||||
setContentView(gvrLayout);
|
||||
if (gvrLayout.setAsyncReprojectionEnabled(true)) {
|
||||
AndroidCompat.setSustainedPerformanceMode(this, true);
|
||||
}
|
||||
AndroidCompat.setVrModeEnabled(this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
gvrLayout.shutdown();
|
||||
nativeDestroyRenderer(nativeRenderer);
|
||||
nativeRenderer = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
surfaceView.queueEvent(()->nativeOnPause(nativeRenderer));
|
||||
surfaceView.onPause();
|
||||
gvrLayout.onPause();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
gvrLayout.onResume();
|
||||
surfaceView.onResume();
|
||||
surfaceView.queueEvent(()->nativeOnResume(nativeRenderer));
|
||||
}
|
||||
|
||||
private void setImmersiveSticky() {
|
||||
getWindow().getDecorView().setSystemUiVisibility(IMMERSIVE_STICKY_VIEW_FLAGS);
|
||||
}
|
||||
}
|
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 6 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 14 KiB |
4
android/app/src/main/res/values/colors.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="white_opaque">#ffffff</color>
|
||||
</resources>
|
3
android/app/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">TestApp</string>
|
||||
</resources>
|
15
android/app/src/main/res/values/styles.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="android:style/Theme.NoTitleBar.Fullscreen">
|
||||
<!-- Customize your theme here. -->
|
||||
<!--item name="android:windowFullscreen">true</item-->
|
||||
<!--item name="android:windowNoTitle">true</item-->
|
||||
<!--item name="android:windowActionBar">false</item-->
|
||||
|
||||
<!--item name="android:windowContentOverlay">@null</item-->
|
||||
<!--item name="android:background">@color/white_opaque</item-->
|
||||
</style>
|
||||
|
||||
|
||||
</resources>
|
91
android/build.gradle
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.3.3'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
task extractQt5jars(type: Copy) {
|
||||
from fileTree(QT5_ROOT + "/jar")
|
||||
into("${project.rootDir}/libraries/jar")
|
||||
include("*.jar")
|
||||
}
|
||||
|
||||
task extractQt5so(type: Copy) {
|
||||
from fileTree(QT5_ROOT + "/lib")
|
||||
into("${project.rootDir}/libraries/jni/armeabi-v7a/")
|
||||
include("libQt5AndroidExtras.so")
|
||||
include("libQt5Concurrent.so")
|
||||
include("libQt5Core.so")
|
||||
include("libQt5Gamepad.so")
|
||||
include("libQt5Gui.so")
|
||||
include("libQt5Location.so")
|
||||
include("libQt5Multimedia.so")
|
||||
include("libQt5MultimediaQuick_p.so")
|
||||
include("libQt5Network.so")
|
||||
include("libQt5NetworkAuth.so")
|
||||
include("libQt5OpenGL.so")
|
||||
include("libQt5Positioning.so")
|
||||
include("libQt5Qml.so")
|
||||
include("libQt5Quick.so")
|
||||
include("libQt5QuickControls2.so")
|
||||
include("libQt5QuickParticles.so")
|
||||
include("libQt5QuickTemplates2.so")
|
||||
include("libQt5QuickWidgets.so")
|
||||
include("libQt5Script.so")
|
||||
include("libQt5ScriptTools.so")
|
||||
include("libQt5Sensors.so")
|
||||
include("libQt5Svg.so")
|
||||
include("libQt5WebChannel.so")
|
||||
include("libQt5WebSockets.so")
|
||||
include("libQt5WebView.so")
|
||||
include("libQt5Widgets.so")
|
||||
include("libQt5Xml.so")
|
||||
include("libQt5XmlPatterns.so")
|
||||
}
|
||||
|
||||
task extractAudioSo(type: Copy) {
|
||||
from zipTree(GVR_ROOT + "/libraries/sdk-audio-1.80.0.aar")
|
||||
into "${project.rootDir}/libraries/"
|
||||
include "jni/armeabi-v7a/libgvr_audio.so"
|
||||
}
|
||||
|
||||
task extractGvrSo(type: Copy) {
|
||||
from zipTree(GVR_ROOT + "/libraries/sdk-base-1.80.0.aar")
|
||||
into "${project.rootDir}/libraries/"
|
||||
include "jni/armeabi-v7a/libgvr.so"
|
||||
}
|
||||
|
||||
task extractNdk { }
|
||||
extractNdk.dependsOn extractAudioSo
|
||||
extractNdk.dependsOn extractGvrSo
|
||||
|
||||
task extractQt5 { }
|
||||
extractQt5.dependsOn extractQt5so
|
||||
extractQt5.dependsOn extractQt5jars
|
||||
|
||||
task extractBinaries { }
|
||||
extractBinaries.dependsOn extractQt5
|
||||
extractBinaries.dependsOn extractNdk
|
||||
|
||||
task deleteBinaries(type: Delete) {
|
||||
delete "${project.rootDir}/libraries/jni"
|
||||
}
|
||||
|
||||
//clean.dependsOn(deleteBinaries)
|
17
android/gradle.properties
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
1
android/settings.gradle
Normal file
|
@ -0,0 +1 @@
|
|||
include ':app'
|
|
@ -1,82 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- IMPORTANT: Do not manually manipulate this automatically generated file, changes will be gone after the next build! -->
|
||||
|
||||
<manifest package="${ANDROID_APK_PACKAGE}" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="${ANDROID_APK_VERSION_NAME}" android:versionCode="${ANDROID_APK_VERSION_CODE}" android:installLocation="auto">
|
||||
<application
|
||||
android:hardwareAccelerated="true"
|
||||
android:name="org.qtproject.qt5.android.bindings.QtApplication"
|
||||
android:label="@string/AppDisplayName"
|
||||
android:icon="@drawable/icon"
|
||||
android:debuggable="${ANDROID_APK_DEBUGGABLE}">
|
||||
|
||||
<!-- VR MODE -->
|
||||
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
|
||||
|
||||
<activity
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
|
||||
android:name="${ANDROID_ACTIVITY_NAME}"
|
||||
android:label="@string/AppDisplayName"
|
||||
android:screenOrientation="landscape"
|
||||
android:launchMode="singleTop"
|
||||
${ANDROID_APK_THEME}>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
<!-- Deploy Qt libs as part of package -->
|
||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
|
||||
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
|
||||
<!-- Run with local libs -->
|
||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<!-- Messages maps -->
|
||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||
<!-- Messages maps -->
|
||||
|
||||
<!-- Splash screen -->
|
||||
<!-- <meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/> -->
|
||||
|
||||
${ANDROID_EXTRA_ACTIVITY_XML}
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.oculusvr.vrlib.PlatformActivity"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="landscape"
|
||||
android:configChanges="screenSize|orientation|keyboardHidden|keyboard">
|
||||
</activity>
|
||||
|
||||
${ANDROID_EXTRA_APPLICATION_XML}
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="${ANDROID_API_LEVEL}" android:targetSdkVersion="${ANDROID_API_LEVEL}"/>
|
||||
|
||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default permissions. -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
|
||||
<!-- camera permission required for GEAR VR passthrough camera -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default features. -->
|
||||
|
||||
<!-- Tell the system this app requires OpenGL ES 3.0. -->
|
||||
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
|
||||
</manifest>
|
|
@ -1,159 +0,0 @@
|
|||
#
|
||||
# QtCreateAPK.cmake
|
||||
#
|
||||
# Created by Stephen Birarda on 11/18/14.
|
||||
# Copyright 2013 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
|
||||
#
|
||||
|
||||
#
|
||||
# OPTIONS
|
||||
# These options will modify how QtCreateAPK behaves. May be useful if somebody wants to fork.
|
||||
# For High Fidelity purposes these should not need to be changed.
|
||||
#
|
||||
set(ANDROID_THIS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) # Directory this CMake file is in
|
||||
|
||||
if (POLICY CMP0026)
|
||||
cmake_policy(SET CMP0026 OLD)
|
||||
endif ()
|
||||
|
||||
macro(qt_create_apk)
|
||||
if(ANDROID_APK_FULLSCREEN)
|
||||
set(ANDROID_APK_THEME "android:theme=\"@android:style/Theme.NoTitleBar.Fullscreen\"")
|
||||
else()
|
||||
set(ANDROID_APK_THEME "")
|
||||
endif()
|
||||
|
||||
if (UPPER_CMAKE_BUILD_TYPE MATCHES RELEASE)
|
||||
set(ANDROID_APK_DEBUGGABLE "false")
|
||||
set(ANDROID_APK_RELEASE_LOCAL ${ANDROID_APK_RELEASE})
|
||||
else ()
|
||||
set(ANDROID_APK_DEBUGGABLE "true")
|
||||
set(ANDROID_APK_RELEASE_LOCAL "0")
|
||||
endif ()
|
||||
|
||||
# Create "AndroidManifest.xml"
|
||||
configure_file("${ANDROID_THIS_DIRECTORY}/AndroidManifest.xml.in" "${ANDROID_APK_BUILD_DIR}/AndroidManifest.xml")
|
||||
|
||||
# create "strings.xml"
|
||||
configure_file("${ANDROID_THIS_DIRECTORY}/strings.xml.in" "${ANDROID_APK_BUILD_DIR}/res/values/strings.xml")
|
||||
|
||||
# find androiddeployqt
|
||||
find_program(ANDROID_DEPLOY_QT androiddeployqt HINTS "${QT_DIR}/bin")
|
||||
|
||||
# set the path to our app shared library
|
||||
set(EXECUTABLE_DESTINATION_PATH "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}/lib${TARGET_NAME}.so")
|
||||
|
||||
# add our dependencies to the deployment file
|
||||
get_property(_DEPENDENCIES TARGET ${TARGET_NAME} PROPERTY INTERFACE_LINK_LIBRARIES)
|
||||
|
||||
foreach(_IGNORE_COPY IN LISTS IGNORE_COPY_LIBS)
|
||||
list(REMOVE_ITEM _DEPENDENCIES ${_IGNORE_COPY})
|
||||
endforeach()
|
||||
|
||||
foreach(_DEP IN LISTS _DEPENDENCIES)
|
||||
if (NOT TARGET ${_DEP})
|
||||
list(APPEND _DEPS_LIST ${_DEP})
|
||||
else ()
|
||||
if(NOT _DEP MATCHES "Qt5::.*")
|
||||
get_property(_DEP_LOCATION TARGET ${_DEP} PROPERTY "LOCATION_${CMAKE_BUILD_TYPE}")
|
||||
|
||||
# recurisvely add libraries which are dependencies of this target
|
||||
get_property(_DEP_DEPENDENCIES TARGET ${_DEP} PROPERTY INTERFACE_LINK_LIBRARIES)
|
||||
|
||||
foreach(_SUB_DEP IN LISTS _DEP_DEPENDENCIES)
|
||||
if (NOT TARGET ${_SUB_DEP} AND NOT _SUB_DEP MATCHES "Qt5::.*")
|
||||
list(APPEND _DEPS_LIST ${_SUB_DEP})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
list(APPEND _DEPS_LIST ${_DEP_LOCATION})
|
||||
endif()
|
||||
endif ()
|
||||
endforeach()
|
||||
|
||||
list(REMOVE_DUPLICATES _DEPS_LIST)
|
||||
|
||||
# just copy static libs to apk libs folder - don't add to deps list
|
||||
foreach(_LOCATED_DEP IN LISTS _DEPS_LIST)
|
||||
if (_LOCATED_DEP MATCHES "\\.a$")
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${_LOCATED_DEP} "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}"
|
||||
)
|
||||
list(REMOVE_ITEM _DEPS_LIST ${_LOCATED_DEP})
|
||||
endif ()
|
||||
endforeach()
|
||||
|
||||
string(REPLACE ";" "," _DEPS "${_DEPS_LIST}")
|
||||
|
||||
configure_file("${ANDROID_THIS_DIRECTORY}/deployment-file.json.in" "${TARGET_NAME}-deployment.json")
|
||||
|
||||
# copy the res folder from the target to the apk build dir
|
||||
add_custom_target(
|
||||
${TARGET_NAME}-copy-res
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/res" "${ANDROID_APK_BUILD_DIR}/res"
|
||||
)
|
||||
|
||||
# copy the assets folder from the target to the apk build dir
|
||||
add_custom_target(
|
||||
${TARGET_NAME}-copy-assets
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/assets" "${ANDROID_APK_BUILD_DIR}/assets"
|
||||
)
|
||||
|
||||
# copy the java folder from src to the apk build dir
|
||||
add_custom_target(
|
||||
${TARGET_NAME}-copy-java
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/src/java" "${ANDROID_APK_BUILD_DIR}/src"
|
||||
)
|
||||
|
||||
# copy the libs folder from src to the apk build dir
|
||||
add_custom_target(
|
||||
${TARGET_NAME}-copy-libs
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/libs" "${ANDROID_APK_BUILD_DIR}/libs"
|
||||
)
|
||||
|
||||
# handle setup for ndk-gdb
|
||||
add_custom_target(${TARGET_NAME}-gdb DEPENDS ${TARGET_NAME})
|
||||
|
||||
if (ANDROID_APK_DEBUGGABLE)
|
||||
get_property(TARGET_LOCATION TARGET ${TARGET_NAME} PROPERTY LOCATION)
|
||||
|
||||
set(GDB_SOLIB_PATH ${ANDROID_APK_BUILD_DIR}/obj/local/${ANDROID_NDK_ABI_NAME}/)
|
||||
|
||||
# generate essential Android Makefiles
|
||||
file(WRITE ${ANDROID_APK_BUILD_DIR}/jni/Android.mk "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n")
|
||||
file(WRITE ${ANDROID_APK_BUILD_DIR}/jni/Application.mk "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n")
|
||||
|
||||
# create gdb.setup
|
||||
get_directory_property(PROJECT_INCLUDES DIRECTORY ${PROJECT_SOURCE_DIR} INCLUDE_DIRECTORIES)
|
||||
string(REGEX REPLACE ";" " " PROJECT_INCLUDES "${PROJECT_INCLUDES}")
|
||||
file(WRITE ${ANDROID_APK_BUILD_DIR}/libs/${ANDROID_NDK_ABI_NAME}/gdb.setup "set solib-search-path ${GDB_SOLIB_PATH}\n")
|
||||
file(APPEND ${ANDROID_APK_BUILD_DIR}/libs/${ANDROID_NDK_ABI_NAME}/gdb.setup "directory ${PROJECT_INCLUDES}\n")
|
||||
|
||||
# copy lib to obj
|
||||
add_custom_command(TARGET ${TARGET_NAME}-gdb PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${GDB_SOLIB_PATH})
|
||||
add_custom_command(TARGET ${TARGET_NAME}-gdb PRE_BUILD COMMAND cp ${TARGET_LOCATION} ${GDB_SOLIB_PATH})
|
||||
|
||||
# strip symbols
|
||||
add_custom_command(TARGET ${TARGET_NAME}-gdb PRE_BUILD COMMAND ${CMAKE_STRIP} ${TARGET_LOCATION})
|
||||
endif ()
|
||||
|
||||
# use androiddeployqt to create the apk
|
||||
add_custom_target(${TARGET_NAME}-apk
|
||||
COMMAND ${ANDROID_DEPLOY_QT} --input "${TARGET_NAME}-deployment.json" --output "${ANDROID_APK_OUTPUT_DIR}" --android-platform android-${ANDROID_API_LEVEL} ${ANDROID_DEPLOY_QT_INSTALL} --verbose --deployment bundled "\\$(ARGS)"
|
||||
DEPENDS ${TARGET_NAME} ${TARGET_NAME}-copy-res ${TARGET_NAME}-copy-assets ${TARGET_NAME}-copy-java ${TARGET_NAME}-copy-libs ${TARGET_NAME}-gdb
|
||||
)
|
||||
|
||||
# rename the APK if the caller asked us to
|
||||
if (ANDROID_APK_CUSTOM_NAME)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}-apk
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E rename "${ANDROID_APK_OUTPUT_DIR}/bin/QtApp-debug.apk" "${ANDROID_APK_OUTPUT_DIR}/bin/${ANDROID_APK_CUSTOM_NAME}"
|
||||
)
|
||||
endif ()
|
||||
endmacro()
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"qt": "@QT_DIR@",
|
||||
"sdk": "@ANDROID_SDK_ROOT@",
|
||||
"ndk": "@ANDROID_NDK@",
|
||||
"toolchain-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@",
|
||||
"tool-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@",
|
||||
"toolchain-version": "@ANDROID_COMPILER_VERSION@",
|
||||
"ndk-host": "@ANDROID_NDK_HOST_SYSTEM_NAME@",
|
||||
"target-architecture": "@ANDROID_ABI@",
|
||||
"application-binary": "@EXECUTABLE_DESTINATION_PATH@",
|
||||
"android-extra-libs": "@_DEPS@",
|
||||
"android-package-source-directory": "@ANDROID_APK_BUILD_DIR@"
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
|
||||
<!-- IMPORTANT: Do not manually manipulate this automatically generated file, changes will be gone after the next build! -->
|
||||
|
||||
<resources>
|
||||
<string name="AppDisplayName">${ANDROID_APP_DISPLAY_NAME}</string>
|
||||
|
||||
<string name="ministro_not_found_msg">Can\'t find Ministro service.\nThe application can\'t start.</string>
|
||||
<string name="ministro_needed_msg">This application requires Ministro service. Would you like to install it?</string>
|
||||
<string name="fatal_error_msg">Your application encountered a fatal error and cannot continue.</string>
|
||||
</resources>
|
2
cmake/externals/glm/CMakeLists.txt
vendored
|
@ -6,7 +6,7 @@ ExternalProject_Add(
|
|||
URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.zip
|
||||
URL_MD5 579ac77a3110befa3244d68c0ceb7281
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTERNAL_ARGS}
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
|
|
4
cmake/externals/tbb/CMakeLists.txt
vendored
|
@ -8,9 +8,6 @@ if (WIN32)
|
|||
elseif (APPLE)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb2017_20170604oss_mac_slim.tar.gz)
|
||||
set(DOWNLOAD_MD5 62bde626b396f8e1a85c6a8ded1d8105)
|
||||
elseif (ANDROID)
|
||||
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_and_slim.tar.gz)
|
||||
set(DOWNLOAD_MD5 04d50b64e1d81245a1be5f75f34d64c7)
|
||||
else ()
|
||||
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_lin_slim.tar.gz)
|
||||
set(DOWNLOAD_MD5 2a5c721f40fa3503ffc12c18dd00011c)
|
||||
|
@ -107,3 +104,4 @@ endif ()
|
|||
if (DEFINED ${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE "List of tbb include directories")
|
||||
endif ()
|
||||
|
||||
|
|
|
@ -34,10 +34,23 @@ file(GLOB HIFI_CUSTOM_MACROS "cmake/macros/*.cmake")
|
|||
foreach(CUSTOM_MACRO ${HIFI_CUSTOM_MACROS})
|
||||
include(${CUSTOM_MACRO})
|
||||
endforeach()
|
||||
unset(HIFI_CUSTOM_MACROS)
|
||||
|
||||
if (ANDROID)
|
||||
file(GLOB ANDROID_CUSTOM_MACROS "cmake/android/*.cmake")
|
||||
foreach(CUSTOM_MACRO ${ANDROID_CUSTOM_MACROS})
|
||||
include(${CUSTOM_MACRO})
|
||||
endforeach()
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)
|
||||
|
||||
string(REGEX REPLACE "\\\\" "/" ANDROID_NDK ${ANDROID_NDK})
|
||||
string(REGEX REPLACE "\\\\" "/" CMAKE_TOOLCHAIN_FILE ${CMAKE_TOOLCHAIN_FILE})
|
||||
string(REGEX REPLACE "\\\\" "/" ANDROID_TOOLCHAIN ${ANDROID_TOOLCHAIN})
|
||||
string(REGEX REPLACE "\\\\" "/" CMAKE_MAKE_PROGRAM ${CMAKE_MAKE_PROGRAM})
|
||||
list(APPEND EXTERNAL_ARGS -DANDROID_ABI=${ANDROID_ABI})
|
||||
list(APPEND EXTERNAL_ARGS -DANDROID_NDK=${ANDROID_NDK})
|
||||
list(APPEND EXTERNAL_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})
|
||||
list(APPEND EXTERNAL_ARGS -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM})
|
||||
list(APPEND EXTERNAL_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE})
|
||||
list(APPEND EXTERNAL_ARGS -DHIFI_ANDROID=${HIFI_ANDROID})
|
||||
list(APPEND EXTERNAL_ARGS -DANDROID_PLATFORM=${ANDROID_PLATFORM})
|
||||
list(APPEND EXTERNAL_ARGS -DANDROID_TOOLCHAIN=${ANDROID_TOOLCHAIN})
|
||||
list(APPEND EXTERNAL_ARGS -DANDROID_STL=${ANDROID_STL})
|
||||
endif ()
|
||||
|
|
|
@ -62,7 +62,9 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
|
|||
# since it's unrunnable by the cross-compiling build machine
|
||||
|
||||
# so, we require the compiling user to point us at a compiled executable version for their native toolchain
|
||||
find_program(NATIVE_SCRIBE scribe PATHS ${SCRIBE_PATH} ENV SCRIBE_PATH)
|
||||
if (NOT NATIVE_SCRIBE)
|
||||
find_program(NATIVE_SCRIBE scribe PATHS ${SCRIBE_PATH} ENV SCRIBE_PATH)
|
||||
endif()
|
||||
|
||||
if (NOT NATIVE_SCRIBE)
|
||||
message(FATAL_ERROR "The High Fidelity scribe tool is required for shader pre-processing. \
|
||||
|
|
|
@ -162,5 +162,6 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
# create a header file our targets can use to find out the application version
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/includes")
|
||||
configure_file("${HF_CMAKE_DIR}/templates/BuildInfo.h.in" "${CMAKE_BINARY_DIR}/includes/BuildInfo.h")
|
||||
include_directories("${CMAKE_BINARY_DIR}/includes")
|
||||
|
||||
endmacro(SET_PACKAGING_PARAMETERS)
|
||||
|
|
|
@ -12,7 +12,7 @@ macro(SETUP_HIFI_LIBRARY)
|
|||
project(${TARGET_NAME})
|
||||
|
||||
# grab the implementation and header files
|
||||
file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c")
|
||||
file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c" "src/*.qrc")
|
||||
list(APPEND ${TARGET_NAME}_SRCS ${LIB_SRCS})
|
||||
|
||||
# add compiler flags to AVX source files
|
||||
|
@ -65,7 +65,7 @@ macro(SETUP_HIFI_LIBRARY)
|
|||
list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core)
|
||||
|
||||
# find these Qt modules and link them to our own target
|
||||
find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED)
|
||||
find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED CMAKE_FIND_ROOT_PATH_BOTH)
|
||||
|
||||
foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES})
|
||||
target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})
|
||||
|
|
|
@ -28,7 +28,7 @@ function(calculate_default_qt_dir _RESULT_NAME)
|
|||
set(QT_DEFAULT_ARCH "gcc_64")
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (WIN32 OR (ANDROID AND ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")))
|
||||
set(QT_DEFAULT_ROOT "c:/Qt")
|
||||
else()
|
||||
set(QT_DEFAULT_ROOT "$ENV{HOME}/Qt")
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_GLEW)
|
||||
add_dependency_external_projects(glew)
|
||||
find_package(GLEW REQUIRED)
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARY})
|
||||
if (NOT ANDROID)
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
add_dependency_external_projects(glew)
|
||||
find_package(GLEW REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARY})
|
||||
endif()
|
||||
endmacro()
|
|
@ -6,15 +6,13 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_OPENGL)
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
if (APPLE)
|
||||
# link in required OS X frameworks and include the right GL headers
|
||||
find_library(OpenGL OpenGL)
|
||||
target_link_libraries(${TARGET_NAME} ${OpenGL})
|
||||
elseif(ANDROID)
|
||||
target_link_libraries(${TARGET_NAME} "-lGLESv3" "-lEGL")
|
||||
target_link_libraries(${TARGET_NAME} GLESv3 EGL)
|
||||
else()
|
||||
target_nsight()
|
||||
find_package(OpenGL REQUIRED)
|
||||
if (${OPENGL_INCLUDE_DIR})
|
||||
include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}")
|
||||
|
@ -22,4 +20,6 @@ macro(TARGET_OPENGL)
|
|||
target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}")
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR})
|
||||
endif()
|
||||
target_nsight()
|
||||
target_glew()
|
||||
endmacro()
|
||||
|
|
32
cmake/macros/TargetOpenSSL.cmake
Normal file
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Bradley Austin Davis on 2015/10/10
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_OPENSSL)
|
||||
|
||||
if (ANDROID)
|
||||
|
||||
# FIXME use a distributable binary
|
||||
set(OPENSSL_INSTALL_DIR C:/Android/openssl)
|
||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_INSTALL_DIR}/include" CACHE TYPE INTERNAL)
|
||||
set(OPENSSL_LIBRARIES "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a;${OPENSSL_INSTALL_DIR}/lib/libssl.a" CACHE TYPE INTERNAL)
|
||||
|
||||
else()
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include")
|
||||
# this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto
|
||||
message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings."
|
||||
"\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES})
|
||||
|
||||
endmacro()
|
24
cmake/macros/TargetTBB.cmake
Normal file
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Bradley Austin Davis on 2015/10/10
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_TBB)
|
||||
|
||||
if (ANDROID)
|
||||
set(TBB_INSTALL_DIR C:/tbb-2018/built)
|
||||
set(TBB_LIBRARY ${HIFI_ANDROID_PRECOMPILED}/libtbb.so CACHE FILEPATH "TBB library location")
|
||||
set(TBB_MALLOC_LIBRARY ${HIFI_ANDROID_PRECOMPILED}/libtbbmalloc.so CACHE FILEPATH "TBB malloc library location")
|
||||
set(TBB_INCLUDE_DIRS ${TBB_INSTALL_DIR}/include CACHE TYPE "List of tbb include directories" CACHE FILEPATH "TBB includes location")
|
||||
set(TBB_LIBRARIES ${TBB_LIBRARY} ${TBB_MALLOC_LIBRARY})
|
||||
else()
|
||||
add_dependency_external_projects(tbb)
|
||||
find_package(TBB REQUIRED)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${TARGET_NAME} ${TBB_LIBRARIES})
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS})
|
||||
|
||||
endmacro()
|
|
@ -171,8 +171,6 @@ else ()
|
|||
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
||||
endif ()
|
||||
|
||||
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes")
|
||||
|
||||
if (WIN32)
|
||||
# These are external plugins, but we need to do the 'add dependency' here so that their
|
||||
# binary directories get added to the fixup path
|
||||
|
@ -214,10 +212,6 @@ target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries
|
|||
target_bullet()
|
||||
target_opengl()
|
||||
|
||||
if (NOT ANDROID)
|
||||
target_glew()
|
||||
endif ()
|
||||
|
||||
# perform standard include and linking for found externals
|
||||
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||
|
||||
|
|
|
@ -116,6 +116,13 @@ Item {
|
|||
wrapMode: Text.WordWrap
|
||||
readOnly: false // we need to leave this property read-only to allow control to accept QKeyEvent
|
||||
selectByMouse: false
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key == Qt.Key_Return) {
|
||||
mirrorText.text = "";
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus
|
||||
|
|
|
@ -21,9 +21,9 @@ Item {
|
|||
property alias text: label.text
|
||||
property var source
|
||||
|
||||
implicitHeight: source.visible ? 2 * label.implicitHeight : 0
|
||||
implicitHeight: source !== null ? source.visible ? 2 * label.implicitHeight : 0 : 0
|
||||
implicitWidth: 2 * hifi.dimensions.menuPadding.x + check.width + label.width + tail.width
|
||||
visible: source.visible
|
||||
visible: source !== null ? source.visible : false
|
||||
width: parent.width
|
||||
|
||||
Item {
|
||||
|
@ -42,7 +42,9 @@ Item {
|
|||
id: checkbox
|
||||
// FIXME: Should use radio buttons if source.exclusiveGroup.
|
||||
width: 20
|
||||
visible: source.visible && source.type === 1 && source.checkable && !source.exclusiveGroup
|
||||
visible: source !== null ?
|
||||
source.visible && source.type === 1 && source.checkable && !source.exclusiveGroup :
|
||||
false
|
||||
checked: setChecked()
|
||||
function setChecked() {
|
||||
if (!source || source.type !== 1 || !source.checkable) {
|
||||
|
@ -58,7 +60,9 @@ Item {
|
|||
id: radiobutton
|
||||
// FIXME: Should use radio buttons if source.exclusiveGroup.
|
||||
width: 20
|
||||
visible: source.visible && source.type === 1 && source.checkable && source.exclusiveGroup
|
||||
visible: source !== null ?
|
||||
source.visible && source.type === 1 && source.checkable && source.exclusiveGroup :
|
||||
false
|
||||
checked: setChecked()
|
||||
function setChecked() {
|
||||
if (!source || source.type !== 1 || !source.checkable) {
|
||||
|
@ -80,9 +84,13 @@ Item {
|
|||
anchors.left: check.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow50
|
||||
enabled: source.visible && (source.type !== 0 ? source.enabled : false)
|
||||
visible: source.visible
|
||||
color: source !== null ?
|
||||
source.enabled ? hifi.colors.baseGrayShadow :
|
||||
hifi.colors.baseGrayShadow50 :
|
||||
"transparent"
|
||||
|
||||
enabled: source !== null ? source.visible && (source.type !== 0 ? source.enabled : false) : false
|
||||
visible: source !== null ? source.visible : false
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
|
@ -93,7 +101,7 @@ Item {
|
|||
leftMargin: hifi.dimensions.menuPadding.x + check.width
|
||||
rightMargin: hifi.dimensions.menuPadding.x + tail.width
|
||||
}
|
||||
visible: source.type === MenuItemType.Separator
|
||||
visible: source !== null ? source.type === MenuItemType.Separator : false
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
|
@ -117,23 +125,23 @@ Item {
|
|||
|
||||
RalewayLight {
|
||||
id: shortcut
|
||||
text: source.shortcut ? source.shortcut : ""
|
||||
text: source !== null ? source.shortcut ? source.shortcut : "" : ""
|
||||
size: hifi.fontSizes.shortcutText
|
||||
color: hifi.colors.baseGrayShadow
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
visible: source.visible && text != ""
|
||||
visible: source !== null ? source.visible && text != "" : false
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
text: hifi.glyphs.disclosureExpand
|
||||
color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow25
|
||||
color: source !== null ? source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow25 : "transparent"
|
||||
size: 70
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
horizontalAlignment: Text.AlignRight
|
||||
visible: source.visible && (source.type === 2)
|
||||
visible: source !== null ? source.visible && (source.type === 2) : false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,6 @@ Item {
|
|||
|
||||
for (var i = 0; i < items.length; ++i) {
|
||||
var item = items[i];
|
||||
if (!item.visible) continue;
|
||||
switch (item.type) {
|
||||
case MenuItemType.Menu:
|
||||
result.append({"name": item.title, "item": item})
|
||||
|
@ -216,5 +215,4 @@ Item {
|
|||
function nextItem() { d.topMenu.nextItem(); }
|
||||
function selectCurrentItem() { d.topMenu.selectCurrentItem(); }
|
||||
function previousPage() { d.topMenu.previousPage(); }
|
||||
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import "../../styles-uit"
|
||||
import "."
|
||||
|
@ -36,7 +34,6 @@ FocusScope {
|
|||
//color: isSubMenu ? hifi.colors.faintGray : hifi.colors.faintGray80
|
||||
}
|
||||
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
x: 0
|
||||
|
@ -68,8 +65,8 @@ FocusScope {
|
|||
delegate: TabletMenuItem {
|
||||
text: name
|
||||
source: item
|
||||
onImplicitHeightChanged: listView.recalcSize()
|
||||
onImplicitWidthChanged: listView.recalcSize()
|
||||
onImplicitHeightChanged: listView !== null ? listView.recalcSize() : 0
|
||||
onImplicitWidthChanged: listView !== null ? listView.recalcSize() : 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
@ -124,8 +121,6 @@ FocusScope {
|
|||
function nextItem() { listView.currentIndex = (listView.currentIndex + listView.count + 1) % listView.count; }
|
||||
function selectCurrentItem() { if (listView.currentIndex != -1) root.selected(currentItem.source); }
|
||||
function previousPage() { root.parent.pop(); }
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -980,7 +980,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
connect(scriptEngines, &ScriptEngines::scriptLoadError,
|
||||
scriptEngines, [](const QString& filename, const QString& error){
|
||||
OffscreenUi::warning(nullptr, "Error Loading Script", filename + " failed to load.");
|
||||
OffscreenUi::asyncWarning(nullptr, "Error Loading Script", filename + " failed to load.");
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -1845,7 +1845,7 @@ void Application::domainConnectionRefused(const QString& reasonMessage, int reas
|
|||
case DomainHandler::ConnectionRefusedReason::Unknown: {
|
||||
QString message = "Unable to connect to the location you are visiting.\n";
|
||||
message += reasonMessage;
|
||||
OffscreenUi::warning("", message);
|
||||
OffscreenUi::asyncWarning("", message);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -2369,6 +2369,7 @@ void Application::initializeUi() {
|
|||
|
||||
// Pre-create a couple of Web3D overlays to speed up tablet UI
|
||||
auto offscreenSurfaceCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
|
||||
offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
|
||||
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
|
||||
}
|
||||
|
||||
|
@ -2581,7 +2582,6 @@ void Application::paintGL() {
|
|||
// scale IPD by sensorToWorldScale, to make the world seem larger or smaller accordingly.
|
||||
ipdScale *= sensorToWorldScale;
|
||||
|
||||
mat4 eyeProjections[2];
|
||||
{
|
||||
PROFILE_RANGE(render, "/mainRender");
|
||||
PerformanceTimer perfTimer("mainRender");
|
||||
|
@ -2638,17 +2638,8 @@ void Application::paintGL() {
|
|||
PerformanceTimer perfTimer("postComposite");
|
||||
renderArgs._batch = &postCompositeBatch;
|
||||
renderArgs._batch->setViewportTransform(ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height()));
|
||||
for_each_eye([&](Eye eye) {
|
||||
|
||||
// apply eye offset and IPD scale to the view matrix
|
||||
mat4 eyeToHead = displayPlugin->getEyeToHeadTransform(eye);
|
||||
vec3 eyeOffset = glm::vec3(eyeToHead[3]);
|
||||
mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * ipdScale);
|
||||
renderArgs._batch->setViewTransform(renderArgs.getViewFrustum().getView() * eyeOffsetTransform);
|
||||
|
||||
renderArgs._batch->setProjectionTransform(eyeProjections[eye]);
|
||||
_overlays.render3DHUDOverlays(&renderArgs);
|
||||
});
|
||||
renderArgs._batch->setViewTransform(renderArgs.getViewFrustum().getView());
|
||||
_overlays.render3DHUDOverlays(&renderArgs);
|
||||
}
|
||||
|
||||
auto frame = _gpuContext->endFrame();
|
||||
|
@ -3751,7 +3742,7 @@ bool Application::acceptSnapshot(const QString& urlString) {
|
|||
DependencyManager::get<AddressManager>()->handleLookupString(snapshotData->getURL().toString());
|
||||
}
|
||||
} else {
|
||||
OffscreenUi::warning("", "No location details were found in the file\n" +
|
||||
OffscreenUi::asyncWarning("", "No location details were found in the file\n" +
|
||||
snapshotPath + "\nTry dragging in an authentic Hifi snapshot.");
|
||||
}
|
||||
return true;
|
||||
|
@ -6151,7 +6142,7 @@ void Application::setSessionUUID(const QUuid& sessionUUID) const {
|
|||
bool Application::askToSetAvatarUrl(const QString& url) {
|
||||
QUrl realUrl(url);
|
||||
if (realUrl.isLocalFile()) {
|
||||
OffscreenUi::warning("", "You can not use local files for avatar components.");
|
||||
OffscreenUi::asyncWarning("", "You can not use local files for avatar components.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -6163,41 +6154,55 @@ bool Application::askToSetAvatarUrl(const QString& url) {
|
|||
QString modelName = fstMapping["name"].toString();
|
||||
QString modelLicense = fstMapping["license"].toString();
|
||||
|
||||
bool agreeToLicence = true; // assume true
|
||||
bool agreeToLicense = true; // assume true
|
||||
//create set avatar callback
|
||||
auto setAvatar = [=] (QString url, QString modelName) {
|
||||
ModalDialogListener* dlg = OffscreenUi::asyncQuestion("Set Avatar",
|
||||
"Would you like to use '" + modelName + "' for your avatar?",
|
||||
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok);
|
||||
QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) {
|
||||
QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
|
||||
bool ok = (QMessageBox::Ok == static_cast<QMessageBox::StandardButton>(answer.toInt()));
|
||||
if (ok) {
|
||||
getMyAvatar()->useFullAvatarURL(url, modelName);
|
||||
emit fullAvatarURLChanged(url, modelName);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Declined to use the avatar: " << url;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (!modelLicense.isEmpty()) {
|
||||
// word wrap the licence text to fit in a reasonable shaped message box.
|
||||
// word wrap the license text to fit in a reasonable shaped message box.
|
||||
const int MAX_CHARACTERS_PER_LINE = 90;
|
||||
modelLicense = simpleWordWrap(modelLicense, MAX_CHARACTERS_PER_LINE);
|
||||
|
||||
agreeToLicence = QMessageBox::Yes == OffscreenUi::question("Avatar Usage License",
|
||||
modelLicense + "\nDo you agree to these terms?",
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
}
|
||||
ModalDialogListener* dlg = OffscreenUi::asyncQuestion("Avatar Usage License",
|
||||
modelLicense + "\nDo you agree to these terms?",
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
QObject::connect(dlg, &ModalDialogListener::response, this, [=, &agreeToLicense] (QVariant answer) {
|
||||
QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
|
||||
bool ok = false;
|
||||
agreeToLicense = (static_cast<QMessageBox::StandardButton>(answer.toInt()) == QMessageBox::Yes);
|
||||
if (agreeToLicense) {
|
||||
switch (modelType) {
|
||||
case FSTReader::HEAD_AND_BODY_MODEL: {
|
||||
setAvatar(url, modelName);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
OffscreenUi::asyncWarning("", modelName + "Does not support a head and body as required.");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Declined to agree to avatar license: " << url;
|
||||
}
|
||||
|
||||
if (!agreeToLicence) {
|
||||
qCDebug(interfaceapp) << "Declined to agree to avatar license: " << url;
|
||||
//auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
});
|
||||
} else {
|
||||
switch (modelType) {
|
||||
|
||||
case FSTReader::HEAD_AND_BODY_MODEL:
|
||||
ok = QMessageBox::Ok == OffscreenUi::question("Set Avatar",
|
||||
"Would you like to use '" + modelName + "' for your avatar?",
|
||||
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok);
|
||||
break;
|
||||
|
||||
default:
|
||||
OffscreenUi::warning("", modelName + "Does not support a head and body as required.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
getMyAvatar()->useFullAvatarURL(url, modelName);
|
||||
emit fullAvatarURLChanged(url, modelName);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Declined to use the avatar: " << url;
|
||||
setAvatar(url, modelName);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -6205,8 +6210,6 @@ bool Application::askToSetAvatarUrl(const QString& url) {
|
|||
|
||||
|
||||
bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
|
||||
QMessageBox::StandardButton reply;
|
||||
|
||||
QString shortName = scriptFilenameOrURL;
|
||||
|
||||
QUrl scriptURL { scriptFilenameOrURL };
|
||||
|
@ -6218,15 +6221,20 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
|
|||
}
|
||||
|
||||
QString message = "Would you like to run this script:\n" + shortName;
|
||||
ModalDialogListener* dlg = OffscreenUi::asyncQuestion(getWindow(), "Run Script", message,
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
reply = OffscreenUi::question(getWindow(), "Run Script", message, QMessageBox::Yes | QMessageBox::No);
|
||||
QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) {
|
||||
const QString& fileName = scriptFilenameOrURL;
|
||||
if (static_cast<QMessageBox::StandardButton>(answer.toInt()) == QMessageBox::Yes) {
|
||||
qCDebug(interfaceapp) << "Chose to run the script: " << fileName;
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(fileName);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Declined to run the script: " << scriptFilenameOrURL;
|
||||
}
|
||||
QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
});
|
||||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
qCDebug(interfaceapp) << "Chose to run the script: " << scriptFilenameOrURL;
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(scriptFilenameOrURL);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Declined to run the script: " << scriptFilenameOrURL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6263,22 +6271,26 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
|
|||
name = nameValue.toString();
|
||||
}
|
||||
|
||||
// display confirmation dialog
|
||||
if (displayAvatarAttachmentConfirmationDialog(name)) {
|
||||
|
||||
// add attachment to avatar
|
||||
auto myAvatar = getMyAvatar();
|
||||
assert(myAvatar);
|
||||
auto attachmentDataVec = myAvatar->getAttachmentData();
|
||||
AttachmentData attachmentData;
|
||||
attachmentData.fromJson(jsonObject);
|
||||
attachmentDataVec.push_back(attachmentData);
|
||||
myAvatar->setAttachmentData(attachmentDataVec);
|
||||
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "User declined to wear the avatar attachment: " << url;
|
||||
}
|
||||
|
||||
auto avatarAttachmentConfirmationTitle = tr("Avatar Attachment Confirmation");
|
||||
auto avatarAttachmentConfirmationMessage = tr("Would you like to wear '%1' on your avatar?").arg(name);
|
||||
ModalDialogListener* dlg = OffscreenUi::asyncQuestion(avatarAttachmentConfirmationTitle,
|
||||
avatarAttachmentConfirmationMessage,
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) {
|
||||
QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
if (static_cast<QMessageBox::StandardButton>(answer.toInt()) == QMessageBox::Yes) {
|
||||
// add attachment to avatar
|
||||
auto myAvatar = getMyAvatar();
|
||||
assert(myAvatar);
|
||||
auto attachmentDataVec = myAvatar->getAttachmentData();
|
||||
AttachmentData attachmentData;
|
||||
attachmentData.fromJson(jsonObject);
|
||||
attachmentDataVec.push_back(attachmentData);
|
||||
myAvatar->setAttachmentData(attachmentDataVec);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "User declined to wear the avatar attachment: " << url;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// json parse error
|
||||
auto avatarAttachmentParseErrorString = tr("Error parsing attachment JSON from url: \"%1\"");
|
||||
|
@ -6351,20 +6363,7 @@ bool Application::askToReplaceDomainContent(const QString& url) {
|
|||
|
||||
void Application::displayAvatarAttachmentWarning(const QString& message) const {
|
||||
auto avatarAttachmentWarningTitle = tr("Avatar Attachment Failure");
|
||||
OffscreenUi::warning(avatarAttachmentWarningTitle, message);
|
||||
}
|
||||
|
||||
bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) const {
|
||||
auto avatarAttachmentConfirmationTitle = tr("Avatar Attachment Confirmation");
|
||||
auto avatarAttachmentConfirmationMessage = tr("Would you like to wear '%1' on your avatar?").arg(name);
|
||||
auto reply = OffscreenUi::question(avatarAttachmentConfirmationTitle,
|
||||
avatarAttachmentConfirmationMessage,
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
if (QMessageBox::Ok == reply) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
OffscreenUi::asyncWarning(avatarAttachmentWarningTitle, message);
|
||||
}
|
||||
|
||||
void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const {
|
||||
|
@ -6686,7 +6685,7 @@ void Application::addAssetToWorldCheckModelSize() {
|
|||
if (dimensions != DEFAULT_DIMENSIONS) {
|
||||
|
||||
// Scale model so that its maximum is exactly specific size.
|
||||
const float MAXIMUM_DIMENSION = 1.0f * getMyAvatar()->getSensorToWorldScale();
|
||||
const float MAXIMUM_DIMENSION = getMyAvatar()->getSensorToWorldScale();
|
||||
auto previousDimensions = dimensions;
|
||||
auto scale = std::min(MAXIMUM_DIMENSION / dimensions.x, std::min(MAXIMUM_DIMENSION / dimensions.y,
|
||||
MAXIMUM_DIMENSION / dimensions.z));
|
||||
|
@ -6945,12 +6944,17 @@ void Application::openUrl(const QUrl& url) const {
|
|||
|
||||
void Application::loadDialog() {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QString fileNameString = OffscreenUi::getOpenFileName(
|
||||
_glWidget, tr("Open Script"), getPreviousScriptLocation(), tr("JavaScript Files (*.js)"));
|
||||
if (!fileNameString.isEmpty() && QFile(fileNameString).exists()) {
|
||||
setPreviousScriptLocation(QFileInfo(fileNameString).absolutePath());
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(fileNameString, true, false, false, true); // Don't load from cache
|
||||
}
|
||||
ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(_glWidget, tr("Open Script"),
|
||||
getPreviousScriptLocation(),
|
||||
tr("JavaScript Files (*.js)"));
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) {
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
const QString& response = answer.toString();
|
||||
if (!response.isEmpty() && QFile(response).exists()) {
|
||||
setPreviousScriptLocation(QFileInfo(response).absolutePath());
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(response, true, false, false, true); // Don't load from cache
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QString Application::getPreviousScriptLocation() {
|
||||
|
@ -6963,12 +6967,16 @@ void Application::setPreviousScriptLocation(const QString& location) {
|
|||
}
|
||||
|
||||
void Application::loadScriptURLDialog() const {
|
||||
QString newScript = OffscreenUi::getText(OffscreenUi::ICON_NONE, "Open and Run Script", "Script URL");
|
||||
if (QUrl(newScript).scheme() == "atp") {
|
||||
OffscreenUi::warning("Error Loading Script", "Cannot load client script over ATP");
|
||||
} else if (!newScript.isEmpty()) {
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(newScript.trimmed());
|
||||
}
|
||||
ModalDialogListener* dlg = OffscreenUi::getTextAsync(OffscreenUi::ICON_NONE, "Open and Run Script", "Script URL");
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
const QString& newScript = response.toString();
|
||||
if (QUrl(newScript).scheme() == "atp") {
|
||||
OffscreenUi::asyncWarning("Error Loading Script", "Cannot load client script over ATP");
|
||||
} else if (!newScript.isEmpty()) {
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(newScript.trimmed());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Application::loadLODToolsDialog() {
|
||||
|
@ -7085,7 +7093,7 @@ void Application::notifyPacketVersionMismatch() {
|
|||
QString message = "The location you are visiting is running an incompatible server version.\n";
|
||||
message += "Content may not display properly.";
|
||||
|
||||
OffscreenUi::warning("", message);
|
||||
OffscreenUi::asyncWarning("", message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7094,7 +7102,7 @@ void Application::checkSkeleton() const {
|
|||
qCDebug(interfaceapp) << "MyAvatar model has no skeleton";
|
||||
|
||||
QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded...";
|
||||
OffscreenUi::warning("", message);
|
||||
OffscreenUi::asyncWarning("", message);
|
||||
|
||||
getMyAvatar()->useFullAvatarURL(AvatarData::defaultFullAvatarModelUrl(), DEFAULT_FULL_AVATAR_MODEL_NAME);
|
||||
} else {
|
||||
|
|
|
@ -429,7 +429,6 @@ private slots:
|
|||
|
||||
bool askToWearAvatarAttachmentUrl(const QString& url);
|
||||
void displayAvatarAttachmentWarning(const QString& message) const;
|
||||
bool displayAvatarAttachmentConfirmationDialog(const QString& name) const;
|
||||
|
||||
bool askToReplaceDomainContent(const QString& url);
|
||||
|
||||
|
|
|
@ -106,30 +106,30 @@ void AvatarBookmarks::changeToBookmarkedAvatar() {
|
|||
}
|
||||
|
||||
void AvatarBookmarks::addBookmark() {
|
||||
bool ok = false;
|
||||
auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar", "Name", QString(), &ok);
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
ModalDialogListener* dlg = OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar", "Name", QString());
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
auto bookmarkName = response.toString();
|
||||
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
|
||||
if (bookmarkName.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
|
||||
if (bookmarkName.length() == 0) {
|
||||
return;
|
||||
}
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
|
||||
const QVariant& avatarScale = myAvatar->getAvatarScale();
|
||||
|
||||
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
|
||||
const QVariant& avatarScale = myAvatar->getAvatarScale();
|
||||
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
|
||||
QVariantMap *bookmark = new QVariantMap;
|
||||
bookmark->insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
|
||||
bookmark->insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
bookmark->insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
|
||||
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, *bookmark);
|
||||
});
|
||||
|
||||
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
|
||||
QVariantMap *bookmark = new QVariantMap;
|
||||
bookmark->insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
|
||||
bookmark->insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
bookmark->insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
|
||||
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, *bookmark);
|
||||
}
|
||||
|
||||
void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) {
|
||||
|
|
|
@ -58,20 +58,25 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QVariant& b
|
|||
Menu* menubar = Menu::getInstance();
|
||||
if (contains(bookmarkName)) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto duplicateBookmarkMessage = offscreenUi->createMessageBox(OffscreenUi::ICON_WARNING, "Duplicate Bookmark",
|
||||
"The bookmark name you entered already exists in your list.",
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?");
|
||||
auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage);
|
||||
if (result != QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
removeBookmarkFromMenu(menubar, bookmarkName);
|
||||
}
|
||||
ModalDialogListener* dlg = OffscreenUi::asyncWarning("Duplicate Bookmark",
|
||||
"The bookmark name you entered already exists in your list.",
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
dlg->setProperty("informativeText", "Would you like to overwrite it?");
|
||||
QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) {
|
||||
QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
|
||||
addBookmarkToMenu(menubar, bookmarkName, bookmark);
|
||||
insert(bookmarkName, bookmark); // Overwrites any item with the same bookmarkName.
|
||||
enableMenuItems(true);
|
||||
if (QMessageBox::Yes == static_cast<QMessageBox::StandardButton>(answer.toInt())) {
|
||||
removeBookmarkFromMenu(menubar, bookmarkName);
|
||||
addBookmarkToMenu(menubar, bookmarkName, bookmark);
|
||||
insert(bookmarkName, bookmark); // Overwrites any item with the same bookmarkName.
|
||||
enableMenuItems(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addBookmarkToMenu(menubar, bookmarkName, bookmark);
|
||||
insert(bookmarkName, bookmark); // Overwrites any item with the same bookmarkName.
|
||||
enableMenuItems(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Bookmarks::insert(const QString& name, const QVariant& bookmark) {
|
||||
|
|
|
@ -74,20 +74,21 @@ void LocationBookmarks::teleportToBookmark() {
|
|||
}
|
||||
|
||||
void LocationBookmarks::addBookmark() {
|
||||
bool ok = false;
|
||||
auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString(), &ok);
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
ModalDialogListener* dlg = OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString());
|
||||
|
||||
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
|
||||
if (bookmarkName.length() == 0) {
|
||||
return;
|
||||
}
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
auto bookmarkName = response.toString();
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
QString bookmarkAddress = addressManager->currentAddress().toString();
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
|
||||
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
|
||||
if (bookmarkName.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
QString bookmarkAddress = addressManager->currentAddress().toString();
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
|
||||
});
|
||||
}
|
||||
|
||||
void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) {
|
||||
|
@ -101,4 +102,4 @@ void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, co
|
|||
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, name, 0, QAction::NoRole);
|
||||
Bookmarks::sortActions(menubar, _bookmarksMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -367,6 +367,20 @@ Menu::Menu() {
|
|||
QString("../../hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog");
|
||||
});
|
||||
|
||||
// Developer > UI >>>
|
||||
MenuWrapper* uiOptionsMenu = developerMenu->addMenu("UI");
|
||||
action = addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::DesktopTabletToToolbar, 0,
|
||||
qApp->getDesktopTabletBecomesToolbarSetting());
|
||||
connect(action, &QAction::triggered, [action] {
|
||||
qApp->setDesktopTabletBecomesToolbarSetting(action->isChecked());
|
||||
});
|
||||
|
||||
action = addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::HMDTabletToToolbar, 0,
|
||||
qApp->getHmdTabletBecomesToolbarSetting());
|
||||
connect(action, &QAction::triggered, [action] {
|
||||
qApp->setHmdTabletBecomesToolbarSetting(action->isChecked());
|
||||
});
|
||||
|
||||
// Developer > Render >>>
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes);
|
||||
|
|
|
@ -200,6 +200,8 @@ namespace MenuOption {
|
|||
const QString VisibleToFriends = "Friends";
|
||||
const QString VisibleToNoOne = "No one";
|
||||
const QString WorldAxes = "World Axes";
|
||||
const QString DesktopTabletToToolbar = "Desktop Tablet Becomes Toolbar";
|
||||
const QString HMDTabletToToolbar = "HMD Tablet Becomes Toolbar";
|
||||
}
|
||||
|
||||
#endif // hifi_Menu_h
|
||||
|
|
|
@ -79,7 +79,7 @@ bool ModelPackager::loadModel() {
|
|||
if (_modelFile.completeSuffix().contains("fst")) {
|
||||
QFile fst(_modelFile.filePath());
|
||||
if (!fst.open(QFile::ReadOnly | QFile::Text)) {
|
||||
OffscreenUi::warning(NULL,
|
||||
OffscreenUi::asyncWarning(NULL,
|
||||
QString("ModelPackager::loadModel()"),
|
||||
QString("Could not open FST file %1").arg(_modelFile.filePath()),
|
||||
QMessageBox::Ok);
|
||||
|
@ -98,7 +98,7 @@ bool ModelPackager::loadModel() {
|
|||
// open the fbx file
|
||||
QFile fbx(_fbxInfo.filePath());
|
||||
if (!_fbxInfo.exists() || !_fbxInfo.isFile() || !fbx.open(QIODevice::ReadOnly)) {
|
||||
OffscreenUi::warning(NULL,
|
||||
OffscreenUi::asyncWarning(NULL,
|
||||
QString("ModelPackager::loadModel()"),
|
||||
QString("Could not open FBX file %1").arg(_fbxInfo.filePath()),
|
||||
QMessageBox::Ok);
|
||||
|
@ -408,7 +408,7 @@ bool ModelPackager::copyTextures(const QString& oldDir, const QDir& newDir) {
|
|||
}
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
OffscreenUi::warning(nullptr, "ModelPackager::copyTextures()",
|
||||
OffscreenUi::asyncWarning(nullptr, "ModelPackager::copyTextures()",
|
||||
"Missing textures:" + errors);
|
||||
qCDebug(interfaceapp) << "ModelPackager::copyTextures():" << errors;
|
||||
return false;
|
||||
|
|
|
@ -201,7 +201,7 @@ void ModelPropertiesDialog::chooseTextureDirectory() {
|
|||
return;
|
||||
}
|
||||
if (!directory.startsWith(_basePath)) {
|
||||
OffscreenUi::warning(NULL, "Invalid texture directory", "Texture directory must be child of base path.");
|
||||
OffscreenUi::asyncWarning(NULL, "Invalid texture directory", "Texture directory must be child of base path.");
|
||||
return;
|
||||
}
|
||||
_textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
|
||||
|
|
|
@ -42,117 +42,161 @@ static const QString COMPOUND_SHAPE_URL_KEY = "compoundShapeURL";
|
|||
static const QString MESSAGE_BOX_TITLE = "ATP Asset Migration";
|
||||
|
||||
void ATPAssetMigrator::loadEntityServerFile() {
|
||||
auto filename = OffscreenUi::getOpenFileName(_dialogParent, tr("Select an entity-server content file to migrate"), QString(), tr("Entity-Server Content (*.gz)"));
|
||||
|
||||
if (!filename.isEmpty()) {
|
||||
qCDebug(asset_migrator) << "Selected filename for ATP asset migration: " << filename;
|
||||
|
||||
static const QString MIGRATION_CONFIRMATION_TEXT {
|
||||
"The ATP Asset Migration process will scan the selected entity-server file,\nupload discovered resources to the"\
|
||||
" current asset-server\nand then save a new entity-server file with the ATP URLs.\n\nAre you ready to"\
|
||||
" continue?\n\nMake sure you are connected to the right domain."
|
||||
};
|
||||
|
||||
auto button = OffscreenUi::question(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT,
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
|
||||
if (button == QMessageBox::No) {
|
||||
return;
|
||||
}
|
||||
|
||||
// try to open the file at the given filename
|
||||
QFile modelsFile { filename };
|
||||
|
||||
if (modelsFile.open(QIODevice::ReadOnly)) {
|
||||
QByteArray compressedJsonData = modelsFile.readAll();
|
||||
QByteArray jsonData;
|
||||
|
||||
if (!gunzip(compressedJsonData, jsonData)) {
|
||||
OffscreenUi::warning(_dialogParent, "Error", "The file at" + filename + "was not in gzip format.");
|
||||
}
|
||||
|
||||
QJsonDocument modelsJSON = QJsonDocument::fromJson(jsonData);
|
||||
_entitiesArray = modelsJSON.object()["Entities"].toArray();
|
||||
|
||||
for (auto jsonValue : _entitiesArray) {
|
||||
QJsonObject entityObject = jsonValue.toObject();
|
||||
QString modelURLString = entityObject.value(MODEL_URL_KEY).toString();
|
||||
QString compoundURLString = entityObject.value(COMPOUND_SHAPE_URL_KEY).toString();
|
||||
ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(_dialogParent, tr("Select an entity-server content file to migrate"),
|
||||
QString(), tr("Entity-Server Content (*.gz)"));
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
bool isModelURL = (i == 0);
|
||||
quint8 replacementType = i;
|
||||
auto migrationURLString = (isModelURL) ? modelURLString : compoundURLString;
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
const QString& filename = response.toString();
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
if (!filename.isEmpty()) {
|
||||
qCDebug(asset_migrator) << "Selected filename for ATP asset migration: " << filename;
|
||||
|
||||
if (!migrationURLString.isEmpty()) {
|
||||
QUrl migrationURL = QUrl(migrationURLString);
|
||||
auto migrateResources = [=](QUrl migrationURL, QJsonValueRef jsonValue, bool isModelURL) {
|
||||
auto request =
|
||||
DependencyManager::get<ResourceManager>()->createResourceRequest(this, migrationURL);
|
||||
|
||||
if (!_ignoredUrls.contains(migrationURL)
|
||||
&& (migrationURL.scheme() == URL_SCHEME_HTTP || migrationURL.scheme() == URL_SCHEME_HTTPS
|
||||
|| migrationURL.scheme() == URL_SCHEME_FILE || migrationURL.scheme() == URL_SCHEME_FTP)) {
|
||||
if (request) {
|
||||
qCDebug(asset_migrator) << "Requesting" << migrationURL << "for ATP asset migration";
|
||||
|
||||
if (_pendingReplacements.contains(migrationURL)) {
|
||||
// we already have a request out for this asset, just store the QJsonValueRef
|
||||
// so we can do the hash replacement when the request comes back
|
||||
_pendingReplacements.insert(migrationURL, { jsonValue, replacementType });
|
||||
} else if (_uploadedAssets.contains(migrationURL)) {
|
||||
// we already have a hash for this asset
|
||||
// so just do the replacement immediately
|
||||
if (isModelURL) {
|
||||
entityObject[MODEL_URL_KEY] = _uploadedAssets.value(migrationURL).toString();
|
||||
} else {
|
||||
entityObject[COMPOUND_SHAPE_URL_KEY] = _uploadedAssets.value(migrationURL).toString();
|
||||
}
|
||||
// add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL
|
||||
// to an ATP one once ready
|
||||
_pendingReplacements.insert(migrationURL, { jsonValue, (isModelURL ? 0 : 1)});
|
||||
|
||||
jsonValue = entityObject;
|
||||
} else if (wantsToMigrateResource(migrationURL)) {
|
||||
auto request =
|
||||
DependencyManager::get<ResourceManager>()->createResourceRequest(this, migrationURL);
|
||||
connect(request, &ResourceRequest::finished, this, [=]() {
|
||||
if (request->getResult() == ResourceRequest::Success) {
|
||||
migrateResource(request);
|
||||
} else {
|
||||
++_errorCount;
|
||||
_pendingReplacements.remove(migrationURL);
|
||||
qWarning() << "Could not retrieve asset at" << migrationURL.toString();
|
||||
|
||||
if (request) {
|
||||
qCDebug(asset_migrator) << "Requesting" << migrationURL << "for ATP asset migration";
|
||||
checkIfFinished();
|
||||
}
|
||||
request->deleteLater();
|
||||
});
|
||||
|
||||
// add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL
|
||||
// to an ATP one once ready
|
||||
_pendingReplacements.insert(migrationURL, { jsonValue, (isModelURL ? 0 : 1)});
|
||||
|
||||
connect(request, &ResourceRequest::finished, this, [=]() {
|
||||
if (request->getResult() == ResourceRequest::Success) {
|
||||
migrateResource(request);
|
||||
} else {
|
||||
++_errorCount;
|
||||
_pendingReplacements.remove(migrationURL);
|
||||
qWarning() << "Could not retrieve asset at" << migrationURL.toString();
|
||||
|
||||
checkIfFinished();
|
||||
}
|
||||
request->deleteLater();
|
||||
});
|
||||
|
||||
request->send();
|
||||
} else {
|
||||
++_errorCount;
|
||||
qWarning() << "Count not create request for asset at" << migrationURL.toString();
|
||||
}
|
||||
|
||||
} else {
|
||||
_ignoredUrls.insert(migrationURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
request->send();
|
||||
} else {
|
||||
++_errorCount;
|
||||
qWarning() << "Count not create request for asset at" << migrationURL.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_doneReading = true;
|
||||
};
|
||||
static const QString MIGRATION_CONFIRMATION_TEXT {
|
||||
"The ATP Asset Migration process will scan the selected entity-server file,\nupload discovered resources to the"\
|
||||
" current asset-server\nand then save a new entity-server file with the ATP URLs.\n\nAre you ready to"\
|
||||
" continue?\n\nMake sure you are connected to the right domain."
|
||||
};
|
||||
ModalDialogListener* migrationConfirmDialog = OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT,
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
QObject::connect(migrationConfirmDialog, &ModalDialogListener::response, this, [=] (QVariant answer) {
|
||||
QObject::disconnect(migrationConfirmDialog, &ModalDialogListener::response, this, nullptr);
|
||||
|
||||
checkIfFinished();
|
||||
|
||||
} else {
|
||||
OffscreenUi::warning(_dialogParent, "Error",
|
||||
"There was a problem loading that entity-server file for ATP asset migration. Please try again");
|
||||
if (QMessageBox::Yes == static_cast<QMessageBox::StandardButton>(answer.toInt())) {
|
||||
// try to open the file at the given filename
|
||||
QFile modelsFile { filename };
|
||||
|
||||
if (modelsFile.open(QIODevice::ReadOnly)) {
|
||||
QByteArray compressedJsonData = modelsFile.readAll();
|
||||
QByteArray jsonData;
|
||||
|
||||
if (!gunzip(compressedJsonData, jsonData)) {
|
||||
OffscreenUi::asyncWarning(_dialogParent, "Error", "The file at" + filename + "was not in gzip format.");
|
||||
}
|
||||
|
||||
QJsonDocument modelsJSON = QJsonDocument::fromJson(jsonData);
|
||||
_entitiesArray = modelsJSON.object()["Entities"].toArray();
|
||||
|
||||
for (auto jsonValue : _entitiesArray) {
|
||||
QJsonObject entityObject = jsonValue.toObject();
|
||||
QString modelURLString = entityObject.value(MODEL_URL_KEY).toString();
|
||||
QString compoundURLString = entityObject.value(COMPOUND_SHAPE_URL_KEY).toString();
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
bool isModelURL = (i == 0);
|
||||
quint8 replacementType = i;
|
||||
auto migrationURLString = (isModelURL) ? modelURLString : compoundURLString;
|
||||
|
||||
if (!migrationURLString.isEmpty()) {
|
||||
QUrl migrationURL = QUrl(migrationURLString);
|
||||
|
||||
if (!_ignoredUrls.contains(migrationURL)
|
||||
&& (migrationURL.scheme() == URL_SCHEME_HTTP || migrationURL.scheme() == URL_SCHEME_HTTPS
|
||||
|| migrationURL.scheme() == URL_SCHEME_FILE || migrationURL.scheme() == URL_SCHEME_FTP)) {
|
||||
|
||||
if (_pendingReplacements.contains(migrationURL)) {
|
||||
// we already have a request out for this asset, just store the QJsonValueRef
|
||||
// so we can do the hash replacement when the request comes back
|
||||
_pendingReplacements.insert(migrationURL, { jsonValue, replacementType });
|
||||
} else if (_uploadedAssets.contains(migrationURL)) {
|
||||
// we already have a hash for this asset
|
||||
// so just do the replacement immediately
|
||||
if (isModelURL) {
|
||||
entityObject[MODEL_URL_KEY] = _uploadedAssets.value(migrationURL).toString();
|
||||
} else {
|
||||
entityObject[COMPOUND_SHAPE_URL_KEY] = _uploadedAssets.value(migrationURL).toString();
|
||||
}
|
||||
|
||||
jsonValue = entityObject;
|
||||
} else {
|
||||
|
||||
static bool hasAskedForCompleteMigration { false };
|
||||
static bool wantsCompleteMigration { false };
|
||||
|
||||
if (!hasAskedForCompleteMigration) {
|
||||
// this is the first resource migration - ask the user if they just want to migrate everything
|
||||
static const QString COMPLETE_MIGRATION_TEXT { "Do you want to migrate all assets found in this entity-server file?\n"\
|
||||
"Select \"Yes\" to upload all discovered assets to the current asset-server immediately.\n"\
|
||||
"Select \"No\" to be prompted for each discovered asset."
|
||||
};
|
||||
ModalDialogListener* migrationConfirmDialog1 = OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE,
|
||||
"Would you like to migrate the following resource?\n" + migrationURL.toString(),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
|
||||
QObject::connect(migrationConfirmDialog1, &ModalDialogListener::response, this, [=] (QVariant answer) {
|
||||
QObject::disconnect(migrationConfirmDialog1, &ModalDialogListener::response, this, nullptr);
|
||||
if (static_cast<QMessageBox::StandardButton>(answer.toInt()) ==
|
||||
QMessageBox::Yes) {
|
||||
wantsCompleteMigration = true;
|
||||
migrateResources(migrationURL, jsonValue, isModelURL);
|
||||
} else {
|
||||
ModalDialogListener* migrationConfirmDialog2 = OffscreenUi::asyncQuestion(_dialogParent, MESSAGE_BOX_TITLE, COMPLETE_MIGRATION_TEXT,
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
|
||||
QObject::connect(migrationConfirmDialog2, &ModalDialogListener::response, this, [=] (QVariant answer) {
|
||||
QObject::disconnect(migrationConfirmDialog2, &ModalDialogListener::response, this, nullptr);
|
||||
if (static_cast<QMessageBox::StandardButton>(answer.toInt()) ==
|
||||
QMessageBox::Yes) {
|
||||
migrateResources(migrationURL, jsonValue, isModelURL);
|
||||
} else {
|
||||
_ignoredUrls.insert(migrationURL);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
hasAskedForCompleteMigration = true;
|
||||
}
|
||||
if (wantsCompleteMigration) {
|
||||
migrateResources(migrationURL, jsonValue, isModelURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_doneReading = true;
|
||||
|
||||
checkIfFinished();
|
||||
|
||||
} else {
|
||||
OffscreenUi::asyncWarning(_dialogParent, "Error",
|
||||
"There was a problem loading that entity-server file for ATP asset migration. Please try again");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ATPAssetMigrator::migrateResource(ResourceRequest* request) {
|
||||
|
@ -248,9 +292,6 @@ void ATPAssetMigrator::checkIfFinished() {
|
|||
// are we out of pending replacements? if so it is time to save the entity-server file
|
||||
if (_doneReading && _pendingReplacements.empty()) {
|
||||
saveEntityServerFile();
|
||||
|
||||
// reset after the attempted save, success or fail
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,39 +329,43 @@ bool ATPAssetMigrator::wantsToMigrateResource(const QUrl& url) {
|
|||
|
||||
void ATPAssetMigrator::saveEntityServerFile() {
|
||||
// show a dialog to ask the user where they want to save the file
|
||||
QString saveName = OffscreenUi::getSaveFileName(_dialogParent, "Save Migrated Entities File");
|
||||
|
||||
QFile saveFile { saveName };
|
||||
|
||||
if (saveFile.open(QIODevice::WriteOnly)) {
|
||||
QJsonObject rootObject;
|
||||
rootObject[ENTITIES_OBJECT_KEY] = _entitiesArray;
|
||||
|
||||
QJsonDocument newDocument { rootObject };
|
||||
QByteArray jsonDataForFile;
|
||||
|
||||
if (gzip(newDocument.toJson(), jsonDataForFile, -1)) {
|
||||
|
||||
saveFile.write(jsonDataForFile);
|
||||
saveFile.close();
|
||||
ModalDialogListener* dlg = OffscreenUi::getSaveFileNameAsync(_dialogParent, "Save Migrated Entities File");
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
const QString& saveName = response.toString();
|
||||
QFile saveFile { saveName };
|
||||
|
||||
QString infoMessage = QString("Your new entities file has been saved at\n%1.").arg(saveName);
|
||||
if (saveFile.open(QIODevice::WriteOnly)) {
|
||||
QJsonObject rootObject;
|
||||
rootObject[ENTITIES_OBJECT_KEY] = _entitiesArray;
|
||||
|
||||
if (_errorCount > 0) {
|
||||
infoMessage += QString("\nThere were %1 models that could not be migrated.\n").arg(_errorCount);
|
||||
infoMessage += "Check the warnings in your log for details.\n";
|
||||
infoMessage += "You can re-attempt migration on those models\nby restarting this process with the newly saved file.";
|
||||
QJsonDocument newDocument { rootObject };
|
||||
QByteArray jsonDataForFile;
|
||||
|
||||
if (gzip(newDocument.toJson(), jsonDataForFile, -1)) {
|
||||
|
||||
saveFile.write(jsonDataForFile);
|
||||
saveFile.close();
|
||||
|
||||
QString infoMessage = QString("Your new entities file has been saved at\n%1.").arg(saveName);
|
||||
|
||||
if (_errorCount > 0) {
|
||||
infoMessage += QString("\nThere were %1 models that could not be migrated.\n").arg(_errorCount);
|
||||
infoMessage += "Check the warnings in your log for details.\n";
|
||||
infoMessage += "You can re-attempt migration on those models\nby restarting this process with the newly saved file.";
|
||||
}
|
||||
|
||||
OffscreenUi::asyncInformation(_dialogParent, "Success", infoMessage);
|
||||
} else {
|
||||
OffscreenUi::asyncWarning(_dialogParent, "Error", "Could not gzip JSON data for new entities file.");
|
||||
}
|
||||
|
||||
OffscreenUi::information(_dialogParent, "Success", infoMessage);
|
||||
} else {
|
||||
OffscreenUi::warning(_dialogParent, "Error", "Could not gzip JSON data for new entities file.");
|
||||
OffscreenUi::asyncWarning(_dialogParent, "Error",
|
||||
QString("Could not open file at %1 to write new entities file to.").arg(saveName));
|
||||
}
|
||||
|
||||
} else {
|
||||
OffscreenUi::warning(_dialogParent, "Error",
|
||||
QString("Could not open file at %1 to write new entities file to.").arg(saveName));
|
||||
}
|
||||
// reset after the attempted save, success or fail
|
||||
reset();
|
||||
});
|
||||
}
|
||||
|
||||
void ATPAssetMigrator::reset() {
|
||||
|
|
|
@ -3043,9 +3043,9 @@ glm::mat4 MyAvatar::getCenterEyeCalibrationMat() const {
|
|||
if (rightEyeIndex >= 0 && leftEyeIndex >= 0) {
|
||||
auto centerEyePos = (getAbsoluteDefaultJointTranslationInObjectFrame(rightEyeIndex) + getAbsoluteDefaultJointTranslationInObjectFrame(leftEyeIndex)) * 0.5f;
|
||||
auto centerEyeRot = Quaternions::Y_180;
|
||||
return createMatFromQuatAndPos(centerEyeRot, centerEyePos);
|
||||
return createMatFromQuatAndPos(centerEyeRot, centerEyePos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_MIDDLE_EYE_ROT, DEFAULT_AVATAR_MIDDLE_EYE_POS);
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_MIDDLE_EYE_ROT, DEFAULT_AVATAR_MIDDLE_EYE_POS / getSensorToWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3055,9 +3055,9 @@ glm::mat4 MyAvatar::getHeadCalibrationMat() const {
|
|||
if (headIndex >= 0) {
|
||||
auto headPos = getAbsoluteDefaultJointTranslationInObjectFrame(headIndex);
|
||||
auto headRot = getAbsoluteDefaultJointRotationInObjectFrame(headIndex);
|
||||
return createMatFromQuatAndPos(headRot, headPos);
|
||||
return createMatFromQuatAndPos(headRot, headPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_HEAD_ROT, DEFAULT_AVATAR_HEAD_POS);
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_HEAD_ROT, DEFAULT_AVATAR_HEAD_POS / getSensorToWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3067,9 +3067,9 @@ glm::mat4 MyAvatar::getSpine2CalibrationMat() const {
|
|||
if (spine2Index >= 0) {
|
||||
auto spine2Pos = getAbsoluteDefaultJointTranslationInObjectFrame(spine2Index);
|
||||
auto spine2Rot = getAbsoluteDefaultJointRotationInObjectFrame(spine2Index);
|
||||
return createMatFromQuatAndPos(spine2Rot, spine2Pos);
|
||||
return createMatFromQuatAndPos(spine2Rot, spine2Pos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_SPINE2_ROT, DEFAULT_AVATAR_SPINE2_POS);
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_SPINE2_ROT, DEFAULT_AVATAR_SPINE2_POS / getSensorToWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3079,9 +3079,9 @@ glm::mat4 MyAvatar::getHipsCalibrationMat() const {
|
|||
if (hipsIndex >= 0) {
|
||||
auto hipsPos = getAbsoluteDefaultJointTranslationInObjectFrame(hipsIndex);
|
||||
auto hipsRot = getAbsoluteDefaultJointRotationInObjectFrame(hipsIndex);
|
||||
return createMatFromQuatAndPos(hipsRot, hipsPos);
|
||||
return createMatFromQuatAndPos(hipsRot, hipsPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_HIPS_ROT, DEFAULT_AVATAR_HIPS_POS);
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_HIPS_ROT, DEFAULT_AVATAR_HIPS_POS / getSensorToWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3091,9 +3091,9 @@ glm::mat4 MyAvatar::getLeftFootCalibrationMat() const {
|
|||
if (leftFootIndex >= 0) {
|
||||
auto leftFootPos = getAbsoluteDefaultJointTranslationInObjectFrame(leftFootIndex);
|
||||
auto leftFootRot = getAbsoluteDefaultJointRotationInObjectFrame(leftFootIndex);
|
||||
return createMatFromQuatAndPos(leftFootRot, leftFootPos);
|
||||
return createMatFromQuatAndPos(leftFootRot, leftFootPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_ROT, DEFAULT_AVATAR_LEFTFOOT_POS);
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_ROT, DEFAULT_AVATAR_LEFTFOOT_POS / getSensorToWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3103,9 +3103,9 @@ glm::mat4 MyAvatar::getRightFootCalibrationMat() const {
|
|||
if (rightFootIndex >= 0) {
|
||||
auto rightFootPos = getAbsoluteDefaultJointTranslationInObjectFrame(rightFootIndex);
|
||||
auto rightFootRot = getAbsoluteDefaultJointRotationInObjectFrame(rightFootIndex);
|
||||
return createMatFromQuatAndPos(rightFootRot, rightFootPos);
|
||||
return createMatFromQuatAndPos(rightFootRot, rightFootPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_ROT, DEFAULT_AVATAR_RIGHTFOOT_POS);
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_ROT, DEFAULT_AVATAR_RIGHTFOOT_POS / getSensorToWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3115,9 +3115,9 @@ glm::mat4 MyAvatar::getRightArmCalibrationMat() const {
|
|||
if (rightArmIndex >= 0) {
|
||||
auto rightArmPos = getAbsoluteDefaultJointTranslationInObjectFrame(rightArmIndex);
|
||||
auto rightArmRot = getAbsoluteDefaultJointRotationInObjectFrame(rightArmIndex);
|
||||
return createMatFromQuatAndPos(rightArmRot, rightArmPos);
|
||||
return createMatFromQuatAndPos(rightArmRot, rightArmPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTARM_ROT, DEFAULT_AVATAR_RIGHTARM_POS);
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTARM_ROT, DEFAULT_AVATAR_RIGHTARM_POS / getSensorToWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3126,9 +3126,9 @@ glm::mat4 MyAvatar::getLeftArmCalibrationMat() const {
|
|||
if (leftArmIndex >= 0) {
|
||||
auto leftArmPos = getAbsoluteDefaultJointTranslationInObjectFrame(leftArmIndex);
|
||||
auto leftArmRot = getAbsoluteDefaultJointRotationInObjectFrame(leftArmIndex);
|
||||
return createMatFromQuatAndPos(leftArmRot, leftArmPos);
|
||||
return createMatFromQuatAndPos(leftArmRot, leftArmPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_LEFTARM_POS);
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_LEFTARM_POS / getSensorToWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3137,9 +3137,9 @@ glm::mat4 MyAvatar::getRightHandCalibrationMat() const {
|
|||
if (rightHandIndex >= 0) {
|
||||
auto rightHandPos = getAbsoluteDefaultJointTranslationInObjectFrame(rightHandIndex);
|
||||
auto rightHandRot = getAbsoluteDefaultJointRotationInObjectFrame(rightHandIndex);
|
||||
return createMatFromQuatAndPos(rightHandRot, rightHandPos);
|
||||
return createMatFromQuatAndPos(rightHandRot, rightHandPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTHAND_ROT, DEFAULT_AVATAR_RIGHTHAND_POS);
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTHAND_ROT, DEFAULT_AVATAR_RIGHTHAND_POS / getSensorToWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3148,9 +3148,9 @@ glm::mat4 MyAvatar::getLeftHandCalibrationMat() const {
|
|||
if (leftHandIndex >= 0) {
|
||||
auto leftHandPos = getAbsoluteDefaultJointTranslationInObjectFrame(leftHandIndex);
|
||||
auto leftHandRot = getAbsoluteDefaultJointRotationInObjectFrame(leftHandIndex);
|
||||
return createMatFromQuatAndPos(leftHandRot, leftHandPos);
|
||||
return createMatFromQuatAndPos(leftHandRot, leftHandPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTHAND_ROT, DEFAULT_AVATAR_LEFTHAND_POS);
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTHAND_ROT, DEFAULT_AVATAR_LEFTHAND_POS / getSensorToWorldScale());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,8 +92,7 @@ void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant&
|
|||
}
|
||||
}
|
||||
|
||||
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const bool defaultState) {
|
||||
PickRay pickRay = qApp->getRayPickManager().getPickRay(_rayPickUID);
|
||||
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState) {
|
||||
if (!renderState.getStartID().isNull()) {
|
||||
QVariantMap startProps;
|
||||
startProps.insert("position", vec3toVariant(pickRay.origin));
|
||||
|
@ -185,12 +184,14 @@ void LaserPointer::disableRenderState(const RenderState& renderState) {
|
|||
|
||||
void LaserPointer::update() {
|
||||
RayPickResult prevRayPickResult = DependencyManager::get<RayPickScriptingInterface>()->getPrevRayPickResult(_rayPickUID);
|
||||
if (_renderingEnabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() && prevRayPickResult.type != IntersectionType::NONE) {
|
||||
updateRenderState(_renderStates[_currentRenderState], prevRayPickResult.type, prevRayPickResult.distance, prevRayPickResult.objectID, false);
|
||||
if (_renderingEnabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
|
||||
(prevRayPickResult.type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) {
|
||||
float distance = _laserLength > 0.0f ? _laserLength : prevRayPickResult.distance;
|
||||
updateRenderState(_renderStates[_currentRenderState], prevRayPickResult.type, distance, prevRayPickResult.objectID, prevRayPickResult.searchRay, false);
|
||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||
} else if (_renderingEnabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
||||
disableRenderState(_renderStates[_currentRenderState]);
|
||||
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), true);
|
||||
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), prevRayPickResult.searchRay, true);
|
||||
} else if (!_currentRenderState.empty()) {
|
||||
disableRenderState(_renderStates[_currentRenderState]);
|
||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||
|
|
|
@ -65,6 +65,7 @@ public:
|
|||
void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps);
|
||||
|
||||
void setPrecisionPicking(const bool precisionPicking) { DependencyManager::get<RayPickScriptingInterface>()->setPrecisionPicking(_rayPickUID, precisionPicking); }
|
||||
void setLaserLength(const float laserLength) { _laserLength = laserLength; }
|
||||
void setIgnoreEntities(const QScriptValue& ignoreEntities) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreEntities(_rayPickUID, ignoreEntities); }
|
||||
void setIncludeEntities(const QScriptValue& includeEntities) { DependencyManager::get<RayPickScriptingInterface>()->setIncludeEntities(_rayPickUID, includeEntities); }
|
||||
void setIgnoreOverlays(const QScriptValue& ignoreOverlays) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreOverlays(_rayPickUID, ignoreOverlays); }
|
||||
|
@ -78,6 +79,7 @@ public:
|
|||
|
||||
private:
|
||||
bool _renderingEnabled;
|
||||
float _laserLength { 0.0f };
|
||||
std::string _currentRenderState { "" };
|
||||
RenderStateMap _renderStates;
|
||||
DefaultRenderStateMap _defaultRenderStates;
|
||||
|
@ -89,7 +91,7 @@ private:
|
|||
QUuid _rayPickUID;
|
||||
|
||||
void updateRenderStateOverlay(const OverlayID& id, const QVariant& props);
|
||||
void updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const bool defaultState);
|
||||
void updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState);
|
||||
void disableRenderState(const RenderState& renderState);
|
||||
|
||||
};
|
||||
|
|
|
@ -14,17 +14,19 @@ QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const La
|
|||
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled) {
|
||||
std::shared_ptr<LaserPointer> laserPointer = std::make_shared<LaserPointer>(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, enabled);
|
||||
if (!laserPointer->getRayUID().isNull()) {
|
||||
QWriteLocker lock(&_addLock);
|
||||
QWriteLocker containsLock(&_containsLock);
|
||||
QUuid id = QUuid::createUuid();
|
||||
_laserPointersToAdd.push(std::pair<QUuid, std::shared_ptr<LaserPointer>>(id, laserPointer));
|
||||
_laserPointers[id] = laserPointer;
|
||||
_laserPointerLocks[id] = std::make_shared<QReadWriteLock>();
|
||||
return id;
|
||||
}
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
void LaserPointerManager::removeLaserPointer(const QUuid uid) {
|
||||
QWriteLocker lock(&_removeLock);
|
||||
_laserPointersToRemove.push(uid);
|
||||
QWriteLocker lock(&_containsLock);
|
||||
_laserPointers.remove(uid);
|
||||
_laserPointerLocks.remove(uid);
|
||||
}
|
||||
|
||||
void LaserPointerManager::enableLaserPointer(const QUuid uid) {
|
||||
|
@ -69,32 +71,12 @@ const RayPickResult LaserPointerManager::getPrevRayPickResult(const QUuid uid) {
|
|||
}
|
||||
|
||||
void LaserPointerManager::update() {
|
||||
QReadLocker lock(&_containsLock);
|
||||
for (QUuid& uid : _laserPointers.keys()) {
|
||||
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
||||
QReadLocker laserLock(_laserPointerLocks[uid].get());
|
||||
_laserPointers[uid]->update();
|
||||
}
|
||||
|
||||
QWriteLocker containsLock(&_containsLock);
|
||||
{
|
||||
QWriteLocker lock(&_addLock);
|
||||
while (!_laserPointersToAdd.empty()) {
|
||||
std::pair<QUuid, std::shared_ptr<LaserPointer>> laserPointerToAdd = _laserPointersToAdd.front();
|
||||
_laserPointersToAdd.pop();
|
||||
_laserPointers[laserPointerToAdd.first] = laserPointerToAdd.second;
|
||||
_laserPointerLocks[laserPointerToAdd.first] = std::make_shared<QReadWriteLock>();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_removeLock);
|
||||
while (!_laserPointersToRemove.empty()) {
|
||||
QUuid uid = _laserPointersToRemove.front();
|
||||
_laserPointersToRemove.pop();
|
||||
_laserPointers.remove(uid);
|
||||
_laserPointerLocks.remove(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LaserPointerManager::setPrecisionPicking(QUuid uid, const bool precisionPicking) {
|
||||
|
@ -105,6 +87,14 @@ void LaserPointerManager::setPrecisionPicking(QUuid uid, const bool precisionPic
|
|||
}
|
||||
}
|
||||
|
||||
void LaserPointerManager::setLaserLength(QUuid uid, const float laserLength) {
|
||||
QReadLocker lock(&_containsLock);
|
||||
if (_laserPointers.contains(uid)) {
|
||||
QWriteLocker laserLock(_laserPointerLocks[uid].get());
|
||||
_laserPointers[uid]->setLaserLength(laserLength);
|
||||
}
|
||||
}
|
||||
|
||||
void LaserPointerManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
|
||||
QReadLocker lock(&_containsLock);
|
||||
if (_laserPointers.contains(uid)) {
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
const RayPickResult getPrevRayPickResult(const QUuid uid);
|
||||
|
||||
void setPrecisionPicking(QUuid uid, const bool precisionPicking);
|
||||
void setLaserLength(QUuid uid, const float laserLength);
|
||||
void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities);
|
||||
void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities);
|
||||
void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays);
|
||||
|
@ -46,10 +47,6 @@ public:
|
|||
private:
|
||||
QHash<QUuid, std::shared_ptr<LaserPointer>> _laserPointers;
|
||||
QHash<QUuid, std::shared_ptr<QReadWriteLock>> _laserPointerLocks;
|
||||
QReadWriteLock _addLock;
|
||||
std::queue<std::pair<QUuid, std::shared_ptr<LaserPointer>>> _laserPointersToAdd;
|
||||
QReadWriteLock _removeLock;
|
||||
std::queue<QUuid> _laserPointersToRemove;
|
||||
QReadWriteLock _containsLock;
|
||||
|
||||
};
|
||||
|
|
|
@ -31,6 +31,7 @@ public slots:
|
|||
Q_INVOKABLE RayPickResult getPrevRayPickResult(QUuid uid) { return qApp->getLaserPointerManager().getPrevRayPickResult(uid); }
|
||||
|
||||
Q_INVOKABLE void setPrecisionPicking(QUuid uid, const bool precisionPicking) { qApp->getLaserPointerManager().setPrecisionPicking(uid, precisionPicking); }
|
||||
Q_INVOKABLE void setLaserLength(QUuid uid, const float laserLength) { qApp->getLaserPointerManager().setLaserLength(uid, laserLength); }
|
||||
Q_INVOKABLE void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) { qApp->getLaserPointerManager().setIgnoreEntities(uid, ignoreEntities); }
|
||||
Q_INVOKABLE void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) { qApp->getLaserPointerManager().setIncludeEntities(uid, includeEntities); }
|
||||
Q_INVOKABLE void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) { qApp->getLaserPointerManager().setIgnoreOverlays(uid, ignoreOverlays); }
|
||||
|
|
|
@ -70,6 +70,9 @@ public:
|
|||
if (doesPickNonCollidable()) {
|
||||
toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE);
|
||||
}
|
||||
if (doesPickCourse()) {
|
||||
toReturn |= getBitMask(PICK_COURSE);
|
||||
}
|
||||
return Flags(toReturn);
|
||||
}
|
||||
Flags getOverlayFlags() const {
|
||||
|
@ -80,6 +83,9 @@ public:
|
|||
if (doesPickNonCollidable()) {
|
||||
toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE);
|
||||
}
|
||||
if (doesPickCourse()) {
|
||||
toReturn |= getBitMask(PICK_COURSE);
|
||||
}
|
||||
return Flags(toReturn);
|
||||
}
|
||||
Flags getAvatarFlags() const { return Flags(getBitMask(PICK_AVATARS)); }
|
||||
|
|
|
@ -38,11 +38,12 @@ void RayPickManager::cacheResult(const bool intersects, const RayPickResult& res
|
|||
res = resTemp;
|
||||
}
|
||||
} else {
|
||||
cache[ray][mask] = RayPickResult();
|
||||
cache[ray][mask] = RayPickResult(res.searchRay);
|
||||
}
|
||||
}
|
||||
|
||||
void RayPickManager::update() {
|
||||
QReadLocker lock(&_containsLock);
|
||||
RayPickCache results;
|
||||
for (auto& uid : _rayPicks.keys()) {
|
||||
std::shared_ptr<RayPick> rayPick = _rayPicks[uid];
|
||||
|
@ -58,7 +59,7 @@ void RayPickManager::update() {
|
|||
}
|
||||
|
||||
QPair<glm::vec3, glm::vec3> rayKey = QPair<glm::vec3, glm::vec3>(ray.origin, ray.direction);
|
||||
RayPickResult res;
|
||||
RayPickResult res = RayPickResult(ray);
|
||||
|
||||
if (rayPick->getFilter().doesPickEntities()) {
|
||||
RayToEntityIntersectionResult entityRes;
|
||||
|
@ -73,7 +74,7 @@ void RayPickManager::update() {
|
|||
}
|
||||
|
||||
if (!fromCache) {
|
||||
cacheResult(entityRes.intersects, RayPickResult(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, entityRes.surfaceNormal),
|
||||
cacheResult(entityRes.intersects, RayPickResult(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, ray, entityRes.surfaceNormal),
|
||||
entityMask, res, rayKey, results);
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +92,7 @@ void RayPickManager::update() {
|
|||
}
|
||||
|
||||
if (!fromCache) {
|
||||
cacheResult(overlayRes.intersects, RayPickResult(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, overlayRes.surfaceNormal),
|
||||
cacheResult(overlayRes.intersects, RayPickResult(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, ray, overlayRes.surfaceNormal),
|
||||
overlayMask, res, rayKey, results);
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +101,7 @@ void RayPickManager::update() {
|
|||
RayPickFilter::Flags avatarMask = rayPick->getFilter().getAvatarFlags();
|
||||
if (!checkAndCompareCachedResults(rayKey, results, res, avatarMask)) {
|
||||
RayToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findRayIntersectionVector(ray, rayPick->getIncludeAvatars(), rayPick->getIgnoreAvatars());
|
||||
cacheResult(avatarRes.intersects, RayPickResult(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection), avatarMask, res, rayKey, results);
|
||||
cacheResult(avatarRes.intersects, RayPickResult(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, ray), avatarMask, res, rayKey, results);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +110,7 @@ void RayPickManager::update() {
|
|||
RayPickFilter::Flags hudMask = rayPick->getFilter().getHUDFlags();
|
||||
if (!checkAndCompareCachedResults(rayKey, results, res, hudMask)) {
|
||||
glm::vec3 hudRes = DependencyManager::get<HMDScriptingInterface>()->calculateRayUICollisionPoint(ray.origin, ray.direction);
|
||||
cacheResult(true, RayPickResult(IntersectionType::HUD, 0, glm::distance(ray.origin, hudRes), hudRes), hudMask, res, rayKey, results);
|
||||
cacheResult(true, RayPickResult(IntersectionType::HUD, 0, glm::distance(ray.origin, hudRes), hudRes, ray), hudMask, res, rayKey, results);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,56 +118,39 @@ void RayPickManager::update() {
|
|||
if (rayPick->getMaxDistance() == 0.0f || (rayPick->getMaxDistance() > 0.0f && res.distance < rayPick->getMaxDistance())) {
|
||||
rayPick->setRayPickResult(res);
|
||||
} else {
|
||||
rayPick->setRayPickResult(RayPickResult());
|
||||
}
|
||||
}
|
||||
|
||||
QWriteLocker containsLock(&_containsLock);
|
||||
{
|
||||
QWriteLocker lock(&_addLock);
|
||||
while (!_rayPicksToAdd.empty()) {
|
||||
std::pair<QUuid, std::shared_ptr<RayPick>> rayPickToAdd = _rayPicksToAdd.front();
|
||||
_rayPicksToAdd.pop();
|
||||
_rayPicks[rayPickToAdd.first] = rayPickToAdd.second;
|
||||
_rayPickLocks[rayPickToAdd.first] = std::make_shared<QReadWriteLock>();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_removeLock);
|
||||
while (!_rayPicksToRemove.empty()) {
|
||||
QUuid uid = _rayPicksToRemove.front();
|
||||
_rayPicksToRemove.pop();
|
||||
_rayPicks.remove(uid);
|
||||
_rayPickLocks.remove(uid);
|
||||
rayPick->setRayPickResult(RayPickResult(ray));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QUuid RayPickManager::createRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const RayPickFilter& filter, const float maxDistance, const bool enabled) {
|
||||
QWriteLocker lock(&_addLock);
|
||||
QWriteLocker lock(&_containsLock);
|
||||
QUuid id = QUuid::createUuid();
|
||||
_rayPicksToAdd.push(std::pair<QUuid, std::shared_ptr<RayPick>>(id, std::make_shared<JointRayPick>(jointName, posOffset, dirOffset, filter, maxDistance, enabled)));
|
||||
_rayPicks[id] = std::make_shared<JointRayPick>(jointName, posOffset, dirOffset, filter, maxDistance, enabled);
|
||||
_rayPickLocks[id] = std::make_shared<QReadWriteLock>();
|
||||
return id;
|
||||
}
|
||||
|
||||
QUuid RayPickManager::createRayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled) {
|
||||
QWriteLocker lock(&_addLock);
|
||||
QWriteLocker lock(&_containsLock);
|
||||
QUuid id = QUuid::createUuid();
|
||||
_rayPicksToAdd.push(std::pair<QUuid, std::shared_ptr<RayPick>>(id, std::make_shared<MouseRayPick>(filter, maxDistance, enabled)));
|
||||
_rayPicks[id] = std::make_shared<MouseRayPick>(filter, maxDistance, enabled);
|
||||
_rayPickLocks[id] = std::make_shared<QReadWriteLock>();
|
||||
return id;
|
||||
}
|
||||
|
||||
QUuid RayPickManager::createRayPick(const glm::vec3& position, const glm::vec3& direction, const RayPickFilter& filter, const float maxDistance, const bool enabled) {
|
||||
QWriteLocker lock(&_addLock);
|
||||
QWriteLocker lock(&_containsLock);
|
||||
QUuid id = QUuid::createUuid();
|
||||
_rayPicksToAdd.push(std::pair<QUuid, std::shared_ptr<RayPick>>(id, std::make_shared<StaticRayPick>(position, direction, filter, maxDistance, enabled)));
|
||||
_rayPicks[id] = std::make_shared<StaticRayPick>(position, direction, filter, maxDistance, enabled);
|
||||
_rayPickLocks[id] = std::make_shared<QReadWriteLock>();
|
||||
return id;
|
||||
}
|
||||
|
||||
void RayPickManager::removeRayPick(const QUuid uid) {
|
||||
QWriteLocker lock(&_removeLock);
|
||||
_rayPicksToRemove.push(uid);
|
||||
QWriteLocker lock(&_containsLock);
|
||||
_rayPicks.remove(uid);
|
||||
_rayPickLocks.remove(uid);
|
||||
}
|
||||
|
||||
void RayPickManager::enableRayPick(const QUuid uid) {
|
||||
|
@ -185,18 +169,6 @@ void RayPickManager::disableRayPick(const QUuid uid) {
|
|||
}
|
||||
}
|
||||
|
||||
const PickRay RayPickManager::getPickRay(const QUuid uid) {
|
||||
QReadLocker containsLock(&_containsLock);
|
||||
if (_rayPicks.contains(uid)) {
|
||||
bool valid;
|
||||
PickRay pickRay = _rayPicks[uid]->getPickRay(valid);
|
||||
if (valid) {
|
||||
return pickRay;
|
||||
}
|
||||
}
|
||||
return PickRay();
|
||||
}
|
||||
|
||||
const RayPickResult RayPickManager::getPrevRayPickResult(const QUuid uid) {
|
||||
QReadLocker containsLock(&_containsLock);
|
||||
if (_rayPicks.contains(uid)) {
|
||||
|
|
|
@ -28,7 +28,6 @@ class RayPickManager {
|
|||
|
||||
public:
|
||||
void update();
|
||||
const PickRay getPickRay(const QUuid uid);
|
||||
|
||||
QUuid createRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const RayPickFilter& filter, const float maxDistance, const bool enabled);
|
||||
QUuid createRayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled);
|
||||
|
@ -49,10 +48,6 @@ public:
|
|||
private:
|
||||
QHash<QUuid, std::shared_ptr<RayPick>> _rayPicks;
|
||||
QHash<QUuid, std::shared_ptr<QReadWriteLock>> _rayPickLocks;
|
||||
QReadWriteLock _addLock;
|
||||
std::queue<std::pair<QUuid, std::shared_ptr<RayPick>>> _rayPicksToAdd;
|
||||
QReadWriteLock _removeLock;
|
||||
std::queue<QUuid> _rayPicksToRemove;
|
||||
QReadWriteLock _containsLock;
|
||||
|
||||
typedef QHash<QPair<glm::vec3, glm::vec3>, std::unordered_map<RayPickFilter::Flags, RayPickResult>> RayPickCache;
|
||||
|
|
|
@ -59,7 +59,7 @@ WindowScriptingInterface::WindowScriptingInterface() {
|
|||
QUrl url(urlString);
|
||||
emit svoImportRequested(url.url());
|
||||
} else {
|
||||
OffscreenUi::warning("Import SVO Error", "You need to be running edit.js to import entities.");
|
||||
OffscreenUi::asyncWarning("Import SVO Error", "You need to be running edit.js to import entities.");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -103,7 +103,7 @@ void WindowScriptingInterface::raiseMainWindow() {
|
|||
/// \param const QString& message message to display
|
||||
/// \return QScriptValue::UndefinedValue
|
||||
void WindowScriptingInterface::alert(const QString& message) {
|
||||
OffscreenUi::warning("", message);
|
||||
OffscreenUi::asyncWarning("", message);
|
||||
}
|
||||
|
||||
/// Display a confirmation box with the options 'Yes' and 'No'
|
||||
|
@ -123,6 +123,17 @@ QScriptValue WindowScriptingInterface::prompt(const QString& message, const QStr
|
|||
return ok ? QScriptValue(result) : QScriptValue::NullValue;
|
||||
}
|
||||
|
||||
/// Display a prompt with a text box
|
||||
/// \param const QString& message message to display
|
||||
/// \param const QString& defaultText default text in the text box
|
||||
void WindowScriptingInterface::promptAsync(const QString& message, const QString& defaultText) {
|
||||
ModalDialogListener* dlg = OffscreenUi::getTextAsync(nullptr, "", message, QLineEdit::Normal, defaultText);
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant result) {
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
emit promptTextChanged(result.toString());
|
||||
});
|
||||
}
|
||||
|
||||
CustomPromptResult WindowScriptingInterface::customPrompt(const QVariant& config) {
|
||||
CustomPromptResult result;
|
||||
bool ok = false;
|
||||
|
@ -191,8 +202,31 @@ QScriptValue WindowScriptingInterface::browseDir(const QString& title, const QSt
|
|||
return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result);
|
||||
}
|
||||
|
||||
/// Display an open file dialog. If `directory` is an invalid file or directory the browser will start at the current
|
||||
/// Display a "browse to directory" dialog. If `directory` is an invalid file or directory the browser will start at the current
|
||||
/// working directory.
|
||||
/// \param const QString& title title of the window
|
||||
/// \param const QString& directory directory to start the file browser at
|
||||
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
|
||||
void WindowScriptingInterface::browseDirAsync(const QString& title, const QString& directory) {
|
||||
ensureReticleVisible();
|
||||
QString path = directory;
|
||||
if (path.isEmpty()) {
|
||||
path = getPreviousBrowseLocation();
|
||||
}
|
||||
#ifndef Q_OS_WIN
|
||||
path = fixupPathForMac(directory);
|
||||
#endif
|
||||
ModalDialogListener* dlg = OffscreenUi::getExistingDirectoryAsync(nullptr, title, path);
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
const QString& result = response.toString();
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
if (!result.isEmpty()) {
|
||||
setPreviousBrowseLocation(QFileInfo(result).absolutePath());
|
||||
}
|
||||
emit browseDirChanged(result);
|
||||
});
|
||||
}
|
||||
|
||||
/// \param const QString& title title of the window
|
||||
/// \param const QString& directory directory to start the file browser at
|
||||
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
|
||||
|
@ -213,6 +247,31 @@ QScriptValue WindowScriptingInterface::browse(const QString& title, const QStrin
|
|||
return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result);
|
||||
}
|
||||
|
||||
/// Display an open file dialog. If `directory` is an invalid file or directory the browser will start at the current
|
||||
/// working directory.
|
||||
/// \param const QString& title title of the window
|
||||
/// \param const QString& directory directory to start the file browser at
|
||||
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
|
||||
void WindowScriptingInterface::browseAsync(const QString& title, const QString& directory, const QString& nameFilter) {
|
||||
ensureReticleVisible();
|
||||
QString path = directory;
|
||||
if (path.isEmpty()) {
|
||||
path = getPreviousBrowseLocation();
|
||||
}
|
||||
#ifndef Q_OS_WIN
|
||||
path = fixupPathForMac(directory);
|
||||
#endif
|
||||
ModalDialogListener* dlg = OffscreenUi::getOpenFileNameAsync(nullptr, title, path, nameFilter);
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
const QString& result = response.toString();
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
if (!result.isEmpty()) {
|
||||
setPreviousBrowseLocation(QFileInfo(result).absolutePath());
|
||||
}
|
||||
emit openFileChanged(result);
|
||||
});
|
||||
}
|
||||
|
||||
/// Display a save file dialog. If `directory` is an invalid file or directory the browser will start at the current
|
||||
/// working directory.
|
||||
/// \param const QString& title title of the window
|
||||
|
@ -235,7 +294,32 @@ QScriptValue WindowScriptingInterface::save(const QString& title, const QString&
|
|||
return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result);
|
||||
}
|
||||
|
||||
/// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid
|
||||
/// Display a save file dialog. If `directory` is an invalid file or directory the browser will start at the current
|
||||
/// working directory.
|
||||
/// \param const QString& title title of the window
|
||||
/// \param const QString& directory directory to start the file browser at
|
||||
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
|
||||
void WindowScriptingInterface::saveAsync(const QString& title, const QString& directory, const QString& nameFilter) {
|
||||
ensureReticleVisible();
|
||||
QString path = directory;
|
||||
if (path.isEmpty()) {
|
||||
path = getPreviousBrowseLocation();
|
||||
}
|
||||
#ifndef Q_OS_WIN
|
||||
path = fixupPathForMac(directory);
|
||||
#endif
|
||||
ModalDialogListener* dlg = OffscreenUi::getSaveFileNameAsync(nullptr, title, path, nameFilter);
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
const QString& result = response.toString();
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
if (!result.isEmpty()) {
|
||||
setPreviousBrowseLocation(QFileInfo(result).absolutePath());
|
||||
}
|
||||
emit saveFileChanged(result);
|
||||
});
|
||||
}
|
||||
|
||||
/// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid
|
||||
/// directory the browser will start at the root directory.
|
||||
/// \param const QString& title title of the window
|
||||
/// \param const QString& directory directory to start the asset browser at
|
||||
|
@ -260,6 +344,35 @@ QScriptValue WindowScriptingInterface::browseAssets(const QString& title, const
|
|||
return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result);
|
||||
}
|
||||
|
||||
/// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid
|
||||
/// directory the browser will start at the root directory.
|
||||
/// \param const QString& title title of the window
|
||||
/// \param const QString& directory directory to start the asset browser at
|
||||
/// \param const QString& nameFilter filter to filter asset names by - see `QFileDialog`
|
||||
void WindowScriptingInterface::browseAssetsAsync(const QString& title, const QString& directory, const QString& nameFilter) {
|
||||
ensureReticleVisible();
|
||||
QString path = directory;
|
||||
if (path.isEmpty()) {
|
||||
path = getPreviousBrowseAssetLocation();
|
||||
}
|
||||
if (path.left(1) != "/") {
|
||||
path = "/" + path;
|
||||
}
|
||||
if (path.right(1) != "/") {
|
||||
path = path + "/";
|
||||
}
|
||||
|
||||
ModalDialogListener* dlg = OffscreenUi::getOpenAssetNameAsync(nullptr, title, path, nameFilter);
|
||||
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
|
||||
const QString& result = response.toString();
|
||||
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
|
||||
if (!result.isEmpty()) {
|
||||
setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath());
|
||||
}
|
||||
emit assetsDirChanged(result);
|
||||
});
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::showAssetServer(const QString& upload) {
|
||||
QMetaObject::invokeMethod(qApp, "showAssetServerWidget", Qt::QueuedConnection, Q_ARG(QString, upload));
|
||||
}
|
||||
|
|
|
@ -51,12 +51,17 @@ public slots:
|
|||
void raiseMainWindow();
|
||||
void alert(const QString& message = "");
|
||||
QScriptValue confirm(const QString& message = "");
|
||||
QScriptValue prompt(const QString& message = "", const QString& defaultText = "");
|
||||
QScriptValue prompt(const QString& message, const QString& defaultText);
|
||||
void promptAsync(const QString& message = "", const QString& defaultText = "");
|
||||
CustomPromptResult customPrompt(const QVariant& config);
|
||||
QScriptValue browseDir(const QString& title = "", const QString& directory = "");
|
||||
void browseDirAsync(const QString& title = "", const QString& directory = "");
|
||||
QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||
void browseAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||
void saveAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||
QScriptValue browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||
void browseAssetsAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||
void showAssetServer(const QString& upload = "");
|
||||
QString checkVersion();
|
||||
void copyToClipboard(const QString& text);
|
||||
|
@ -89,6 +94,11 @@ signals:
|
|||
void announcement(const QString& message);
|
||||
|
||||
void messageBoxClosed(int id, int button);
|
||||
void browseDirChanged(QString browseDir);
|
||||
void assetsDirChanged(QString assetsDir);
|
||||
void saveFileChanged(QString filename);
|
||||
void openFileChanged(QString filename);
|
||||
void promptTextChanged(QString text);
|
||||
|
||||
// triggered when window size or position changes
|
||||
void geometryChanged(QRect geometry);
|
||||
|
|
|
@ -73,11 +73,11 @@ void AddressBarDialog::loadForward() {
|
|||
}
|
||||
|
||||
void AddressBarDialog::displayAddressOfflineMessage() {
|
||||
OffscreenUi::critical("", "That user or place is currently offline");
|
||||
OffscreenUi::asyncCritical("", "That user or place is currently offline");
|
||||
}
|
||||
|
||||
void AddressBarDialog::displayAddressNotFoundMessage() {
|
||||
OffscreenUi::critical("", "There is no address information for that user or place");
|
||||
OffscreenUi::asyncCritical("", "There is no address information for that user or place");
|
||||
}
|
||||
|
||||
void AddressBarDialog::observeShownChanged(bool visible) {
|
||||
|
|
|
@ -91,16 +91,6 @@ void setupPreferences() {
|
|||
preference->setMax(500);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getDesktopTabletBecomesToolbarSetting(); };
|
||||
auto setter = [](bool value) { qApp->setDesktopTabletBecomesToolbarSetting(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Desktop Tablet Becomes Toolbar", getter, setter));
|
||||
}
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getHmdTabletBecomesToolbarSetting(); };
|
||||
auto setter = [](bool value) { qApp->setHmdTabletBecomesToolbarSetting(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "HMD Tablet Becomes Toolbar", getter, setter));
|
||||
}
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getPreferAvatarFingerOverStylus(); };
|
||||
auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); };
|
||||
|
|
|
@ -268,18 +268,17 @@ void Base3DOverlay::update(float duration) {
|
|||
|
||||
// In Base3DOverlay, if its location or bound changed, the renderTrasnformDirty flag is true.
|
||||
// then the correct transform used for rendering is computed in the update transaction and assigned.
|
||||
// TODO: Fix the value to be computed in main thread now and passed by value to the render item.
|
||||
// This is the simplest fix for the web overlay of the tablet for now
|
||||
if (_renderTransformDirty) {
|
||||
_renderTransformDirty = false;
|
||||
auto itemID = getRenderItemID();
|
||||
if (render::Item::isValidID(itemID)) {
|
||||
_renderTransformDirty = false;
|
||||
// Capture the render transform value in game loop before
|
||||
auto latestTransform = evalRenderTransform();
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
transaction.updateItem<Overlay>(itemID, [](Overlay& data) {
|
||||
transaction.updateItem<Overlay>(itemID, [latestTransform](Overlay& data) {
|
||||
auto overlay3D = dynamic_cast<Base3DOverlay*>(&data);
|
||||
if (overlay3D) {
|
||||
auto latestTransform = overlay3D->evalRenderTransform();
|
||||
overlay3D->setRenderTransform(latestTransform);
|
||||
}
|
||||
});
|
||||
|
@ -292,7 +291,7 @@ void Base3DOverlay::notifyRenderTransformChange() const {
|
|||
_renderTransformDirty = true;
|
||||
}
|
||||
|
||||
Transform Base3DOverlay::evalRenderTransform() const {
|
||||
Transform Base3DOverlay::evalRenderTransform() {
|
||||
return getTransform();
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ protected:
|
|||
virtual void parentDeleted() override;
|
||||
|
||||
mutable Transform _renderTransform;
|
||||
virtual Transform evalRenderTransform() const;
|
||||
virtual Transform evalRenderTransform();
|
||||
virtual void setRenderTransform(const Transform& transform);
|
||||
const Transform& getRenderTransform() const { return _renderTransform; }
|
||||
|
||||
|
|
|
@ -45,3 +45,14 @@ bool Billboard3DOverlay::applyTransformTo(Transform& transform, bool force) {
|
|||
}
|
||||
return transformChanged;
|
||||
}
|
||||
|
||||
Transform Billboard3DOverlay::evalRenderTransform() {
|
||||
Transform transform = getTransform();
|
||||
bool transformChanged = applyTransformTo(transform, true);
|
||||
// If the transform is not modified, setting the transform to
|
||||
// itself will cause drift over time due to floating point errors.
|
||||
if (transformChanged) {
|
||||
setTransform(transform);
|
||||
}
|
||||
return transform;
|
||||
}
|
|
@ -28,6 +28,8 @@ public:
|
|||
|
||||
protected:
|
||||
virtual bool applyTransformTo(Transform& transform, bool force = false) override;
|
||||
|
||||
Transform evalRenderTransform() override;
|
||||
};
|
||||
|
||||
#endif // hifi_Billboard3DOverlay_h
|
||||
|
|
|
@ -84,12 +84,7 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
batch.setPipeline(args->_shapePipeline->pipeline);
|
||||
}
|
||||
|
||||
// FIXME: THe line width of _lineWidth is not supported anymore, we ll need a workaround
|
||||
// FIXME Start using the _renderTransform instead of calling for Transform from here, do the custom things needed in evalRenderTransform()
|
||||
|
||||
auto transform = getTransform();
|
||||
transform.postScale(glm::vec3(getDimensions(), 1.0f));
|
||||
batch.setModelTransform(transform);
|
||||
batch.setModelTransform(getRenderTransform());
|
||||
|
||||
// for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
|
||||
// we just draw a line...
|
||||
|
|
|
@ -53,18 +53,11 @@ void Cube3DOverlay::render(RenderArgs* args) {
|
|||
const float MAX_COLOR = 255.0f;
|
||||
glm::vec4 cubeColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||
|
||||
// TODO: handle registration point??
|
||||
// FIXME Start using the _renderTransform instead of calling for Transform from here, do the custom things needed in evalRenderTransform()
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
|
||||
auto batch = args->_batch;
|
||||
|
||||
if (batch) {
|
||||
Transform transform;
|
||||
transform.setTranslation(position);
|
||||
transform.setRotation(rotation);
|
||||
Transform transform = getRenderTransform();
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
auto shapePipeline = args->_shapePipeline;
|
||||
if (!shapePipeline) {
|
||||
|
@ -72,12 +65,12 @@ void Cube3DOverlay::render(RenderArgs* args) {
|
|||
}
|
||||
|
||||
if (_isSolid) {
|
||||
transform.setScale(dimensions);
|
||||
batch->setModelTransform(transform);
|
||||
geometryCache->renderSolidCubeInstance(args, *batch, cubeColor, shapePipeline);
|
||||
} else {
|
||||
geometryCache->bindSimpleProgram(*batch, false, false, false, true, true);
|
||||
if (getIsDashedLine()) {
|
||||
auto dimensions = transform.getScale();
|
||||
transform.setScale(1.0f);
|
||||
batch->setModelTransform(transform);
|
||||
|
||||
|
@ -108,7 +101,6 @@ void Cube3DOverlay::render(RenderArgs* args) {
|
|||
geometryCache->renderDashedLine(*batch, bottomRightFar, topRightFar, cubeColor, _geometryIds[11]);
|
||||
|
||||
} else {
|
||||
transform.setScale(dimensions);
|
||||
batch->setModelTransform(transform);
|
||||
geometryCache->renderWireCubeInstance(args, *batch, cubeColor, shapePipeline);
|
||||
}
|
||||
|
@ -149,3 +141,16 @@ QVariant Cube3DOverlay::getProperty(const QString& property) {
|
|||
|
||||
return Volume3DOverlay::getProperty(property);
|
||||
}
|
||||
|
||||
Transform Cube3DOverlay::evalRenderTransform() {
|
||||
// TODO: handle registration point??
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
Transform transform;
|
||||
transform.setScale(dimensions);
|
||||
transform.setTranslation(position);
|
||||
transform.setRotation(rotation);
|
||||
return transform;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,9 @@ public:
|
|||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
private:
|
||||
float _borderSize;
|
||||
// edges on a cube
|
||||
|
|
|
@ -79,10 +79,7 @@ void Grid3DOverlay::render(RenderArgs* args) {
|
|||
position += glm::vec3(cameraPosition.x, 0.0f, cameraPosition.z);
|
||||
}
|
||||
|
||||
// FIXME Start using the _renderTransform instead of calling for Transform from here, do the custom things needed in evalRenderTransform()
|
||||
Transform transform;
|
||||
transform.setRotation(getRotation());
|
||||
transform.setScale(glm::vec3(getDimensions(), 1.0f));
|
||||
Transform transform = getRenderTransform();
|
||||
transform.setTranslation(position);
|
||||
batch->setModelTransform(transform);
|
||||
const float MINOR_GRID_EDGE = 0.0025f;
|
||||
|
@ -146,3 +143,10 @@ void Grid3DOverlay::updateGrid() {
|
|||
_minorGridRowDivisions = getDimensions().x / _minorGridEvery;
|
||||
_minorGridColDivisions = getDimensions().y / _minorGridEvery;
|
||||
}
|
||||
|
||||
Transform Grid3DOverlay::evalRenderTransform() {
|
||||
Transform transform;
|
||||
transform.setRotation(getRotation());
|
||||
transform.setScale(glm::vec3(getDimensions(), 1.0f));
|
||||
return transform;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,9 @@ public:
|
|||
// Grids are UI tools, and may not be intersected (pickable)
|
||||
virtual bool findRayIntersection(const glm::vec3&, const glm::vec3&, float&, BoxFace&, glm::vec3&) override { return false; }
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
private:
|
||||
void updateGrid();
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ void Image3DOverlay::update(float deltatime) {
|
|||
setTransform(transform);
|
||||
}
|
||||
#endif
|
||||
Parent::update(deltatime);
|
||||
}
|
||||
|
||||
void Image3DOverlay::render(RenderArgs* args) {
|
||||
|
@ -116,18 +117,8 @@ void Image3DOverlay::render(RenderArgs* args) {
|
|||
const float MAX_COLOR = 255.0f;
|
||||
xColor color = getColor();
|
||||
float alpha = getAlpha();
|
||||
|
||||
// FIXME Start using the _renderTransform instead of calling for Transform from here, do the custom things needed in evalRenderTransform()
|
||||
Transform transform = getTransform();
|
||||
bool transformChanged = applyTransformTo(transform, true);
|
||||
// If the transform is not modified, setting the transform to
|
||||
// itself will cause drift over time due to floating point errors.
|
||||
if (transformChanged) {
|
||||
setTransform(transform);
|
||||
}
|
||||
transform.postScale(glm::vec3(getDimensions(), 1.0f));
|
||||
|
||||
batch->setModelTransform(transform);
|
||||
batch->setModelTransform(getRenderTransform());
|
||||
batch->setResourceTexture(0, _texture->getGPUTexture());
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(
|
||||
|
@ -249,3 +240,9 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec
|
|||
Image3DOverlay* Image3DOverlay::createClone() const {
|
||||
return new Image3DOverlay(this);
|
||||
}
|
||||
|
||||
Transform Image3DOverlay::evalRenderTransform() {
|
||||
auto transform = Parent::evalRenderTransform();
|
||||
transform.postScale(glm::vec3(getDimensions(), 1.0f));
|
||||
return transform;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
class Image3DOverlay : public Billboard3DOverlay {
|
||||
Q_OBJECT
|
||||
using Parent = Billboard3DOverlay;
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
|
@ -46,6 +47,9 @@ public:
|
|||
|
||||
virtual Image3DOverlay* createClone() const override;
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
private:
|
||||
QString _url;
|
||||
NetworkTexturePointer _texture;
|
||||
|
|
|
@ -132,10 +132,10 @@ void Line3DOverlay::render(RenderArgs* args) {
|
|||
glm::vec4 colorv4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||
auto batch = args->_batch;
|
||||
if (batch) {
|
||||
// FIXME Start using the _renderTransform instead of calling for Transform and start and end from here, do the custom things needed in evalRenderTransform()
|
||||
batch->setModelTransform(Transform());
|
||||
glm::vec3 start = getStart();
|
||||
glm::vec3 end = getEnd();
|
||||
auto& renderTransform = getRenderTransform();
|
||||
glm::vec3 start = renderTransform.getTranslation();
|
||||
glm::vec3 end = renderTransform.transform(vec3(0.0, 0.0, -1.0));
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (getIsDashedLine()) {
|
||||
|
@ -268,3 +268,21 @@ QVariant Line3DOverlay::getProperty(const QString& property) {
|
|||
Line3DOverlay* Line3DOverlay::createClone() const {
|
||||
return new Line3DOverlay(this);
|
||||
}
|
||||
|
||||
Transform Line3DOverlay::evalRenderTransform() {
|
||||
// Capture start and endin the renderTransform:
|
||||
// start is the origin
|
||||
// end is at the tip of the front axis aka -Z
|
||||
Transform transform;
|
||||
transform.setTranslation( getStart());
|
||||
auto endPos = getEnd();
|
||||
|
||||
auto vec = endPos - transform.getTranslation();
|
||||
auto scale = glm::length(vec);
|
||||
auto dir = vec / scale;
|
||||
auto orientation = glm::rotation(glm::vec3(0,0,-1), dir);
|
||||
transform.setRotation(orientation);
|
||||
transform.setScale(scale);
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
class Line3DOverlay : public Base3DOverlay {
|
||||
Q_OBJECT
|
||||
using Parent = Base3DOverlay;
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
|
@ -56,6 +57,9 @@ public:
|
|||
QUuid getEndParentID() const { return _endParentID; }
|
||||
quint16 getEndJointIndex() const { return _endParentJointIndex; }
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
private:
|
||||
QUuid _endParentID;
|
||||
quint16 _endParentJointIndex { INVALID_JOINT_INDEX };
|
||||
|
|
|
@ -46,10 +46,7 @@ void ModelOverlay::update(float deltatime) {
|
|||
if (_updateModel) {
|
||||
_updateModel = false;
|
||||
_model->setSnapModelToCenter(true);
|
||||
Transform transform = getTransform();
|
||||
#ifndef USE_SN_SCALE
|
||||
transform.setScale(1.0f); // disable inherited scale
|
||||
#endif
|
||||
Transform transform = evalRenderTransform();
|
||||
if (_scaleToFit) {
|
||||
_model->setScaleToFit(true, transform.getScale() * getDimensions());
|
||||
} else {
|
||||
|
@ -282,6 +279,12 @@ ModelOverlay* ModelOverlay::createClone() const {
|
|||
return new ModelOverlay(this);
|
||||
}
|
||||
|
||||
Transform ModelOverlay::evalRenderTransform() {
|
||||
Transform transform = getTransform();
|
||||
transform.setScale(1.0f); // disable inherited scale
|
||||
return transform;
|
||||
}
|
||||
|
||||
void ModelOverlay::locationChanged(bool tellPhysics) {
|
||||
Base3DOverlay::locationChanged(tellPhysics);
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ public:
|
|||
float getLoadPriority() const { return _loadPriority; }
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
// helper to extract metadata from our Model's rigged joints
|
||||
template <typename itemType> using mapFunction = std::function<itemType(int jointIndex)>;
|
||||
template <typename vectorType, typename itemType>
|
||||
|
|
|
@ -35,6 +35,11 @@ AABox Planar3DOverlay::getBounds() const {
|
|||
return AABox(extents);
|
||||
}
|
||||
|
||||
void Planar3DOverlay::setDimensions(const glm::vec2& value) {
|
||||
_dimensions = value;
|
||||
notifyRenderTransformChange();
|
||||
}
|
||||
|
||||
void Planar3DOverlay::setProperties(const QVariantMap& properties) {
|
||||
Base3DOverlay::setProperties(properties);
|
||||
|
||||
|
@ -67,7 +72,7 @@ bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve
|
|||
return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), getDimensions(), distance);
|
||||
}
|
||||
|
||||
Transform Planar3DOverlay::evalRenderTransform() const {
|
||||
Transform Planar3DOverlay::evalRenderTransform() {
|
||||
auto transform = getTransform();
|
||||
transform.setScale(1.0f); // ignore inherited scale factor from parents
|
||||
if (glm::length2(getDimensions()) != 1.0f) {
|
||||
|
|
|
@ -24,8 +24,8 @@ public:
|
|||
virtual glm::vec2 getSize() const { return _dimensions; };
|
||||
|
||||
glm::vec2 getDimensions() const { return _dimensions; }
|
||||
void setDimensions(float value) { _dimensions = glm::vec2(value); }
|
||||
void setDimensions(const glm::vec2& value) { _dimensions = value; }
|
||||
void setDimensions(float value) { setDimensions(glm::vec2(value)); }
|
||||
void setDimensions(const glm::vec2& value);
|
||||
|
||||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
|
@ -33,10 +33,10 @@ public:
|
|||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal) override;
|
||||
|
||||
Transform evalRenderTransform() const override;
|
||||
|
||||
protected:
|
||||
glm::vec2 _dimensions;
|
||||
|
||||
Transform evalRenderTransform() override;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -124,3 +124,8 @@ void Rectangle3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
Rectangle3DOverlay* Rectangle3DOverlay::createClone() const {
|
||||
return new Rectangle3DOverlay(this);
|
||||
}
|
||||
|
||||
Transform Rectangle3DOverlay::evalRenderTransform() {
|
||||
return getTransform();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,10 @@ public:
|
|||
void setProperties(const QVariantMap& properties) override;
|
||||
|
||||
virtual Rectangle3DOverlay* createClone() const override;
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
private:
|
||||
int _geometryCacheID;
|
||||
std::array<int, 4> _rectGeometryIds;
|
||||
|
|
|
@ -33,26 +33,15 @@ void Shape3DOverlay::render(RenderArgs* args) {
|
|||
const float MAX_COLOR = 255.0f;
|
||||
glm::vec4 cubeColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||
|
||||
// FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform()
|
||||
// TODO: handle registration point??
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
auto batch = args->_batch;
|
||||
|
||||
if (batch) {
|
||||
Transform transform;
|
||||
transform.setTranslation(position);
|
||||
transform.setRotation(rotation);
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
auto shapePipeline = args->_shapePipeline;
|
||||
if (!shapePipeline) {
|
||||
shapePipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline();
|
||||
}
|
||||
|
||||
transform.setScale(dimensions);
|
||||
batch->setModelTransform(transform);
|
||||
batch->setModelTransform(getRenderTransform());
|
||||
if (_isSolid) {
|
||||
geometryCache->renderSolidShapeInstance(args, *batch, _shape, cubeColor, shapePipeline);
|
||||
} else {
|
||||
|
@ -129,3 +118,16 @@ QVariant Shape3DOverlay::getProperty(const QString& property) {
|
|||
|
||||
return Volume3DOverlay::getProperty(property);
|
||||
}
|
||||
|
||||
Transform Shape3DOverlay::evalRenderTransform() {
|
||||
// TODO: handle registration point??
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
Transform transform;
|
||||
transform.setScale(dimensions);
|
||||
transform.setTranslation(position);
|
||||
transform.setRotation(rotation);
|
||||
return transform;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,9 @@ public:
|
|||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
private:
|
||||
float _borderSize;
|
||||
GeometryCache::Shape _shape { GeometryCache::Hexagon };
|
||||
|
|
|
@ -39,13 +39,7 @@ void Sphere3DOverlay::render(RenderArgs* args) {
|
|||
auto batch = args->_batch;
|
||||
|
||||
if (batch) {
|
||||
// FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform()
|
||||
Transform transform = getTransform();
|
||||
#ifndef USE_SN_SCALE
|
||||
transform.setScale(1.0f); // ignore inherited scale from SpatiallyNestable
|
||||
#endif
|
||||
transform.postScale(getDimensions() * SPHERE_OVERLAY_SCALE);
|
||||
batch->setModelTransform(transform);
|
||||
batch->setModelTransform(getRenderTransform());
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
auto shapePipeline = args->_shapePipeline;
|
||||
|
@ -75,3 +69,11 @@ const render::ShapeKey Sphere3DOverlay::getShapeKey() {
|
|||
Sphere3DOverlay* Sphere3DOverlay::createClone() const {
|
||||
return new Sphere3DOverlay(this);
|
||||
}
|
||||
|
||||
Transform Sphere3DOverlay::evalRenderTransform() {
|
||||
Transform transform = getTransform();
|
||||
transform.setScale(1.0f); // ignore inherited scale from SpatiallyNestable
|
||||
transform.postScale(getDimensions() * SPHERE_OVERLAY_SCALE);
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,9 @@ public:
|
|||
virtual const render::ShapeKey getShapeKey() override;
|
||||
|
||||
virtual Sphere3DOverlay* createClone() const override;
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <RenderDeferredTask.h>
|
||||
#include <TextRenderer3D.h>
|
||||
|
||||
#include <AbstractViewStateInterface.h>
|
||||
|
||||
const int FIXED_FONT_POINT_SIZE = 40;
|
||||
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 80.0f; // this is a ratio determined through experimentation
|
||||
const float LINE_SCALE_RATIO = 1.2f;
|
||||
|
@ -96,10 +98,7 @@ void Text3DOverlay::render(RenderArgs* args) {
|
|||
Q_ASSERT(args->_batch);
|
||||
auto& batch = *args->_batch;
|
||||
|
||||
// FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform()
|
||||
Transform transform = getTransform();
|
||||
applyTransformTo(transform, true);
|
||||
setTransform(transform);
|
||||
auto transform = getRenderTransform();
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
const float MAX_COLOR = 255.0f;
|
||||
|
@ -114,6 +113,7 @@ void Text3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
|
||||
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, quadColor.a < 1.0f, false, false, false, false);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, quadColor, _geometryId);
|
||||
|
||||
// Same font properties as textSize()
|
||||
|
@ -133,13 +133,8 @@ void Text3DOverlay::render(RenderArgs* args) {
|
|||
glm::vec4 textColor = { _color.red / MAX_COLOR, _color.green / MAX_COLOR,
|
||||
_color.blue / MAX_COLOR, getTextAlpha() };
|
||||
|
||||
// FIXME: Factor out textRenderer so that Text3DOverlay overlay parts can be grouped by pipeline
|
||||
// for a gpu performance increase. Currently,
|
||||
// Text renderer sets its own pipeline,
|
||||
_textRenderer->draw(batch, 0, 0, getText(), textColor, glm::vec2(-1.0f), getDrawInFront());
|
||||
// so before we continue, we must reset the pipeline
|
||||
batch.setPipeline(args->_shapePipeline->pipeline);
|
||||
args->_shapePipeline->prepare(batch, args);
|
||||
// FIXME: Factor out textRenderer so that Text3DOverlay overlay parts can be grouped by pipeline for a gpu performance increase.
|
||||
_textRenderer->draw(batch, 0, 0, getText(), textColor, glm::vec2(-1.0f), true);
|
||||
}
|
||||
|
||||
const render::ShapeKey Text3DOverlay::getShapeKey() {
|
||||
|
@ -160,7 +155,18 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
|
||||
auto textAlpha = properties["textAlpha"];
|
||||
if (textAlpha.isValid()) {
|
||||
float prevTextAlpha = getTextAlpha();
|
||||
setTextAlpha(textAlpha.toFloat());
|
||||
// Update our payload key if necessary to handle transparency
|
||||
if ((prevTextAlpha < 1.0f && _textAlpha >= 1.0f) || (prevTextAlpha >= 1.0f && _textAlpha < 1.0f)) {
|
||||
auto itemID = getRenderItemID();
|
||||
if (render::Item::isValidID(itemID)) {
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
transaction.updateItem(itemID);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool valid;
|
||||
|
@ -249,3 +255,8 @@ bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3
|
|||
setTransform(transform);
|
||||
return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face, surfaceNormal);
|
||||
}
|
||||
|
||||
Transform Text3DOverlay::evalRenderTransform() {
|
||||
return Parent::evalRenderTransform();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ class TextRenderer3D;
|
|||
|
||||
class Text3DOverlay : public Billboard3DOverlay {
|
||||
Q_OBJECT
|
||||
using Parent = Billboard3DOverlay;
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
|
@ -43,6 +44,7 @@ public:
|
|||
xColor getBackgroundColor();
|
||||
float getTextAlpha() { return _textAlpha; }
|
||||
float getBackgroundAlpha() { return getAlpha(); }
|
||||
bool isTransparent() override { return Overlay::isTransparent() || _textAlpha < 1.0f; }
|
||||
|
||||
// setters
|
||||
void setText(const QString& text);
|
||||
|
@ -63,6 +65,9 @@ public:
|
|||
|
||||
virtual Text3DOverlay* createClone() const override;
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
private:
|
||||
TextRenderer3D* _textRenderer = nullptr;
|
||||
|
||||
|
|
|
@ -26,6 +26,11 @@ AABox Volume3DOverlay::getBounds() const {
|
|||
return AABox(extents);
|
||||
}
|
||||
|
||||
void Volume3DOverlay::setDimensions(const glm::vec3& value) {
|
||||
_localBoundingBox.setBox(-value / 2.0f, value);
|
||||
notifyRenderTransformChange();
|
||||
}
|
||||
|
||||
void Volume3DOverlay::setProperties(const QVariantMap& properties) {
|
||||
Base3DOverlay::setProperties(properties);
|
||||
|
||||
|
@ -57,9 +62,7 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve
|
|||
// extents is the entity relative, scaled, centered extents of the entity
|
||||
glm::mat4 worldToEntityMatrix;
|
||||
Transform transform = getTransform();
|
||||
#ifndef USE_SN_SCALE
|
||||
transform.setScale(1.0f); // ignore any inherited scale from SpatiallyNestable
|
||||
#endif
|
||||
transform.getInverseMatrix(worldToEntityMatrix);
|
||||
|
||||
glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
|
||||
|
@ -69,3 +72,11 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve
|
|||
// and testing intersection there.
|
||||
return _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face, surfaceNormal);
|
||||
}
|
||||
|
||||
Transform Volume3DOverlay::evalRenderTransform() {
|
||||
Transform transform = getTransform();
|
||||
#ifndef USE_SN_SCALE
|
||||
transform.setScale(1.0f); // ignore any inherited scale from SpatiallyNestable
|
||||
#endif
|
||||
return transform;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
class Volume3DOverlay : public Base3DOverlay {
|
||||
Q_OBJECT
|
||||
using Parent = Base3DOverlay;
|
||||
|
||||
public:
|
||||
Volume3DOverlay() {}
|
||||
|
@ -23,8 +24,8 @@ public:
|
|||
virtual AABox getBounds() const override;
|
||||
|
||||
const glm::vec3& getDimensions() const { return _localBoundingBox.getDimensions(); }
|
||||
void setDimensions(float value) { _localBoundingBox.setBox(glm::vec3(-value / 2.0f), value); }
|
||||
void setDimensions(const glm::vec3& value) { _localBoundingBox.setBox(-value / 2.0f, value); }
|
||||
void setDimensions(float value) { setDimensions(glm::vec3(value)); }
|
||||
void setDimensions(const glm::vec3& value);
|
||||
|
||||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
|
@ -35,6 +36,8 @@ public:
|
|||
protected:
|
||||
// Centered local bounding box
|
||||
AABox _localBoundingBox{ vec3(0.0f), 1.0f };
|
||||
|
||||
Transform evalRenderTransform() override;
|
||||
};
|
||||
|
||||
|
||||
|
|