diff --git a/.gitattributes b/.gitattributes index 406780d20a..4a06c4288a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,6 +10,7 @@ *.json text *.js text *.qml text +*.qrc text *.slf text *.slh text *.slv text diff --git a/.gitignore b/.gitignore index d6227f1f30..8aa82865a4 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,11 @@ ext/ Makefile *.user +# Android Studio +*.iml +local.properties +android/libraries + # Xcode *.xcodeproj *.xcworkspace diff --git a/BUILD_ANDROID.md b/BUILD_ANDROID.md index d69d20ee8a..cc51e58b1d 100644 --- a/BUILD_ANDROID.md +++ b/BUILD_ANDROID.md @@ -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. diff --git a/CMakeLists.txt b/CMakeLists.txt index be513abddb..9d3296a168 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt new file mode 100644 index 0000000000..2d6df925e9 --- /dev/null +++ b/android/app/CMakeLists.txt @@ -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") diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000000..bd1c596bf3 --- /dev/null +++ b/android/app/build.gradle @@ -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') diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000000..b3c0078513 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -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 diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..05547bd5ae --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/cpp/GoogleVRHelpers.h b/android/app/src/main/cpp/GoogleVRHelpers.h new file mode 100644 index 0000000000..10c46b036f --- /dev/null +++ b/android/app/src/main/cpp/GoogleVRHelpers.h @@ -0,0 +1,50 @@ +#include +#include +#include + +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; + } + +} diff --git a/android/app/src/main/cpp/native-lib.cpp b/android/app/src/main/cpp/native-lib.cpp new file mode 100644 index 0000000000..156d43d849 --- /dev/null +++ b/android/app/src/main/cpp/native-lib.cpp @@ -0,0 +1,78 @@ +#include + +#include +#include + +#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(renderer); +} + +static NativeRenderer *fromJni(jlong renderer) { + return reinterpret_cast(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(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" diff --git a/android/app/src/main/cpp/renderer.cpp b/android/app/src/main/cpp/renderer.cpp new file mode 100644 index 0000000000..a877ebd777 --- /dev/null +++ b/android/app/src/main/cpp/renderer.cpp @@ -0,0 +1,636 @@ +#include "renderer.h" + +#include + +#include +#include + +#include "GoogleVRHelpers.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include +#endif + + +template +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 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 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 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 TRIANGLE_VERTS {{ + -0.5f, -0.5f, 0.0f, + 0.5f, -0.5f, 0.0f, + 0.0f, 0.5f, 0.0f + }}; +} + +std::array buildViewports(const std::unique_ptr &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 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::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(); +} diff --git a/android/app/src/main/cpp/renderer.h b/android/app/src/main/cpp/renderer.h new file mode 100644 index 0000000000..df7c51cab4 --- /dev/null +++ b/android/app/src/main/cpp/renderer.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +#define GVR + +#if defined(GVR) +#include +#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 start; +#if defined(GVR) + void InitializeVR(); + void PrepareFramebuffer(); + + std::unique_ptr _gvrapi; + gvr::ViewerType _gvr_viewer_type; + std::unique_ptr _viewportlist; + std::unique_ptr _swapchain; + std::array _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 _modelview_cube; + std::array _modelview_floor; + std::array _modelview_projection_cube; + std::array _modelview_projection_floor; + std::array _light_pos_eye_space; + const glm::vec4 _light_pos_world_space{ 0, 2, 0, 1}; +}; diff --git a/android/app/src/main/java/org/saintandreas/testapp/MainActivity.java b/android/app/src/main/java/org/saintandreas/testapp/MainActivity.java new file mode 100644 index 0000000000..7eea14dce9 --- /dev/null +++ b/android/app/src/main/java/org/saintandreas/testapp/MainActivity.java @@ -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); + } +} diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..cde69bccce Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000..9a078e3e1a Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..c133a0cbd3 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000..efc028a636 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..bfa42f0e7b Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..3af2608a44 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..324e72cdd7 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..9bec2e6231 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..aee44e1384 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..34947cd6bb Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..344907f039 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #ffffff + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..5d6a4c1b99 --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TestApp + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..033324ac58 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000000..77c3dd498c --- /dev/null +++ b/android/build.gradle @@ -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) diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000000..aac7c9b461 --- /dev/null +++ b/android/gradle.properties @@ -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 diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000000..e7b4def49c --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/cmake/android/AndroidManifest.xml.in b/cmake/android/AndroidManifest.xml.in deleted file mode 100755 index aa834f3384..0000000000 --- a/cmake/android/AndroidManifest.xml.in +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${ANDROID_EXTRA_ACTIVITY_XML} - - - - - - ${ANDROID_EXTRA_APPLICATION_XML} - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/cmake/android/QtCreateAPK.cmake b/cmake/android/QtCreateAPK.cmake deleted file mode 100644 index 30ee2f57bd..0000000000 --- a/cmake/android/QtCreateAPK.cmake +++ /dev/null @@ -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() \ No newline at end of file diff --git a/cmake/android/android.toolchain.cmake b/cmake/android/android.toolchain.cmake deleted file mode 100755 index 806cef6b18..0000000000 --- a/cmake/android/android.toolchain.cmake +++ /dev/null @@ -1,1725 +0,0 @@ -# Copyright (c) 2010-2011, Ethan Rublee -# Copyright (c) 2011-2014, Andrey Kamaev -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# ------------------------------------------------------------------------------ -# Android CMake toolchain file, for use with the Android NDK r5-r10d -# Requires cmake 2.6.3 or newer (2.8.9 or newer is recommended). -# See home page: https://github.com/taka-no-me/android-cmake -# -# Usage Linux: -# $ export ANDROID_NDK=/absolute/path/to/the/android-ndk -# $ mkdir build && cd build -# $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake .. -# $ make -j8 -# -# Usage Windows: -# You need native port of make to build your project. -# Android NDK r7 (and newer) already has make.exe on board. -# For older NDK you have to install it separately. -# For example, this one: http://gnuwin32.sourceforge.net/packages/make.htm -# -# $ SET ANDROID_NDK=C:\absolute\path\to\the\android-ndk -# $ mkdir build && cd build -# $ cmake.exe -G"MinGW Makefiles" -# -DCMAKE_TOOLCHAIN_FILE=path\to\the\android.toolchain.cmake -# -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%\prebuilt\windows\bin\make.exe" .. -# $ cmake.exe --build . -# -# -# Options (can be set as cmake parameters: -D=): -# ANDROID_NDK=/opt/android-ndk - path to the NDK root. -# Can be set as environment variable. Can be set only at first cmake run. -# -# ANDROID_ABI=armeabi-v7a - specifies the target Application Binary -# Interface (ABI). This option nearly matches to the APP_ABI variable -# used by ndk-build tool from Android NDK. -# -# Possible targets are: -# "armeabi" - ARMv5TE based CPU with software floating point operations -# "armeabi-v7a" - ARMv7 based devices with hardware FPU instructions -# this ABI target is used by default -# "armeabi-v7a with NEON" - same as armeabi-v7a, but -# sets NEON as floating-point unit -# "armeabi-v7a with VFPV3" - same as armeabi-v7a, but -# sets VFPV3 as floating-point unit (has 32 registers instead of 16) -# "armeabi-v6 with VFP" - tuned for ARMv6 processors having VFP -# "x86" - IA-32 instruction set -# "mips" - MIPS32 instruction set -# -# 64-bit ABIs for NDK r10 and newer: -# "arm64-v8a" - ARMv8 AArch64 instruction set -# "x86_64" - Intel64 instruction set (r1) -# "mips64" - MIPS64 instruction set (r6) -# -# ANDROID_NATIVE_API_LEVEL=android-8 - level of Android API compile for. -# Option is read-only when standalone toolchain is used. -# Note: building for "android-L" requires explicit configuration. -# -# ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.9 - the name of compiler -# toolchain to be used. The list of possible values depends on the NDK -# version. For NDK r10c the possible values are: -# -# * aarch64-linux-android-4.9 -# * aarch64-linux-android-clang3.4 -# * aarch64-linux-android-clang3.5 -# * arm-linux-androideabi-4.6 -# * arm-linux-androideabi-4.8 -# * arm-linux-androideabi-4.9 (default) -# * arm-linux-androideabi-clang3.4 -# * arm-linux-androideabi-clang3.5 -# * mips64el-linux-android-4.9 -# * mips64el-linux-android-clang3.4 -# * mips64el-linux-android-clang3.5 -# * mipsel-linux-android-4.6 -# * mipsel-linux-android-4.8 -# * mipsel-linux-android-4.9 -# * mipsel-linux-android-clang3.4 -# * mipsel-linux-android-clang3.5 -# * x86-4.6 -# * x86-4.8 -# * x86-4.9 -# * x86-clang3.4 -# * x86-clang3.5 -# * x86_64-4.9 -# * x86_64-clang3.4 -# * x86_64-clang3.5 -# -# ANDROID_FORCE_ARM_BUILD=OFF - set ON to generate 32-bit ARM instructions -# instead of Thumb. Is not available for "armeabi-v6 with VFP" -# (is forced to be ON) ABI. -# -# ANDROID_NO_UNDEFINED=ON - set ON to show all undefined symbols as linker -# errors even if they are not used. -# -# ANDROID_SO_UNDEFINED=OFF - set ON to allow undefined symbols in shared -# libraries. Automatically turned for NDK r5x and r6x due to GLESv2 -# problems. -# -# ANDROID_STL=gnustl_static - specify the runtime to use. -# -# Possible values are: -# none -> Do not configure the runtime. -# system -> Use the default minimal system C++ runtime library. -# Implies -fno-rtti -fno-exceptions. -# Is not available for standalone toolchain. -# system_re -> Use the default minimal system C++ runtime library. -# Implies -frtti -fexceptions. -# Is not available for standalone toolchain. -# gabi++_static -> Use the GAbi++ runtime as a static library. -# Implies -frtti -fno-exceptions. -# Available for NDK r7 and newer. -# Is not available for standalone toolchain. -# gabi++_shared -> Use the GAbi++ runtime as a shared library. -# Implies -frtti -fno-exceptions. -# Available for NDK r7 and newer. -# Is not available for standalone toolchain. -# stlport_static -> Use the STLport runtime as a static library. -# Implies -fno-rtti -fno-exceptions for NDK before r7. -# Implies -frtti -fno-exceptions for NDK r7 and newer. -# Is not available for standalone toolchain. -# stlport_shared -> Use the STLport runtime as a shared library. -# Implies -fno-rtti -fno-exceptions for NDK before r7. -# Implies -frtti -fno-exceptions for NDK r7 and newer. -# Is not available for standalone toolchain. -# gnustl_static -> Use the GNU STL as a static library. -# Implies -frtti -fexceptions. -# gnustl_shared -> Use the GNU STL as a shared library. -# Implies -frtti -fno-exceptions. -# Available for NDK r7b and newer. -# Silently degrades to gnustl_static if not available. -# c++_static -> Use the LLVM libc++ runtime as a static library. -# c++_shared -> Use the LLVM libc++ runtime as a shared library. -# -# ANDROID_STL_FORCE_FEATURES=ON - turn rtti and exceptions support based on -# chosen runtime. If disabled, then the user is responsible for settings -# these options. -# -# What?: -# android-cmake toolchain searches for NDK/toolchain in the following order: -# ANDROID_NDK - cmake parameter -# ANDROID_NDK - environment variable -# ANDROID_STANDALONE_TOOLCHAIN - cmake parameter -# ANDROID_STANDALONE_TOOLCHAIN - environment variable -# ANDROID_NDK - default locations -# ANDROID_STANDALONE_TOOLCHAIN - default locations -# -# Make sure to do the following in your scripts: -# SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_cxx_flags}" ) -# SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${my_cxx_flags}" ) -# The flags will be prepopulated with critical flags, so don't loose them. -# Also be aware that toolchain also sets configuration-specific compiler -# flags and linker flags. -# -# ANDROID and BUILD_ANDROID will be set to true, you may test any of these -# variables to make necessary Android-specific configuration changes. -# -# Also ARMEABI or ARMEABI_V7A or X86 or MIPS or ARM64_V8A or X86_64 or MIPS64 -# will be set true, mutually exclusive. NEON option will be set true -# if VFP is set to NEON. -# -# ------------------------------------------------------------------------------ - -cmake_minimum_required( VERSION 2.6.3 ) - -if( DEFINED CMAKE_CROSSCOMPILING ) - # subsequent toolchain loading is not really needed - return() -endif() - -if( CMAKE_TOOLCHAIN_FILE ) - # touch toolchain variable to suppress "unused variable" warning -endif() - -# inherit settings in recursive loads -get_property( _CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE ) -if( _CMAKE_IN_TRY_COMPILE ) - include( "${CMAKE_CURRENT_SOURCE_DIR}/../android.toolchain.config.cmake" OPTIONAL ) -endif() - -# this one is important -if( CMAKE_VERSION VERSION_GREATER "3.0.99" ) - set( CMAKE_SYSTEM_NAME Android ) -else() - set( CMAKE_SYSTEM_NAME Linux ) -endif() - -# this one not so much -set( CMAKE_SYSTEM_VERSION 1 ) - -# rpath makes low sense for Android -set( CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "" ) -set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." ) - -# NDK search paths -set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r10d -r10c -r10b -r10 -r9d -r9c -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) -if( NOT DEFINED ANDROID_NDK_SEARCH_PATHS ) - if( CMAKE_HOST_WIN32 ) - file( TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_NDK_SEARCH_PATHS ) - set( ANDROID_NDK_SEARCH_PATHS "${ANDROID_NDK_SEARCH_PATHS}" "$ENV{SystemDrive}/NVPACK" ) - else() - file( TO_CMAKE_PATH "$ENV{HOME}" ANDROID_NDK_SEARCH_PATHS ) - set( ANDROID_NDK_SEARCH_PATHS /opt "${ANDROID_NDK_SEARCH_PATHS}/NVPACK" ) - endif() -endif() -if( NOT DEFINED ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) - set( ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH /opt/android-toolchain ) -endif() - -# known ABIs -set( ANDROID_SUPPORTED_ABIS_arm "armeabi-v7a;armeabi;armeabi-v7a with NEON;armeabi-v7a with VFPV3;armeabi-v6 with VFP" ) -set( ANDROID_SUPPORTED_ABIS_arm64 "arm64-v8a" ) -set( ANDROID_SUPPORTED_ABIS_x86 "x86" ) -set( ANDROID_SUPPORTED_ABIS_x86_64 "x86_64" ) -set( ANDROID_SUPPORTED_ABIS_mips "mips" ) -set( ANDROID_SUPPORTED_ABIS_mips64 "mips64" ) - -# API level defaults -set( ANDROID_DEFAULT_NDK_API_LEVEL 8 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_arm64 21 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_x86 9 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_x86_64 21 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_mips 9 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_mips64 21 ) - - -macro( __LIST_FILTER listvar regex ) - if( ${listvar} ) - foreach( __val ${${listvar}} ) - if( __val MATCHES "${regex}" ) - list( REMOVE_ITEM ${listvar} "${__val}" ) - endif() - endforeach() - endif() -endmacro() - -macro( __INIT_VARIABLE var_name ) - set( __test_path 0 ) - foreach( __var ${ARGN} ) - if( __var STREQUAL "PATH" ) - set( __test_path 1 ) - break() - endif() - endforeach() - - if( __test_path AND NOT EXISTS "${${var_name}}" ) - unset( ${var_name} CACHE ) - endif() - - if( " ${${var_name}}" STREQUAL " " ) - set( __values 0 ) - foreach( __var ${ARGN} ) - if( __var STREQUAL "VALUES" ) - set( __values 1 ) - elseif( NOT __var STREQUAL "PATH" ) - if( __var MATCHES "^ENV_.*$" ) - string( REPLACE "ENV_" "" __var "${__var}" ) - set( __value "$ENV{${__var}}" ) - elseif( DEFINED ${__var} ) - set( __value "${${__var}}" ) - elseif( __values ) - set( __value "${__var}" ) - else() - set( __value "" ) - endif() - - if( NOT " ${__value}" STREQUAL " " AND (NOT __test_path OR EXISTS "${__value}") ) - set( ${var_name} "${__value}" ) - break() - endif() - endif() - endforeach() - unset( __value ) - unset( __values ) - endif() - - if( __test_path ) - file( TO_CMAKE_PATH "${${var_name}}" ${var_name} ) - endif() - unset( __test_path ) -endmacro() - -macro( __DETECT_NATIVE_API_LEVEL _var _path ) - set( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+([0-9]+)[\t ]*.*$" ) - file( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" ) - if( NOT __apiFileContent ) - message( SEND_ERROR "Could not get Android native API level. Probably you have specified invalid level value, or your copy of NDK/toolchain is broken." ) - endif() - string( REGEX REPLACE "${__ndkApiLevelRegex}" "\\1" ${_var} "${__apiFileContent}" ) - unset( __apiFileContent ) - unset( __ndkApiLevelRegex ) -endmacro() - -macro( __DETECT_TOOLCHAIN_MACHINE_NAME _var _root ) - if( EXISTS "${_root}" ) - file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" ) - __LIST_FILTER( __gccExePath "^[.].*" ) - list( LENGTH __gccExePath __gccExePathsCount ) - if( NOT __gccExePathsCount EQUAL 1 AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "Could not determine machine name for compiler from ${_root}" ) - set( ${_var} "" ) - else() - get_filename_component( __gccExeName "${__gccExePath}" NAME_WE ) - string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" ) - endif() - unset( __gccExePath ) - unset( __gccExePathsCount ) - unset( __gccExeName ) - else() - set( ${_var} "" ) - endif() -endmacro() - - -# fight against cygwin -set( ANDROID_FORBID_SYGWIN TRUE CACHE BOOL "Prevent cmake from working under cygwin and using cygwin tools") -mark_as_advanced( ANDROID_FORBID_SYGWIN ) -if( ANDROID_FORBID_SYGWIN ) - if( CYGWIN ) - message( FATAL_ERROR "Android NDK and android-cmake toolchain are not welcome Cygwin. It is unlikely that this cmake toolchain will work under cygwin. But if you want to try then you can set cmake variable ANDROID_FORBID_SYGWIN to FALSE and rerun cmake." ) - endif() - - if( CMAKE_HOST_WIN32 ) - # remove cygwin from PATH - set( __new_path "$ENV{PATH}") - __LIST_FILTER( __new_path "cygwin" ) - set(ENV{PATH} "${__new_path}") - unset(__new_path) - endif() -endif() - - -# detect current host platform -if( NOT DEFINED ANDROID_NDK_HOST_X64 AND (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64" OR CMAKE_HOST_APPLE) ) - set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" ) - mark_as_advanced( ANDROID_NDK_HOST_X64 ) -endif() - -set( TOOL_OS_SUFFIX "" ) -if( CMAKE_HOST_APPLE ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "darwin-x86_64" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME2 "darwin-x86" ) -elseif( CMAKE_HOST_WIN32 ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "windows-x86_64" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME2 "windows" ) - set( TOOL_OS_SUFFIX ".exe" ) -elseif( CMAKE_HOST_UNIX ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "linux-x86_64" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME2 "linux-x86" ) -else() - message( FATAL_ERROR "Cross-compilation on your platform is not supported by this cmake toolchain" ) -endif() - -if( NOT ANDROID_NDK_HOST_X64 ) - set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) -endif() - -# see if we have path to Android NDK -if( NOT ANDROID_NDK AND NOT ANDROID_STANDALONE_TOOLCHAIN ) - __INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK ) -endif() -if( NOT ANDROID_NDK ) - # see if we have path to Android standalone toolchain - __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN ) - - if( NOT ANDROID_STANDALONE_TOOLCHAIN ) - #try to find Android NDK in one of the the default locations - set( __ndkSearchPaths ) - foreach( __ndkSearchPath ${ANDROID_NDK_SEARCH_PATHS} ) - foreach( suffix ${ANDROID_SUPPORTED_NDK_VERSIONS} ) - list( APPEND __ndkSearchPaths "${__ndkSearchPath}/android-ndk${suffix}" ) - endforeach() - endforeach() - __INIT_VARIABLE( ANDROID_NDK PATH VALUES ${__ndkSearchPaths} ) - unset( __ndkSearchPaths ) - - if( ANDROID_NDK ) - message( STATUS "Using default path for Android NDK: ${ANDROID_NDK}" ) - message( STATUS " If you prefer to use a different location, please define a cmake or environment variable: ANDROID_NDK" ) - else() - #try to find Android standalone toolchain in one of the the default locations - __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) - - if( ANDROID_STANDALONE_TOOLCHAIN ) - message( STATUS "Using default path for standalone toolchain ${ANDROID_STANDALONE_TOOLCHAIN}" ) - message( STATUS " If you prefer to use a different location, please define the variable: ANDROID_STANDALONE_TOOLCHAIN" ) - endif( ANDROID_STANDALONE_TOOLCHAIN ) - endif( ANDROID_NDK ) - endif( NOT ANDROID_STANDALONE_TOOLCHAIN ) -endif( NOT ANDROID_NDK ) - -# remember found paths -if( ANDROID_NDK ) - get_filename_component( ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE ) - set( ANDROID_NDK "${ANDROID_NDK}" CACHE INTERNAL "Path of the Android NDK" FORCE ) - set( BUILD_WITH_ANDROID_NDK True ) - if( EXISTS "${ANDROID_NDK}/RELEASE.TXT" ) - file( STRINGS "${ANDROID_NDK}/RELEASE.TXT" ANDROID_NDK_RELEASE_FULL LIMIT_COUNT 1 REGEX "r[0-9]+[a-z]?" ) - string( REGEX MATCH "r([0-9]+)([a-z]?)" ANDROID_NDK_RELEASE "${ANDROID_NDK_RELEASE_FULL}" ) - else() - set( ANDROID_NDK_RELEASE "r1x" ) - set( ANDROID_NDK_RELEASE_FULL "unreleased" ) - endif() - string( REGEX REPLACE "r([0-9]+)([a-z]?)" "\\1*1000" ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE}" ) - string( FIND " abcdefghijklmnopqastuvwxyz" "${CMAKE_MATCH_2}" __ndkReleaseLetterNum ) - math( EXPR ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE_NUM}+${__ndkReleaseLetterNum}" ) -elseif( ANDROID_STANDALONE_TOOLCHAIN ) - get_filename_component( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" ABSOLUTE ) - # try to detect change - if( CMAKE_AR ) - string( LENGTH "${ANDROID_STANDALONE_TOOLCHAIN}" __length ) - string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidStandaloneToolchainPreviousPath ) - if( NOT __androidStandaloneToolchainPreviousPath STREQUAL ANDROID_STANDALONE_TOOLCHAIN ) - message( FATAL_ERROR "It is not possible to change path to the Android standalone toolchain on subsequent run." ) - endif() - unset( __androidStandaloneToolchainPreviousPath ) - unset( __length ) - endif() - set( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" CACHE INTERNAL "Path of the Android standalone toolchain" FORCE ) - set( BUILD_WITH_STANDALONE_TOOLCHAIN True ) -else() - list(GET ANDROID_NDK_SEARCH_PATHS 0 ANDROID_NDK_SEARCH_PATH) - message( FATAL_ERROR "Could not find neither Android NDK nor Android standalone toolchain. - You should either set an environment variable: - export ANDROID_NDK=~/my-android-ndk - or - export ANDROID_STANDALONE_TOOLCHAIN=~/my-android-toolchain - or put the toolchain or NDK in the default path: - sudo ln -s ~/my-android-ndk ${ANDROID_NDK_SEARCH_PATH}/android-ndk - sudo ln -s ~/my-android-toolchain ${ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH}" ) -endif() - -# android NDK layout -if( BUILD_WITH_ANDROID_NDK ) - if( NOT DEFINED ANDROID_NDK_LAYOUT ) - # try to automatically detect the layout - if( EXISTS "${ANDROID_NDK}/RELEASE.TXT") - set( ANDROID_NDK_LAYOUT "RELEASE" ) - elseif( EXISTS "${ANDROID_NDK}/../../linux-x86/toolchain/" ) - set( ANDROID_NDK_LAYOUT "LINARO" ) - elseif( EXISTS "${ANDROID_NDK}/../../gcc/" ) - set( ANDROID_NDK_LAYOUT "ANDROID" ) - endif() - endif() - set( ANDROID_NDK_LAYOUT "${ANDROID_NDK_LAYOUT}" CACHE STRING "The inner layout of NDK" ) - mark_as_advanced( ANDROID_NDK_LAYOUT ) - if( ANDROID_NDK_LAYOUT STREQUAL "LINARO" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment - set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../${ANDROID_NDK_HOST_SYSTEM_NAME}/toolchain" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) - elseif( ANDROID_NDK_LAYOUT STREQUAL "ANDROID" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment - set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../gcc/${ANDROID_NDK_HOST_SYSTEM_NAME}/arm" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) - else() # ANDROID_NDK_LAYOUT STREQUAL "RELEASE" - set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/toolchains" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME}" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME2}" ) - endif() - get_filename_component( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK_TOOLCHAINS_PATH}" ABSOLUTE ) - - # try to detect change of NDK - if( CMAKE_AR ) - string( LENGTH "${ANDROID_NDK_TOOLCHAINS_PATH}" __length ) - string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidNdkPreviousPath ) - if( NOT __androidNdkPreviousPath STREQUAL ANDROID_NDK_TOOLCHAINS_PATH ) - message( FATAL_ERROR "It is not possible to change the path to the NDK on subsequent CMake run. You must remove all generated files from your build folder first. - " ) - endif() - unset( __androidNdkPreviousPath ) - unset( __length ) - endif() -endif() - - -# get all the details about standalone toolchain -if( BUILD_WITH_STANDALONE_TOOLCHAIN ) - __DETECT_NATIVE_API_LEVEL( ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot/usr/include/android/api-level.h" ) - set( ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) - set( __availableToolchains "standalone" ) - __DETECT_TOOLCHAIN_MACHINE_NAME( __availableToolchainMachines "${ANDROID_STANDALONE_TOOLCHAIN}" ) - if( NOT __availableToolchainMachines ) - message( FATAL_ERROR "Could not determine machine name of your toolchain. Probably your Android standalone toolchain is broken." ) - endif() - if( __availableToolchainMachines MATCHES x86_64 ) - set( __availableToolchainArchs "x86_64" ) - elseif( __availableToolchainMachines MATCHES i686 ) - set( __availableToolchainArchs "x86" ) - elseif( __availableToolchainMachines MATCHES aarch64 ) - set( __availableToolchainArchs "arm64" ) - elseif( __availableToolchainMachines MATCHES arm ) - set( __availableToolchainArchs "arm" ) - elseif( __availableToolchainMachines MATCHES mips64el ) - set( __availableToolchainArchs "mips64" ) - elseif( __availableToolchainMachines MATCHES mipsel ) - set( __availableToolchainArchs "mips" ) - endif() - execute_process( COMMAND "${ANDROID_STANDALONE_TOOLCHAIN}/bin/${__availableToolchainMachines}-gcc${TOOL_OS_SUFFIX}" -dumpversion - OUTPUT_VARIABLE __availableToolchainCompilerVersions OUTPUT_STRIP_TRAILING_WHITESPACE ) - string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9]+)?" __availableToolchainCompilerVersions "${__availableToolchainCompilerVersions}" ) - if( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/bin/clang${TOOL_OS_SUFFIX}" ) - list( APPEND __availableToolchains "standalone-clang" ) - list( APPEND __availableToolchainMachines ${__availableToolchainMachines} ) - list( APPEND __availableToolchainArchs ${__availableToolchainArchs} ) - list( APPEND __availableToolchainCompilerVersions ${__availableToolchainCompilerVersions} ) - endif() -endif() - -macro( __GLOB_NDK_TOOLCHAINS __availableToolchainsVar __availableToolchainsLst __toolchain_subpath ) - foreach( __toolchain ${${__availableToolchainsLst}} ) - if( "${__toolchain}" MATCHES "-clang3[.][0-9]$" AND NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}${__toolchain_subpath}" ) - SET( __toolchainVersionRegex "^TOOLCHAIN_VERSION[\t ]+:=[\t ]+(.*)$" ) - FILE( STRINGS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}/setup.mk" __toolchainVersionStr REGEX "${__toolchainVersionRegex}" ) - if( __toolchainVersionStr ) - string( REGEX REPLACE "${__toolchainVersionRegex}" "\\1" __toolchainVersionStr "${__toolchainVersionStr}" ) - string( REGEX REPLACE "-clang3[.][0-9]$" "-${__toolchainVersionStr}" __gcc_toolchain "${__toolchain}" ) - else() - string( REGEX REPLACE "-clang3[.][0-9]$" "-4.6" __gcc_toolchain "${__toolchain}" ) - endif() - unset( __toolchainVersionStr ) - unset( __toolchainVersionRegex ) - else() - set( __gcc_toolchain "${__toolchain}" ) - endif() - __DETECT_TOOLCHAIN_MACHINE_NAME( __machine "${ANDROID_NDK_TOOLCHAINS_PATH}/${__gcc_toolchain}${__toolchain_subpath}" ) - if( __machine ) - string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9x]+)?$" __version "${__gcc_toolchain}" ) - if( __machine MATCHES x86_64 ) - set( __arch "x86_64" ) - elseif( __machine MATCHES i686 ) - set( __arch "x86" ) - elseif( __machine MATCHES aarch64 ) - set( __arch "arm64" ) - elseif( __machine MATCHES arm ) - set( __arch "arm" ) - elseif( __machine MATCHES mips64el ) - set( __arch "mips64" ) - elseif( __machine MATCHES mipsel ) - set( __arch "mips" ) - else() - set( __arch "" ) - endif() - #message("machine: !${__machine}!\narch: !${__arch}!\nversion: !${__version}!\ntoolchain: !${__toolchain}!\n") - if (__arch) - list( APPEND __availableToolchainMachines "${__machine}" ) - list( APPEND __availableToolchainArchs "${__arch}" ) - list( APPEND __availableToolchainCompilerVersions "${__version}" ) - list( APPEND ${__availableToolchainsVar} "${__toolchain}" ) - endif() - endif() - unset( __gcc_toolchain ) - endforeach() -endmacro() - -# get all the details about NDK -if( BUILD_WITH_ANDROID_NDK ) - file( GLOB ANDROID_SUPPORTED_NATIVE_API_LEVELS RELATIVE "${ANDROID_NDK}/platforms" "${ANDROID_NDK}/platforms/android-*" ) - string( REPLACE "android-" "" ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_SUPPORTED_NATIVE_API_LEVELS}" ) - set( __availableToolchains "" ) - set( __availableToolchainMachines "" ) - set( __availableToolchainArchs "" ) - set( __availableToolchainCompilerVersions "" ) - if( ANDROID_TOOLCHAIN_NAME AND EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_TOOLCHAIN_NAME}/" ) - # do not go through all toolchains if we know the name - set( __availableToolchainsLst "${ANDROID_TOOLCHAIN_NAME}" ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) - if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) - if( __availableToolchains ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) - endif() - endif() - endif() - if( NOT __availableToolchains ) - file( GLOB __availableToolchainsLst RELATIVE "${ANDROID_NDK_TOOLCHAINS_PATH}" "${ANDROID_NDK_TOOLCHAINS_PATH}/*" ) - if( __availableToolchainsLst ) - list(SORT __availableToolchainsLst) # we need clang to go after gcc - endif() - __LIST_FILTER( __availableToolchainsLst "^[.]" ) - __LIST_FILTER( __availableToolchainsLst "llvm" ) - __LIST_FILTER( __availableToolchainsLst "renderscript" ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) - if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) - if( __availableToolchains ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) - endif() - endif() - endif() - if( NOT __availableToolchains ) - message( FATAL_ERROR "Could not find any working toolchain in the NDK. Probably your Android NDK is broken." ) - endif() -endif() - -# build list of available ABIs -set( ANDROID_SUPPORTED_ABIS "" ) -set( __uniqToolchainArchNames ${__availableToolchainArchs} ) -list( REMOVE_DUPLICATES __uniqToolchainArchNames ) -list( SORT __uniqToolchainArchNames ) -foreach( __arch ${__uniqToolchainArchNames} ) - list( APPEND ANDROID_SUPPORTED_ABIS ${ANDROID_SUPPORTED_ABIS_${__arch}} ) -endforeach() -unset( __uniqToolchainArchNames ) -if( NOT ANDROID_SUPPORTED_ABIS ) - message( FATAL_ERROR "No one of known Android ABIs is supported by this cmake toolchain." ) -endif() - -# choose target ABI -__INIT_VARIABLE( ANDROID_ABI VALUES ${ANDROID_SUPPORTED_ABIS} ) -# verify that target ABI is supported -list( FIND ANDROID_SUPPORTED_ABIS "${ANDROID_ABI}" __androidAbiIdx ) -if( __androidAbiIdx EQUAL -1 ) - string( REPLACE ";" "\", \"" PRINTABLE_ANDROID_SUPPORTED_ABIS "${ANDROID_SUPPORTED_ABIS}" ) - message( FATAL_ERROR "Specified ANDROID_ABI = \"${ANDROID_ABI}\" is not supported by this cmake toolchain or your NDK/toolchain. - Supported values are: \"${PRINTABLE_ANDROID_SUPPORTED_ABIS}\" - " ) -endif() -unset( __androidAbiIdx ) - -# set target ABI options -if( ANDROID_ABI STREQUAL "x86" ) - set( X86 true ) - set( ANDROID_NDK_ABI_NAME "x86" ) - set( ANDROID_ARCH_NAME "x86" ) - set( ANDROID_LLVM_TRIPLE "i686-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "i686" ) -elseif( ANDROID_ABI STREQUAL "x86_64" ) - set( X86 true ) - set( X86_64 true ) - set( ANDROID_NDK_ABI_NAME "x86_64" ) - set( ANDROID_ARCH_NAME "x86_64" ) - set( CMAKE_SYSTEM_PROCESSOR "x86_64" ) - set( ANDROID_LLVM_TRIPLE "x86_64-none-linux-android" ) -elseif( ANDROID_ABI STREQUAL "mips64" ) - set( MIPS64 true ) - set( ANDROID_NDK_ABI_NAME "mips64" ) - set( ANDROID_ARCH_NAME "mips64" ) - set( ANDROID_LLVM_TRIPLE "mips64el-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "mips64" ) -elseif( ANDROID_ABI STREQUAL "mips" ) - set( MIPS true ) - set( ANDROID_NDK_ABI_NAME "mips" ) - set( ANDROID_ARCH_NAME "mips" ) - set( ANDROID_LLVM_TRIPLE "mipsel-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "mips" ) -elseif( ANDROID_ABI STREQUAL "arm64-v8a" ) - set( ARM64_V8A true ) - set( ANDROID_NDK_ABI_NAME "arm64-v8a" ) - set( ANDROID_ARCH_NAME "arm64" ) - set( ANDROID_LLVM_TRIPLE "aarch64-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "aarch64" ) - set( VFPV3 true ) - set( NEON true ) -elseif( ANDROID_ABI STREQUAL "armeabi" ) - set( ARMEABI true ) - set( ANDROID_NDK_ABI_NAME "armeabi" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv5te" ) -elseif( ANDROID_ABI STREQUAL "armeabi-v6 with VFP" ) - set( ARMEABI_V6 true ) - set( ANDROID_NDK_ABI_NAME "armeabi" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv6" ) - # need always fallback to older platform - set( ARMEABI true ) -elseif( ANDROID_ABI STREQUAL "armeabi-v7a") - set( ARMEABI_V7A true ) - set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) -elseif( ANDROID_ABI STREQUAL "armeabi-v7a with VFPV3" ) - set( ARMEABI_V7A true ) - set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) - set( VFPV3 true ) -elseif( ANDROID_ABI STREQUAL "armeabi-v7a with NEON" ) - set( ARMEABI_V7A true ) - set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) - set( VFPV3 true ) - set( NEON true ) -else() - message( SEND_ERROR "Unknown ANDROID_ABI=\"${ANDROID_ABI}\" is specified." ) -endif() - -if( CMAKE_BINARY_DIR AND EXISTS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" ) - # really dirty hack - # it is not possible to change CMAKE_SYSTEM_PROCESSOR after the first run... - file( APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" "SET(CMAKE_SYSTEM_PROCESSOR \"${CMAKE_SYSTEM_PROCESSOR}\")\n" ) -endif() - -if( ANDROID_ARCH_NAME STREQUAL "arm" AND NOT ARMEABI_V6 ) - __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD VALUES OFF ) - set( ANDROID_FORCE_ARM_BUILD ${ANDROID_FORCE_ARM_BUILD} CACHE BOOL "Use 32-bit ARM instructions instead of Thumb-1" FORCE ) - mark_as_advanced( ANDROID_FORCE_ARM_BUILD ) -else() - unset( ANDROID_FORCE_ARM_BUILD CACHE ) -endif() - -# choose toolchain -if( ANDROID_TOOLCHAIN_NAME ) - list( FIND __availableToolchains "${ANDROID_TOOLCHAIN_NAME}" __toolchainIdx ) - if( __toolchainIdx EQUAL -1 ) - list( SORT __availableToolchains ) - string( REPLACE ";" "\n * " toolchains_list "${__availableToolchains}" ) - set( toolchains_list " * ${toolchains_list}") - message( FATAL_ERROR "Specified toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is missing in your NDK or broken. Please verify that your NDK is working or select another compiler toolchain. -To configure the toolchain set CMake variable ANDROID_TOOLCHAIN_NAME to one of the following values:\n${toolchains_list}\n" ) - endif() - list( GET __availableToolchainArchs ${__toolchainIdx} __toolchainArch ) - if( NOT __toolchainArch STREQUAL ANDROID_ARCH_NAME ) - message( SEND_ERROR "Selected toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is not able to compile binaries for the \"${ANDROID_ARCH_NAME}\" platform." ) - endif() -else() - set( __toolchainIdx -1 ) - set( __applicableToolchains "" ) - set( __toolchainMaxVersion "0.0.0" ) - list( LENGTH __availableToolchains __availableToolchainsCount ) - math( EXPR __availableToolchainsCount "${__availableToolchainsCount}-1" ) - foreach( __idx RANGE ${__availableToolchainsCount} ) - list( GET __availableToolchainArchs ${__idx} __toolchainArch ) - if( __toolchainArch STREQUAL ANDROID_ARCH_NAME ) - list( GET __availableToolchainCompilerVersions ${__idx} __toolchainVersion ) - string( REPLACE "x" "99" __toolchainVersion "${__toolchainVersion}") - if( __toolchainVersion VERSION_GREATER __toolchainMaxVersion ) - set( __toolchainMaxVersion "${__toolchainVersion}" ) - set( __toolchainIdx ${__idx} ) - endif() - endif() - endforeach() - unset( __availableToolchainsCount ) - unset( __toolchainMaxVersion ) - unset( __toolchainVersion ) -endif() -unset( __toolchainArch ) -if( __toolchainIdx EQUAL -1 ) - message( FATAL_ERROR "No one of available compiler toolchains is able to compile for ${ANDROID_ARCH_NAME} platform." ) -endif() -list( GET __availableToolchains ${__toolchainIdx} ANDROID_TOOLCHAIN_NAME ) -list( GET __availableToolchainMachines ${__toolchainIdx} ANDROID_TOOLCHAIN_MACHINE_NAME ) -list( GET __availableToolchainCompilerVersions ${__toolchainIdx} ANDROID_COMPILER_VERSION ) - -unset( __toolchainIdx ) -unset( __availableToolchains ) -unset( __availableToolchainMachines ) -unset( __availableToolchainArchs ) -unset( __availableToolchainCompilerVersions ) - -# choose native API level -__INIT_VARIABLE( ANDROID_NATIVE_API_LEVEL ENV_ANDROID_NATIVE_API_LEVEL ANDROID_API_LEVEL ENV_ANDROID_API_LEVEL ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME} ANDROID_DEFAULT_NDK_API_LEVEL ) -string( REPLACE "android-" "" ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" ) -string( STRIP "${ANDROID_NATIVE_API_LEVEL}" ANDROID_NATIVE_API_LEVEL ) -# adjust API level -set( __real_api_level ${ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME}} ) -foreach( __level ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) - if( (__level LESS ANDROID_NATIVE_API_LEVEL OR __level STREQUAL ANDROID_NATIVE_API_LEVEL) AND NOT __level LESS __real_api_level ) - set( __real_api_level ${__level} ) - endif() -endforeach() -if( __real_api_level AND NOT ANDROID_NATIVE_API_LEVEL STREQUAL __real_api_level ) - message( STATUS "Adjusting Android API level 'android-${ANDROID_NATIVE_API_LEVEL}' to 'android-${__real_api_level}'") - set( ANDROID_NATIVE_API_LEVEL ${__real_api_level} ) -endif() -unset(__real_api_level) -# validate -list( FIND ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_NATIVE_API_LEVEL}" __levelIdx ) -if( __levelIdx EQUAL -1 ) - message( SEND_ERROR "Specified Android native API level 'android-${ANDROID_NATIVE_API_LEVEL}' is not supported by your NDK/toolchain." ) -else() - if( BUILD_WITH_ANDROID_NDK ) - __DETECT_NATIVE_API_LEVEL( __realApiLevel "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include/android/api-level.h" ) - if( NOT __realApiLevel EQUAL ANDROID_NATIVE_API_LEVEL AND NOT __realApiLevel GREATER 9000 ) - message( SEND_ERROR "Specified Android API level (${ANDROID_NATIVE_API_LEVEL}) does not match to the level found (${__realApiLevel}). Probably your copy of NDK is broken." ) - endif() - unset( __realApiLevel ) - endif() - set( ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android API level for native code" FORCE ) - set( CMAKE_ANDROID_API ${ANDROID_NATIVE_API_LEVEL} ) - if( CMAKE_VERSION VERSION_GREATER "2.8" ) - list( SORT ANDROID_SUPPORTED_NATIVE_API_LEVELS ) - set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) - endif() -endif() -unset( __levelIdx ) - - -# remember target ABI -set( ANDROID_ABI "${ANDROID_ABI}" CACHE STRING "The target ABI for Android. If arm, then armeabi-v7a is recommended for hardware floating point." FORCE ) -if( CMAKE_VERSION VERSION_GREATER "2.8" ) - list( SORT ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME} ) - set_property( CACHE ANDROID_ABI PROPERTY STRINGS ${ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME}} ) -endif() - - -# runtime choice (STL, rtti, exceptions) -if( NOT ANDROID_STL ) - set( ANDROID_STL gnustl_static ) -endif() -set( ANDROID_STL "${ANDROID_STL}" CACHE STRING "C++ runtime" ) -set( ANDROID_STL_FORCE_FEATURES ON CACHE BOOL "automatically configure rtti and exceptions support based on C++ runtime" ) -mark_as_advanced( ANDROID_STL ANDROID_STL_FORCE_FEATURES ) - -if( BUILD_WITH_ANDROID_NDK ) - if( NOT "${ANDROID_STL}" MATCHES "^(none|system|system_re|gabi\\+\\+_static|gabi\\+\\+_shared|stlport_static|stlport_shared|gnustl_static|gnustl_shared|c\\+\\+_static|c\\+\\+_shared)$") - message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". -The possible values are: - none -> Do not configure the runtime. - system -> Use the default minimal system C++ runtime library. - system_re -> Same as system but with rtti and exceptions. - gabi++_static -> Use the GAbi++ runtime as a static library. - gabi++_shared -> Use the GAbi++ runtime as a shared library. - stlport_static -> Use the STLport runtime as a static library. - stlport_shared -> Use the STLport runtime as a shared library. - gnustl_static -> (default) Use the GNU STL as a static library. - gnustl_shared -> Use the GNU STL as a shared library. - c++_static -> Use the LLVM libc++ runtime as a static library. - c++_shared -> Use the LLVM libc++ runtime as a shared library. -" ) - endif() -elseif( BUILD_WITH_STANDALONE_TOOLCHAIN ) - if( NOT "${ANDROID_STL}" MATCHES "^(none|gnustl_static|gnustl_shared)$") - message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". -The possible values are: - none -> Do not configure the runtime. - gnustl_static -> (default) Use the GNU STL as a static library. - gnustl_shared -> Use the GNU STL as a shared library. -" ) - endif() -endif() - -unset( ANDROID_RTTI ) -unset( ANDROID_EXCEPTIONS ) -unset( ANDROID_STL_INCLUDE_DIRS ) -unset( __libstl ) -unset( __libsupcxx ) - -if( NOT _CMAKE_IN_TRY_COMPILE AND ANDROID_NDK_RELEASE STREQUAL "r7b" AND ARMEABI_V7A AND NOT VFPV3 AND ANDROID_STL MATCHES "gnustl" ) - message( WARNING "The GNU STL armeabi-v7a binaries from NDK r7b can crash non-NEON devices. The files provided with NDK r7b were not configured properly, resulting in crashes on Tegra2-based devices and others when trying to use certain floating-point functions (e.g., cosf, sinf, expf). -You are strongly recommended to switch to another NDK release. -" ) -endif() - -if( NOT _CMAKE_IN_TRY_COMPILE AND X86 AND ANDROID_STL MATCHES "gnustl" AND ANDROID_NDK_RELEASE STREQUAL "r6" ) - message( WARNING "The x86 system header file from NDK r6 has incorrect definition for ptrdiff_t. You are recommended to upgrade to a newer NDK release or manually patch the header: -See https://android.googlesource.com/platform/development.git f907f4f9d4e56ccc8093df6fee54454b8bcab6c2 - diff --git a/ndk/platforms/android-9/arch-x86/include/machine/_types.h b/ndk/platforms/android-9/arch-x86/include/machine/_types.h - index 5e28c64..65892a1 100644 - --- a/ndk/platforms/android-9/arch-x86/include/machine/_types.h - +++ b/ndk/platforms/android-9/arch-x86/include/machine/_types.h - @@ -51,7 +51,11 @@ typedef long int ssize_t; - #endif - #ifndef _PTRDIFF_T - #define _PTRDIFF_T - -typedef long ptrdiff_t; - +# ifdef __ANDROID__ - + typedef int ptrdiff_t; - +# else - + typedef long ptrdiff_t; - +# endif - #endif -" ) -endif() - - -# setup paths and STL for standalone toolchain -if( BUILD_WITH_STANDALONE_TOOLCHAIN ) - set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) - set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) - set( ANDROID_SYSROOT "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot" ) - - if( NOT ANDROID_STL STREQUAL "none" ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/include/c++/${ANDROID_COMPILER_VERSION}" ) - if( NOT EXISTS "${ANDROID_STL_INCLUDE_DIRS}" ) - # old location ( pre r8c ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}" ) - endif() - if( ARMEABI_V7A AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/bits" ) - list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}" ) - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb/bits" ) - list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb" ) - else() - list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" ) - endif() - # always search static GNU STL to get the location of libsupc++.a - if( ARMEABI_V7A AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb" ) - elseif( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}" ) - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb" ) - elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib" ) - endif() - if( __libstl ) - set( __libsupcxx "${__libstl}/libsupc++.a" ) - set( __libstl "${__libstl}/libstdc++.a" ) - endif() - if( NOT EXISTS "${__libsupcxx}" ) - message( FATAL_ERROR "The required libstdsupc++.a is missing in your standalone toolchain. - Usually it happens because of bug in make-standalone-toolchain.sh script from NDK r7, r7b and r7c. - You need to either upgrade to newer NDK or manually copy - $ANDROID_NDK/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a - to - ${__libsupcxx} - " ) - endif() - if( ANDROID_STL STREQUAL "gnustl_shared" ) - if( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) - elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) - endif() - endif() - endif() -endif() - -# clang -if( "${ANDROID_TOOLCHAIN_NAME}" STREQUAL "standalone-clang" ) - set( ANDROID_COMPILER_IS_CLANG 1 ) - execute_process( COMMAND "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/clang${TOOL_OS_SUFFIX}" --version OUTPUT_VARIABLE ANDROID_CLANG_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) - string( REGEX MATCH "[0-9]+[.][0-9]+" ANDROID_CLANG_VERSION "${ANDROID_CLANG_VERSION}") -elseif( "${ANDROID_TOOLCHAIN_NAME}" MATCHES "-clang3[.][0-9]?$" ) - string( REGEX MATCH "3[.][0-9]$" ANDROID_CLANG_VERSION "${ANDROID_TOOLCHAIN_NAME}") - string( REGEX REPLACE "-clang${ANDROID_CLANG_VERSION}$" "-${ANDROID_COMPILER_VERSION}" ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) - if( NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}/bin/clang${TOOL_OS_SUFFIX}" ) - message( FATAL_ERROR "Could not find the Clang compiler driver" ) - endif() - set( ANDROID_COMPILER_IS_CLANG 1 ) - set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) -else() - set( ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) - unset( ANDROID_COMPILER_IS_CLANG CACHE ) -endif() - -string( REPLACE "." "" _clang_name "clang${ANDROID_CLANG_VERSION}" ) -if( NOT EXISTS "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" ) - set( _clang_name "clang" ) -endif() - - -# setup paths and STL for NDK -if( BUILD_WITH_ANDROID_NDK ) - set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) - set( ANDROID_SYSROOT "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}" ) - - if( ANDROID_STL STREQUAL "none" ) - # do nothing - elseif( ANDROID_STL STREQUAL "system" ) - set( ANDROID_RTTI OFF ) - set( ANDROID_EXCEPTIONS OFF ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) - elseif( ANDROID_STL STREQUAL "system_re" ) - set( ANDROID_RTTI ON ) - set( ANDROID_EXCEPTIONS ON ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) - elseif( ANDROID_STL MATCHES "gabi" ) - if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 - message( FATAL_ERROR "gabi++ is not available in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.") - endif() - set( ANDROID_RTTI ON ) - set( ANDROID_EXCEPTIONS OFF ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/gabi++/include" ) - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gabi++/libs/${ANDROID_NDK_ABI_NAME}/libgabi++_static.a" ) - elseif( ANDROID_STL MATCHES "stlport" ) - if( NOT ANDROID_NDK_RELEASE_NUM LESS 8004 ) # before r8d - set( ANDROID_EXCEPTIONS ON ) - else() - set( ANDROID_EXCEPTIONS OFF ) - endif() - if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 - set( ANDROID_RTTI OFF ) - else() - set( ANDROID_RTTI ON ) - endif() - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/stlport/stlport" ) - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/stlport/libs/${ANDROID_NDK_ABI_NAME}/libstlport_static.a" ) - elseif( ANDROID_STL MATCHES "gnustl" ) - set( ANDROID_EXCEPTIONS ON ) - set( ANDROID_RTTI ON ) - if( EXISTS "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) - if( ARMEABI_V7A AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.7" AND ANDROID_NDK_RELEASE STREQUAL "r8d" ) - # gnustl binary for 4.7 compiler is buggy :( - # TODO: look for right fix - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.6" ) - else() - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) - endif() - else() - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++" ) - endif() - set( ANDROID_STL_INCLUDE_DIRS "${__libstl}/include" "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/include" "${__libstl}/include/backward" ) - if( EXISTS "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) - set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) - else() - set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libstdc++.a" ) - endif() - elseif( ANDROID_STL MATCHES "c\\+\\+_shared" OR ANDROID_STL MATCHES "c\\+\\+_static" ) - set( ANDROID_EXCEPTIONS ON ) - set( ANDROID_RTTI ON ) - set( ANDROID_CXX_ROOT "${ANDROID_NDK}/sources/cxx-stl/" ) - set( ANDROID_LLVM_ROOT "${ANDROID_CXX_ROOT}/llvm-libc++" ) - - if( X86 ) - set( ANDROID_ABI_INCLUDE_DIRS "${ANDROID_CXX_ROOT}/gabi++/include" ) - else() - set( ANDROID_ABI_INCLUDE_DIRS "${ANDROID_CXX_ROOT}/llvm-libc++abi/include" ) - endif() - - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_LLVM_ROOT}/libcxx/include" "${ANDROID_ABI_INCLUDE_DIRS}" ) - - # android support sfiles - include_directories ( SYSTEM ${ANDROID_NDK}/sources/android/support/include ) - - if(ANDROID_STL MATCHES "c\\+\\+_shared") - set ( LLVM_LIBRARY_NAME "libc++_shared.so") - else() - set ( LLVM_LIBRARY_NAME "libc++_static.a" ) - endif () - - if( EXISTS "${ANDROID_LLVM_ROOT}/libs/${ANDROID_NDK_ABI_NAME}/${LLVM_LIBRARY_NAME}" ) - set( __libstl "${ANDROID_LLVM_ROOT}/libs/${ANDROID_NDK_ABI_NAME}/${LLVM_LIBRARY_NAME}" ) - else() - message( FATAL_ERROR "Could not find libc++ library" ) - endif() - else() - message( FATAL_ERROR "Unknown runtime: ${ANDROID_STL}" ) - endif() - # find libsupc++.a - rtti & exceptions - if( ANDROID_STL STREQUAL "system_re" OR ANDROID_STL MATCHES "gnustl" ) - set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r8b or newer - if( NOT EXISTS "${__libsupcxx}" ) - set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r7-r8 - endif() - if( NOT EXISTS "${__libsupcxx}" ) # before r7 - if( ARMEABI_V7A ) - if( ANDROID_FORCE_ARM_BUILD ) - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libsupc++.a" ) - else() - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libsupc++.a" ) - endif() - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD ) - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libsupc++.a" ) - else() - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" ) - endif() - endif() - if( NOT EXISTS "${__libsupcxx}") - message( ERROR "Could not find libsupc++.a for a chosen platform. Either your NDK is not supported or is broken.") - endif() - endif() -endif() - - -# case of shared STL linkage -if( ANDROID_STL MATCHES "shared" AND DEFINED __libstl ) - string( REPLACE "_static.a" "_shared.so" __libstl "${__libstl}" ) - # TODO: check if .so file exists before the renaming -endif() - - -# ccache support -__INIT_VARIABLE( _ndk_ccache NDK_CCACHE ENV_NDK_CCACHE ) -if( _ndk_ccache ) - if( DEFINED NDK_CCACHE AND NOT EXISTS NDK_CCACHE ) - unset( NDK_CCACHE CACHE ) - endif() - find_program( NDK_CCACHE "${_ndk_ccache}" DOC "The path to ccache binary") -else() - unset( NDK_CCACHE CACHE ) -endif() -unset( _ndk_ccache ) - - -# setup the cross-compiler -if( NOT CMAKE_C_COMPILER ) - if( NDK_CCACHE AND NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) - set( CMAKE_C_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C compiler" ) - set( CMAKE_CXX_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C++ compiler" ) - if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_C_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") - set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") - else() - set( CMAKE_C_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") - set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") - endif() - else() - if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_C_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") - set( CMAKE_CXX_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") - else() - set( CMAKE_C_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler" ) - set( CMAKE_CXX_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler" ) - endif() - endif() - set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "assembler" ) - set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip" ) - if( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" ) - # Use gcc-ar if we have it for better LTO support. - set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) - else() - set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) - endif() - set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker" ) - set( CMAKE_NM "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-nm${TOOL_OS_SUFFIX}" CACHE PATH "nm" ) - set( CMAKE_OBJCOPY "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy" ) - set( CMAKE_OBJDUMP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objdump${TOOL_OS_SUFFIX}" CACHE PATH "objdump" ) - set( CMAKE_RANLIB "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ranlib${TOOL_OS_SUFFIX}" CACHE PATH "ranlib" ) -endif() - -set( _CMAKE_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_MACHINE_NAME}-" ) -if( CMAKE_VERSION VERSION_LESS 2.8.5 ) - set( CMAKE_ASM_COMPILER_ARG1 "-c" ) -endif() -if( APPLE ) - find_program( CMAKE_INSTALL_NAME_TOOL NAMES install_name_tool ) - if( NOT CMAKE_INSTALL_NAME_TOOL ) - message( FATAL_ERROR "Could not find install_name_tool, please check your installation." ) - endif() - mark_as_advanced( CMAKE_INSTALL_NAME_TOOL ) -endif() - -# Force set compilers because standard identification works badly for us -include( CMakeForceCompiler ) -CMAKE_FORCE_C_COMPILER( "${CMAKE_C_COMPILER}" GNU ) -if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_C_COMPILER_ID Clang ) -endif() -set( CMAKE_C_PLATFORM_ID Linux ) -if( X86_64 OR MIPS64 OR ARM64_V8A ) - set( CMAKE_C_SIZEOF_DATA_PTR 8 ) -else() - set( CMAKE_C_SIZEOF_DATA_PTR 4 ) -endif() -set( CMAKE_C_HAS_ISYSROOT 1 ) -set( CMAKE_C_COMPILER_ABI ELF ) -CMAKE_FORCE_CXX_COMPILER( "${CMAKE_CXX_COMPILER}" GNU ) -if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_CXX_COMPILER_ID Clang) -endif() -set( CMAKE_CXX_PLATFORM_ID Linux ) -set( CMAKE_CXX_SIZEOF_DATA_PTR ${CMAKE_C_SIZEOF_DATA_PTR} ) -set( CMAKE_CXX_HAS_ISYSROOT 1 ) -set( CMAKE_CXX_COMPILER_ABI ELF ) -set( CMAKE_CXX_SOURCE_FILE_EXTENSIONS cc cp cxx cpp CPP c++ C ) -# force ASM compiler (required for CMake < 2.8.5) -set( CMAKE_ASM_COMPILER_ID_RUN TRUE ) -set( CMAKE_ASM_COMPILER_ID GNU ) -set( CMAKE_ASM_COMPILER_WORKS TRUE ) -set( CMAKE_ASM_COMPILER_FORCED TRUE ) -set( CMAKE_COMPILER_IS_GNUASM 1) -set( CMAKE_ASM_SOURCE_FILE_EXTENSIONS s S asm ) - -foreach( lang C CXX ASM ) - if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_CLANG_VERSION} ) - else() - set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_COMPILER_VERSION} ) - endif() -endforeach() - -# flags and definitions -remove_definitions( -DANDROID ) -add_definitions( -DANDROID ) - -if( ANDROID_SYSROOT MATCHES "[ ;\"]" ) - if( CMAKE_HOST_WIN32 ) - # try to convert path to 8.3 form - file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "@echo %~s1" ) - execute_process( COMMAND "$ENV{ComSpec}" /c "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "${ANDROID_SYSROOT}" - OUTPUT_VARIABLE __path OUTPUT_STRIP_TRAILING_WHITESPACE - RESULT_VARIABLE __result ERROR_QUIET ) - if( __result EQUAL 0 ) - file( TO_CMAKE_PATH "${__path}" ANDROID_SYSROOT ) - set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) - else() - set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" ) - endif() - else() - set( ANDROID_CXX_FLAGS "'--sysroot=${ANDROID_SYSROOT}'" ) - endif() - if( NOT _CMAKE_IN_TRY_COMPILE ) - # quotes can break try_compile and compiler identification - message(WARNING "Path to your Android NDK (or toolchain) has non-alphanumeric symbols.\nThe build might be broken.\n") - endif() -else() - set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) -endif() - -# NDK flags -if (ARM64_V8A ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) - set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" ) - endif() -elseif( ARMEABI OR ARMEABI_V7A) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) - if( NOT ANDROID_FORCE_ARM_BUILD AND NOT ARMEABI_V6 ) - set( ANDROID_CXX_FLAGS_RELEASE "-mthumb -fomit-frame-pointer -fno-strict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -finline-limit=64" ) - endif() - else() - # always compile ARMEABI_V6 in arm mode; otherwise there is no difference from ARMEABI - set( ANDROID_CXX_FLAGS_RELEASE "-marm -fomit-frame-pointer -fstrict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) - endif() - endif() -elseif( X86 OR X86_64 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) - endif() - set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) -elseif( MIPS OR MIPS64 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-strict-aliasing -finline-functions -funwind-tables -fmessage-length=0" ) - set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer" ) - set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" ) - set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" ) - endif() -elseif() - set( ANDROID_CXX_FLAGS_RELEASE "" ) - set( ANDROID_CXX_FLAGS_DEBUG "" ) -endif() - -set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) # good/necessary when porting desktop libraries - -if( NOT X86 AND NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "-Wno-psabi ${ANDROID_CXX_FLAGS}" ) -endif() - -if( NOT ANDROID_COMPILER_VERSION VERSION_LESS "4.6" ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -no-canonical-prefixes" ) # see https://android-review.googlesource.com/#/c/47564/ -endif() - -# ABI-specific flags -if( ARMEABI_V7A ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv7-a -mfloat-abi=softfp" ) - if( NEON ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=neon" ) - elseif( VFPV3 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3" ) - else() - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3-d16" ) - endif() -elseif( ARMEABI_V6 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv6 -mfloat-abi=softfp -mfpu=vfp" ) # vfp == vfpv2 -elseif( ARMEABI ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv5te -mtune=xscale -msoft-float" ) -endif() - -if( ANDROID_STL MATCHES "gnustl" AND (EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}") ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) - set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) - set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) -else() - set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) - set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) - set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) -endif() - -# STL -if( EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}" ) - if( EXISTS "${__libstl}" ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libstl}\"" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libstl}\"" ) - set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libstl}\"" ) - endif() - if( EXISTS "${__libsupcxx}" ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) - set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) - # C objects: - set( CMAKE_C_CREATE_SHARED_LIBRARY " -o " ) - set( CMAKE_C_CREATE_SHARED_MODULE " -o " ) - set( CMAKE_C_LINK_EXECUTABLE " -o " ) - set( CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) - set( CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_C_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) - set( CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) - endif() - if( ANDROID_STL MATCHES "gnustl" ) - if( NOT EXISTS "${ANDROID_LIBM_PATH}" ) - set( ANDROID_LIBM_PATH -lm ) - endif() - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} ${ANDROID_LIBM_PATH}" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} ${ANDROID_LIBM_PATH}" ) - set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} ${ANDROID_LIBM_PATH}" ) - endif() -endif() - -# variables controlling optional build flags -if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 - # libGLESv2.so in NDK's prior to r7 refers to missing external symbols. - # So this flag option is required for all projects using OpenGL from native. - __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES ON ) -else() - __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) -endif() -__INIT_VARIABLE( ANDROID_NO_UNDEFINED VALUES ON ) -__INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON ) -__INIT_VARIABLE( ANDROID_GOLD_LINKER VALUES ON ) -__INIT_VARIABLE( ANDROID_NOEXECSTACK VALUES ON ) -__INIT_VARIABLE( ANDROID_RELRO VALUES ON ) - -set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" ) -set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) -set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Put each function in separate section and enable garbage collection of unused input sections at link time" ) -set( ANDROID_GOLD_LINKER ${ANDROID_GOLD_LINKER} CACHE BOOL "Enables gold linker" ) -set( ANDROID_NOEXECSTACK ${ANDROID_NOEXECSTACK} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) -set( ANDROID_RELRO ${ANDROID_RELRO} CACHE BOOL "Enables RELRO - a memory corruption mitigation technique" ) -mark_as_advanced( ANDROID_NO_UNDEFINED ANDROID_SO_UNDEFINED ANDROID_FUNCTION_LEVEL_LINKING ANDROID_GOLD_LINKER ANDROID_NOEXECSTACK ANDROID_RELRO ) - -# linker flags -set( ANDROID_LINKER_FLAGS "" ) - -if( ARMEABI_V7A ) - # this is *required* to use the following linker flags that routes around - # a CPU bug in some Cortex-A8 implementations: - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--fix-cortex-a8" ) -endif() - -if( ANDROID_NO_UNDEFINED ) - if( MIPS ) - # there is some sysroot-related problem in mips linker... - if( NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined -Wl,-rpath-link,${ANDROID_SYSROOT}/usr/lib" ) - endif() - else() - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined" ) - endif() -endif() - -if( ANDROID_SO_UNDEFINED ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-allow-shlib-undefined" ) -endif() - -if( ANDROID_FUNCTION_LEVEL_LINKING ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--gc-sections" ) -endif() - -if( ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" ) - if( ANDROID_GOLD_LINKER AND (CMAKE_HOST_UNIX OR ANDROID_NDK_RELEASE_NUM GREATER 8002) AND (ARMEABI OR ARMEABI_V7A OR X86) ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold" ) - elseif( ANDROID_NDK_RELEASE_NUM GREATER 8002 ) # after r8b - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=bfd" ) - elseif( ANDROID_NDK_RELEASE STREQUAL "r8b" AND ARMEABI AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "The default bfd linker from arm GCC 4.6 toolchain can fail with 'unresolvable R_ARM_THM_CALL relocation' error message. See https://code.google.com/p/android/issues/detail?id=35342 - On Linux and OS X host platform you can workaround this problem using gold linker (default). - Rerun cmake with -DANDROID_GOLD_LINKER=ON option in case of problems. -" ) - endif() -endif() # version 4.6 - -if( ANDROID_NOEXECSTACK ) - if( ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Xclang -mnoexecstack" ) - else() - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Wa,--noexecstack" ) - endif() - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,noexecstack" ) -endif() - -if( ANDROID_RELRO ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now" ) -endif() - -if( ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "-target ${ANDROID_LLVM_TRIPLE} -Qunused-arguments ${ANDROID_CXX_FLAGS}" ) - if( BUILD_WITH_ANDROID_NDK ) - set( ANDROID_CXX_FLAGS "-gcc-toolchain ${ANDROID_TOOLCHAIN_ROOT} ${ANDROID_CXX_FLAGS}" ) - endif() -endif() - -# cache flags -set( CMAKE_CXX_FLAGS "" CACHE STRING "c++ flags" ) -set( CMAKE_C_FLAGS "" CACHE STRING "c flags" ) -set( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c++ Release flags" ) -set( CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c Release flags" ) -set( CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c++ Debug flags" ) -set( CMAKE_C_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c Debug flags" ) -set( CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "shared linker flags" ) -set( CMAKE_MODULE_LINKER_FLAGS "" CACHE STRING "module linker flags" ) -set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "executable linker flags" ) - -# put flags to cache (for debug purpose only) -set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" CACHE INTERNAL "Android specific c/c++ flags" ) -set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE}" CACHE INTERNAL "Android specific c/c++ Release flags" ) -set( ANDROID_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG}" CACHE INTERNAL "Android specific c/c++ Debug flags" ) -set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}" CACHE INTERNAL "Android specific c/c++ linker flags" ) - -# finish flags -set( CMAKE_CXX_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_CXX_FLAGS}" ) -set( CMAKE_C_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_C_FLAGS}" ) -set( CMAKE_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}" ) -set( CMAKE_C_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}" ) -set( CMAKE_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_DEBUG}" ) -set( CMAKE_C_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_C_FLAGS_DEBUG}" ) -set( CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" ) -set( CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) -set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) - -if( MIPS AND BUILD_WITH_ANDROID_NDK AND ANDROID_NDK_RELEASE STREQUAL "r8" ) - set( CMAKE_SHARED_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_SHARED_LINKER_FLAGS}" ) - set( CMAKE_MODULE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_MODULE_LINKER_FLAGS}" ) - set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.x ${CMAKE_EXE_LINKER_FLAGS}" ) -endif() - -# pie/pic -if( NOT (ANDROID_NATIVE_API_LEVEL LESS 16) AND (NOT DEFINED ANDROID_APP_PIE OR ANDROID_APP_PIE) AND (CMAKE_VERSION VERSION_GREATER 2.8.8) ) - set( CMAKE_POSITION_INDEPENDENT_CODE TRUE ) - set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie") -else() - set( CMAKE_POSITION_INDEPENDENT_CODE FALSE ) - set( CMAKE_CXX_FLAGS "-fpic ${CMAKE_CXX_FLAGS}" ) - set( CMAKE_C_FLAGS "-fpic ${CMAKE_C_FLAGS}" ) -endif() - -# configure rtti -if( DEFINED ANDROID_RTTI AND ANDROID_STL_FORCE_FEATURES ) - if( ANDROID_RTTI ) - set( CMAKE_CXX_FLAGS "-frtti ${CMAKE_CXX_FLAGS}" ) - else() - set( CMAKE_CXX_FLAGS "-fno-rtti ${CMAKE_CXX_FLAGS}" ) - endif() -endif() - -# configure exceptios -if( DEFINED ANDROID_EXCEPTIONS AND ANDROID_STL_FORCE_FEATURES ) - if( ANDROID_EXCEPTIONS ) - set( CMAKE_CXX_FLAGS "-fexceptions ${CMAKE_CXX_FLAGS}" ) - set( CMAKE_C_FLAGS "-fexceptions ${CMAKE_C_FLAGS}" ) - else() - set( CMAKE_CXX_FLAGS "-fno-exceptions ${CMAKE_CXX_FLAGS}" ) - set( CMAKE_C_FLAGS "-fno-exceptions ${CMAKE_C_FLAGS}" ) - endif() -endif() - -# global includes and link directories -include_directories( SYSTEM "${ANDROID_SYSROOT}/usr/include" ${ANDROID_STL_INCLUDE_DIRS} ) -get_filename_component(__android_install_path "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ABSOLUTE) # avoid CMP0015 policy warning -link_directories( "${__android_install_path}" ) - -# detect if need link crtbegin_so.o explicitly -if( NOT DEFINED ANDROID_EXPLICIT_CRT_LINK ) - set( __cmd "${CMAKE_CXX_CREATE_SHARED_LIBRARY}" ) - string( REPLACE "" "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_CXX_FLAGS}" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_SHARED_LINKER_FLAGS}" __cmd "${__cmd}" ) - string( REPLACE "" "-shared" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain_crtlink_test.so" __cmd "${__cmd}" ) - string( REPLACE "" "\"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - separate_arguments( __cmd ) - foreach( __var ANDROID_NDK ANDROID_NDK_TOOLCHAINS_PATH ANDROID_STANDALONE_TOOLCHAIN ) - if( ${__var} ) - set( __tmp "${${__var}}" ) - separate_arguments( __tmp ) - string( REPLACE "${__tmp}" "${${__var}}" __cmd "${__cmd}") - endif() - endforeach() - string( REPLACE "'" "" __cmd "${__cmd}" ) - string( REPLACE "\"" "" __cmd "${__cmd}" ) - execute_process( COMMAND ${__cmd} RESULT_VARIABLE __cmd_result OUTPUT_QUIET ERROR_QUIET ) - if( __cmd_result EQUAL 0 ) - set( ANDROID_EXPLICIT_CRT_LINK ON ) - else() - set( ANDROID_EXPLICIT_CRT_LINK OFF ) - endif() -endif() - -if( ANDROID_EXPLICIT_CRT_LINK ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) -endif() - -# setup output directories -set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" ) - -if( DEFINED LIBRARY_OUTPUT_PATH_ROOT - OR EXISTS "${CMAKE_SOURCE_DIR}/AndroidManifest.xml" - OR (EXISTS "${CMAKE_SOURCE_DIR}/../AndroidManifest.xml" AND EXISTS "${CMAKE_SOURCE_DIR}/../jni/") ) - set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "Root for binaries output, set this to change where Android libs are installed to" ) - if( NOT _CMAKE_IN_TRY_COMPILE ) - if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" ) - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" ) - else() - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" ) - endif() - set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for Android libs" ) - endif() -endif() - -# copy shaed stl library to build directory -if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" AND DEFINED LIBRARY_OUTPUT_PATH ) - get_filename_component( __libstlname "${__libstl}" NAME ) - execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) - if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") - message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) - endif() - unset( __fileCopyProcess ) - unset( __libstlname ) -endif() - - -# set these global flags for cmake client scripts to change behavior -set( ANDROID True ) -set( BUILD_ANDROID True ) - -# where is the target environment -set( CMAKE_FIND_ROOT_PATH "${ANDROID_TOOLCHAIN_ROOT}/bin" "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" "${ANDROID_SYSROOT}" "${CMAKE_INSTALL_PREFIX}" "${CMAKE_INSTALL_PREFIX}/share" ) - -# only search for libraries and includes in the ndk toolchain -set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) -set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) -set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) - - -# macro to find packages on the host OS -macro( find_host_package ) - set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) - set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER ) - set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER ) - if( CMAKE_HOST_WIN32 ) - SET( WIN32 1 ) - SET( UNIX ) - elseif( CMAKE_HOST_APPLE ) - SET( APPLE 1 ) - SET( UNIX ) - endif() - find_package( ${ARGN} ) - SET( WIN32 ) - SET( APPLE ) - SET( UNIX 1 ) - set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) - set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) - set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) -endmacro() - - -# macro to find programs on the host OS -macro( find_host_program ) - set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) - set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER ) - set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER ) - if( CMAKE_HOST_WIN32 ) - SET( WIN32 1 ) - SET( UNIX ) - elseif( CMAKE_HOST_APPLE ) - SET( APPLE 1 ) - SET( UNIX ) - endif() - find_program( ${ARGN} ) - SET( WIN32 ) - SET( APPLE ) - SET( UNIX 1 ) - set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) - set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) - set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) -endmacro() - - -# export toolchain settings for the try_compile() command -if( NOT _CMAKE_IN_TRY_COMPILE ) - set( __toolchain_config "") - foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN - ANDROID_NDK_HOST_X64 - ANDROID_NDK - ANDROID_NDK_LAYOUT - ANDROID_STANDALONE_TOOLCHAIN - ANDROID_TOOLCHAIN_NAME - ANDROID_ABI - ANDROID_NATIVE_API_LEVEL - ANDROID_STL - ANDROID_STL_FORCE_FEATURES - ANDROID_FORCE_ARM_BUILD - ANDROID_NO_UNDEFINED - ANDROID_SO_UNDEFINED - ANDROID_FUNCTION_LEVEL_LINKING - ANDROID_GOLD_LINKER - ANDROID_NOEXECSTACK - ANDROID_RELRO - ANDROID_LIBM_PATH - ANDROID_EXPLICIT_CRT_LINK - ANDROID_APP_PIE - ) - if( DEFINED ${__var} ) - if( ${__var} MATCHES " ") - set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" ) - else() - set( __toolchain_config "${__toolchain_config}set( ${__var} ${${__var}} CACHE INTERNAL \"\" )\n" ) - endif() - endif() - endforeach() - file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android.toolchain.config.cmake" "${__toolchain_config}" ) - unset( __toolchain_config ) -endif() - - -# force cmake to produce / instead of \ in build commands for Ninja generator -if( CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_HOST_WIN32 ) - # it is a bad hack after all - # CMake generates Ninja makefiles with UNIX paths only if it thinks that we are going to build with MinGW - set( CMAKE_COMPILER_IS_MINGW TRUE ) # tell CMake that we are MinGW - set( CMAKE_CROSSCOMPILING TRUE ) # stop recursion - enable_language( C ) - enable_language( CXX ) - # unset( CMAKE_COMPILER_IS_MINGW ) # can't unset because CMake does not convert back-slashes in response files without it - unset( MINGW ) -endif() - - -# Variables controlling behavior or set by cmake toolchain: -# ANDROID_ABI : "armeabi-v7a" (default), "armeabi", "armeabi-v7a with NEON", "armeabi-v7a with VFPV3", "armeabi-v6 with VFP", "x86", "mips", "arm64-v8a", "x86_64", "mips64" -# ANDROID_NATIVE_API_LEVEL : 3,4,5,8,9,14,15,16,17,18,19,21 (depends on NDK version) -# ANDROID_STL : gnustl_static/gnustl_shared/stlport_static/stlport_shared/gabi++_static/gabi++_shared/system_re/system/none -# ANDROID_FORBID_SYGWIN : ON/OFF -# ANDROID_NO_UNDEFINED : ON/OFF -# ANDROID_SO_UNDEFINED : OFF/ON (default depends on NDK version) -# ANDROID_FUNCTION_LEVEL_LINKING : ON/OFF -# ANDROID_GOLD_LINKER : ON/OFF -# ANDROID_NOEXECSTACK : ON/OFF -# ANDROID_RELRO : ON/OFF -# ANDROID_FORCE_ARM_BUILD : ON/OFF -# ANDROID_STL_FORCE_FEATURES : ON/OFF -# ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product//obj/lib/libm.so) to workaround unresolved `sincos` -# Can be set only at the first run: -# ANDROID_NDK : path to your NDK install -# NDK_CCACHE : path to your ccache executable -# ANDROID_TOOLCHAIN_NAME : the NDK name of compiler toolchain -# ANDROID_NDK_HOST_X64 : try to use x86_64 toolchain (default for x64 host systems) -# ANDROID_NDK_LAYOUT : the inner NDK structure (RELEASE, LINARO, ANDROID) -# LIBRARY_OUTPUT_PATH_ROOT : -# ANDROID_STANDALONE_TOOLCHAIN -# -# Primary read-only variables: -# ANDROID : always TRUE -# ARMEABI : TRUE for arm v6 and older devices -# ARMEABI_V6 : TRUE for arm v6 -# ARMEABI_V7A : TRUE for arm v7a -# ARM64_V8A : TRUE for arm64-v8a -# NEON : TRUE if NEON unit is enabled -# VFPV3 : TRUE if VFP version 3 is enabled -# X86 : TRUE if configured for x86 -# X86_64 : TRUE if configured for x86_64 -# MIPS : TRUE if configured for mips -# MIPS64 : TRUE if configured for mips64 -# BUILD_WITH_ANDROID_NDK : TRUE if NDK is used -# BUILD_WITH_STANDALONE_TOOLCHAIN : TRUE if standalone toolchain is used -# ANDROID_NDK_HOST_SYSTEM_NAME : "windows", "linux-x86" or "darwin-x86" depending on host platform -# ANDROID_NDK_ABI_NAME : "armeabi", "armeabi-v7a", "x86", "mips", "arm64-v8a", "x86_64", "mips64" depending on ANDROID_ABI -# ANDROID_NDK_RELEASE : from r5 to r10d; set only for NDK -# ANDROID_NDK_RELEASE_NUM : numeric ANDROID_NDK_RELEASE version (1000*major+minor) -# ANDROID_ARCH_NAME : "arm", "x86", "mips", "arm64", "x86_64", "mips64" depending on ANDROID_ABI -# ANDROID_SYSROOT : path to the compiler sysroot -# TOOL_OS_SUFFIX : "" or ".exe" depending on host platform -# ANDROID_COMPILER_IS_CLANG : TRUE if clang compiler is used -# -# Secondary (less stable) read-only variables: -# ANDROID_COMPILER_VERSION : GCC version used (not Clang version) -# ANDROID_CLANG_VERSION : version of clang compiler if clang is used -# ANDROID_CXX_FLAGS : C/C++ compiler flags required by Android platform -# ANDROID_SUPPORTED_ABIS : list of currently allowed values for ANDROID_ABI -# ANDROID_TOOLCHAIN_MACHINE_NAME : "arm-linux-androideabi", "arm-eabi" or "i686-android-linux" -# ANDROID_TOOLCHAIN_ROOT : path to the top level of toolchain (standalone or placed inside NDK) -# ANDROID_CLANG_TOOLCHAIN_ROOT : path to clang tools -# ANDROID_SUPPORTED_NATIVE_API_LEVELS : list of native API levels found inside NDK -# ANDROID_STL_INCLUDE_DIRS : stl include paths -# ANDROID_RTTI : if rtti is enabled by the runtime -# ANDROID_EXCEPTIONS : if exceptions are enabled by the runtime -# ANDROID_GCC_TOOLCHAIN_NAME : read-only, differs from ANDROID_TOOLCHAIN_NAME only if clang is used -# -# Defaults: -# ANDROID_DEFAULT_NDK_API_LEVEL -# ANDROID_DEFAULT_NDK_API_LEVEL_${ARCH} -# ANDROID_NDK_SEARCH_PATHS -# ANDROID_SUPPORTED_ABIS_${ARCH} -# ANDROID_SUPPORTED_NDK_VERSIONS diff --git a/cmake/android/deployment-file.json.in b/cmake/android/deployment-file.json.in deleted file mode 100644 index 81ed8a6ecc..0000000000 --- a/cmake/android/deployment-file.json.in +++ /dev/null @@ -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@" -} diff --git a/cmake/android/strings.xml.in b/cmake/android/strings.xml.in deleted file mode 100644 index 6e6ce7b12e..0000000000 --- a/cmake/android/strings.xml.in +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - ${ANDROID_APP_DISPLAY_NAME} - - Can\'t find Ministro service.\nThe application can\'t start. - This application requires Ministro service. Would you like to install it? - Your application encountered a fatal error and cannot continue. - diff --git a/cmake/externals/glm/CMakeLists.txt b/cmake/externals/glm/CMakeLists.txt index 79a44fa48e..bc8089074f 100644 --- a/cmake/externals/glm/CMakeLists.txt +++ b/cmake/externals/glm/CMakeLists.txt @@ -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= + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= ${EXTERNAL_ARGS} LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 diff --git a/cmake/externals/tbb/CMakeLists.txt b/cmake/externals/tbb/CMakeLists.txt index 71d7b94597..9664fe7250 100644 --- a/cmake/externals/tbb/CMakeLists.txt +++ b/cmake/externals/tbb/CMakeLists.txt @@ -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 () + diff --git a/cmake/init.cmake b/cmake/init.cmake index 75fb3a4b52..9d7b0fd94c 100644 --- a/cmake/init.cmake +++ b/cmake/init.cmake @@ -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 () diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index c43ade45d2..c5b35b7e90 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -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. \ diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 8458d53f68..8faa4e6d96 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -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) diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake index d0fc58af0c..04687e2c84 100644 --- a/cmake/macros/SetupHifiLibrary.cmake +++ b/cmake/macros/SetupHifiLibrary.cmake @@ -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}) diff --git a/cmake/macros/SetupQt.cmake b/cmake/macros/SetupQt.cmake index b2a89f81e5..ece8607b9b 100644 --- a/cmake/macros/SetupQt.cmake +++ b/cmake/macros/SetupQt.cmake @@ -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") diff --git a/cmake/macros/TargetGlew.cmake b/cmake/macros/TargetGlew.cmake index 5f71f021ec..bc4d5cb033 100644 --- a/cmake/macros/TargetGlew.cmake +++ b/cmake/macros/TargetGlew.cmake @@ -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() \ No newline at end of file diff --git a/cmake/macros/TargetOpenGL.cmake b/cmake/macros/TargetOpenGL.cmake index 73c92e651a..6ad92259bb 100644 --- a/cmake/macros/TargetOpenGL.cmake +++ b/cmake/macros/TargetOpenGL.cmake @@ -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() diff --git a/cmake/macros/TargetOpenSSL.cmake b/cmake/macros/TargetOpenSSL.cmake new file mode 100644 index 0000000000..7ee0283a48 --- /dev/null +++ b/cmake/macros/TargetOpenSSL.cmake @@ -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() diff --git a/cmake/macros/TargetTBB.cmake b/cmake/macros/TargetTBB.cmake new file mode 100644 index 0000000000..e9c4639c3d --- /dev/null +++ b/cmake/macros/TargetTBB.cmake @@ -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() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 43e50c6d33..305a6475f6 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -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}) diff --git a/interface/resources/qml/controls-uit/Keyboard.qml b/interface/resources/qml/controls-uit/Keyboard.qml index 4739534fcd..ab361491bd 100644 --- a/interface/resources/qml/controls-uit/Keyboard.qml +++ b/interface/resources/qml/controls-uit/Keyboard.qml @@ -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 diff --git a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml index 71e59e0d01..11d3cab35e 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml @@ -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 } } } diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml index e7eefbc5e7..ce4fac3bd5 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml @@ -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(); } - } diff --git a/interface/resources/qml/hifi/tablet/TabletMenuView.qml b/interface/resources/qml/hifi/tablet/TabletMenuView.qml index 92e7f59524..ecfb653923 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuView.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuView.qml @@ -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(); } - - } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e1c3af1939..480928ccd2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -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(); + 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()->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(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(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(); + }); } 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(answer.toInt()) == QMessageBox::Yes) { + qCDebug(interfaceapp) << "Chose to run the script: " << fileName; + DependencyManager::get()->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()->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(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(); - 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()->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()->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()->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()->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 { diff --git a/interface/src/Application.h b/interface/src/Application.h index 86e4f94917..04441276eb 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -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); diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 73192b0bef..5b9c45eef5 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -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()->getMyAvatar(); - auto myAvatar = DependencyManager::get()->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) { diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp index 0bd6b01128..f48b5e1f5b 100644 --- a/interface/src/Bookmarks.cpp +++ b/interface/src/Bookmarks.cpp @@ -58,20 +58,25 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QVariant& b Menu* menubar = Menu::getInstance(); if (contains(bookmarkName)) { auto offscreenUi = DependencyManager::get(); - 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(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) { diff --git a/interface/src/LocationBookmarks.cpp b/interface/src/LocationBookmarks.cpp index b234f8c07b..285f533a7f 100644 --- a/interface/src/LocationBookmarks.cpp +++ b/interface/src/LocationBookmarks.cpp @@ -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(); - 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(); + 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); } -} \ No newline at end of file +} diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 005d478411..9df22ab08e 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -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); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index d77c87a6fc..854f8d8c2b 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -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 diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 44768db93c..5f4c7526e0 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -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; diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index d41a913c95..ae352974ae 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -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)); diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 667c2587b0..8de40865b7 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -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()->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()->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(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(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(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() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e508c972c6..19179d613d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -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()); } } diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index b696afa8e5..55ddd01123 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -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()->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); diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 23f023cf7a..5467a8233e 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -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()->setPrecisionPicking(_rayPickUID, precisionPicking); } + void setLaserLength(const float laserLength) { _laserLength = laserLength; } void setIgnoreEntities(const QScriptValue& ignoreEntities) { DependencyManager::get()->setIgnoreEntities(_rayPickUID, ignoreEntities); } void setIncludeEntities(const QScriptValue& includeEntities) { DependencyManager::get()->setIncludeEntities(_rayPickUID, includeEntities); } void setIgnoreOverlays(const QScriptValue& ignoreOverlays) { DependencyManager::get()->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); }; diff --git a/interface/src/raypick/LaserPointerManager.cpp b/interface/src/raypick/LaserPointerManager.cpp index 1fd0fe9e88..b19ecc14f0 100644 --- a/interface/src/raypick/LaserPointerManager.cpp +++ b/interface/src/raypick/LaserPointerManager.cpp @@ -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 = std::make_shared(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>(id, laserPointer)); + _laserPointers[id] = laserPointer; + _laserPointerLocks[id] = std::make_shared(); 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> laserPointerToAdd = _laserPointersToAdd.front(); - _laserPointersToAdd.pop(); - _laserPointers[laserPointerToAdd.first] = laserPointerToAdd.second; - _laserPointerLocks[laserPointerToAdd.first] = std::make_shared(); - } - } - - { - 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)) { diff --git a/interface/src/raypick/LaserPointerManager.h b/interface/src/raypick/LaserPointerManager.h index b573410fe7..6494bb7056 100644 --- a/interface/src/raypick/LaserPointerManager.h +++ b/interface/src/raypick/LaserPointerManager.h @@ -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> _laserPointers; QHash> _laserPointerLocks; - QReadWriteLock _addLock; - std::queue>> _laserPointersToAdd; - QReadWriteLock _removeLock; - std::queue _laserPointersToRemove; QReadWriteLock _containsLock; }; diff --git a/interface/src/raypick/LaserPointerScriptingInterface.h b/interface/src/raypick/LaserPointerScriptingInterface.h index d65eb335b3..2f6da87b5f 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.h +++ b/interface/src/raypick/LaserPointerScriptingInterface.h @@ -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); } diff --git a/interface/src/raypick/RayPick.h b/interface/src/raypick/RayPick.h index 04045b2116..0686a05718 100644 --- a/interface/src/raypick/RayPick.h +++ b/interface/src/raypick/RayPick.h @@ -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)); } diff --git a/interface/src/raypick/RayPickManager.cpp b/interface/src/raypick/RayPickManager.cpp index 1ec8efc331..bfc6e3fcb2 100644 --- a/interface/src/raypick/RayPickManager.cpp +++ b/interface/src/raypick/RayPickManager.cpp @@ -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 = _rayPicks[uid]; @@ -58,7 +59,7 @@ void RayPickManager::update() { } QPair rayKey = QPair(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()->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()->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> rayPickToAdd = _rayPicksToAdd.front(); - _rayPicksToAdd.pop(); - _rayPicks[rayPickToAdd.first] = rayPickToAdd.second; - _rayPickLocks[rayPickToAdd.first] = std::make_shared(); - } - } - - { - 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>(id, std::make_shared(jointName, posOffset, dirOffset, filter, maxDistance, enabled))); + _rayPicks[id] = std::make_shared(jointName, posOffset, dirOffset, filter, maxDistance, enabled); + _rayPickLocks[id] = std::make_shared(); 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>(id, std::make_shared(filter, maxDistance, enabled))); + _rayPicks[id] = std::make_shared(filter, maxDistance, enabled); + _rayPickLocks[id] = std::make_shared(); 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>(id, std::make_shared(position, direction, filter, maxDistance, enabled))); + _rayPicks[id] = std::make_shared(position, direction, filter, maxDistance, enabled); + _rayPickLocks[id] = std::make_shared(); 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)) { diff --git a/interface/src/raypick/RayPickManager.h b/interface/src/raypick/RayPickManager.h index f2b1ff4ae4..9717767f19 100644 --- a/interface/src/raypick/RayPickManager.h +++ b/interface/src/raypick/RayPickManager.h @@ -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> _rayPicks; QHash> _rayPickLocks; - QReadWriteLock _addLock; - std::queue>> _rayPicksToAdd; - QReadWriteLock _removeLock; - std::queue _rayPicksToRemove; QReadWriteLock _containsLock; typedef QHash, std::unordered_map> RayPickCache; diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index f63c128cf5..4b981207f1 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -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)); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 4b5e2e81fc..0d58e6162d 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -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); diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 8aaaac1a57..d7b59ba912 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -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) { diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 9cd00dfdf2..b3bd6fbd34 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -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); }; diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 6f55260133..9afab80243 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -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(itemID, [](Overlay& data) { + transaction.updateItem(itemID, [latestTransform](Overlay& data) { auto overlay3D = dynamic_cast(&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(); } diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 55b55ed16f..93a973e60a 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -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; } diff --git a/interface/src/ui/overlays/Billboard3DOverlay.cpp b/interface/src/ui/overlays/Billboard3DOverlay.cpp index f5668caa71..960f0de095 100644 --- a/interface/src/ui/overlays/Billboard3DOverlay.cpp +++ b/interface/src/ui/overlays/Billboard3DOverlay.cpp @@ -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; +} \ No newline at end of file diff --git a/interface/src/ui/overlays/Billboard3DOverlay.h b/interface/src/ui/overlays/Billboard3DOverlay.h index d429537b5b..6b3aa40451 100644 --- a/interface/src/ui/overlays/Billboard3DOverlay.h +++ b/interface/src/ui/overlays/Billboard3DOverlay.h @@ -28,6 +28,8 @@ public: protected: virtual bool applyTransformTo(Transform& transform, bool force = false) override; + + Transform evalRenderTransform() override; }; #endif // hifi_Billboard3DOverlay_h diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 57911c0786..4e51844d21 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -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... diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index ca7355b86f..0ac9dba34b 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -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(); 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; +} diff --git a/interface/src/ui/overlays/Cube3DOverlay.h b/interface/src/ui/overlays/Cube3DOverlay.h index 9289af4de5..e7b58ad911 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.h +++ b/interface/src/ui/overlays/Cube3DOverlay.h @@ -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 diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index 3172403731..ca275368cb 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -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; +} diff --git a/interface/src/ui/overlays/Grid3DOverlay.h b/interface/src/ui/overlays/Grid3DOverlay.h index 0d042af6ca..5a67b21e07 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.h +++ b/interface/src/ui/overlays/Grid3DOverlay.h @@ -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(); diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index c79d363811..998cc312eb 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -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()->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; +} diff --git a/interface/src/ui/overlays/Image3DOverlay.h b/interface/src/ui/overlays/Image3DOverlay.h index 4f813e7368..aa802a82a9 100644 --- a/interface/src/ui/overlays/Image3DOverlay.h +++ b/interface/src/ui/overlays/Image3DOverlay.h @@ -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; diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 534261c839..87021cf852 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -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(); 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; +} diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index 9abc2f1a8d..bcb65b1f1e 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -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 }; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index e2a7df7ae6..ca5ca54144 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -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); diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 59548dfe62..8d8429b29e 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -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 using mapFunction = std::function; template diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index e865714e58..ac3fe66ddc 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -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) { diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index 2ed90ab4ed..1360cccfc2 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -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; }; diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index dc8badbbd1..47d47b76a5 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -124,3 +124,8 @@ void Rectangle3DOverlay::setProperties(const QVariantMap& properties) { Rectangle3DOverlay* Rectangle3DOverlay::createClone() const { return new Rectangle3DOverlay(this); } + +Transform Rectangle3DOverlay::evalRenderTransform() { + return getTransform(); +} + diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.h b/interface/src/ui/overlays/Rectangle3DOverlay.h index a0c342a25b..a26ed524fc 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.h +++ b/interface/src/ui/overlays/Rectangle3DOverlay.h @@ -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 _rectGeometryIds; diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index fc54cc19ff..2c1df478f6 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -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(); 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; +} diff --git a/interface/src/ui/overlays/Shape3DOverlay.h b/interface/src/ui/overlays/Shape3DOverlay.h index 2361001721..e9e26e3c94 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.h +++ b/interface/src/ui/overlays/Shape3DOverlay.h @@ -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 }; diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 83dc4b0e2b..f2c9968687 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -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(); 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; +} diff --git a/interface/src/ui/overlays/Sphere3DOverlay.h b/interface/src/ui/overlays/Sphere3DOverlay.h index 991b2ab51e..ebe6dc8d83 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.h +++ b/interface/src/ui/overlays/Sphere3DOverlay.h @@ -27,6 +27,9 @@ public: virtual const render::ShapeKey getShapeKey() override; virtual Sphere3DOverlay* createClone() const override; + +protected: + Transform evalRenderTransform() override; }; diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 43a2854206..57e3c32060 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -17,6 +17,8 @@ #include #include +#include + 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()->bindSimpleProgram(batch, false, quadColor.a < 1.0f, false, false, false, false); DependencyManager::get()->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(); +} + diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index e7b09c9040..6ed31f37b3 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -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; diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index 5e3e4ccee7..5be3247cec 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -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; +} diff --git a/interface/src/ui/overlays/Volume3DOverlay.h b/interface/src/ui/overlays/Volume3DOverlay.h index 04b694b2f8..bde8c71aef 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.h +++ b/interface/src/ui/overlays/Volume3DOverlay.h @@ -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; }; diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 0807d1c117..811e169faf 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -62,6 +62,7 @@ static const float OPAQUE_ALPHA_THRESHOLD = 0.99f; const QString Web3DOverlay::TYPE = "web3d"; const QString Web3DOverlay::QML = "Web3DOverlay.qml"; + Web3DOverlay::Web3DOverlay() : _dpi(DPI) { _touchDevice.setCapabilities(QTouchDevice::Position); _touchDevice.setType(QTouchDevice::TouchScreen); @@ -97,6 +98,10 @@ Web3DOverlay::~Web3DOverlay() { } } +void Web3DOverlay::rebuildWebSurface() { + destroyWebSurface(); + buildWebSurface(); +} void Web3DOverlay::destroyWebSurface() { if (!_webSurface) { @@ -136,17 +141,23 @@ void Web3DOverlay::buildWebSurface() { return; } gl::withSavedContext([&] { - _webSurface = DependencyManager::get()->acquire(pickURL()); // FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces // and the current rendering load) if (_currentMaxFPS != _desiredMaxFPS) { setMaxFPS(_desiredMaxFPS); } - loadSourceURL(); - _webSurface->resume(); + + if (isWebContent()) { + _webSurface = DependencyManager::get()->acquire(QML); + _webSurface->getRootItem()->setProperty("url", _url); + _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL); + } else { + _webSurface = DependencyManager::get()->acquire(_url); + setupQmlSurface(); + } + _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getPosition())); _webSurface->resize(QSize(_resolution.x, _resolution.y)); - _webSurface->getRootItem()->setProperty("url", _url); - _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL); + _webSurface->resume(); }); auto selfOverlayID = getOverlayID(); @@ -187,88 +198,61 @@ void Web3DOverlay::update(float deltatime) { Parent::update(deltatime); } -QString Web3DOverlay::pickURL() { +bool Web3DOverlay::isWebContent() const { QUrl sourceUrl(_url); if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" || _url.toLower().endsWith(".htm") || _url.toLower().endsWith(".html")) { - if (_webSurface) { - _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); - } - return "Web3DOverlay.qml"; - } else { - return QUrl::fromLocalFile(PathUtils::resourcesPath()).toString() + "/" + _url; + return true; } + return false; } -void Web3DOverlay::loadSourceURL() { - if (!_webSurface) { - return; - } +void Web3DOverlay::setupQmlSurface() { + _webSurface->getSurfaceContext()->setContextProperty("Users", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("UserActivityLogger", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("Preferences", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("Vec3", new Vec3()); + _webSurface->getSurfaceContext()->setContextProperty("Quat", new Quat()); + _webSurface->getSurfaceContext()->setContextProperty("MyAvatar", DependencyManager::get()->getMyAvatar().get()); + _webSurface->getSurfaceContext()->setContextProperty("Entities", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("Snapshot", DependencyManager::get().data()); - QUrl sourceUrl(_url); - if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" || - _url.toLower().endsWith(".htm") || _url.toLower().endsWith(".html")) { + if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") { + auto tabletScriptingInterface = DependencyManager::get(); + auto flags = tabletScriptingInterface->getFlags(); - _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); - _webSurface->load("Web3DOverlay.qml"); - _webSurface->resume(); - _webSurface->getRootItem()->setProperty("url", _url); - _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL); - - } else { - _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath())); - _webSurface->load(_url, [&](QQmlContext* context, QObject* obj) {}); - _webSurface->resume(); - - _webSurface->getSurfaceContext()->setContextProperty("Users", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags); + _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("Account", AccountScriptingInterface::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("Audio", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("AudioStats", DependencyManager::get()->getStats().data()); _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("UserActivityLogger", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("Preferences", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("Vec3", new Vec3()); - _webSurface->getSurfaceContext()->setContextProperty("Quat", new Quat()); + _webSurface->getSurfaceContext()->setContextProperty("fileDialogHelper", new FileDialogHelper()); _webSurface->getSurfaceContext()->setContextProperty("MyAvatar", DependencyManager::get()->getMyAvatar().get()); - _webSurface->getSurfaceContext()->setContextProperty("Entities", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("Snapshot", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("Tablet", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("Assets", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("LODManager", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("DCModel", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("InputConfiguration", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance()); - if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") { - auto tabletScriptingInterface = DependencyManager::get(); - auto flags = tabletScriptingInterface->getFlags(); + _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); - _webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags); - _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("Account", AccountScriptingInterface::getInstance()); - _webSurface->getSurfaceContext()->setContextProperty("Audio", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("AudioStats", DependencyManager::get()->getStats().data()); - _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("fileDialogHelper", new FileDialogHelper()); - _webSurface->getSurfaceContext()->setContextProperty("MyAvatar", DependencyManager::get()->getMyAvatar().get()); - _webSurface->getSurfaceContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("Tablet", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("Assets", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("LODManager", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("DCModel", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); - _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance()); - _webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance()); - _webSurface->getSurfaceContext()->setContextProperty("InputConfiguration", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance()); - - _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); - - tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); - - // mark the TabletProxy object as cpp ownership. - QObject* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"); - _webSurface->getSurfaceContext()->engine()->setObjectOwnership(tablet, QQmlEngine::CppOwnership); - - // Override min fps for tablet UI, for silky smooth scrolling - setMaxFPS(90); - } + tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); + // mark the TabletProxy object as cpp ownership. + QObject* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"); + _webSurface->getSurfaceContext()->engine()->setObjectOwnership(tablet, QQmlEngine::CppOwnership); + // Override min fps for tablet UI, for silky smooth scrolling + setMaxFPS(90); } - _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getPosition())); } void Web3DOverlay::setMaxFPS(uint8_t maxFPS) { @@ -302,7 +286,6 @@ void Web3DOverlay::render(RenderArgs* args) { emit resizeWebSurface(); } - vec2 halfSize = getSize() / 2.0f; vec4 color(toGlm(getColor()), getAlpha()); if (!_texture) { @@ -318,8 +301,11 @@ void Web3DOverlay::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; batch.setResourceTexture(0, _texture); + auto renderTransform = getRenderTransform(); - batch.setModelTransform(getRenderTransform()); + auto size = renderTransform.getScale(); + renderTransform.setScale(1.0f); + batch.setModelTransform(renderTransform); auto geometryCache = DependencyManager::get(); if (color.a < OPAQUE_ALPHA_THRESHOLD) { @@ -327,10 +313,19 @@ void Web3DOverlay::render(RenderArgs* args) { } else { geometryCache->bindWebBrowserProgram(batch); } + + vec2 halfSize = vec2(size.x, size.y) / 2.0f; geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color, _geometryId); batch.setResourceTexture(0, nullptr); // restore default white color after me } +Transform Web3DOverlay::evalRenderTransform() { + Transform transform = Parent::evalRenderTransform(); + transform.setScale(1.0f); + transform.postScale(glm::vec3(getSize(), 1.0f)); + return transform; +} + const render::ShapeKey Web3DOverlay::getShapeKey() { auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias().withOwnPipeline(); if (isTransparent()) { @@ -594,11 +589,25 @@ QVariant Web3DOverlay::getProperty(const QString& property) { } void Web3DOverlay::setURL(const QString& url) { - _url = url; - if (_webSurface) { - AbstractViewStateInterface::instance()->postLambdaEvent([this, url] { - loadSourceURL(); - }); + if (url != _url) { + bool wasWebContent = isWebContent(); + _url = url; + if (_webSurface) { + if (wasWebContent && isWebContent()) { + // If we're just targeting a new web URL, then switch to that without messing around + // with the underlying QML + AbstractViewStateInterface::instance()->postLambdaEvent([this, url] { + _webSurface->getRootItem()->setProperty("url", _url); + _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL); + }); + } else { + // If we're switching to or from web content, or between different QML content + // we need to destroy and rebuild the entire QML surface + AbstractViewStateInterface::instance()->postLambdaEvent([this, url] { + rebuildWebSurface(); + }); + } + } } } diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 6bd540d120..de74b95b3e 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -31,8 +31,6 @@ public: Web3DOverlay(const Web3DOverlay* Web3DOverlay); virtual ~Web3DOverlay(); - QString pickURL(); - void loadSourceURL(); void setMaxFPS(uint8_t maxFPS); virtual void render(RenderArgs* args) override; virtual const render::ShapeKey getShapeKey() override; @@ -78,7 +76,14 @@ signals: void requestWebSurface(); void releaseWebSurface(); +protected: + Transform evalRenderTransform() override; + private: + void setupQmlSurface(); + void rebuildWebSurface(); + bool isWebContent() const; + InputMode _inputMode { Touch }; QSharedPointer _webSurface; gpu::TexturePointer _texture; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index cbfcd473dc..78475f5b68 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1503,6 +1503,11 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes); _inputRingBuffer.resizeForFrameSize(numFrameSamples); +#if defined(Q_OS_ANDROID) + if (_audioInput) { + connect(_audioInput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(audioInputStateChanged(QAudio::State))); + } +#endif _inputDevice = _audioInput->start(); if (_inputDevice) { @@ -1541,6 +1546,31 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn return supportedFormat; } +#if defined(Q_OS_ANDROID) +void AudioClient::audioInputStateChanged(QAudio::State state) { + switch (state) { + case QAudio::StoppedState: + if (!_audioInput) { + break; + } + // Stopped on purpose + if (_shouldRestartInputSetup) { + Lock lock(_deviceMutex); + _inputDevice = _audioInput->start(); + lock.unlock(); + if (_inputDevice) { + connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); + } + } + break; + case QAudio::ActiveState: + break; + default: + break; + } +} +#endif + void AudioClient::outputNotify() { int recentUnfulfilled = _audioOutputIODevice.getRecentUnfulfilledReads(); if (recentUnfulfilled > 0) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index ff0ea968a8..01a487455c 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include @@ -173,6 +173,9 @@ public slots: void sendDownstreamAudioStatsPacket() { _stats.publish(); } void handleMicAudioInput(); +#if defined(Q_OS_ANDROID) + void audioInputStateChanged(QAudio::State state); +#endif void handleDummyAudioInput(); void handleRecordedAudioInput(const QByteArray& audio); void reset(); @@ -403,6 +406,10 @@ private: RateCounter<> _silentInbound; RateCounter<> _audioInbound; +#if defined(Q_OS_ANDROID) + bool _shouldRestartInputSetup { true }; // Should we restart the input device because of an unintended stop? +#endif + QTimer* _checkDevicesTimer { nullptr }; QTimer* _checkPeakValuesTimer { nullptr }; }; diff --git a/libraries/audio/src/AudioGate.cpp b/libraries/audio/src/AudioGate.cpp index a4d731a447..5b2561da07 100644 --- a/libraries/audio/src/AudioGate.cpp +++ b/libraries/audio/src/AudioGate.cpp @@ -6,11 +6,12 @@ // Copyright 2017 High Fidelity, Inc. // +#include "AudioGate.h" + #include #include - +#include #include "AudioDynamics.h" -#include "AudioGate.h" // log2 domain headroom bits above 0dB (int32_t) static const int LOG2_HEADROOM_Q30 = 1; @@ -417,7 +418,7 @@ void GateMono::process(int16_t* input, int16_t* output, int numFrames) { _dc.process(x); // peak detect - int32_t peak = abs(x); + int32_t peak = std::abs(x); // convert to log2 domain peak = fixlog2(peak); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 16245601dd..877bcd9353 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -103,7 +103,7 @@ AvatarData::~AvatarData() { QUrl AvatarData::_defaultFullAvatarModelUrl = {}; // In C++, if this initialization were in the AvatarInfo, every file would have it's own copy, even for class vars. const QUrl& AvatarData::defaultFullAvatarModelUrl() { if (_defaultFullAvatarModelUrl.isEmpty()) { - _defaultFullAvatarModelUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full.fst"); + _defaultFullAvatarModelUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "/meshes/defaultAvatar_full.fst"); } return _defaultFullAvatarModelUrl; } diff --git a/libraries/controllers/CMakeLists.txt b/libraries/controllers/CMakeLists.txt index 6b1ab72c60..9c6bbf4aae 100644 --- a/libraries/controllers/CMakeLists.txt +++ b/libraries/controllers/CMakeLists.txt @@ -8,7 +8,3 @@ link_hifi_libraries(shared) include_hifi_library_headers(networking) GroupSources("src/controllers") - -add_dependency_external_projects(glm) -find_package(GLM REQUIRED) -target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS} "${CMAKE_BINARY_DIR}/includes") diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 3e090dc7b3..da226d146b 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -28,11 +28,14 @@ DisplayPluginList getDisplayPlugins() { #ifdef DEBUG new NullDisplayPlugin(), #endif + +#if !defined(Q_OS_ANDROID) // Stereo modes // SBS left/right new SideBySideStereoDisplayPlugin(), // Interleaved left/right new InterleavedStereoDisplayPlugin(), +#endif nullptr }; diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp index 1e0e7e6c1f..6e397efbe5 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp @@ -40,14 +40,6 @@ bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) { return Parent::beginFrameRender(frameIndex); } -// DLL based display plugins MUST initialize GLEW inside the DLL code. -void DebugHmdDisplayPlugin::customizeContext() { - glewExperimental = true; - glewInit(); - glGetError(); // clear the potential error from glewExperimental - Parent::customizeContext(); -} - bool DebugHmdDisplayPlugin::internalActivate() { _ipd = 0.0327499993f * 2.0f; _eyeProjections[0][0] = vec4{ 0.759056330, 0.000000000, 0.000000000, 0.000000000 }; @@ -61,7 +53,7 @@ bool DebugHmdDisplayPlugin::internalActivate() { _eyeInverseProjections[0] = glm::inverse(_eyeProjections[0]); _eyeInverseProjections[1] = glm::inverse(_eyeProjections[1]); _eyeOffsets[0][3] = vec4{ -0.0327499993, 0.0, 0.0149999997, 1.0 }; - _eyeOffsets[0][3] = vec4{ 0.0327499993, 0.0, 0.0149999997, 1.0 }; + _eyeOffsets[1][3] = vec4{ 0.0327499993, 0.0, 0.0149999997, 1.0 }; _renderTargetSize = { 3024, 1680 }; _cullingProjection = _eyeProjections[0]; // This must come after the initialization, so that the values calculated diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h index 9bb82b1836..cd6fdd44b9 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h @@ -26,7 +26,6 @@ protected: void updatePresentPose() override; void hmdPresent() override {} bool isHmdMounted() const override { return true; } - void customizeContext() override; bool internalActivate() override; private: static const QString NAME; diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index caeba37839..88ec94eefb 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -441,7 +441,7 @@ void HmdDisplayPlugin::OverlayRenderer::updatePipeline() { this->uniformsLocation = program->getUniformBuffers().findLocation("overlayBuffer"); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(gpu::State::DepthTest(true, true, gpu::LESS_EQUAL)); + state->setDepthTest(gpu::State::DepthTest(false, false, gpu::LESS_EQUAL)); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index c29d92bae9..0cb25a2e2f 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1078,4 +1078,4 @@ void EntityTreeRenderer::onEntityChanged(const EntityItemID& id) { _changedEntitiesGuard.withWriteLock([&] { _changedEntities.insert(id); }); -} +} \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 799a84aaee..d1e47fd906 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -318,8 +318,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { updateModelBounds(); // should never fall in here when collision model not fully loaded - // hence we assert that all geometries exist and are loaded - assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); + // TODO: assert that all geometries exist and are loaded + //assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); const FBXGeometry& collisionGeometry = _compoundShapeResource->getFBXGeometry(); ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); @@ -407,8 +407,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { } shapeInfo.setParams(type, dimensions, getCompoundShapeURL()); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { - // should never fall in here when model not fully loaded - assert(_model && _model->isLoaded()); + // TODO: assert we never fall in here when model not fully loaded + //assert(_model && _model->isLoaded()); updateModelBounds(); model->updateGeometry(); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index f4e4c0ea8f..b4c64aed6f 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -110,7 +110,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { if (!_geometryID) { _geometryID = geometryCache->allocateID(); } - geometryCache->bindSimpleProgram(batch, false, transparent, false, false, true); + geometryCache->bindSimpleProgram(batch, false, transparent, false, false, false, false); geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID); float scale = _lineHeight / _textRenderer->getFontSize(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 5dc75dad08..0561fa5130 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -176,8 +176,6 @@ void WebEntityRenderer::doRender(RenderArgs* args) { } } - //_timer.singleShot - PerformanceTimer perfTimer("WebEntityRenderer::render"); static const glm::vec2 texMin(0.0f), texMax(1.0f), topLeft(-0.5f), bottomRight(0.5f); @@ -187,11 +185,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) { float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); - if (fadeRatio < OPAQUE_ALPHA_THRESHOLD) { - DependencyManager::get()->bindWebBrowserProgram(batch, true); - } else { - DependencyManager::get()->bindWebBrowserProgram(batch); - } + DependencyManager::get()->bindWebBrowserProgram(batch, fadeRatio < OPAQUE_ALPHA_THRESHOLD); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId); } diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 47d1f72eee..738561eccc 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -38,7 +38,7 @@ void main(void) { int frontCondition = 1 -int(gl_FrontFacing) * 2; vec3 color = varColor.rgb; packDeferredFragmentTranslucent( - interpolatedNormal * frontCondition, + float(frontCondition) * interpolatedNormal, texel.a * varColor.a, color * texel.rgb, vec3(0.01, 0.01, 0.01), diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f91bc14fe4..c6b5bc953b 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -659,22 +659,22 @@ QVector EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frust } QVector EntityScriptingInterface::findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const { - EntityTypes::EntityType type = EntityTypes::getEntityTypeFromName(entityType); + EntityTypes::EntityType type = EntityTypes::getEntityTypeFromName(entityType); - QVector result; - if (_entityTree) { - QVector entities; - _entityTree->withReadLock([&] { - _entityTree->findEntities(center, radius, entities); - }); + QVector result; + if (_entityTree) { + QVector entities; + _entityTree->withReadLock([&] { + _entityTree->findEntities(center, radius, entities); + }); - foreach(EntityItemPointer entity, entities) { - if (entity->getType() == type) { - result << entity->getEntityItemID(); - } - } - } - return result; + foreach(EntityItemPointer entity, entities) { + if (entity->getType() == type) { + result << entity->getEntityItemID(); + } + } + } + return result; } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 9b2b6360f3..7248c1f851 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -215,12 +215,12 @@ public slots: /// this function will not find any models in script engine contexts which don't have access to entities Q_INVOKABLE QVector findEntitiesInFrustum(QVariantMap frustum) const; - /// finds entities of the indicated type within a sphere given by the center point and radius - /// @param {QString} string representation of entity type - /// @param {vec3} center point - /// @param {float} radius to search - /// this function will not find any entities in script engine contexts which don't have access to entities - Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const; + /// finds entities of the indicated type within a sphere given by the center point and radius + /// @param {QString} string representation of entity type + /// @param {vec3} center point + /// @param {float} radius to search + /// this function will not find any entities in script engine contexts which don't have access to entities + Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const; /// If the scripting context has visible entities, this will determine a ray intersection, the results /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index cf5babdb1a..08acf9b058 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -590,7 +590,7 @@ bool EntityTree::findNearPointOperation(const OctreeElementPointer& element, voi bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) { RayArgs* args = static_cast(extraData); bool keepSearching = true; - EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast(element); + EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); if (entityTreeElementPointer->findRayIntersection(args->origin, args->direction, keepSearching, args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->intersectedObject, args->precisionPicking)) { diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index b7c6cc1e73..f79b6105c3 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -99,7 +99,6 @@ class PolyLineEntityItem : public EntityItem { // disable these external interfaces as PolyLineEntities caculate their own dimensions based on the points they contain virtual void setRegistrationPoint(const glm::vec3& value) override {}; - virtual void debugDump() const override; static const float DEFAULT_LINE_WIDTH; static const int MAX_POINTS_PER_LINE; diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 9d74662e7c..586344ee81 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -223,7 +223,7 @@ void ShapeEntityItem::debugDump() const { qCDebug(entities) << " position:" << debugTreeVector(getPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << "SHAPE EntityItem Ptr:" << this; + qCDebug(entities) << "SHAPE EntityItem Ptr:" << this; } void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 3099782588..e0c2efd72e 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -28,6 +28,7 @@ #include "FBXReader.h" #include "ModelFormatLogging.h" +#include QHash COMMENT_SCALE_HINTS = {{"This file uses centimeters as units", 1.0f / 100.0f}, {"This file uses millimeters as units", 1.0f / 1000.0f}}; @@ -51,6 +52,10 @@ const QByteArray OBJTokenizer::getLineAsDatum() { return _device->readLine().trimmed(); } +float OBJTokenizer::getFloat() { + return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); +} + int OBJTokenizer::nextToken() { if (_pushedBackToken != NO_PUSHBACKED_TOKEN) { int token = _pushedBackToken; @@ -125,7 +130,7 @@ glm::vec3 OBJTokenizer::getVec3() { } bool OBJTokenizer::getVertex(glm::vec3& vertex, glm::vec3& vertexColor) { // Used for vertices which may also have a vertex color (RGB [0,1]) to follow. - // NOTE: Returns true if there is a vertex color. + // NOTE: Returns true if there is a vertex color. auto x = getFloat(); // N.B.: getFloat() has side-effect auto y = getFloat(); // And order of arguments is different on Windows/Linux. auto z = getFloat(); @@ -168,7 +173,7 @@ void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) { } // OBJFace -// NOTE (trent, 7/20/17): The vertexColors vector being passed-in isn't necessary here, but I'm just +// NOTE (trent, 7/20/17): The vertexColors vector being passed-in isn't necessary here, but I'm just // pairing it with the vertices vector for consistency. bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices, const QVector& vertexColors) { bool ok; @@ -544,9 +549,9 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, fbxMeshParts.append(FBXMeshPart()); FBXMeshPart& meshPartNew = fbxMeshParts.last(); - meshPartNew.quadIndices = QVector(meshPart.quadIndices); // Copy over quad indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. + meshPartNew.quadIndices = QVector(meshPart.quadIndices); // Copy over quad indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. meshPartNew.quadTrianglesIndices = QVector(meshPart.quadTrianglesIndices); // Copy over quad triangulated indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. - meshPartNew.triangleIndices = QVector(meshPart.triangleIndices); // Copy over triangle indices. + meshPartNew.triangleIndices = QVector(meshPart.triangleIndices); // Copy over triangle indices. // Do some of the material logic (which previously lived below) now. // All the faces in the same group will have the same name and material. diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 9a32871590..fb250833cf 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -22,7 +22,7 @@ public: glm::vec3 getVec3(); bool getVertex(glm::vec3& vertex, glm::vec3& vertexColor); glm::vec2 getVec2(); - float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); } + float getFloat(); private: QIODevice* _device; diff --git a/libraries/gl/CMakeLists.txt b/libraries/gl/CMakeLists.txt index fd3197410b..9fc7a0c10f 100644 --- a/libraries/gl/CMakeLists.txt +++ b/libraries/gl/CMakeLists.txt @@ -1,9 +1,5 @@ set(TARGET_NAME gl) setup_hifi_library(OpenGL Qml Quick) -link_hifi_libraries(shared networking) - +link_hifi_libraries(shared) target_opengl() -if (NOT ANDROID) - target_glew() -endif () diff --git a/libraries/gl/src/gl/Config.cpp b/libraries/gl/src/gl/Config.cpp new file mode 100644 index 0000000000..1f29fe21b1 --- /dev/null +++ b/libraries/gl/src/gl/Config.cpp @@ -0,0 +1,35 @@ +// +// GPUConfig.h +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 12/4/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 +// + +#include "Config.h" + +#include + +#if defined(Q_OS_ANDROID) +PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT = NULL; +PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT = NULL; +PFNGLFRAMEBUFFERTEXTUREEXTPROC glFramebufferTextureEXT = NULL; +#endif + +void gl::initModuleGl() { + static std::once_flag once; + std::call_once(once, [] { +#if defined(Q_OS_ANDROID) + glQueryCounterEXT = (PFNGLQUERYCOUNTEREXTPROC)eglGetProcAddress("glQueryCounterEXT"); + glGetQueryObjectui64vEXT = (PFNGLGETQUERYOBJECTUI64VEXTPROC)eglGetProcAddress("glGetQueryObjectui64vEXT"); + glFramebufferTextureEXT = (PFNGLFRAMEBUFFERTEXTUREEXTPROC)eglGetProcAddress("glFramebufferTextureEXT"); +#else + glewExperimental = true; + glewInit(); +#endif + }); +} + diff --git a/libraries/gl/src/gl/Config.h b/libraries/gl/src/gl/Config.h index 9efae96f2a..ff282a1ca0 100644 --- a/libraries/gl/src/gl/Config.h +++ b/libraries/gl/src/gl/Config.h @@ -12,25 +12,67 @@ #ifndef hifi_gpu_GPUConfig_h #define hifi_gpu_GPUConfig_h -#define GL_GLEXT_PROTOTYPES 1 +#include +#if defined(QT_OPENGL_ES_3_1) +// Minimum GL ES version required is 3.1 +#define GL_MIN_VERSION_MAJOR 0x03 +#define GL_MIN_VERSION_MINOR 0x01 +#define GL_DEFAULT_VERSION_MAJOR GL_MIN_VERSION_MAJOR +#define GL_DEFAULT_VERSION_MINOR GL_MIN_VERSION_MINOR +#else +// Minimum desktop GL version required is 4.1 +#define GL_MIN_VERSION_MAJOR 0x04 +#define GL_MIN_VERSION_MINOR 0x01 +#define GL_DEFAULT_VERSION_MAJOR 0x04 +#define GL_DEFAULT_VERSION_MINOR 0x05 +#endif + +#define MINIMUM_GL_VERSION ((GL_MIN_VERSION_MAJOR << 8) | GL_MIN_VERSION_MINOR) + +#if defined(Q_OS_ANDROID) + +#include +#include + +#define GL_DEPTH_COMPONENT32_OES 0x81A7 +#define GL_TIME_ELAPSED_EXT 0x88BF +#define GL_TIMESTAMP_EXT 0x8E28 +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_TEXTURE_BORDER_COLOR_EXT 0x1004 +#define GL_CLAMP_TO_BORDER_EXT 0x812D +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + + +// Add some additional extensions missing from GLES 3.1 +extern "C" { + typedef void (GL_APIENTRYP PFNGLQUERYCOUNTEREXTPROC) (GLuint id, GLenum target); + typedef void (GL_APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64 *params); + typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); + extern PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT; + extern PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT; + extern PFNGLFRAMEBUFFERTEXTUREEXTPROC glFramebufferTextureEXT; +} + +#else // !defined(Q_OS_ANDROID) + +#define GL_GLEXT_PROTOTYPES 1 #include -#if defined(__APPLE__) - +#if defined(Q_OS_DARWIN) #include #include #include - -#endif - -#if defined(WIN32) - +#elif defined(Q_OS_WIN64) #include - -// Uncomment this define and recompile to be able to avoid code path preventing to be able to run nsight graphics debug -//#define HIFI_ENABLE_NSIGHT_DEBUG 1 - #endif +#endif // !defined(Q_OS_ANDROID) + +// Platform specific code to load the GL functions +namespace gl { + void initModuleGl(); +} + #endif // hifi_gpu_GPUConfig_h diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 28982703dd..ed0594135a 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -28,6 +28,13 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { static QSurfaceFormat format; static std::once_flag once; std::call_once(once, [] { +#if defined(QT_OPENGL_ES_3_1) + format.setRenderableType(QSurfaceFormat::OpenGLES); + format.setRedBufferSize(8); + format.setGreenBufferSize(8); + format.setBlueBufferSize(8); + format.setAlphaBufferSize(8); +#endif // Qt Quick may need a depth and stencil buffer. Always make sure these are available. format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h index 84229b97d2..80fc2c5f70 100644 --- a/libraries/gl/src/gl/GLHelpers.h +++ b/libraries/gl/src/gl/GLHelpers.h @@ -25,7 +25,12 @@ class QSurfaceFormat; class QGLFormat; template -void setGLFormatVersion(F& format, int major = 4, int minor = 5) { format.setVersion(major, minor); } +#if defined(QT_OPENGL_ES_3_1) +void setGLFormatVersion(F& format, int major = 3, int minor = 1) +#else +void setGLFormatVersion(F& format, int major = 4, int minor = 5) +#endif + { format.setVersion(major, minor); } size_t evalGLFormatSwapchainPixelSize(const QSurfaceFormat& format); diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 3f1d629638..b974564705 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -59,7 +59,7 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { bool OffscreenGLCanvas::makeCurrent() { bool result = _context->makeCurrent(_offscreenSurface); - std::call_once(_reportOnce, [this]{ + std::call_once(_reportOnce, []{ qCDebug(glLogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); qCDebug(glLogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); qCDebug(glLogging) << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR)); diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp index f24a9bb932..771a8b9a75 100644 --- a/libraries/gl/src/gl/OpenGLVersionChecker.cpp +++ b/libraries/gl/src/gl/OpenGLVersionChecker.cpp @@ -21,9 +21,6 @@ #include "GLHelpers.h" -// Minimum gl version required is 4.1 -#define MINIMUM_GL_VERSION 0x0401 - OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) : QApplication(argc, argv) { diff --git a/libraries/gpu-gl/CMakeLists.txt b/libraries/gpu-gl/CMakeLists.txt index 65130d6d07..dc744e73f2 100644 --- a/libraries/gpu-gl/CMakeLists.txt +++ b/libraries/gpu-gl/CMakeLists.txt @@ -5,10 +5,5 @@ if (UNIX) target_link_libraries(${TARGET_NAME} pthread) endif(UNIX) GroupSources("src") - target_opengl() -target_nsight() -if (NOT ANDROID) - target_glew() -endif () diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp index b5a8dcb7a9..ff9ddaae63 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp @@ -7,9 +7,9 @@ // #include "GL41Backend.h" #include "../gl/GLShader.h" -//#include using namespace gpu; +using namespace gpu::gl; using namespace gpu::gl41; // GLSL version @@ -84,7 +84,7 @@ int GL41Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::Binding return ssboCount; } -void GL41Backend::makeProgramBindings(gl::ShaderObject& shaderObject) { +void GL41Backend::makeProgramBindings(ShaderObject& shaderObject) { if (!shaderObject.glprogram) { return; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp index 8a5e8d0064..c2490524ae 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp @@ -10,6 +10,7 @@ //#include using namespace gpu; +using namespace gpu::gl; using namespace gpu::gl45; // GLSL version @@ -132,7 +133,7 @@ int GL45Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::Binding return ssboCount;*/ } -void GL45Backend::makeProgramBindings(gl::ShaderObject& shaderObject) { +void GL45Backend::makeProgramBindings(ShaderObject& shaderObject) { if (!shaderObject.glprogram) { return; } diff --git a/libraries/gpu-gles/CMakeLists.txt b/libraries/gpu-gles/CMakeLists.txt new file mode 100644 index 0000000000..55ec53b184 --- /dev/null +++ b/libraries/gpu-gles/CMakeLists.txt @@ -0,0 +1,11 @@ +set(TARGET_NAME gpu-gles) +setup_hifi_library(OpenGL) +link_hifi_libraries(shared gl gpu) +GroupSources("src") + +target_opengl() +target_nsight() + +if (NOT ANDROID) + target_glew() +endif () diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp new file mode 100644 index 0000000000..1d66618703 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp @@ -0,0 +1,722 @@ +// +// GLBackend.cpp +// libraries/gpu-gl-android/src/gpu/gl +// +// Created by Cristian Duarte & Gabriel Calero on 9/21/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" + +#include +#include +#include +#include +#include + +#include "../gles/GLESBackend.h" + +#if defined(NSIGHT_FOUND) +#include "nvToolsExt.h" +#endif + +#include +#include +#include + +#include "GLTexture.h" +#include "GLShader.h" +using namespace gpu; +using namespace gpu::gl; + +static GLBackend* INSTANCE{ nullptr }; +static const char* GL_BACKEND_PROPERTY_NAME = "com.highfidelity.gl.backend"; + +BackendPointer GLBackend::createBackend() { + // FIXME provide a mechanism to override the backend for testing + // Where the gpuContext is initialized and where the TRUE Backend is created and assigned + auto version = QOpenGLContextWrapper::currentContextVersion(); + std::shared_ptr result; + + qDebug() << "Using OpenGL ES backend"; + result = std::make_shared(); + + result->initInput(); + result->initTransform(); + + INSTANCE = result.get(); + void* voidInstance = &(*result); + qApp->setProperty(GL_BACKEND_PROPERTY_NAME, QVariant::fromValue(voidInstance)); + + gl::GLTexture::initTextureTransferHelper(); + return result; +} + +GLBackend& getBackend() { + if (!INSTANCE) { + INSTANCE = static_cast(qApp->property(GL_BACKEND_PROPERTY_NAME).value()); + } + return *INSTANCE; +} + +bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) { + return GLShader::makeProgram(getBackend(), shader, slotBindings); +} + +std::array commandNames = { + {QString("draw"),QString("drawIndexed"),QString("drawInstanced"),QString("drawIndexedInstanced"),QString("multiDrawIndirect"),QString("multiDrawIndexedIndirect"),QString("setInputFormat"),QString("setInputBuffer"),QString("setIndexBuffer"),QString("setIndirectBuffer"),QString("setModelTransform"),QString("setViewTransform"),QString("setProjectionTransform"),QString("setViewportTransform"),QString("setDepthRangeTransform"),QString("setPipeline"),QString("setStateBlendFactor"),QString("setStateScissorRect"),QString("setUniformBuffer"),QString("setResourceTexture"),QString("setFramebuffer"),QString("clearFramebuffer"),QString("blit"),QString("generateTextureMips"),QString("beginQuery"),QString("endQuery"),QString("getQuery"),QString("resetStages"),QString("runLambda"),QString("startNamedCall"),QString("stopNamedCall"),QString("glUniform1i"),QString("glUniform1f"),QString("glUniform2f"),QString("glUniform3f"),QString("glUniform4f"),QString("glUniform3fv"),QString("glUniform4fv"),QString("glUniform4iv"),QString("glUniformMatrix3fv"),QString("glUniformMatrix4fv"),QString("glColor4f"),QString("pushProfileRange"),QString("popProfileRange"),QString("NUM_COMMANDS")} +}; + +GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = +{ + (&::gpu::gl::GLBackend::do_draw), + (&::gpu::gl::GLBackend::do_drawIndexed), + (&::gpu::gl::GLBackend::do_drawInstanced), + (&::gpu::gl::GLBackend::do_drawIndexedInstanced), + (&::gpu::gl::GLBackend::do_multiDrawIndirect), + (&::gpu::gl::GLBackend::do_multiDrawIndexedIndirect), + + (&::gpu::gl::GLBackend::do_setInputFormat), + (&::gpu::gl::GLBackend::do_setInputBuffer), + (&::gpu::gl::GLBackend::do_setIndexBuffer), + (&::gpu::gl::GLBackend::do_setIndirectBuffer), + + (&::gpu::gl::GLBackend::do_setModelTransform), + (&::gpu::gl::GLBackend::do_setViewTransform), + (&::gpu::gl::GLBackend::do_setProjectionTransform), + (&::gpu::gl::GLBackend::do_setViewportTransform), + (&::gpu::gl::GLBackend::do_setDepthRangeTransform), + + (&::gpu::gl::GLBackend::do_setPipeline), + (&::gpu::gl::GLBackend::do_setStateBlendFactor), + (&::gpu::gl::GLBackend::do_setStateScissorRect), + + (&::gpu::gl::GLBackend::do_setUniformBuffer), + (&::gpu::gl::GLBackend::do_setResourceTexture), + + (&::gpu::gl::GLBackend::do_setFramebuffer), + (&::gpu::gl::GLBackend::do_clearFramebuffer), + (&::gpu::gl::GLBackend::do_blit), + (&::gpu::gl::GLBackend::do_generateTextureMips), + + (&::gpu::gl::GLBackend::do_beginQuery), + (&::gpu::gl::GLBackend::do_endQuery), + (&::gpu::gl::GLBackend::do_getQuery), + + (&::gpu::gl::GLBackend::do_resetStages), + + (&::gpu::gl::GLBackend::do_runLambda), + + (&::gpu::gl::GLBackend::do_startNamedCall), + (&::gpu::gl::GLBackend::do_stopNamedCall), + + (&::gpu::gl::GLBackend::do_glUniform1i), + (&::gpu::gl::GLBackend::do_glUniform1f), + (&::gpu::gl::GLBackend::do_glUniform2f), + (&::gpu::gl::GLBackend::do_glUniform3f), + (&::gpu::gl::GLBackend::do_glUniform4f), + (&::gpu::gl::GLBackend::do_glUniform3fv), + (&::gpu::gl::GLBackend::do_glUniform4fv), + (&::gpu::gl::GLBackend::do_glUniform4iv), + (&::gpu::gl::GLBackend::do_glUniformMatrix3fv), + (&::gpu::gl::GLBackend::do_glUniformMatrix4fv), + + (&::gpu::gl::GLBackend::do_glColor4f), + + (&::gpu::gl::GLBackend::do_pushProfileRange), + (&::gpu::gl::GLBackend::do_popProfileRange), +}; + +void GLBackend::init() { + static std::once_flag once; + std::call_once(once, [] { + QString vendor{ (const char*)glGetString(GL_VENDOR) }; + QString renderer{ (const char*)glGetString(GL_RENDERER) }; + qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); + qCDebug(gpugllogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); + qCDebug(gpugllogging) << "GL Vendor: " << vendor; + qCDebug(gpugllogging) << "GL Renderer: " << renderer; + GPUIdent* gpu = GPUIdent::getInstance(vendor, renderer); + // From here on, GPUIdent::getInstance()->getMumble() should efficiently give the same answers. + qCDebug(gpugllogging) << "GPU:"; + qCDebug(gpugllogging) << "\tcard:" << gpu->getName(); + qCDebug(gpugllogging) << "\tdriver:" << gpu->getDriver(); + qCDebug(gpugllogging) << "\tdedicated memory:" << gpu->getMemory() << "MB"; + + /*glewExperimental = true; + GLenum err = glewInit(); + glGetError(); // clear the potential error from glewExperimental + if (GLEW_OK != err) { + // glewInit failed, something is seriously wrong. + qCDebug(gpugllogging, "Error: %s\n", glewGetErrorString(err)); + } + qCDebug(gpugllogging, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); + */ + + }); +} + +GLBackend::GLBackend() { + _pipeline._cameraCorrectionBuffer._buffer->flush(); + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment); +} + + +GLBackend::~GLBackend() { + resetStages(); + + killInput(); + killTransform(); +} + +void GLBackend::renderPassTransfer(const Batch& batch) { + const size_t numCommands = batch.getCommands().size(); + const Batch::Commands::value_type* command = batch.getCommands().data(); + const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); + + _inRenderTransferPass = true; + { // Sync all the buffers + ANDROID_PROFILE(render, "syncGPUBuffer", 0xffaaffaa, 1) + + for (auto& cached : batch._buffers._items) { + if (cached._data) { + syncGPUObject(*cached._data); + } + } + } + + { // Sync all the buffers + ANDROID_PROFILE(render, "syncCPUTransform", 0xffaaaaff, 1) + _transform._cameras.clear(); + _transform._cameraOffsets.clear(); + + for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { + switch (*command) { + case Batch::COMMAND_draw: + case Batch::COMMAND_drawIndexed: + case Batch::COMMAND_drawInstanced: + case Batch::COMMAND_drawIndexedInstanced: + case Batch::COMMAND_multiDrawIndirect: + case Batch::COMMAND_multiDrawIndexedIndirect: + _transform.preUpdate(_commandIndex, _stereo); + break; + + case Batch::COMMAND_setViewportTransform: + case Batch::COMMAND_setViewTransform: + case Batch::COMMAND_setProjectionTransform: { + ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xffeeaaff, 1) + CommandCall call = _commandCalls[(*command)]; + (this->*(call))(batch, *offset); + break; + } + + default: + break; + } + command++; + offset++; + } + } + + { // Sync the transform buffers + //PROFILE_RANGE(render_gpu_gl, "transferTransformState"); + ANDROID_PROFILE(render, "transferTransformState", 0xff0000ff, 1) + transferTransformState(batch); + } + + _inRenderTransferPass = false; +} + +void GLBackend::renderPassDraw(const Batch& batch) { + _currentDraw = -1; + _transform._camerasItr = _transform._cameraOffsets.begin(); + const size_t numCommands = batch.getCommands().size(); + const Batch::Commands::value_type* command = batch.getCommands().data(); + const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); + for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { + switch (*command) { + // Ignore these commands on this pass, taken care of in the transfer pass + // Note we allow COMMAND_setViewportTransform to occur in both passes + // as it both updates the transform object (and thus the uniforms in the + // UBO) as well as executes the actual viewport call + case Batch::COMMAND_setModelTransform: + case Batch::COMMAND_setViewTransform: + case Batch::COMMAND_setProjectionTransform: + break; + + case Batch::COMMAND_draw: + case Batch::COMMAND_drawIndexed: + case Batch::COMMAND_drawInstanced: + case Batch::COMMAND_drawIndexedInstanced: + case Batch::COMMAND_multiDrawIndirect: + case Batch::COMMAND_multiDrawIndexedIndirect: { + // updates for draw calls + ++_currentDraw; + updateInput(); + updateTransform(batch); + updatePipeline(); + {ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xff0000ff, 1) + CommandCall call = _commandCalls[(*command)]; + (this->*(call))(batch, *offset); + } + break; + } + default: { + ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xffff00ff, 1) + CommandCall call = _commandCalls[(*command)]; + (this->*(call))(batch, *offset); + break; + } + } + + command++; + offset++; + } +} + +void GLBackend::render(const Batch& batch) { + ANDROID_PROFILE(render, "GLBackendRender", 0xffff00ff, 1) + _transform._skybox = _stereo._skybox = batch.isSkyboxEnabled(); + // Allow the batch to override the rendering stereo settings + // for things like full framebuffer copy operations (deferred lighting passes) + bool savedStereo = _stereo._enable; + if (!batch.isStereoEnabled()) { + _stereo._enable = false; + } + + { + //PROFILE_RANGE(render_gpu_gl, "Transfer"); + ANDROID_PROFILE(render, "Transfer", 0xff0000ff, 1) + renderPassTransfer(batch); + } + + { + //PROFILE_RANGE(render_gpu_gl, _stereo._enable ? "Render Stereo" : "Render"); + ANDROID_PROFILE(render, "RenderPassDraw", 0xff00ddff, 1) + renderPassDraw(batch); + } + + // Restore the saved stereo state for the next batch + _stereo._enable = savedStereo; +} + + +void GLBackend::syncCache() { + syncTransformStateCache(); + syncPipelineStateCache(); + syncInputStateCache(); + syncOutputStateCache(); + + //glEnable(GL_LINE_SMOOTH); + qDebug() << "TODO: GLBackend.cpp:syncCache GL_LINE_SMOOTH"; +} + +void GLBackend::setupStereoSide(int side) { + ivec4 vp = _transform._viewport; + vp.z /= 2; + glViewport(vp.x + side * vp.z, vp.y, vp.z, vp.w); + +#ifdef GPU_STEREO_CAMERA_BUFFER +#ifdef GPU_STEREO_DRAWCALL_DOUBLED + //glVertexAttribI1i(14, side); + glVertexAttribI4i(14, side, 0, 0, 0); + +#endif +#else + _transform.bindCurrentCamera(side); +#endif +} + +void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) { + resetStages(); +} + +void GLBackend::do_runLambda(const Batch& batch, size_t paramOffset) { + std::function f = batch._lambdas.get(batch._params[paramOffset]._uint); + f(); +} + +void GLBackend::do_startNamedCall(const Batch& batch, size_t paramOffset) { + batch._currentNamedCall = batch._names.get(batch._params[paramOffset]._uint); + _currentDraw = -1; +} + +void GLBackend::do_stopNamedCall(const Batch& batch, size_t paramOffset) { + batch._currentNamedCall.clear(); +} + +void GLBackend::resetStages() { + resetInputStage(); + resetPipelineStage(); + resetTransformStage(); + resetUniformStage(); + resetResourceStage(); + resetOutputStage(); + resetQueryStage(); + + (void) CHECK_GL_ERROR(); +} + + +void GLBackend::do_pushProfileRange(const Batch& batch, size_t paramOffset) { + auto name = batch._profileRanges.get(batch._params[paramOffset]._uint); + profileRanges.push_back(name); +#if defined(NSIGHT_FOUND) + nvtxRangePush(name.c_str()); +#endif +} + +void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) { + profileRanges.pop_back(); +#if defined(NSIGHT_FOUND) + nvtxRangePop(); +#endif +} + +// TODO: As long as we have gl calls explicitely issued from interface +// code, we need to be able to record and batch these calls. THe long +// term strategy is to get rid of any GL calls in favor of the HIFI GPU API + +// As long as we don;t use several versions of shaders we can avoid this more complex code path +// #define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc, isStereo()); +#define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc + +void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + + glUniform1f( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int), + batch._params[paramOffset + 0]._int); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform1f(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + + glUniform1f( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int), + batch._params[paramOffset + 0]._float); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform2f(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + glUniform2f( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int), + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 0]._float); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform3f(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + glUniform3f( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int), + batch._params[paramOffset + 2]._float, + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 0]._float); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform4f(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + glUniform4f( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 4]._int), + batch._params[paramOffset + 3]._float, + batch._params[paramOffset + 2]._float, + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 0]._float); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform3fv(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + glUniform3fv( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int), + batch._params[paramOffset + 1]._uint, + (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); + + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform4fv(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + + GLint location = GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int); + GLsizei count = batch._params[paramOffset + 1]._uint; + const GLfloat* value = (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint); + glUniform4fv(location, count, value); + + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform4iv(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + glUniform4iv( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int), + batch._params[paramOffset + 1]._uint, + (const GLint*)batch.readData(batch._params[paramOffset + 0]._uint)); + + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + + glUniformMatrix3fv( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int), + batch._params[paramOffset + 2]._uint, + batch._params[paramOffset + 1]._uint, + (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + + glUniformMatrix4fv( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int), + batch._params[paramOffset + 2]._uint, + batch._params[paramOffset + 1]._uint, + (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) { + + glm::vec4 newColor( + batch._params[paramOffset + 3]._float, + batch._params[paramOffset + 2]._float, + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 0]._float); + + if (_input._colorAttribute != newColor) { + _input._colorAttribute = newColor; + glVertexAttrib4fv(gpu::Stream::COLOR, &_input._colorAttribute.r); + } + (void)CHECK_GL_ERROR(); +} + +void GLBackend::releaseBuffer(GLuint id, Size size) const { + Lock lock(_trashMutex); + _buffersTrash.push_back({ id, size }); +} + +void GLBackend::releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const { + Lock lock(_trashMutex); + _externalTexturesTrash.push_back({ id, recycler }); +} + +void GLBackend::releaseTexture(GLuint id, Size size) const { + Lock lock(_trashMutex); + _texturesTrash.push_back({ id, size }); +} + +void GLBackend::releaseFramebuffer(GLuint id) const { + Lock lock(_trashMutex); + _framebuffersTrash.push_back(id); +} + +void GLBackend::releaseShader(GLuint id) const { + Lock lock(_trashMutex); + _shadersTrash.push_back(id); +} + +void GLBackend::releaseProgram(GLuint id) const { + Lock lock(_trashMutex); + _programsTrash.push_back(id); +} + +void GLBackend::releaseQuery(GLuint id) const { + Lock lock(_trashMutex); + _queriesTrash.push_back(id); +} + +void GLBackend::queueLambda(const std::function lambda) const { + Lock lock(_trashMutex); + _lambdaQueue.push_back(lambda); +} + +void GLBackend::recycle() const { + { + std::list> lamdbasTrash; + { + Lock lock(_trashMutex); + std::swap(_lambdaQueue, lamdbasTrash); + } + for (auto lambda : lamdbasTrash) { + lambda(); + } + } + + { + std::vector ids; + std::list> buffersTrash; + { + Lock lock(_trashMutex); + std::swap(_buffersTrash, buffersTrash); + } + ids.reserve(buffersTrash.size()); + for (auto pair : buffersTrash) { + ids.push_back(pair.first); + } + if (!ids.empty()) { + glDeleteBuffers((GLsizei)ids.size(), ids.data()); + } + } + + { + std::vector ids; + std::list framebuffersTrash; + { + Lock lock(_trashMutex); + std::swap(_framebuffersTrash, framebuffersTrash); + } + ids.reserve(framebuffersTrash.size()); + for (auto id : framebuffersTrash) { + ids.push_back(id); + } + if (!ids.empty()) { + glDeleteFramebuffers((GLsizei)ids.size(), ids.data()); + } + } + + { + std::vector ids; + std::list> texturesTrash; + { + Lock lock(_trashMutex); + std::swap(_texturesTrash, texturesTrash); + } + ids.reserve(texturesTrash.size()); + for (auto pair : texturesTrash) { + ids.push_back(pair.first); + } + if (!ids.empty()) { + glDeleteTextures((GLsizei)ids.size(), ids.data()); + } + } + + { + std::list> externalTexturesTrash; + { + Lock lock(_trashMutex); + std::swap(_externalTexturesTrash, externalTexturesTrash); + } + if (!externalTexturesTrash.empty()) { + std::vector fences; + fences.resize(externalTexturesTrash.size()); + for (size_t i = 0; i < externalTexturesTrash.size(); ++i) { + fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + // External texture fences will be read in another thread/context, so we need a flush + glFlush(); + size_t index = 0; + for (auto pair : externalTexturesTrash) { + auto fence = fences[index++]; + pair.second(pair.first, fence); + } + } + } + + { + std::list programsTrash; + { + Lock lock(_trashMutex); + std::swap(_programsTrash, programsTrash); + } + for (auto id : programsTrash) { + glDeleteProgram(id); + } + } + + { + std::list shadersTrash; + { + Lock lock(_trashMutex); + std::swap(_shadersTrash, shadersTrash); + } + for (auto id : shadersTrash) { + glDeleteShader(id); + } + } + + { + std::vector ids; + std::list queriesTrash; + { + Lock lock(_trashMutex); + std::swap(_queriesTrash, queriesTrash); + } + ids.reserve(queriesTrash.size()); + for (auto id : queriesTrash) { + ids.push_back(id); + } + if (!ids.empty()) { + glDeleteQueries((GLsizei)ids.size(), ids.data()); + } + } + +#ifndef THREADED_TEXTURE_TRANSFER + gl::GLTexture::_textureTransferHelper->process(); +#endif +} + +void GLBackend::setCameraCorrection(const Mat4& correction) { + _transform._correction.correction = correction; + _transform._correction.correctionInverse = glm::inverse(correction); + _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); + _pipeline._cameraCorrectionBuffer._buffer->flush(); +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.h b/libraries/gpu-gles/src/gpu/gl/GLBackend.h new file mode 100644 index 0000000000..f8f307bc17 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.h @@ -0,0 +1,427 @@ +// +// Created by Cristian Duarte & Gabriel Calero on 09/21/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gles_Backend_h +#define hifi_gpu_gles_Backend_h + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "GLShared.h" + + +// Different versions for the stereo drawcall +// Current preferred is "instanced" which draw the shape twice but instanced and rely on clipping plane to draw left/right side only +//#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER +//#define GPU_STEREO_TECHNIQUE_INSTANCED + +#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER +#define GPU_STEREO_DRAWCALL_DOUBLED +#define GPU_STEREO_CAMERA_BUFFER +#endif + +#ifdef GPU_STEREO_TECHNIQUE_INSTANCED +#define GPU_STEREO_DRAWCALL_INSTANCED +#define GPU_STEREO_CAMERA_BUFFER +#endif + +//#define ANDROID_INTENSIVE_INSTRUMENTATION 1 + +#ifdef ANDROID_INTENSIVE_INSTRUMENTATION +#define ANDROID_PROFILE_COMMAND(category, commandIndex, argbColor, payload, ...) PROFILE_RANGE_EX(category, commandNames[commandIndex], argbColor, payload, ##__VA_ARGS__); +#define ANDROID_PROFILE(category, name, argbColor, payload, ...) PROFILE_RANGE_EX(category, name, argbColor, payload, ##__VA_ARGS__); +#else +#define ANDROID_PROFILE_COMMAND(category, commandIndex, argbColor, payload, ...) +#define ANDROID_PROFILE(category, name, argbColor, payload, ...) +#endif +namespace gpu { namespace gl { + + class GLBackend : public Backend, public std::enable_shared_from_this { + // Context Backend static interface required + friend class gpu::Context; + static void init(); + static BackendPointer createBackend(); + + protected: + explicit GLBackend(bool syncCache); + GLBackend(); + public: + static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); + + ~GLBackend(); + + void setCameraCorrection(const Mat4& correction); + void render(const Batch& batch) final override; + + // This call synchronize the Full Backend cache with the current GLState + // THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync + // the gpu::Backend state with the true gl state which has probably been messed up by these ugly naked gl calls + // Let's try to avoid to do that as much as possible! + void syncCache() final override; + + // This is the ugly "download the pixels to sysmem for taking a snapshot" + // Just avoid using it, it's ugly and will break performances + virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, + const Vec4i& region, QImage& destImage) final override; + + + // this is the maximum numeber of available input buffers + size_t getNumInputBuffers() const { return _input._invalidBuffers.size(); } + + // this is the maximum per shader stage on the low end apple + // TODO make it platform dependant at init time + static const int MAX_NUM_UNIFORM_BUFFERS = 12; + size_t getMaxNumUniformBuffers() const { return MAX_NUM_UNIFORM_BUFFERS; } + + // this is the maximum per shader stage on the low end apple + // TODO make it platform dependant at init time + static const int MAX_NUM_RESOURCE_TEXTURES = 16; + size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; } + + // Draw Stage + virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0; + virtual void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) = 0; + virtual void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) = 0; + + // Input Stage + virtual void do_setInputFormat(const Batch& batch, size_t paramOffset) final; + virtual void do_setInputBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final; + + // Transform Stage + virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final; + + // Uniform Stage + virtual void do_setUniformBuffer(const Batch& batch, size_t paramOffset) final; + + // Resource Stage + virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final; + + // Pipeline Stage + virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final; + + // Output stage + virtual void do_setFramebuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_clearFramebuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_blit(const Batch& batch, size_t paramOffset) = 0; + + // Query section + virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final; + virtual void do_endQuery(const Batch& batch, size_t paramOffset) final; + virtual void do_getQuery(const Batch& batch, size_t paramOffset) final; + + // Reset stages + virtual void do_resetStages(const Batch& batch, size_t paramOffset) final; + + virtual void do_runLambda(const Batch& batch, size_t paramOffset) final; + + virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final; + virtual void do_stopNamedCall(const Batch& batch, size_t paramOffset) final; + + static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; + // The drawcall Info attribute channel is reserved and is the upper bound for the number of availables Input buffers + static const int MAX_NUM_INPUT_BUFFERS = Stream::DRAW_CALL_INFO; + + virtual void do_pushProfileRange(const Batch& batch, size_t paramOffset) final; + virtual void do_popProfileRange(const Batch& batch, size_t paramOffset) final; + + // TODO: As long as we have gl calls explicitely issued from interface + // code, we need to be able to record and batch these calls. THe long + // term strategy is to get rid of any GL calls in favor of the HIFI GPU API + virtual void do_glUniform1i(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform1f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform2f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform3f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform3fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4iv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final; + + virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final; + + // The State setters called by the GLState::Commands when a new state is assigned + virtual void do_setStateFillMode(int32 mode) final; + virtual void do_setStateCullMode(int32 mode) final; + virtual void do_setStateFrontFaceClockwise(bool isClockwise) final; + virtual void do_setStateDepthClampEnable(bool enable) final; + virtual void do_setStateScissorEnable(bool enable) final; + virtual void do_setStateMultisampleEnable(bool enable) final; + virtual void do_setStateAntialiasedLineEnable(bool enable) final; + virtual void do_setStateDepthBias(Vec2 bias) final; + virtual void do_setStateDepthTest(State::DepthTest test) final; + virtual void do_setStateStencil(State::StencilActivation activation, State::StencilTest frontTest, State::StencilTest backTest) final; + virtual void do_setStateAlphaToCoverageEnable(bool enable) final; + virtual void do_setStateSampleMask(uint32 mask) final; + virtual void do_setStateBlend(State::BlendFunction blendFunction) final; + virtual void do_setStateColorWriteMask(uint32 mask) final; + virtual void do_setStateBlendFactor(const Batch& batch, size_t paramOffset) final; + virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final; + + virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; + virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0; + virtual GLuint getBufferID(const Buffer& buffer) = 0; + virtual GLuint getQueryID(const QueryPointer& query) = 0; + virtual bool isTextureReady(const TexturePointer& texture); + + virtual void releaseBuffer(GLuint id, Size size) const; + virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const; + virtual void releaseTexture(GLuint id, Size size) const; + virtual void releaseFramebuffer(GLuint id) const; + virtual void releaseShader(GLuint id) const; + virtual void releaseProgram(GLuint id) const; + virtual void releaseQuery(GLuint id) const; + virtual void queueLambda(const std::function lambda) const; + + bool isTextureManagementSparseEnabled() const override { return (_textureManagement._sparseCapable && Texture::getEnableSparseTextures()); } + + protected: + + void recycle() const override; + virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; + virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; + virtual GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) = 0; + virtual GLQuery* syncGPUObject(const Query& query) = 0; + + static const size_t INVALID_OFFSET = (size_t)-1; + bool _inRenderTransferPass { false }; + int32_t _uboAlignment { 0 }; + int _currentDraw { -1 }; + + std::list profileRanges; + mutable Mutex _trashMutex; + mutable std::list> _buffersTrash; + mutable std::list> _texturesTrash; + mutable std::list> _externalTexturesTrash; + mutable std::list _framebuffersTrash; + mutable std::list _shadersTrash; + mutable std::list _programsTrash; + mutable std::list _queriesTrash; + mutable std::list> _lambdaQueue; + + void renderPassTransfer(const Batch& batch); + void renderPassDraw(const Batch& batch); + void setupStereoSide(int side); + + virtual void initInput() final; + virtual void killInput() final; + virtual void syncInputStateCache() final; + virtual void resetInputStage(); + virtual void updateInput(); + + struct InputStageState { + bool _invalidFormat { true }; + Stream::FormatPointer _format; + std::string _formatKey; + + typedef std::bitset ActivationCache; + ActivationCache _attributeActivation { 0 }; + + typedef std::bitset BuffersState; + + BuffersState _invalidBuffers{ 0 }; + BuffersState _attribBindingBuffers{ 0 }; + + Buffers _buffers; + Offsets _bufferOffsets; + Offsets _bufferStrides; + std::vector _bufferVBOs; + + glm::vec4 _colorAttribute{ 0.0f }; + + BufferPointer _indexBuffer; + Offset _indexBufferOffset { 0 }; + Type _indexBufferType { UINT32 }; + + BufferPointer _indirectBuffer; + Offset _indirectBufferOffset{ 0 }; + Offset _indirectBufferStride{ 0 }; + + GLuint _defaultVAO { 0 }; + + InputStageState() : + _invalidFormat(true), + _format(0), + _formatKey(), + _attributeActivation(0), + _buffers(_invalidBuffers.size(), BufferPointer(0)), + _bufferOffsets(_invalidBuffers.size(), 0), + _bufferStrides(_invalidBuffers.size(), 0), + _bufferVBOs(_invalidBuffers.size(), 0) {} + } _input; + + virtual void initTransform() = 0; + void killTransform(); + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncTransformStateCache(); + void updateTransform(const Batch& batch); + void resetTransformStage(); + + // Allows for correction of the camera pose to account for changes + // between the time when a was recorded and the time(s) when it is + // executed + struct CameraCorrection { + Mat4 correction; + Mat4 correctionInverse; + }; + + struct TransformStageState { +#ifdef GPU_STEREO_CAMERA_BUFFER + struct Cameras { + TransformCamera _cams[2]; + + Cameras() {}; + Cameras(const TransformCamera& cam) { memcpy(_cams, &cam, sizeof(TransformCamera)); }; + Cameras(const TransformCamera& camL, const TransformCamera& camR) { memcpy(_cams, &camL, sizeof(TransformCamera)); memcpy(_cams + 1, &camR, sizeof(TransformCamera)); }; + }; + + using CameraBufferElement = Cameras; +#else + using CameraBufferElement = TransformCamera; +#endif + using TransformCameras = std::vector; + + TransformCamera _camera; + TransformCameras _cameras; + + mutable std::map _drawCallInfoOffsets; + + GLuint _objectBuffer { 0 }; + GLuint _cameraBuffer { 0 }; + GLuint _drawCallInfoBuffer { 0 }; + GLuint _objectBufferTexture { 0 }; + size_t _cameraUboSize { 0 }; + bool _viewIsCamera{ false }; + bool _skybox { false }; + Transform _view; + CameraCorrection _correction; + + Mat4 _projection; + Vec4i _viewport { 0, 0, 1, 1 }; + Vec2 _depthRange { 0.0f, 1.0f }; + bool _invalidView { false }; + bool _invalidProj { false }; + bool _invalidViewport { false }; + + bool _enabledDrawcallInfoBuffer{ false }; + + using Pair = std::pair; + using List = std::list; + List _cameraOffsets; + mutable List::const_iterator _camerasItr; + mutable size_t _currentCameraOffset{ INVALID_OFFSET }; + + void preUpdate(size_t commandIndex, const StereoState& stereo); + void update(size_t commandIndex, const StereoState& stereo) const; + void bindCurrentCamera(int stereoSide) const; + } _transform; + + virtual void transferTransformState(const Batch& batch) const = 0; + + struct UniformStageState { + std::array _buffers; + //Buffers _buffers { }; + } _uniform; + + void releaseUniformBuffer(uint32_t slot); + void resetUniformStage(); + + // update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s + void releaseResourceTexture(uint32_t slot); + + void resetResourceStage(); + + struct ResourceStageState { + std::array _textures; + //Textures _textures { { MAX_NUM_RESOURCE_TEXTURES } }; + int findEmptyTextureSlot() const; + } _resource; + + size_t _commandIndex{ 0 }; + + // Standard update pipeline check that the current Program and current State or good to go for a + void updatePipeline(); + // Force to reset all the state fields indicated by the 'toBeReset" signature + void resetPipelineState(State::Signature toBeReset); + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncPipelineStateCache(); + void resetPipelineStage(); + + struct PipelineStageState { + PipelinePointer _pipeline; + + GLuint _program { 0 }; + GLint _cameraCorrectionLocation { -1 }; + GLShader* _programShader { nullptr }; + bool _invalidProgram { false }; + + BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; + + State::Data _stateCache{ State::DEFAULT }; + State::Signature _stateSignatureCache { 0 }; + + GLState* _state { nullptr }; + bool _invalidState { false }; + + PipelineStageState() { + _cameraCorrectionBuffer.edit() = CameraCorrection(); + } + } _pipeline; + + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncOutputStateCache(); + void resetOutputStage(); + + struct OutputStageState { + FramebufferPointer _framebuffer { nullptr }; + GLuint _drawFBO { 0 }; + } _output; + + void resetQueryStage(); + struct QueryStageState { + uint32_t _rangeQueryDepth { 0 }; + } _queryStage; + + void resetStages(); + + struct TextureManagementStageState { + bool _sparseCapable { false }; + } _textureManagement; + virtual void initTextureManagementStage() {} + + typedef void (GLBackend::*CommandCall)(const Batch&, size_t); + static CommandCall _commandCalls[Batch::NUM_COMMANDS]; + friend class GLState; + friend class GLTexture; + }; + + } } + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp new file mode 100644 index 0000000000..057682584d --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp @@ -0,0 +1,338 @@ +// +// GLBackendInput.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLShared.h" +#include "GLInputFormat.h" + +using namespace gpu; +using namespace gpu::gl; + +void GLBackend::do_setInputFormat(const Batch& batch, size_t paramOffset) { + Stream::FormatPointer format = batch._streamFormats.get(batch._params[paramOffset]._uint); + if (format != _input._format) { + _input._format = format; + if (format) { + auto inputFormat = GLInputFormat::sync((*format)); + assert(inputFormat); + if (_input._formatKey != inputFormat->key) { + _input._formatKey = inputFormat->key; + _input._invalidFormat = true; + } + } else { + _input._formatKey.clear(); + _input._invalidFormat = true; + } + } +} + +void GLBackend::do_setInputBuffer(const Batch& batch, size_t paramOffset) { + Offset stride = batch._params[paramOffset + 0]._uint; + Offset offset = batch._params[paramOffset + 1]._uint; + BufferPointer buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); + uint32 channel = batch._params[paramOffset + 3]._uint; + + if (channel < getNumInputBuffers()) { + bool isModified = false; + if (_input._buffers[channel] != buffer) { + _input._buffers[channel] = buffer; + + GLuint vbo = 0; + if (buffer) { + vbo = getBufferID((*buffer)); + } + _input._bufferVBOs[channel] = vbo; + + isModified = true; + } + + if (_input._bufferOffsets[channel] != offset) { + _input._bufferOffsets[channel] = offset; + isModified = true; + } + + if (_input._bufferStrides[channel] != stride) { + _input._bufferStrides[channel] = stride; + isModified = true; + } + + if (isModified) { + _input._invalidBuffers.set(channel); + } + } +} + +void GLBackend::initInput() { + if(!_input._defaultVAO) { + glGenVertexArrays(1, &_input._defaultVAO); + } + qDebug() << "glBindVertexArray(" << _input._defaultVAO << ")"; + glBindVertexArray(_input._defaultVAO); + (void) CHECK_GL_ERROR(); +} + +void GLBackend::killInput() { + qDebug() << "glBindVertexArray(0)"; + glBindVertexArray(0); + if(_input._defaultVAO) { + glDeleteVertexArrays(1, &_input._defaultVAO); + } + (void) CHECK_GL_ERROR(); +} + +void GLBackend::syncInputStateCache() { + for (uint32_t i = 0; i < _input._attributeActivation.size(); i++) { + GLint active = 0; + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &active); + _input._attributeActivation[i] = active; + } + //_input._defaultVAO + qDebug() << "glBindVertexArray("<<_input._defaultVAO<< ")"; + glBindVertexArray(_input._defaultVAO); +} + +void GLBackend::resetInputStage() { + // Reset index buffer + _input._indexBufferType = UINT32; + _input._indexBufferOffset = 0; + _input._indexBuffer.reset(); + //qDebug() << "GLBackend::resetInputStage glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);"; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + (void) CHECK_GL_ERROR(); + + // Reset vertex buffer and format + _input._format.reset(); + _input._formatKey.clear(); + _input._invalidFormat = false; + _input._attributeActivation.reset(); + + for (uint32_t i = 0; i < _input._buffers.size(); i++) { + _input._buffers[i].reset(); + _input._bufferOffsets[i] = 0; + _input._bufferStrides[i] = 0; + _input._bufferVBOs[i] = 0; + } + _input._invalidBuffers.reset(); + + // THe vertex array binding MUST be reset in the specific Backend versions as they use different techniques +} + +void GLBackend::do_setIndexBuffer(const Batch& batch, size_t paramOffset) { + _input._indexBufferType = (Type)batch._params[paramOffset + 2]._uint; + _input._indexBufferOffset = batch._params[paramOffset + 0]._uint; + + BufferPointer indexBuffer = batch._buffers.get(batch._params[paramOffset + 1]._uint); + if (indexBuffer != _input._indexBuffer) { + _input._indexBuffer = indexBuffer; + if (indexBuffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getBufferID(*indexBuffer)); + } else { + // FIXME do we really need this? Is there ever a draw call where we care that the element buffer is null? + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + } + (void) CHECK_GL_ERROR(); +} + +void GLBackend::do_setIndirectBuffer(const Batch& batch, size_t paramOffset) { + _input._indirectBufferOffset = batch._params[paramOffset + 1]._uint; + _input._indirectBufferStride = batch._params[paramOffset + 2]._uint; + + BufferPointer buffer = batch._buffers.get(batch._params[paramOffset]._uint); + if (buffer != _input._indirectBuffer) { + _input._indirectBuffer = buffer; + if (buffer) { + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, getBufferID(*buffer)); + } else { + // FIXME do we really need this? Is there ever a draw call where we care that the element buffer is null? + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); + } + } + + (void)CHECK_GL_ERROR(); +} + + +// Core 41 doesn't expose the features to really separate the vertex format from the vertex buffers binding +// Core 43 does :) +// FIXME crashing problem with glVertexBindingDivisor / glVertexAttribFormat +// Once resolved, break this up into the GL 4.1 and 4.5 backends +#if 1 || (GPU_INPUT_PROFILE == GPU_CORE_41) +#define NO_SUPPORT_VERTEX_ATTRIB_FORMAT +#else +#define SUPPORT_VERTEX_ATTRIB_FORMAT +#endif + +void GLBackend::updateInput() { +#if defined(SUPPORT_VERTEX_ATTRIB_FORMAT) + if (_input._invalidFormat) { + + InputStageState::ActivationCache newActivation; + + // Assign the vertex format required + if (_input._format) { + for (auto& it : _input._format->getAttributes()) { + const Stream::Attribute& attrib = (it).second; + + GLuint slot = attrib._slot; + GLuint count = attrib._element.getLocationScalarCount(); + uint8_t locationCount = attrib._element.getLocationCount(); + GLenum type = _elementTypeToGL41Type[attrib._element.getType()]; + GLuint offset = attrib._offset;; + GLboolean isNormalized = attrib._element.isNormalized(); + + GLenum perLocationSize = attrib._element.getLocationSize(); + + for (size_t locNum = 0; locNum < locationCount; ++locNum) { + newActivation.set(slot + locNum); + glVertexAttribFormat(slot + locNum, count, type, isNormalized, offset + locNum * perLocationSize); + glVertexAttribBinding(slot + locNum, attrib._channel); + } + glVertexBindingDivisor(attrib._channel, attrib._frequency); + } + (void)CHECK_GL_ERROR(); + } + + // Manage Activation what was and what is expected now + for (size_t i = 0; i < newActivation.size(); i++) { + bool newState = newActivation[i]; + if (newState != _input._attributeActivation[i]) { + if (newState) { + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + } + _input._attributeActivation.flip(i); + } + } + (void)CHECK_GL_ERROR(); + + _input._invalidFormat = false; + _stats._ISNumFormatChanges++; + } + + if (_input._invalidBuffers.any()) { + int numBuffers = _input._buffers.size(); + auto buffer = _input._buffers.data(); + auto vbo = _input._bufferVBOs.data(); + auto offset = _input._bufferOffsets.data(); + auto stride = _input._bufferStrides.data(); + + for (int bufferNum = 0; bufferNum < numBuffers; bufferNum++) { + if (_input._invalidBuffers.test(bufferNum)) { + glBindVertexBuffer(bufferNum, (*vbo), (*offset), (*stride)); + } + buffer++; + vbo++; + offset++; + stride++; + } + _input._invalidBuffers.reset(); + (void)CHECK_GL_ERROR(); + } +#else + if (_input._invalidFormat || _input._invalidBuffers.any()) { + + if (_input._invalidFormat) { + InputStageState::ActivationCache newActivation; + + _stats._ISNumFormatChanges++; + + // Check expected activation + if (_input._format) { + for (auto& it : _input._format->getAttributes()) { + const Stream::Attribute& attrib = (it).second; + uint8_t locationCount = attrib._element.getLocationCount(); + for (int i = 0; i < locationCount; ++i) { + newActivation.set(attrib._slot + i); + } + } + } + + // Manage Activation what was and what is expected now + for (unsigned int i = 0; i < newActivation.size(); i++) { + bool newState = newActivation[i]; + if (newState != _input._attributeActivation[i]) { + + if (newState) { + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + } + (void)CHECK_GL_ERROR(); + + _input._attributeActivation.flip(i); + } + } + } + + // now we need to bind the buffers and assign the attrib pointers + if (_input._format) { + const Buffers& buffers = _input._buffers; + const Offsets& offsets = _input._bufferOffsets; + const Offsets& strides = _input._bufferStrides; + + const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); + auto& inputChannels = _input._format->getChannels(); + _stats._ISNumInputBufferChanges++; + + GLuint boundVBO = 0; + for (auto& channelIt : inputChannels) { + const Stream::Format::ChannelMap::value_type::second_type& channel = (channelIt).second; + if ((channelIt).first < buffers.size()) { + int bufferNum = (channelIt).first; + + if (_input._invalidBuffers.test(bufferNum) || _input._invalidFormat) { + // GLuint vbo = gpu::GL41Backend::getBufferID((*buffers[bufferNum])); + GLuint vbo = _input._bufferVBOs[bufferNum]; + if (boundVBO != vbo) { + //qDebug() << "GLBackend::updateInput glBindBuffer(GL_ARRAY_BUFFER, " << vbo <<")"; + glBindBuffer(GL_ARRAY_BUFFER, vbo); + (void)CHECK_GL_ERROR(); + boundVBO = vbo; + } + _input._invalidBuffers[bufferNum] = false; + + for (unsigned int i = 0; i < channel._slots.size(); i++) { + const Stream::Attribute& attrib = attributes.at(channel._slots[i]); + GLuint slot = attrib._slot; + GLuint count = attrib._element.getLocationScalarCount(); + uint8_t locationCount = attrib._element.getLocationCount(); + GLenum type = gl::ELEMENT_TYPE_TO_GL[attrib._element.getType()]; + // GLenum perLocationStride = strides[bufferNum]; + GLenum perLocationStride = attrib._element.getLocationSize(); + GLuint stride = (GLuint)strides[bufferNum]; + GLuint pointer = (GLuint)(attrib._offset + offsets[bufferNum]); + GLboolean isNormalized = attrib._element.isNormalized(); + + for (size_t locNum = 0; locNum < locationCount; ++locNum) { + glVertexAttribPointer(slot + (GLuint)locNum, count, type, isNormalized, stride, + reinterpret_cast(pointer + perLocationStride * (GLuint)locNum)); +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency * (isStereo() ? 2 : 1)); +#else + glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency); +#endif + } + + // TODO: Support properly the IAttrib version + + (void)CHECK_GL_ERROR(); + } + } + } + } + } + // everything format related should be in sync now + _input._invalidFormat = false; + } +#endif +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp new file mode 100644 index 0000000000..6fddb810ee --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp @@ -0,0 +1,169 @@ +// +// GLBackendTexture.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 1/19/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLShared.h" +#include "GLFramebuffer.h" + +#include + +using namespace gpu; +using namespace gpu::gl; + +void GLBackend::syncOutputStateCache() { + GLint currentFBO; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBO); + + _output._drawFBO = currentFBO; + _output._framebuffer.reset(); +} + +void GLBackend::resetOutputStage() { + if (_output._framebuffer) { + _output._framebuffer.reset(); + _output._drawFBO = 0; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + } + + glEnable(GL_FRAMEBUFFER_SRGB_EXT); +} + +void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) { + auto framebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); + if (_output._framebuffer != framebuffer) { + auto newFBO = getFramebufferID(framebuffer); + if (_output._drawFBO != newFBO) { + _output._drawFBO = newFBO; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, newFBO); + } + _output._framebuffer = framebuffer; + } +} + +void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { + if (_stereo._enable && !_pipeline._stateCache.scissorEnable) { + //qWarning("Clear without scissor in stereo mode"); + } + + uint32 masks = batch._params[paramOffset + 7]._uint; + Vec4 color; + color.x = batch._params[paramOffset + 6]._float; + color.y = batch._params[paramOffset + 5]._float; + color.z = batch._params[paramOffset + 4]._float; + color.w = batch._params[paramOffset + 3]._float; + float depth = batch._params[paramOffset + 2]._float; + int stencil = batch._params[paramOffset + 1]._int; + int useScissor = batch._params[paramOffset + 0]._int; + + GLuint glmask = 0; + if (masks & Framebuffer::BUFFER_STENCIL) { + glClearStencil(stencil); + glmask |= GL_STENCIL_BUFFER_BIT; + // TODO: we will probably need to also check the write mask of stencil like we do + // for depth buffer, but as would say a famous Fez owner "We'll cross that bridge when we come to it" + } + + bool restoreDepthMask = false; + if (masks & Framebuffer::BUFFER_DEPTH) { + glClearDepthf(depth); + + glmask |= GL_DEPTH_BUFFER_BIT; + + bool cacheDepthMask = _pipeline._stateCache.depthTest.getWriteMask(); + if (!cacheDepthMask) { + restoreDepthMask = true; + glDepthMask(GL_TRUE); + } + } + + std::vector drawBuffers; + if (masks & Framebuffer::BUFFER_COLORS) { + if (_output._framebuffer) { + for (unsigned int i = 0; i < Framebuffer::MAX_NUM_RENDER_BUFFERS; i++) { + if (masks & (1 << i)) { + drawBuffers.push_back(GL_COLOR_ATTACHMENT0 + i); + } + } + + if (!drawBuffers.empty()) { + glDrawBuffers((GLsizei)drawBuffers.size(), drawBuffers.data()); + glClearColor(color.x, color.y, color.z, color.w); + glmask |= GL_COLOR_BUFFER_BIT; + + (void) CHECK_GL_ERROR(); + } + } else { + glClearColor(color.x, color.y, color.z, color.w); + glmask |= GL_COLOR_BUFFER_BIT; + } + + // Force the color mask cache to WRITE_ALL if not the case + do_setStateColorWriteMask(State::ColorMask::WRITE_ALL); + } + + // Apply scissor if needed and if not already on + bool doEnableScissor = (useScissor && (!_pipeline._stateCache.scissorEnable)); + if (doEnableScissor) { + glEnable(GL_SCISSOR_TEST); + } + + // Clear! + glClear(glmask); + + // Restore scissor if needed + if (doEnableScissor) { + glDisable(GL_SCISSOR_TEST); + } + + // Restore write mask meaning turn back off + if (restoreDepthMask) { + glDepthMask(GL_FALSE); + } + + // Restore the color draw buffers only if a frmaebuffer is bound + if (_output._framebuffer && !drawBuffers.empty()) { + auto glFramebuffer = syncGPUObject(*_output._framebuffer); + if (glFramebuffer) { + glDrawBuffers((GLsizei)glFramebuffer->_colorBuffers.size(), glFramebuffer->_colorBuffers.data()); + } + } + + (void) CHECK_GL_ERROR(); +} + +void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) { + auto readFBO = getFramebufferID(srcFramebuffer); + if (srcFramebuffer && readFBO) { + if ((srcFramebuffer->getWidth() < (region.x + region.z)) || (srcFramebuffer->getHeight() < (region.y + region.w))) { + qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : srcFramebuffer is too small to provide the region queried"; + return; + } + } + + if ((destImage.width() < region.z) || (destImage.height() < region.w)) { + qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : destImage is too small to receive the region of the framebuffer"; + return; + } + + GLenum format = GL_RGBA; + //GLenum format = GL_BGRA; + qDebug() << "TODO: GLBackendOutput.cpp:do_clearFramebuffer GL_BGRA"; + + if (destImage.format() != QImage::Format_ARGB32) { + qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : destImage format must be FORMAT_ARGB32 to receive the region of the framebuffer"; + return; + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, getFramebufferID(srcFramebuffer)); + glReadPixels(region.x, region.y, region.z, region.w, format, GL_UNSIGNED_BYTE, destImage.bits()); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + (void) CHECK_GL_ERROR(); +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp new file mode 100644 index 0000000000..c35966d440 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp @@ -0,0 +1,250 @@ +// +// GLBackendPipeline.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLShared.h" +#include "GLPipeline.h" +#include "GLShader.h" +#include "GLState.h" +#include "GLBuffer.h" +#include "GLTexture.h" + +using namespace gpu; +using namespace gpu::gl; + +void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { + PipelinePointer pipeline = batch._pipelines.get(batch._params[paramOffset + 0]._uint); + + if (_pipeline._pipeline == pipeline) { + return; + } + + // A true new Pipeline + _stats._PSNumSetPipelines++; + + // null pipeline == reset + if (!pipeline) { + qDebug() << " null pipeline"; + _pipeline._pipeline.reset(); + + _pipeline._program = 0; + _pipeline._cameraCorrectionLocation = -1; + _pipeline._programShader = nullptr; + _pipeline._invalidProgram = true; + + _pipeline._state = nullptr; + _pipeline._invalidState = true; + } else { + auto pipelineObject = GLPipeline::sync(*this, *pipeline); + if (!pipelineObject) { + return; + } + + // check the program cache + // pick the program version + // check the program cache + // pick the program version +#ifdef GPU_STEREO_CAMERA_BUFFER + GLuint glprogram = pipelineObject->_program->getProgram((GLShader::Version) isStereo()); +#else + GLuint glprogram = pipelineObject->_program->getProgram(); +#endif + + if (_pipeline._program != glprogram) { + _pipeline._program = glprogram; + _pipeline._programShader = pipelineObject->_program; + _pipeline._invalidProgram = true; + _pipeline._cameraCorrectionLocation = pipelineObject->_cameraCorrection; + } + + // Now for the state + if (_pipeline._state != pipelineObject->_state) { + _pipeline._state = pipelineObject->_state; + _pipeline._invalidState = true; + } + + // Remember the new pipeline + _pipeline._pipeline = pipeline; + } + + // THis should be done on Pipeline::update... + if (_pipeline._invalidProgram) { + glUseProgram(_pipeline._program); + if (_pipeline._cameraCorrectionLocation != -1) { + auto cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer); + glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection)); + } + (void) CHECK_GL_ERROR(); + _pipeline._invalidProgram = false; + } +} + +void GLBackend::updatePipeline() { + if (_pipeline._invalidProgram) { + // doing it here is aproblem for calls to glUniform.... so will do it on assing... + glUseProgram(_pipeline._program); + (void) CHECK_GL_ERROR(); + _pipeline._invalidProgram = false; + } + + if (_pipeline._invalidState) { + if (_pipeline._state) { + // first reset to default what should be + // the fields which were not to default and are default now + resetPipelineState(_pipeline._state->_signature); + + // Update the signature cache with what's going to be touched + _pipeline._stateSignatureCache |= _pipeline._state->_signature; + + // And perform + for (auto command: _pipeline._state->_commands) { + command->run(this); + } + } else { + // No state ? anyway just reset everything + resetPipelineState(0); + } + _pipeline._invalidState = false; + } +} + +void GLBackend::resetPipelineStage() { + // First reset State to default + State::Signature resetSignature(0); + resetPipelineState(resetSignature); + _pipeline._state = nullptr; + _pipeline._invalidState = false; + + // Second the shader side + _pipeline._invalidProgram = false; + _pipeline._program = 0; + _pipeline._programShader = nullptr; + _pipeline._pipeline.reset(); + glUseProgram(0); +} + +void GLBackend::releaseUniformBuffer(uint32_t slot) { + auto& buf = _uniform._buffers[slot]; + if (buf) { + auto* object = Backend::getGPUObject(*buf); + if (object) { + glBindBufferBase(GL_UNIFORM_BUFFER, slot, 0); // RELEASE + (void) CHECK_GL_ERROR(); + } + buf.reset(); + } +} + +void GLBackend::resetUniformStage() { + for (uint32_t i = 0; i < _uniform._buffers.size(); i++) { + releaseUniformBuffer(i); + } +} + +void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { + GLuint slot = batch._params[paramOffset + 3]._uint; + BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); + GLintptr rangeStart = batch._params[paramOffset + 1]._uint; + GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; + + if (!uniformBuffer) { + releaseUniformBuffer(slot); + return; + } + + // check cache before thinking + if (_uniform._buffers[slot] == uniformBuffer) { + return; + } + + // Sync BufferObject + auto* object = syncGPUObject(*uniformBuffer); + if (object) { + glBindBufferRange(GL_UNIFORM_BUFFER, slot, object->_buffer, rangeStart, rangeSize); + + _uniform._buffers[slot] = uniformBuffer; + (void) CHECK_GL_ERROR(); + } else { + releaseUniformBuffer(slot); + return; + } +} + +void GLBackend::releaseResourceTexture(uint32_t slot) { + auto& tex = _resource._textures[slot]; + if (tex) { + auto* object = Backend::getGPUObject(*tex); + if (object) { + GLuint target = object->_target; + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(target, 0); // RELEASE + (void) CHECK_GL_ERROR(); + } + tex.reset(); + } +} + +void GLBackend::resetResourceStage() { + for (uint32_t i = 0; i < _resource._textures.size(); i++) { + releaseResourceTexture(i); + } +} + +void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { + GLuint slot = batch._params[paramOffset + 1]._uint; + if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) { + // "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" + slot + " which doesn't exist. MaxNumResourceTextures = " + getMaxNumResourceTextures()); + return; + } + + TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + + if (!resourceTexture) { + releaseResourceTexture(slot); + return; + } + // check cache before thinking + if (_resource._textures[slot] == resourceTexture) { + return; + } + + // One more True texture bound + _stats._RSNumTextureBounded++; + + // Always make sure the GLObject is in sync + GLTexture* object = syncGPUObject(resourceTexture); + if (object) { + GLuint to = object->_texture; + GLuint target = object->_target; + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(target, to); + + (void) CHECK_GL_ERROR(); + + _resource._textures[slot] = resourceTexture; + + _stats._RSAmountTextureMemoryBounded += object->size(); + + } else { + releaseResourceTexture(slot); + return; + } +} + +int GLBackend::ResourceStageState::findEmptyTextureSlot() const { + // start from the end of the slots, try to find an empty one that can be used + for (auto i = MAX_NUM_RESOURCE_TEXTURES - 1; i > 0; i--) { + if (!_textures[i]) { + return i; + } + } + return -1; +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp new file mode 100644 index 0000000000..530e01d8ff --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp @@ -0,0 +1,93 @@ +// +// GLBackendQuery.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 7/7/2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLQuery.h" +#include "GLShared.h" + +using namespace gpu; +using namespace gpu::gl; + +// Eventually, we want to test with TIME_ELAPSED instead of TIMESTAMP +#ifdef Q_OS_MAC +const uint32_t MAX_RANGE_QUERY_DEPTH = 1; +static bool timeElapsed = true; +#else +const uint32_t MAX_RANGE_QUERY_DEPTH = 10000; +static bool timeElapsed = false; +#endif + +void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { + auto query = batch._queries.get(batch._params[paramOffset]._uint); + GLQuery* glquery = syncGPUObject(*query); + if (glquery) { + //glGetInteger64v(GL_TIMESTAMP_EXT, (GLint64*)&glquery->_batchElapsedTime); + glquery->_batchElapsedTime = 1; + if (timeElapsed) { + glBeginQuery(GL_TIME_ELAPSED_EXT, glquery->_endqo); + } else { + if (glQueryCounterEXT != NULL) { + glQueryCounterEXT(glquery->_beginqo, GL_TIMESTAMP_EXT); + } + } + glquery->_rangeQueryDepth = _queryStage._rangeQueryDepth; + (void)CHECK_GL_ERROR(); + } +} + +void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { + auto query = batch._queries.get(batch._params[paramOffset]._uint); + GLQuery* glquery = syncGPUObject(*query); + if (glquery) { + if (timeElapsed) { + glEndQuery(GL_TIME_ELAPSED_EXT); + } else { + if (glQueryCounterEXT != NULL) { + glQueryCounterEXT(glquery->_endqo, GL_TIMESTAMP_EXT); + } + } + + --_queryStage._rangeQueryDepth; + GLint64 now; + //glGetInteger64v(GL_TIMESTAMP_EXT, &now); + //glquery->_batchElapsedTime = now - glquery->_batchElapsedTime; + now = 1; + glquery->_batchElapsedTime = 1; + + PROFILE_RANGE_END(render_gpu_gl, glquery->_profileRangeId); + + (void)CHECK_GL_ERROR(); + } +} + +void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { + auto query = batch._queries.get(batch._params[paramOffset]._uint); + if (glGetQueryObjectui64vEXT == NULL) + return; + GLQuery* glquery = syncGPUObject(*query); + if (glquery) { + glGetQueryObjectui64vEXT(glquery->_endqo, GL_QUERY_RESULT_AVAILABLE, &glquery->_result); + if (glquery->_result == GL_TRUE) { + if (timeElapsed) { + glGetQueryObjectui64vEXT(glquery->_endqo, GL_QUERY_RESULT, &glquery->_result); + } else { + GLuint64 start, end; + glGetQueryObjectui64vEXT(glquery->_beginqo, GL_QUERY_RESULT, &start); + glGetQueryObjectui64vEXT(glquery->_endqo, GL_QUERY_RESULT, &end); + glquery->_result = end - start; + } + query->triggerReturnHandler(glquery->_result, glquery->_batchElapsedTime); + } + (void)CHECK_GL_ERROR(); + } +} + +void GLBackend::resetQueryStage() { +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp new file mode 100644 index 0000000000..27b8d23bf3 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp @@ -0,0 +1,334 @@ +// +// GLBackendState.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/22/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLState.h" + +using namespace gpu; +using namespace gpu::gl; + +void GLBackend::resetPipelineState(State::Signature nextSignature) { + auto currentNotSignature = ~_pipeline._stateSignatureCache; + auto nextNotSignature = ~nextSignature; + auto fieldsToBeReset = currentNotSignature ^ (currentNotSignature | nextNotSignature); + if (fieldsToBeReset.any()) { + for (auto i = 0; i < State::NUM_FIELDS; i++) { + if (fieldsToBeReset[i]) { + GLState::_resetStateCommands[i]->run(this); + _pipeline._stateSignatureCache.reset(i); + } + } + } +} + +void GLBackend::syncPipelineStateCache() { + State::Data state; + + //glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + qDebug() << "TODO: GLBackendState.cpp:syncPipelineStateCache GL_TEXTURE_CUBE_MAP_SEAMLESS"; + + // Point size is always on + // FIXME CORE + //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + //glEnable(GL_PROGRAM_POINT_SIZE_EXT); + qDebug() << "TODO: GLBackendState.cpp:syncPipelineStateCache GL_PROGRAM_POINT_SIZE_EXT"; + + //glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + qDebug() << "TODO: GLBackendState.cpp:syncPipelineStateCache GL_VERTEX_PROGRAM_POINT_SIZE"; + + // Default line width accross the board + glLineWidth(1.0f); + + getCurrentGLState(state); + State::Signature signature = State::evalSignature(state); + + _pipeline._stateCache = state; + _pipeline._stateSignatureCache = signature; +} + + +void GLBackend::do_setStateFillMode(int32 mode) { + if (_pipeline._stateCache.fillMode != mode) { + static GLenum GL_FILL_MODES[] = { /*GL_POINT, GL_LINE, GL_FILL*/ }; + qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_POINT"; + qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_LINE"; + qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_FILL"; + //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL_MODES[mode]); + qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode glPolygonMode"; + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.fillMode = State::FillMode(mode); + } +} + +void GLBackend::do_setStateCullMode(int32 mode) { + if (_pipeline._stateCache.cullMode != mode) { + static GLenum GL_CULL_MODES[] = { GL_FRONT_AND_BACK, GL_FRONT, GL_BACK }; + if (mode == State::CULL_NONE) { + glDisable(GL_CULL_FACE); + glCullFace(GL_FRONT_AND_BACK); + } else { + glEnable(GL_CULL_FACE); + glCullFace(GL_CULL_MODES[mode]); + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.cullMode = State::CullMode(mode); + } +} + +void GLBackend::do_setStateFrontFaceClockwise(bool isClockwise) { + if (_pipeline._stateCache.frontFaceClockwise != isClockwise) { + static GLenum GL_FRONT_FACES[] = { GL_CCW, GL_CW }; + glFrontFace(GL_FRONT_FACES[isClockwise]); + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.frontFaceClockwise = isClockwise; + } +} + +void GLBackend::do_setStateDepthClampEnable(bool enable) { + if (_pipeline._stateCache.depthClampEnable != enable) { + if (enable) { + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthClampEnable GL_DEPTH_CLAMP"; + //glEnable(GL_DEPTH_CLAMP); + } else { + //glDisable(GL_DEPTH_CLAMP); + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthClampEnable GL_DEPTH_CLAMP"; + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.depthClampEnable = enable; + } +} + +void GLBackend::do_setStateScissorEnable(bool enable) { + if (_pipeline._stateCache.scissorEnable != enable) { + if (enable) { + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.scissorEnable = enable; + } +} + +void GLBackend::do_setStateMultisampleEnable(bool enable) { + if (_pipeline._stateCache.multisampleEnable != enable) { + if (enable) { + //glEnable(GL_MULTISAMPLE); + qDebug() << "TODO: GLBackendState.cpp:do_setStateMultisampleEnable GL_MULTISAMPLE"; + } else { + //glDisable(GL_MULTISAMPLE); + qDebug() << "TODO: GLBackendState.cpp:do_setStateMultisampleEnable GL_MULTISAMPLE"; + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.multisampleEnable = enable; + } +} + +void GLBackend::do_setStateAntialiasedLineEnable(bool enable) { + if (_pipeline._stateCache.antialisedLineEnable != enable) { + if (enable) { + //glEnable(GL_LINE_SMOOTH); + qDebug() << "TODO: GLBackendState.cpp:do_setStateAntialiasedLineEnable GL_LINE_SMOOTH"; + } else { + //glDisable(GL_LINE_SMOOTH); + qDebug() << "TODO: GLBackendState.cpp:do_setStateAntialiasedLineEnable GL_LINE_SMOOTH"; + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.antialisedLineEnable = enable; + } +} + +void GLBackend::do_setStateDepthBias(Vec2 bias) { + if ((bias.x != _pipeline._stateCache.depthBias) || (bias.y != _pipeline._stateCache.depthBiasSlopeScale)) { + if ((bias.x != 0.0f) || (bias.y != 0.0f)) { + glEnable(GL_POLYGON_OFFSET_FILL); + //glEnable(GL_POLYGON_OFFSET_LINE); + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_LINE"; + //glEnable(GL_POLYGON_OFFSET_POINT); + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_POINT"; + glPolygonOffset(bias.x, bias.y); + } else { + glDisable(GL_POLYGON_OFFSET_FILL); + //glDisable(GL_POLYGON_OFFSET_LINE); + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_LINE"; + //glDisable(GL_POLYGON_OFFSET_POINT); + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_POINT"; + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.depthBias = bias.x; + _pipeline._stateCache.depthBiasSlopeScale = bias.y; + } +} + +void GLBackend::do_setStateDepthTest(State::DepthTest test) { + const auto& current = _pipeline._stateCache.depthTest; + if (current != test) { + if (test.isEnabled()) { + glEnable(GL_DEPTH_TEST); + } else { + glDisable(GL_DEPTH_TEST); + } + if (test.getWriteMask() != current.getWriteMask()) { + glDepthMask(test.getWriteMask()); + } + if (test.getFunction() != current.getFunction()) { + glDepthFunc(COMPARISON_TO_GL[test.getFunction()]); + } + if (CHECK_GL_ERROR()) { + qDebug() << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled") + << "Mask=" << (test.getWriteMask() ? "Write" : "no Write") + << "Func=" << test.getFunction() + << "Raw=" << test.getRaw(); + } + _pipeline._stateCache.depthTest = test; + } +} + +void GLBackend::do_setStateStencil(State::StencilActivation activation, State::StencilTest testFront, State::StencilTest testBack) { + const auto& currentActivation = _pipeline._stateCache.stencilActivation; + const auto& currentTestFront = _pipeline._stateCache.stencilTestFront; + const auto& currentTestBack = _pipeline._stateCache.stencilTestBack; + if ((currentActivation != activation) + || (currentTestFront != testFront) + || (currentTestBack != testBack)) { + + if (activation.isEnabled()) { + glEnable(GL_STENCIL_TEST); + } else { + glDisable(GL_STENCIL_TEST); + } + + if (activation.getWriteMaskFront() != activation.getWriteMaskBack()) { + glStencilMaskSeparate(GL_FRONT, activation.getWriteMaskFront()); + glStencilMaskSeparate(GL_BACK, activation.getWriteMaskBack()); + } else { + glStencilMask(activation.getWriteMaskFront()); + } + + static GLenum STENCIL_OPS[State::NUM_STENCIL_OPS] = { + GL_KEEP, + GL_ZERO, + GL_REPLACE, + GL_INCR_WRAP, + GL_DECR_WRAP, + GL_INVERT, + GL_INCR, + GL_DECR }; + + if (testFront != testBack) { + glStencilOpSeparate(GL_FRONT, STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getDepthFailOp()], STENCIL_OPS[testFront.getPassOp()]); + glStencilFuncSeparate(GL_FRONT, COMPARISON_TO_GL[testFront.getFunction()], testFront.getReference(), testFront.getReadMask()); + + glStencilOpSeparate(GL_BACK, STENCIL_OPS[testBack.getFailOp()], STENCIL_OPS[testBack.getDepthFailOp()], STENCIL_OPS[testBack.getPassOp()]); + glStencilFuncSeparate(GL_BACK, COMPARISON_TO_GL[testBack.getFunction()], testBack.getReference(), testBack.getReadMask()); + } else { + glStencilOp(STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getDepthFailOp()], STENCIL_OPS[testFront.getPassOp()]); + glStencilFunc(COMPARISON_TO_GL[testFront.getFunction()], testFront.getReference(), testFront.getReadMask()); + } + + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.stencilActivation = activation; + _pipeline._stateCache.stencilTestFront = testFront; + _pipeline._stateCache.stencilTestBack = testBack; + } +} + +void GLBackend::do_setStateAlphaToCoverageEnable(bool enable) { + if (_pipeline._stateCache.alphaToCoverageEnable != enable) { + if (enable) { + glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } else { + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.alphaToCoverageEnable = enable; + } +} + +void GLBackend::do_setStateSampleMask(uint32 mask) { + if (_pipeline._stateCache.sampleMask != mask) { + if (mask == 0xFFFFFFFF) { + glDisable(GL_SAMPLE_MASK); + } else { + glEnable(GL_SAMPLE_MASK); + glSampleMaski(0, mask); + } + (void)CHECK_GL_ERROR(); + _pipeline._stateCache.sampleMask = mask; + } +} + +void GLBackend::do_setStateBlend(State::BlendFunction function) { + if (_pipeline._stateCache.blendFunction != function) { + if (function.isEnabled()) { + glEnable(GL_BLEND); + + glBlendEquationSeparate(BLEND_OPS_TO_GL[function.getOperationColor()], BLEND_OPS_TO_GL[function.getOperationAlpha()]); + (void)CHECK_GL_ERROR(); + + + glBlendFuncSeparate(BLEND_ARGS_TO_GL[function.getSourceColor()], BLEND_ARGS_TO_GL[function.getDestinationColor()], + BLEND_ARGS_TO_GL[function.getSourceAlpha()], BLEND_ARGS_TO_GL[function.getDestinationAlpha()]); + } else { + glDisable(GL_BLEND); + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.blendFunction = function; + } +} + +void GLBackend::do_setStateColorWriteMask(uint32 mask) { + if (_pipeline._stateCache.colorWriteMask != mask) { + glColorMask(mask & State::ColorMask::WRITE_RED, + mask & State::ColorMask::WRITE_GREEN, + mask & State::ColorMask::WRITE_BLUE, + mask & State::ColorMask::WRITE_ALPHA); + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.colorWriteMask = mask; + } +} + + +void GLBackend::do_setStateBlendFactor(const Batch& batch, size_t paramOffset) { + Vec4 factor(batch._params[paramOffset + 0]._float, + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 2]._float, + batch._params[paramOffset + 3]._float); + + glBlendColor(factor.x, factor.y, factor.z, factor.w); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_setStateScissorRect(const Batch& batch, size_t paramOffset) { + Vec4i rect; + memcpy(&rect, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i)); + + if (_stereo._enable) { + rect.z /= 2; + if (_stereo._pass) { + rect.x += rect.z; + } + } + glScissor(rect.x, rect.y, rect.z, rect.w); + (void)CHECK_GL_ERROR(); +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp new file mode 100644 index 0000000000..4be7682a4f --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp @@ -0,0 +1,40 @@ +// +// +// GLBackendTexture.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 1/19/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLTexture.h" + +using namespace gpu; +using namespace gpu::gl; + +bool GLBackend::isTextureReady(const TexturePointer& texture) { + // DO not transfer the texture, this call is expected for rendering texture + GLTexture* object = syncGPUObject(texture, true); + qDebug() << "GLBackendTexture isTextureReady syncGPUObject"; + return object && object->isReady(); +} + + +void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { + TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + if (!resourceTexture) { + return; + } + + // DO not transfer the texture, this call is expected for rendering texture + GLTexture* object = syncGPUObject(resourceTexture, false); + qDebug() << "GLBackendTexture do_generateTextureMips syncGPUObject"; + if (!object) { + return; + } + + object->generateMips(); +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp new file mode 100644 index 0000000000..3068e24dac --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp @@ -0,0 +1,212 @@ +// +// GLBackendTransform.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +// Transform Stage +void GLBackend::do_setModelTransform(const Batch& batch, size_t paramOffset) { + qDebug() << "do_setModelTransform"; +} + +void GLBackend::do_setViewTransform(const Batch& batch, size_t paramOffset) { + _transform._view = batch._transforms.get(batch._params[paramOffset]._uint); + _transform._viewIsCamera = batch._params[paramOffset + 1]._uint != 0; + _transform._invalidView = true; +} + +void GLBackend::do_setProjectionTransform(const Batch& batch, size_t paramOffset) { + memcpy(&_transform._projection, batch.readData(batch._params[paramOffset]._uint), sizeof(Mat4)); + _transform._invalidProj = true; +} + +void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) { + memcpy(&_transform._viewport, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i)); + +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + { + ivec4& vp = _transform._viewport; + glViewport(vp.x, vp.y, vp.z, vp.w); + + // Where we assign the GL viewport + if (_stereo._enable) { + vp.z /= 2; + if (_stereo._pass) { + vp.x += vp.z; + } + } + } +#else + if (!_inRenderTransferPass && !isStereo()) { + ivec4& vp = _transform._viewport; + glViewport(vp.x, vp.y, vp.z, vp.w); + } +#endif + + // The Viewport is tagged invalid because the CameraTransformUBO is not up to date and will need update on next drawcall + _transform._invalidViewport = true; +} + +void GLBackend::do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) { + + Vec2 depthRange(batch._params[paramOffset + 1]._float, batch._params[paramOffset + 0]._float); + + if ((depthRange.x != _transform._depthRange.x) || (depthRange.y != _transform._depthRange.y)) { + _transform._depthRange = depthRange; + + glDepthRangef(depthRange.x, depthRange.y); + } +} + +void GLBackend::killTransform() { + glDeleteBuffers(1, &_transform._objectBuffer); + glDeleteBuffers(1, &_transform._cameraBuffer); + glDeleteBuffers(1, &_transform._drawCallInfoBuffer); + glDeleteTextures(1, &_transform._objectBufferTexture); +} + +void GLBackend::syncTransformStateCache() { + _transform._invalidViewport = true; + _transform._invalidProj = true; + _transform._invalidView = true; + + glGetIntegerv(GL_VIEWPORT, (GLint*) &_transform._viewport); + + glGetFloatv(GL_DEPTH_RANGE, (GLfloat*)&_transform._depthRange); + + Mat4 modelView; + auto modelViewInv = glm::inverse(modelView); + _transform._view.evalFromRawMatrix(modelViewInv); + + glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); + _transform._enabledDrawcallInfoBuffer = false; +} + +void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo) { + // Check all the dirty flags and update the state accordingly + if (_invalidViewport) { + _camera._viewport = glm::vec4(_viewport); + } + + if (_invalidProj) { + _camera._projection = _projection; + } + + if (_invalidView) { + // Apply the correction + if (_viewIsCamera && _correction.correction != glm::mat4()) { + // FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out? + Transform result; + _view.mult(result, _view, _correction.correction); + if (_skybox) { + result.setTranslation(vec3()); + } + _view = result; + } + // This is when the _view matrix gets assigned + _view.getInverseMatrix(_camera._view); + } + + if (_invalidView || _invalidProj || _invalidViewport) { + size_t offset = _cameraUboSize * _cameras.size(); + _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); + + if (stereo._enable) { +#ifdef GPU_STEREO_CAMERA_BUFFER + _cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view))); +#else + _cameras.push_back((_camera.getEyeCamera(0, stereo, _view))); + _cameras.push_back((_camera.getEyeCamera(1, stereo, _view))); +#endif + } else { +#ifdef GPU_STEREO_CAMERA_BUFFER + _cameras.push_back(CameraBufferElement(_camera.recomputeDerived(_view))); +#else + _cameras.push_back((_camera.recomputeDerived(_view))); +#endif + } + } + + // Flags are clean + _invalidView = _invalidProj = _invalidViewport = false; +} + +void GLBackend::TransformStageState::update(size_t commandIndex, const StereoState& stereo) const { + size_t offset = INVALID_OFFSET; + while ((_camerasItr != _cameraOffsets.end()) && (commandIndex >= (*_camerasItr).first)) { + offset = (*_camerasItr).second; + _currentCameraOffset = offset; + ++_camerasItr; + } + + if (offset != INVALID_OFFSET) { +#ifdef GPU_STEREO_CAMERA_BUFFER + bindCurrentCamera(0); +#else + if (!stereo._enable) { + bindCurrentCamera(0); + } +#endif + } + (void)CHECK_GL_ERROR(); +} + +void GLBackend::TransformStageState::bindCurrentCamera(int eye) const { + if (_currentCameraOffset != INVALID_OFFSET) { + //qDebug() << "GLBackend::TransformStageState::bindCurrentCamera"; + glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _cameraBuffer, _currentCameraOffset + eye * _cameraUboSize, sizeof(CameraBufferElement)); + } +} + +void GLBackend::updateTransform(const Batch& batch) { + _transform.update(_commandIndex, _stereo); + + auto& drawCallInfoBuffer = batch.getDrawCallInfoBuffer(); + if (batch._currentNamedCall.empty()) { + (void)CHECK_GL_ERROR(); + auto& drawCallInfo = drawCallInfoBuffer[_currentDraw]; + glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is disabled + (void)CHECK_GL_ERROR(); + GLint current_vao, current_vbo, maxVertexAtribs; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, ¤t_vao); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤t_vbo); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAtribs); + glVertexAttribI4i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused, 0, 0); + + //int values[] = {drawCallInfo.index, drawCallInfo.unused}; + //glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_INT, 0, (const GLvoid *) values); + + /* + //glDisableVertexAttribArray currentvao 1 current vbo 0 + GL_INVALID_OPERATION is generated + a non-zero vertex array object is bound, + zero is bound to the GL_ARRAY_BUFFER buffer object binding point and + the pointer argument is not NULL. TRUE + */ + //qDebug() << "GLBackend::updateTransform glVertexAttribIPointer done"; + (void)CHECK_GL_ERROR(); + + } else { + //qDebug() << "GLBackend::updateTransform else"; + glEnableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is enabled + glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer); + glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_UNSIGNED_SHORT, 0, + _transform._drawCallInfoOffsets[batch._currentNamedCall]); + glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, 1); + } + + (void)CHECK_GL_ERROR(); +} + +void GLBackend::resetTransformStage() { + +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp b/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp new file mode 100644 index 0000000000..4f7d0a8632 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp @@ -0,0 +1,35 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 09/27/2016 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GLBuffer.h" +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +GLBuffer::~GLBuffer() { + Backend::bufferCount.decrement(); + Backend::bufferGPUMemSize.update(_size, 0); + + if (_id) { + auto backend = _backend.lock(); + if (backend) { + backend->releaseBuffer(_id, _size); + } + } +} + +GLBuffer::GLBuffer(const std::weak_ptr& backend, const Buffer& buffer, GLuint id) : + GLObject(backend, buffer, id), + _size((GLuint)buffer._renderSysmem.getSize()), + _stamp(buffer._renderSysmem.getStamp()) +{ + Backend::bufferCount.increment(); + Backend::bufferGPUMemSize.update(0, _size); +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLBuffer.h b/libraries/gpu-gles/src/gpu/gl/GLBuffer.h new file mode 100644 index 0000000000..182014e764 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBuffer.h @@ -0,0 +1,66 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLBuffer_h +#define hifi_gpu_gl_GLBuffer_h + +#include "GLShared.h" +#include "GLBackend.h" + +namespace gpu { namespace gl { + +class GLBuffer : public GLObject { +public: + template + static GLBufferType* sync(GLBackend& backend, const Buffer& buffer) { + if (buffer.getSysmem().getSize() != 0) { + if (buffer._getUpdateCount == 0) { + qWarning() << "Unsynced buffer"; + } + if (buffer._getUpdateCount < buffer._applyUpdateCount) { + qWarning() << "Unsynced buffer " << buffer._getUpdateCount << " " << buffer._applyUpdateCount; + } + } + GLBufferType* object = Backend::getGPUObject(buffer); + + // Has the storage size changed? + if (!object || object->_stamp != buffer._renderSysmem.getStamp()) { + object = new GLBufferType(backend.shared_from_this(), buffer, object); + } + + if (0 != (buffer._renderPages._flags & PageManager::DIRTY)) { + object->transfer(); + } + + return object; + } + + template + static GLuint getId(GLBackend& backend, const Buffer& buffer) { + GLBuffer* bo = sync(backend, buffer); + if (bo) { + return bo->_buffer; + } else { + return 0; + } + } + + const GLuint& _buffer { _id }; + const GLuint _size; + const Stamp _stamp; + + ~GLBuffer(); + + virtual void transfer() = 0; + +protected: + GLBuffer(const std::weak_ptr& backend, const Buffer& buffer, GLuint id); +}; + +} } + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp new file mode 100644 index 0000000000..150bb2be70 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp @@ -0,0 +1,48 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 09/27/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GLFramebuffer.h" +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +GLFramebuffer::~GLFramebuffer() { + if (_id) { + auto backend = _backend.lock(); + if (backend) { + backend->releaseFramebuffer(_id); + } + } +} + +bool GLFramebuffer::checkStatus(GLenum target) const { + bool result = false; + switch (_status) { + case GL_FRAMEBUFFER_COMPLETE: + // Success ! + result = true; + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT."; + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT."; + break; +/* TODO: case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER."; + break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER."; + break; */ + case GL_FRAMEBUFFER_UNSUPPORTED: + qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; + break; + } + return result; +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h new file mode 100644 index 0000000000..a2fd0999f3 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h @@ -0,0 +1,77 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 09/27/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLFramebuffer_h +#define hifi_gpu_gl_GLFramebuffer_h + +#include "GLShared.h" +#include "GLBackend.h" + +namespace gpu { namespace gl { + +class GLFramebuffer : public GLObject { +public: + template + static GLFramebufferType* sync(GLBackend& backend, const Framebuffer& framebuffer) { + GLFramebufferType* object = Backend::getGPUObject(framebuffer); + + bool needsUpate { false }; + if (!object || + framebuffer.getDepthStamp() != object->_depthStamp || + framebuffer.getColorStamps() != object->_colorStamps) { + needsUpate = true; + } + + // If GPU object already created and in sync + if (!needsUpate) { + return object; + } else if (framebuffer.isEmpty()) { + // NO framebuffer definition yet so let's avoid thinking + return nullptr; + } + + // need to have a gpu object? + if (!object) { + // All is green, assign the gpuobject to the Framebuffer + object = new GLFramebufferType(backend.shared_from_this(), framebuffer); + Backend::setGPUObject(framebuffer, object); + (void)CHECK_GL_ERROR(); + } + + object->update(); + return object; + } + + template + static GLuint getId(GLBackend& backend, const Framebuffer& framebuffer) { + GLFramebufferType* fbo = sync(backend, framebuffer); + if (fbo) { + return fbo->_id; + } else { + return 0; + } + } + + const GLuint& _fbo { _id }; + std::vector _colorBuffers; + Stamp _depthStamp { 0 }; + std::vector _colorStamps; + +protected: + GLenum _status { GL_FRAMEBUFFER_COMPLETE }; + virtual void update() = 0; + bool checkStatus(GLenum target) const; + + GLFramebuffer(const std::weak_ptr& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {} + ~GLFramebuffer(); + +}; + +} } + + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLInputFormat.cpp b/libraries/gpu-gles/src/gpu/gl/GLInputFormat.cpp new file mode 100644 index 0000000000..7f42350c3b --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLInputFormat.cpp @@ -0,0 +1,33 @@ +// +// Created by Sam Gateau on 2016/07/21 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GLInputFormat.h" +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + + +GLInputFormat::GLInputFormat() { +} + +GLInputFormat:: ~GLInputFormat() { + +} + +GLInputFormat* GLInputFormat::sync(const Stream::Format& inputFormat) { + GLInputFormat* object = Backend::getGPUObject(inputFormat); + + if (!object) { + object = new GLInputFormat(); + object->key = inputFormat.getKey(); + Backend::setGPUObject(inputFormat, object); + } + + return object; +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLInputFormat.h b/libraries/gpu-gles/src/gpu/gl/GLInputFormat.h new file mode 100644 index 0000000000..a14e3d4d91 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLInputFormat.h @@ -0,0 +1,29 @@ +// +// Created by Sam Gateau on 2016/07/21 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLInputFormat_h +#define hifi_gpu_gl_GLInputFormat_h + +#include "GLShared.h" + +namespace gpu { +namespace gl { + +class GLInputFormat : public GPUObject { + public: + static GLInputFormat* sync(const Stream::Format& inputFormat); + + GLInputFormat(); + ~GLInputFormat(); + + std::string key; +}; + +} +} + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp b/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp new file mode 100644 index 0000000000..09c09de353 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp @@ -0,0 +1,62 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GLPipeline.h" + +#include "GLShader.h" +#include "GLState.h" + +using namespace gpu; +using namespace gpu::gl; + +GLPipeline* GLPipeline::sync(GLBackend& backend, const Pipeline& pipeline) { + GLPipeline* object = Backend::getGPUObject(pipeline); + + // If GPU object already created then good + if (object) { + return object; + } + + // No object allocated yet, let's see if it's worth it... + ShaderPointer shader = pipeline.getProgram(); + + // If this pipeline's shader has already failed to compile, don't try again + if (shader->compilationHasFailed()) { + return nullptr; + } + + GLShader* programObject = GLShader::sync(backend, *shader); + if (programObject == nullptr) { + shader->setCompilationHasFailed(true); + return nullptr; + } + + StatePointer state = pipeline.getState(); + GLState* stateObject = GLState::sync(*state); + if (stateObject == nullptr) { + return nullptr; + } + + // Program and state are valid, we can create the pipeline object + if (!object) { + object = new GLPipeline(); + Backend::setGPUObject(pipeline, object); + } + + // Special case for view correction matrices, any pipeline that declares the correction buffer + // uniform will automatically have it provided without any client code necessary. + // Required for stable lighting in the HMD. + //CLIMAX_MERGE_START + //getbuffers() doesnt exist anymore.. use get uniformbuffers()? + object->_cameraCorrection = shader->getUniformBuffers().findLocation("cameraCorrectionBuffer"); + //CLIMAX_MERGE_END + object->_program = programObject; + object->_state = stateObject; + + return object; +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLPipeline.h b/libraries/gpu-gles/src/gpu/gl/GLPipeline.h new file mode 100644 index 0000000000..a298f149d9 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLPipeline.h @@ -0,0 +1,29 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLPipeline_h +#define hifi_gpu_gl_GLPipeline_h + +#include "GLShared.h" + +namespace gpu { namespace gl { + +class GLPipeline : public GPUObject { +public: + static GLPipeline* sync(GLBackend& backend, const Pipeline& pipeline); + + GLShader* _program { nullptr }; + GLState* _state { nullptr }; + // Bit of a hack, any pipeline can need the camera correction buffer at execution time, so + // we store whether a given pipeline has declared the uniform buffer for it. + int32 _cameraCorrection { -1 }; +}; + +} } + + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLQuery.h b/libraries/gpu-gles/src/gpu/gl/GLQuery.h new file mode 100644 index 0000000000..23b1f38621 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLQuery.h @@ -0,0 +1,67 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLQuery_h +#define hifi_gpu_gl_GLQuery_h + +#include "GLShared.h" +#include "GLBackend.h" + +namespace gpu { namespace gl { + +class GLQuery : public GLObject { + using Parent = gpu::gl::GLObject; +public: + template + static GLQueryType* sync(GLBackend& backend, const Query& query) { + GLQueryType* object = Backend::getGPUObject(query); + + // need to have a gpu object? + if (!object) { + // All is green, assign the gpuobject to the Query + object = new GLQueryType(backend.shared_from_this(), query); + (void)CHECK_GL_ERROR(); + Backend::setGPUObject(query, object); + } + + return object; + } + + template + static GLuint getId(GLBackend& backend, const QueryPointer& query) { + if (!query) { + return 0; + } + + GLQuery* object = sync(backend, *query); + if (!object) { + return 0; + } + + return object->_endqo; + } + + const GLuint& _endqo = { _id }; + const GLuint _beginqo = { 0 }; + GLuint64 _result { (GLuint64)-1 }; + GLuint64 _batchElapsedTime { (GLuint64) 0 }; + uint64_t _profileRangeId { 0 }; + uint32_t _rangeQueryDepth { 0 }; + +protected: + GLQuery(const std::weak_ptr& backend, const Query& query, GLuint endId, GLuint beginId) : Parent(backend, query, endId), _beginqo(beginId) {} + ~GLQuery() { + if (_id) { + GLuint ids[2] = { _endqo, _beginqo }; + glDeleteQueries(2, ids); + } + } +}; + +} } + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp new file mode 100644 index 0000000000..b728010470 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp @@ -0,0 +1,224 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLShader.h" +#include + +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +GLShader::GLShader(const std::weak_ptr& backend) : _backend(backend) { +} + +GLShader::~GLShader() { + for (auto& so : _shaderObjects) { + auto backend = _backend.lock(); + if (backend) { + if (so.glshader != 0) { + backend->releaseShader(so.glshader); + } + if (so.glprogram != 0) { + backend->releaseProgram(so.glprogram); + } + } + } +} + +// GLSL version +static const std::string glslVersion { + "#version 310 es" +}; + +// Shader domain +static const size_t NUM_SHADER_DOMAINS = 3; + +// GL Shader type enums +// Must match the order of type specified in gpu::Shader::Type +static const std::array SHADER_DOMAINS { { + GL_VERTEX_SHADER, + GL_FRAGMENT_SHADER, + //GL_GEOMETRY_SHADER, +} }; + +// Domain specific defines +// Must match the order of type specified in gpu::Shader::Type +static const std::array DOMAIN_DEFINES { { + "#define GPU_VERTEX_SHADER", + "#define GPU_PIXEL_SHADER", + "#define GPU_GEOMETRY_SHADER", +} }; + +// Stereo specific defines +static const std::string stereoVersion { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN" +#endif +#ifdef GPU_STEREO_DRAWCALL_DOUBLED +#ifdef GPU_STEREO_CAMERA_BUFFER + "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED" +#else + "#define GPU_TRANSFORM_IS_STEREO" +#endif +#endif +}; + +// Versions specific of the shader +static const std::array VERSION_DEFINES { { + "", + stereoVersion +} }; + +GLShader* compileBackendShader(GLBackend& backend, const Shader& shader) { + // Any GLSLprogram ? normally yes... + const std::string& shaderSource = shader.getSource().getCode(); + GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; + GLShader::ShaderObjects shaderObjects; + + for (int version = 0; version < GLShader::NumVersions; version++) { + auto& shaderObject = shaderObjects[version]; + std::string shaderDefines = glslVersion + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version] + + "\n" + "#extension GL_EXT_texture_buffer : enable" + + "\nprecision lowp float; // check precision 2" + + "\nprecision lowp samplerBuffer;" + + "\nprecision lowp sampler2DShadow;"; + // TODO Delete bool result = compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram); + std::string error; + + +#ifdef SEPARATE_PROGRAM + bool result = ::gl::compileShader(shaderDomain, shaderSource.c_str(), shaderDefines.c_str(), shaderObject.glshader, shaderObject.glprogram, error); +#else + bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error); +#endif + if (!result) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str(); + return nullptr; + } + } + + // So far so good, the shader is created successfully + GLShader* object = new GLShader(backend.shared_from_this()); + object->_shaderObjects = shaderObjects; + + return object; +} + +GLShader* compileBackendProgram(GLBackend& backend, const Shader& program) { + if (!program.isProgram()) { + return nullptr; + } + + GLShader::ShaderObjects programObjects; + + for (int version = 0; version < GLShader::NumVersions; version++) { + auto& programObject = programObjects[version]; + + // Let's go through every shaders and make sure they are ready to go + std::vector< GLuint > shaderGLObjects; + for (auto subShader : program.getShaders()) { + auto object = GLShader::sync(backend, *subShader); + if (object) { + shaderGLObjects.push_back(object->_shaderObjects[version].glshader); + } else { + qCDebug(gpugllogging) << "GLShader::compileBackendProgram - One of the shaders of the program is not compiled?"; + return nullptr; + } + } + + std::string error; + GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error); + if (glprogram == 0) { + qCWarning(gpugllogging) << error.c_str(); + return nullptr; + } + + programObject.glprogram = glprogram; + + makeProgramBindings(programObject); + } + + // So far so good, the program versions have all been created successfully + GLShader* object = new GLShader(backend.shared_from_this()); + object->_shaderObjects = programObjects; + + return object; +} + +GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { + GLShader* object = Backend::getGPUObject(shader); + + // If GPU object already created then good + if (object) { + return object; + } + // need to have a gpu object? + if (shader.isProgram()) { + GLShader* tempObject = compileBackendProgram(backend, shader); + if (tempObject) { + object = tempObject; + Backend::setGPUObject(shader, object); + } + } else if (shader.isDomain()) { + GLShader* tempObject = compileBackendShader(backend, shader); + if (tempObject) { + object = tempObject; + Backend::setGPUObject(shader, object); + } + } + + glFinish(); + return object; +} + +bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings) { + + // First make sure the Shader has been compiled + GLShader* object = sync(backend, shader); + if (!object) { + return false; + } + + // Apply bindings to all program versions and generate list of slots from default version + for (int version = 0; version < GLShader::NumVersions; version++) { + auto& shaderObject = object->_shaderObjects[version]; + if (shaderObject.glprogram) { + Shader::SlotSet buffers; + makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers); + + Shader::SlotSet uniforms; + Shader::SlotSet textures; + Shader::SlotSet samplers; + makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers); + + Shader::SlotSet resourceBuffers; + makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers); + + Shader::SlotSet inputs; + makeInputSlots(shaderObject.glprogram, slotBindings, inputs); + + Shader::SlotSet outputs; + makeOutputSlots(shaderObject.glprogram, slotBindings, outputs); + + // Define the public slots only from the default version + if (version == 0) { + shader.defineSlots(uniforms, buffers, resourceBuffers, textures, samplers, inputs, outputs); + } // else + { + GLShader::UniformMapping mapping; + for (auto srcUniform : shader.getUniforms()) { + mapping[srcUniform._location] = uniforms.findLocation(srcUniform._name); + } + object->_uniformMappings.push_back(mapping); + } + } + } + + return true; +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.h b/libraries/gpu-gles/src/gpu/gl/GLShader.h new file mode 100644 index 0000000000..e03b487a60 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.h @@ -0,0 +1,59 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLShader_h +#define hifi_gpu_gl_GLShader_h + +#include "GLShared.h" + +namespace gpu { namespace gl { + +class GLShader : public GPUObject { +public: + static GLShader* sync(GLBackend& backend, const Shader& shader); + static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings); + + enum Version { + Mono = 0, + Stereo, + + NumVersions + }; + + using ShaderObject = gpu::gl::ShaderObject; + using ShaderObjects = std::array< ShaderObject, NumVersions >; + + using UniformMapping = std::map; + using UniformMappingVersions = std::vector; + + GLShader(const std::weak_ptr& backend); + ~GLShader(); + + ShaderObjects _shaderObjects; + UniformMappingVersions _uniformMappings; + + GLuint getProgram(Version version = Mono) const { + return _shaderObjects[version].glprogram; + } + + GLint getUniformLocation(GLint srcLoc, Version version = Mono) const { + // This check protect against potential invalid src location for this shader, if unknown then return -1. + const auto& mapping = _uniformMappings[version]; + auto found = mapping.find(srcLoc); + if (found == mapping.end()) { + return -1; + } + return found->second; + } + + const std::weak_ptr _backend; +}; + +} } + + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLShared.cpp b/libraries/gpu-gles/src/gpu/gl/GLShared.cpp new file mode 100644 index 0000000000..5d340889a6 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLShared.cpp @@ -0,0 +1,879 @@ +// +// Created by Bradley Austin Davis on 2016/05/14 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLShared.h" + +#include + +#include + +#include +#include +#include + +Q_LOGGING_CATEGORY(gpugllogging, "hifi.gpu.gl") +Q_LOGGING_CATEGORY(trace_render_gpu_gl, "trace.render.gpu.gl") + +namespace gpu { namespace gl { + +bool checkGLError(const char* name) { + GLenum error = glGetError(); + if (!error) { + return false; + } else { + switch (error) { + case GL_INVALID_ENUM: + qCDebug(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; + break; + case GL_INVALID_VALUE: + qCDebug(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag"; + break; + case GL_INVALID_OPERATION: + qCDebug(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag.."; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + qCDebug(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag."; + break; + case GL_OUT_OF_MEMORY: + qCDebug(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded."; + break; + default: + qCDebug(gpugllogging) << "GLBackend" << name << ": Unknown error: " << error; + break; + } + return true; + } +} + +bool checkGLErrorDebug(const char* name) { +#ifdef DEBUG + return checkGLError(name); +#else + Q_UNUSED(name); + return false; +#endif +} + +gpu::Size getFreeDedicatedMemory() { + Size result { 0 }; + static bool nvidiaMemorySupported { false }; + static bool atiMemorySupported { false }; + if (nvidiaMemorySupported) { + + GLint nvGpuMemory { 0 }; + qDebug() << "TODO: GLShared.cpp getFreeDedicatedMemory GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX"; + //glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &nvGpuMemory); + if (GL_NO_ERROR == glGetError()) { + result = KB_TO_BYTES(nvGpuMemory); + } else { + nvidiaMemorySupported = false; + } + } else if (atiMemorySupported) { + GLint atiGpuMemory[4]; + qDebug() << "TODO: GLShared.cpp getFreeDedicatedMemory GL_TEXTURE_FREE_MEMORY_ATI"; + // not really total memory, but close enough if called early enough in the application lifecycle + //glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, atiGpuMemory); + if (GL_NO_ERROR == glGetError()) { + result = KB_TO_BYTES(atiGpuMemory[0]); + } else { + atiMemorySupported = false; + } + } + return result; +} + +gpu::Size getDedicatedMemory() { + static Size dedicatedMemory { 0 }; + static std::once_flag once; + std::call_once(once, [&] { + if (!dedicatedMemory) { + GLint atiGpuMemory[4]; + // not really total memory, but close enough if called early enough in the application lifecycle + //glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, atiGpuMemory); + qDebug() << "TODO: GLShared.cpp.cpp:initInput GL_TEXTURE_FREE_MEMORY_ATI"; + if (GL_NO_ERROR == glGetError()) { + dedicatedMemory = KB_TO_BYTES(atiGpuMemory[0]); + } + } + + if (!dedicatedMemory) { + GLint nvGpuMemory { 0 }; + qDebug() << "TODO: GLShared.cpp.cpp:initInput GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX"; + //glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &nvGpuMemory); + if (GL_NO_ERROR == glGetError()) { + dedicatedMemory = KB_TO_BYTES(nvGpuMemory); + } + } + + if (!dedicatedMemory) { + auto gpuIdent = GPUIdent::getInstance(); + if (gpuIdent && gpuIdent->isValid()) { + dedicatedMemory = MB_TO_BYTES(gpuIdent->getMemory()); + } + } + }); + + return dedicatedMemory; +} + + + + +ComparisonFunction comparisonFuncFromGL(GLenum func) { + if (func == GL_NEVER) { + return NEVER; + } else if (func == GL_LESS) { + return LESS; + } else if (func == GL_EQUAL) { + return EQUAL; + } else if (func == GL_LEQUAL) { + return LESS_EQUAL; + } else if (func == GL_GREATER) { + return GREATER; + } else if (func == GL_NOTEQUAL) { + return NOT_EQUAL; + } else if (func == GL_GEQUAL) { + return GREATER_EQUAL; + } else if (func == GL_ALWAYS) { + return ALWAYS; + } + + return ALWAYS; +} + +State::StencilOp stencilOpFromGL(GLenum stencilOp) { + if (stencilOp == GL_KEEP) { + return State::STENCIL_OP_KEEP; + } else if (stencilOp == GL_ZERO) { + return State::STENCIL_OP_ZERO; + } else if (stencilOp == GL_REPLACE) { + return State::STENCIL_OP_REPLACE; + } else if (stencilOp == GL_INCR_WRAP) { + return State::STENCIL_OP_INCR_SAT; + } else if (stencilOp == GL_DECR_WRAP) { + return State::STENCIL_OP_DECR_SAT; + } else if (stencilOp == GL_INVERT) { + return State::STENCIL_OP_INVERT; + } else if (stencilOp == GL_INCR) { + return State::STENCIL_OP_INCR; + } else if (stencilOp == GL_DECR) { + return State::STENCIL_OP_DECR; + } + + return State::STENCIL_OP_KEEP; +} + +State::BlendOp blendOpFromGL(GLenum blendOp) { + if (blendOp == GL_FUNC_ADD) { + return State::BLEND_OP_ADD; + } else if (blendOp == GL_FUNC_SUBTRACT) { + return State::BLEND_OP_SUBTRACT; + } else if (blendOp == GL_FUNC_REVERSE_SUBTRACT) { + return State::BLEND_OP_REV_SUBTRACT; + } else if (blendOp == GL_MIN) { + return State::BLEND_OP_MIN; + } else if (blendOp == GL_MAX) { + return State::BLEND_OP_MAX; + } + + return State::BLEND_OP_ADD; +} + +State::BlendArg blendArgFromGL(GLenum blendArg) { + if (blendArg == GL_ZERO) { + return State::ZERO; + } else if (blendArg == GL_ONE) { + return State::ONE; + } else if (blendArg == GL_SRC_COLOR) { + return State::SRC_COLOR; + } else if (blendArg == GL_ONE_MINUS_SRC_COLOR) { + return State::INV_SRC_COLOR; + } else if (blendArg == GL_DST_COLOR) { + return State::DEST_COLOR; + } else if (blendArg == GL_ONE_MINUS_DST_COLOR) { + return State::INV_DEST_COLOR; + } else if (blendArg == GL_SRC_ALPHA) { + return State::SRC_ALPHA; + } else if (blendArg == GL_ONE_MINUS_SRC_ALPHA) { + return State::INV_SRC_ALPHA; + } else if (blendArg == GL_DST_ALPHA) { + return State::DEST_ALPHA; + } else if (blendArg == GL_ONE_MINUS_DST_ALPHA) { + return State::INV_DEST_ALPHA; + } else if (blendArg == GL_CONSTANT_COLOR) { + return State::FACTOR_COLOR; + } else if (blendArg == GL_ONE_MINUS_CONSTANT_COLOR) { + return State::INV_FACTOR_COLOR; + } else if (blendArg == GL_CONSTANT_ALPHA) { + return State::FACTOR_ALPHA; + } else if (blendArg == GL_ONE_MINUS_CONSTANT_ALPHA) { + return State::INV_FACTOR_ALPHA; + } + + return State::ONE; +} + +void getCurrentGLState(State::Data& state) { + { + GLint modes[2]; + //glGetIntegerv(GL_POLYGON_MODE, modes); + qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_POLYGON_MODE"; + qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_FILL"; + qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_LINE"; + + if (modes[0] == 0 /*GL_FILL*/) { + state.fillMode = State::FILL_FACE; + } else { + if (modes[0] == 0 /*GL_LINE*/) { + state.fillMode = State::FILL_LINE; + } else { + state.fillMode = State::FILL_POINT; + } + } + } + { + if (glIsEnabled(GL_CULL_FACE)) { + GLint mode; + glGetIntegerv(GL_CULL_FACE_MODE, &mode); + state.cullMode = (mode == GL_FRONT ? State::CULL_FRONT : State::CULL_BACK); + } else { + state.cullMode = State::CULL_NONE; + } + } + { + GLint winding; + glGetIntegerv(GL_FRONT_FACE, &winding); + state.frontFaceClockwise = (winding == GL_CW); + //state.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP); + qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_DEPTH_CLAMP"; + state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST); + //state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE); + qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_MULTISAMPLE"; + + //state.antialisedLineEnable = glIsEnabled(GL_LINE_SMOOTH); + qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_LINE_SMOOTH"; + + } + { + if (glIsEnabled(GL_POLYGON_OFFSET_FILL)) { + glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &state.depthBiasSlopeScale); + glGetFloatv(GL_POLYGON_OFFSET_UNITS, &state.depthBias); + } + } + { + GLboolean isEnabled = glIsEnabled(GL_DEPTH_TEST); + GLboolean writeMask; + glGetBooleanv(GL_DEPTH_WRITEMASK, &writeMask); + GLint func; + glGetIntegerv(GL_DEPTH_FUNC, &func); + + state.depthTest = State::DepthTest(isEnabled, writeMask, comparisonFuncFromGL(func)); + } + { + GLboolean isEnabled = glIsEnabled(GL_STENCIL_TEST); + + GLint frontWriteMask; + GLint frontReadMask; + GLint frontRef; + GLint frontFail; + GLint frontDepthFail; + GLint frontPass; + GLint frontFunc; + glGetIntegerv(GL_STENCIL_WRITEMASK, &frontWriteMask); + glGetIntegerv(GL_STENCIL_VALUE_MASK, &frontReadMask); + glGetIntegerv(GL_STENCIL_REF, &frontRef); + glGetIntegerv(GL_STENCIL_FAIL, &frontFail); + glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &frontDepthFail); + glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &frontPass); + glGetIntegerv(GL_STENCIL_FUNC, &frontFunc); + + GLint backWriteMask; + GLint backReadMask; + GLint backRef; + GLint backFail; + GLint backDepthFail; + GLint backPass; + GLint backFunc; + glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &backWriteMask); + glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &backReadMask); + glGetIntegerv(GL_STENCIL_BACK_REF, &backRef); + glGetIntegerv(GL_STENCIL_BACK_FAIL, &backFail); + glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &backDepthFail); + glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &backPass); + glGetIntegerv(GL_STENCIL_BACK_FUNC, &backFunc); + + state.stencilActivation = State::StencilActivation(isEnabled, frontWriteMask, backWriteMask); + state.stencilTestFront = State::StencilTest(frontRef, frontReadMask, comparisonFuncFromGL(frontFunc), stencilOpFromGL(frontFail), stencilOpFromGL(frontDepthFail), stencilOpFromGL(frontPass)); + state.stencilTestBack = State::StencilTest(backRef, backReadMask, comparisonFuncFromGL(backFunc), stencilOpFromGL(backFail), stencilOpFromGL(backDepthFail), stencilOpFromGL(backPass)); + } + { + GLint mask = 0xFFFFFFFF; + if (glIsEnabled(GL_SAMPLE_MASK)) { + glGetIntegerv(GL_SAMPLE_MASK, &mask); + state.sampleMask = mask; + } + state.sampleMask = mask; + } + { + state.alphaToCoverageEnable = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + { + GLboolean isEnabled = glIsEnabled(GL_BLEND); + GLint srcRGB; + GLint srcA; + GLint dstRGB; + GLint dstA; + glGetIntegerv(GL_BLEND_SRC_RGB, &srcRGB); + glGetIntegerv(GL_BLEND_SRC_ALPHA, &srcA); + glGetIntegerv(GL_BLEND_DST_RGB, &dstRGB); + glGetIntegerv(GL_BLEND_DST_ALPHA, &dstA); + + GLint opRGB; + GLint opA; + glGetIntegerv(GL_BLEND_EQUATION_RGB, &opRGB); + glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &opA); + + state.blendFunction = State::BlendFunction(isEnabled, + blendArgFromGL(srcRGB), blendOpFromGL(opRGB), blendArgFromGL(dstRGB), + blendArgFromGL(srcA), blendOpFromGL(opA), blendArgFromGL(dstA)); + } + { + GLboolean mask[4]; + glGetBooleanv(GL_COLOR_WRITEMASK, mask); + state.colorWriteMask = (mask[0] ? State::WRITE_RED : 0) + | (mask[1] ? State::WRITE_GREEN : 0) + | (mask[2] ? State::WRITE_BLUE : 0) + | (mask[3] ? State::WRITE_ALPHA : 0); + } + + (void)CHECK_GL_ERROR(); +} + + +class ElementResource { +public: + gpu::Element _element; + uint16 _resource; + + ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {} +}; + +ElementResource getFormatFromGLUniform(GLenum gltype) { + switch (gltype) { + case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + /* + case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + */ + case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); + + case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); +#if defined(Q_OS_WIN) + case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); +#endif + + case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); + + + case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + + /* {GL_FLOAT_MAT2x3 mat2x3}, + {GL_FLOAT_MAT2x4 mat2x4}, + {GL_FLOAT_MAT3x2 mat3x2}, + {GL_FLOAT_MAT3x4 mat3x4}, + {GL_FLOAT_MAT4x2 mat4x2}, + {GL_FLOAT_MAT4x3 mat4x3}, + {GL_DOUBLE_MAT2 dmat2}, + {GL_DOUBLE_MAT3 dmat3}, + {GL_DOUBLE_MAT4 dmat4}, + {GL_DOUBLE_MAT2x3 dmat2x3}, + {GL_DOUBLE_MAT2x4 dmat2x4}, + {GL_DOUBLE_MAT3x2 dmat3x2}, + {GL_DOUBLE_MAT3x4 dmat3x4}, + {GL_DOUBLE_MAT4x2 dmat4x2}, + {GL_DOUBLE_MAT4x3 dmat4x3}, + */ + + //qDebug() << "TODO: GLShared.cpp.cpp:ElementResource GL_SAMPLER_1D"; + //case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); + case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); + + case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); + case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); + +#if defined(Q_OS_WIN) + case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); +#endif + + case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); +#if defined(Q_OS_WIN) + case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); + + case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); +#endif + + // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, + // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, + + // {GL_SAMPLER_BUFFER samplerBuffer}, + // {GL_SAMPLER_2D_RECT sampler2DRect}, + // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, + +#if defined(Q_OS_WIN) + case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); + case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); + case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + + // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, + // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, + + case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); + case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); + case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); +#endif + // {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, + // {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, + /* + {GL_IMAGE_1D image1D}, + {GL_IMAGE_2D image2D}, + {GL_IMAGE_3D image3D}, + {GL_IMAGE_2D_RECT image2DRect}, + {GL_IMAGE_CUBE imageCube}, + {GL_IMAGE_BUFFER imageBuffer}, + {GL_IMAGE_1D_ARRAY image1DArray}, + {GL_IMAGE_2D_ARRAY image2DArray}, + {GL_IMAGE_2D_MULTISAMPLE image2DMS}, + {GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, + {GL_INT_IMAGE_1D iimage1D}, + {GL_INT_IMAGE_2D iimage2D}, + {GL_INT_IMAGE_3D iimage3D}, + {GL_INT_IMAGE_2D_RECT iimage2DRect}, + {GL_INT_IMAGE_CUBE iimageCube}, + {GL_INT_IMAGE_BUFFER iimageBuffer}, + {GL_INT_IMAGE_1D_ARRAY iimage1DArray}, + {GL_INT_IMAGE_2D_ARRAY iimage2DArray}, + {GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, + {GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, + {GL_UNSIGNED_INT_IMAGE_1D uimage1D}, + {GL_UNSIGNED_INT_IMAGE_2D uimage2D}, + {GL_UNSIGNED_INT_IMAGE_3D uimage3D}, + {GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, + {GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot + + {GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, + {GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, + {GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, + {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, + {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, + {GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} + */ + default: + return ElementResource(Element(), Resource::BUFFER); + } + +}; + +int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { + GLint uniformsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); + + for (int i = 0; i < uniformsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + GLint location = glGetUniformLocation(glprogram, name); + const GLint INVALID_UNIFORM_LOCATION = -1; + + // Try to make sense of the gltype + auto elementResource = getFormatFromGLUniform(type); + + // The uniform as a standard var type + if (location != INVALID_UNIFORM_LOCATION) { + // Let's make sure the name doesn't contains an array element + std::string sname(name); + auto foundBracket = sname.find_first_of('['); + if (foundBracket != std::string::npos) { + // std::string arrayname = sname.substr(0, foundBracket); + + if (sname[foundBracket + 1] == '0') { + sname = sname.substr(0, foundBracket); + } else { + // skip this uniform since it's not the first element of an array + continue; + } + } + + if (elementResource._resource == Resource::BUFFER) { + uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); + } else { + // For texture/Sampler, the location is the actual binding value + GLint binding = -1; + glGetUniformiv(glprogram, location, &binding); + + auto requestedBinding = slotBindings.find(std::string(sname)); + if (requestedBinding != slotBindings.end()) { + if (binding != (*requestedBinding)._location) { + binding = (*requestedBinding)._location; + glProgramUniform1i(glprogram, location, binding); + } + } + + textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + } + } + } + + return uniformsCount; +} + +const GLint UNUSED_SLOT = -1; +bool isUnusedSlot(GLint binding) { + return (binding == UNUSED_SLOT); +} + +int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { + GLint buffersCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); + + // fast exit + if (buffersCount == 0) { + return 0; + } + + GLint maxNumUniformBufferSlots = 0; + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); + std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); + + struct UniformBlockInfo { + using Vector = std::vector; + const GLuint index{ 0 }; + const std::string name; + GLint binding{ -1 }; + GLint size{ 0 }; + + static std::string getName(GLuint glprogram, GLuint i) { + static const GLint NAME_LENGTH = 256; + GLint length = 0; + GLchar nameBuffer[NAME_LENGTH]; + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); + glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer); + return std::string(nameBuffer); + } + + UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding); + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); + } + }; + + UniformBlockInfo::Vector uniformBlocks; + uniformBlocks.reserve(buffersCount); + for (int i = 0; i < buffersCount; i++) { + uniformBlocks.push_back(UniformBlockInfo(glprogram, i)); + } + + for (auto& info : uniformBlocks) { + auto requestedBinding = slotBindings.find(info.name); + if (requestedBinding != slotBindings.end()) { + info.binding = (*requestedBinding)._location; + glUniformBlockBinding(glprogram, info.index, info.binding); + uniformBufferSlotMap[info.binding] = info.index; + } + } + + for (auto& info : uniformBlocks) { + if (slotBindings.count(info.name)) { + continue; + } + + // If the binding is 0, or the binding maps to an already used binding + if (info.binding == 0 || uniformBufferSlotMap[info.binding] != UNUSED_SLOT) { + // If no binding was assigned then just do it finding a free slot + auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot); + if (slotIt != uniformBufferSlotMap.end()) { + info.binding = slotIt - uniformBufferSlotMap.begin(); + glUniformBlockBinding(glprogram, info.index, info.binding); + } else { + // This should neve happen, an active ubo cannot find an available slot among the max available?! + info.binding = -1; + } + } + + uniformBufferSlotMap[info.binding] = info.index; + } + + for (auto& info : uniformBlocks) { + static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); + buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); + } + return buffersCount; +} +//CLIMAX_MERGE_START +//This has been copied over from gl45backendshader.cpp +int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { + GLint buffersCount = 0; + glGetProgramInterfaceiv(glprogram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &buffersCount); + + // fast exit + if (buffersCount == 0) { + return 0; + } + + GLint maxNumResourceBufferSlots = 0; + glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxNumResourceBufferSlots); + std::vector resourceBufferSlotMap(maxNumResourceBufferSlots, -1); + + struct ResourceBlockInfo { + using Vector = std::vector; + const GLuint index{ 0 }; + const std::string name; + GLint binding{ -1 }; + GLint size{ 0 }; + + static std::string getName(GLuint glprogram, GLuint i) { + static const GLint NAME_LENGTH = 256; + GLint length = 0; + GLchar nameBuffer[NAME_LENGTH]; + glGetProgramResourceName(glprogram, GL_SHADER_STORAGE_BLOCK, i, NAME_LENGTH, &length, nameBuffer); + return std::string(nameBuffer); + } + + ResourceBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { + GLenum props[2] = { GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE}; + glGetProgramResourceiv(glprogram, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, nullptr, &binding); + } + }; + + ResourceBlockInfo::Vector resourceBlocks; + resourceBlocks.reserve(buffersCount); + for (int i = 0; i < buffersCount; i++) { + resourceBlocks.push_back(ResourceBlockInfo(glprogram, i)); + } + + for (auto& info : resourceBlocks) { + auto requestedBinding = slotBindings.find(info.name); + if (requestedBinding != slotBindings.end()) { + info.binding = (*requestedBinding)._location; + glUniformBlockBinding(glprogram, info.index, info.binding); + resourceBufferSlotMap[info.binding] = info.index; + } + } + + for (auto& info : resourceBlocks) { + if (slotBindings.count(info.name)) { + continue; + } + + // If the binding is -1, or the binding maps to an already used binding + if (info.binding == -1 || !isUnusedSlot(resourceBufferSlotMap[info.binding])) { + // If no binding was assigned then just do it finding a free slot + auto slotIt = std::find_if(resourceBufferSlotMap.begin(), resourceBufferSlotMap.end(), isUnusedSlot); + if (slotIt != resourceBufferSlotMap.end()) { + info.binding = slotIt - resourceBufferSlotMap.begin(); + glUniformBlockBinding(glprogram, info.index, info.binding); + } else { + // This should never happen, an active ssbo cannot find an available slot among the max available?! + info.binding = -1; + } + } + + resourceBufferSlotMap[info.binding] = info.index; + } + + for (auto& info : resourceBlocks) { + static const Element element(SCALAR, gpu::UINT32, gpu::RESOURCE_BUFFER); + resourceBuffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); + } + return buffersCount; +} +//CLIMAX_MERGE_END + +int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { + GLint inputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + GLint binding = glGetAttribLocation(glprogram, name); + + auto elementResource = getFormatFromGLUniform(type); + inputs.insert(Shader::Slot(name, binding, elementResource._element, -1)); + } + + return inputsCount; +} + +int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { + /* GLint outputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + auto element = getFormatFromGLUniform(type); + outputs.insert(Shader::Slot(name, i, element)); + } + */ + return 0; //inputsCount; +} + +void makeProgramBindings(ShaderObject& shaderObject) { + if (!shaderObject.glprogram) { + return; + } + GLuint glprogram = shaderObject.glprogram; + GLint loc = -1; + + //Check for gpu specific attribute slotBindings + loc = glGetAttribLocation(glprogram, "inPosition"); + if (loc >= 0 && loc != gpu::Stream::POSITION) { + glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition"); + } + + loc = glGetAttribLocation(glprogram, "inNormal"); + if (loc >= 0 && loc != gpu::Stream::NORMAL) { + glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal"); + } + + loc = glGetAttribLocation(glprogram, "inColor"); + if (loc >= 0 && loc != gpu::Stream::COLOR) { + glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor"); + } + + loc = glGetAttribLocation(glprogram, "inTexCoord0"); + if (loc >= 0 && loc != gpu::Stream::TEXCOORD) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0"); + } + + loc = glGetAttribLocation(glprogram, "inTangent"); + if (loc >= 0 && loc != gpu::Stream::TANGENT) { + glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent"); + } + + loc = glGetAttribLocation(glprogram, "inTexCoord1"); + if (loc >= 0 && loc != gpu::Stream::TEXCOORD1) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD1, "inTexCoord1"); + } + + loc = glGetAttribLocation(glprogram, "inSkinClusterIndex"); + if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex"); + } + + loc = glGetAttribLocation(glprogram, "inSkinClusterWeight"); + if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight"); + } + + loc = glGetAttribLocation(glprogram, "_drawCallInfo"); + if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) { + glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo"); + } + + // Link again to take into account the assigned attrib location + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + if (!linked) { + qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; + } + + // now assign the ubo binding, then DON't relink! + + //Check for gpu specific uniform slotBindings + loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer"); + if (loc >= 0) { + // FIXME GLES + // glShaderStorageBlockBinding(glprogram, loc, TRANSFORM_OBJECT_SLOT); + shaderObject.transformObjectSlot = TRANSFORM_OBJECT_SLOT; + } + + loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, TRANSFORM_CAMERA_SLOT); + shaderObject.transformCameraSlot = TRANSFORM_CAMERA_SLOT; + } + + (void)CHECK_GL_ERROR(); +} + +void serverWait() { + auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + assert(fence); + glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(fence); +} + +void clientWait() { + auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + assert(fence); + auto result = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0); + while (GL_TIMEOUT_EXPIRED == result || GL_WAIT_FAILED == result) { + // Minimum sleep + QThread::usleep(1); + result = glClientWaitSync(fence, 0, 0); + } + glDeleteSync(fence); +} + +} } + + +using namespace gpu; + + diff --git a/libraries/gpu-gles/src/gpu/gl/GLShared.h b/libraries/gpu-gles/src/gpu/gl/GLShared.h new file mode 100644 index 0000000000..54209b106d --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLShared.h @@ -0,0 +1,167 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_GLShared_h +#define hifi_gpu_GLShared_h + +#include +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(gpugllogging) +Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl) + +namespace gpu { namespace gl { + + static const GLint TRANSFORM_OBJECT_SLOT { 14 }; // SSBO binding slot + +// Create a fence and inject a GPU wait on the fence +void serverWait(); + +// Create a fence and synchronously wait on the fence +void clientWait(); + +gpu::Size getDedicatedMemory(); +gpu::Size getFreeDedicatedMemory(); +ComparisonFunction comparisonFuncFromGL(GLenum func); +State::StencilOp stencilOpFromGL(GLenum stencilOp); +State::BlendOp blendOpFromGL(GLenum blendOp); +State::BlendArg blendArgFromGL(GLenum blendArg); +void getCurrentGLState(State::Data& state); + +struct ShaderObject { + GLuint glshader { 0 }; + GLuint glprogram { 0 }; + GLint transformCameraSlot { -1 }; + GLint transformObjectSlot { -1 }; +}; + +int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers); +int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers); +int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs); +int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs); +//CLIMAX_MERGE_START +//makeResourceBufferSlots has been added to glbacked as a virtual function and is being used in gl42 and gl45 overrides. +//Since these files dont exist in the andoid version create a stub here. +int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers); +//CLIMAX_MERGE_END +void makeProgramBindings(ShaderObject& shaderObject); + +enum GLSyncState { + // The object is currently undergoing no processing, although it's content + // may be out of date, or it's storage may be invalid relative to the + // owning GPU object + Idle, + // The object has been queued for transfer to the GPU + Pending, + // The object has been transferred to the GPU, but is awaiting + // any post transfer operations that may need to occur on the + // primary rendering thread + Transferred, +}; + +static const GLenum BLEND_OPS_TO_GL[State::NUM_BLEND_OPS] = { + GL_FUNC_ADD, + GL_FUNC_SUBTRACT, + GL_FUNC_REVERSE_SUBTRACT, + GL_MIN, + GL_MAX +}; + +static const GLenum BLEND_ARGS_TO_GL[State::NUM_BLEND_ARGS] = { + GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_SRC_ALPHA_SATURATE, + GL_CONSTANT_COLOR, + GL_ONE_MINUS_CONSTANT_COLOR, + GL_CONSTANT_ALPHA, + GL_ONE_MINUS_CONSTANT_ALPHA, +}; + +static const GLenum COMPARISON_TO_GL[gpu::NUM_COMPARISON_FUNCS] = { + GL_NEVER, + GL_LESS, + GL_EQUAL, + GL_LEQUAL, + GL_GREATER, + GL_NOTEQUAL, + GL_GEQUAL, + GL_ALWAYS +}; + +static const GLenum PRIMITIVE_TO_GL[gpu::NUM_PRIMITIVES] = { + GL_POINTS, + GL_LINES, + GL_LINE_STRIP, + GL_TRIANGLES, + GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN, +}; + +static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = { + GL_FLOAT, + GL_INT, + GL_UNSIGNED_INT, + GL_HALF_FLOAT, + GL_SHORT, + GL_UNSIGNED_SHORT, + GL_BYTE, + GL_UNSIGNED_BYTE, + // Normalized values + GL_INT, + GL_UNSIGNED_INT, + GL_SHORT, + GL_UNSIGNED_SHORT, + GL_BYTE, + GL_UNSIGNED_BYTE +}; + +bool checkGLError(const char* name = nullptr); +bool checkGLErrorDebug(const char* name = nullptr); + +class GLBackend; + +template +struct GLObject : public GPUObject { +public: + GLObject(const std::weak_ptr& backend, const GPUType& gpuObject, GLuint id) : _gpuObject(gpuObject), _id(id), _backend(backend) {} + + virtual ~GLObject() { } + + const GPUType& _gpuObject; + const GLuint _id; +protected: + const std::weak_ptr _backend; +}; + +class GlBuffer; +class GLFramebuffer; +class GLPipeline; +class GLQuery; +class GLState; +class GLShader; +class GLTexture; + +} } // namespace gpu::gl + +#define CHECK_GL_ERROR() gpu::gl::checkGLErrorDebug(__FUNCTION__) + +#endif + + + diff --git a/libraries/gpu-gles/src/gpu/gl/GLState.cpp b/libraries/gpu-gles/src/gpu/gl/GLState.cpp new file mode 100644 index 0000000000..b6d917b928 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLState.cpp @@ -0,0 +1,248 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 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 +// + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1 +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif +#endif + + +#include "GLState.h" + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +typedef GLState::Command Command; +typedef GLState::CommandPointer CommandPointer; +typedef GLState::Command1 Command1U; +typedef GLState::Command1 Command1I; +typedef GLState::Command1 Command1B; +typedef GLState::Command1 CommandDepthBias; +typedef GLState::Command1 CommandDepthTest; +typedef GLState::Command3 CommandStencil; +typedef GLState::Command1 CommandBlend; + +const GLState::Commands makeResetStateCommands(); + +// NOTE: This must stay in sync with the ordering of the State::Field enum +const GLState::Commands makeResetStateCommands() { + // Since State::DEFAULT is a static defined in another .cpp the initialisation order is random + // and we have a 50/50 chance that State::DEFAULT is not yet initialized. + // Since State::DEFAULT = State::Data() it is much easier to not use the actual State::DEFAULT + // but another State::Data object with a default initialization. + const State::Data DEFAULT = State::Data(); + + auto depthBiasCommand = std::make_shared(&GLBackend::do_setStateDepthBias, + Vec2(DEFAULT.depthBias, DEFAULT.depthBiasSlopeScale)); + auto stencilCommand = std::make_shared(&GLBackend::do_setStateStencil, DEFAULT.stencilActivation, + DEFAULT.stencilTestFront, DEFAULT.stencilTestBack); + + // The state commands to reset to default, + // WARNING depending on the order of the State::Field enum + return { + std::make_shared(&GLBackend::do_setStateFillMode, DEFAULT.fillMode), + std::make_shared(&GLBackend::do_setStateCullMode, DEFAULT.cullMode), + std::make_shared(&GLBackend::do_setStateFrontFaceClockwise, DEFAULT.frontFaceClockwise), + std::make_shared(&GLBackend::do_setStateDepthClampEnable, DEFAULT.depthClampEnable), + std::make_shared(&GLBackend::do_setStateScissorEnable, DEFAULT.scissorEnable), + std::make_shared(&GLBackend::do_setStateMultisampleEnable, DEFAULT.multisampleEnable), + std::make_shared(&GLBackend::do_setStateAntialiasedLineEnable, DEFAULT.antialisedLineEnable), + + // Depth bias has 2 fields in State but really one call in GLBackend + CommandPointer(depthBiasCommand), + CommandPointer(depthBiasCommand), + + std::make_shared(&GLBackend::do_setStateDepthTest, DEFAULT.depthTest), + + // Depth bias has 3 fields in State but really one call in GLBackend + CommandPointer(stencilCommand), + CommandPointer(stencilCommand), + CommandPointer(stencilCommand), + + std::make_shared(&GLBackend::do_setStateSampleMask, DEFAULT.sampleMask), + + std::make_shared(&GLBackend::do_setStateAlphaToCoverageEnable, DEFAULT.alphaToCoverageEnable), + + std::make_shared(&GLBackend::do_setStateBlend, DEFAULT.blendFunction), + + std::make_shared(&GLBackend::do_setStateColorWriteMask, DEFAULT.colorWriteMask) + }; +} + +const GLState::Commands GLState::_resetStateCommands = makeResetStateCommands(); + + +void generateFillMode(GLState::Commands& commands, State::FillMode fillMode) { + commands.push_back(std::make_shared(&GLBackend::do_setStateFillMode, int32(fillMode))); +} + +void generateCullMode(GLState::Commands& commands, State::CullMode cullMode) { + commands.push_back(std::make_shared(&GLBackend::do_setStateCullMode, int32(cullMode))); +} + +void generateFrontFaceClockwise(GLState::Commands& commands, bool isClockwise) { + commands.push_back(std::make_shared(&GLBackend::do_setStateFrontFaceClockwise, isClockwise)); +} + +void generateDepthClampEnable(GLState::Commands& commands, bool enable) { + commands.push_back(std::make_shared(&GLBackend::do_setStateDepthClampEnable, enable)); +} + +void generateScissorEnable(GLState::Commands& commands, bool enable) { + commands.push_back(std::make_shared(&GLBackend::do_setStateScissorEnable, enable)); +} + +void generateMultisampleEnable(GLState::Commands& commands, bool enable) { + commands.push_back(std::make_shared(&GLBackend::do_setStateMultisampleEnable, enable)); +} + +void generateAntialiasedLineEnable(GLState::Commands& commands, bool enable) { + commands.push_back(std::make_shared(&GLBackend::do_setStateAntialiasedLineEnable, enable)); +} + +void generateDepthBias(GLState::Commands& commands, const State& state) { + commands.push_back(std::make_shared(&GLBackend::do_setStateDepthBias, Vec2(state.getDepthBias(), state.getDepthBiasSlopeScale()))); +} + +void generateDepthTest(GLState::Commands& commands, const State::DepthTest& test) { + commands.push_back(std::make_shared(&GLBackend::do_setStateDepthTest, int32(test.getRaw()))); +} + +void generateStencil(GLState::Commands& commands, const State& state) { + commands.push_back(std::make_shared(&GLBackend::do_setStateStencil, state.getStencilActivation(), state.getStencilTestFront(), state.getStencilTestBack())); +} + +void generateAlphaToCoverageEnable(GLState::Commands& commands, bool enable) { + commands.push_back(std::make_shared(&GLBackend::do_setStateAlphaToCoverageEnable, enable)); +} + +void generateSampleMask(GLState::Commands& commands, uint32 mask) { + commands.push_back(std::make_shared(&GLBackend::do_setStateSampleMask, mask)); +} + +void generateBlend(GLState::Commands& commands, const State& state) { + commands.push_back(std::make_shared(&GLBackend::do_setStateBlend, state.getBlendFunction())); +} + +void generateColorWriteMask(GLState::Commands& commands, uint32 mask) { + commands.push_back(std::make_shared(&GLBackend::do_setStateColorWriteMask, mask)); +} + +GLState* GLState::sync(const State& state) { + GLState* object = Backend::getGPUObject(state); + + // If GPU object already created then good + if (object) { + return object; + } + + // Else allocate and create the GLState + if (!object) { + object = new GLState(); + Backend::setGPUObject(state, object); + } + + // here, we need to regenerate something so let's do it all + object->_commands.clear(); + object->_stamp = state.getStamp(); + object->_signature = state.getSignature(); + + bool depthBias = false; + bool stencilState = false; + + // go thorugh the list of state fields in the State and record the corresponding gl command + for (int i = 0; i < State::NUM_FIELDS; i++) { + if (state.getSignature()[i]) { + switch (i) { + case State::FILL_MODE: { + generateFillMode(object->_commands, state.getFillMode()); + break; + } + case State::CULL_MODE: { + generateCullMode(object->_commands, state.getCullMode()); + break; + } + case State::DEPTH_BIAS: + case State::DEPTH_BIAS_SLOPE_SCALE: { + depthBias = true; + break; + } + case State::FRONT_FACE_CLOCKWISE: { + generateFrontFaceClockwise(object->_commands, state.isFrontFaceClockwise()); + break; + } + case State::DEPTH_CLAMP_ENABLE: { + generateDepthClampEnable(object->_commands, state.isDepthClampEnable()); + break; + } + case State::SCISSOR_ENABLE: { + generateScissorEnable(object->_commands, state.isScissorEnable()); + break; + } + case State::MULTISAMPLE_ENABLE: { + generateMultisampleEnable(object->_commands, state.isMultisampleEnable()); + break; + } + case State::ANTIALISED_LINE_ENABLE: { + generateAntialiasedLineEnable(object->_commands, state.isAntialiasedLineEnable()); + break; + } + case State::DEPTH_TEST: { + generateDepthTest(object->_commands, state.getDepthTest()); + break; + } + + case State::STENCIL_ACTIVATION: + case State::STENCIL_TEST_FRONT: + case State::STENCIL_TEST_BACK: { + stencilState = true; + break; + } + + case State::SAMPLE_MASK: { + generateSampleMask(object->_commands, state.getSampleMask()); + break; + } + case State::ALPHA_TO_COVERAGE_ENABLE: { + generateAlphaToCoverageEnable(object->_commands, state.isAlphaToCoverageEnabled()); + break; + } + + case State::BLEND_FUNCTION: { + generateBlend(object->_commands, state); + break; + } + + case State::COLOR_WRITE_MASK: { + generateColorWriteMask(object->_commands, state.getColorWriteMask()); + break; + } + } + } + } + + if (depthBias) { + generateDepthBias(object->_commands, state); + } + + if (stencilState) { + generateStencil(object->_commands, state); + } + + return object; +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLState.h b/libraries/gpu-gles/src/gpu/gl/GLState.h new file mode 100644 index 0000000000..82635db893 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLState.h @@ -0,0 +1,73 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLState_h +#define hifi_gpu_gl_GLState_h + +#include "GLShared.h" + +#include + +namespace gpu { namespace gl { + +class GLBackend; +class GLState : public GPUObject { +public: + static GLState* sync(const State& state); + + class Command { + public: + virtual void run(GLBackend* backend) = 0; + Command() {} + virtual ~Command() {}; + }; + + template class Command1 : public Command { + public: + typedef void (GLBackend::*GLFunction)(T); + void run(GLBackend* backend) { (backend->*(_func))(_param); } + Command1(GLFunction func, T param) : _func(func), _param(param) {}; + GLFunction _func; + T _param; + }; + template class Command2 : public Command { + public: + typedef void (GLBackend::*GLFunction)(T, U); + void run(GLBackend* backend) { (backend->*(_func))(_param0, _param1); } + Command2(GLFunction func, T param0, U param1) : _func(func), _param0(param0), _param1(param1) {}; + GLFunction _func; + T _param0; + U _param1; + }; + + template class Command3 : public Command { + public: + typedef void (GLBackend::*GLFunction)(T, U, V); + void run(GLBackend* backend) { (backend->*(_func))(_param0, _param1, _param2); } + Command3(GLFunction func, T param0, U param1, V param2) : _func(func), _param0(param0), _param1(param1), _param2(param2) {}; + GLFunction _func; + T _param0; + U _param1; + V _param2; + }; + + typedef std::shared_ptr< Command > CommandPointer; + typedef std::vector< CommandPointer > Commands; + + Commands _commands; + Stamp _stamp; + State::Signature _signature; + + // The state commands to reset to default, + static const Commands _resetStateCommands; + + friend class GLBackend; +}; + +} } + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp new file mode 100644 index 0000000000..6eec4b5292 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp @@ -0,0 +1,648 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GLTexelFormat.h" + +using namespace gpu; +using namespace gpu::gl; + + +GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { +// qDebug() << "GLTexelFormat::evalGLTexelFormatInternal " << dstFormat.getDimension() << ", " << dstFormat.getSemantic() << ", " << dstFormat.getType(); + GLenum result = GL_RGBA8; + switch (dstFormat.getDimension()) { + case gpu::SCALAR: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + case gpu::SRGB: + case gpu::SRGBA: + switch (dstFormat.getType()) { + case gpu::UINT32: + result = GL_R32UI; + break; + case gpu::INT32: + result = GL_R32I; + break; + case gpu::NUINT32: + result = GL_R8; + break; + case gpu::NINT32: + result = GL_R8_SNORM; + break; + case gpu::FLOAT: + result = GL_R32F; + break; + case gpu::UINT16: + result = GL_R16UI; + break; + case gpu::INT16: + result = GL_R16I; + break; + case gpu::HALF: + result = GL_R16F; + break; + case gpu::UINT8: + result = GL_R8UI; + break; + case gpu::INT8: + result = GL_R8I; + break; + case gpu::NUINT8: + if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { + //result = GL_SLUMINANCE8; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormatInternal GL_SLUMINANCE8"; + } else { + result = GL_R8; + } + break; + case gpu::NINT8: + result = GL_R8_SNORM; + break; + default: + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormatInternal " << dstFormat.getType(); + Q_UNREACHABLE(); + break; + } + break; + case gpu::R11G11B10: + // the type should be float + result = GL_R11F_G11F_B10F; + break; + + case gpu::DEPTH: + result = GL_DEPTH_COMPONENT16; + switch (dstFormat.getType()) { + case gpu::FLOAT: + result = GL_DEPTH_COMPONENT32F; + break; + case gpu::UINT16: + case gpu::INT16: + case gpu::NUINT16: + case gpu::NINT16: + case gpu::HALF: + result = GL_DEPTH_COMPONENT16; + break; + case gpu::UINT8: + case gpu::INT8: + case gpu::NUINT8: + case gpu::NINT8: + result = GL_DEPTH_COMPONENT24; + break; + default: + Q_UNREACHABLE(); + break; + } + break; + + case gpu::DEPTH_STENCIL: + result = GL_DEPTH24_STENCIL8; + break; + + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + case gpu::VEC2: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + result = GL_RG8; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC3: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + result = GL_RGB8; + break; + case gpu::SRGB: + case gpu::SRGBA: + //result = GL_SRGB8; // standard 2.2 gamma correction color + result = GL_RGB8; // standard 2.2 gamma correction color + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC4: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + result = GL_RGB8; + break; + case gpu::RGBA: + switch (dstFormat.getType()) { + case gpu::UINT32: + result = GL_RGBA32UI; + break; + case gpu::INT32: + result = GL_RGBA32I; + break; + case gpu::FLOAT: + result = GL_RGBA32F; + break; + case gpu::UINT16: + result = GL_RGBA16UI; + break; + case gpu::INT16: + result = GL_RGBA16I; + break; + case gpu::HALF: + result = GL_RGBA16F; + break; + case gpu::UINT8: + result = GL_RGBA8UI; + break; + case gpu::INT8: + result = GL_RGBA8I; + break; + case gpu::NUINT8: + result = GL_RGBA8; + break; + case gpu::NINT8: + result = GL_RGBA8_SNORM; + break; + case gpu::NUINT32: + case gpu::NINT32: + case gpu::NUM_TYPES: // quiet compiler + Q_UNREACHABLE(); + } + break; + case gpu::SRGB: + //result = GL_SRGB8; + result = GL_RGB8; + qDebug() << "SRGBA Here 2"; + break; + case gpu::SRGBA: + result = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + //qDebug() << "GLTexelFormat::evalGLTexelFormatInternal result " << result; + + return result; +} + +GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) { +// qDebug() << "GLTexelFormat::evalGLTexelFormat dst.getDimension=" << dstFormat.getDimension() << " dst.getType=" << dstFormat.getType() << " dst.getSemantic=" << dstFormat.getSemantic(); +// qDebug() << "GLTexelFormat::evalGLTexelFormat src.getDimension=" << srcFormat.getDimension() << " src.getType=" << srcFormat.getType() << " src.getSemantic=" << srcFormat.getSemantic(); + + if (dstFormat != srcFormat) { + GLTexelFormat texel = { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }; + + switch (dstFormat.getDimension()) { + case gpu::SCALAR: { + texel.format = GL_RED; + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_R8; + break; + + //CLIMAX_MERGE_START + // case gpu::COMPRESSED_R: + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RED_RGTC1"; + // //texel.internalFormat = GL_COMPRESSED_RED_RGTC1; + // break; + //CLIMAX_MERGE_END + + case gpu::DEPTH: + texel.internalFormat = GL_DEPTH_COMPONENT32_OES; + break; + case gpu::DEPTH_STENCIL: + texel.type = GL_UNSIGNED_INT_24_8; + texel.format = GL_DEPTH_STENCIL; + texel.internalFormat = GL_DEPTH24_STENCIL8; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + case gpu::VEC2: { + texel.format = GL_RG; + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_RG8; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC3: { + texel.format = GL_RGB; + + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_RGB8; + break; + //CLIMAX_MERGE_START + //not needed? + // case gpu::COMPRESSED_RGB: + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGB"; + // //texel.internalFormat = GL_COMPRESSED_RGB; + // break; + // case gpu::COMPRESSED_SRGB: + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB"; + // //texel.internalFormat = GL_COMPRESSED_SRGB; + // break; + //CLIMAX_MERGE_END + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC4: { + texel.format = GL_RGBA; + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (srcFormat.getSemantic()) { + case gpu::BGRA: + case gpu::SBGRA: + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_BGRA"; + //texel.format = GL_BGRA; + break; + case gpu::RGB: + case gpu::RGBA: + case gpu::SRGB: + case gpu::SRGBA: + default: + break; + }; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + texel.internalFormat = GL_RGB8; + break; + case gpu::RGBA: + texel.internalFormat = GL_RGBA8; + break; + case gpu::SRGB: + //texel.internalFormat = GL_SRGB8; + texel.internalFormat = GL_RGB8; + qDebug() << "SRGBA Here 3"; + break; + case gpu::SRGBA: + texel.internalFormat = GL_SRGB8_ALPHA8; + break; + + //CLIMAX_MERGE_START + // case gpu::COMPRESSED_RGBA: + // //texel.internalFormat = GL_COMPRESSED_RGBA; + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGBA"; + // break; + // case gpu::COMPRESSED_SRGBA: + // //texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA; + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB_ALPHA"; + // break; + //CLIMAX_MERGE_END + // FIXME: WE will want to support this later + /* + case gpu::COMPRESSED_BC3_RGBA: + texel.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + case gpu::COMPRESSED_BC3_SRGBA: + texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + break; + + case gpu::COMPRESSED_BC7_RGBA: + texel.internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + break; + case gpu::COMPRESSED_BC7_SRGBA: + texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; + break; + */ + + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + return texel; + } else { + GLTexelFormat texel = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }; + + switch (dstFormat.getDimension()) { + case gpu::SCALAR: { + texel.format = GL_RED; + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + //CLIMAX_MERGE_START + // case gpu::COMPRESSED_R: { + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RED_RGTC1"; + // //texel.internalFormat = GL_COMPRESSED_RED_RGTC1; + // break; + // } + case gpu::RGB: + case gpu::RGBA: + case gpu::SRGB: + case gpu::SRGBA: + texel.internalFormat = GL_RED; + switch (dstFormat.getType()) { + case gpu::UINT32: { + texel.internalFormat = GL_R32UI; + break; + } + case gpu::INT32: { + texel.internalFormat = GL_R32I; + break; + } + case gpu::NUINT32: { + texel.internalFormat = GL_R8; + break; + } + case gpu::NINT32: { + texel.internalFormat = GL_R8_SNORM; + break; + } + case gpu::FLOAT: { + texel.internalFormat = GL_R32F; + break; + } + case gpu::UINT16: { + texel.internalFormat = GL_R16UI; + break; + } + case gpu::INT16: { + texel.internalFormat = GL_R16I; + break; + } + case gpu::NUINT16: { + //texel.internalFormat = GL_R16; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_R16"; + break; + } + case gpu::NINT16: { + //texel.internalFormat = GL_R16_SNORM; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_R16_SNORM"; + break; + } + case gpu::HALF: { + texel.internalFormat = GL_R16F; + break; + } + case gpu::UINT8: { + texel.internalFormat = GL_R8UI; + break; + } + case gpu::INT8: { + texel.internalFormat = GL_R8I; + break; + } + case gpu::NUINT8: { + if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { +// texel.internalFormat = GL_SLUMINANCE8; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_SLUMINANCE8"; + + } else { + texel.internalFormat = GL_R8; + } + break; + } + case gpu::NINT8: { + texel.internalFormat = GL_R8_SNORM; + break; + } + case gpu::NUM_TYPES: { // quiet compiler + Q_UNREACHABLE(); + } + + } + break; + + case gpu::R11G11B10: + texel.format = GL_RGB; + // the type should be float + texel.internalFormat = GL_R11F_G11F_B10F; + break; + + case gpu::DEPTH: + texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it + texel.internalFormat = GL_DEPTH_COMPONENT32_OES; + switch (dstFormat.getType()) { + case gpu::UINT32: + case gpu::INT32: + case gpu::NUINT32: + case gpu::NINT32: { + texel.internalFormat = GL_DEPTH_COMPONENT32_OES; + break; + } + case gpu::FLOAT: { + texel.internalFormat = GL_DEPTH_COMPONENT32F; + break; + } + case gpu::UINT16: + case gpu::INT16: + case gpu::NUINT16: + case gpu::NINT16: + case gpu::HALF: { + texel.internalFormat = GL_DEPTH_COMPONENT16; + break; + } + case gpu::UINT8: + case gpu::INT8: + case gpu::NUINT8: + case gpu::NINT8: { + texel.internalFormat = GL_DEPTH_COMPONENT24; + break; + } + case gpu::NUM_TYPES: { // quiet compiler + Q_UNREACHABLE(); + } + } + break; + case gpu::DEPTH_STENCIL: + texel.type = GL_UNSIGNED_INT_24_8; + texel.format = GL_DEPTH_STENCIL; + texel.internalFormat = GL_DEPTH24_STENCIL8; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC2: { + texel.format = GL_RG; + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_RG8; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC3: { + texel.format = GL_RGB; + + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_RGB8; + break; + case gpu::SRGB: + case gpu::SRGBA: + //texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color + texel.internalFormat = GL_RGB8; // standard 2.2 gamma correction color + break; + //CLIMAX_MERGE_START + // case gpu::COMPRESSED_RGB: + // //texel.internalFormat = GL_COMPRESSED_RGB; + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGB"; + // break; + // case gpu::COMPRESSED_SRGB: + // //texel.internalFormat = GL_COMPRESSED_SRGB; + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB"; + // break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + case gpu::VEC4: { + texel.format = GL_RGBA; + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + texel.internalFormat = GL_RGB8; + break; + case gpu::RGBA: + texel.internalFormat = GL_RGBA8; + switch (dstFormat.getType()) { + case gpu::UINT32: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA32UI; + break; + case gpu::INT32: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA32I; + break; + case gpu::FLOAT: + texel.internalFormat = GL_RGBA32F; + break; + case gpu::UINT16: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA16UI; + break; + case gpu::INT16: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA16I; + break; + case gpu::NUINT16: + texel.format = GL_RGBA; + //texel.internalFormat = GL_RGBA16; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_RGBA16"; + break; + case gpu::NINT16: + texel.format = GL_RGBA; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_RGBA16_SNORM"; + //texel.internalFormat = GL_RGBA16_SNORM; + break; + case gpu::HALF: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA16F; + break; + case gpu::UINT8: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA8UI; + break; + case gpu::INT8: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA8I; + break; + case gpu::NUINT8: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8; + break; + case gpu::NINT8: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8_SNORM; + break; + case gpu::NUINT32: + case gpu::NINT32: + case gpu::NUM_TYPES: // quiet compiler + Q_UNREACHABLE(); + } + break; + case gpu::SRGB: + //texel.internalFormat = GL_SRGB8; + texel.internalFormat = GL_RGB8; // standard 2.2 gamma correction color + break; + case gpu::SRGBA: + texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color + break; + //CLIMAX_MERGE_START + // case gpu::COMPRESSED_RGBA: + // //texel.internalFormat = GL_COMPRESSED_RGBA; + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGBA"; + // break; + // case gpu::COMPRESSED_SRGBA: + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB_ALPHA"; + // //texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA; + // break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + //qDebug() << "GLTexelFormat::evalGLTexelFormat Texel.type " << texel.type << " - texel.format=" << texel.format << " texel.internalFormat=" << texel.internalFormat; + return texel; + } +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h new file mode 100644 index 0000000000..94ded3dc23 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLTexelFormat_h +#define hifi_gpu_gl_GLTexelFormat_h + +#include "GLShared.h" + +namespace gpu { namespace gl { + +class GLTexelFormat { +public: + GLenum internalFormat; + GLenum format; + GLenum type; + + static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) { + return evalGLTexelFormat(dstFormat, dstFormat); + } + static GLenum evalGLTexelFormatInternal(const Element& dstFormat); + + static GLTexelFormat evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat); +}; + +} } + + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp new file mode 100644 index 0000000000..5f5e3a9be1 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp @@ -0,0 +1,323 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GLTexture.h" + +#include + +#include "GLTextureTransfer.h" +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +std::shared_ptr GLTexture::_textureTransferHelper; + +// FIXME placeholder for texture memory over-use +#define DEFAULT_MAX_MEMORY_MB 256 +#define MIN_FREE_GPU_MEMORY_PERCENTAGE 0.25f +#define OVER_MEMORY_PRESSURE 2.0f + +const GLenum GLTexture::CUBE_FACE_LAYOUT[6] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +}; + +const GLenum GLTexture::WRAP_MODES[Sampler::NUM_WRAP_MODES] = { + GL_REPEAT, // WRAP_REPEAT, + GL_MIRRORED_REPEAT, // WRAP_MIRROR, + GL_CLAMP_TO_EDGE, // WRAP_CLAMP, + GL_CLAMP_TO_BORDER_EXT, // WRAP_BORDER, + + //GL_MIRROR_CLAMP_TO_EDGE_EXT // WRAP_MIRROR_ONCE, +// qDebug() << "TODO: GLTexture.cpp:WRAP_MODES GL_MIRROR_CLAMP_TO_EDGE_EXT"; +}; + +const GLFilterMode GLTexture::FILTER_MODES[Sampler::NUM_FILTERS] = { + { GL_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_POINT, + { GL_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR, + { GL_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT, + { GL_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR, + + { GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT, + { GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_MAG_POINT_MIP_LINEAR, + { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, + { GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_POINT_MAG_MIP_LINEAR, + { GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_MIP_POINT, + { GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, + { GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR_MIP_POINT, + { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_MIP_LINEAR, + { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR } //FILTER_ANISOTROPIC, +}; + +GLenum GLTexture::getGLTextureType(const Texture& texture) { + switch (texture.getType()) { + case Texture::TEX_2D: + return GL_TEXTURE_2D; + break; + + case Texture::TEX_CUBE: + return GL_TEXTURE_CUBE_MAP; + break; + + default: + qFatal("Unsupported texture type"); + } + Q_UNREACHABLE(); + return GL_TEXTURE_2D; +} + + +const std::vector& GLTexture::getFaceTargets(GLenum target) { + static std::vector cubeFaceTargets { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z + }; + static std::vector faceTargets { + GL_TEXTURE_2D + }; + switch (target) { + case GL_TEXTURE_2D: + return faceTargets; + case GL_TEXTURE_CUBE_MAP: + return cubeFaceTargets; + default: + Q_UNREACHABLE(); + break; + } + Q_UNREACHABLE(); + return faceTargets; +} + +// Default texture memory = GPU total memory - 2GB +#define GPU_MEMORY_RESERVE_BYTES MB_TO_BYTES(2048) +// Minimum texture memory = 1GB +#define TEXTURE_MEMORY_MIN_BYTES MB_TO_BYTES(1024) + + +float GLTexture::getMemoryPressure() { + // Check for an explicit memory limit + auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage(); + + + // If no memory limit has been set, use a percentage of the total dedicated memory + if (!availableTextureMemory) { +#if 0 + auto totalMemory = getDedicatedMemory(); + if ((GPU_MEMORY_RESERVE_BYTES + TEXTURE_MEMORY_MIN_BYTES) > totalMemory) { + availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; + } else { + availableTextureMemory = totalMemory - GPU_MEMORY_RESERVE_BYTES; + } +#else + // Hardcode texture limit for sparse textures at 1 GB for now + availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; +#endif + } + + // Return the consumed texture memory divided by the available texture memory. + //CLIMAX_MERGE_START + //auto consumedGpuMemory = Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage(); + //float memoryPressure = (float)consumedGpuMemory / (float)availableTextureMemory; + //static Context::Size lastConsumedGpuMemory = 0; + //if (memoryPressure > 1.0f && lastConsumedGpuMemory != consumedGpuMemory) { + // lastConsumedGpuMemory = consumedGpuMemory; + // qCDebug(gpugllogging) << "Exceeded max allowed texture memory: " << consumedGpuMemory << " / " << availableTextureMemory; + //} + //return memoryPressure; + return 0; + +} + + +// Create the texture and allocate storage +GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable) : + GLObject(backend, texture, id), + _external(false), + _source(texture.source()), + _storageStamp(texture.getStamp()), + _target(getGLTextureType(texture)), + _internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())), + _maxMip(texture.getMaxMip()), + _minMip(texture.getMinMip()), + _virtualSize(texture.evalTotalSize()), + _transferrable(transferrable) +{ + //qDebug() << "GLTexture::GLTexture building GLTexture with _internalFormat" << _internalFormat; + auto strongBackend = _backend.lock(); + strongBackend->recycle(); + //CLIMAX_MERGE_START + //Backend::incrementTextureGPUCount(); + //Backend::updateTextureGPUVirtualMemoryUsage(0, _virtualSize); + //CLIMAX_MERGE_END + Backend::setGPUObject(texture, this); +} + +GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : + GLObject(backend, texture, id), + _external(true), + _source(texture.source()), + _storageStamp(0), + _target(getGLTextureType(texture)), + _internalFormat(GL_RGBA8), + // FIXME force mips to 0? + _maxMip(texture.getMaxMip()), + _minMip(texture.getMinMip()), + _virtualSize(0), + _transferrable(false) +{ + Backend::setGPUObject(texture, this); + + // FIXME Is this necessary? + //withPreservedTexture([this] { + // syncSampler(); + // if (_gpuObject.isAutogenerateMips()) { + // generateMips(); + // } + //}); +} + +GLTexture::~GLTexture() { + auto backend = _backend.lock(); + if (backend) { + if (_external) { + auto recycler = _gpuObject.getExternalRecycler(); + if (recycler) { + backend->releaseExternalTexture(_id, recycler); + } else { + qWarning() << "No recycler available for texture " << _id << " possible leak"; + } + } else if (_id) { + // WARNING! Sparse textures do not use this code path. See GL45BackendTexture for + // the GL45Texture destructor for doing any required work tracking GPU stats + backend->releaseTexture(_id, _size); + } + + ////CLIMAX_MERGE_START + //if (!_external && !_transferrable) { + // Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0); + //} + } + //Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); + //CLIMAX_MERGE_END +} + +void GLTexture::createTexture() { + withPreservedTexture([&] { + allocateStorage(); + (void)CHECK_GL_ERROR(); + syncSampler(); + (void)CHECK_GL_ERROR(); + }); +} + +void GLTexture::withPreservedTexture(std::function f) const { + GLint boundTex = -1; + switch (_target) { + case GL_TEXTURE_2D: + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); + break; + + case GL_TEXTURE_CUBE_MAP: + glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); + break; + + default: + qFatal("Unsupported texture type"); + } + (void)CHECK_GL_ERROR(); + + glBindTexture(_target, _texture); + f(); + glBindTexture(_target, boundTex); + (void)CHECK_GL_ERROR(); +} + +void GLTexture::setSize(GLuint size) const { + ////CLIMAX_MERGE_START + //if (!_external && !_transferrable) { + // Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0); + //} + //Backend::updateTextureGPUMemoryUsage(_size, size); + const_cast(_size) = size; +} + +bool GLTexture::isInvalid() const { + return _storageStamp < _gpuObject.getStamp(); +} + +bool GLTexture::isOutdated() const { + return GLSyncState::Idle == _syncState && _contentStamp < _gpuObject.getDataStamp(); +} + +bool GLTexture::isReady() const { + // If we have an invalid texture, we're never ready + if (isInvalid()) { + return false; + } + + auto syncState = _syncState.load(); + if (isOutdated() || Idle != syncState) { + return false; + } + + return true; +} + + +// Do any post-transfer operations that might be required on the main context / rendering thread +void GLTexture::postTransfer() { + //CLIMAX_MERGE_START + + // setSyncState(GLSyncState::Idle); + // ++_transferCount; + + // // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory + // switch (_gpuObject.getType()) { + // case Texture::TEX_2D: + // for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + // if (_gpuObject.isStoredMipFaceAvailable(i)) { + // _gpuObject.notifyMipFaceGPULoaded(i); + // } + // } + // break; + + // case Texture::TEX_CUBE: + // // transfer pixels from each faces + // for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) { + // for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + // if (_gpuObject.isStoredMipFaceAvailable(i, f)) { + // _gpuObject.notifyMipFaceGPULoaded(i, f); + // } + // } + // } + // break; + + // default: + // qCWarning(gpugllogging) << __FUNCTION__ << " case for Texture Type " << _gpuObject.getType() << " not supported"; + // break; + // } + //CLIMAX_MERGE_END +} + +void GLTexture::initTextureTransferHelper() { + _textureTransferHelper = std::make_shared(); +} + +void GLTexture::startTransfer() { + createTexture(); +} + +void GLTexture::finishTransfer() { + if (_gpuObject.isAutogenerateMips()) { + generateMips(); + } +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexture.h b/libraries/gpu-gles/src/gpu/gl/GLTexture.h new file mode 100644 index 0000000000..03353ae67d --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTexture.h @@ -0,0 +1,233 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLTexture_h +#define hifi_gpu_gl_GLTexture_h + +#include "GLShared.h" +#include "GLTextureTransfer.h" +#include "GLBackend.h" +#include "GLTexelFormat.h" + +namespace gpu { namespace gl { + +struct GLFilterMode { + GLint minFilter; + GLint magFilter; +}; + + +class GLTexture : public GLObject { +public: + static const uint16_t INVALID_MIP { (uint16_t)-1 }; + static const uint8_t INVALID_FACE { (uint8_t)-1 }; + + static void initTextureTransferHelper(); + static std::shared_ptr _textureTransferHelper; + + template + static GLTexture* sync(GLBackend& backend, const TexturePointer& texturePointer, bool needTransfer) { + const Texture& texture = *texturePointer; + + // Special case external textures + //CLIMAX_MERGE_START + //Z:/HiFi_Android/HiFi_GIT/libraries/gpu-gl-android/src/gpu/gl/../gles/../gl/GLTexture.h:37:32: error: no member named 'isExternal' in 'gpu::Texture::Usage' + // The only instance of this being used again. replace. + // if (texture.getUsage().isExternal()) { + // Texture::ExternalUpdates updates = texture.getUpdates(); + // if (!updates.empty()) { + // Texture::ExternalRecycler recycler = texture.getExternalRecycler(); + // Q_ASSERT(recycler); + // // Discard any superfluous updates + // while (updates.size() > 1) { + // const auto& update = updates.front(); + // // Superfluous updates will never have been read, but we want to ensure the previous + // // writes to them are complete before they're written again, so return them with the + // // same fences they arrived with. This can happen on any thread because no GL context + // // work is involved + // recycler(update.first, update.second); + // updates.pop_front(); + // } + + // // The last texture remaining is the one we'll use to create the GLTexture + // const auto& update = updates.front(); + // // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence + // if (update.second) { + // GLsync fence = static_cast(update.second); + // glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + // glDeleteSync(fence); + // } + + // // Create the new texture object (replaces any previous texture object) + // new GLTextureType(backend.shared_from_this(), texture, update.first); + // } + + + // Return the texture object (if any) associated with the texture, without extensive logic + // (external textures are + //return Backend::getGPUObject(texture); + //} + //CLIMAX_MERGE_END + if (!texture.isDefined()) { + // NO texture definition yet so let's avoid thinking + return nullptr; + } + + // If the object hasn't been created, or the object definition is out of date, drop and re-create + GLTexture* object = Backend::getGPUObject(texture); + + // Create the texture if need be (force re-creation if the storage stamp changes + // for easier use of immutable storage) + if (!object || object->isInvalid()) { + // This automatically any previous texture + object = new GLTextureType(backend.shared_from_this(), texture, needTransfer); + if (!object->_transferrable) { + object->createTexture(); + object->_contentStamp = texture.getDataStamp(); + object->updateSize(); + object->postTransfer(); + } + } + + // Object maybe doens't neet to be tranasferred after creation + if (!object->_transferrable) { + return object; + } + + // If we just did a transfer, return the object after doing post-transfer work + if (GLSyncState::Transferred == object->getSyncState()) { + object->postTransfer(); + } + + if (object->isOutdated()) { + // Object might be outdated, if so, start the transfer + // (outdated objects that are already in transfer will have reported 'true' for ready() + _textureTransferHelper->transferTexture(texturePointer); + return nullptr; + } + + if (!object->isReady()) { + return nullptr; + } + + ((GLTexture*)object)->updateMips(); + + return object; + } + + template + static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) { + if (!texture) { + return 0; + } + GLTexture* object { nullptr }; + if (shouldSync) { + object = sync(backend, texture, shouldSync); + } else { + object = Backend::getGPUObject(*texture); + } + + if (!object) { + return 0; + } + + if (!shouldSync) { + return object->_id; + } + + // Don't return textures that are in transfer state + if ((object->getSyncState() != GLSyncState::Idle) || + // Don't return transferrable textures that have never completed transfer + (!object->_transferrable || 0 != object->_transferCount)) { + return 0; + } + + return object->_id; + } + + ~GLTexture(); + + // Is this texture generated outside the GPU library? + const bool _external; + const GLuint& _texture { _id }; + const std::string _source; + const Stamp _storageStamp; + const GLenum _target; + const GLenum _internalFormat; + const uint16 _maxMip; + uint16 _minMip; + const GLuint _virtualSize; // theoretical size as expected + Stamp _contentStamp { 0 }; + const bool _transferrable; + Size _transferCount { 0 }; + GLuint size() const { return _size; } + GLSyncState getSyncState() const { return _syncState; } + + // Is the storage out of date relative to the gpu texture? + bool isInvalid() const; + + // Is the content out of date relative to the gpu texture? + bool isOutdated() const; + + // Is the texture in a state where it can be rendered with no work? + bool isReady() const; + + // Execute any post-move operations that must occur only on the main thread + virtual void postTransfer(); + + uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; } + + static const size_t CUBE_NUM_FACES = 6; + static const GLenum CUBE_FACE_LAYOUT[6]; + static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS]; + static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES]; + + // Return a floating point value indicating how much of the allowed + // texture memory we are currently consuming. A value of 0 indicates + // no texture memory usage, while a value of 1 indicates all available / allowed memory + // is consumed. A value above 1 indicates that there is a problem. + static float getMemoryPressure(); +protected: + + static const std::vector& getFaceTargets(GLenum textureType); + + static GLenum getGLTextureType(const Texture& texture); + + + const GLuint _size { 0 }; // true size as reported by the gl api + std::atomic _syncState { GLSyncState::Idle }; + + GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable); + GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); + + void setSyncState(GLSyncState syncState) { _syncState = syncState; } + + void createTexture(); + + virtual void updateMips() {} + virtual void allocateStorage() const = 0; + virtual void updateSize() const = 0; + virtual void syncSampler() const = 0; + virtual void generateMips() const = 0; + virtual void withPreservedTexture(std::function f) const; + +protected: + void setSize(GLuint size) const; + + virtual void startTransfer(); + // Returns true if this is the last block required to complete transfer + virtual bool continueTransfer() { return false; } + virtual void finishTransfer(); + +private: + friend class GLTextureTransferHelper; + friend class GLBackend; +}; + +} } + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp new file mode 100644 index 0000000000..cec46cb90d --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp @@ -0,0 +1,207 @@ +// +// Created by Bradley Austin Davis on 2016/04/03 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLTextureTransfer.h" + +#include +#include + +#include "GLShared.h" +#include "GLTexture.h" + +#ifdef HAVE_NSIGHT +#include "nvToolsExt.h" +std::unordered_map _map; +#endif + + +#ifdef TEXTURE_TRANSFER_PBOS +#define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024) +#define TEXTURE_TRANSFER_PBO_COUNT 128 +#endif + +using namespace gpu; +using namespace gpu::gl; + +GLTextureTransferHelper::GLTextureTransferHelper() { +#ifdef THREADED_TEXTURE_TRANSFER + setObjectName("TextureTransferThread"); + _context.create(); + initialize(true, QThread::LowPriority); + // Clean shutdown on UNIX, otherwise _canvas is freed early + connect(qApp, &QCoreApplication::aboutToQuit, [&] { terminate(); }); +#else + initialize(false, QThread::LowPriority); +#endif +} + +GLTextureTransferHelper::~GLTextureTransferHelper() { +#ifdef THREADED_TEXTURE_TRANSFER + if (isStillRunning()) { + terminate(); + } +#else + terminate(); +#endif +} + +void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { + GLTexture* object = Backend::getGPUObject(*texturePointer); + + //CLIMAX_MERGE_START + //Backend::incrementTextureGPUTransferCount(); + object->setSyncState(GLSyncState::Pending); + Lock lock(_mutex); + _pendingTextures.push_back(texturePointer); +} + +void GLTextureTransferHelper::setup() { +#ifdef THREADED_TEXTURE_TRANSFER + _context.makeCurrent(); + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + // FIXME don't use opengl 4.5 DSA functionality without verifying it's present + glCreateRenderbuffers(1, &_drawRenderbuffer); + glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128); + glCreateFramebuffers(1, &_drawFramebuffer); + glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer); + glCreateFramebuffers(1, &_readFramebuffer); +#endif + +#ifdef TEXTURE_TRANSFER_PBOS + std::array pbos; + glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]); + for (uint32_t i = 0; i < TEXTURE_TRANSFER_PBO_COUNT; ++i) { + TextureTransferBlock newBlock; + newBlock._pbo = pbos[i]; + glNamedBufferStorage(newBlock._pbo, TEXTURE_TRANSFER_BLOCK_SIZE, 0, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); + newBlock._mapped = glMapNamedBufferRange(newBlock._pbo, 0, TEXTURE_TRANSFER_BLOCK_SIZE, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); + _readyQueue.push(newBlock); + } +#endif +#endif +} + +void GLTextureTransferHelper::shutdown() { +#ifdef THREADED_TEXTURE_TRANSFER + _context.makeCurrent(); +#endif + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); + glDeleteFramebuffers(1, &_drawFramebuffer); + _drawFramebuffer = 0; + glDeleteFramebuffers(1, &_readFramebuffer); + _readFramebuffer = 0; + + glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0); + glDeleteRenderbuffers(1, &_drawRenderbuffer); + _drawRenderbuffer = 0; +#endif +} + +void GLTextureTransferHelper::queueExecution(VoidLambda lambda) { + Lock lock(_mutex); + _pendingCommands.push_back(lambda); +} + +#define MAX_TRANSFERS_PER_PASS 2 + +bool GLTextureTransferHelper::process() { + // Take any new textures or commands off the queue + VoidLambdaList pendingCommands; + TextureList newTransferTextures; + { + Lock lock(_mutex); + newTransferTextures.swap(_pendingTextures); + pendingCommands.swap(_pendingCommands); + } + + if (!pendingCommands.empty()) { + for (auto command : pendingCommands) { + command(); + } + glFlush(); + } + + if (!newTransferTextures.empty()) { + for (auto& texturePointer : newTransferTextures) { +#ifdef HAVE_NSIGHT + _map[texturePointer] = nvtxRangeStart("TextureTansfer"); +#endif + GLTexture* object = Backend::getGPUObject(*texturePointer); + object->startTransfer(); + _transferringTextures.push_back(texturePointer); + _textureIterator = _transferringTextures.begin(); + } + _transferringTextures.sort([](const gpu::TexturePointer& a, const gpu::TexturePointer& b)->bool { + return a->getSize() < b->getSize(); + }); + } + + // No transfers in progress, sleep + if (_transferringTextures.empty()) { +#ifdef THREADED_TEXTURE_TRANSFER + QThread::usleep(1); +#endif + return true; + } + + static auto lastReport = usecTimestampNow(); + auto now = usecTimestampNow(); + auto lastReportInterval = now - lastReport; + if (lastReportInterval > USECS_PER_SECOND * 4) { + lastReport = now; + qDebug() << "Texture list " << _transferringTextures.size(); + } + + size_t transferCount = 0; + for (_textureIterator = _transferringTextures.begin(); _textureIterator != _transferringTextures.end();) { + if (++transferCount > MAX_TRANSFERS_PER_PASS) { + break; + } + auto texture = *_textureIterator; + GLTexture* gltexture = Backend::getGPUObject(*texture); + if (gltexture->continueTransfer()) { + ++_textureIterator; + continue; + } + + gltexture->finishTransfer(); + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + // FIXME force a draw on the texture transfer thread before passing the texture to the main thread for use +#endif + +#ifdef THREADED_TEXTURE_TRANSFER + clientWait(); +#endif + gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp(); + gltexture->updateSize(); + gltexture->setSyncState(gpu::gl::GLSyncState::Transferred); + //CLIMAX_MERGE_START + //Backend::decrementTextureGPUTransferCount(); +#ifdef HAVE_NSIGHT + // Mark the texture as transferred + nvtxRangeEnd(_map[texture]); + _map.erase(texture); +#endif + _textureIterator = _transferringTextures.erase(_textureIterator); + } + +#ifdef THREADED_TEXTURE_TRANSFER + if (!_transferringTextures.empty()) { + // Don't saturate the GPU + clientWait(); + } else { + // Don't saturate the CPU + QThread::msleep(1); + } +#endif + + return true; +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h new file mode 100644 index 0000000000..a23c282fd4 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h @@ -0,0 +1,78 @@ +// +// Created by Bradley Austin Davis on 2016/04/03 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLTextureTransfer_h +#define hifi_gpu_gl_GLTextureTransfer_h + +#include +#include + +#include + +#include + +#include "GLShared.h" + +#ifdef Q_OS_WIN +#define THREADED_TEXTURE_TRANSFER +#endif + +#ifdef THREADED_TEXTURE_TRANSFER +// FIXME when sparse textures are enabled, it's harder to force a draw on the transfer thread +// also, the current draw code is implicitly using OpenGL 4.5 functionality +//#define TEXTURE_TRANSFER_FORCE_DRAW +// FIXME PBO's increase the complexity and don't seem to work reliably +//#define TEXTURE_TRANSFER_PBOS +#endif + +namespace gpu { namespace gl { + +using TextureList = std::list; +using TextureListIterator = TextureList::iterator; + +class GLTextureTransferHelper : public GenericThread { +public: + using VoidLambda = std::function; + using VoidLambdaList = std::list; + using Pointer = std::shared_ptr; + GLTextureTransferHelper(); + ~GLTextureTransferHelper(); + void transferTexture(const gpu::TexturePointer& texturePointer); + void queueExecution(VoidLambda lambda); + + void setup() override; + void shutdown() override; + bool process() override; + +private: +#ifdef THREADED_TEXTURE_TRANSFER + ::gl::OffscreenContext _context; +#endif + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + // Framebuffers / renderbuffers for forcing access to the texture on the transfer thread + GLuint _drawRenderbuffer { 0 }; + GLuint _drawFramebuffer { 0 }; + GLuint _readFramebuffer { 0 }; +#endif + + // A mutex for protecting items access on the render and transfer threads + Mutex _mutex; + // Commands that have been submitted for execution on the texture transfer thread + VoidLambdaList _pendingCommands; + // Textures that have been submitted for transfer + TextureList _pendingTextures; + // Textures currently in the transfer process + // Only used on the transfer thread + TextureList _transferringTextures; + TextureListIterator _textureIterator; + +}; + +} } + +#endif \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp new file mode 100644 index 0000000000..8c843c1ce3 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp @@ -0,0 +1,197 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 9/27/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" + +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(gpugleslogging, "hifi.gpu.gles") + +using namespace gpu; +using namespace gpu::gles; + +const std::string GLESBackend::GLES_VERSION { "GLES" }; + +void GLESBackend::do_draw(const Batch& batch, size_t paramOffset) { + Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; + uint32 numVertices = batch._params[paramOffset + 1]._uint; + uint32 startVertex = batch._params[paramOffset + 0]._uint; + + if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, 2); +#else + + setupStereoSide(0); + glDrawArrays(mode, startVertex, numVertices); + setupStereoSide(1); + glDrawArrays(mode, startVertex, numVertices); + +#endif + _stats._DSNumTriangles += 2 * numVertices / 3; + _stats._DSNumDrawcalls += 2; + + } else { + glDrawArrays(mode, startVertex, numVertices); + _stats._DSNumTriangles += numVertices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; + + (void) CHECK_GL_ERROR(); +} + +void GLESBackend::do_drawIndexed(const Batch& batch, size_t paramOffset) { + Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; + uint32 numIndices = batch._params[paramOffset + 1]._uint; + uint32 startIndex = batch._params[paramOffset + 0]._uint; + + GLenum glType = gl::ELEMENT_TYPE_TO_GL[_input._indexBufferType]; + + auto typeByteSize = TYPE_SIZE[_input._indexBufferType]; + GLvoid* indexBufferByteOffset = reinterpret_cast(startIndex * typeByteSize + _input._indexBufferOffset); + + if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawElementsInstanced(mode, numIndices, glType, indexBufferByteOffset, 2); +#else + setupStereoSide(0); + glDrawElements(mode, numIndices, glType, indexBufferByteOffset); + setupStereoSide(1); + glDrawElements(mode, numIndices, glType, indexBufferByteOffset); +#endif + _stats._DSNumTriangles += 2 * numIndices / 3; + _stats._DSNumDrawcalls += 2; + } else { + //qDebug() << "GLESBackend::do_drawIndexed glDrawElements " << numIndices; + glDrawElements(mode, numIndices, glType, indexBufferByteOffset); + _stats._DSNumTriangles += numIndices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; + + (void) CHECK_GL_ERROR(); +} + +void GLESBackend::do_drawInstanced(const Batch& batch, size_t paramOffset) { + GLint numInstances = batch._params[paramOffset + 4]._uint; + Primitive primitiveType = (Primitive)batch._params[paramOffset + 3]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; + uint32 numVertices = batch._params[paramOffset + 2]._uint; + uint32 startVertex = batch._params[paramOffset + 1]._uint; + + + if (isStereo()) { + GLint trueNumInstances = 2 * numInstances; + +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, trueNumInstances); +#else + setupStereoSide(0); + glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); + setupStereoSide(1); + glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); +#endif + _stats._DSNumTriangles += (trueNumInstances * numVertices) / 3; + _stats._DSNumDrawcalls += trueNumInstances; + } else { + //qDebug() << "GLESBackend::do_drawInstanced glDrawArraysInstancedEXT " << numVertices << "," << numInstances; + glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); + _stats._DSNumTriangles += (numInstances * numVertices) / 3; + _stats._DSNumDrawcalls += numInstances; + } + _stats._DSNumAPIDrawcalls++; + + (void) CHECK_GL_ERROR(); +} + +void glbackend_glDrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount, GLint basevertex, GLuint baseinstance) { +//#if (GPU_INPUT_PROFILE == GPU_CORE_43) + //glDrawElementsInstancedBaseVertexBaseInstance(mode, count, type, indices, primcount, basevertex, baseinstance); +//#else + glDrawElementsInstanced(mode, count, type, indices, primcount); +//#endif +} + +void GLESBackend::do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) { + GLint numInstances = batch._params[paramOffset + 4]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 3]._uint]; + uint32 numIndices = batch._params[paramOffset + 2]._uint; + uint32 startIndex = batch._params[paramOffset + 1]._uint; + // FIXME glDrawElementsInstancedBaseVertexBaseInstance is only available in GL 4.3 + // and higher, so currently we ignore this field + uint32 startInstance = batch._params[paramOffset + 0]._uint; + GLenum glType = gl::ELEMENT_TYPE_TO_GL[_input._indexBufferType]; + + auto typeByteSize = TYPE_SIZE[_input._indexBufferType]; + GLvoid* indexBufferByteOffset = reinterpret_cast(startIndex * typeByteSize + _input._indexBufferOffset); + + if (isStereo()) { + GLint trueNumInstances = 2 * numInstances; + +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, trueNumInstances, 0, startInstance); +#else + setupStereoSide(0); + glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance); + setupStereoSide(1); + glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance); +#endif + + _stats._DSNumTriangles += (trueNumInstances * numIndices) / 3; + _stats._DSNumDrawcalls += trueNumInstances; + } else { + //qDebug() << "GLESBackend::do_drawIndexedInstanced glbackend_glDrawElementsInstancedBaseVertexBaseInstance " << numInstances; + glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance); + _stats._DSNumTriangles += (numInstances * numIndices) / 3; + _stats._DSNumDrawcalls += numInstances; + } + + _stats._DSNumAPIDrawcalls++; + + (void)CHECK_GL_ERROR(); +} + + +void GLESBackend::do_multiDrawIndirect(const Batch& batch, size_t paramOffset) { +#if (GPU_INPUT_PROFILE == GPU_CORE_43) + uint commandCount = batch._params[paramOffset + 0]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 1]._uint]; + + //glMultiDrawArraysIndirect(mode, reinterpret_cast(_input._indirectBufferOffset), commandCount, (GLsizei)_input._indirectBufferStride); + qDebug() << "TODO: GLESBackend.cpp:do_multiDrawIndirect do_multiDrawIndirect"; + _stats._DSNumDrawcalls += commandCount; + _stats._DSNumAPIDrawcalls++; + +#else + // FIXME implement the slow path +#endif + (void)CHECK_GL_ERROR(); + +} + +void GLESBackend::do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) { +//#if (GPU_INPUT_PROFILE == GPU_CORE_43) + uint commandCount = batch._params[paramOffset + 0]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 1]._uint]; + GLenum indexType = gl::ELEMENT_TYPE_TO_GL[_input._indexBufferType]; + + //glMultiDrawElementsIndirect(mode, indexType, reinterpret_cast(_input._indirectBufferOffset), commandCount, (GLsizei)_input._indirectBufferStride); + qDebug() << "TODO: GLESBackend.cpp:do_multiDrawIndexedIndirect glMultiDrawElementsIndirect"; + _stats._DSNumDrawcalls += commandCount; + _stats._DSNumAPIDrawcalls++; +//#else + // FIXME implement the slow path +//#endif + (void)CHECK_GL_ERROR(); +} diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h new file mode 100644 index 0000000000..69a417d952 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h @@ -0,0 +1,99 @@ +// +// GLESBackend.h +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Gabriel Calero & Cristian Duarte on 9/27/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gles_GLESBackend_h +#define hifi_gpu_gles_GLESBackend_h + +#include + +#include "../gl/GLBackend.h" +#include "../gl/GLTexture.h" + + +namespace gpu { namespace gles { + +using namespace gpu::gl; + +class GLESBackend : public GLBackend { + using Parent = GLBackend; + // Context Backend static interface required + friend class Context; + +public: + explicit GLESBackend(bool syncCache) : Parent(syncCache) {} + GLESBackend() : Parent() {} + virtual ~GLESBackend() { + // call resetStages here rather than in ~GLBackend dtor because it will call releaseResourceBuffer + // which is pure virtual from GLBackend's dtor. + resetStages(); + } + + static const std::string GLES_VERSION; + const std::string& getVersion() const override { return GLES_VERSION; } + + + class GLESTexture : public GLTexture { + using Parent = GLTexture; + GLuint allocate(); + public: + GLESTexture(const std::weak_ptr& backend, const Texture& buffer, GLuint externalId); + GLESTexture(const std::weak_ptr& backend, const Texture& buffer, bool transferrable); + + protected: + void transferMip(uint16_t mipLevel, uint8_t face) const; + void startTransfer() override; + void allocateStorage() const override; + void updateSize() const override; + void syncSampler() const override; + void generateMips() const override; + }; + + +protected: + GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; + GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; + + GLuint getBufferID(const Buffer& buffer) override; + GLBuffer* syncGPUObject(const Buffer& buffer) override; + + GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override; + GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override; + + GLuint getQueryID(const QueryPointer& query) override; + GLQuery* syncGPUObject(const Query& query) override; + + // Draw Stage + void do_draw(const Batch& batch, size_t paramOffset) override; + void do_drawIndexed(const Batch& batch, size_t paramOffset) override; + void do_drawInstanced(const Batch& batch, size_t paramOffset) override; + void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) override; + void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) override; + void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) override; + + // Input Stage + void updateInput() override; + void resetInputStage() override; + + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void transferTransformState(const Batch& batch) const override; + void initTransform() override; + void updateTransform(const Batch& batch); + void resetTransformStage(); + + // Output stage + void do_blit(const Batch& batch, size_t paramOffset) override; +}; + +} } + +Q_DECLARE_LOGGING_CATEGORY(gpugleslogging) + + +#endif \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp new file mode 100644 index 0000000000..f6bdea45af --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp @@ -0,0 +1,69 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 09/27/2016 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" +#include "../gl/GLBuffer.h" + +namespace gpu { + namespace gles { + + class GLESBuffer : public gpu::gl::GLBuffer { + using Parent = gpu::gl::GLBuffer; + static GLuint allocate() { + GLuint result; + glGenBuffers(1, &result); + return result; + } + + public: + GLESBuffer(const std::weak_ptr& backend, const Buffer& buffer, GLESBuffer* original) : Parent(backend, buffer, allocate()) { + glBindBuffer(GL_ARRAY_BUFFER, _buffer); + glBufferData(GL_ARRAY_BUFFER, _size, nullptr, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + if (original && original->_size) { + glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer); + glBindBuffer(GL_COPY_READ_BUFFER, original->_buffer); + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, original->_size); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + glBindBuffer(GL_COPY_READ_BUFFER, 0); + (void)CHECK_GL_ERROR(); + } + Backend::setGPUObject(buffer, this); + } + + void transfer() override { + glBindBuffer(GL_ARRAY_BUFFER, _buffer); + (void)CHECK_GL_ERROR(); + Size offset; + Size size; + Size currentPage { 0 }; + auto data = _gpuObject._renderSysmem.readData(); + while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) { + glBufferSubData(GL_ARRAY_BUFFER, offset, size, data + offset); + (void)CHECK_GL_ERROR(); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + (void)CHECK_GL_ERROR(); + _gpuObject._renderPages._flags &= ~PageManager::DIRTY; + } + }; + } +} + +using namespace gpu; +using namespace gpu::gl; +using namespace gpu::gles; + + +GLuint GLESBackend::getBufferID(const Buffer& buffer) { + return GLESBuffer::getId(*this, buffer); +} + +GLBuffer* GLESBackend::syncGPUObject(const Buffer& buffer) { + return GLESBuffer::sync(*this, buffer); +} diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendInput.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendInput.cpp new file mode 100644 index 0000000000..d37a01eb90 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendInput.cpp @@ -0,0 +1,29 @@ +// +// GLESBackendInput.cpp +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Cristian Duarte & Gabriel Calero on 10/7/2016. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" + +using namespace gpu; +using namespace gpu::gles; + +void GLESBackend::updateInput() { + Parent::updateInput(); +} + + +void GLESBackend::resetInputStage() { + Parent::resetInputStage(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + for (uint32_t i = 0; i < _input._attributeActivation.size(); i++) { + glDisableVertexAttribArray(i); + glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, 0, 0); + } +} \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp new file mode 100644 index 0000000000..8bf9267fde --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp @@ -0,0 +1,169 @@ +// +// GLESBackendOutput.cpp +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Gabriel Calero & Cristian Duarte on 9/27/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" + +#include + +#include "../gl/GLFramebuffer.h" +#include "../gl/GLTexture.h" + +namespace gpu { namespace gles { + +class GLESFramebuffer : public gl::GLFramebuffer { + using Parent = gl::GLFramebuffer; + static GLuint allocate() { + GLuint result; + glGenFramebuffers(1, &result); + return result; + } +public: + void update() override { + GLint currentFBO = -1; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBO); + glBindFramebuffer(GL_FRAMEBUFFER, _fbo); + gl::GLTexture* gltexture = nullptr; + TexturePointer surface; + if (_gpuObject.getColorStamps() != _colorStamps) { + if (_gpuObject.hasColor()) { + _colorBuffers.clear(); + static const GLenum colorAttachments[] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3, + GL_COLOR_ATTACHMENT4, + GL_COLOR_ATTACHMENT5, + GL_COLOR_ATTACHMENT6, + GL_COLOR_ATTACHMENT7, + GL_COLOR_ATTACHMENT8, + GL_COLOR_ATTACHMENT9, + GL_COLOR_ATTACHMENT10, + GL_COLOR_ATTACHMENT11, + GL_COLOR_ATTACHMENT12, + GL_COLOR_ATTACHMENT13, + GL_COLOR_ATTACHMENT14, + GL_COLOR_ATTACHMENT15 }; + + int unit = 0; + for (auto& b : _gpuObject.getRenderBuffers()) { + surface = b._texture; + if (surface) { + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + } else { + gltexture = nullptr; + } + + if (gltexture) { + glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[unit], GL_TEXTURE_2D, gltexture->_texture, 0); + _colorBuffers.push_back(colorAttachments[unit]); + } else { + glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[unit], GL_TEXTURE_2D, 0, 0); + } + unit++; + } + } + _colorStamps = _gpuObject.getColorStamps(); + } + + GLenum attachement = GL_DEPTH_STENCIL_ATTACHMENT; + if (!_gpuObject.hasStencil()) { + attachement = GL_DEPTH_ATTACHMENT; + } else if (!_gpuObject.hasDepth()) { + attachement = GL_STENCIL_ATTACHMENT; + } + + if (_gpuObject.getDepthStamp() != _depthStamp) { + auto surface = _gpuObject.getDepthStencilBuffer(); + if (_gpuObject.hasDepthStencil() && surface) { + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + } + + if (gltexture) { + glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, gltexture->_texture, 0); + } else { + glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, 0, 0); + } + _depthStamp = _gpuObject.getDepthStamp(); + } + + + // Last but not least, define where we draw + if (!_colorBuffers.empty()) { + glDrawBuffers((GLsizei)_colorBuffers.size(), _colorBuffers.data()); + } else { + glDrawBuffers(1, {GL_NONE}); + } + + // Now check for completness + _status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + // restore the current framebuffer + if (currentFBO != -1) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO); + } + + checkStatus(GL_DRAW_FRAMEBUFFER); + } + + +public: + GLESFramebuffer(const std::weak_ptr& backend, const gpu::Framebuffer& framebuffer) + : Parent(backend, framebuffer, allocate()) { } +}; + +gl::GLFramebuffer* gpu::gles::GLESBackend::syncGPUObject(const Framebuffer& framebuffer) { + return GLESFramebuffer::sync(*this, framebuffer); +} + +GLuint GLESBackend::getFramebufferID(const FramebufferPointer& framebuffer) { + return framebuffer ? GLESFramebuffer::getId(*this, *framebuffer) : 0; +} + +void GLESBackend::do_blit(const Batch& batch, size_t paramOffset) { + auto srcframebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); + Vec4i srcvp; + for (auto i = 0; i < 4; ++i) { + srcvp[i] = batch._params[paramOffset + 1 + i]._int; + } + + auto dstframebuffer = batch._framebuffers.get(batch._params[paramOffset + 5]._uint); + Vec4i dstvp; + for (auto i = 0; i < 4; ++i) { + dstvp[i] = batch._params[paramOffset + 6 + i]._int; + } + + // Assign dest framebuffer if not bound already + auto newDrawFBO = getFramebufferID(dstframebuffer); + if (_output._drawFBO != newDrawFBO) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, newDrawFBO); + } + + // always bind the read fbo + glBindFramebuffer(GL_READ_FRAMEBUFFER, getFramebufferID(srcframebuffer)); + + // Blit! + glBlitFramebuffer(srcvp.x, srcvp.y, srcvp.z, srcvp.w, + dstvp.x, dstvp.y, dstvp.z, dstvp.w, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + + // Always clean the read fbo to 0 + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + // Restore draw fbo if changed + if (_output._drawFBO != newDrawFBO) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _output._drawFBO); + } + + (void) CHECK_GL_ERROR(); +} + + +} } diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp new file mode 100644 index 0000000000..db541b07bc --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp @@ -0,0 +1,38 @@ +// +// GLESBackendQuery.cpp +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Gabriel Calero & Cristian Duarte on 9/27/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" + +#include "../gl/GLQuery.h" + +using namespace gpu; +using namespace gpu::gl; +using namespace gpu::gles; + +class GLESQuery : public GLQuery { + using Parent = GLQuery; +public: + static GLuint allocateQuery() { + GLuint result; + glGenQueries(1, &result); + return result; + } + + GLESQuery(const std::weak_ptr& backend, const Query& query) + : Parent(backend, query, allocateQuery(), allocateQuery()) { } +}; + +GLQuery* GLESBackend::syncGPUObject(const Query& query) { + return GLESQuery::sync(*this, query); +} + +GLuint GLESBackend::getQueryID(const QueryPointer& query) { + return GLESQuery::getId(*this, query); +} diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp new file mode 100644 index 0000000000..31a98edd12 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp @@ -0,0 +1,176 @@ +// +// GLESBackendTexture.cpp +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Gabriel Calero & Cristian Duarte on 9/27/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" + +#include +#include +#include + +// #include "../gl/GLTexelFormat.h" + +using namespace gpu; +using namespace gpu::gl; +using namespace gpu::gles; + +//using GL41TexelFormat = GLTexelFormat; +using GLESTexture = GLESBackend::GLESTexture; + +GLuint GLESTexture::allocate() { + //CLIMAX_MERGE_START + //Backend::incrementTextureGPUCount(); + //CLIMAX_MERGE_END + GLuint result; + glGenTextures(1, &result); + return result; +} + +GLuint GLESBackend::getTextureID(const TexturePointer& texture, bool transfer) { + return GLESTexture::getId(*this, texture, transfer); +} + +GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texture, bool transfer) { + return GLESTexture::sync(*this, texture, transfer); +} + +GLESTexture::GLESTexture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId) + : GLTexture(backend, texture, externalId) { +} + +GLESTexture::GLESTexture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) + : GLTexture(backend, texture, allocate(), transferrable) { +} + +void GLESTexture::generateMips() const { + withPreservedTexture([&] { + glGenerateMipmap(_target); + }); + (void)CHECK_GL_ERROR(); +} + +void GLESTexture::allocateStorage() const { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); + (void)CHECK_GL_ERROR(); + glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); + (void)CHECK_GL_ERROR(); +/* if (GLEW_VERSION_4_2 && !_gpuObject.getTexelFormat().isCompressed()) { + // Get the dimensions, accounting for the downgrade level + Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip); + glTexStorage2D(_target, usedMipLevels(), texelFormat.internalFormat, dimensions.x, dimensions.y); + (void)CHECK_GL_ERROR(); + } else {*/ + for (uint16_t l = _minMip; l <= _maxMip; l++) { + // Get the mip level dimensions, accounting for the downgrade level + Vec3u dimensions = _gpuObject.evalMipDimensions(l); + for (GLenum target : getFaceTargets(_target)) { + glTexImage2D(target, l - _minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL); + (void)CHECK_GL_ERROR(); + } + } + //} +} + +void GLESTexture::updateSize() const { + setSize(_virtualSize); + if (!_id) { + return; + } + + if (_gpuObject.getTexelFormat().isCompressed()) { + GLenum proxyType = GL_TEXTURE_2D; + GLuint numFaces = 1; + if (_gpuObject.getType() == gpu::Texture::TEX_CUBE) { + proxyType = CUBE_FACE_LAYOUT[0]; + numFaces = (GLuint)CUBE_NUM_FACES; + } + GLint gpuSize{ 0 }; + glGetTexLevelParameteriv(proxyType, 0, GL_TEXTURE_COMPRESSED, &gpuSize); + (void)CHECK_GL_ERROR(); + + if (gpuSize) { + for (GLuint level = _minMip; level < _maxMip; level++) { + GLint levelSize{ 0 }; + //glGetTexLevelParameteriv(proxyType, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &levelSize); + //qDebug() << "TODO: GLBackendTexture.cpp:updateSize GL_TEXTURE_COMPRESSED_IMAGE_SIZE"; + levelSize *= numFaces; + + if (levelSize <= 0) { + break; + } + gpuSize += levelSize; + } + (void)CHECK_GL_ERROR(); + setSize(gpuSize); + return; + } + } +} + +// Move content bits from the CPU to the GPU for a given mip / face +void GLESTexture::transferMip(uint16_t mipLevel, uint8_t face) const { + auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); + //GLenum target = getFaceTargets()[face]; + GLenum target = _target == GL_TEXTURE_2D ? GL_TEXTURE_2D : CUBE_FACE_LAYOUT[face]; + auto size = _gpuObject.evalMipDimensions(mipLevel); + glTexSubImage2D(target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + (void)CHECK_GL_ERROR(); +} + +void GLESTexture::startTransfer() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + Parent::startTransfer(); + + glBindTexture(_target, _id); + (void)CHECK_GL_ERROR(); + + // transfer pixels from each faces + uint8_t numFaces = (Texture::TEX_CUBE == _gpuObject.getType()) ? CUBE_NUM_FACES : 1; + for (uint8_t f = 0; f < numFaces; f++) { + for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + if (_gpuObject.isStoredMipFaceAvailable(i, f)) { + transferMip(i, f); + } + } + } +} + +void GLESBackend::GLESTexture::syncSampler() const { + const Sampler& sampler = _gpuObject.getSampler(); + const auto& fm = FILTER_MODES[sampler.getFilter()]; + glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, fm.minFilter); + glTexParameteri(_target, GL_TEXTURE_MAG_FILTER, fm.magFilter); + + if (sampler.doComparison()) { + glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(_target, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); + } else { + glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + + glTexParameteri(_target, GL_TEXTURE_WRAP_S, WRAP_MODES[sampler.getWrapModeU()]); + glTexParameteri(_target, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); + glTexParameteri(_target, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); + + glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR_EXT, (const float*)&sampler.getBorderColor()); + + + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, (uint16)sampler.getMipOffset()); + glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); + glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); + (void)CHECK_GL_ERROR(); + //qDebug() << "[GPU-GL-GLBackend] syncSampler 12 " << _target << "," << sampler.getMaxAnisotropy(); + //glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); + //(void)CHECK_GL_ERROR(); + //qDebug() << "[GPU-GL-GLBackend] syncSampler end"; + +} + diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp new file mode 100644 index 0000000000..5050db6edd --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp @@ -0,0 +1,67 @@ +// +// GLESBackendTransform.cpp +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Gabriel Calero & Cristian Duarte on 9/28/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" + +using namespace gpu; +using namespace gpu::gles; + +void GLESBackend::initTransform() { + glGenBuffers(1, &_transform._objectBuffer); + glGenBuffers(1, &_transform._cameraBuffer); + glGenBuffers(1, &_transform._drawCallInfoBuffer); + glGenTextures(1, &_transform._objectBufferTexture); + size_t cameraSize = sizeof(TransformStageState::CameraBufferElement); + while (_transform._cameraUboSize < cameraSize) { + _transform._cameraUboSize += _uboAlignment; + } +} + +void GLESBackend::transferTransformState(const Batch& batch) const { + // FIXME not thread safe + static std::vector bufferData; + if (!_transform._cameras.empty()) { + bufferData.resize(_transform._cameraUboSize * _transform._cameras.size()); + for (size_t i = 0; i < _transform._cameras.size(); ++i) { + memcpy(bufferData.data() + (_transform._cameraUboSize * i), &_transform._cameras[i], sizeof(TransformStageState::CameraBufferElement)); + } + glBindBuffer(GL_UNIFORM_BUFFER, _transform._cameraBuffer); + glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + + if (!batch._objects.empty()) { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, _transform._objectBuffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_STREAM_DRAW); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + } + + if (!batch._namedData.empty()) { + bufferData.clear(); + for (auto& data : batch._namedData) { + auto currentSize = bufferData.size(); + auto bytesToCopy = data.second.drawCallInfos.size() * sizeof(Batch::DrawCallInfo); + bufferData.resize(currentSize + bytesToCopy); + memcpy(bufferData.data() + currentSize, data.second.drawCallInfos.data(), bytesToCopy); + _transform._drawCallInfoOffsets[data.first] = (GLvoid*)currentSize; + } + + glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer); + glBufferData(GL_ARRAY_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._objectBuffer); + + CHECK_GL_ERROR(); + + // Make sure the current Camera offset is unknown before render Draw + _transform._currentCameraOffset = INVALID_OFFSET; +} diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index c5df94235c..01cc652fd1 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -159,6 +159,7 @@ protected: friend class gl::GLBuffer; friend class gl41::GL41Buffer; friend class gl45::GL45Buffer; + friend class gles::GLESBuffer; }; using BufferUpdates = std::vector; diff --git a/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv b/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv index c9cc9e9dfa..1f788051bc 100755 --- a/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv +++ b/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv @@ -35,5 +35,5 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - varTexCoord0 = ((pos.xy + 1) * 0.5) * texcoordRect.zw + texcoordRect.xy; + varTexCoord0 = ((pos.xy + 1.0) * 0.5) * texcoordRect.zw + texcoordRect.xy; } diff --git a/libraries/gpu/src/gpu/DrawTransformUnitQuad.slv b/libraries/gpu/src/gpu/DrawTransformUnitQuad.slv index f55abf1089..845cf0326d 100755 --- a/libraries/gpu/src/gpu/DrawTransformUnitQuad.slv +++ b/libraries/gpu/src/gpu/DrawTransformUnitQuad.slv @@ -32,5 +32,5 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - varTexCoord0 = (pos.xy + 1) * 0.5; + varTexCoord0 = (pos.xy + 1.0) * 0.5; } diff --git a/libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv b/libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv index 1426ae48fd..289d8f96b1 100644 --- a/libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv +++ b/libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv @@ -23,7 +23,7 @@ void main(void) { ); vec4 pos = UNIT_QUAD[gl_VertexID]; - varTexCoord0 = (pos.xy + 1) * 0.5; + varTexCoord0 = (pos.xy + 1.0) * 0.5; gl_Position = pos; } diff --git a/libraries/gpu/src/gpu/DrawViewportQuadTransformTexcoord.slv b/libraries/gpu/src/gpu/DrawViewportQuadTransformTexcoord.slv index 1f9bc13700..554728417b 100755 --- a/libraries/gpu/src/gpu/DrawViewportQuadTransformTexcoord.slv +++ b/libraries/gpu/src/gpu/DrawViewportQuadTransformTexcoord.slv @@ -28,7 +28,7 @@ void main(void) { vec4 pos = UNIT_QUAD[gl_VertexID]; // standard transform but applied to the Texcoord - vec4 tc = vec4((pos.xy + 1) * 0.5, pos.zw); + vec4 tc = vec4((pos.xy + 1.0) * 0.5, pos.zw); TransformObject obj = getTransformObject(); <$transformModelToWorldPos(obj, tc, tc)$> diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index 88800652a5..8100efafaf 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -137,6 +137,11 @@ namespace gpu { class GL45Backend; class GL45Buffer; } + + namespace gles { + class GLESBackend; + class GLESBuffer; + } } #endif diff --git a/libraries/gpu/src/gpu/Frame.cpp b/libraries/gpu/src/gpu/Frame.cpp index 4854559d61..58ab7257f7 100644 --- a/libraries/gpu/src/gpu/Frame.cpp +++ b/libraries/gpu/src/gpu/Frame.cpp @@ -11,6 +11,10 @@ using namespace gpu; +Frame::Frame() { + batches.reserve(16); +} + Frame::~Frame() { if (framebuffer && framebufferRecycler) { framebufferRecycler(framebuffer); diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index bfebe85753..69872906e3 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -20,8 +20,8 @@ namespace gpu { friend class Context; public: + Frame(); virtual ~Frame(); - using Batches = std::vector; using FramebufferRecycler = std::function; using OverlayRecycler = std::function; diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index b49c681889..f1257e7c83 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -13,6 +13,7 @@ #include #include +#include #include using namespace gpu; diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index a65aaf765b..b3a500d68f 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -132,7 +132,11 @@ public: float getAspectRatio() const { return getWidth() / (float) getHeight() ; } +#ifndef ANDROID static const uint32 MAX_NUM_RENDER_BUFFERS = 8; +#else + static const uint32 MAX_NUM_RENDER_BUFFERS = 4; +#endif static uint32 getMaxNumRenderBuffers() { return MAX_NUM_RENDER_BUFFERS; } const GPUObjectPointer gpuObject {}; diff --git a/libraries/gpu/src/gpu/Query.cpp b/libraries/gpu/src/gpu/Query.cpp index 38a9d6db8c..03312e0db9 100644 --- a/libraries/gpu/src/gpu/Query.cpp +++ b/libraries/gpu/src/gpu/Query.cpp @@ -45,8 +45,8 @@ void Query::triggerReturnHandler(uint64_t queryResult, uint64_t batchElapsedTime RangeTimer::RangeTimer(const std::string& name) : _name(name) { for (int i = 0; i < QUERY_QUEUE_SIZE; i++) { - _timerQueries.push_back(std::make_shared([&, i] (const Query& query) { - _tailIndex ++; + _timerQueries.push_back(std::make_shared([this] (const Query& query) { + _tailIndex++; _movingAverageGPU.addSample(query.getGPUElapsedTime()); _movingAverageBatch.addSample(query.getBatchElapsedTime()); diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index b786222198..9feca4a3c9 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -43,16 +43,21 @@ layout(location=14) in int _inStereoSide; flat out int _stereoSide; // In stereo drawcall mode Instances are drawn twice (left then right) hence the true InstanceID is the gl_InstanceID / 2 -int gpu_InstanceID = gl_InstanceID >> 1; +int gpu_InstanceID() { + return gl_InstanceID >> 1; +} #else -int gpu_InstanceID = gl_InstanceID; - +int gpu_InstanceID() { + return gl_InstanceID; +} #endif #else -int gpu_InstanceID = gl_InstanceID; +int gpu_InstanceID() { + return gl_InstanceID; +} #endif @@ -166,7 +171,7 @@ TransformObject getTransformObject() { #ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN vec4 eyeClipEdge[2]= vec4[2](vec4(-1,0,0,1), vec4(1,0,0,1)); vec2 eyeOffsetScale = vec2(-0.5, +0.5); - uint eyeIndex = _stereoSide; + uint eyeIndex = uint(_stereoSide); gl_ClipDistance[0] = dot(<$clipPos$>, eyeClipEdge[eyeIndex]); float newClipPosX = <$clipPos$>.x * 0.5 + eyeOffsetScale[eyeIndex] * <$clipPos$>.w; <$clipPos$>.x = newClipPosX; diff --git a/libraries/image/CMakeLists.txt b/libraries/image/CMakeLists.txt index 85d3d8f1ae..442fa714b3 100644 --- a/libraries/image/CMakeLists.txt +++ b/libraries/image/CMakeLists.txt @@ -2,10 +2,10 @@ set(TARGET_NAME image) setup_hifi_library() link_hifi_libraries(shared gpu) -target_glm() - -add_dependency_external_projects(nvtt) -find_package(NVTT REQUIRED) -target_include_directories(${TARGET_NAME} PRIVATE ${NVTT_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${NVTT_LIBRARIES}) -add_paths_to_fixup_libs(${NVTT_DLL_PATH}) +if (NOT ANDROID) + add_dependency_external_projects(nvtt) + find_package(NVTT REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${NVTT_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${NVTT_LIBRARIES}) + add_paths_to_fixup_libs(${NVTT_DLL_PATH}) +endif() \ No newline at end of file diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 6081355eaa..b4b5bdeae1 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -11,7 +11,8 @@ #include "Image.h" -#include +#include + #include #include @@ -27,7 +28,13 @@ using namespace gpu; +#if defined(Q_OS_ANDROID) +#define CPU_MIPMAPS 0 +#else #define CPU_MIPMAPS 1 +#include +#endif + static const glm::uvec2 SPARSE_PAGE_SIZE(128); static const glm::uvec2 MAX_TEXTURE_SIZE(4096); @@ -295,6 +302,7 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } +#if defined(NVTT_API) struct MyOutputHandler : public nvtt::OutputHandler { MyOutputHandler(gpu::Texture* texture, int face) : _texture(texture), _face(face) {} @@ -333,6 +341,7 @@ struct MyErrorHandler : public nvtt::ErrorHandler { qCWarning(imagelogging) << "Texture compression error:" << nvtt::errorString(e); } }; +#endif void generateMips(gpu::Texture* texture, QImage& image, const std::atomic& abortProcessing = false, int face = -1) { #if CPU_MIPMAPS @@ -472,7 +481,7 @@ void generateMips(gpu::Texture* texture, QImage& image, const std::atomic& compressor.setTaskDispatcher(&dispatcher); compressor.process(inputOptions, compressionOptions, outputOptions); #else - texture->autoGenerateMips(-1); + texture->setAutoGenerateMips(true); #endif } diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h index 02dcbe4664..c0cca200d7 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h +++ b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h @@ -30,7 +30,7 @@ public: TOUCH_AXIS_Y_NEG, }; - enum TouchGestureAxisChannel { + enum TouchGestureAxisChannel { TOUCH_GESTURE_PINCH_POS = TOUCH_AXIS_Y_NEG + 1, TOUCH_GESTURE_PINCH_NEG, }; diff --git a/libraries/ktx/CMakeLists.txt b/libraries/ktx/CMakeLists.txt index 967ea71eb6..efd8b76567 100644 --- a/libraries/ktx/CMakeLists.txt +++ b/libraries/ktx/CMakeLists.txt @@ -1,3 +1,3 @@ set(TARGET_NAME ktx) setup_hifi_library() -include_hifi_library_headers(shared) \ No newline at end of file +link_hifi_libraries(shared) diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index 0ffa27986d..013ec056e3 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -66,7 +66,7 @@ Q_INVOKABLE void thruModeEnable(bool enable); public: Midi(); - virtual ~Midi(); + virtual ~Midi(); }; #endif // hifi_Midi_h diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 74c8d06736..2756334a1a 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -175,9 +175,9 @@ void GeometryReader::run() { QString urlname = _url.path().toLower(); if (!urlname.isEmpty() && !_url.path().isEmpty() && - (_url.path().toLower().endsWith(".fbx") || - _url.path().toLower().endsWith(".obj") || - _url.path().toLower().endsWith(".obj.gz"))) { + (_url.path().toLower().endsWith(".fbx") || + _url.path().toLower().endsWith(".obj") || + _url.path().toLower().endsWith(".obj.gz"))) { FBXGeometry::Pointer fbxGeometry; if (_url.path().toLower().endsWith(".fbx")) { @@ -187,15 +187,15 @@ void GeometryReader::run() { } } else if (_url.path().toLower().endsWith(".obj")) { fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _combineParts, _url)); - } else if (_url.path().toLower().endsWith(".obj.gz")) { - QByteArray uncompressedData; - if (gunzip(_data, uncompressedData)){ - fbxGeometry.reset(OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url)); - } else { - throw QString("failed to decompress .obj.gz" ); - } + } else if (_url.path().toLower().endsWith(".obj.gz")) { + QByteArray uncompressedData; + if (gunzip(_data, uncompressedData)){ + fbxGeometry.reset(OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url)); + } else { + throw QString("failed to decompress .obj.gz" ); + } - } else { + } else { throw QString("unsupported format"); } @@ -241,7 +241,6 @@ private: }; void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { - qDebug() << "Processing geometry: " << _effectiveBaseURL; _url = _effectiveBaseURL; _textureBaseUrl = _effectiveBaseURL; QThreadPool::globalInstance()->start(new GeometryReader(_self, _effectiveBaseURL, _mapping, data, _combineParts)); @@ -255,7 +254,6 @@ void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxG QHash materialIDAtlas; for (const FBXMaterial& material : _fbxGeometry->materials) { materialIDAtlas[material.materialID] = _materials.size(); - qDebug() << "setGeometryDefinition() " << _textureBaseUrl; _materials.push_back(std::make_shared(material, _textureBaseUrl)); } @@ -348,7 +346,6 @@ Geometry::Geometry(const Geometry& geometry) { _materials.reserve(geometry._materials.size()); for (const auto& material : geometry._materials) { - qDebug() << "Geometry() no base url..."; _materials.push_back(std::make_shared(*material)); } @@ -434,7 +431,6 @@ void GeometryResource::deleter() { void GeometryResource::setTextures() { if (_fbxGeometry) { for (const FBXMaterial& material : _fbxGeometry->materials) { - qDebug() << "setTextures() " << _textureBaseUrl; _materials.push_back(std::make_shared(material, _textureBaseUrl)); } } @@ -536,7 +532,6 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) : model::Material(*material._material) { - qDebug() << "Created network material with base url: " << textureBaseUrl; _textures = Textures(MapChannel::NUM_MAP_CHANNELS); if (!material.albedoTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); diff --git a/libraries/model/src/model/Light.slh b/libraries/model/src/model/Light.slh index 67946abea0..093a87adc8 100644 --- a/libraries/model/src/model/Light.slh +++ b/libraries/model/src/model/Light.slh @@ -41,7 +41,7 @@ SphericalHarmonics getLightAmbientSphere(LightAmbient l) { return l._ambientSphe float getLightAmbientIntensity(LightAmbient l) { return l._ambient.x; } -bool getLightHasAmbientMap(LightAmbient l) { return l._ambient.y > 0; } +bool getLightHasAmbientMap(LightAmbient l) { return l._ambient.y > 0.0; } float getLightAmbientMapNumMips(LightAmbient l) { return l._ambient.y; } <@func declareLightBuffer(N)@> diff --git a/libraries/model/src/model/LightIrradiance.shared.slh b/libraries/model/src/model/LightIrradiance.shared.slh index 4a2ee40c9d..13d6cf4b93 100644 --- a/libraries/model/src/model/LightIrradiance.shared.slh +++ b/libraries/model/src/model/LightIrradiance.shared.slh @@ -37,7 +37,7 @@ float lightIrradiance_evalLightAttenuation(LightIrradiance li, float d) { // "Fade" the edges of light sources to make things look a bit more attractive. // Note: this tends to look a bit odd at lower exponents. - attenuation *= min(1, max(0, -(d - cutoff))); + attenuation *= min(1.0, max(0.0, -(d - cutoff))); return attenuation; } diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index d327593573..fd3061afa5 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -97,7 +97,10 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky } auto skyState = std::make_shared(); - skyState->setStencilTest(true, 0xFF, gpu::State::StencilTest(1, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); + // Must match PrepareStencil::STENCIL_BACKGROUND + const int8_t STENCIL_BACKGROUND = 0; + skyState->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_BACKGROUND, 0xFF, gpu::EQUAL, + gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); thePipeline = gpu::Pipeline::create(skyShader, skyState); } diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index 288e98d5a5..c3592c5da2 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -2,44 +2,18 @@ set(TARGET_NAME networking) setup_hifi_library(Network) link_hifi_libraries(shared) -target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes") +target_openssl() +target_tbb() if (WIN32) - # we need ws2_32.lib on windows, but it's static so we don't bubble it up - target_link_libraries(${TARGET_NAME} ws2_32.lib) -endif () - -add_dependency_external_projects(tbb) - -# find required dependencies -find_package(OpenSSL REQUIRED) -find_package(TBB REQUIRED) - -if (APPLE) + # we need ws2_32.lib on windows, but it's static so we don't bubble it up + target_link_libraries(${TARGET_NAME} ws2_32.lib) +elseif(APPLE) + # IOKit is needed for getting machine fingerprint find_library(FRAMEWORK_IOKIT IOKit) find_library(CORE_FOUNDATION CoreFoundation) -endif () - -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 () - -include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") - -# append OpenSSL to our list of libraries to link -target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES} ${TBB_LIBRARIES}) - -# IOKit is needed for getting machine fingerprint -if (APPLE) target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT} ${CORE_FOUNDATION}) -endif (APPLE) - -# libcrypto uses dlopen in libdl -if (UNIX) - target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) -endif (UNIX) - -# append tbb includes to our list of includes to bubble -target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) +elseif(UNIX) + # libcrypto uses dlopen in libdl + target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) +endif () diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 99e1962387..2376a3b2b6 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -29,11 +29,15 @@ #include "UserActivityLogger.h" #include "udt/PacketHeaders.h" +#ifdef Q_OS_ANDROID +const QString DEFAULT_HIFI_ADDRESS = "hifi://android/0.0,0.0,-200"; +#else #if USE_STABLE_GLOBAL_SERVICES const QString DEFAULT_HIFI_ADDRESS = "hifi://welcome/hello"; #else const QString DEFAULT_HIFI_ADDRESS = "hifi://dev-welcome/hello"; #endif +#endif const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 940daf4d19..6fdfc5a42b 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -59,7 +59,11 @@ void AssetClient::init() { auto& networkAccessManager = NetworkAccessManager::getInstance(); if (!networkAccessManager.cache()) { if (_cacheDir.isEmpty()) { +#ifdef Q_OS_ANDROID + QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); +#else QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); +#endif _cacheDir = !cachePath.isEmpty() ? cachePath : "interfaceCache"; } QNetworkDiskCache* cache = new QNetworkDiskCache(); diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index c9ca6ebd43..2dc22a9337 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -202,7 +202,7 @@ void AssetResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytes emit progress(bytesReceived, bytesTotal); auto now = p_high_resolution_clock::now(); - + // Recording ATP bytes downloaded in stats DependencyManager::get()->updateStat(STAT_ATP_RESOURCE_TOTAL_BYTES, bytesReceived); diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index c6d0370a70..edba520bd5 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -201,11 +201,11 @@ void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesT _sendTimer->start(); emit progress(bytesReceived, bytesTotal); - + // Recording HTTP bytes downloaded in stats DependencyManager::get()->updateStat(STAT_HTTP_RESOURCE_TOTAL_BYTES, bytesReceived); - - + + } void HTTPResourceRequest::onTimeout() { diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 954b9685af..d0cb0109c7 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -668,7 +668,11 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t } // insert the new node and release our read lock +#ifdef Q_OS_ANDROID + _nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer)); +#else _nodeHash.emplace(newNode->getUUID(), newNodePointer); +#endif readLocker.unlock(); qCDebug(networking) << "Added" << *newNode; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 48e5c8a62c..fcf369f786 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -809,7 +809,7 @@ void NodeList::sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationN void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { // enumerate the nodes to send a reliable ignore packet to each that can leverage it if (!nodeID.isNull() && _sessionUUID != nodeID) { - eachMatchingNode([&nodeID](const SharedNodePointer& node)->bool { + eachMatchingNode([](const SharedNodePointer& node)->bool { if (node->getType() == NodeType::AudioMixer || node->getType() == NodeType::AvatarMixer) { return true; } else { diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index e94c43b6fb..67359ee862 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -9,9 +9,28 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "NodePermissions.h" + +#include #include #include -#include "NodePermissions.h" + + + +size_t std::hash::operator()(const NodePermissionsKey& key) const { + size_t result = qHash(key.first); + result <<= sizeof(size_t) / 2; + +#if (QT_POINTER_SIZE == 8) + const uint MASK = 0x00FF; +#else + const uint MASK = 0xFFFF; +#endif + + result |= (qHash(key.second) & MASK); + return result; +} + NodePermissionsKey NodePermissions::standardNameLocalhost = NodePermissionsKey("localhost", 0); NodePermissionsKey NodePermissions::standardNameLoggedIn = NodePermissionsKey("logged-in", 0); diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 129d7e5c08..8f7bdaebfe 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -29,13 +29,8 @@ using NodePermissionsKeyList = QList>; namespace std { template<> - struct hash { - size_t operator()(const NodePermissionsKey& key) const { - size_t result = qHash(key.first); - result <<= 32; - result |= qHash(key.second); - return result; - } + struct hash { + size_t operator()(const NodePermissionsKey& key) const; }; } diff --git a/libraries/networking/src/udt/CongestionControl.h b/libraries/networking/src/udt/CongestionControl.h index 3a93134a5e..e6a462651e 100644 --- a/libraries/networking/src/udt/CongestionControl.h +++ b/libraries/networking/src/udt/CongestionControl.h @@ -123,9 +123,9 @@ private: p_high_resolution_clock::time_point _lastRCTime = p_high_resolution_clock::now(); // last rate increase time - bool _slowStart { true }; // if in slow start phase + bool _slowStart { true }; // if in slow start phase SequenceNumber _lastACK; // last ACKed sequence number from previous - bool _loss { false }; // if loss happened since last rate increase + bool _loss { false }; // if loss happened since last rate increase SequenceNumber _lastDecreaseMaxSeq; // max pkt seq num sent out when last decrease happened double _lastDecreasePeriod { 1 }; // value of _packetSendPeriod when last decrease happened int _nakCount { 0 }; // number of NAKs in congestion epoch diff --git a/libraries/networking/src/udt/ControlPacket.cpp b/libraries/networking/src/udt/ControlPacket.cpp index 0d5d3e8c25..6fdefd36f6 100644 --- a/libraries/networking/src/udt/ControlPacket.cpp +++ b/libraries/networking/src/udt/ControlPacket.cpp @@ -68,7 +68,7 @@ ControlPacket::ControlPacket(std::unique_ptr data, qint64 size, const Hi } ControlPacket::ControlPacket(ControlPacket&& other) : - BasePacket(std::move(other)) + BasePacket(std::move(other)) { _type = other._type; } diff --git a/libraries/physics/src/CharacterRayResult.h b/libraries/physics/src/CharacterRayResult.h index e8b0bb7f99..239ab9570a 100644 --- a/libraries/physics/src/CharacterRayResult.h +++ b/libraries/physics/src/CharacterRayResult.h @@ -26,19 +26,19 @@ public: protected: const CharacterGhostObject* _character; - // Note: Public data members inherited from ClosestRayResultCallback - // - // btVector3 m_rayFromWorld;//used to calculate hitPointWorld from hitFraction - // btVector3 m_rayToWorld; - // btVector3 m_hitNormalWorld; - // btVector3 m_hitPointWorld; - // - // Note: Public data members inherited from RayResultCallback - // - // btScalar m_closestHitFraction; - // const btCollisionObject* m_collisionObject; - // short int m_collisionFilterGroup; - // short int m_collisionFilterMask; + // Note: Public data members inherited from ClosestRayResultCallback + // + // btVector3 m_rayFromWorld;//used to calculate hitPointWorld from hitFraction + // btVector3 m_rayToWorld; + // btVector3 m_hitNormalWorld; + // btVector3 m_hitPointWorld; + // + // Note: Public data members inherited from RayResultCallback + // + // btScalar m_closestHitFraction; + // const btCollisionObject* m_collisionObject; + // short int m_collisionFilterGroup; + // short int m_collisionFilterMask; }; #endif // hifi_CharacterRayResult_h diff --git a/libraries/physics/src/CharacterSweepResult.cpp b/libraries/physics/src/CharacterSweepResult.cpp index a5c4092b1d..36e36185d3 100755 --- a/libraries/physics/src/CharacterSweepResult.cpp +++ b/libraries/physics/src/CharacterSweepResult.cpp @@ -25,7 +25,7 @@ CharacterSweepResult::CharacterSweepResult(const CharacterGhostObject* character } btScalar CharacterSweepResult::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool useWorldFrame) { - // skip objects that we shouldn't collide with + // skip objects that we shouldn't collide with if (!convexResult.m_hitCollisionObject->hasContactResponse()) { return btScalar(1.0); } @@ -37,6 +37,6 @@ btScalar CharacterSweepResult::addSingleResult(btCollisionWorld::LocalConvexResu } void CharacterSweepResult::resetHitHistory() { - m_hitCollisionObject = nullptr; - m_closestHitFraction = btScalar(1.0f); + m_hitCollisionObject = nullptr; + m_closestHitFraction = btScalar(1.0f); } diff --git a/libraries/physics/src/CharacterSweepResult.h b/libraries/physics/src/CharacterSweepResult.h index 1e2898a3cf..3c7bb66282 100644 --- a/libraries/physics/src/CharacterSweepResult.h +++ b/libraries/physics/src/CharacterSweepResult.h @@ -22,13 +22,13 @@ class CharacterSweepResult : public btCollisionWorld::ClosestConvexResultCallbac public: CharacterSweepResult(const CharacterGhostObject* character); virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool useWorldFrame) override; - void resetHitHistory(); + void resetHitHistory(); protected: const CharacterGhostObject* _character; // NOTE: Public data members inherited from ClosestConvexResultCallback: // - // btVector3 m_convexFromWorld; // unused except by btClosestNotMeConvexResultCallback + // btVector3 m_convexFromWorld; // unused except by btClosestNotMeConvexResultCallback // btVector3 m_convexToWorld; // unused except by btClosestNotMeConvexResultCallback // btVector3 m_hitNormalWorld; // btVector3 m_hitPointWorld; diff --git a/libraries/physics/src/CollisionRenderMeshCache.h b/libraries/physics/src/CollisionRenderMeshCache.h index 6a6857a5ae..10b2440db2 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.h +++ b/libraries/physics/src/CollisionRenderMeshCache.h @@ -21,7 +21,7 @@ class CollisionRenderMeshCache { public: - using Key = const void*; // must actually be a const btCollisionShape* + using Key = const void*; // must actually be a const btCollisionShape* CollisionRenderMeshCache(); ~CollisionRenderMeshCache(); diff --git a/libraries/procedural/CMakeLists.txt b/libraries/procedural/CMakeLists.txt index 3ebd0f3d14..daf6fefccc 100644 --- a/libraries/procedural/CMakeLists.txt +++ b/libraries/procedural/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME procedural) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() -link_hifi_libraries(shared gpu gpu-gl networking model model-networking ktx image) +link_hifi_libraries(shared gpu networking model model-networking ktx image) diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index b062fcdd63..9544759037 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -23,7 +23,10 @@ ProceduralSkybox::ProceduralSkybox() : model::Skybox() { _procedural._fragmentSource = skybox_frag; // Adjust the pipeline state for background using the stencil test _procedural.setDoesFade(false); - _procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(1, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); + // Must match PrepareStencil::STENCIL_BACKGROUND + const int8_t STENCIL_BACKGROUND = 0; + _procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_BACKGROUND, 0xFF, gpu::EQUAL, + gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); } bool ProceduralSkybox::empty() { diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 0079ffae66..94232c81b2 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -3,7 +3,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") setup_hifi_library(Widgets OpenGL Network Qml Quick Script) -link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities image procedural) +link_hifi_libraries(shared ktx gpu model model-networking render animation fbx image procedural) include_hifi_library_headers(networking) include_hifi_library_headers(octree) include_hifi_library_headers(audio) diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 3013ad9ebb..70c2e3b5ce 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -70,9 +70,8 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline(RenderArgs* ar gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - PrepareStencil::testMaskNoAA(*state); - state->setDepthTest(false, false, gpu::LESS_EQUAL); + PrepareStencil::testNoAA(*state); // Good to go add the brand new pipeline _antialiasingPipeline = gpu::Pipeline::create(program, state); @@ -95,7 +94,7 @@ const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setDepthTest(false, false, gpu::LESS_EQUAL); - PrepareStencil::testMaskNoAA(*state); + PrepareStencil::testNoAA(*state); // Good to go add the brand new pipeline _blendPipeline = gpu::Pipeline::create(program, state); diff --git a/libraries/render-utils/src/DeferredBufferRead.slh b/libraries/render-utils/src/DeferredBufferRead.slh index 315de30fea..fbca241bb9 100644 --- a/libraries/render-utils/src/DeferredBufferRead.slh +++ b/libraries/render-utils/src/DeferredBufferRead.slh @@ -90,7 +90,7 @@ DeferredFragment unpackDeferredFragmentNoPositionNoAmbient(vec2 texcoord) { vec4 diffuseVal; DeferredFragment frag; - frag.depthVal = -1; + frag.depthVal = -1.0; normalVal = texture(normalMap, texcoord); diffuseVal = texture(albedoMap, texcoord); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 2b5fdc1d74..b0488867e9 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -420,7 +420,7 @@ void PrepareDeferred::run(const RenderContextPointer& renderContext, const Input gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_COLOR1 | gpu::Framebuffer::BUFFER_COLOR2 | gpu::Framebuffer::BUFFER_COLOR3 | gpu::Framebuffer::BUFFER_DEPTH | gpu::Framebuffer::BUFFER_STENCIL, - vec4(vec3(0), 0), 1.0, 1, true); + vec4(vec3(0), 0), 1.0, 0, true); // For the rest of the rendering, bind the lighting model batch.setUniformBuffer(LIGHTING_MODEL_BUFFER_SLOT, lightingModel->getParametersBuffer()); diff --git a/libraries/render-utils/src/FadeEffect.h b/libraries/render-utils/src/FadeEffect.h index 4b4e401332..e4a7debd1e 100644 --- a/libraries/render-utils/src/FadeEffect.h +++ b/libraries/render-utils/src/FadeEffect.h @@ -47,8 +47,8 @@ private: glm::vec3 _lastBaseOffset { 0.f, 0.f, 0.f }; glm::vec3 _lastBaseInvSize { 1.f, 1.f, 1.f }; - explicit FadeEffect(); - virtual ~FadeEffect() { } + explicit FadeEffect(); + virtual ~FadeEffect() { } }; #endif // hifi_render_utils_FadeEffect_h diff --git a/libraries/render-utils/src/ForwardBuffer.slh b/libraries/render-utils/src/ForwardBuffer.slh new file mode 100644 index 0000000000..4d1dc89aa4 --- /dev/null +++ b/libraries/render-utils/src/ForwardBuffer.slh @@ -0,0 +1,68 @@ + +<@if not FORWARD_BUFFER_SLH@> +<@def FORWARD_BUFFER_SLH@> + +<@include gpu/PackedNormal.slh@> + +// Unpack the metallic-mode value +const float FRAG_PACK_SHADED_NON_METALLIC = 0.0; +const float FRAG_PACK_SHADED_METALLIC = 0.1; +const float FRAG_PACK_SHADED_RANGE_INV = 1.0 / (FRAG_PACK_SHADED_METALLIC - FRAG_PACK_SHADED_NON_METALLIC); + +const float FRAG_PACK_LIGHTMAPPED_NON_METALLIC = 0.2; +const float FRAG_PACK_LIGHTMAPPED_METALLIC = 0.3; +const float FRAG_PACK_LIGHTMAPPED_RANGE_INV = 1.0 / (FRAG_PACK_LIGHTMAPPED_METALLIC - FRAG_PACK_LIGHTMAPPED_NON_METALLIC); + +const float FRAG_PACK_SCATTERING_NON_METALLIC = 0.4; +const float FRAG_PACK_SCATTERING_METALLIC = 0.5; +const float FRAG_PACK_SCATTERING_RANGE_INV = 1.0 / (FRAG_PACK_SCATTERING_METALLIC - FRAG_PACK_SCATTERING_NON_METALLIC); + +const float FRAG_PACK_UNLIT = 0.6; + +const int FRAG_MODE_UNLIT = 0; +const int FRAG_MODE_SHADED = 1; +const int FRAG_MODE_LIGHTMAPPED = 2; +const int FRAG_MODE_SCATTERING = 3; + +void unpackModeMetallic(float rawValue, out int mode, out float metallic) { + if (rawValue <= FRAG_PACK_SHADED_METALLIC) { + mode = FRAG_MODE_SHADED; + metallic = clamp((rawValue - FRAG_PACK_SHADED_NON_METALLIC) * FRAG_PACK_SHADED_RANGE_INV, 0.0, 1.0); + } else if (rawValue <= FRAG_PACK_LIGHTMAPPED_METALLIC) { + mode = FRAG_MODE_LIGHTMAPPED; + metallic = clamp((rawValue - FRAG_PACK_LIGHTMAPPED_NON_METALLIC) * FRAG_PACK_LIGHTMAPPED_RANGE_INV, 0.0, 1.0); + } else if (rawValue <= FRAG_PACK_SCATTERING_METALLIC) { + mode = FRAG_MODE_SCATTERING; + metallic = clamp((rawValue - FRAG_PACK_SCATTERING_NON_METALLIC) * FRAG_PACK_SCATTERING_RANGE_INV, 0.0, 1.0); + } else if (rawValue >= FRAG_PACK_UNLIT) { + mode = FRAG_MODE_UNLIT; + metallic = 0.0; + } +} + +float packShadedMetallic(float metallic) { + return mix(FRAG_PACK_SHADED_NON_METALLIC, FRAG_PACK_SHADED_METALLIC, metallic); +} + +float packLightmappedMetallic(float metallic) { + return mix(FRAG_PACK_LIGHTMAPPED_NON_METALLIC, FRAG_PACK_LIGHTMAPPED_METALLIC, metallic); +} + +float packScatteringMetallic(float metallic) { + return mix(FRAG_PACK_SCATTERING_NON_METALLIC, FRAG_PACK_SCATTERING_METALLIC, metallic); +} + +float packUnlit() { + return FRAG_PACK_UNLIT; +} + +<@endif@> diff --git a/libraries/render-utils/src/ForwardBufferWrite.slh b/libraries/render-utils/src/ForwardBufferWrite.slh new file mode 100644 index 0000000000..873514d51f --- /dev/null +++ b/libraries/render-utils/src/ForwardBufferWrite.slh @@ -0,0 +1,63 @@ + +<@if not FORWARD_BUFFER_WRITE_SLH@> +<@def FORWARD_BUFFER_WRITE_SLH@> + +<@include ForwardBuffer.slh@> + + +layout(location = 0) out vec4 _fragColor0; + +// the alpha threshold +const float alphaThreshold = 0.5; +float evalOpaqueFinalAlpha(float alpha, float mapAlpha) { + return mix(alpha, 1.0 - alpha, step(mapAlpha, alphaThreshold)); +} + +const float DEFAULT_ROUGHNESS = 0.9; +const float DEFAULT_SHININESS = 10.0; +const float DEFAULT_METALLIC = 0.0; +const vec3 DEFAULT_SPECULAR = vec3(0.1); +const vec3 DEFAULT_EMISSIVE = vec3(0.0); +const float DEFAULT_OCCLUSION = 1.0; +const float DEFAULT_SCATTERING = 0.0; +const vec3 DEFAULT_FRESNEL = DEFAULT_EMISSIVE; + +void packForwardFragment(vec3 normal, float alpha, vec3 albedo, float roughness, float metallic, vec3 emissive, float occlusion, float scattering) { + if (alpha != 1.0) { + discard; + } + _fragColor0 = vec4(albedo, ((scattering > 0.0) ? packScatteringMetallic(metallic) : packShadedMetallic(metallic))); +} + +void packForwardFragmentLightmap(vec3 normal, float alpha, vec3 albedo, float roughness, float metallic, vec3 fresnel, vec3 lightmap) { + if (alpha != 1.0) { + discard; + } + + _fragColor0 = vec4(albedo, packLightmappedMetallic(metallic)); +} + +void packForwardFragmentUnlit(vec3 normal, float alpha, vec3 color) { + if (alpha != 1.0) { + discard; + } + _fragColor0 = vec4(color, packUnlit()); +} + +void packForwardFragmentTranslucent(vec3 normal, float alpha, vec3 albedo, vec3 fresnel, float roughness) { + if (alpha <= 0.0) { + discard; + } + _fragColor0 = vec4(albedo.rgb, alpha); +} + +<@endif@> diff --git a/libraries/render-utils/src/ForwardGlobalLight.slh b/libraries/render-utils/src/ForwardGlobalLight.slh new file mode 100644 index 0000000000..aba0498ef5 --- /dev/null +++ b/libraries/render-utils/src/ForwardGlobalLight.slh @@ -0,0 +1,198 @@ + +<@if not DEFERRED_GLOBAL_LIGHT_SLH@> +<@def DEFERRED_GLOBAL_LIGHT_SLH@> + +<@include model/Light.slh@> + +<@include LightingModel.slh@> +<$declareLightBuffer()$> +<$declareLightAmbientBuffer()$> + +<@include LightAmbient.slh@> +<@include LightDirectional.slh@> + + +<@func prepareGlobalLight(isScattering)@> + // prepareGlobalLight + // Transform directions to worldspace + vec3 fragNormal = vec3((normal)); + vec3 fragEyeVector = vec3(invViewMat * vec4(-1.0*position, 0.0)); + vec3 fragEyeDir = normalize(fragEyeVector); + + // Get light + Light light = getLight(); + LightAmbient lightAmbient = getLightAmbient(); + + vec3 lightDirection = getLightDirection(light); + vec3 lightIrradiance = getLightIrradiance(light); + + vec3 color = vec3(0.0); + +<@endfunc@> + + +<@func declareEvalAmbientGlobalColor()@> +vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, float roughness) { + <$prepareGlobalLight()$> + color += albedo * getLightColor(light) * obscurance * getLightAmbientIntensity(lightAmbient); + return color; +} +<@endfunc@> + +<@func declareEvalAmbientSphereGlobalColor(supportScattering)@> + +<$declareLightingAmbient(1, _SCRIBE_NULL, _SCRIBE_NULL, $supportScattering$)$> +<$declareLightingDirectional($supportScattering$)$> + +<@if supportScattering@> +<$declareDeferredCurvature()$> +<@endif@> + +vec3 evalAmbientSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, +vec3 albedo, vec3 fresnel, float metallic, float roughness +<@if supportScattering@> + , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature +<@endif@> ) { + + <$prepareGlobalLight($supportScattering$)$> + + // Ambient + vec3 ambientDiffuse; + vec3 ambientSpecular; + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance +<@if supportScattering@> + ,scattering, midNormalCurvature, lowNormalCurvature +<@endif@> ); + color += ambientDiffuse; + color += ambientSpecular; + + + // Directional + vec3 directionalDiffuse; + vec3 directionalSpecular; + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation +<@if supportScattering@> + ,scattering, midNormalCurvature, lowNormalCurvature +<@endif@> ); + color += directionalDiffuse; + color += directionalSpecular; + + return color; +} + +<@endfunc@> + + +<@func declareEvalSkyboxGlobalColor(supportScattering)@> + +<$declareLightingAmbient(_SCRIBE_NULL, 1, _SCRIBE_NULL, $supportScattering$)$> +<$declareLightingDirectional($supportScattering$)$> + +<@if supportScattering@> +<$declareDeferredCurvature()$> +<@endif@> + +vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, + vec3 albedo, vec3 fresnel, float metallic, float roughness +<@if supportScattering@> + , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature +<@endif@> + ) { + <$prepareGlobalLight($supportScattering$)$> + + // Ambient + vec3 ambientDiffuse; + vec3 ambientSpecular; + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance +<@if supportScattering@> + ,scattering, midNormalCurvature, lowNormalCurvature +<@endif@> + ); + color += ambientDiffuse; + color += ambientSpecular; + + + // Directional + vec3 directionalDiffuse; + vec3 directionalSpecular; + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation +<@if supportScattering@> + ,scattering, midNormalCurvature, lowNormalCurvature +<@endif@> + ); + color += directionalDiffuse; + color += directionalSpecular; + + return color; +} + +<@endfunc@> + +<@func declareEvalLightmappedColor()@> +vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 normal, vec3 albedo, vec3 lightmap) { + Light light = getLight(); + LightAmbient ambient = getLightAmbient(); + + // Catch normals perpendicular to the projection plane, hence the magic number for the threshold + // It should be just 0, but we have inaccuracy so we overshoot + const float PERPENDICULAR_THRESHOLD = -0.005; + vec3 fragNormal = vec3(invViewMat * vec4(normal, 0.0)); // transform to worldspace + float diffuseDot = dot(fragNormal, -getLightDirection(light)); + float facingLight = step(PERPENDICULAR_THRESHOLD, diffuseDot); + + // Reevaluate the shadow attenuation for light facing fragments + float lightAttenuation = (1.0 - facingLight) + facingLight * shadowAttenuation; + + // Diffuse light is the lightmap dimmed by shadow + vec3 diffuseLight = lightAttenuation * lightmap; + + // Ambient light is the lightmap when in shadow + vec3 ambientLight = (1.0 - lightAttenuation) * lightmap * getLightAmbientIntensity(ambient); + + return isLightmapEnabled() * obscurance * albedo * (diffuseLight + ambientLight); +} +<@endfunc@> + + + + +<@func declareEvalGlobalLightingAlphaBlended()@> + +<$declareLightingAmbient(1, 1, 1)$> +<$declareLightingDirectional()$> + +vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) { + <$prepareGlobalLight()$> + + color += emissive * isEmissiveEnabled(); + + // Ambient + vec3 ambientDiffuse; + vec3 ambientSpecular; + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance); + color += ambientDiffuse; + color += ambientSpecular / opacity; + + // Directional + vec3 directionalDiffuse; + vec3 directionalSpecular; + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + color += directionalDiffuse; + color += directionalSpecular / opacity; + + return color; +} + +<@endfunc@> + + +<@endif@> diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 3bf83d08c9..658eab7210 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -18,9 +18,11 @@ #include #include +#include +#include + #include #include -#include #include "TextureCache.h" #include "RenderUtilsLogging.h" @@ -561,7 +563,7 @@ void GeometryCache::initializeShapePipelines() { render::ShapePipelinePointer GeometryCache::getShapePipeline(bool textured, bool transparent, bool culled, bool unlit, bool depthBias) { - return std::make_shared(getSimplePipeline(textured, transparent, culled, unlit, depthBias, false), nullptr, + return std::make_shared(getSimplePipeline(textured, transparent, culled, unlit, depthBias, false, true), nullptr, [](const render::ShapePipeline& , gpu::Batch& batch, render::Args*) { batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, DependencyManager::get()->getWhiteTexture()); } @@ -1877,6 +1879,7 @@ public: IS_UNLIT_FLAG, HAS_DEPTH_BIAS_FLAG, IS_FADING_FLAG, + IS_ANTIALIASED_FLAG, NUM_FLAGS, }; @@ -1888,6 +1891,7 @@ public: IS_UNLIT = (1 << IS_UNLIT_FLAG), HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG), IS_FADING = (1 << IS_FADING_FLAG), + IS_ANTIALIASED = (1 << IS_ANTIALIASED_FLAG), }; typedef unsigned short Flags; @@ -1899,6 +1903,7 @@ public: bool isUnlit() const { return isFlag(IS_UNLIT); } bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); } bool isFading() const { return isFlag(IS_FADING); } + bool isAntiAliased() const { return isFlag(IS_ANTIALIASED); } Flags _flags = 0; short _spare = 0; @@ -1907,9 +1912,9 @@ public: SimpleProgramKey(bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false, bool fading = false) { + bool unlit = false, bool depthBias = false, bool fading = false, bool isAntiAliased = true) { _flags = (textured ? IS_TEXTURED : 0) | (transparent ? IS_TRANSPARENT : 0) | (culled ? IS_CULLED : 0) | - (unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0) | (fading ? IS_FADING : 0); + (unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0) | (fading ? IS_FADING : 0) | (isAntiAliased ? IS_ANTIALIASED : 0); } SimpleProgramKey(int bitmask) : _flags(bitmask) {} @@ -1958,8 +1963,8 @@ gpu::PipelinePointer GeometryCache::getWebBrowserProgram(bool transparent) { return transparent ? _simpleTransparentWebBrowserPipelineNoAA : _simpleOpaqueWebBrowserPipelineNoAA; } -void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) { - batch.setPipeline(getSimplePipeline(textured, transparent, culled, unlit, depthBiased)); +void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased, bool isAntiAliased) { + batch.setPipeline(getSimplePipeline(textured, transparent, culled, unlit, depthBiased, false, isAntiAliased)); // If not textured, set a default albedo map if (!textured) { @@ -1968,8 +1973,8 @@ void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool tra } } -gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transparent, bool culled, bool unlit, bool depthBiased, bool fading) { - SimpleProgramKey config { textured, transparent, culled, unlit, depthBiased, fading }; +gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transparent, bool culled, bool unlit, bool depthBiased, bool fading, bool isAntiAliased) { + SimpleProgramKey config { textured, transparent, culled, unlit, depthBiased, fading, isAntiAliased }; // If the pipeline already exists, return it auto it = _simplePrograms.find(config); @@ -2027,11 +2032,10 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - if (config.isTransparent()) { - PrepareStencil::testMask(*state); - } - else { - PrepareStencil::testMaskDrawShape(*state); + if (config.isAntiAliased()) { + config.isTransparent() ? PrepareStencil::testMask(*state) : PrepareStencil::testMaskDrawShape(*state); + } else { + PrepareStencil::testMaskDrawShapeNoAA(*state); } gpu::ShaderPointer program = (config.isUnlit()) ? (config.isFading() ? _unlitFadeShader : _unlitShader) : (config.isFading() ? _simpleFadeShader : _simpleShader); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index a90842403b..288ab363f0 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -161,10 +161,10 @@ public: // Bind the pipeline and get the state to render static geometry void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false); + bool unlit = false, bool depthBias = false, bool isAntiAliased = true); // Get the pipeline to render static geometry static gpu::PipelinePointer getSimplePipeline(bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false, bool fading = false); + bool unlit = false, bool depthBias = false, bool fading = false, bool isAntiAliased = true); void bindWebBrowserProgram(gpu::Batch& batch, bool transparent = false); gpu::PipelinePointer getWebBrowserProgram(bool transparent); diff --git a/libraries/render-utils/src/LightClusterGrid_shared.slh b/libraries/render-utils/src/LightClusterGrid_shared.slh index 50499cf31a..6d43e71920 100644 --- a/libraries/render-utils/src/LightClusterGrid_shared.slh +++ b/libraries/render-utils/src/LightClusterGrid_shared.slh @@ -36,7 +36,7 @@ vec3 frustumGrid_volumeToGrid(vec3 vpos, ivec3 dims) { vec4 frustumGrid_volumeToClip(vec3 vpos, float rangeNear, float rangeFar) { vec3 ndcPos = vec3(-1.0f + 2.0f * vpos.x, -1.0f + 2.0f * vpos.y, vpos.z); - float depth = rangeNear * (1 - ndcPos.z) + rangeFar * (ndcPos.z); + float depth = rangeNear * (1.0f - ndcPos.z) + rangeFar * (ndcPos.z); vec4 clipPos = vec4(ndcPos.x * depth, ndcPos.y * depth, 1.0f, depth); return clipPos; } @@ -114,14 +114,14 @@ int frustumGrid_eyeDepthToClusterLayer(float eyeZ) { float volumeZ = frustumGrid_eyeToVolumeDepth(eyeZ, frustumGrid.rangeNear, frustumGrid.rangeFar); - float gridZ = frustumGrid_volumeToGridDepth(volumeZ, frustumGrid.dims); + int gridZ = int(frustumGrid_volumeToGridDepth(volumeZ, frustumGrid.dims)); if (gridZ >= frustumGrid.dims.z) { gridZ = frustumGrid.dims.z; } - return int(gridZ); + return gridZ; } ivec3 frustumGrid_eyeToClusterPos(vec3 eyePos) { @@ -134,8 +134,8 @@ ivec3 frustumGrid_eyeToClusterPos(vec3 eyePos) { vec3 gridPos = frustumGrid_volumeToGrid(volumePos, frustumGrid.dims); - if (gridPos.z >= frustumGrid.dims.z) { - gridPos.z = frustumGrid.dims.z; + if (gridPos.z >= float(frustumGrid.dims.z)) { + gridPos.z = float(frustumGrid.dims.z); } ivec3 igridPos = ivec3(floor(gridPos)); @@ -154,7 +154,7 @@ ivec3 frustumGrid_eyeToClusterPos(vec3 eyePos) { int frustumGrid_eyeToClusterDirH(vec3 eyeDir) { if (eyeDir.z >= 0.0f) { - return (eyeDir.x > 0 ? frustumGrid.dims.x : -1); + return (eyeDir.x > 0.0f ? frustumGrid.dims.x : -1); } float eyeDepth = -eyeDir.z; @@ -168,7 +168,7 @@ int frustumGrid_eyeToClusterDirH(vec3 eyeDir) { int frustumGrid_eyeToClusterDirV(vec3 eyeDir) { if (eyeDir.z >= 0.0f) { - return (eyeDir.y > 0 ? frustumGrid.dims.y : -1); + return (eyeDir.y > 0.0f ? frustumGrid.dims.y : -1); } float eyeDepth = -eyeDir.z; diff --git a/libraries/render-utils/src/LightPoint.slh b/libraries/render-utils/src/LightPoint.slh index ac1e415d9d..7e389e11f6 100644 --- a/libraries/render-utils/src/LightPoint.slh +++ b/libraries/render-utils/src/LightPoint.slh @@ -59,12 +59,12 @@ bool evalLightPointEdge(out vec3 color, Light light, vec4 fragLightDirLen, vec3 // Show edges float edge = abs(2.0 * ((lightVolume_getRadius(light.volume) - fragLightDistance) / (0.1)) - 1.0); - if (edge < 1) { + if (edge < 1.0) { float edgeCoord = exp2(-8.0*edge*edge); color = vec3(edgeCoord * edgeCoord * getLightColor(light)); } - return (edge < 1); + return (edge < 1.0); } <@endfunc@> diff --git a/libraries/render-utils/src/LightSpot.slh b/libraries/render-utils/src/LightSpot.slh index a38851b039..8627dae0eb 100644 --- a/libraries/render-utils/src/LightSpot.slh +++ b/libraries/render-utils/src/LightSpot.slh @@ -66,12 +66,12 @@ bool evalLightSpotEdge(out vec3 color, Light light, vec4 fragLightDirLen, float float edgeDistS = dot(fragLightDistance * vec2(cosSpotAngle, sqrt(1.0 - cosSpotAngle * cosSpotAngle)), -lightVolume_getSpotOutsideNormal2(light.volume)); float edgeDist = min(edgeDistR, edgeDistS); float edge = abs(2.0 * (edgeDist / (0.1)) - 1.0); - if (edge < 1) { + if (edge < 1.0) { float edgeCoord = exp2(-8.0*edge*edge); color = vec3(edgeCoord * edgeCoord * getLightColor(light)); } - return (edge < 1); + return (edge < 1.0); } <@endfunc@> diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 209a1f38d6..521c4894dc 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -157,7 +157,7 @@ vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float m float power = specularDistribution(roughness, fragNormal, halfDir); vec3 specular = fresnelColor * power * diffuse; - return vec4(specular, (1.0 - metallic) * diffuse * (1 - fresnelColor.x)); + return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x)); } // Frag Shading returns the diffuse amount as W and the specular rgb as xyz @@ -171,7 +171,7 @@ vec4 evalPBRShadingDielectric(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDi float power = specularDistribution(roughness, fragNormal, halfDir); vec3 specular = vec3(fresnelScalar) * power * diffuse; - return vec4(specular, diffuse * (1 - fresnelScalar)); + return vec4(specular, diffuse * (1.0 - fresnelScalar)); } vec4 evalPBRShadingMetallic(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, vec3 fresnel) { diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index e694935361..6c77fc4a91 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -66,7 +66,7 @@ vec3 fetchNormalMap(vec2 uv) { // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out vec2 t = 2.0 * (texture(normalMap, uv).rg - vec2(0.5, 0.5)); vec2 t2 = t*t; - return vec3(t.x, sqrt(1 - t2.x - t2.y), t.y); + return vec3(t.x, sqrt(1.0 - t2.x - t2.y), t.y); } <@endif@> @@ -163,7 +163,7 @@ vec3 fetchLightmapMap(vec2 uv) { vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); // attenuate the normal map divergence from the mesh normal based on distance // THe attenuation range [20,100] meters from the eye is arbitrary for now - vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(20, 100, (-<$fragPos$>).z)); + vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(20.0, 100.0, (-<$fragPos$>).z)); <$normal$> = vec3(normalizedTangent * localNormal.x + normalizedNormal * localNormal.y + normalizedBitangent * localNormal.z); } <@endfunc@> diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 3f57a1779a..dc419c03c3 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -14,7 +14,6 @@ #include #include "DeferredLightingEffect.h" -#include "EntityItem.h" using namespace render; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 9948a8bddd..f25cad8a6e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -228,18 +228,20 @@ void Model::updateRenderItems() { // We need to update them here so we can correctly update the bounding box. self->updateClusterMatrices(); + Transform modelTransform = self->getTransform(); + Transform physicsTransform = modelTransform; + modelTransform.setScale(glm::vec3(1.0f)); + uint32_t deleteGeometryCounter = self->_deleteGeometryCounter; render::Transaction transaction; foreach (auto itemID, self->_modelMeshRenderItemsMap.keys()) { - transaction.updateItem(itemID, [deleteGeometryCounter](ModelMeshPartPayload& data) { + transaction.updateItem(itemID, [deleteGeometryCounter, modelTransform](ModelMeshPartPayload& data) { ModelPointer model = data._model.lock(); if (model && model->isLoaded()) { // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == model->_deleteGeometryCounter) { - Transform modelTransform = model->getTransform(); - modelTransform.setScale(glm::vec3(1.0f)); - + const Model::MeshState& state = model->getMeshState(data._meshIndex); Transform renderTransform = modelTransform; if (state.clusterMatrices.size() == 1) { @@ -254,11 +256,10 @@ void Model::updateRenderItems() { // collision mesh does not share the same unit scale as the FBX file's mesh: only apply offset Transform collisionMeshOffset; collisionMeshOffset.setIdentity(); - Transform modelTransform = self->getTransform(); foreach(auto itemID, self->_collisionRenderItemsMap.keys()) { - transaction.updateItem(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) { + transaction.updateItem(itemID, [physicsTransform, collisionMeshOffset](MeshPartPayload& data) { // update the model transform for this render item. - data.updateTransform(modelTransform, collisionMeshOffset); + data.updateTransform(physicsTransform, collisionMeshOffset); }); } @@ -1306,55 +1307,6 @@ bool Model::isRenderable() const { return !_meshStates.isEmpty() || (isLoaded() && _renderGeometry->getMeshes().empty()); } -bool Model::initWhenReady(const render::ScenePointer& scene) { - // NOTE: this only called by SkeletonModel - if (_addedToScene || !isRenderable()) { - return false; - } - - createRenderItemSet(); - - render::Transaction transaction; - - bool addedTransaction = false; - if (_collisionGeometry) { - foreach (auto renderItem, _collisionRenderItems) { - auto item = scene->allocateID(); - auto renderPayload = std::make_shared(renderItem); - _collisionRenderItemsMap.insert(item, renderPayload); - transaction.resetItem(item, renderPayload); - } - addedTransaction = !_collisionRenderItems.empty(); - } else { - bool hasTransparent = false; - size_t verticesCount = 0; - foreach (auto renderItem, _modelMeshRenderItems) { - auto item = scene->allocateID(); - auto renderPayload = std::make_shared(renderItem); - - hasTransparent = hasTransparent || renderItem.get()->getShapeKey().isTranslucent(); - verticesCount += renderItem.get()->getVerticesCount(); - _modelMeshRenderItemsMap.insert(item, renderPayload); - transaction.resetItem(item, renderPayload); - } - addedTransaction = !_modelMeshRenderItemsMap.empty(); - _renderInfoVertexCount = verticesCount; - _renderInfoDrawCalls = _modelMeshRenderItemsMap.count(); - _renderInfoHasTransparent = hasTransparent; - } - _addedToScene = addedTransaction; - if (addedTransaction) { - scene->enqueueTransaction(transaction); - // NOTE: updateRender items enqueues identical transaction (using a lambda) - // so it looks like we're doing double work here, but I don't want to remove the call - // for fear there is some side effect we'll miss. -- Andrew 2016.07.21 - // TODO: figure out if we really need this call to updateRenderItems() or not. - updateRenderItems(); - } - - return true; -} - class CollisionRenderGeometry : public Geometry { public: CollisionRenderGeometry(model::MeshPointer mesh) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 63aeacf80c..6d338b4598 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -87,7 +87,6 @@ public: bool needsFixupInScene() const; bool needsReload() const { return _needsReload; } - bool initWhenReady(const render::ScenePointer& scene); bool addToScene(const render::ScenePointer& scene, render::Transaction& transaction) { auto getters = render::Item::Status::Getters(0); @@ -382,8 +381,8 @@ protected: QVector> _collisionRenderItems; QMap _collisionRenderItemsMap; - QVector> _modelMeshRenderItems; - QMap _modelMeshRenderItemsMap; + QVector> _modelMeshRenderItems; + QMap _modelMeshRenderItemsMap; render::ItemIDs _modelMeshRenderItemIDs; diff --git a/libraries/render-utils/src/PickItemsJob.cpp b/libraries/render-utils/src/PickItemsJob.cpp index 48ba605d7b..860262a969 100644 --- a/libraries/render-utils/src/PickItemsJob.cpp +++ b/libraries/render-utils/src/PickItemsJob.cpp @@ -31,26 +31,26 @@ void PickItemsJob::run(const render::RenderContextPointer& renderContext, const } render::ItemBound PickItemsJob::findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const { - const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition(); - const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection(); - BoxFace face; - glm::vec3 normal; - float isectDistance; - render::ItemBound nearestItem( render::Item::INVALID_ITEM_ID ); - const float minDistance = 0.2f; - const float maxDistance = 50.f; + const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition(); + const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection(); + BoxFace face; + glm::vec3 normal; + float isectDistance; + render::ItemBound nearestItem( render::Item::INVALID_ITEM_ID ); + const float minDistance = 0.2f; + const float maxDistance = 50.f; render::ItemKey itemKey; - for (const auto& itemBound : inputs) { - if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) { - auto& item = renderContext->_scene->getItem(itemBound.id); + for (const auto& itemBound : inputs) { + if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) { + auto& item = renderContext->_scene->getItem(itemBound.id); itemKey = item.getKey(); - if (itemKey.isWorldSpace() && isectDistance>minDistance && isectDistance < minIsectDistance && isectDistanceminDistance && isectDistance < minIsectDistance && isectDistance; + using Config = PickItemsConfig; + using Input = render::ItemBounds; + using Output = render::ItemBounds; + using JobModel = render::Job::ModelIO; PickItemsJob(render::ItemKey::Flags validKeys = render::ItemKey::Builder().withTypeMeta().withTypeShape().build()._flags, render::ItemKey::Flags excludeKeys = 0); - void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, const PickItemsJob::Input& input, PickItemsJob::Output& output); + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, const PickItemsJob::Input& input, PickItemsJob::Output& output); private: @@ -47,7 +47,7 @@ private: render::ItemKey::Flags _excludeKeys; bool _isEnabled{ false }; - render::ItemBound findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const; + render::ItemBound findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const; }; #endif // hifi_render_utils_PickItemsJob_h diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 296eea1da8..c83251c605 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -17,6 +17,7 @@ #include #include #include +#include "StencilMaskPass.h" #include "FramebufferCache.h" #include "TextureCache.h" @@ -93,7 +94,7 @@ void PrepareFramebuffer::run(const RenderContextPointer& renderContext, gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH | gpu::Framebuffer::BUFFER_STENCIL, - vec4(vec3(0), 1), 1.0, 0.0, true); + vec4(vec3(0), 1), 1.0, 0, true); }); framebuffer = _framebuffer; @@ -130,11 +131,7 @@ const gpu::PipelinePointer Stencil::getPipeline() { auto state = std::make_shared(); state->setDepthTest(true, false, gpu::LESS_EQUAL); - const gpu::int8 STENCIL_OPAQUE = 1; - state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, - gpu::State::STENCIL_OP_REPLACE, - gpu::State::STENCIL_OP_REPLACE, - gpu::State::STENCIL_OP_KEEP)); + PrepareStencil::drawBackground(*state); _stencilPipeline = gpu::Pipeline::create(program, state); } diff --git a/libraries/render-utils/src/StencilMaskPass.cpp b/libraries/render-utils/src/StencilMaskPass.cpp index 295e124ed1..0e0d8b56b3 100644 --- a/libraries/render-utils/src/StencilMaskPass.cpp +++ b/libraries/render-utils/src/StencilMaskPass.cpp @@ -104,30 +104,51 @@ void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::F }); } +// Always draw MASK to the stencil buffer (used to always prevent drawing in certain areas later) void PrepareStencil::drawMask(gpu::State& state) { - state.setStencilTest(true, 0xFF, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE)); + state.setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_MASK, 0xFF, gpu::ALWAYS, + gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE)); } +// Draw BACKGROUND to the stencil buffer behind everything else +void PrepareStencil::drawBackground(gpu::State& state) { + state.setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_BACKGROUND, 0xFF, gpu::ALWAYS, + gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_KEEP)); +} + +// Pass if this area has NOT been marked as MASK void PrepareStencil::testMask(gpu::State& state) { - state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); + state.setStencilTest(true, 0x00, gpu::State::StencilTest(STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, + gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); } -void PrepareStencil::testMaskNoAA(gpu::State& state) { - state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK | PrepareStencil::STENCIL_NO_AA, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); +// Pass if this area has NOT been marked as NO_AA or anything containing NO_AA +void PrepareStencil::testNoAA(gpu::State& state) { + state.setStencilTest(true, 0x00, gpu::State::StencilTest(STENCIL_NO_AA, STENCIL_NO_AA, gpu::NOT_EQUAL, + gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); } +// Pass if this area WAS marked as BACKGROUND +// (see: model/src/Skybox.cpp, procedural/src/ProceduralSkybox.cpp) void PrepareStencil::testBackground(gpu::State& state) { - state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_BACKGROUND, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); -} - -void PrepareStencil::testMaskDrawShape(gpu::State& state) { - state.setStencilTest(true, 0xFF, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO)); -} - -void PrepareStencil::testMaskDrawShapeNoAA(gpu::State& state) { - state.setStencilTest(true, 0xFF, gpu::State::StencilTest(PrepareStencil::STENCIL_MASK | PrepareStencil::STENCIL_NO_AA, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); + state.setStencilTest(true, 0x00, gpu::State::StencilTest(STENCIL_BACKGROUND, 0xFF, gpu::EQUAL, + gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); } +// Pass if this area WAS marked as SHAPE or anything containing SHAPE void PrepareStencil::testShape(gpu::State& state) { - state.setStencilTest(true, 0x00, gpu::State::StencilTest(PrepareStencil::STENCIL_SHAPE, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); + state.setStencilTest(true, 0x00, gpu::State::StencilTest(STENCIL_SHAPE, STENCIL_SHAPE, gpu::EQUAL, + gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); +} + +// Pass if this area was NOT marked as MASK, write to SHAPE if it passes +void PrepareStencil::testMaskDrawShape(gpu::State& state) { + state.setStencilTest(true, STENCIL_SHAPE, gpu::State::StencilTest(STENCIL_MASK | STENCIL_SHAPE, STENCIL_MASK, gpu::NOT_EQUAL, + gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); +} + +// Pass if this area was NOT marked as MASK, write to SHAPE and NO_AA if it passes +void PrepareStencil::testMaskDrawShapeNoAA(gpu::State& state) { + state.setStencilTest(true, STENCIL_SHAPE | STENCIL_NO_AA, gpu::State::StencilTest(STENCIL_MASK | STENCIL_SHAPE | STENCIL_NO_AA, STENCIL_MASK, gpu::NOT_EQUAL, + gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); } \ No newline at end of file diff --git a/libraries/render-utils/src/StencilMaskPass.h b/libraries/render-utils/src/StencilMaskPass.h index 2c0294c471..ddbf4a7ac0 100644 --- a/libraries/render-utils/src/StencilMaskPass.h +++ b/libraries/render-utils/src/StencilMaskPass.h @@ -41,20 +41,20 @@ public: void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& dstFramebuffer); - static const gpu::int8 STENCIL_SHAPE = 0; - static const gpu::int8 STENCIL_BACKGROUND = 1 << 0; - static const gpu::int8 STENCIL_MASK = 1 << 1; - static const gpu::int8 STENCIL_NO_AA = 1 << 2; - + // Always use 0 to clear the stencil buffer to set it to background + static const gpu::int8 STENCIL_BACKGROUND = 0; // must match values in Skybox.cpp and ProceduralSkybox.cpp + static const gpu::int8 STENCIL_MASK = 1 << 0; + static const gpu::int8 STENCIL_SHAPE = 1 << 1; + static const gpu::int8 STENCIL_NO_AA = 1 << 2; static void drawMask(gpu::State& state); + static void drawBackground(gpu::State& state); + static void testNoAA(gpu::State& state); static void testMask(gpu::State& state); - static void testMaskNoAA(gpu::State& state); static void testBackground(gpu::State& state); + static void testShape(gpu::State& state); static void testMaskDrawShape(gpu::State& state); static void testMaskDrawShapeNoAA(gpu::State& state); - static void testShape(gpu::State& state); - private: gpu::PipelinePointer _meshStencilPipeline; diff --git a/libraries/render-utils/src/SubsurfaceScattering.slh b/libraries/render-utils/src/SubsurfaceScattering.slh index 42ffafd9ff..201ec2291a 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.slh +++ b/libraries/render-utils/src/SubsurfaceScattering.slh @@ -105,7 +105,7 @@ vec3 integrate(float cosTheta, float skinRadius) { vec3 totalLight = vec3(0.0); const float _PI = 3.14159265358979523846; - const float step = 2.0 * _PI / <$NumIntegrationSteps$>; + const float step = 2.0 * _PI / float(<$NumIntegrationSteps$>); float a = -(_PI); diff --git a/libraries/render-utils/src/forward_model.slf b/libraries/render-utils/src/forward_model.slf index daeead65ec..7b708a1d24 100644 --- a/libraries/render-utils/src/forward_model.slf +++ b/libraries/render-utils/src/forward_model.slf @@ -1,7 +1,7 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> -// model.frag +// forward_model.frag // fragment shader // // Created by Andrzej Kapolka on 10/14/13. @@ -11,12 +11,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> + !> + +<@include ForwardGlobalLight.slh@> +<$declareEvalSkyboxGlobalColor()$> <@include model/Material.slh@> +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + <@include MaterialTextures.slh@> -<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, _SCRIBE_NULL)$> in vec4 _position; in vec3 _normal; @@ -24,12 +30,13 @@ in vec3 _color; in vec2 _texCoord0; in vec2 _texCoord1; +out vec4 _fragColor; void main(void) { Material mat = getMaterial(); int matKey = getMaterialKey(mat); <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$> - <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + !> float opacity = 1.0; <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; @@ -39,21 +46,43 @@ void main(void) { <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; albedo *= _color; + float metallic = getMaterialMetallic(mat); + vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value + if (metallic <= 0.5) { + metallic = 0.0; + } else { + fresnel = albedo; + metallic = 1.0; + } + float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; - float scattering = getMaterialScattering(mat); - packDeferredFragment( - normalize(_normal.xyz), - opacity, + vec3 fragPosition = _position.xyz; + + TransformCamera cam = getTransformCamera(); + vec3 fragNormal; + <$transformEyeToWorldDir(cam, _normal, fragNormal)$> + + /* vec4 color = vec4(evalSkyboxGlobalColor( + cam._viewInverse, + 1.0, + 1.0, + fragPosition, + fragNormal, albedo, - roughness, - getMaterialMetallic(mat), - emissive, - occlusionTex, - scattering); + fresnel, + metallic, + roughness), + opacity); + color.rgb += emissive * isEmissiveEnabled(); + + */ + + _fragColor = vec4(albedo, opacity); + } diff --git a/libraries/render-utils/src/forward_model_normal_map.slf b/libraries/render-utils/src/forward_model_normal_map.slf index 5cc1a1859f..a199483b9f 100644 --- a/libraries/render-utils/src/forward_model_normal_map.slf +++ b/libraries/render-utils/src/forward_model_normal_map.slf @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// model_normal_map.frag +// forward_model_normal_map.frag // fragment shader // // Created by Andrzej Kapolka on 10/29/13. @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> +<@include ForwardBufferWrite.slh@> <@include model/Material.slh@> @@ -47,12 +47,12 @@ void main(void) { <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> + <$tangentToViewSpace(normalTex, _normal, _tangent, viewNormal)$> float scattering = getMaterialScattering(mat); <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; - packDeferredFragment( + packForwardFragment( viewNormal, opacity, albedo, diff --git a/libraries/render-utils/src/forward_model_normal_specular_map.slf b/libraries/render-utils/src/forward_model_normal_specular_map.slf index 9e079b33a0..ca6bbec3da 100644 --- a/libraries/render-utils/src/forward_model_normal_specular_map.slf +++ b/libraries/render-utils/src/forward_model_normal_specular_map.slf @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// model_normal_specular_map.frag +// forward_model_normal_specular_map.frag // fragment shader // // Created by Andrzej Kapolka on 5/6/14. @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> +<@include ForwardBufferWrite.slh@> <@include model/Material.slh@> @@ -47,14 +47,14 @@ void main(void) { <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> + <$tangentToViewSpace(normalTex, _normal, _tangent, viewNormal)$> float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; float scattering = getMaterialScattering(mat); - packDeferredFragment( + packForwardFragment( normalize(viewNormal.xyz), opacity, albedo, diff --git a/libraries/render-utils/src/forward_model_specular_map.slf b/libraries/render-utils/src/forward_model_specular_map.slf index 47b5e3389d..d2fdd18794 100644 --- a/libraries/render-utils/src/forward_model_specular_map.slf +++ b/libraries/render-utils/src/forward_model_specular_map.slf @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// model_specular_map.frag +// forward_model_specular_map.frag // fragment shader // // Created by Andrzej Kapolka on 5/6/14. @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> +<@include ForwardBufferWrite.slh@> <@include model/Material.slh@> @@ -51,7 +51,7 @@ void main(void) { float scattering = getMaterialScattering(mat); - packDeferredFragment( + packForwardFragment( normalize(_normal), opacity, albedo, diff --git a/libraries/render-utils/src/forward_model_translucent.slf b/libraries/render-utils/src/forward_model_translucent.slf new file mode 100644 index 0000000000..52e8ce50e7 --- /dev/null +++ b/libraries/render-utils/src/forward_model_translucent.slf @@ -0,0 +1,81 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// forward_model_translucent.frag +// fragment shader +// +// Created by Sam Gateau on 2/15/2016. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include model/Material.slh@> + +<@include ForwardGlobalLight.slh@> + +<$declareEvalGlobalLightingAlphaBlended()$> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> + +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec4 _position; +in vec3 _normal; +in vec3 _color; +in float _alpha; + +out vec4 _fragColor; + +void main(void) { + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = getMaterialOpacity(mat) * _alpha; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value + if (metallic <= 0.5) { + metallic = 0.0; + } else { + fresnel = albedo; + metallic = 1.0; + } + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + vec3 fragPosition = _position.xyz; + vec3 fragNormal = normalize(_normal); + + TransformCamera cam = getTransformCamera(); + + _fragColor = vec4(evalGlobalLightingAlphaBlended( + cam._viewInverse, + 1.0, + occlusionTex, + fragPosition, + fragNormal, + albedo, + fresnel, + metallic, + emissive, + roughness, opacity), + opacity); +} diff --git a/libraries/render-utils/src/forward_model_unlit.slf b/libraries/render-utils/src/forward_model_unlit.slf index 750b51fe8c..fb760467c9 100644 --- a/libraries/render-utils/src/forward_model_unlit.slf +++ b/libraries/render-utils/src/forward_model_unlit.slf @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// material_opaque_unlit.frag +// forward_model_unlit.frag // fragment shader // // Created by Sam Gateau on 5/5/2016. @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> +<@include ForwardBufferWrite.slh@> <@include LightingModel.slh@> <@include model/Material.slh@> @@ -38,7 +38,7 @@ void main(void) { <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; albedo *= _color; - packDeferredFragmentUnlit( + packForwardFragmentUnlit( normalize(_normal), opacity, albedo * isUnlitEnabled()); diff --git a/libraries/render-utils/src/glowLine.slf b/libraries/render-utils/src/glowLine.slf index 580a49dd3e..73e14bd319 100644 --- a/libraries/render-utils/src/glowLine.slf +++ b/libraries/render-utils/src/glowLine.slf @@ -21,7 +21,7 @@ void main(void) { float alpha = 1.0 - abs(distanceFromCenter); // Convert from a linear alpha curve to a sharp peaked one - alpha = _color.a * pow(alpha, 10); + alpha = _color.a * pow(alpha, 10.0); // Drop everything where the curve falls off to nearly nothing if (alpha <= 0.05) { diff --git a/libraries/render-utils/src/lightClusters_drawClusterContent.slv b/libraries/render-utils/src/lightClusters_drawClusterContent.slv index c570957dfb..b88e2e9ee2 100644 --- a/libraries/render-utils/src/lightClusters_drawClusterContent.slv +++ b/libraries/render-utils/src/lightClusters_drawClusterContent.slv @@ -53,12 +53,12 @@ void main(void) { - ivec3 cluster = clusterGrid_getCluster(gpu_InstanceID); + ivec3 cluster = clusterGrid_getCluster(gpu_InstanceID()); int numLights = cluster.x + cluster.y; float numLightsScale = clamp(numLights * 0.1, 0.0, 1.0); - ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID); + ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID()); float boxScale = 0.99; vec3 eyePos = frustumGrid_clusterPosToEye(clusterPos, vec3((1.0 - boxScale) * 0.5 + (1.0 - numLightsScale) * boxScale * 0.5) + numLightsScale * boxScale * pos.xyz); @@ -69,5 +69,5 @@ void main(void) { TransformCamera cam = getTransformCamera(); <$transformWorldToClipPos(cam, worldPos, gl_Position)$> - varColor = vec4(colorWheel(fract(float(gpu_InstanceID) / float(frustumGrid_numClusters()))), (numLights >0 ? 0.9 : 0.1)); + varColor = vec4(colorWheel(fract(float(gpu_InstanceID()) / float(frustumGrid_numClusters()))), (numLights >0 ? 0.9 : 0.1)); } \ No newline at end of file diff --git a/libraries/render-utils/src/lightClusters_drawClusterFromDepth.slv b/libraries/render-utils/src/lightClusters_drawClusterFromDepth.slv index 5840f11d0a..912c39f93c 100644 --- a/libraries/render-utils/src/lightClusters_drawClusterFromDepth.slv +++ b/libraries/render-utils/src/lightClusters_drawClusterFromDepth.slv @@ -53,7 +53,7 @@ void main(void) { ); vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; - ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID); + ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID()); vec3 eyePos = frustumGrid_clusterPosToEye(clusterPos, vec3(0.05) + 0.9 * pos.xyz); vec4 worldPos = frustumGrid_eyeToWorld(vec4(eyePos.xyz, 1.0)); @@ -62,5 +62,5 @@ void main(void) { TransformCamera cam = getTransformCamera(); <$transformWorldToClipPos(cam, worldPos, gl_Position)$> - varColor = vec4(colorWheel(fract(float(gpu_InstanceID) / float(frustumGrid_numClusters()))), 0.9); + varColor = vec4(colorWheel(fract(float(gpu_InstanceID()) / float(frustumGrid_numClusters()))), 0.9); } \ No newline at end of file diff --git a/libraries/render-utils/src/lightClusters_drawGrid.slv b/libraries/render-utils/src/lightClusters_drawGrid.slv index 1475666ec6..aac7fe59a5 100644 --- a/libraries/render-utils/src/lightClusters_drawGrid.slv +++ b/libraries/render-utils/src/lightClusters_drawGrid.slv @@ -54,10 +54,10 @@ void main(void) { vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; - ivec3 cluster = clusterGrid_getCluster(gpu_InstanceID); + ivec3 cluster = clusterGrid_getCluster(gpu_InstanceID()); int numLights = cluster.x + cluster.y; - ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID); + ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID()); float boxScale = 1.0; @@ -69,5 +69,5 @@ void main(void) { TransformCamera cam = getTransformCamera(); <$transformWorldToClipPos(cam, worldPos, gl_Position)$> - varColor = vec4(colorWheel(fract(float(gpu_InstanceID) / float(frustumGrid_numClusters()))), (numLights > 0 ? 0.9 : 0.0)); + varColor = vec4(colorWheel(fract(float(gpu_InstanceID()) / float(frustumGrid_numClusters()))), (numLights > 0 ? 0.9 : 0.0)); } \ No newline at end of file diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf index f578895c85..8863e15b00 100644 --- a/libraries/render-utils/src/sdf_text3D.slf +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -1,7 +1,7 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> -// sdf_text.frag +// sdf_text3D.frag // fragment shader // // Created by Bradley Austin Davis on 2015-02-04 @@ -38,18 +38,21 @@ void main() { // perform adaptive anti-aliasing of the edges // The larger we're rendering, the less anti-aliasing we need float s = smoothing * length(fwidth(_texCoord0)); - float w = clamp( s, 0.0, 0.5); + float w = clamp(s, 0.0, 0.5); float a = smoothstep(0.5 - w, 0.5 + w, sdf); - // discard if unvisible + // discard if invisible if (a < 0.01) { discard; } - - packDeferredFragmentTranslucent( + + packDeferredFragment( normalize(_normal), a * Color.a, Color.rgb, - DEFAULT_FRESNEL, - DEFAULT_ROUGHNESS); + DEFAULT_ROUGHNESS, + DEFAULT_METALLIC, + DEFAULT_EMISSIVE, + DEFAULT_OCCLUSION, + DEFAULT_SCATTERING); } \ No newline at end of file diff --git a/libraries/render-utils/src/sdf_text3D.slv b/libraries/render-utils/src/sdf_text3D.slv index 29bc1a9e85..d9f6e0cdda 100644 --- a/libraries/render-utils/src/sdf_text3D.slv +++ b/libraries/render-utils/src/sdf_text3D.slv @@ -1,7 +1,7 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> -// sdf_text.vert +// sdf_text3D.vert // vertex shader // // Created by Brad Davis on 10/14/13. @@ -27,5 +27,6 @@ void main() { TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> - <$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal.xyz)$> + const vec3 normal = vec3(0, 0, 1); + <$transformModelToWorldDir(cam, obj, normal, _normal)$> } \ No newline at end of file diff --git a/libraries/render-utils/src/sdf_text3D_overlay.slf b/libraries/render-utils/src/sdf_text3D_transparent.slf similarity index 80% rename from libraries/render-utils/src/sdf_text3D_overlay.slf rename to libraries/render-utils/src/sdf_text3D_transparent.slf index d357b05e14..fc78ab3a37 100644 --- a/libraries/render-utils/src/sdf_text3D_overlay.slf +++ b/libraries/render-utils/src/sdf_text3D_transparent.slf @@ -1,7 +1,7 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> -// sdf_text.frag +// sdf_text3D_transparent.frag // fragment shader // // Created by Bradley Austin Davis on 2015-02-04 @@ -10,6 +10,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +<@include DeferredBufferWrite.slh@> + uniform sampler2D Font; uniform bool Outline; uniform vec4 Color; @@ -18,8 +20,6 @@ uniform vec4 Color; in vec3 _normal; in vec2 _texCoord0; -layout(location = 0) out vec4 _fragColor0; - const float gamma = 2.2; const float smoothing = 32.0; const float interiorCutoff = 0.8; @@ -38,15 +38,18 @@ void main() { // perform adaptive anti-aliasing of the edges // The larger we're rendering, the less anti-aliasing we need float s = smoothing * length(fwidth(_texCoord0)); - float w = clamp( s, 0.0, 0.5); + float w = clamp(s, 0.0, 0.5); float a = smoothstep(0.5 - w, 0.5 + w, sdf); - // gamma correction for linear attenuation - a = pow(a, 1.0 / gamma); - - // discard if unvisible + // discard if invisible if (a < 0.01) { discard; } - _fragColor0 = vec4(Color.rgb, a); + + packDeferredFragmentTranslucent( + normalize(_normal), + a * Color.a, + Color.rgb, + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); } \ No newline at end of file diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 0dd10b8e1e..fd45c6c134 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -72,7 +72,7 @@ void main(void) { normal, 1.0, diffuse, - max(0, 1.0 - shininess / 128.0), + max(0.0, 1.0 - shininess / 128.0), DEFAULT_METALLIC, specular, vec3(clamp(emissiveAmount, 0.0, 1.0))); @@ -81,7 +81,7 @@ void main(void) { normal, 1.0, diffuse, - max(0, 1.0 - shininess / 128.0), + max(0.0, 1.0 - shininess / 128.0), length(specular), DEFAULT_EMISSIVE, DEFAULT_OCCLUSION, diff --git a/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf b/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf index e96ac60b45..ecbc60b648 100644 --- a/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf +++ b/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf @@ -123,7 +123,7 @@ void main(void) { // Calculate dF/du and dF/dv vec2 viewportScale = perspectiveScale * getInvWidthHeight(); - vec2 du = vec2( viewportScale.x * (stereoSide.w > 0.0 ? 0.5 : 1.0), 0.0f ); + vec2 du = vec2( viewportScale.x * (float(stereoSide.w) > 0.0 ? 0.5 : 1.0), 0.0f ); vec2 dv = vec2( 0.0f, viewportScale.y ); vec4 dFdu = vec4(getWorldNormalDiff(frameTexcoordPos, du), getEyeDepthDiff(frameTexcoordPos, du)); diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index f51a779066..8449c58c7c 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -9,10 +9,11 @@ #include "sdf_text3D_vert.h" #include "sdf_text3D_frag.h" -#include "sdf_text3D_overlay_frag.h" +#include "sdf_text3D_transparent_frag.h" #include "../RenderUtilsLogging.h" #include "FontFamilies.h" +#include "../StencilMaskPass.h" static std::mutex fontMutex; @@ -224,13 +225,13 @@ void Font::setupGPU() { { auto vertexShader = gpu::Shader::createVertex(std::string(sdf_text3D_vert)); auto pixelShader = gpu::Shader::createPixel(std::string(sdf_text3D_frag)); - auto pixelShaderOverlay = gpu::Shader::createPixel(std::string(sdf_text3D_overlay_frag)); + auto pixelShaderTransparent = gpu::Shader::createPixel(std::string(sdf_text3D_transparent_frag)); gpu::ShaderPointer program = gpu::Shader::createProgram(vertexShader, pixelShader); - gpu::ShaderPointer programOverlay = gpu::Shader::createProgram(vertexShader, pixelShaderOverlay); + gpu::ShaderPointer programTransparent = gpu::Shader::createProgram(vertexShader, pixelShaderTransparent); gpu::Shader::BindingSet slotBindings; gpu::Shader::makeProgram(*program, slotBindings); - gpu::Shader::makeProgram(*programOverlay, slotBindings); + gpu::Shader::makeProgram(*programTransparent, slotBindings); _fontLoc = program->getTextures().findLocation("Font"); _outlineLoc = program->getUniforms().findLocation("Outline"); @@ -239,15 +240,20 @@ void Font::setupGPU() { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setBlendFunction(true, + state->setBlendFunction(false, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + PrepareStencil::testMaskDrawShapeNoAA(*state); _pipeline = gpu::Pipeline::create(program, state); - auto layeredState = std::make_shared(); - layeredState->setCullMode(gpu::State::CULL_BACK); - layeredState->setDepthTest(true, true, gpu::LESS_EQUAL); - _layeredPipeline = gpu::Pipeline::create(programOverlay, layeredState); + auto transparentState = std::make_shared(); + transparentState->setCullMode(gpu::State::CULL_BACK); + transparentState->setDepthTest(true, true, gpu::LESS_EQUAL); + transparentState->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + PrepareStencil::testMaskDrawShapeNoAA(*transparentState); + _transparentPipeline = gpu::Pipeline::create(programTransparent, transparentState); } // Sanity checks @@ -361,7 +367,7 @@ void Font::drawString(gpu::Batch& batch, float x, float y, const QString& str, c setupGPU(); - batch.setPipeline(layered ? _layeredPipeline : _pipeline); + batch.setPipeline(((*color).a < 1.0f || layered) ? _transparentPipeline : _pipeline); batch.setResourceTexture(_fontLoc, _texture); batch._glUniform1i(_outlineLoc, (effectType == OUTLINE_EFFECT)); diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index 2b61f19492..a41f720f15 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -63,7 +63,7 @@ private: // gpu structures gpu::PipelinePointer _pipeline; - gpu::PipelinePointer _layeredPipeline; + gpu::PipelinePointer _transparentPipeline; gpu::TexturePointer _texture; gpu::Stream::FormatPointer _format; gpu::BufferPointer _verticesBuffer; diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 561dff4290..8fd05bd320 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -2,8 +2,7 @@ set(TARGET_NAME render) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() -link_hifi_libraries(shared ktx gpu model) # render needs octree only for getAccuracyAngle(float, int) -include_hifi_library_headers(octree) +link_hifi_libraries(shared ktx gpu model octree) target_nsight() diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 240693b422..1650d09c5d 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -16,7 +16,7 @@ #include "Scene.h" #include "../task/Task.h" -#include "gpu/Batch.h" +#include namespace render { diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 21638fbb70..96faf9719e 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -25,7 +25,7 @@ #include "Args.h" -#include "model/Material.h" +#include #include "ShapePipeline.h" namespace render { diff --git a/libraries/render/src/render/TransitionStage.h b/libraries/render/src/render/TransitionStage.h index 8dfef1b78e..226d531d8b 100644 --- a/libraries/render/src/render/TransitionStage.h +++ b/libraries/render/src/render/TransitionStage.h @@ -18,19 +18,19 @@ namespace render { - // Transition stage to set up Transition-related effects - class TransitionStage : public render::Stage { - public: + // Transition stage to set up Transition-related effects + class TransitionStage : public render::Stage { + public: static const std::string& getName() { return _name; } - using Index = indexed_container::Index; - static const Index INVALID_INDEX{ indexed_container::INVALID_INDEX }; + using Index = indexed_container::Index; + static const Index INVALID_INDEX{ indexed_container::INVALID_INDEX }; using TransitionIdList = indexed_container::Indices; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } - bool checkTransitionId(Index index) const { return _transitions.checkIndex(index); } + bool checkTransitionId(Index index) const { return _transitions.checkIndex(index); } const Transition& getTransition(Index TransitionId) const { return _transitions.get(TransitionId); } @@ -48,10 +48,10 @@ namespace render { static std::string _name; - Transitions _transitions; + Transitions _transitions; TransitionIdList _activeTransitionIds; - }; - using TransitionStagePointer = std::shared_ptr; + }; + using TransitionStagePointer = std::shared_ptr; class TransitionStageSetup { public: diff --git a/libraries/render/src/render/drawItemBounds.slf b/libraries/render/src/render/drawItemBounds.slf index 4fb23df8f6..e01d1607bd 100644 --- a/libraries/render/src/render/drawItemBounds.slf +++ b/libraries/render/src/render/drawItemBounds.slf @@ -19,7 +19,7 @@ out vec4 outFragColor; void main(void) { float var = step(fract(varTexcoord.x * varTexcoord.y * 1.0), 0.5); - if (varColor.a == 0) { + if (varColor.a == 0.0) { outFragColor = vec4(mix(vec3(0.0), varColor.xyz, var), mix(0.0, 1.0, var)); } else { diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 6dc3188b3f..fef11c12e9 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -33,7 +33,7 @@ bool UsersScriptingInterface::getIgnoreStatus(const QUuid& nodeID) { void UsersScriptingInterface::personalMute(const QUuid& nodeID, bool muteEnabled) { // ask the NodeList to mute the user with the given session ID - // "Personal Mute" only applies one way and is not global + // "Personal Mute" only applies one way and is not global DependencyManager::get()->personalMuteNodeBySessionID(nodeID, muteEnabled); } diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index a156f56d96..c21f96cd47 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -90,6 +90,6 @@ glm::vec3 Vec3::fromPolar(float elevation, float azimuth) { } float Vec3::getAngle(const glm::vec3& v1, const glm::vec3& v2) { - return glm::acos(glm::dot(glm::normalize(v1), glm::normalize(v2))); + return glm::acos(glm::dot(glm::normalize(v1), glm::normalize(v2))); } diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 19148b26e9..76faaab415 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -25,7 +25,7 @@ WebSocketClass::WebSocketClass(QScriptEngine* engine, QString url) : WebSocketClass::WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket) : _webSocket(qWebSocket), - _engine(engine) + _engine(engine) { initialize(); } diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 1fefda06b3..f9b835df5c 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -3,8 +3,6 @@ set(TARGET_NAME shared) # TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp) setup_hifi_library(Gui Network Script Widgets) -target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes") - if (WIN32) target_link_libraries(${TARGET_NAME} Wbemuuid.lib) endif() diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index 7a453e63c3..e6fc7ce96b 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -12,6 +12,7 @@ #ifndef hifi_DependencyManager_h #define hifi_DependencyManager_h +#include #include #include #include @@ -64,6 +65,15 @@ public: template static void registerInheritance(); + template + static size_t typeHash() { +#ifdef Q_OS_ANDROID + size_t hashCode = std::hash{}( typeid(T).name() ); +#else + size_t hashCode = typeid(T).hash_code(); +#endif + return hashCode; + } private: static DependencyManager& manager(); @@ -134,14 +144,14 @@ void DependencyManager::destroy() { template void DependencyManager::registerInheritance() { - size_t baseHashCode = typeid(Base).hash_code(); - size_t derivedHashCode = typeid(Derived).hash_code(); + size_t baseHashCode = typeHash(); + size_t derivedHashCode = typeHash(); manager()._inheritanceHash.insert(baseHashCode, derivedHashCode); } template size_t DependencyManager::getHashCode() { - size_t hashCode = typeid(T).hash_code(); + size_t hashCode = typeHash(); auto derivedHashCode = _inheritanceHash.find(hashCode); while (derivedHashCode != _inheritanceHash.end()) { diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 394517aac4..39fec45d90 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -473,6 +473,14 @@ glm::mat4 createMatFromScaleQuatAndPos(const glm::vec3& scale, const glm::quat& glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f)); } +glm::mat4 createMatFromScale(const glm::vec3& scale) { + glm::vec3 xAxis = glm::vec3(scale.x, 0.0f, 0.0f); + glm::vec3 yAxis = glm::vec3(0.0f, scale.y, 0.0f); + glm::vec3 zAxis = glm::vec3(0.0f, 0.0f, scale.z); + return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), + glm::vec4(zAxis, 0.0f), glm::vec4(Vectors::ZERO, 1.0f)); +} + // cancel out roll glm::quat cancelOutRoll(const glm::quat& q) { glm::vec3 forward = q * Vectors::FRONT; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 3386ea2c22..7248f4cb46 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -231,6 +231,7 @@ glm::tvec4 lerp(const glm::tvec4& x, const glm::tvec4& y, T a) glm::mat4 createMatFromQuatAndPos(const glm::quat& q, const glm::vec3& p); glm::mat4 createMatFromScaleQuatAndPos(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans); +glm::mat4 createMatFromScale(const glm::vec3& scale); glm::quat cancelOutRoll(const glm::quat& q); glm::quat cancelOutRollAndPitch(const glm::quat& q); glm::mat4 cancelOutRollAndPitch(const glm::mat4& m); diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 20e30e15e8..46613f1283 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "PathUtils.h" + #include #include #include @@ -16,7 +18,6 @@ #include #include #include -#include "PathUtils.h" #include #include // std::once #include "shared/GlobalAppProperties.h" @@ -105,18 +106,17 @@ QUrl PathUtils::defaultScriptsLocation(const QString& newDefaultPath) { if (!overriddenDefaultScriptsLocation.isEmpty()) { path = overriddenDefaultScriptsLocation; } else { -#ifdef Q_OS_WIN - path = QCoreApplication::applicationDirPath() + "/scripts"; -#elif defined(Q_OS_OSX) +#if defined(Q_OS_OSX) path = QCoreApplication::applicationDirPath() + "/../Resources/scripts"; +#elif defined(Q_OS_ANDROID) + path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/scripts"; #else path = QCoreApplication::applicationDirPath() + "/scripts"; #endif } // turn the string into a legit QUrl - QFileInfo fileInfo(path); - return QUrl::fromLocalFile(fileInfo.canonicalFilePath()); + return QUrl::fromLocalFile(QFileInfo(path).canonicalFilePath()); } QString PathUtils::stripFilename(const QUrl& url) { diff --git a/libraries/shared/src/Preferences.h b/libraries/shared/src/Preferences.h index 6fa2cb9b1f..a243a6d58d 100644 --- a/libraries/shared/src/Preferences.h +++ b/libraries/shared/src/Preferences.h @@ -257,12 +257,12 @@ public: }; class SpinnerSliderPreference : public FloatPreference { - Q_OBJECT + Q_OBJECT public: - SpinnerSliderPreference(const QString& category, const QString& name, Getter getter, Setter setter) - : FloatPreference(category, name, getter, setter) { } + SpinnerSliderPreference(const QString& category, const QString& name, Getter getter, Setter setter) + : FloatPreference(category, name, getter, setter) { } - Type getType() override { return SpinnerSlider; } + Type getType() override { return SpinnerSlider; } }; class IntSpinnerPreference : public IntPreference { diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 8257c883a2..7d0df3ac78 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -762,6 +762,8 @@ QScriptValue rayPickResultToScriptValue(QScriptEngine* engine, const RayPickResu QScriptValue intersection = vec3toScriptValue(engine, rayPickResult.intersection); obj.setProperty("intersection", intersection); obj.setProperty("intersects", rayPickResult.type != NONE); + QScriptValue searchRay = pickRayToScriptValue(engine, rayPickResult.searchRay); + obj.setProperty("searchRay", searchRay); QScriptValue surfaceNormal = vec3toScriptValue(engine, rayPickResult.surfaceNormal); obj.setProperty("surfaceNormal", surfaceNormal); return obj; diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index ed928a6e7b..7b7d8d8f47 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -137,7 +137,7 @@ QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay) void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay); enum IntersectionType { - NONE, + NONE = 0, ENTITY, OVERLAY, AVATAR, @@ -147,12 +147,14 @@ enum IntersectionType { class RayPickResult { public: RayPickResult() {} - RayPickResult(const IntersectionType type, const QUuid& objectID, const float distance, const glm::vec3& intersection, const glm::vec3& surfaceNormal = glm::vec3(NAN)) : - type(type), objectID(objectID), distance(distance), intersection(intersection), surfaceNormal(surfaceNormal) {} + RayPickResult(const PickRay& searchRay) : searchRay(searchRay) {} + RayPickResult(const IntersectionType type, const QUuid& objectID, const float distance, const glm::vec3& intersection, const PickRay& searchRay, const glm::vec3& surfaceNormal = glm::vec3(NAN)) : + type(type), objectID(objectID), distance(distance), intersection(intersection), searchRay(searchRay), surfaceNormal(surfaceNormal) {} IntersectionType type { NONE }; - QUuid objectID { 0 }; + QUuid objectID; float distance { FLT_MAX }; glm::vec3 intersection { NAN }; + PickRay searchRay; glm::vec3 surfaceNormal { NAN }; }; Q_DECLARE_METATYPE(RayPickResult) diff --git a/libraries/shared/src/SettingHandle.h b/libraries/shared/src/SettingHandle.h index 341a4cb101..e77ee00b05 100644 --- a/libraries/shared/src/SettingHandle.h +++ b/libraries/shared/src/SettingHandle.h @@ -39,7 +39,7 @@ public: QStringList childKeys() const; QStringList allKeys() const; bool contains(const QString& key) const; - int beginReadArray(const QString & prefix); + int beginReadArray(const QString & prefix); void beginWriteArray(const QString& prefix, int size = -1); void endArray(); void setArrayIndex(int i); diff --git a/libraries/shared/src/shared/PlatformHacks.h b/libraries/shared/src/shared/PlatformHacks.h new file mode 100644 index 0000000000..909f0bac9a --- /dev/null +++ b/libraries/shared/src/shared/PlatformHacks.h @@ -0,0 +1,18 @@ +// +// Created by Bradley Austin Davis on 2017/09/15 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once +#ifndef hifi_shared_PlatformHacks_h +#define hifi_shared_PlatformHacks_h + +#include + +#if defined(Q_OS_ANDROID) +#include "platform/AndroidHacks.h" +#endif + +#endif diff --git a/libraries/shared/src/shared/platform/AndroidHacks.h b/libraries/shared/src/shared/platform/AndroidHacks.h new file mode 100644 index 0000000000..29ab013f98 --- /dev/null +++ b/libraries/shared/src/shared/platform/AndroidHacks.h @@ -0,0 +1,50 @@ +// +// androidhacks.h +// interface/src +// +// Created by Cristian Duarte & Gabriel Calero on 1/4/17. +// Copyright 2017 High Fidelity, Inc. +// +// hacks to get android to compile +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#pragma once +#ifndef hifi_shared_platform_androidhacks_h +#define hifi_shared_platform_androidhacks_h + +#include +#include + +#include + +// Only for gnu stl, so checking if using llvm +// (If there is a better check than this http://stackoverflow.com/questions/31657499/how-to-detect-stdlib-libc-in-the-preprocessor, improve this one) +#if (_LIBCPP_VERSION) + // NOTHING, these functions are well defined on libc++ +#else + +using namespace std; +namespace std +{ + // to_string impl + // error: no member named 'to_string' in namespace 'std' + // http://stackoverflow.com/questions/26095886/error-to-string-is-not-a-member-of-std + template + inline std::string to_string(T value) { + std::ostringstream os; + os << value; + return os.str(); + } + + inline float stof(std::string str) { + return atof(str.c_str()); + } +} + +#endif // _LIBCPP_VERSION + +#endif // hifi_androidhacks_h diff --git a/libraries/trackers/src/trackers/EyeTracker.cpp b/libraries/trackers/src/trackers/EyeTracker.cpp index 8733461dbb..e641abc630 100644 --- a/libraries/trackers/src/trackers/EyeTracker.cpp +++ b/libraries/trackers/src/trackers/EyeTracker.cpp @@ -136,7 +136,7 @@ void EyeTracker::onStreamStarted() { qCWarning(interfaceapp) << "Eye Tracker: Error starting streaming:" << smiReturnValueToString(result); // Display error dialog unless SMI SDK has already displayed an error message. if (result != SMI_ERROR_HMD_NOT_SUPPORTED) { - OffscreenUi::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); + OffscreenUi::asyncWarning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); } } else { qCDebug(interfaceapp) << "Eye Tracker: Started streaming"; @@ -149,7 +149,7 @@ void EyeTracker::onStreamStarted() { result = smi_loadCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); if (result != SMI_RET_SUCCESS) { qCWarning(interfaceapp) << "Eye Tracker: Error loading calibration:" << smiReturnValueToString(result); - OffscreenUi::warning(nullptr, "Eye Tracker Error", "Error loading calibration" + OffscreenUi::asyncWarning(nullptr, "Eye Tracker Error", "Error loading calibration" + smiReturnValueToString(result)); } else { qCDebug(interfaceapp) << "Eye Tracker: Loaded calibration"; @@ -165,7 +165,7 @@ void EyeTracker::setEnabled(bool enabled, bool simulate) { int result = smi_setCallback(eyeTrackerCallback); if (result != SMI_RET_SUCCESS) { qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << smiReturnValueToString(result); - OffscreenUi::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); + OffscreenUi::asyncWarning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); } else { _isInitialized = true; } @@ -270,7 +270,7 @@ void EyeTracker::calibrate(int points) { } if (result != SMI_RET_SUCCESS) { - OffscreenUi::warning(nullptr, "Eye Tracker Error", "Calibration error: " + smiReturnValueToString(result)); + OffscreenUi::asyncWarning(nullptr, "Eye Tracker Error", "Calibration error: " + smiReturnValueToString(result)); } } #endif diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index 68a6fd25b9..f28157ff97 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -2,7 +2,5 @@ set(TARGET_NAME ui) setup_hifi_library(OpenGL Network Qml Quick Script WebChannel WebEngine WebSockets XmlPatterns) link_hifi_libraries(shared networking gl audio) -if (NOT ANDROID) - # Required for some low level GL interaction in the OffscreenQMLSurface - target_glew() -endif () +# Required for some low level GL interaction in the OffscreenQMLSurface +target_opengl() diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index b4a07ecd47..9dfe831081 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -91,6 +91,13 @@ QObject* OffscreenUi::getFlags() { return offscreenFlags; } +void OffscreenUi::removeModalDialog(QObject* modal) { + if (modal) { + _modalDialogListeners.removeOne(modal); + modal->deleteLater(); + } +} + void OffscreenUi::create() { OffscreenQmlSurface::create(); auto myContext = getSurfaceContext(); @@ -145,45 +152,6 @@ bool OffscreenUi::isVisible(const QString& name) { } } -class ModalDialogListener : public QObject { - Q_OBJECT - friend class OffscreenUi; - -protected: - ModalDialogListener(QQuickItem* dialog) : _dialog(dialog) { - if (!dialog) { - _finished = true; - return; - } - connect(_dialog, SIGNAL(destroyed()), this, SLOT(onDestroyed())); - } - - ~ModalDialogListener() { - if (_dialog) { - disconnect(_dialog); - } - } - - virtual QVariant waitForResult() { - while (!_finished) { - QCoreApplication::processEvents(); - } - return _result; - } - -protected slots: - void onDestroyed() { - _finished = true; - disconnect(_dialog); - _dialog = nullptr; - } - -protected: - QQuickItem* _dialog; - bool _finished { false }; - QVariant _result; -}; - class MessageBoxListener : public ModalDialogListener { Q_OBJECT @@ -204,6 +172,9 @@ private slots: void onSelected(int button) { _result = button; _finished = true; + auto offscreenUi = DependencyManager::get(); + emit response(_result); + offscreenUi->removeModalDialog(qobject_cast(this)); disconnect(_dialog); } }; @@ -263,6 +234,25 @@ QMessageBox::StandardButton OffscreenUi::messageBox(Icon icon, const QString& ti return static_cast(waitForMessageBoxResult(createMessageBox(icon, title, text, buttons, defaultButton))); } +ModalDialogListener* OffscreenUi::asyncMessageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { + if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; + BLOCKING_INVOKE_METHOD(this, "asyncMessageBox", + Q_RETURN_ARG(ModalDialogListener*, ret), + Q_ARG(Icon, icon), + Q_ARG(QString, title), + Q_ARG(QString, text), + Q_ARG(QMessageBox::StandardButtons, buttons), + Q_ARG(QMessageBox::StandardButton, defaultButton)); + return ret; + } + + MessageBoxListener* messageBoxListener = new MessageBoxListener(createMessageBox(icon, title, text, buttons, defaultButton)); + QObject* modalDialog = qobject_cast(messageBoxListener); + _modalDialogListeners.push_back(modalDialog); + return messageBoxListener; +} + QMessageBox::StandardButton OffscreenUi::critical(const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return DependencyManager::get()->messageBox(OffscreenUi::Icon::ICON_CRITICAL, title, text, buttons, defaultButton); @@ -271,15 +261,36 @@ QMessageBox::StandardButton OffscreenUi::information(const QString& title, const QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return DependencyManager::get()->messageBox(OffscreenUi::Icon::ICON_INFORMATION, title, text, buttons, defaultButton); } + +ModalDialogListener* OffscreenUi::asyncCritical(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { + return DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_CRITICAL, title, text, buttons, defaultButton); +} + +ModalDialogListener* OffscreenUi::asyncInformation(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { + return DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_INFORMATION, title, text, buttons, defaultButton); +} + QMessageBox::StandardButton OffscreenUi::question(const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return DependencyManager::get()->messageBox(OffscreenUi::Icon::ICON_QUESTION, title, text, buttons, defaultButton); } + +ModalDialogListener *OffscreenUi::asyncQuestion(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { + return DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_QUESTION, title, text, buttons, defaultButton); +} + QMessageBox::StandardButton OffscreenUi::warning(const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return DependencyManager::get()->messageBox(OffscreenUi::Icon::ICON_WARNING, title, text, buttons, defaultButton); } +ModalDialogListener* OffscreenUi::asyncWarning(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { + return DependencyManager::get()->asyncMessageBox(OffscreenUi::Icon::ICON_WARNING, title, text, buttons, defaultButton); +} class InputDialogListener : public ModalDialogListener { @@ -296,6 +307,9 @@ class InputDialogListener : public ModalDialogListener { private slots: void onSelected(const QVariant& result) { _result = result; + auto offscreenUi = DependencyManager::get(); + emit response(_result); + offscreenUi->removeModalDialog(qobject_cast(this)); _finished = true; disconnect(_dialog); } @@ -349,6 +363,31 @@ QVariant OffscreenUi::getCustomInfo(const Icon icon, const QString& title, const return result; } +ModalDialogListener* OffscreenUi::getTextAsync(const Icon icon, const QString& title, const QString& label, const QString& text) { + return DependencyManager::get()->inputDialogAsync(icon, title, label, text); +} + +ModalDialogListener* OffscreenUi::getItemAsync(const Icon icon, const QString& title, const QString& label, const QStringList& items, + int current, bool editable) { + + auto offscreenUi = DependencyManager::get(); + auto inputDialog = offscreenUi->createInputDialog(icon, title, label, current); + if (!inputDialog) { + return nullptr; + } + inputDialog->setProperty("items", items); + inputDialog->setProperty("editable", editable); + + InputDialogListener* inputDialogListener = new InputDialogListener(inputDialog); + offscreenUi->getModalDialogListeners().push_back(qobject_cast(inputDialogListener)); + + return inputDialogListener; +} + +ModalDialogListener* OffscreenUi::getCustomInfoAsync(const Icon icon, const QString& title, const QVariantMap& config) { + return DependencyManager::get()->customInputDialogAsync(icon, title, config); +} + QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current) { if (QThread::currentThread() != thread()) { QVariant result; @@ -364,6 +403,24 @@ QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const Q return waitForInputDialogResult(createInputDialog(icon, title, label, current)); } +ModalDialogListener* OffscreenUi::inputDialogAsync(const Icon icon, const QString& title, const QString& label, const QVariant& current) { + if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; + BLOCKING_INVOKE_METHOD(this, "inputDialogAsync", + Q_RETURN_ARG(ModalDialogListener*, ret), + Q_ARG(Icon, icon), + Q_ARG(QString, title), + Q_ARG(QString, label), + Q_ARG(QVariant, current)); + return ret; + } + + InputDialogListener* inputDialogListener = new InputDialogListener(createInputDialog(icon, title, label, current)); + QObject* inputDialog = qobject_cast(inputDialogListener); + _modalDialogListeners.push_back(inputDialog); + return inputDialogListener; +} + QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, const QVariantMap& config) { if (QThread::currentThread() != thread()) { QVariant result; @@ -384,6 +441,23 @@ QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, c return result; } +ModalDialogListener* OffscreenUi::customInputDialogAsync(const Icon icon, const QString& title, const QVariantMap& config) { + if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; + BLOCKING_INVOKE_METHOD(this, "customInputDialogAsync", + Q_RETURN_ARG(ModalDialogListener*, ret), + Q_ARG(Icon, icon), + Q_ARG(QString, title), + Q_ARG(QVariantMap, config)); + return ret; + } + + InputDialogListener* inputDialogListener = new InputDialogListener(createCustomInputDialog(icon, title, config)); + QObject* inputDialog = qobject_cast(inputDialogListener); + _modalDialogListeners.push_back(inputDialog); + return inputDialogListener; +} + void OffscreenUi::togglePinned() { bool invokeResult = QMetaObject::invokeMethod(_desktop, "togglePinned"); if (!invokeResult) { @@ -555,6 +629,7 @@ void OffscreenUi::createDesktop(const QUrl& url) { #endif load(url, [=](QQmlContext* context, QObject* newObject) { + Q_UNUSED(context) _desktop = static_cast(newObject); getSurfaceContext()->setContextProperty("desktop", _desktop); _toolWindow = _desktop->findChild("ToolWindow"); @@ -600,8 +675,11 @@ class FileDialogListener : public ModalDialogListener { private slots: void onSelectedFile(QVariant file) { - _result = file; + _result = file.toUrl().toLocalFile(); _finished = true; + auto offscreenUi = DependencyManager::get(); + emit response(_result); + offscreenUi->removeModalDialog(qobject_cast(this)); disconnect(_dialog); } }; @@ -637,6 +715,35 @@ QString OffscreenUi::fileDialog(const QVariantMap& properties) { return result.toUrl().toLocalFile(); } +ModalDialogListener* OffscreenUi::fileDialogAsync(const QVariantMap& properties) { + QVariant buildDialogResult; + bool invokeResult; + auto tabletScriptingInterface = DependencyManager::get(); + TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + if (tablet->getToolbarMode()) { + invokeResult = QMetaObject::invokeMethod(_desktop, "fileDialog", + Q_RETURN_ARG(QVariant, buildDialogResult), + Q_ARG(QVariant, QVariant::fromValue(properties))); + } else { + QQuickItem* tabletRoot = tablet->getTabletRoot(); + invokeResult = QMetaObject::invokeMethod(tabletRoot, "fileDialog", + Q_RETURN_ARG(QVariant, buildDialogResult), + Q_ARG(QVariant, QVariant::fromValue(properties))); + emit tabletScriptingInterface->tabletNotification(); + } + + if (!invokeResult) { + qWarning() << "Failed to create file open dialog"; + return nullptr; + } + + FileDialogListener* fileDialogListener = new FileDialogListener(qvariant_cast(buildDialogResult)); + QObject* fileModalDialog = qobject_cast(fileDialogListener); + _modalDialogListeners.push_back(fileModalDialog); + + return fileDialogListener; +} + QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { QString result; @@ -659,6 +766,28 @@ QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, return fileDialog(map); } +ModalDialogListener* OffscreenUi::fileOpenDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { + if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; + BLOCKING_INVOKE_METHOD(this, "fileOpenDialogAsync", + Q_RETURN_ARG(ModalDialogListener*, ret), + Q_ARG(QString, caption), + Q_ARG(QString, dir), + Q_ARG(QString, filter), + Q_ARG(QString*, selectedFilter), + Q_ARG(QFileDialog::Options, options)); + return ret; + } + + // FIXME support returning the selected filter... somehow? + QVariantMap map; + map.insert("caption", caption); + map.insert("dir", QUrl::fromLocalFile(dir)); + map.insert("filter", filter); + map.insert("options", static_cast(options)); + return fileDialogAsync(map); +} + QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { QString result; @@ -683,6 +812,30 @@ QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, return fileDialog(map); } +ModalDialogListener* OffscreenUi::fileSaveDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { + if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; + BLOCKING_INVOKE_METHOD(this, "fileSaveDialogAsync", + Q_RETURN_ARG(ModalDialogListener*, ret), + Q_ARG(QString, caption), + Q_ARG(QString, dir), + Q_ARG(QString, filter), + Q_ARG(QString*, selectedFilter), + Q_ARG(QFileDialog::Options, options)); + return ret; + } + + // FIXME support returning the selected filter... somehow? + QVariantMap map; + map.insert("caption", caption); + map.insert("dir", QUrl::fromLocalFile(dir)); + map.insert("filter", filter); + map.insert("options", static_cast(options)); + map.insert("saveDialog", true); + + return fileDialogAsync(map); +} + QString OffscreenUi::existingDirectoryDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { QString result; @@ -705,18 +858,58 @@ QString OffscreenUi::existingDirectoryDialog(const QString& caption, const QStri return fileDialog(map); } +ModalDialogListener* OffscreenUi::existingDirectoryDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { + if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; + BLOCKING_INVOKE_METHOD(this, "existingDirectoryDialogAsync", + Q_RETURN_ARG(ModalDialogListener*, ret), + Q_ARG(QString, caption), + Q_ARG(QString, dir), + Q_ARG(QString, filter), + Q_ARG(QString*, selectedFilter), + Q_ARG(QFileDialog::Options, options)); + return ret; + } + + QVariantMap map; + map.insert("caption", caption); + map.insert("dir", QUrl::fromLocalFile(dir)); + map.insert("filter", filter); + map.insert("options", static_cast(options)); + map.insert("selectDirectory", true); + return fileDialogAsync(map); +} + QString OffscreenUi::getOpenFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) return DependencyManager::get()->fileOpenDialog(caption, dir, filter, selectedFilter, options); } +ModalDialogListener* OffscreenUi::getOpenFileNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) + return DependencyManager::get()->fileOpenDialogAsync(caption, dir, filter, selectedFilter, options); +} + QString OffscreenUi::getSaveFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) return DependencyManager::get()->fileSaveDialog(caption, dir, filter, selectedFilter, options); } +ModalDialogListener* OffscreenUi::getSaveFileNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) + return DependencyManager::get()->fileSaveDialogAsync(caption, dir, filter, selectedFilter, options); +} + QString OffscreenUi::getExistingDirectory(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) return DependencyManager::get()->existingDirectoryDialog(caption, dir, filter, selectedFilter, options); } +ModalDialogListener* OffscreenUi::getExistingDirectoryAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + Q_UNUSED(ignored) + return DependencyManager::get()->existingDirectoryDialogAsync(caption, dir, filter, selectedFilter, options); +} + class AssetDialogListener : public ModalDialogListener { // ATP equivalent of FileDialogListener. Q_OBJECT @@ -732,6 +925,9 @@ class AssetDialogListener : public ModalDialogListener { private slots: void onSelectedAsset(QVariant asset) { _result = asset; + auto offscreenUi = DependencyManager::get(); + emit response(_result); + offscreenUi->removeModalDialog(qobject_cast(this)); _finished = true; disconnect(_dialog); } @@ -769,6 +965,39 @@ QString OffscreenUi::assetDialog(const QVariantMap& properties) { return result.toUrl().toString(); } +ModalDialogListener *OffscreenUi::assetDialogAsync(const QVariantMap& properties) { + // ATP equivalent of fileDialog(). + QVariant buildDialogResult; + bool invokeResult; + auto tabletScriptingInterface = DependencyManager::get(); + TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + if (tablet->getToolbarMode()) { + invokeResult = QMetaObject::invokeMethod(_desktop, "assetDialog", + Q_RETURN_ARG(QVariant, buildDialogResult), + Q_ARG(QVariant, QVariant::fromValue(properties))); + } else { + QQuickItem* tabletRoot = tablet->getTabletRoot(); + invokeResult = QMetaObject::invokeMethod(tabletRoot, "assetDialog", + Q_RETURN_ARG(QVariant, buildDialogResult), + Q_ARG(QVariant, QVariant::fromValue(properties))); + emit tabletScriptingInterface->tabletNotification(); + } + + if (!invokeResult) { + qWarning() << "Failed to create asset open dialog"; + return nullptr; + } + + AssetDialogListener* assetDialogListener = new AssetDialogListener(qvariant_cast(buildDialogResult)); + QObject* assetModalDialog = qobject_cast(assetDialogListener); + _modalDialogListeners.push_back(assetModalDialog); + return assetDialogListener; +} + +QList &OffscreenUi::getModalDialogListeners() { + return _modalDialogListeners; +} + QString OffscreenUi::assetOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { // ATP equivalent of fileOpenDialog(). if (QThread::currentThread() != thread()) { @@ -792,11 +1021,41 @@ QString OffscreenUi::assetOpenDialog(const QString& caption, const QString& dir, return assetDialog(map); } +ModalDialogListener* OffscreenUi::assetOpenDialogAsync(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { + // ATP equivalent of fileOpenDialog(). + if (QThread::currentThread() != thread()) { + ModalDialogListener* ret; + BLOCKING_INVOKE_METHOD(this, "assetOpenDialogAsync", + Q_RETURN_ARG(ModalDialogListener*, ret), + Q_ARG(QString, caption), + Q_ARG(QString, dir), + Q_ARG(QString, filter), + Q_ARG(QString*, selectedFilter), + Q_ARG(QFileDialog::Options, options)); + return ret; + } + + // FIXME support returning the selected filter... somehow? + QVariantMap map; + map.insert("caption", caption); + map.insert("dir", dir); + map.insert("filter", filter); + map.insert("options", static_cast(options)); + return assetDialogAsync(map); +} + QString OffscreenUi::getOpenAssetName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { // ATP equivalent of getOpenFileName(). + Q_UNUSED(ignored) return DependencyManager::get()->assetOpenDialog(caption, dir, filter, selectedFilter, options); } +ModalDialogListener* OffscreenUi::getOpenAssetNameAsync(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + // ATP equivalent of getOpenFileName(). + Q_UNUSED(ignored) + return DependencyManager::get()->assetOpenDialogAsync(caption, dir, filter, selectedFilter, options); +} + bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { if (!filterEnabled(originalDestination, event)) { return false; @@ -836,5 +1095,31 @@ unsigned int OffscreenUi::getMenuUserDataId() const { return _vrMenu->_userDataId; } -#include "OffscreenUi.moc" +ModalDialogListener::ModalDialogListener(QQuickItem *dialog) : _dialog(dialog) { + if (!dialog) { + _finished = true; + return; + } + connect(_dialog, SIGNAL(destroyed()), this, SLOT(onDestroyed())); +} +ModalDialogListener::~ModalDialogListener() { + if (_dialog) { + disconnect(_dialog); + } +} + +QVariant ModalDialogListener::waitForResult() { + while (!_finished) { + QCoreApplication::processEvents(); + } + return _result; +} + +void ModalDialogListener::onDestroyed() { + _finished = true; + disconnect(_dialog); + _dialog = nullptr; +} + +#include "OffscreenUi.moc" diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 3032263fa5..391d7da6c7 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -30,6 +30,27 @@ class VrMenu; #define OFFSCREEN_VISIBILITY_PROPERTY "shown" +class ModalDialogListener : public QObject { + Q_OBJECT + friend class OffscreenUi; + +protected: + ModalDialogListener(QQuickItem* dialog); + virtual ~ModalDialogListener(); + virtual QVariant waitForResult(); + +signals: + void response(const QVariant& value); + +protected slots: + void onDestroyed(); + +protected: + QQuickItem* _dialog; + bool _finished { false }; + QVariant _result; +}; + class OffscreenUi : public OffscreenQmlSurface, public Dependency { Q_OBJECT @@ -71,6 +92,7 @@ public: // Message box compatibility Q_INVOKABLE QMessageBox::StandardButton messageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton); + Q_INVOKABLE ModalDialogListener* asyncMessageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton); // Must be called from the main thread QQuickItem* createMessageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton); // Must be called from the main thread @@ -88,18 +110,39 @@ public: QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return information(title, text, buttons, defaultButton); } + static ModalDialogListener* asyncCritical(void* ignored, const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { + return asyncCritical(title, text, buttons, defaultButton); + } + static ModalDialogListener* asyncInformation(void* ignored, const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { + return asyncInformation(title, text, buttons, defaultButton); + } /// Same design as QMessageBox::question(), will block, returns result static QMessageBox::StandardButton question(void* ignored, const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return question(title, text, buttons, defaultButton); } + + static ModalDialogListener* asyncQuestion(void* ignored, const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { + return asyncQuestion(title, text, buttons, defaultButton); + } /// Same design as QMessageBox::warning(), will block, returns result static QMessageBox::StandardButton warning(void* ignored, const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { return warning(title, text, buttons, defaultButton); } + static ModalDialogListener* asyncWarning(void* ignored, const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) { + return asyncWarning(title, text, buttons, defaultButton); + } static QMessageBox::StandardButton critical(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, @@ -107,30 +150,55 @@ public: static QMessageBox::StandardButton information(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + static ModalDialogListener* asyncCritical(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + static ModalDialogListener *asyncInformation(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); static QMessageBox::StandardButton question(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + static ModalDialogListener* asyncQuestion (const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); static QMessageBox::StandardButton warning(const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + static ModalDialogListener *asyncWarning(const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE ModalDialogListener* fileOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE QString fileSaveDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE ModalDialogListener* fileSaveDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE QString existingDirectoryDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE ModalDialogListener* existingDirectoryDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); Q_INVOKABLE QString assetOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE ModalDialogListener* assetOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // Compatibility with QFileDialog::getOpenFileName - static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static ModalDialogListener* getOpenFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + // Compatibility with QFileDialog::getSaveFileName static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static ModalDialogListener* getSaveFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // Compatibility with QFileDialog::getExistingDirectory static QString getExistingDirectory(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static ModalDialogListener* getExistingDirectoryAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); static QString getOpenAssetName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static ModalDialogListener* getOpenAssetNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); Q_INVOKABLE QVariant inputDialog(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant()); + Q_INVOKABLE ModalDialogListener* inputDialogAsync(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant()); Q_INVOKABLE QVariant customInputDialog(const Icon icon, const QString& title, const QVariantMap& config); + Q_INVOKABLE ModalDialogListener* customInputDialogAsync(const Icon icon, const QString& title, const QVariantMap& config); QQuickItem* createInputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current); QQuickItem* createCustomInputDialog(const Icon icon, const QString& title, const QVariantMap& config); QVariant waitForInputDialogResult(QQuickItem* inputDialog); @@ -148,21 +216,47 @@ public: return getItem(OffscreenUi::ICON_NONE, title, label, items, current, editable, ok); } + // Compatibility with QInputDialog::getText + static ModalDialogListener* getTextAsync(void* ignored, const QString & title, const QString & label, + QLineEdit::EchoMode mode = QLineEdit::Normal, const QString & text = QString(), bool * ok = 0, + Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone) { + return getTextAsync(OffscreenUi::ICON_NONE, title, label, text); + } + // Compatibility with QInputDialog::getItem + static ModalDialogListener* getItemAsync(void *ignored, const QString & title, const QString & label, const QStringList & items, + int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0, + Qt::InputMethodHints inputMethodHints = Qt::ImhNone) { + return getItemAsync(OffscreenUi::ICON_NONE, title, label, items, current, editable); + } + static QString getText(const Icon icon, const QString & title, const QString & label, const QString & text = QString(), bool * ok = 0); static QString getItem(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0); static QVariant getCustomInfo(const Icon icon, const QString& title, const QVariantMap& config, bool* ok = 0); + static ModalDialogListener* getTextAsync(const Icon icon, const QString & title, const QString & label, const QString & text = QString()); + static ModalDialogListener* getItemAsync(const Icon icon, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true); + static ModalDialogListener* getCustomInfoAsync(const Icon icon, const QString& title, const QVariantMap& config); unsigned int getMenuUserDataId() const; + QList &getModalDialogListeners(); signals: void showDesktop(); +// void response(QMessageBox::StandardButton response); +// void fileDialogResponse(QString response); +// void assetDialogResponse(QString response); +// void inputDialogResponse(QVariant response); +public slots: + void removeModalDialog(QObject* modal); private: QString fileDialog(const QVariantMap& properties); + ModalDialogListener *fileDialogAsync(const QVariantMap &properties); QString assetDialog(const QVariantMap& properties); + ModalDialogListener* assetDialogAsync(const QVariantMap& properties); QQuickItem* _desktop { nullptr }; QQuickItem* _toolWindow { nullptr }; + QList _modalDialogListeners; std::unordered_map _pressedKeys; VrMenu* _vrMenu { nullptr }; QQueue> _queuedMenuInitializers; diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index 70daff944a..12cb7e2e4b 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -170,7 +170,7 @@ void VrMenu::addMenu(QMenu* menu) { QObject* parent = menu->parent(); QObject* qmlParent = nullptr; QMenu* parentMenu = dynamic_cast(parent); - if (parentMenu) { + if (parentMenu && menu->menuAction()) { MenuUserData* userData = MenuUserData::forObject(parentMenu->menuAction()); if (!userData) { return; diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 071ccd46b1..6cf8a927ff 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -827,7 +827,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even switch (event->type()) { case QEvent::Resize: { QResizeEvent* resizeEvent = static_cast(event); - QWidget* widget = dynamic_cast(originalDestination); + QWidget* widget = static_cast(originalDestination); if (widget) { this->resize(resizeEvent->size()); } @@ -932,7 +932,7 @@ void OffscreenQmlSurface::focusDestroyed(QObject *obj) { } void OffscreenQmlSurface::onFocusObjectChanged(QObject* object) { - QQuickItem* item = dynamic_cast(object); + QQuickItem* item = static_cast(object); if (!item) { setFocusText(false); _currentFocusItem = nullptr; @@ -1019,6 +1019,10 @@ void OffscreenQmlSurface::synthesizeKeyPress(QString key, QObject* targetOverrid } void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool numeric) { +#if Q_OS_ANDROID + return; +#endif + if (!object) { return; } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 8ab03b60d0..4e625c2494 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -26,6 +26,8 @@ // FIXME move to global app properties const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system"; const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; +const QString TabletScriptingInterface::QML = "hifi/tablet/TabletRoot.qml"; + TabletScriptingInterface::TabletScriptingInterface() { } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index d3590ec62e..386bce45a8 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -42,6 +42,7 @@ class TabletScriptingInterface : public QObject, public Dependency { public: TabletScriptingInterface(); ~TabletScriptingInterface(); + static const QString QML; void setToolbarScriptingInterface(ToolbarScriptingInterface* toolbarScriptingInterface) { _toolbarScriptingInterface = toolbarScriptingInterface; } diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index 18844a1995..3d06a4b223 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -88,7 +88,7 @@ ovrSession acquireOculusSession() { } if (!session) { - ovrInitParams initParams { + ovrInitParams initParams { ovrInit_RequestVersion | ovrInit_MixedRendering, OVR_MINOR_VERSION, nullptr, 0, 0 }; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 80c8698bb6..804ff7d62e 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -181,7 +181,7 @@ bool OculusLegacyDisplayPlugin::internalActivate() { } void OculusLegacyDisplayPlugin::internalDeactivate() { - Parent::internalDeactivate(); + Parent::internalDeactivate(); ovrHmd_Destroy(_hmd); _hmd = nullptr; ovr_Shutdown(); diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 20345467df..36bdd1c792 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -17,7 +17,7 @@ const float TARGET_RATE_OculusLegacy = 75.0f; class GLWindow; class OculusLegacyDisplayPlugin : public HmdDisplayPlugin { - using Parent = HmdDisplayPlugin; + using Parent = HmdDisplayPlugin; public: OculusLegacyDisplayPlugin(); bool isSupported() const override; diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 5a1c23839e..430dc193a3 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -375,7 +375,7 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle calibrateFromHandController(inputCalibrationData); calibrateFromUI(inputCalibrationData); - updateCalibratedLimbs(); + updateCalibratedLimbs(inputCalibrationData); _lastSimPoseData = _nextSimPoseData; } @@ -676,40 +676,53 @@ void ViveControllerManager::InputDevice::uncalibrate() { _overrideHands = false; } -void ViveControllerManager::InputDevice::updateCalibratedLimbs() { - _poseStateMap[controller::LEFT_FOOT] = addOffsetToPuckPose(controller::LEFT_FOOT); - _poseStateMap[controller::RIGHT_FOOT] = addOffsetToPuckPose(controller::RIGHT_FOOT); - _poseStateMap[controller::HIPS] = addOffsetToPuckPose(controller::HIPS); - _poseStateMap[controller::SPINE2] = addOffsetToPuckPose(controller::SPINE2); - _poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(controller::RIGHT_ARM); - _poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(controller::LEFT_ARM); +void ViveControllerManager::InputDevice::updateCalibratedLimbs(const controller::InputCalibrationData& inputCalibration) { + _poseStateMap[controller::LEFT_FOOT] = addOffsetToPuckPose(inputCalibration, controller::LEFT_FOOT); + _poseStateMap[controller::RIGHT_FOOT] = addOffsetToPuckPose(inputCalibration, controller::RIGHT_FOOT); + _poseStateMap[controller::HIPS] = addOffsetToPuckPose(inputCalibration, controller::HIPS); + _poseStateMap[controller::SPINE2] = addOffsetToPuckPose(inputCalibration, controller::SPINE2); + _poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(inputCalibration, controller::RIGHT_ARM); + _poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(inputCalibration, controller::LEFT_ARM); if (_overrideHead) { - _poseStateMap[controller::HEAD] = addOffsetToPuckPose(controller::HEAD); + _poseStateMap[controller::HEAD] = addOffsetToPuckPose(inputCalibration, controller::HEAD); } if (_overrideHands) { - _poseStateMap[controller::LEFT_HAND] = addOffsetToPuckPose(controller::LEFT_HAND); - _poseStateMap[controller::RIGHT_HAND] = addOffsetToPuckPose(controller::RIGHT_HAND); + _poseStateMap[controller::LEFT_HAND] = addOffsetToPuckPose(inputCalibration, controller::LEFT_HAND); + _poseStateMap[controller::RIGHT_HAND] = addOffsetToPuckPose(inputCalibration, controller::RIGHT_HAND); } } -controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(int joint) const { +controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(const controller::InputCalibrationData& inputCalibration, int joint) const { auto puck = _jointToPuckMap.find(joint); if (puck != _jointToPuckMap.end()) { uint32_t puckIndex = puck->second; - auto puckPose = _poseStateMap.find(puckIndex); - auto puckPostOffset = _pucksPostOffset.find(puckIndex); - auto puckPreOffset = _pucksPreOffset.find(puckIndex); - if (puckPose != _poseStateMap.end()) { - if (puckPreOffset != _pucksPreOffset.end() && puckPostOffset != _pucksPostOffset.end()) { - return puckPose->second.postTransform(puckPostOffset->second).transform(puckPreOffset->second); - } else if (puckPostOffset != _pucksPostOffset.end()) { - return puckPose->second.postTransform(puckPostOffset->second); - } else if (puckPreOffset != _pucksPreOffset.end()) { - return puckPose->second.transform(puckPreOffset->second); + // use sensor space pose. + auto puckPoseIter = _validTrackedObjects.begin(); + while (puckPoseIter != _validTrackedObjects.end()) { + if (puckPoseIter->first == puckIndex) { + break; } + puckPoseIter++; + } + + if (puckPoseIter != _validTrackedObjects.end()) { + + glm::mat4 postMat; // identity + auto postIter = _pucksPostOffset.find(puckIndex); + if (postIter != _pucksPostOffset.end()) { + postMat = postIter->second; + } + + glm::mat4 preMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat; + auto preIter = _pucksPreOffset.find(puckIndex); + if (preIter != _pucksPreOffset.end()) { + preMat = preMat * preIter->second; + } + + return puckPoseIter->second.postTransform(postMat).transform(preMat); } } return controller::Pose(); @@ -924,15 +937,12 @@ void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint void ViveControllerManager::InputDevice::handleHeadPoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, const vec3& linearVelocity, const vec3& angularVelocity) { - //perform a 180 flip to make the HMD face the +z instead of -z, beacuse the head faces +z glm::mat4 matYFlip = mat * Matrices::Y_180; controller::Pose pose(extractTranslation(matYFlip), glmExtractRotation(matYFlip), linearVelocity, angularVelocity); - - glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; glm::mat4 defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) * inputCalibrationData.defaultHeadMat; - controller::Pose hmdHeadPose = pose.transform(sensorToAvatar); - _poseStateMap[controller::HEAD] = hmdHeadPose.postTransform(defaultHeadOffset); + glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; + _poseStateMap[controller::HEAD] = pose.postTransform(defaultHeadOffset).transform(sensorToAvatar); } void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 9a7b2cbc93..4a7fcaf85e 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -79,10 +79,10 @@ private: void sendUserActivityData(QString activity); void configureCalibrationSettings(const QJsonObject configurationSettings); QJsonObject configurationSettings(); - controller::Pose addOffsetToPuckPose(int joint) const; + controller::Pose addOffsetToPuckPose(const controller::InputCalibrationData& inputCalibration, int joint) const; glm::mat4 calculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration); glm::mat4 calculateDefaultToReferenceForHmd(const controller::InputCalibrationData& inputCalibration); - void updateCalibratedLimbs(); + void updateCalibratedLimbs(const controller::InputCalibrationData& inputCalibration); bool checkForCalibrationEvent(); void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand); void handleHmd(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData); diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 22987245a4..9295f50518 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -10,7 +10,7 @@ /* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, RayPick, controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true, LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES, - getGrabPointSphereOffset, HMD, MyAvatar, Messages + getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities */ controllerDispatcherPlugins = {}; @@ -27,6 +27,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; var PROFILE = false; + var DEBUG = true; if (typeof Test !== "undefined") { PROFILE = true; @@ -195,7 +196,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var h; for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { if (controllerLocations[h].valid) { - var nearbyOverlays = Overlays.findOverlays(controllerLocations[h].position, NEAR_MAX_RADIUS * sensorScaleFactor); + var nearbyOverlays = + Overlays.findOverlays(controllerLocations[h].position, NEAR_MAX_RADIUS * sensorScaleFactor); nearbyOverlays.sort(function (a, b) { var aPosition = Overlays.getProperty(a, "position"); var aDistance = Vec3.distance(aPosition, controllerLocations[h].position); @@ -236,6 +238,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); RayPick.getPrevRayPickResult(_this.leftControllerHudRayPick), RayPick.getPrevRayPickResult(_this.rightControllerHudRayPick) ]; + var mouseRayPick = RayPick.getPrevRayPickResult(_this.mouseRayPick); // if the pickray hit something very nearby, put it into the nearby entities list for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { @@ -264,6 +267,20 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }); } + // sometimes, during a HMD snap-turn, an equipped or held item wont be near + // the hand when the findEntities is done. Gather up any hand-children here. + for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { + var handChildrenIDs = findHandChildEntities(h); + handChildrenIDs.forEach(function (handChildID) { + if (handChildID in nearbyEntityPropertiesByID) { + return; + } + var props = Entities.getEntityProperties(handChildID, DISPATCHER_PROPERTIES); + props.id = handChildID; + nearbyEntityPropertiesByID[handChildID] = props; + }); + } + // bundle up all the data about the current situation var controllerData = { triggerValues: [_this.leftTriggerValue, _this.rightTriggerValue], @@ -274,7 +291,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); nearbyEntityPropertiesByID: nearbyEntityPropertiesByID, nearbyOverlayIDs: nearbyOverlayIDs, rayPicks: rayPicks, - hudRayPicks: hudRayPicks + hudRayPicks: hudRayPicks, + mouseRayPick: mouseRayPick }; if (PROFILE) { Script.endProfileRange("dispatch.gather"); @@ -298,6 +316,9 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); // activity-slots which this plugin consumes as "in use" _this.runningPluginNames[orderedPluginName] = true; _this.markSlots(candidatePlugin, orderedPluginName); + if (DEBUG) { + print("controllerDispatcher running " + orderedPluginName); + } } if (PROFILE) { Script.endProfileRange("dispatch.isReady." + orderedPluginName); @@ -330,6 +351,9 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); // of running plugins and mark its activity-slots as "not in use" delete _this.runningPluginNames[runningPluginName]; _this.markSlots(plugin, false); + if (DEBUG) { + print("controllerDispatcher stopping " + runningPluginName); + } } if (PROFILE) { Script.endProfileRange("dispatch.run." + runningPluginName); @@ -390,6 +414,11 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true) }); + this.mouseRayPick = RayPick.createRayPick({ + joint: "Mouse", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, + enabled: true + }); this.handleHandMessage = function(channel, message, sender) { var data; diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index fe868493f4..29db02c6de 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -254,6 +254,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.triggerValue = 0; this.messageGrabEntity = false; this.grabEntityProps = null; + this.shouldSendStart = false; this.parameters = makeDispatcherModuleParameters( 300, @@ -498,6 +499,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var cloneID = this.cloneHotspot(grabbedProperties, controllerData); this.targetEntityID = cloneID; Entities.editEntity(this.targetEntityID, reparentProps); + controllerData.nearbyEntityPropertiesByID[this.targetEntityID] = grabbedProperties; isClone = true; } else if (!grabbedProperties.locked) { Entities.editEntity(this.targetEntityID, reparentProps); @@ -507,8 +509,9 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa return; } - var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(this.targetEntityID, "startEquip", args); + // we don't want to send startEquip message until the trigger is released. otherwise, + // guns etc will fire right as they are equipped. + this.shouldSendStart = true; Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'equip', @@ -588,22 +591,21 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa // if the potentialHotspot is cloneable, clone it and return it // if the potentialHotspot os not cloneable and locked return null - if (potentialEquipHotspot) { - if ((this.triggerSmoothedSqueezed() && !this.waitForTriggerRelease) || this.messageGrabEntity) { - this.grabbedHotspot = potentialEquipHotspot; - this.targetEntityID = this.grabbedHotspot.entityID; - this.startEquipEntity(controllerData); - this.messageGrabEnity = false; - } + if (potentialEquipHotspot && + ((this.triggerSmoothedSqueezed() && !this.waitForTriggerRelease) || this.messageGrabEntity)) { + this.grabbedHotspot = potentialEquipHotspot; + this.targetEntityID = this.grabbedHotspot.entityID; + this.startEquipEntity(controllerData); + this.messageGrabEnity = false; return makeRunningValues(true, [potentialEquipHotspot.entityID], []); } else { return makeRunningValues(false, [], []); } }; - this.isTargetIDValid = function() { - var entityProperties = Entities.getEntityProperties(this.targetEntityID, ["type"]); - return "type" in entityProperties; + this.isTargetIDValid = function(controllerData) { + var entityProperties = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; + return entityProperties && "type" in entityProperties; }; this.isReady = function (controllerData, deltaTime) { @@ -616,7 +618,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var timestamp = Date.now(); this.updateInputs(controllerData); - if (!this.isTargetIDValid()) { + if (!this.isTargetIDValid(controllerData)) { this.endEquipEntity(); return makeRunningValues(false, [], []); } @@ -643,6 +645,13 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var dropDetected = this.dropGestureProcess(deltaTime); if (this.triggerSmoothedReleased()) { + if (this.shouldSendStart) { + // we don't want to send startEquip message until the trigger is released. otherwise, + // guns etc will fire right as they are equipped. + var startArgs = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "startEquip", startArgs); + this.shouldSendStart = false; + } this.waitForTriggerRelease = false; } @@ -674,8 +683,10 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa equipHotspotBuddy.update(deltaTime, timestamp, controllerData); - var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(this.targetEntityID, "continueEquip", args); + if (!this.shouldSendStart) { + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "continueEquip", args); + } return makeRunningValues(true, [this.targetEntityID], []); }; diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index d2b5f92fde..03e2c0baee 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -13,7 +13,7 @@ makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, AVATAR_SELF_ID, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, - getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays + getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI */ @@ -21,7 +21,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { - var PICK_WITH_HAND_RAY = true; var halfPath = { @@ -423,19 +422,9 @@ Script.include("/~/system/libraries/controllers.js"); } }; - this.isPointingAtUI = function(controllerData) { - var hudRayPickInfo = controllerData.hudRayPicks[this.hand]; - var hudPoint2d = HMD.overlayFromWorldPoint(hudRayPickInfo.intersection); - if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(hudPoint2d || Reticle.position)) { - return true; - } - - return false; - }; - this.run = function (controllerData) { if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || - this.notPointingAtEntity(controllerData) || this.isPointingAtUI(controllerData)) { + this.notPointingAtEntity(controllerData)) { this.endNearGrabAction(); this.laserPointerOff(); return makeRunningValues(false, [], []); diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js new file mode 100644 index 0000000000..487e491201 --- /dev/null +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -0,0 +1,246 @@ +// +// hudOverlayPointer.js +// +// scripts/system/controllers/controllerModules/ +// +// Created by Dante Ruiz 2017-9-21 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, + getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, NULL_UUID, + enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, + makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, + AVATAR_SELF_ID, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, + getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI + +*/ +(function() { + Script.include("/~/system/libraries/controllers.js"); + var ControllerDispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); + var halfPath = { + type: "line3d", + color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, + visible: true, + alpha: 1, + solid: true, + glow: 1.0, + lineWidth: 5, + ignoreRayIntersection: true, // always ignore this + drawHUDLayer: true, // Even when burried inside of something, show it. + parentID: AVATAR_SELF_ID + }; + var halfEnd = { + type: "sphere", + solid: true, + color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, + alpha: 0.9, + ignoreRayIntersection: true, + drawHUDLayer: true, // Even when burried inside of something, show it. + visible: true + }; + var fullPath = { + type: "line3d", + color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, + visible: true, + alpha: 1, + solid: true, + glow: 1.0, + lineWidth: 5, + ignoreRayIntersection: true, // always ignore this + drawHUDLayer: true, // Even when burried inside of something, show it. + parentID: AVATAR_SELF_ID + }; + var fullEnd = { + type: "sphere", + solid: true, + color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, + alpha: 0.9, + ignoreRayIntersection: true, + drawHUDLayer: true, // Even when burried inside of something, show it. + visible: true + }; + var holdPath = { + type: "line3d", + color: COLORS_GRAB_DISTANCE_HOLD, + visible: true, + alpha: 1, + solid: true, + glow: 1.0, + lineWidth: 5, + ignoreRayIntersection: true, // always ignore this + drawHUDLayer: true, // Even when burried inside of something, show it. + parentID: AVATAR_SELF_ID + }; + + var renderStates = [ + {name: "half", path: halfPath, end: halfEnd}, + {name: "full", path: fullPath, end: fullEnd}, + {name: "hold", path: holdPath} + ]; + + var defaultRenderStates = [ + {name: "half", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: halfPath}, + {name: "full", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: fullPath}, + {name: "hold", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: holdPath} + ]; + + var MARGIN = 25; + + function HudOverlayPointer(hand) { + var _this = this; + this.hand = hand; + this.reticleMinX = MARGIN; + this.reticleMaxX; + this.reticleMinY = MARGIN; + this.reticleMaxY; + this.clicked = false; + this.triggerClicked = 0; + this.movedAway = false; + this.parameters = ControllerDispatcherUtils.makeDispatcherModuleParameters( + 540, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.getOtherHandController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; + }; + + _this.isClicked = function() { + return _this.triggerClicked; + }; + + this.handToController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + }; + + this.updateRecommendedArea = function() { + var dims = Controller.getViewportDimensions(); + this.reticleMaxX = dims.x - MARGIN; + this.reticleMaxY = dims.y - MARGIN; + }; + + this.updateLaserPointer = function(controllerData) { + var RADIUS = 0.005; + var dim = { x: RADIUS, y: RADIUS, z: RADIUS }; + + if (this.mode === "full") { + this.fullEnd.dimensions = dim; + LaserPointers.editRenderState(this.laserPointer, this.mode, {path: fullPath, end: this.fullEnd}); + } else if (this.mode === "half") { + this.halfEnd.dimensions = dim; + LaserPointers.editRenderState(this.laserPointer, this.mode, {path: halfPath, end: this.halfEnd}); + } + + LaserPointers.enableLaserPointer(this.laserPointer); + LaserPointers.setRenderState(this.laserPointer, this.mode); + }; + + this.processControllerTriggers = function(controllerData) { + if (controllerData.triggerClicks[this.hand]) { + this.mode = "full"; + } else if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + this.clicked = false; + this.mode = "half"; + } else { + this.mode = "none"; + } + }; + + this.calculateNewReticlePosition = function(intersection) { + this.updateRecommendedArea(); + var point2d = HMD.overlayFromWorldPoint(intersection); + point2d.x = Math.max(this.reticleMinX, Math.min(point2d.x, this.reticleMaxX)); + point2d.y = Math.max(this.reticleMinY, Math.min(point2d.y, this.reticleMaxY)); + return point2d; + }; + + this.setReticlePosition = function(point2d) { + Reticle.setPosition(point2d); + }; + + this.pointingAtTablet = function(controllerData) { + var rayPick = controllerData.rayPicks[this.hand]; + return (rayPick.objectID === HMD.tabletScreenID || rayPick.objectID === HMD.homeButtonID); + }; + + this.processLaser = function(controllerData) { + var controllerLocation = controllerData.controllerLocations[this.hand]; + if ((controllerData.triggerValues[this.hand] < ControllerDispatcherUtils.TRIGGER_ON_VALUE || !controllerLocation.valid) || + this.pointingAtTablet(controllerData)) { + this.exitModule(); + return false; + } + var hudRayPick = controllerData.hudRayPicks[this.hand]; + var point2d = this.calculateNewReticlePosition(hudRayPick.intersection); + this.setReticlePosition(point2d); + if (!Reticle.isPointingAtSystemOverlay(point2d)) { + this.exitModule(); + return false; + } + Reticle.visible = false; + this.movedAway = false; + this.triggerClicked = controllerData.triggerClicks[this.hand]; + this.processControllerTriggers(controllerData); + this.updateLaserPointer(controllerData); + return true; + }; + + this.exitModule = function() { + LaserPointers.disableLaserPointer(this.laserPointer); + }; + + this.isReady = function (controllerData) { + if (this.processLaser(controllerData)) { + return ControllerDispatcherUtils.makeRunningValues(true, [], []); + } else { + return ControllerDispatcherUtils.makeRunningValues(false, [], []); + } + }; + + this.run = function (controllerData, deltaTime) { + return this.isReady(controllerData); + }; + + this.cleanup = function () { + LaserPointers.disableLaserPointer(this.laserPointer); + LaserPointers.removeLaserPointer(this.laserPointer); + }; + + this.halfEnd = halfEnd; + this.fullEnd = fullEnd; + this.laserPointer = LaserPointers.createLaserPointer({ + joint: (this.hand === RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", + filter: RayPick.PICK_HUD, + maxDistance: PICK_MAX_DISTANCE, + posOffset: getGrabPointSphereOffset(this.handToController(), true), + renderStates: renderStates, + enabled: true, + defaultRenderStates: defaultRenderStates + }); + } + + + var leftHudOverlayPointer = new HudOverlayPointer(LEFT_HAND); + var rightHudOverlayPointer = new HudOverlayPointer(RIGHT_HAND); + + var clickMapping = Controller.newMapping('HudOverlayPointer-click'); + clickMapping.from(rightHudOverlayPointer.isClicked).to(Controller.Actions.ReticleClick); + clickMapping.from(leftHudOverlayPointer.isClicked).to(Controller.Actions.ReticleClick); + clickMapping.enable(); + + ControllerDispatcherUtils.enableDispatcherModule("LeftHudOverlayPointer", leftHudOverlayPointer); + ControllerDispatcherUtils.enableDispatcherModule("RightHudOverlayPointer", rightHudOverlayPointer); + + function cleanup() { + ControllerDispatcherUtils.disableDispatcherModule("LeftHudOverlayPointer"); + ControllerDispatcherUtils.disbaleDispatcherModule("RightHudOverlayPointer"); + } + Script.scriptEnding.connect(cleanup); + +})(); diff --git a/scripts/system/controllers/controllerModules/mouseHMD.js b/scripts/system/controllers/controllerModules/mouseHMD.js new file mode 100644 index 0000000000..9ccf4912a1 --- /dev/null +++ b/scripts/system/controllers/controllerModules/mouseHMD.js @@ -0,0 +1,137 @@ +// +// mouseHMD.js +// +// scripts/system/controllers/controllerModules/ +// +// Created by Dante Ruiz 2017-9-22 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var ControllerDispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); + + function TimeLock(experation) { + this.experation = experation; + this.last = 0; + this.update = function(time) { + this.last = time || Date.now(); + }; + + this.expired = function(time) { + return ((time || Date.now()) - this.last) > this.experation; + }; + } + + function MouseHMD() { + var _this = this; + this.mouseMoved = false; + this.mouseActivity = new TimeLock(5000); + this.handControllerActivity = new TimeLock(4000); + this.parameters = ControllerDispatcherUtils.makeDispatcherModuleParameters( + 10, + ["mouse"], + [], + 100); + + this.onMouseMove = function() { + _this.updateMouseActivity(); + }; + + this.onMouseClick = function() { + _this.updateMouseActivity(); + }; + + this.updateMouseActivity = function(isClick) { + if (_this.ignoreMouseActivity()) { + return; + } + + if (HMD.active) { + var now = Date.now(); + _this.mouseActivity.update(now); + } + }; + + this.adjustReticleDepth = function(controllerData) { + if (Reticle.isPointingAtSystemOverlay(Reticle.position)) { + var reticlePositionOnHUD = HMD.worldPointFromOverlay(Reticle.position); + Reticle.depth = Vec3.distance(reticlePositionOnHUD, HMD.position); + } else { + var APPARENT_MAXIMUM_DEPTH = 100.0; + var result = controllerData.mouseRayPick; + Reticle.depth = result.intersects ? result.distance : APPARENT_MAXIMUM_DEPTH; + } + }; + + this.ignoreMouseActivity = function() { + if (!Reticle.allowMouseCapture) { + return true; + } + + var pos = Reticle.position; + if (!pos || (pos.x === -1 && pos.y === -1)) { + return true; + } + + if (!_this.handControllerActivity.expired()) { + return true; + } + + return false; + }; + + this.triggersPressed = function(controllerData, now) { + var onValue = ControllerDispatcherUtils.TRIGGER_ON_VALUE; + var rightHand = ControllerDispatcherUtils.RIGHT_HAND; + var leftHand = ControllerDispatcherUtils.LEFT_HAND; + var leftTriggerValue = controllerData.triggerValues[leftHand]; + var rightTriggerValue = controllerData.triggerValues[rightHand]; + + if (leftTriggerValue > onValue || rightTriggerValue > onValue) { + this.handControllerActivity.update(now); + return true; + } + + return false; + }; + + this.isReady = function(controllerData, deltaTime) { + var now = Date.now(); + this.triggersPressed(controllerData, now); + if ((HMD.active && !this.mouseActivity.expired(now)) && _this.handControllerActivity.expired()) { + Reticle.visible = true; + return ControllerDispatcherUtils.makeRunningValues(true, [], []); + } + if (HMD.active) { + Reticle.visible = false; + } + + return ControllerDispatcherUtils.makeRunningValues(false, [], []); + }; + + this.run = function(controllerData, deltaTime) { + var now = Date.now(); + if (this.mouseActivity.expired(now) || this.triggersPressed(controllerData, now)) { + Reticle.visible = false; + return ControllerDispatcherUtils.makeRunningValues(false, [], []); + } + this.adjustReticleDepth(controllerData); + return ControllerDispatcherUtils.makeRunningValues(true, [], []); + }; + } + + var mouseHMD = new MouseHMD(); + ControllerDispatcherUtils.enableDispatcherModule("MouseHMD", mouseHMD); + + Controller.mouseMoveEvent.connect(mouseHMD.onMouseMove); + Controller.mousePressEvent.connect(mouseHMD.onMouseClick); + + function cleanup() { + ControllerDispatcherUtils.disableDispatcherModule("MouseHMD"); + } + + Script.scriptEnding.connect(cleanup); +})(); diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index bd7a64572a..2484067655 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -182,7 +182,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); } if (targetProps) { - if (!propsArePhysical(targetProps) && !propsAreCloneDynamic(targetProps)) { + if ((!propsArePhysical(targetProps) && !propsAreCloneDynamic(targetProps)) || + targetProps.parentID != NULL_UUID) { return makeRunningValues(false, [], []); // let nearParentGrabEntity handle it } else { this.targetEntityID = targetProps.id; @@ -216,7 +217,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var targetProps = this.getTargetProps(controllerData); if (targetProps) { - if (controllerData.triggerClicks[this.hand] || controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) { + if (controllerData.triggerClicks[this.hand] || + controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) { // switch to grabbing var targetCloneable = entityIsCloneable(targetProps); if (targetCloneable) { diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 39e9371931..9323f651a2 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -8,8 +8,10 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, - TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent, - Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE + TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, + findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, + HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME, + TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -28,6 +30,9 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.previousParentJointIndex = {}; this.previouslyUnhooked = {}; this.hapticTargetID = null; + this.lastUnequipCheckTime = 0; + this.autoUnequipCounter = 0; + this.lastUnexpectedChildrenCheckTime = 0; this.parameters = makeDispatcherModuleParameters( 500, @@ -40,11 +45,11 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); this.controllerJointIndex = getControllerJointIndex(this.hand); - this.getOtherModule = function() { - return (this.hand === RIGHT_HAND) ? leftNearParentingGrabEntity : rightNearParentingGrabEntity; - }; - this.thisHandIsParent = function(props) { + if (!props) { + return false; + } + if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) { return false; } @@ -93,8 +98,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); if (this.thisHandIsParent(targetProps)) { // this should never happen, but if it does, don't set previous parent to be this hand. - // this.previousParentID[targetProps.id] = NULL; - // this.previousParentJointIndex[targetProps.id] = -1; + this.previousParentID[targetProps.id] = null; + this.previousParentJointIndex[targetProps.id] = -1; } else { this.previousParentID[targetProps.id] = targetProps.parentID; this.previousParentJointIndex[targetProps.id] = targetProps.parentJointIndex; @@ -111,20 +116,24 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.grabbing = true; }; - this.endNearParentingGrabEntity = function () { - if (this.previousParentID[this.targetEntityID] === NULL_UUID || this.previousParentID === undefined) { - Entities.editEntity(this.targetEntityID, { - parentID: this.previousParentID[this.targetEntityID], - parentJointIndex: this.previousParentJointIndex[this.targetEntityID] - }); - } else { - // we're putting this back as a child of some other parent, so zero its velocity - Entities.editEntity(this.targetEntityID, { - parentID: this.previousParentID[this.targetEntityID], - parentJointIndex: this.previousParentJointIndex[this.targetEntityID], - localVelocity: {x: 0, y: 0, z: 0}, - localAngularVelocity: {x: 0, y: 0, z: 0} - }); + this.endNearParentingGrabEntity = function (controllerData) { + this.hapticTargetID = null; + var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; + if (this.thisHandIsParent(props)) { + if (this.previousParentID[this.targetEntityID] === NULL_UUID || this.previousParentID === undefined) { + Entities.editEntity(this.targetEntityID, { + parentID: this.previousParentID[this.targetEntityID], + parentJointIndex: this.previousParentJointIndex[this.targetEntityID] + }); + } else { + // we're putting this back as a child of some other parent, so zero its velocity + Entities.editEntity(this.targetEntityID, { + parentID: this.previousParentID[this.targetEntityID], + parentJointIndex: this.previousParentJointIndex[this.targetEntityID], + localVelocity: {x: 0, y: 0, z: 0}, + localAngularVelocity: {x: 0, y: 0, z: 0} + }); + } } var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; @@ -133,6 +142,74 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.targetEntityID = null; }; + this.checkForChildTooFarAway = function (controllerData) { + var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; + var now = Date.now(); + if (now - this.lastUnequipCheckTime > MSECS_PER_SEC * TEAR_AWAY_CHECK_TIME) { + this.lastUnequipCheckTime = now; + if (props.parentID == AVATAR_SELF_ID) { + var sensorScaleFactor = MyAvatar.sensorToWorldScale; + var handPosition = controllerData.controllerLocations[this.hand].position; + var dist = distanceBetweenPointAndEntityBoundingBox(handPosition, props); + var distance = Vec3.distance(props.position, handPosition); + if ((dist > TEAR_AWAY_DISTANCE) || + (distance > NEAR_GRAB_RADIUS * sensorScaleFactor)) { + this.autoUnequipCounter++; + } else { + this.autoUnequipCounter = 0; + } + if (this.autoUnequipCounter >= TEAR_AWAY_COUNT) { + return true; + } + } + } + return false; + }; + + + this.checkForUnexpectedChildren = function (controllerData) { + // sometimes things can get parented to a hand and this script is unaware. Search for such entities and + // unhook them. + + var now = Date.now(); + var UNEXPECTED_CHILDREN_CHECK_TIME = 0.1; // seconds + if (now - this.lastUnexpectedChildrenCheckTime > MSECS_PER_SEC * UNEXPECTED_CHILDREN_CHECK_TIME) { + this.lastUnexpectedChildrenCheckTime = now; + + var children = findHandChildEntities(this.hand); + var _this = this; + + children.forEach(function(childID) { + // we appear to be holding something and this script isn't in a state that would be holding something. + // unhook it. if we previously took note of this entity's parent, put it back where it was. This + // works around some problems that happen when more than one hand or avatar is passing something around. + if (_this.previousParentID[childID]) { + var previousParentID = _this.previousParentID[childID]; + var previousParentJointIndex = _this.previousParentJointIndex[childID]; + + // The main flaw with keeping track of previous parentage in individual scripts is: + // (1) A grabs something (2) B takes it from A (3) A takes it from B (4) A releases it + // now A and B will take turns passing it back to the other. Detect this and stop the loop here... + var UNHOOK_LOOP_DETECT_MS = 200; + if (_this.previouslyUnhooked[childID]) { + if (now - _this.previouslyUnhooked[childID] < UNHOOK_LOOP_DETECT_MS) { + previousParentID = NULL_UUID; + previousParentJointIndex = -1; + } + } + _this.previouslyUnhooked[childID] = now; + + Entities.editEntity(childID, { + parentID: previousParentID, + parentJointIndex: previousParentJointIndex + }); + } else { + Entities.editEntity(childID, { parentID: NULL_UUID }); + } + }); + } + }; + this.getTargetProps = function (controllerData) { // nearbyEntityProperties is already sorted by length from controller var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; @@ -140,8 +217,10 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); for (var i = 0; i < nearbyEntityProperties.length; i++) { var props = nearbyEntityProperties[i]; var handPosition = controllerData.controllerLocations[this.hand].position; - var distance = Vec3.distance(props.position, handPosition); - if (distance > NEAR_GRAB_RADIUS * sensorScaleFactor) { + var dist = distanceBetweenPointAndEntityBoundingBox(handPosition, props); + var distance = Vec3.distance(handPosition, props.position); + if ((dist > TEAR_AWAY_DISTANCE) || + (distance > NEAR_GRAB_RADIUS * sensorScaleFactor)) { continue; } if (entityIsGrabbable(props)) { @@ -168,11 +247,13 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var targetProps = this.getTargetProps(controllerData); if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { + this.checkForUnexpectedChildren(controllerData); return makeRunningValues(false, [], []); } if (targetProps) { - if (propsArePhysical(targetProps) || propsAreCloneDynamic(targetProps)) { + if ((propsArePhysical(targetProps) || propsAreCloneDynamic(targetProps)) && + targetProps.parentID == NULL_UUID) { return makeRunningValues(false, [], []); // let nearActionGrabEntity handle it } else { this.targetEntityID = targetProps.id; @@ -188,16 +269,23 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); if (this.grabbing) { if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { - this.endNearParentingGrabEntity(); + this.endNearParentingGrabEntity(controllerData); + return makeRunningValues(false, [], []); + } + + var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; + if (!props) { + // entity was deleted + this.grabbing = false; + this.targetEntityID = null; this.hapticTargetID = null; return makeRunningValues(false, [], []); } - var props = Entities.getEntityProperties(this.targetEntityID); - if (!this.thisHandIsParent(props)) { - this.grabbing = false; - this.targetEntityID = null; - this.hapticTargetID = null; + if (this.checkForChildTooFarAway(controllerData)) { + // if the held entity moves too far from the hand, release it + print("nearParentGrabEntity -- autoreleasing held item because it is far from hand"); + this.endNearParentingGrabEntity(controllerData); return makeRunningValues(false, [], []); } @@ -205,7 +293,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args); } else { // still searching / highlighting - var readiness = this.isReady (controllerData); + var readiness = this.isReady(controllerData); if (!readiness.active) { return readiness; } @@ -218,7 +306,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; var cloneID = cloneEntity(targetProps, worldEntityProps); var cloneProps = Entities.getEntityProperties(cloneID); - this.grabbing = true; this.targetEntityID = cloneID; this.startNearParentingGrabEntity(controllerData, cloneProps); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 8d093afe2c..f9557f685f 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -88,13 +88,8 @@ Script.include("/~/system/libraries/utils.js"); this.startNearParentingGrabOverlay = function (controllerData) { Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); - var handJointIndex; - // if (this.ignoreIK) { - // handJointIndex = this.controllerJointIndex; - // } else { - // handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); - // } - handJointIndex = this.controllerJointIndex; + this.controllerJointIndex = getControllerJointIndex(this.hand); + var handJointIndex = this.controllerJointIndex; var grabbedProperties = this.getGrabbedProperties(); diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index edea1f993d..09ba5b9430 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -26,6 +26,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.previousParentID = {}; this.previousParentJointIndex = {}; this.previouslyUnhooked = {}; + this.startSent = false; this.parameters = makeDispatcherModuleParameters( 520, @@ -76,7 +77,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var targetProps = this.getTargetProps(controllerData); if (targetProps) { this.targetEntityID = targetProps.id; - this.startNearTrigger(controllerData); return makeRunningValues(true, [this.targetEntityID], []); } else { return makeRunningValues(false, [], []); @@ -84,12 +84,16 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.run = function (controllerData) { - if (controllerData.triggerClicks[this.hand] === 0) { + if (!this.startSent) { + this.startNearTrigger(controllerData); + this.startSent = true; + } else if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { this.endNearTrigger(controllerData); + this.startSent = false; return makeRunningValues(false, [], []); + } else { + this.continueNearTrigger(controllerData); } - - this.continueNearTrigger(controllerData); return makeRunningValues(true, [this.targetEntityID], []); }; diff --git a/scripts/system/controllers/controllerModules/scaleAvatar.js b/scripts/system/controllers/controllerModules/scaleAvatar.js index fc28f4a00f..de0434258c 100644 --- a/scripts/system/controllers/controllerModules/scaleAvatar.js +++ b/scripts/system/controllers/controllerModules/scaleAvatar.js @@ -76,9 +76,9 @@ dispatcherUtils.enableDispatcherModule("LeftScaleAvatar", leftScaleAvatar); dispatcherUtils.enableDispatcherModule("RightScaleAvatar", rightScaleAvatar); - this.cleanup = function() { + function cleanup() { dispatcherUtils.disableDispatcherModule("LeftScaleAvatar"); dispatcherUtils.disableDispatcherModule("RightScaleAvatar"); }; - Script.scriptEnding.connect(this.cleanup); + Script.scriptEnding.connect(cleanup); })(); diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 9d01ceef65..def958b223 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -248,10 +248,17 @@ Script.include("/~/system/libraries/controllers.js"); } }; + this.nearGrabWantsToRun = function(controllerData) { + var moduleName = this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"; + var module = getEnabledModuleByName(moduleName); + var ready = module ? module.isReady(controllerData) : makeRunningValues(false, [], []); + return ready.active; + }; + this.processStylus = function(controllerData) { this.updateStylusTip(); - if (!this.stylusTip.valid || this.overlayLaserActive(controllerData)) { + if (!this.stylusTip.valid || this.overlayLaserActive(controllerData) || this.nearGrabWantsToRun(controllerData)) { this.pointFinger(false); this.hideStylus(); return false; diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index 548179761c..d2717a1348 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -14,7 +14,6 @@ enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Vec3, LaserPointers, RayPick, HMD, Uuid, AvatarList */ -/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ Script.include("/~/system/libraries/Xform.js"); Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -22,388 +21,354 @@ Script.include("/~/system/libraries/controllers.js"); (function() { // BEGIN LOCAL_SCOPE -var inTeleportMode = false; + var TARGET_MODEL_URL = Script.resolvePath("../../assets/models/teleport-destination.fbx"); + var TOO_CLOSE_MODEL_URL = Script.resolvePath("../../assets/models/teleport-cancel.fbx"); + var SEAT_MODEL_URL = Script.resolvePath("../../assets/models/teleport-seat.fbx"); -var SMOOTH_ARRIVAL_SPACING = 33; -var NUMBER_OF_STEPS = 6; - -var TARGET_MODEL_URL = Script.resolvePath("../../assets/models/teleport-destination.fbx"); -var TOO_CLOSE_MODEL_URL = Script.resolvePath("../../assets/models/teleport-cancel.fbx"); -var SEAT_MODEL_URL = Script.resolvePath("../../assets/models/teleport-seat.fbx"); - -var TARGET_MODEL_DIMENSIONS = { - x: 1.15, - y: 0.5, - z: 1.15 -}; - -var COLORS_TELEPORT_SEAT = { - red: 255, - green: 0, - blue: 170 -}; - -var COLORS_TELEPORT_CAN_TELEPORT = { - red: 97, - green: 247, - blue: 255 -}; - -var COLORS_TELEPORT_CANCEL = { - red: 255, - green: 184, - blue: 73 -}; - -var TELEPORT_CANCEL_RANGE = 1; -var COOL_IN_DURATION = 500; - -var handInfo = { - right: { - controllerInput: Controller.Standard.RightHand - }, - left: { - controllerInput: Controller.Standard.LeftHand - } -}; - -var cancelPath = { - type: "line3d", - color: COLORS_TELEPORT_CANCEL, - ignoreRayIntersection: true, - alpha: 1, - solid: true, - drawInFront: true, - glow: 1.0 -}; -var teleportPath = { - type: "line3d", - color: COLORS_TELEPORT_CAN_TELEPORT, - ignoreRayIntersection: true, - alpha: 1, - solid: true, - drawInFront: true, - glow: 1.0 -}; -var seatPath = { - type: "line3d", - color: COLORS_TELEPORT_SEAT, - ignoreRayIntersection: true, - alpha: 1, - solid: true, - drawInFront: true, - glow: 1.0 -}; -var cancelEnd = { - type: "model", - url: TOO_CLOSE_MODEL_URL, - dimensions: TARGET_MODEL_DIMENSIONS, - ignoreRayIntersection: true -}; -var teleportEnd = { - type: "model", - url: TARGET_MODEL_URL, - dimensions: TARGET_MODEL_DIMENSIONS, - ignoreRayIntersection: true -}; -var seatEnd = { - type: "model", - url: SEAT_MODEL_URL, - dimensions: TARGET_MODEL_DIMENSIONS, - ignoreRayIntersection: true -}; - -var teleportRenderStates = [{name: "cancel", path: cancelPath, end: cancelEnd}, - {name: "teleport", path: teleportPath, end: teleportEnd}, - {name: "seat", path: seatPath, end: seatEnd}]; - -var DEFAULT_DISTANCE = 50; -var teleportDefaultRenderStates = [{name: "cancel", distance: DEFAULT_DISTANCE, path: cancelPath}]; - -function ThumbPad(hand) { - this.hand = hand; - var _thisPad = this; - - this.buttonPress = function(value) { - _thisPad.buttonValue = value; - }; -} - -function Trigger(hand) { - this.hand = hand; - var _this = this; - - this.buttonPress = function(value) { - _this.buttonValue = value; + var TARGET_MODEL_DIMENSIONS = { + x: 1.15, + y: 0.5, + z: 1.15 }; - this.down = function() { - var down = _this.buttonValue === 1 ? 1.0 : 0.0; - return down; - }; -} - -var coolInTimeout = null; -var ignoredEntities = []; - -var TELEPORTER_STATES = { - IDLE: 'idle', - COOL_IN: 'cool_in', - TARGETTING: 'targetting', - TARGETTING_INVALID: 'targetting_invalid', -}; - -var TARGET = { - NONE: 'none', // Not currently targetting anything - INVISIBLE: 'invisible', // The current target is an invvsible surface - INVALID: 'invalid', // The current target is invalid (wall, ceiling, etc.) - SURFACE: 'surface', // The current target is a valid surface - SEAT: 'seat', // The current target is a seat -}; - -function Teleporter(hand) { - var _this = this; - this.hand = hand; - this.buttonValue = 0; - this.active = false; - this.state = TELEPORTER_STATES.IDLE; - this.currentTarget = TARGET.INVALID; - this.currentResult = null; - - this.getOtherModule = function() { - var otherModule = this.hand === RIGHT_HAND ? leftTeleporter : rightTeleporter; - return otherModule; + var COLORS_TELEPORT_SEAT = { + red: 255, + green: 0, + blue: 170 }; - this.teleportRayHandVisible = LaserPointers.createLaserPointer({ - joint: (_this.hand === RIGHT_HAND) ? "RightHand" : "LeftHand", - filter: RayPick.PICK_ENTITIES, - faceAvatar: true, - centerEndY: false, - renderStates: teleportRenderStates, - defaultRenderStates: teleportDefaultRenderStates - }); - this.teleportRayHandInvisible = LaserPointers.createLaserPointer({ - joint: (_this.hand === RIGHT_HAND) ? "RightHand" : "LeftHand", - filter: RayPick.PICK_ENTITIES | RayPick.PICK_INCLUDE_INVISIBLE, - faceAvatar: true, - centerEndY: false, - renderStates: teleportRenderStates - }); - this.teleportRayHeadVisible = LaserPointers.createLaserPointer({ - joint: "Avatar", - filter: RayPick.PICK_ENTITIES, - faceAvatar: true, - centerEndY: false, - renderStates: teleportRenderStates, - defaultRenderStates: teleportDefaultRenderStates - }); - this.teleportRayHeadInvisible = LaserPointers.createLaserPointer({ - joint: "Avatar", - filter: RayPick.PICK_ENTITIES | RayPick.PICK_INCLUDE_INVISIBLE, - faceAvatar: true, - centerEndY: false, - renderStates: teleportRenderStates - }); - - this.teleporterMappingInternalName = 'Hifi-Teleporter-Internal-Dev-' + Math.random(); - this.teleportMappingInternal = Controller.newMapping(this.teleporterMappingInternalName); - - this.enableMappings = function() { - Controller.enableMapping(this.teleporterMappingInternalName); + var COLORS_TELEPORT_CAN_TELEPORT = { + red: 97, + green: 247, + blue: 255 }; - this.disableMappings = function() { - Controller.disableMapping(teleporter.teleporterMappingInternalName); + var COLORS_TELEPORT_CANCEL = { + red: 255, + green: 184, + blue: 73 }; - this.cleanup = function() { - this.disableMappings(); + var TELEPORT_CANCEL_RANGE = 1; + var COOL_IN_DURATION = 300; - LaserPointers.removeLaserPointer(this.teleportRayHandVisible); - LaserPointers.removeLaserPointer(this.teleportRayHandInvisible); - LaserPointers.removeLaserPointer(this.teleportRayHeadVisible); - LaserPointers.removeLaserPointer(this.teleportRayHeadInvisible); - }; - - this.buttonPress = function(value) { - _this.buttonValue = value; - }; - - this.parameters = makeDispatcherModuleParameters( - 80, - this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], - [], - 100); - - this.enterTeleport = function() { - if (coolInTimeout !== null) { - Script.clearTimeout(coolInTimeout); + var handInfo = { + right: { + controllerInput: Controller.Standard.RightHand + }, + left: { + controllerInput: Controller.Standard.LeftHand } + }; - this.state = TELEPORTER_STATES.COOL_IN; - coolInTimeout = Script.setTimeout(function() { - if (_this.state === TELEPORTER_STATES.COOL_IN) { - _this.state = TELEPORTER_STATES.TARGETTING; + var cancelPath = { + type: "line3d", + color: COLORS_TELEPORT_CANCEL, + ignoreRayIntersection: true, + alpha: 1, + solid: true, + drawInFront: true, + glow: 1.0 + }; + var teleportPath = { + type: "line3d", + color: COLORS_TELEPORT_CAN_TELEPORT, + ignoreRayIntersection: true, + alpha: 1, + solid: true, + drawInFront: true, + glow: 1.0 + }; + var seatPath = { + type: "line3d", + color: COLORS_TELEPORT_SEAT, + ignoreRayIntersection: true, + alpha: 1, + solid: true, + drawInFront: true, + glow: 1.0 + }; + var cancelEnd = { + type: "model", + url: TOO_CLOSE_MODEL_URL, + dimensions: TARGET_MODEL_DIMENSIONS, + ignoreRayIntersection: true + }; + var teleportEnd = { + type: "model", + url: TARGET_MODEL_URL, + dimensions: TARGET_MODEL_DIMENSIONS, + ignoreRayIntersection: true + }; + var seatEnd = { + type: "model", + url: SEAT_MODEL_URL, + dimensions: TARGET_MODEL_DIMENSIONS, + ignoreRayIntersection: true + }; + + var teleportRenderStates = [{name: "cancel", path: cancelPath, end: cancelEnd}, + {name: "teleport", path: teleportPath, end: teleportEnd}, + {name: "seat", path: seatPath, end: seatEnd}]; + + var DEFAULT_DISTANCE = 50; + var teleportDefaultRenderStates = [{name: "cancel", distance: DEFAULT_DISTANCE, path: cancelPath}]; + + var coolInTimeout = null; + var ignoredEntities = []; + + var TELEPORTER_STATES = { + IDLE: 'idle', + COOL_IN: 'cool_in', + TARGETTING: 'targetting', + TARGETTING_INVALID: 'targetting_invalid' + }; + + var TARGET = { + NONE: 'none', // Not currently targetting anything + INVISIBLE: 'invisible', // The current target is an invvsible surface + INVALID: 'invalid', // The current target is invalid (wall, ceiling, etc.) + SURFACE: 'surface', // The current target is a valid surface + SEAT: 'seat' // The current target is a seat + }; + + function Teleporter(hand) { + var _this = this; + this.hand = hand; + this.buttonValue = 0; + this.disabled = false; // used by the 'Hifi-Teleport-Disabler' message handler + this.active = false; + this.state = TELEPORTER_STATES.IDLE; + this.currentTarget = TARGET.INVALID; + this.currentResult = null; + + this.getOtherModule = function() { + var otherModule = this.hand === RIGHT_HAND ? leftTeleporter : rightTeleporter; + return otherModule; + }; + + this.teleportRayHandVisible = LaserPointers.createLaserPointer({ + joint: (_this.hand === RIGHT_HAND) ? "RightHand" : "LeftHand", + filter: RayPick.PICK_ENTITIES, + faceAvatar: true, + centerEndY: false, + renderStates: teleportRenderStates, + defaultRenderStates: teleportDefaultRenderStates + }); + this.teleportRayHandInvisible = LaserPointers.createLaserPointer({ + joint: (_this.hand === RIGHT_HAND) ? "RightHand" : "LeftHand", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_INCLUDE_INVISIBLE, + faceAvatar: true, + centerEndY: false, + renderStates: teleportRenderStates + }); + this.teleportRayHeadVisible = LaserPointers.createLaserPointer({ + joint: "Avatar", + filter: RayPick.PICK_ENTITIES, + faceAvatar: true, + centerEndY: false, + renderStates: teleportRenderStates, + defaultRenderStates: teleportDefaultRenderStates + }); + this.teleportRayHeadInvisible = LaserPointers.createLaserPointer({ + joint: "Avatar", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_INCLUDE_INVISIBLE, + faceAvatar: true, + centerEndY: false, + renderStates: teleportRenderStates + }); + + this.cleanup = function() { + LaserPointers.removeLaserPointer(this.teleportRayHandVisible); + LaserPointers.removeLaserPointer(this.teleportRayHandInvisible); + LaserPointers.removeLaserPointer(this.teleportRayHeadVisible); + LaserPointers.removeLaserPointer(this.teleportRayHeadInvisible); + }; + + this.buttonPress = function(value) { + _this.buttonValue = value; + }; + + this.parameters = makeDispatcherModuleParameters( + 80, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.enterTeleport = function() { + if (coolInTimeout !== null) { + Script.clearTimeout(coolInTimeout); } - }, COOL_IN_DURATION); - // pad scale with avatar size - var AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS = Vec3.multiply(MyAvatar.sensorToWorldScale, TARGET_MODEL_DIMENSIONS); + this.state = TELEPORTER_STATES.COOL_IN; + coolInTimeout = Script.setTimeout(function() { + if (_this.state === TELEPORTER_STATES.COOL_IN) { + _this.state = TELEPORTER_STATES.TARGETTING; + } + }, COOL_IN_DURATION); - if (!Vec3.equal(AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS, cancelEnd.dimensions)) { - cancelEnd.dimensions = AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS; - teleportEnd.dimensions = AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS; - seatEnd.dimensions = AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS; + // pad scale with avatar size + var AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS = Vec3.multiply(MyAvatar.sensorToWorldScale, TARGET_MODEL_DIMENSIONS); - teleportRenderStates = [{name: "cancel", path: cancelPath, end: cancelEnd}, - {name: "teleport", path: teleportPath, end: teleportEnd}, - {name: "seat", path: seatPath, end: seatEnd}]; + if (!Vec3.equal(AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS, cancelEnd.dimensions)) { + cancelEnd.dimensions = AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS; + teleportEnd.dimensions = AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS; + seatEnd.dimensions = AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS; - LaserPointers.editRenderState(this.teleportRayHandVisible, "cancel", teleportRenderStates[0]); - LaserPointers.editRenderState(this.teleportRayHandInvisible, "cancel", teleportRenderStates[0]); - LaserPointers.editRenderState(this.teleportRayHeadVisible, "cancel", teleportRenderStates[0]); - LaserPointers.editRenderState(this.teleportRayHeadInvisible, "cancel", teleportRenderStates[0]); + teleportRenderStates = [{name: "cancel", path: cancelPath, end: cancelEnd}, + {name: "teleport", path: teleportPath, end: teleportEnd}, + {name: "seat", path: seatPath, end: seatEnd}]; - LaserPointers.editRenderState(this.teleportRayHandVisible, "teleport", teleportRenderStates[1]); - LaserPointers.editRenderState(this.teleportRayHandInvisible, "teleport", teleportRenderStates[1]); - LaserPointers.editRenderState(this.teleportRayHeadVisible, "teleport", teleportRenderStates[1]); - LaserPointers.editRenderState(this.teleportRayHeadInvisible, "teleport", teleportRenderStates[1]); + LaserPointers.editRenderState(this.teleportRayHandVisible, "cancel", teleportRenderStates[0]); + LaserPointers.editRenderState(this.teleportRayHandInvisible, "cancel", teleportRenderStates[0]); + LaserPointers.editRenderState(this.teleportRayHeadVisible, "cancel", teleportRenderStates[0]); + LaserPointers.editRenderState(this.teleportRayHeadInvisible, "cancel", teleportRenderStates[0]); - LaserPointers.editRenderState(this.teleportRayHandVisible, "seat", teleportRenderStates[2]); - LaserPointers.editRenderState(this.teleportRayHandInvisible, "seat", teleportRenderStates[2]); - LaserPointers.editRenderState(this.teleportRayHeadVisible, "seat", teleportRenderStates[2]); - LaserPointers.editRenderState(this.teleportRayHeadInvisible, "seat", teleportRenderStates[2]); - } - }; + LaserPointers.editRenderState(this.teleportRayHandVisible, "teleport", teleportRenderStates[1]); + LaserPointers.editRenderState(this.teleportRayHandInvisible, "teleport", teleportRenderStates[1]); + LaserPointers.editRenderState(this.teleportRayHeadVisible, "teleport", teleportRenderStates[1]); + LaserPointers.editRenderState(this.teleportRayHeadInvisible, "teleport", teleportRenderStates[1]); - this.isReady = function(controllerData, deltaTime) { - var otherModule = this.getOtherModule(); - if (_this.buttonValue !== 0 && !otherModule.active) { - this.active = true; - this.enterTeleport(); - return makeRunningValues(true, [], []); - } - return makeRunningValues(false, [], []); - }; + LaserPointers.editRenderState(this.teleportRayHandVisible, "seat", teleportRenderStates[2]); + LaserPointers.editRenderState(this.teleportRayHandInvisible, "seat", teleportRenderStates[2]); + LaserPointers.editRenderState(this.teleportRayHeadVisible, "seat", teleportRenderStates[2]); + LaserPointers.editRenderState(this.teleportRayHeadInvisible, "seat", teleportRenderStates[2]); + } + }; - this.run = function(controllerData, deltaTime) { - //_this.state = TELEPORTER_STATES.TARGETTING; + this.isReady = function(controllerData, deltaTime) { + var otherModule = this.getOtherModule(); + if (!this.disabled && this.buttonValue !== 0 && !otherModule.active) { + this.active = true; + this.enterTeleport(); + return makeRunningValues(true, [], []); + } + return makeRunningValues(false, [], []); + }; - // Get current hand pose information to see if the pose is valid - var pose = Controller.getPoseValue(handInfo[(_this.hand === RIGHT_HAND) ? 'right' : 'left'].controllerInput); - var mode = pose.valid ? _this.hand : 'head'; - if (!pose.valid) { + this.run = function(controllerData, deltaTime) { + + // Get current hand pose information to see if the pose is valid + var pose = Controller.getPoseValue(handInfo[(_this.hand === RIGHT_HAND) ? 'right' : 'left'].controllerInput); + var mode = pose.valid ? _this.hand : 'head'; + if (!pose.valid) { + LaserPointers.disableLaserPointer(_this.teleportRayHandVisible); + LaserPointers.disableLaserPointer(_this.teleportRayHandInvisible); + LaserPointers.enableLaserPointer(_this.teleportRayHeadVisible); + LaserPointers.enableLaserPointer(_this.teleportRayHeadInvisible); + } else { + LaserPointers.enableLaserPointer(_this.teleportRayHandVisible); + LaserPointers.enableLaserPointer(_this.teleportRayHandInvisible); + LaserPointers.disableLaserPointer(_this.teleportRayHeadVisible); + LaserPointers.disableLaserPointer(_this.teleportRayHeadInvisible); + } + + // We do up to 2 ray picks to find a teleport location. + // There are 2 types of teleport locations we are interested in: + // 1. A visible floor. This can be any entity surface that points within some degree of "up" + // 2. A seat. The seat can be visible or invisible. + // + // * In the first pass we pick against visible and invisible entities so that we can find invisible seats. + // We might hit an invisible entity that is not a seat, so we need to do a second pass. + // * In the second pass we pick against visible entities only. + // + var result; + if (mode === 'head') { + result = LaserPointers.getPrevRayPickResult(_this.teleportRayHeadInvisible); + } else { + result = LaserPointers.getPrevRayPickResult(_this.teleportRayHandInvisible); + } + + var teleportLocationType = getTeleportTargetType(result); + if (teleportLocationType === TARGET.INVISIBLE) { + if (mode === 'head') { + result = LaserPointers.getPrevRayPickResult(_this.teleportRayHeadVisible); + } else { + result = LaserPointers.getPrevRayPickResult(_this.teleportRayHandVisible); + } + teleportLocationType = getTeleportTargetType(result); + } + + if (teleportLocationType === TARGET.NONE) { + // Use the cancel default state + this.setTeleportState(mode, "cancel", ""); + } else if (teleportLocationType === TARGET.INVALID || teleportLocationType === TARGET.INVISIBLE) { + this.setTeleportState(mode, "", "cancel"); + } else if (teleportLocationType === TARGET.SURFACE) { + if (this.state === TELEPORTER_STATES.COOL_IN) { + this.setTeleportState(mode, "cancel", ""); + } else { + this.setTeleportState(mode, "teleport", ""); + } + } else if (teleportLocationType === TARGET.SEAT) { + this.setTeleportState(mode, "", "seat"); + } + return this.teleport(result, teleportLocationType); + }; + + this.teleport = function(newResult, target) { + var result = newResult; + if (_this.buttonValue !== 0) { + return makeRunningValues(true, [], []); + } + + if (target === TARGET.NONE || target === TARGET.INVALID || this.state === TELEPORTER_STATES.COOL_IN) { + // Do nothing + } else if (target === TARGET.SEAT) { + Entities.callEntityMethod(result.objectID, 'sit'); + } else if (target === TARGET.SURFACE) { + var offset = getAvatarFootOffset(); + result.intersection.y += offset; + MyAvatar.goToLocation(result.intersection, false, {x: 0, y: 0, z: 0, w: 1}, false); + HMD.centerUI(); + MyAvatar.centerBody(); + } + + this.disableLasers(); + this.active = false; + return makeRunningValues(false, [], []); + }; + + this.disableLasers = function() { LaserPointers.disableLaserPointer(_this.teleportRayHandVisible); LaserPointers.disableLaserPointer(_this.teleportRayHandInvisible); - LaserPointers.enableLaserPointer(_this.teleportRayHeadVisible); - LaserPointers.enableLaserPointer(_this.teleportRayHeadInvisible); - } else { - LaserPointers.enableLaserPointer(_this.teleportRayHandVisible); - LaserPointers.enableLaserPointer(_this.teleportRayHandInvisible); LaserPointers.disableLaserPointer(_this.teleportRayHeadVisible); LaserPointers.disableLaserPointer(_this.teleportRayHeadInvisible); - } + }; - // We do up to 2 ray picks to find a teleport location. - // There are 2 types of teleport locations we are interested in: - // 1. A visible floor. This can be any entity surface that points within some degree of "up" - // 2. A seat. The seat can be visible or invisible. - // - // * In the first pass we pick against visible and invisible entities so that we can find invisible seats. - // We might hit an invisible entity that is not a seat, so we need to do a second pass. - // * In the second pass we pick against visible entities only. - // - var result; - if (mode === 'head') { - result = LaserPointers.getPrevRayPickResult(_this.teleportRayHeadInvisible); - } else { - result = LaserPointers.getPrevRayPickResult(_this.teleportRayHandInvisible); - } - - var teleportLocationType = getTeleportTargetType(result); - if (teleportLocationType === TARGET.INVISIBLE) { + this.setTeleportState = function(mode, visibleState, invisibleState) { if (mode === 'head') { - result = LaserPointers.getPrevRayPickResult(_this.teleportRayHeadVisible); + LaserPointers.setRenderState(_this.teleportRayHeadVisible, visibleState); + LaserPointers.setRenderState(_this.teleportRayHeadInvisible, invisibleState); } else { - result = LaserPointers.getPrevRayPickResult(_this.teleportRayHandVisible); + LaserPointers.setRenderState(_this.teleportRayHandVisible, visibleState); + LaserPointers.setRenderState(_this.teleportRayHandInvisible, invisibleState); } - teleportLocationType = getTeleportTargetType(result); - } + }; - if (teleportLocationType === TARGET.NONE) { - // Use the cancel default state - this.setTeleportState(mode, "cancel", ""); - } else if (teleportLocationType === TARGET.INVALID || teleportLocationType === TARGET.INVISIBLE) { - this.setTeleportState(mode, "", "cancel"); - } else if (teleportLocationType === TARGET.SURFACE) { - if (this.state === TELEPORTER_STATES.COOL_IN) { - this.setTeleportState(mode, "cancel", ""); - } else { - this.setTeleportState(mode, "teleport", ""); - } - } else if (teleportLocationType === TARGET.SEAT) { - this.setTeleportState(mode, "", "seat"); - } - return this.teleport(result, teleportLocationType); - }; - - this.teleport = function(newResult, target) { - var result = newResult; - if (_this.buttonValue !== 0) { - return makeRunningValues(true, [], []); - } - - if (target === TARGET.NONE || target === TARGET.INVALID || this.state === TELEPORTER_STATES.COOL_IN) { - // Do nothing - } else if (target === TARGET.SEAT) { - Entities.callEntityMethod(result.objectID, 'sit'); - } else if (target === TARGET.SURFACE) { - var offset = getAvatarFootOffset(); - result.intersection.y += offset; - MyAvatar.goToLocation(result.intersection, false, {x: 0, y: 0, z: 0, w: 1}, false); - HMD.centerUI(); - MyAvatar.centerBody(); - } - - this.disableLasers(); - this.active = false; - return makeRunningValues(false, [], []); - }; - - this.disableLasers = function() { - LaserPointers.disableLaserPointer(_this.teleportRayHandVisible); - LaserPointers.disableLaserPointer(_this.teleportRayHandInvisible); - LaserPointers.disableLaserPointer(_this.teleportRayHeadVisible); - LaserPointers.disableLaserPointer(_this.teleportRayHeadInvisible); - }; - - this.setTeleportState = function(mode, visibleState, invisibleState) { - if (mode === 'head') { - LaserPointers.setRenderState(_this.teleportRayHeadVisible, visibleState); - LaserPointers.setRenderState(_this.teleportRayHeadInvisible, invisibleState); - } else { - LaserPointers.setRenderState(_this.teleportRayHandVisible, visibleState); - LaserPointers.setRenderState(_this.teleportRayHandInvisible, invisibleState); - } - }; -} + this.setIgnoreEntities = function(entitiesToIgnore) { + LaserPointers.setIgnoreEntities(this.teleportRayHandVisible, entitiesToIgnore); + LaserPointers.setIgnoreEntities(this.teleportRayHandInvisible, entitiesToIgnore); + LaserPointers.setIgnoreEntities(this.teleportRayHeadVisible, entitiesToIgnore); + LaserPointers.setIgnoreEntities(this.teleportRayHeadInvisible, entitiesToIgnore); + }; + } // related to repositioning the avatar after you teleport var FOOT_JOINT_NAMES = ["RightToe_End", "RightToeBase", "RightFoot"]; var DEFAULT_ROOT_TO_FOOT_OFFSET = 0.5; function getAvatarFootOffset() { - + // find a valid foot jointIndex var footJointIndex = -1; var i, l = FOOT_JOINT_NAMES.length; for (i = 0; i < l; i++) { footJointIndex = MyAvatar.getJointIndex(FOOT_JOINT_NAMES[i]); - if (footJointIndex != -1) { + if (footJointIndex !== -1) { break; } } - if (footJointIndex != -1) { + if (footJointIndex !== -1) { // default vertical offset from foot to avatar root. var footPos = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(footJointIndex); if (footPos.x === 0 && footPos.y === 0 && footPos.z === 0.0) { @@ -417,23 +382,8 @@ function Teleporter(hand) { } } - var leftPad = new ThumbPad('left'); - var rightPad = new ThumbPad('right'); - var mappingName, teleportMapping; - var TELEPORT_DELAY = 0; - - function isMoving() { - var LY = Controller.getValue(Controller.Standard.LY); - var LX = Controller.getValue(Controller.Standard.LX); - if (LY !== 0 || LX !== 0) { - return true; - } else { - return false; - } - } - function parseJSON(json) { try { return JSON.parse(json); @@ -447,10 +397,10 @@ function Teleporter(hand) { // you can't teleport there. var MAX_ANGLE_FROM_UP_TO_TELEPORT = 70; function getTeleportTargetType(result) { - if (result.type == RayPick.INTERSECTED_NONE) { + if (result.type === RayPick.INTERSECTED_NONE) { return TARGET.NONE; } - + var props = Entities.getEntityProperties(result.objectID, ['userData', 'visible']); var data = parseJSON(props.userData); if (data !== undefined && data.seat !== undefined) { @@ -472,7 +422,7 @@ function Teleporter(hand) { if (angleUp < (90 - MAX_ANGLE_FROM_UP_TO_TELEPORT) || angleUp > (90 + MAX_ANGLE_FROM_UP_TO_TELEPORT) || - Vec3.distance(MyAvatar.position, result.intersection) <= TELEPORT_CANCEL_RANGE) { + Vec3.distance(MyAvatar.position, result.intersection) <= TELEPORT_CANCEL_RANGE * MyAvatar.sensorToWorldScale) { return TARGET.INVALID; } else { return TARGET.SURFACE; @@ -482,7 +432,7 @@ function Teleporter(hand) { function registerMappings() { mappingName = 'Hifi-Teleporter-Dev-' + Math.random(); teleportMapping = Controller.newMapping(mappingName); - + teleportMapping.from(Controller.Standard.RightPrimaryThumb).peek().to(rightTeleporter.buttonPress); teleportMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(leftTeleporter.buttonPress); } @@ -502,41 +452,37 @@ function Teleporter(hand) { } Script.scriptEnding.connect(cleanup); - var setIgnoreEntities = function() { - LaserPointers.setIgnoreEntities(teleporter.teleportRayRightVisible, ignoredEntities); - LaserPointers.setIgnoreEntities(teleporter.teleportRayRightInvisible, ignoredEntities); - LaserPointers.setIgnoreEntities(teleporter.teleportRayLeftVisible, ignoredEntities); - LaserPointers.setIgnoreEntities(teleporter.teleportRayLeftInvisible, ignoredEntities); - LaserPointers.setIgnoreEntities(teleporter.teleportRayHeadVisible, ignoredEntities); - LaserPointers.setIgnoreEntities(teleporter.teleportRayHeadInvisible, ignoredEntities); - }; - - var isDisabled = false; var handleTeleportMessages = function(channel, message, sender) { if (sender === MyAvatar.sessionUUID) { if (channel === 'Hifi-Teleport-Disabler') { if (message === 'both') { - isDisabled = 'both'; + leftTeleporter.disabled = true; + rightTeleporter.disabled = true; } if (message === 'left') { - isDisabled = 'left'; + leftTeleporter.disabled = true; + rightTeleporter.disabled = false; } if (message === 'right') { - isDisabled = 'right'; + leftTeleporter.disabled = false; + rightTeleporter.disabled = true; } if (message === 'none') { - isDisabled = false; + leftTeleporter.disabled = false; + rightTeleporter.disabled = false; } } else if (channel === 'Hifi-Teleport-Ignore-Add' && !Uuid.isNull(message) && ignoredEntities.indexOf(message) === -1) { ignoredEntities.push(message); - setIgnoreEntities(); + leftTeleporter.setIgnoreEntities(ignoredEntities); + rightTeleporter.setIgnoreEntities(ignoredEntities); } else if (channel === 'Hifi-Teleport-Ignore-Remove' && !Uuid.isNull(message)) { var removeIndex = ignoredEntities.indexOf(message); if (removeIndex > -1) { ignoredEntities.splice(removeIndex, 1); - setIgnoreEntities(); + leftTeleporter.setIgnoreEntities(ignoredEntities); + rightTeleporter.setIgnoreEntities(ignoredEntities); } } } diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 6b140173b8..2b4bc7e1fb 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -12,7 +12,6 @@ var CONTOLLER_SCRIPTS = [ "squeezeHands.js", "controllerDisplayManager.js", - "handControllerPointer.js", "grab.js", "toggleAdvancedMovementForHandControllers.js", "controllerDispatcher.js", @@ -30,6 +29,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/farTrigger.js", "controllerModules/teleport.js", "controllerModules/scaleAvatar.js", + "controllerModules/hudOverlayPointer.js", + "controllerModules/mouseHMD.js", "controllerModules/scaleEntity.js" ]; diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js deleted file mode 100644 index 832fe10d5f..0000000000 --- a/scripts/system/controllers/handControllerPointer.js +++ /dev/null @@ -1,706 +0,0 @@ -"use strict"; - -// -// handControllerPointer.js -// examples/controllers -// -// Created by Howard Stearns on 2016/04/22 -// Copyright 2016 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 -// - -(function() { // BEGIN LOCAL_SCOPE - -// Control the "mouse" using hand controller. (HMD and desktop.) -// First-person only. -// Starts right handed, but switches to whichever is free: Whichever hand was NOT most recently squeezed. -// (For now, the thumb buttons on both controllers are always on.) -// When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand -// controller beam intersects the HUD. - -var activeTrigger; -function isLaserOn() { - return activeTrigger.partial(); -} -Script.include("../libraries/controllers.js"); - -// UTILITIES ------------- -// -function ignore() { } - -// Utility to make it easier to setup and disconnect cleanly. -function setupHandler(event, handler) { - event.connect(handler); - Script.scriptEnding.connect(function () { - event.disconnect(handler); - }); -} - -// If some capability is not available until expiration milliseconds after the last update. -function TimeLock(expiration) { - var last = 0; - this.update = function (optionalNow) { - last = optionalNow || Date.now(); - }; - this.expired = function (optionalNow) { - return ((optionalNow || Date.now()) - last) > expiration; - }; -} - -var handControllerLockOut = new TimeLock(2000); - -function Trigger(label) { - // This part is copied and adapted from handControllerGrab.js. Maybe we should refactor this. - var that = this; - that.label = label; - that.TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing - that.TRIGGER_OFF_VALUE = 0.10; - that.TRIGGER_ON_VALUE = that.TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab - that.rawTriggerValue = 0; - that.triggerValue = 0; // rolling average of trigger value - that.triggerClicked = false; - that.triggerClick = function (value) { that.triggerClicked = value; }; - that.triggerPress = function (value) { that.rawTriggerValue = value; }; - that.updateSmoothedTrigger = function () { // e.g., call once/update for effect - var triggerValue = that.rawTriggerValue; - // smooth out trigger value - that.triggerValue = (that.triggerValue * that.TRIGGER_SMOOTH_RATIO) + - (triggerValue * (1.0 - that.TRIGGER_SMOOTH_RATIO)); - OffscreenFlags.navigationFocusDisabled = that.triggerValue != 0.0; - }; - // Current smoothed state, without hysteresis. Answering booleans. - that.triggerSmoothedClick = function () { - return that.triggerClicked; - }; - that.triggerSmoothedSqueezed = function () { - return that.triggerValue > that.TRIGGER_ON_VALUE; - }; - that.triggerSmoothedReleased = function () { - return that.triggerValue < that.TRIGGER_OFF_VALUE; - }; - - // This part is not from handControllerGrab.js - that.state = null; // tri-state: falsey, 'partial', 'full' - that.update = function () { // update state, called from an update function - var state = that.state; - that.updateSmoothedTrigger(); - - // The first two are independent of previous state: - if (that.triggerSmoothedClick()) { - state = 'full'; - } else if (that.triggerSmoothedReleased()) { - state = null; - } else if (that.triggerSmoothedSqueezed()) { - // Another way to do this would be to have hysteresis in this branch, but that seems to make things harder to use. - // In particular, the vive has a nice detent as you release off of full, and we want that to be a transition from - // full to partial. - state = 'partial'; - } - that.state = state; - }; - // Answer a controller source function (answering either 0.0 or 1.0). - that.partial = function () { - return that.state ? 1.0 : 0.0; // either 'partial' or 'full' - }; - that.full = function () { - return (that.state === 'full') ? 1.0 : 0.0; - }; -} - -// VERTICAL FIELD OF VIEW --------- -// -// Cache the verticalFieldOfView setting and update it every so often. -var verticalFieldOfView, DEFAULT_VERTICAL_FIELD_OF_VIEW = 45; // degrees -function updateFieldOfView() { - verticalFieldOfView = Settings.getValue('fieldOfView') || DEFAULT_VERTICAL_FIELD_OF_VIEW; -} - -// SHIMS ---------- -// -var weMovedReticle = false; -function ignoreMouseActivity() { - // If we're paused, or if change in cursor position is from this script, not the hardware mouse. - if (!Reticle.allowMouseCapture) { - return true; - } - var pos = Reticle.position; - if (!pos || (pos.x == -1 && pos.y == -1)) { - return true; - } - // Only we know if we moved it, which is why this script has to replace depthReticle.js - if (!weMovedReticle) { - return false; - } - weMovedReticle = false; - return true; -} -var MARGIN = 25; -var reticleMinX = MARGIN, reticleMaxX, reticleMinY = MARGIN, reticleMaxY; -function updateRecommendedArea() { - var dims = Controller.getViewportDimensions(); - reticleMaxX = dims.x - MARGIN; - reticleMaxY = dims.y - MARGIN; -} -var setReticlePosition = function (point2d) { - weMovedReticle = true; - point2d.x = Math.max(reticleMinX, Math.min(point2d.x, reticleMaxX)); - point2d.y = Math.max(reticleMinY, Math.min(point2d.y, reticleMaxY)); - Reticle.setPosition(point2d); -}; - -// VISUAL AID ----------- -// Same properties as handControllerGrab search sphere -var LASER_ALPHA = 0.5; -var LASER_SEARCH_COLOR = {red: 10, green: 10, blue: 255}; -var LASER_TRIGGER_COLOR = {red: 250, green: 10, blue: 10}; -var END_DIAMETER = 0.05; -var systemLaserOn = false; - -var triggerPath = { - type: "line3d", - color: LASER_TRIGGER_COLOR, - ignoreRayIntersection: true, - visible: true, - alpha: LASER_ALPHA, - solid: true, - glow: 1.0, - drawHUDLayer: true -} -var triggerEnd = { - type: "sphere", - dimensions: {x: END_DIAMETER, y: END_DIAMETER, z: END_DIAMETER}, - color: LASER_TRIGGER_COLOR, - ignoreRayIntersection: true, - visible: true, - alpha: LASER_ALPHA, - solid: true, - drawHUDLayer: true -} - -var searchPath = { - type: "line3d", - color: LASER_SEARCH_COLOR, - ignoreRayIntersection: true, - visible: true, - alpha: LASER_ALPHA, - solid: true, - glow: 1.0, - drawHUDLayer: true -} -var searchEnd = { - type: "sphere", - dimensions: {x: END_DIAMETER, y: END_DIAMETER, z: END_DIAMETER}, - color: LASER_SEARCH_COLOR, - ignoreRayIntersection: true, - visible: true, - alpha: LASER_ALPHA, - solid: true, - drawHUDLayer: true -} - -var hudRayStates = [{name: "trigger", path: triggerPath, end: triggerEnd}, - {name: "search", path: searchPath, end: searchEnd}]; -// this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp:378 -var GRAB_POINT_SPHERE_OFFSET_RIGHT = { x: 0.04, y: 0.13, z: 0.039 }; -var GRAB_POINT_SPHERE_OFFSET_LEFT = { x: -0.04, y: 0.13, z: 0.039 }; -var hudRayRight = LaserPointers.createLaserPointer({ - joint: "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", - filter: RayPick.PICK_HUD, - posOffset: GRAB_POINT_SPHERE_OFFSET_RIGHT, - renderStates: hudRayStates, - enabled: true -}); -var hudRayLeft = LaserPointers.createLaserPointer({ - joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", - filter: RayPick.PICK_HUD, - posOffset: GRAB_POINT_SPHERE_OFFSET_LEFT, - renderStates: hudRayStates, - enabled: true -}); - -// NOTE: keep this offset in sync with scripts/system/librarires/controllers.js:57 -var VERTICAL_HEAD_LASER_OFFSET = 0.1; -var hudRayHead = LaserPointers.createLaserPointer({ - joint: "Avatar", - filter: RayPick.PICK_HUD, - posOffset: {x: 0, y: VERTICAL_HEAD_LASER_OFFSET, z: 0}, - renderStates: hudRayStates, - enabled: true -}); - -var mouseRayPick = RayPick.createRayPick({ - joint: "Mouse", - filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, - enabled: true -}); - -function isPointingAtOverlay(optionalHudPosition2d) { - return Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(optionalHudPosition2d || Reticle.position); -} - -// Generalized HUD utilities, with or without HMD: -// This "var" is for documentation. Do not change the value! -var PLANAR_PERPENDICULAR_HUD_DISTANCE = 1; -function calculateRayUICollisionPoint(position, direction, isHands) { - // Answer the 3D intersection of the HUD by the given ray, or falsey if no intersection. - if (HMD.active) { - var laserPointer; - if (isHands) { - laserPointer = activeHand == Controller.Standard.RightHand ? hudRayRight : hudRayLeft; - } else { - laserPointer = hudRayHead; - } - var result = LaserPointers.getPrevRayPickResult(laserPointer); - if (result.type != RayPick.INTERSECTED_NONE) { - return result.intersection; - } else { - return null; - } - } - // interect HUD plane, 1m in front of camera, using formula: - // scale = hudNormal dot (hudPoint - position) / hudNormal dot direction - // intersection = postion + scale*direction - var hudNormal = Quat.getForward(Camera.getOrientation()); - var hudPoint = Vec3.sum(Camera.getPosition(), hudNormal); // must also scale if PLANAR_PERPENDICULAR_HUD_DISTANCE!=1 - var denominator = Vec3.dot(hudNormal, direction); - if (denominator === 0) { - return null; - } // parallel to plane - var numerator = Vec3.dot(hudNormal, Vec3.subtract(hudPoint, position)); - var scale = numerator / denominator; - return Vec3.sum(position, Vec3.multiply(scale, direction)); -} -var DEGREES_TO_HALF_RADIANS = Math.PI / 360; -function overlayFromWorldPoint(point) { - // Answer the 2d pixel-space location in the HUD that covers the given 3D point. - // REQUIRES: that the 3d point be on the hud surface! - // Note that this is based on the Camera, and doesn't know anything about any - // ray that may or may not have been used to compute the point. E.g., the - // overlay point is NOT the intersection of some non-camera ray with the HUD. - if (HMD.active) { - return HMD.overlayFromWorldPoint(point); - } - var cameraToPoint = Vec3.subtract(point, Camera.getPosition()); - var cameraX = Vec3.dot(cameraToPoint, Quat.getRight(Camera.getOrientation())); - var cameraY = Vec3.dot(cameraToPoint, Quat.getUp(Camera.getOrientation())); - var size = Controller.getViewportDimensions(); - var hudHeight = 2 * Math.tan(verticalFieldOfView * DEGREES_TO_HALF_RADIANS); // must adjust if PLANAR_PERPENDICULAR_HUD_DISTANCE!=1 - var hudWidth = hudHeight * size.x / size.y; - var horizontalFraction = (cameraX / hudWidth + 0.5); - var verticalFraction = 1 - (cameraY / hudHeight + 0.5); - var horizontalPixels = size.x * horizontalFraction; - var verticalPixels = size.y * verticalFraction; - return { x: horizontalPixels, y: verticalPixels }; -} - -var gamePad = Controller.findDevice("GamePad"); -function activeHudPoint2dGamePad() { - if (!HMD.active) { - return; - } - var headPosition = MyAvatar.getHeadPosition(); - var headDirection = Quat.getUp(Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 }))); - - var hudPoint3d = calculateRayUICollisionPoint(headPosition, headDirection, false); - - if (!hudPoint3d) { - if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here - print('Controller is parallel to HUD'); // so let us know that our assumptions are wrong. - } - return; - } - var hudPoint2d = overlayFromWorldPoint(hudPoint3d); - - // We don't know yet if we'll want to make the cursor or laser visble, but we need to move it to see if - // it's pointing at a QML tool (aka system overlay). - setReticlePosition(hudPoint2d); - - return hudPoint2d; -} - - -function activeHudPoint2d(activeHand) { // if controller is valid, update reticle position and answer 2d point. Otherwise falsey. - var controllerPose = getControllerWorldLocation(activeHand, true); // note: this will return head pose if hand pose is invalid (third eye) - if (!controllerPose.valid) { - return; // Controller is cradled. - } - var controllerPosition = controllerPose.position; - var controllerDirection = Quat.getUp(controllerPose.rotation); - - var hudPoint3d = calculateRayUICollisionPoint(controllerPosition, controllerDirection, true); - if (!hudPoint3d) { - if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here - print('Controller is parallel to HUD'); // so let us know that our assumptions are wrong. - } - return; - } - var hudPoint2d = overlayFromWorldPoint(hudPoint3d); - - // We don't know yet if we'll want to make the cursor or laser visble, but we need to move it to see if - // it's pointing at a QML tool (aka system overlay). - setReticlePosition(hudPoint2d); - return hudPoint2d; -} - -// MOUSE ACTIVITY -------- -// -var isSeeking = false; -var averageMouseVelocity = 0, lastIntegration = 0, lastMouse; -var WEIGHTING = 1 / 20; // simple moving average over last 20 samples -var ONE_MINUS_WEIGHTING = 1 - WEIGHTING; -var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 20; -function isShakingMouse() { // True if the person is waving the mouse around trying to find it. - var now = Date.now(), mouse = Reticle.position, isShaking = false; - if (lastIntegration && (lastIntegration !== now)) { - var velocity = Vec3.length(Vec3.subtract(mouse, lastMouse)) / (now - lastIntegration); - averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * velocity); - if (averageMouseVelocity > AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO) { - isShaking = true; - } - } - lastIntegration = now; - lastMouse = mouse; - return isShaking; -} -var NON_LINEAR_DIVISOR = 2; -var MINIMUM_SEEK_DISTANCE = 0.1; -function updateSeeking(doNotStartSeeking) { - if (!doNotStartSeeking && !isLaserOn() && (!Reticle.visible || isShakingMouse())) { - isSeeking = true; - } // e.g., if we're about to turn it on with first movement. - if (!isSeeking) { - return; - } - averageMouseVelocity = lastIntegration = 0; - var lookAt2D = HMD.getHUDLookAtPosition2D(); - if (!lookAt2D) { // If this happens, something has gone terribly wrong. - isSeeking = false; - return; // E.g., if parallel to location in HUD - } - var copy = Reticle.position; - function updateDimension(axis) { - var distanceBetween = lookAt2D[axis] - Reticle.position[axis]; - var move = distanceBetween / NON_LINEAR_DIVISOR; - if (Math.abs(move) < MINIMUM_SEEK_DISTANCE) { - return false; - } - copy[axis] += move; - return true; - } - var okX = !updateDimension('x'), okY = !updateDimension('y'); // Evaluate both. Don't short-circuit. - if (okX && okY) { - isSeeking = false; - } else { - Reticle.setPosition(copy); // Not setReticlePosition - } -} - -var mouseCursorActivity = new TimeLock(5000); -var APPARENT_MAXIMUM_DEPTH = 100.0; // this is a depth at which things all seem sufficiently distant -function updateMouseActivity(isClick) { - if (ignoreMouseActivity()) { - return; - } - var now = Date.now(); - mouseCursorActivity.update(now); - if (isClick) { - return; - } // Bug: mouse clicks should keep going. Just not hand controller clicks - handControllerLockOut.update(now); - Reticle.visible = true; -} -function expireMouseCursor(now) { - if (!isPointingAtOverlay() && mouseCursorActivity.expired(now)) { - Reticle.visible = false; - } -} -function hudReticleDistance() { // 3d distance from camera to the reticle position on hud - // (The camera is only in the center of the sphere on reset.) - var reticlePositionOnHUD = HMD.worldPointFromOverlay(Reticle.position); - return Vec3.distance(reticlePositionOnHUD, HMD.position); -} - -function maybeAdjustReticleDepth() { - if (HMD.active) { // set depth - if (isPointingAtOverlay()) { - Reticle.depth = hudReticleDistance(); - } - } -} -var ADJUST_RETICLE_DEPTH_INTERVAL = 50; // 20hz -Script.setInterval(maybeAdjustReticleDepth,ADJUST_RETICLE_DEPTH_INTERVAL); - -function onMouseMove() { - // Display cursor at correct depth (as in depthReticle.js), and updateMouseActivity. - if (ignoreMouseActivity()) { - return; - } - - if (HMD.active) { // set depth - updateSeeking(); - if (isPointingAtOverlay()) { - Reticle.depth = hudReticleDistance(); - } else { - var result = RayPick.getPrevRayPickResult(mouseRayPick); - Reticle.depth = result.intersects ? result.distance : APPARENT_MAXIMUM_DEPTH; - } - } - updateMouseActivity(); // After the above, just in case the depth movement is awkward when becoming visible. -} -function onMouseClick() { - updateMouseActivity(true); -} -setupHandler(Controller.mouseMoveEvent, onMouseMove); -setupHandler(Controller.mousePressEvent, onMouseClick); -setupHandler(Controller.mouseDoublePressEvent, onMouseClick); - -// CONTROLLER MAPPING --------- -// - -var leftTrigger = new Trigger('left'); -var rightTrigger = new Trigger('right'); -activeTrigger = rightTrigger; -var activeHand = Controller.Standard.RightHand; -var LEFT_HUD_LASER = 1; -var RIGHT_HUD_LASER = 2; -var BOTH_HUD_LASERS = LEFT_HUD_LASER + RIGHT_HUD_LASER; -var activeHudLaser = RIGHT_HUD_LASER; -function toggleHand() { // unequivocally switch which hand controls mouse position - if (activeHand === Controller.Standard.RightHand) { - activeHand = Controller.Standard.LeftHand; - activeTrigger = leftTrigger; - activeHudLaser = LEFT_HUD_LASER; - } else { - activeHand = Controller.Standard.RightHand; - activeTrigger = rightTrigger; - activeHudLaser = RIGHT_HUD_LASER; - } - clearSystemLaser(); -} -function makeToggleAction(hand) { // return a function(0|1) that makes the specified hand control mouse when 1 - return function (on) { - if (on && (activeHand !== hand)) { - toggleHand(); - } - }; -} - -var clickMapping = Controller.newMapping('handControllerPointer-click'); -Script.scriptEnding.connect(clickMapping.disable); - -// Gather the trigger data for smoothing. -clickMapping.from(Controller.Standard.RT).peek().to(rightTrigger.triggerPress); -clickMapping.from(Controller.Standard.LT).peek().to(leftTrigger.triggerPress); -clickMapping.from(Controller.Standard.RTClick).peek().to(rightTrigger.triggerClick); -clickMapping.from(Controller.Standard.LTClick).peek().to(leftTrigger.triggerClick); -// Full smoothed trigger is a click. -function isPointingAtOverlayStartedNonFullTrigger(trigger) { - // true if isPointingAtOverlay AND we were NOT full triggered when we became so. - // The idea is to not count clicks when we're full-triggering and reach the edge of a window. - var lockedIn = false; - return function () { - if (trigger !== activeTrigger) { - return lockedIn = false; - } - if (!isPointingAtOverlay()) { - return lockedIn = false; - } - if (lockedIn) { - return true; - } - lockedIn = !trigger.full(); - return lockedIn; - } -} -clickMapping.from(rightTrigger.full).when(isPointingAtOverlayStartedNonFullTrigger(rightTrigger)).to(Controller.Actions.ReticleClick); -clickMapping.from(leftTrigger.full).when(isPointingAtOverlayStartedNonFullTrigger(leftTrigger)).to(Controller.Actions.ReticleClick); -// The following is essentially like Left and Right versions of -// clickMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(Controller.Actions.ContextMenu); -// except that we first update the reticle position from the appropriate hand position, before invoking the . -var wantsMenu = 0; -clickMapping.from(function () { return wantsMenu; }).to(Controller.Actions.ContextMenu); -clickMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(function (clicked) { - if (clicked) { - activeHudPoint2d(Controller.Standard.RightHand); - Messages.sendLocalMessage("toggleHand", Controller.Standard.RightHand); - } - wantsMenu = clicked; -}); -clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { - if (clicked) { - activeHudPoint2d(Controller.Standard.LeftHand); - Messages.sendLocalMessage("toggleHand", Controller.Standard.LeftHand); - } - wantsMenu = clicked; -}); -clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { - if (clicked) { - activeHudPoint2dGamePad(); - var noHands = -1; - Messages.sendLocalMessage("toggleHand", Controller.Standard.LeftHand); - } - - wantsMenu = clicked; -}); -clickMapping.from(Controller.Hardware.Keyboard.RightMouseClicked).peek().to(function () { - // Allow the reticle depth to be set correctly: - // Wait a tick for the context menu to be displayed, and then simulate a (non-hand-controller) mouse move - // so that the system updates qml state (Reticle.pointingAtSystemOverlay) before it gives us a mouseMove. - // We don't want the system code to always do this for us, because, e.g., we do not want to get a mouseMove - // after the Left/RightSecondaryThumb gives us a context menu. Only from the mouse. - Script.setTimeout(function () { - var noHands = -1; - Messages.sendLocalMessage("toggleHand", noHands); - Reticle.setPosition(Reticle.position); - }, 0); -}); -// Partial smoothed trigger is activation. -clickMapping.from(rightTrigger.partial).to(makeToggleAction(Controller.Standard.RightHand)); -clickMapping.from(leftTrigger.partial).to(makeToggleAction(Controller.Standard.LeftHand)); -clickMapping.enable(); - -var HIFI_POINTER_DISABLE_MESSAGE_CHANNEL = "Hifi-Pointer-Disable"; -var isPointerEnabled = true; - -function clearSystemLaser() { - if (!systemLaserOn) { - return; - } - HMD.deactivateHMDHandMouse(); - LaserPointers.setRenderState(hudRayRight, ""); - LaserPointers.setRenderState(hudRayLeft, ""); - LaserPointers.setRenderState(hudRayHead, ""); - systemLaserOn = false; - weMovedReticle = true; -} -function setColoredLaser() { // answer trigger state if lasers supported, else falsey. - var mode = (activeTrigger.state === 'full') ? 'trigger' : 'search'; - - if (!systemLaserOn) { - HMD.activateHMDHandMouse(); - } - - var pose = Controller.getPoseValue(activeHand); - if (!pose.valid) { - LaserPointers.setRenderState(hudRayRight, ""); - LaserPointers.setRenderState(hudRayLeft, ""); - LaserPointers.setRenderState(hudRayHead, mode); - return true; - } - - var right = activeHand == Controller.Standard.RightHand; - LaserPointers.setRenderState(hudRayRight, right ? mode : ""); - LaserPointers.setRenderState(hudRayLeft, right ? "" : mode); - LaserPointers.setRenderState(hudRayHead, ""); - - return activeTrigger.state; -} - -// MAIN OPERATIONS ----------- -// -function update() { - var now = Date.now(); - function off() { - expireMouseCursor(); - clearSystemLaser(); - } - - updateSeeking(true); - if (!handControllerLockOut.expired(now)) { - return off(); // Let them use mouse in peace. - } - - if ((!Window.hasFocus() && !HMD.active) || !Reticle.allowMouseCapture) { - // In desktop it's pretty clear when another app is on top. In that case we bail, because - // hand controllers might be sputtering "valid" data and that will keep someone from deliberately - // using the mouse on another app. (Fogbugz case 546.) - // However, in HMD, you might not realize you're not on top, and you wouldn't be able to operate - // other apps anyway. So in that case, we DO keep going even though we're not on top. (Fogbugz 1831.) - return off(); // Don't mess with other apps or paused mouse activity - } - - leftTrigger.update(); - rightTrigger.update(); - if (!activeTrigger.state) { - return off(); // No trigger - } - - if (getGrabCommunications()) { - return off(); - } - - - var hudPoint2d = activeHudPoint2d(activeHand); - if (!hudPoint2d) { - return off(); - } - - // If there's a HUD element at the (newly moved) reticle, just make it visible and bail. - if (isPointingAtOverlay(hudPoint2d) && isPointerEnabled) { - if (HMD.active) { - Reticle.depth = hudReticleDistance(); - - var pose = Controller.getPoseValue(activeHand); - if (!pose.valid) { - var mode = (activeTrigger.state === 'full') ? 'trigger' : 'search'; - if (!systemLaserOn) { - HMD.activateHMDHandMouse(); - } - LaserPointers.setRenderState(hudRayHead, mode); - } - } - - if (activeTrigger.state && (!systemLaserOn || (systemLaserOn !== activeTrigger.state))) { // last=>wrong color - // If the active plugin doesn't implement hand lasers, show the mouse reticle instead. - systemLaserOn = setColoredLaser(); - Reticle.visible = !systemLaserOn; - } else if ((systemLaserOn || Reticle.visible) && !activeTrigger.state) { - clearSystemLaser(); - Reticle.visible = false; - } - return; - } - // We are not pointing at a HUD element (but it could be a 3d overlay). - clearSystemLaser(); - Reticle.visible = false; -} - -// Check periodically for changes to setup. -var SETTINGS_CHANGE_RECHECK_INTERVAL = 10 * 1000; // 10 seconds -function checkSettings() { - updateFieldOfView(); - updateRecommendedArea(); -} -checkSettings(); - -// Enable/disable pointer. -function handleMessages(channel, message, sender) { - if (sender === MyAvatar.sessionUUID && channel === HIFI_POINTER_DISABLE_MESSAGE_CHANNEL) { - var data = JSON.parse(message); - if (data.pointerEnabled !== undefined) { - print("pointerEnabled: " + data.pointerEnabled); - isPointerEnabled = data.pointerEnabled; - } - } -} - -Messages.subscribe(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL); -Messages.messageReceived.connect(handleMessages); - -var settingsChecker = Script.setInterval(checkSettings, SETTINGS_CHANGE_RECHECK_INTERVAL); -Script.update.connect(update); -Script.scriptEnding.connect(function () { - Messages.unsubscribe(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL); - Messages.messageReceived.disconnect(handleMessages); - Script.clearInterval(settingsChecker); - Script.update.disconnect(update); - OffscreenFlags.navigationFocusDisabled = false; - LaserPointers.removeLaserPointer(hudRayRight); - LaserPointers.removeLaserPointer(hudRayLeft); - LaserPointers.removeLaserPointer(hudRayHead); - HMD.deactivateHMDHandMouse(); -}); - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 569d4812dc..467fa95dd7 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -247,9 +247,12 @@ var toolBar = (function () { direction = MyAvatar.orientation; } direction = Vec3.multiplyQbyV(direction, Vec3.UNIT_Z); - + // Align entity with Avatar orientation. + properties.rotation = MyAvatar.orientation; + var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web"]; if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) { + // Adjust position of entity per bounding box prior to creating it. var registration = properties.registration; if (registration === undefined) { @@ -259,7 +262,14 @@ var toolBar = (function () { var orientation = properties.orientation; if (orientation === undefined) { - var DEFAULT_ORIENTATION = Quat.fromPitchYawRollDegrees(0, 0, 0); + properties.orientation = MyAvatar.orientation; + var DEFAULT_ORIENTATION = properties.orientation; + orientation = DEFAULT_ORIENTATION; + } else { + // If the orientation is already defined, we perform the corresponding rotation assuming that + // our start referential is the avatar referential. + properties.orientation = Quat.multiply(MyAvatar.orientation, properties.orientation); + var DEFAULT_ORIENTATION = properties.orientation; orientation = DEFAULT_ORIENTATION; } @@ -433,17 +443,8 @@ var toolBar = (function () { }); addButton("importEntitiesButton", "assets-01.svg", function() { - var importURL = null; - var fullPath = Window.browse("Select Model to Import", "", "*.json"); - if (fullPath) { - importURL = "file:///" + fullPath; - } - if (importURL) { - if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { - toolBar.toggle(); - } - importSVO(importURL); - } + Window.openFileChanged.connect(onFileOpenChanged); + Window.browseAsync("Select Model to Import", "", "*.json"); }); addButton("openAssetBrowserButton", "assets-01.svg", function() { @@ -1461,6 +1462,39 @@ function toggleSelectedEntitiesVisible() { } } +function onFileSaveChanged(filename) { + Window.saveFileChanged.disconnect(onFileSaveChanged); + if (filename !== "") { + var success = Clipboard.exportEntities(filename, selectionManager.selections); + if (!success) { + Window.notifyEditError("Export failed."); + } + } +} + +function onFileOpenChanged(filename) { + var importURL = null; + if (filename !== "") { + importURL = "file:///" + filename; + } + if (importURL) { + if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { + toolBar.toggle(); + } + importSVO(importURL); + } +} + +function onPromptTextChanged(prompt) { + Window.promptTextChanged.disconnect(onPromptTextChanged); + if (prompt !== "") { + if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { + toolBar.toggle(); + } + importSVO(prompt); + } +} + function handeMenuEvent(menuItem) { if (menuItem === "Allow Selecting of Small Models") { allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); @@ -1478,30 +1512,16 @@ function handeMenuEvent(menuItem) { if (!selectionManager.hasSelection()) { Window.notifyEditError("No entities have been selected."); } else { - var filename = Window.save("Select Where to Save", "", "*.json"); - if (filename) { - var success = Clipboard.exportEntities(filename, selectionManager.selections); - if (!success) { - Window.notifyEditError("Export failed."); - } - } + Window.saveFileChanged.connect(onFileSaveChanged); + Window.saveAsync("Select Where to Save", "", "*.json"); } } else if (menuItem === "Import Entities" || menuItem === "Import Entities from URL") { - var importURL = null; if (menuItem === "Import Entities") { - var fullPath = Window.browse("Select Model to Import", "", "*.json"); - if (fullPath) { - importURL = "file:///" + fullPath; - } + Window.openFileChanged.connect(onFileOpenChanged); + Window.browseAsync("Select Model to Import", "", "*.json"); } else { - importURL = Window.prompt("URL of SVO to import", ""); - } - - if (importURL) { - if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { - toolBar.toggle(); - } - importSVO(importURL); + Window.promptTextChanged.connect(onFileOpenChanged); + Window.promptAsync("URL of SVO to import", ""); } } else if (menuItem === "Entity List...") { entityListTool.toggleVisible(); diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index c5f8168c30..92a5857390 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -222,6 +222,10 @@ WebTablet.prototype.getTabletTextureResolution = function() { } }; +WebTablet.prototype.getLandscape = function() { + return this.landscape; +} + WebTablet.prototype.setLandscape = function(newLandscapeValue) { if (this.landscape === newLandscapeValue) { return; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index f7a08f3abb..e9e25b058b 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, +/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, NULL_UUID:true, AVATAR_SELF_ID:true, FORBIDDEN_GRAB_TYPES:true, HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true, DEFAULT_REGISTRATION_POINT:true, INCHES_TO_METERS:true, @@ -40,7 +40,12 @@ entityHasActions:true, ensureDynamic:true, findGroupParent:true, - BUMPER_ON_VALUE:true + BUMPER_ON_VALUE:true, + findHandChildEntities:true, + TEAR_AWAY_DISTANCE:true, + TEAR_AWAY_COUNT:true, + TEAR_AWAY_CHECK_TIME:true, + distanceBetweenPointAndEntityBoundingBox:true */ MSECS_PER_SEC = 1000.0; @@ -79,6 +84,10 @@ COLORS_GRAB_DISTANCE_HOLD = { red: 238, green: 75, blue: 214 }; NEAR_GRAB_RADIUS = 1.0; +TEAR_AWAY_DISTANCE = 0.1; // ungrab an entity if its bounding-box moves this far from the hand +TEAR_AWAY_COUNT = 2; // multiply by TEAR_AWAY_CHECK_TIME to know how long the item must be away +TEAR_AWAY_CHECK_TIME = 0.15; // seconds, duration between checks + DISPATCHER_PROPERTIES = [ "position", "registrationPoint", @@ -193,17 +202,6 @@ entityIsDistanceGrabbable = function(props) { return false; } - // XXX - // var distance = Vec3.distance(props.position, handPosition); - // this.otherGrabbingUUID = entityIsGrabbedByOther(entityID); - // if (this.otherGrabbingUUID !== null) { - // // don't distance grab something that is already grabbed. - // if (debug) { - // print("distance grab is skipping '" + props.name + "': already grabbed by another."); - // } - // return false; - // } - return true; }; @@ -296,8 +294,9 @@ ensureDynamic = function (entityID) { }; findGroupParent = function (controllerData, targetProps) { - while (targetProps.parentID && targetProps.parentID !== NULL_UUID) { - // XXX use controllerData.nearbyEntityPropertiesByID ? + while (targetProps.parentID && + targetProps.parentID !== NULL_UUID && + Entities.getNestableType(targetProps.parentID) == "entity") { var parentProps = Entities.getEntityProperties(targetProps.parentID, DISPATCHER_PROPERTIES); if (!parentProps) { break; @@ -310,6 +309,50 @@ findGroupParent = function (controllerData, targetProps) { return targetProps; }; + +findHandChildEntities = function(hand) { + // find children of avatar's hand joint + var handJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, handJointIndex); + children = children.concat(Entities.getChildrenIDsOfJoint(AVATAR_SELF_ID, handJointIndex)); + + // find children of faux controller joint + var controllerJointIndex = getControllerJointIndex(hand); + children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, controllerJointIndex)); + children = children.concat(Entities.getChildrenIDsOfJoint(AVATAR_SELF_ID, controllerJointIndex)); + + // find children of faux camera-relative controller joint + var controllerCRJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); + children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, controllerCRJointIndex)); + children = children.concat(Entities.getChildrenIDsOfJoint(AVATAR_SELF_ID, controllerCRJointIndex)); + + return children.filter(function (childID) { + var childType = Entities.getNestableType(childID); + return childType == "entity"; + }); +}; + +distanceBetweenPointAndEntityBoundingBox = function(point, entityProps) { + var entityXform = new Xform(entityProps.rotation, entityProps.position); + var localPoint = entityXform.inv().xformPoint(point); + var minOffset = Vec3.multiplyVbyV(entityProps.registrationPoint, entityProps.dimensions); + var maxOffset = Vec3.multiplyVbyV(Vec3.subtract(ONE_VEC, entityProps.registrationPoint), entityProps.dimensions); + var localMin = Vec3.subtract(entityXform.trans, minOffset); + var localMax = Vec3.sum(entityXform.trans, maxOffset); + + var v = {x: localPoint.x, y: localPoint.y, z: localPoint.z}; + v.x = Math.max(v.x, localMin.x); + v.x = Math.min(v.x, localMax.x); + v.y = Math.max(v.y, localMin.y); + v.y = Math.min(v.y, localMax.y); + v.z = Math.max(v.z, localMin.z); + v.z = Math.min(v.z, localMax.z); + + return Vec3.distance(v, localPoint); +}; + if (typeof module !== 'undefined') { module.exports = { makeDispatcherModuleParameters: makeDispatcherModuleParameters, @@ -319,11 +362,13 @@ if (typeof module !== 'undefined') { LEFT_HAND: LEFT_HAND, RIGHT_HAND: RIGHT_HAND, BUMPER_ON_VALUE: BUMPER_ON_VALUE, + TEAR_AWAY_DISTANCE: TEAR_AWAY_DISTANCE, propsArePhysical: propsArePhysical, entityIsGrabbable: entityIsGrabbable, NEAR_GRAB_RADIUS: NEAR_GRAB_RADIUS, projectOntoOverlayXYPlane: projectOntoOverlayXYPlane, - projectOntoEntityXYPlane: projectOntoEntityXYPlane - + projectOntoEntityXYPlane: projectOntoEntityXYPlane, + TRIGGER_OFF_VALUE: TRIGGER_OFF_VALUE, + TRIGGER_ON_VALUE: TRIGGER_ON_VALUE }; } diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 64a05fcebf..9d9689000e 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -108,6 +108,16 @@ EntityListTool = function(opts) { webView.emitScriptEvent(JSON.stringify(data)); }; + function onFileSaveChanged(filename) { + Window.saveFileChanged.disconnect(onFileSaveChanged); + if (filename !== "") { + var success = Clipboard.exportEntities(filename, selectionManager.selections); + if (!success) { + Window.notifyEditError("Export failed."); + } + } + } + webView.webEventReceived.connect(function(data) { try { data = JSON.parse(data); @@ -139,13 +149,8 @@ EntityListTool = function(opts) { if (!selectionManager.hasSelection()) { Window.notifyEditError("No entities have been selected."); } else { - var filename = Window.save("Select Where to Save", "", "*.json"); - if (filename) { - var success = Clipboard.exportEntities(filename, selectionManager.selections); - if (!success) { - Window.notifyEditError("Export failed."); - } - } + Window.saveFileChanged.connect(onFileSaveChanged); + Window.saveAsync("Select Where to Save", "", "*.json"); } } else if (data.type == "pal") { var sessionIds = {}; // Collect the sessionsIds of all selected entitities, w/o duplicates. diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 53cffa18d8..ffe93d13e8 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -249,7 +249,7 @@ noticeWidth = notice.width * NOTIFICATION_3D_SCALE + NOTIFICATION_3D_BUTTON_WIDTH; noticeHeight = notice.height * NOTIFICATION_3D_SCALE; - notice.size = { x: noticeWidth, y: noticeHeight}; + notice.size = { x: noticeWidth, y: noticeHeight }; positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y); diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index e5c60af77b..8f08b29cff 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -113,15 +113,8 @@ function onMessage(message) { openLoginWindow(); break; case 'chooseSnapshotLocation': - var snapshotPath = Window.browseDir("Choose Snapshots Directory", "", ""); - - if (snapshotPath) { // not cancelled - Snapshot.setSnapshotsLocation(snapshotPath); - tablet.emitScriptEvent(JSON.stringify({ - type: "snapshot", - action: "snapshotLocationChosen" - })); - } + Window.browseDirChanged.connect(snapshotDirChanged); + Window.browseDirAsync("Choose Snapshots Directory", "", ""); break; case 'openSettings': if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false)) @@ -572,6 +565,17 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { }); } +function snapshotDirChanged(snapshotPath) { + Window.browseDirChanged.disconnect(snapshotDirChanged); + if (snapshotPath !== "") { // not cancelled + Snapshot.setSnapshotsLocation(snapshotPath); + tablet.emitScriptEvent(JSON.stringify({ + type: "snapshot", + action: "snapshotLocationChosen" + })); + } +} + function processingGifStarted(pathStillSnapshot) { Window.processingGifStarted.disconnect(processingGifStarted); Window.processingGifCompleted.connect(processingGifCompleted); diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 63c1cc51aa..78b14eb7c6 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -97,7 +97,7 @@ checkTablet() tabletScalePercentage = getTabletScalePercentageFromSettings(); - UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml", + UIWebTablet = new WebTablet("hifi/tablet/TabletRoot.qml", DEFAULT_WIDTH * (tabletScalePercentage / 100), null, activeHand, true, null, false); UIWebTablet.register(); @@ -192,7 +192,9 @@ return; } - if (now - validCheckTime > MSECS_PER_SEC) { + var needInstantUpdate = UIWebTablet && UIWebTablet.getLandscape() !== landscape; + + if ((now - validCheckTime > MSECS_PER_SEC) || needInstantUpdate) { validCheckTime = now; updateTabletWidthFromSettings(); @@ -272,6 +274,36 @@ Messages.subscribe("home"); Messages.messageReceived.connect(handleMessage); + var clickMapping = Controller.newMapping('tabletToggle-click'); + var wantsMenu = 0; + clickMapping.from(function () { return wantsMenu; }).to(Controller.Actions.ContextMenu); + clickMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(function (clicked) { + if (clicked) { + //activeHudPoint2d(Controller.Standard.RightHand); + Messages.sendLocalMessage("toggleHand", Controller.Standard.RightHand); + } + wantsMenu = clicked; + }); + + clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { + if (clicked) { + //activeHudPoint2d(Controller.Standard.LeftHand); + Messages.sendLocalMessage("toggleHand", Controller.Standard.LeftHand); + } + wantsMenu = clicked; + }); + + clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { + if (clicked) { + //activeHudPoint2dGamePad(); + var noHands = -1; + Messages.sendLocalMessage("toggleHand", Controller.Standard.LeftHand); + } + + wantsMenu = clicked; + }); + clickMapping.enable(); + Script.setInterval(updateShowTablet, 100); Script.scriptEnding.connect(function () { diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index 9847e9f7b9..7c6886ad93 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -19,6 +19,7 @@ #include #include +#include #include @@ -114,13 +115,37 @@ public: } }; + + +const std::string VERTEX_SHADER_DEFINES{ R"GLSL( +#version 410 core +#define GPU_VERTEX_SHADER +#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 410 core +#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" }; + void testShaderBuild(const char* vs_src, const char * fs_src) { - auto vs = gpu::Shader::createVertex(std::string(vs_src)); - auto fs = gpu::Shader::createPixel(std::string(fs_src)); - auto pr = gpu::Shader::createProgram(vs, fs); - if (!gpu::Shader::makeProgram(*pr)) { + 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 QTestWindow::draw() { diff --git a/tools/oven/src/ui/BakeWidget.cpp b/tools/oven/src/ui/BakeWidget.cpp index 23a4822d82..9fb8f2f880 100644 --- a/tools/oven/src/ui/BakeWidget.cpp +++ b/tools/oven/src/ui/BakeWidget.cpp @@ -17,7 +17,7 @@ #include "BakeWidget.h" BakeWidget::BakeWidget(QWidget* parent, Qt::WindowFlags flags) : - QWidget(parent, flags) + QWidget(parent, flags) { } diff --git a/unpublishedScripts/marketplace/record/record.js b/unpublishedScripts/marketplace/record/record.js index 6110721ff9..68c7ea3f5a 100644 --- a/unpublishedScripts/marketplace/record/record.js +++ b/unpublishedScripts/marketplace/record/record.js @@ -486,6 +486,15 @@ return isFinishOnOpen; } + function onAssetsDirChanged(recording) { + Window.assetsDirChanged.disconnect(onAssetsDirChanged); + if (recording !== "") { + log("Load recording " + recording); + UserActivityLogger.logAction("record_load_recording", logDetails()); + Player.playRecording("atp:" + recording, MyAvatar.position, MyAvatar.orientation); + } + } + function onWebEventReceived(data) { var message, recording; @@ -520,12 +529,8 @@ break; case LOAD_RECORDING_ACTION: // User wants to select an ATP recording to play. - recording = Window.browseAssets("Select Recording to Play", "recordings", "*.hfr"); - if (recording) { - log("Load recording " + recording); - UserActivityLogger.logAction("record_load_recording", logDetails()); - Player.playRecording("atp:" + recording, MyAvatar.position, MyAvatar.orientation); - } + Window.assetsDirChanged.connect(onAssetsDirChanged); + Window.browseAssetsAsync("Select Recording to Play", "recordings", "*.hfr"); break; case START_RECORDING_ACTION: // Start making a recording.