diff --git a/.gitignore b/.gitignore index 5a965b494c..4c262f9f4a 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ android/**/bin android/**/src/main/res/values/libs.xml android/**/src/main/assets android/**/gradle* +*.class # VSCode # List taken from Github Global Ignores master@435c4d92 diff --git a/BUILD_QUEST.md b/BUILD_QUEST.md new file mode 100644 index 0000000000..e093969f83 --- /dev/null +++ b/BUILD_QUEST.md @@ -0,0 +1,65 @@ +Please read the [general build guide](BUILD.md) for information on building other platform. Only Quest specific instructions are found in this file. + +# Dependencies + +Building is currently supported on OSX, Windows and Linux platforms, but developers intending to do work on the library dependencies are strongly urged to use 64 bit Linux as a build platform + +You will need the following tools to build Android targets. + +* [Android Studio](https://developer.android.com/studio/index.html) + +### 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 levels 24 and 26. + +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 +* NDK (even if you have the NDK installed separately) + +Make sure the NDK installed version is 18 (or higher) + +# Environment + +Setting up the environment for android builds requires some additional steps + +#### Set up machine specific Gradle properties + +Create a `gradle.properties` file in $HOME/.gradle. Edit the file to contain the following + + HIFI_ANDROID_PRECOMPILED=/Android/hifi_externals + HIFI_ANDROID_KEYSTORE=/.jks + HIFI_ANDROID_KEYSTORE_PASSWORD= + HIFI_ANDROID_KEY_ALIAS= + HIFI_ANDROID_KEY_PASSWORD= + +Note, do not use `$HOME` for the path. It must be a fully qualified path name. + +### Setup the repository + +Clone the repository + +`git clone https://github.com/highfidelity/hifi.git` + +Enter the repository `android` directory + +`cd hifi/android` + +# Building & Running + +* Open Android Studio +* Choose _Open Existing Android Studio Project_ +* Navigate to the `hifi` repository and choose the `android` folder and select _OK_ +* Open Gradle.settings and comment out any projects not necessary +* From _File_ menu select _Sync with File System_ to resync Gradle settings +* From the _Build_ menu select _Make Project_ +* From +* Once the build completes, from the _Run_ menu select _Run App_ + diff --git a/CMakeLists.txt b/CMakeLists.txt index c8710eed05..c126dce56a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,13 @@ if (ANDROID) add_definitions(-DCUSTOM_DISPLAY_PLUGINS) set(PLATFORM_PLUGIN_LIBRARIES oculusMobile oculusMobilePlugin) endif() + + # Allow client code to use preprocessor macros to distinguish between quest and non-quest builds + if (${HIFI_ANDROID_APP} STREQUAL "questInterface") + add_definitions(-DANDROID_APP_QUEST_INTERFACE) + elseif(${HIFI_ANDROID_APP} STREQUAL "interface") + add_definitions(-DANDROID_APP_INTERFACE) + endif() else () set(PLATFORM_QT_COMPONENTS WebEngine Xml) endif () diff --git a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index a7bda3c29b..3cdb9f5a09 100644 --- a/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/apps/interface/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -81,6 +81,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW private boolean nativeEnterBackgroundCallEnqueued = false; private SlidingDrawer mWebSlidingDrawer; private boolean mStartInDomain; + private boolean isLoading; // private GvrApi gvrApi; // Opaque native pointer to the Application C++ object. // This object is owned by the InterfaceActivity instance and passed to the native methods. @@ -94,7 +95,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW @Override public void onCreate(Bundle savedInstanceState) { - super.isLoading = true; + isLoading = true; Intent intent = getIntent(); if (intent.hasExtra(DOMAIN_URL) && !TextUtils.isEmpty(intent.getStringExtra(DOMAIN_URL))) { intent.putExtra("applicationArguments", "--url " + intent.getStringExtra(DOMAIN_URL)); @@ -145,7 +146,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW @Override protected void onPause() { super.onPause(); - if (super.isLoading) { + if (isLoading) { nativeEnterBackgroundCallEnqueued = true; } else { nativeEnterBackground(); @@ -172,7 +173,6 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW super.onResume(); nativeEnterForeground(); surfacesWorkaround(); - keepInterfaceRunning = false; registerReceiver(headsetStateReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); //gvrApi.resumeTracking(); } @@ -382,7 +382,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW } public void onAppLoadedComplete() { - super.isLoading = false; + isLoading = false; if (nativeEnterBackgroundCallEnqueued) { nativeEnterBackground(); } @@ -413,7 +413,6 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW @Override public void onExpand() { - keepInterfaceRunning = true; } @Override diff --git a/android/apps/questFramePlayer/CMakeLists.txt b/android/apps/questFramePlayer/CMakeLists.txt index 5889585a6c..ea60e27a7d 100644 --- a/android/apps/questFramePlayer/CMakeLists.txt +++ b/android/apps/questFramePlayer/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME questFramePlayer) setup_hifi_library(AndroidExtras) link_hifi_libraries(shared ktx shaders gpu gl oculusMobile ${PLATFORM_GL_BACKEND}) -target_include_directories(${TARGET_NAME} PRIVATE ${HIFI_ANDROID_PRECOMPILED}/ovr/VrApi/Include) + target_link_libraries(${TARGET_NAME} android log m) target_opengl() target_oculus_mobile() diff --git a/android/apps/questFramePlayer/src/main/AndroidManifest.xml b/android/apps/questFramePlayer/src/main/AndroidManifest.xml index 721e8cee89..ba14b04532 100644 --- a/android/apps/questFramePlayer/src/main/AndroidManifest.xml +++ b/android/apps/questFramePlayer/src/main/AndroidManifest.xml @@ -19,24 +19,6 @@ android:name="org.qtproject.qt5.android.bindings.QtApplication" tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon"> - - - - - - - - - + + + + + + + diff --git a/android/apps/questFramePlayer/src/main/cpp/AndroidHelper.cpp b/android/apps/questFramePlayer/src/main/cpp/AndroidHelper.cpp new file mode 100644 index 0000000000..797040ab69 --- /dev/null +++ b/android/apps/questFramePlayer/src/main/cpp/AndroidHelper.cpp @@ -0,0 +1,30 @@ +// +// Created by Bradley Austin Davis on 2019/02/15 +// Copyright 2013-2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "AndroidHelper.h" + +#include +#include + +AndroidHelper::AndroidHelper() { +} + +AndroidHelper::~AndroidHelper() { +} + +void AndroidHelper::notifyLoadComplete() { + emit qtAppLoadComplete(); +} + +void AndroidHelper::notifyEnterForeground() { + emit enterForeground(); +} + +void AndroidHelper::notifyEnterBackground() { + emit enterBackground(); +} + diff --git a/android/apps/questFramePlayer/src/main/cpp/AndroidHelper.h b/android/apps/questFramePlayer/src/main/cpp/AndroidHelper.h new file mode 100644 index 0000000000..ef6722462c --- /dev/null +++ b/android/apps/questFramePlayer/src/main/cpp/AndroidHelper.h @@ -0,0 +1,43 @@ +// +// Created by Bradley Austin Davis on 2019/02/15 +// Copyright 2013-2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Android_Helper_h +#define hifi_Android_Helper_h + +#include +#include +#include +#include + +class AndroidHelper : public QObject { + Q_OBJECT +public: + AndroidHelper(AndroidHelper const&) = delete; + void operator=(AndroidHelper const&) = delete; + + static AndroidHelper& instance() { + static AndroidHelper instance; + return instance; + } + + void notifyLoadComplete(); + void notifyEnterForeground(); + void notifyEnterBackground(); + + +signals: + void qtAppLoadComplete(); + void enterForeground(); + void enterBackground(); + +private: + AndroidHelper(); + ~AndroidHelper(); +}; + +#endif diff --git a/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.cpp b/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.cpp index ec2986298e..8f78b1946a 100644 --- a/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.cpp +++ b/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.cpp @@ -11,15 +11,11 @@ #include PlayerWindow::PlayerWindow() { - installEventFilter(this); - setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint); + setFlags(Qt::Window); setSurfaceType(QSurface::OpenGLSurface); create(); showFullScreen(); // Ensure the window is visible and the GL context is valid QCoreApplication::processEvents(); - _renderThread.initialize(this); -} - -PlayerWindow::~PlayerWindow() { + _renderThread.initialize(); } diff --git a/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.h b/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.h index e4dd6cef43..5e7dc82781 100644 --- a/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.h +++ b/android/apps/questFramePlayer/src/main/cpp/PlayerWindow.h @@ -8,22 +8,13 @@ #pragma once #include -#include - -#include #include "RenderThread.h" -// Create a simple OpenGL window that renders text in various ways class PlayerWindow : public QWindow { public: PlayerWindow(); - virtual ~PlayerWindow(); - -protected: - //bool eventFilter(QObject* obj, QEvent* event) override; - //void keyPressEvent(QKeyEvent* event) override; + virtual ~PlayerWindow() {} private: - QSettings _settings; RenderThread _renderThread; }; diff --git a/android/apps/questFramePlayer/src/main/cpp/RenderThread.cpp b/android/apps/questFramePlayer/src/main/cpp/RenderThread.cpp index 5eabe6b9b1..78a4487284 100644 --- a/android/apps/questFramePlayer/src/main/cpp/RenderThread.cpp +++ b/android/apps/questFramePlayer/src/main/cpp/RenderThread.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -29,9 +30,7 @@ #include #include -static JNIEnv* _env { nullptr }; -static JavaVM* _vm { nullptr }; -static jobject _activity { nullptr }; +#include "AndroidHelper.h" struct HandController{ ovrInputTrackedRemoteCapabilities caps {}; @@ -48,21 +47,43 @@ struct HandController{ }; std::vector devices; +QAndroidJniObject __interfaceActivity; extern "C" { -JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *, void *) { +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void *) { __android_log_write(ANDROID_LOG_WARN, "QQQ", __FUNCTION__); return JNI_VERSION_1_6; } +JNIEXPORT void JNICALL +Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnCreate(JNIEnv *env, jobject obj) { + __android_log_print(ANDROID_LOG_INFO, "QQQ", __FUNCTION__); + __interfaceActivity = QAndroidJniObject(obj); + QObject::connect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, []() { + __interfaceActivity.callMethod("onAppLoadedComplete", "()V"); + QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, nullptr, nullptr); + }); +} -JNIEXPORT void JNICALL Java_io_highfidelity_frameplayer_QuestQtActivity_nativeOnCreate(JNIEnv* env, jobject obj) { - env->GetJavaVM(&_vm); - _activity = env->NewGlobalRef(obj); +JNIEXPORT void +Java_io_highfidelity_oculus_OculusMobileActivity_questOnAppAfterLoad(JNIEnv *env, jobject obj) { + AndroidHelper::instance().moveToThread(qApp->thread()); } + +JNIEXPORT void JNICALL +Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnPause(JNIEnv *env, jobject obj) { + AndroidHelper::instance().notifyEnterBackground(); } +JNIEXPORT void JNICALL +Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnResume(JNIEnv *env, jobject obj) { + AndroidHelper::instance().notifyEnterForeground(); +} + +} + + static const char* FRAME_FILE = "assets:/frames/20190121_1220.json"; static void textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer) { @@ -84,11 +105,10 @@ void RenderThread::move(const glm::vec3& v) { _correction = glm::inverse(glm::translate(mat4(), v)) * _correction; } -void RenderThread::initialize(QWindow* window) { +void RenderThread::initialize() { std::unique_lock lock(_frameLock); setObjectName("RenderThread"); Parent::initialize(); - _window = window; _thread->setObjectName("RenderThread"); } @@ -96,14 +116,7 @@ void RenderThread::setup() { // Wait until the context has been moved to this thread { std::unique_lock lock(_frameLock); } - ovr::VrHandler::initVr(); - __android_log_write(ANDROID_LOG_WARN, "QQQ", "Launching oculus activity"); - _vm->AttachCurrentThread(&_env, nullptr); - jclass cls = _env->GetObjectClass(_activity); - jmethodID mid = _env->GetMethodID(cls, "launchOculusActivity", "()V"); - _env->CallVoidMethod(_activity, mid); - __android_log_write(ANDROID_LOG_WARN, "QQQ", "Launching oculus activity done"); ovr::VrHandler::setHandler(this); makeCurrent(); @@ -169,7 +182,6 @@ void RenderThread::handleInput() { const auto &remote = controller.state; if (remote.Joystick.x != 0.0f || remote.Joystick.y != 0.0f) { glm::vec3 translation; - float rotation = 0.0f; if (caps.ControllerCapabilities & ovrControllerCaps_LeftHand) { translation = glm::vec3{0.0f, -remote.Joystick.y, 0.0f}; } else { diff --git a/android/apps/questFramePlayer/src/main/cpp/RenderThread.h b/android/apps/questFramePlayer/src/main/cpp/RenderThread.h index 701cd25f5b..747d0d9e8d 100644 --- a/android/apps/questFramePlayer/src/main/cpp/RenderThread.h +++ b/android/apps/questFramePlayer/src/main/cpp/RenderThread.h @@ -20,11 +20,9 @@ class RenderThread : public GenericThread, ovr::VrHandler { using Parent = GenericThread; public: - QWindow* _window{ nullptr }; std::mutex _mutex; gpu::ContextPointer _gpuContext; // initialized during window creation std::shared_ptr _backend; - std::atomic _presentCount{ 0 }; std::mutex _frameLock; std::queue _pendingFrames; gpu::FramePointer _activeFrame; @@ -39,6 +37,6 @@ public: void handleInput(); void submitFrame(const gpu::FramePointer& frame); - void initialize(QWindow* window); + void initialize(); void renderFrame(); }; diff --git a/android/apps/questFramePlayer/src/main/cpp/main.cpp b/android/apps/questFramePlayer/src/main/cpp/main.cpp index 4730d3fa15..123ba904f4 100644 --- a/android/apps/questFramePlayer/src/main/cpp/main.cpp +++ b/android/apps/questFramePlayer/src/main/cpp/main.cpp @@ -11,30 +11,33 @@ #include #include #include +#include #include #include "PlayerWindow.h" +#include "AndroidHelper.h" + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { if (!message.isEmpty()) { - const char * local=message.toStdString().c_str(); + const char* local = message.toStdString().c_str(); switch (type) { case QtDebugMsg: - __android_log_write(ANDROID_LOG_DEBUG,"Interface",local); + __android_log_write(ANDROID_LOG_DEBUG, "Interface", local); break; case QtInfoMsg: - __android_log_write(ANDROID_LOG_INFO,"Interface",local); + __android_log_write(ANDROID_LOG_INFO, "Interface", local); break; case QtWarningMsg: - __android_log_write(ANDROID_LOG_WARN,"Interface",local); + __android_log_write(ANDROID_LOG_WARN, "Interface", local); break; case QtCriticalMsg: - __android_log_write(ANDROID_LOG_ERROR,"Interface",local); + __android_log_write(ANDROID_LOG_ERROR, "Interface", local); break; case QtFatalMsg: default: - __android_log_write(ANDROID_LOG_FATAL,"Interface",local); + __android_log_write(ANDROID_LOG_FATAL, "Interface", local); abort(); } } @@ -46,11 +49,13 @@ int main(int argc, char** argv) { auto oldMessageHandler = qInstallMessageHandler(messageHandler); DependencyManager::set(); PlayerWindow window; - __android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec"); + QTimer::singleShot(10, []{ + __android_log_write(ANDROID_LOG_WARN, "QQQ", "notifyLoadComplete"); + AndroidHelper::instance().notifyLoadComplete(); + }); + __android_log_write(ANDROID_LOG_WARN, "QQQ", "Exec"); app.exec(); - __android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec done"); + __android_log_write(ANDROID_LOG_WARN, "QQQ", "Exec done"); qInstallMessageHandler(oldMessageHandler); return 0; } - - diff --git a/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestQtActivity.java b/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestQtActivity.java deleted file mode 100644 index d498e27547..0000000000 --- a/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestQtActivity.java +++ /dev/null @@ -1,53 +0,0 @@ -// -// Created by Bradley Austin Davis on 2018/11/20 -// Copyright 2013-2018 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 -// -package io.highfidelity.frameplayer; - -import android.content.Intent; -import android.os.Bundle; -import android.util.Log; - -import org.qtproject.qt5.android.bindings.QtActivity; - -import io.highfidelity.oculus.OculusMobileActivity; - - -public class QuestQtActivity extends QtActivity { - private native void nativeOnCreate(); - private boolean launchedQuestMode = false; - - @Override - public void onCreate(Bundle savedInstanceState) { - Log.w("QQQ_Qt", "QuestQtActivity::onCreate"); - super.onCreate(savedInstanceState); - nativeOnCreate(); - } - - @Override - public void onDestroy() { - Log.w("QQQ_Qt", "QuestQtActivity::onDestroy"); - super.onDestroy(); - } - - public void launchOculusActivity() { - Log.w("QQQ_Qt", "QuestQtActivity::launchOculusActivity"); - runOnUiThread(()->{ - keepInterfaceRunning = true; - launchedQuestMode = true; - moveTaskToBack(true); - startActivity(new Intent(this, QuestRenderActivity.class)); - }); - } - - @Override - public void onResume() { - super.onResume(); - if (launchedQuestMode) { - moveTaskToBack(true); - } - } -} diff --git a/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestRenderActivity.java b/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestRenderActivity.java index a395a32b68..9e7c0ab973 100644 --- a/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestRenderActivity.java +++ b/android/apps/questFramePlayer/src/main/java/io/highfidelity/frameplayer/QuestRenderActivity.java @@ -1,14 +1,6 @@ package io.highfidelity.frameplayer; -import android.content.Intent; -import android.os.Bundle; - import io.highfidelity.oculus.OculusMobileActivity; public class QuestRenderActivity extends OculusMobileActivity { - @Override - public void onCreate(Bundle savedState) { - super.onCreate(savedState); - startActivity(new Intent(this, QuestQtActivity.class)); - } } diff --git a/android/apps/questInterface/CMakeLists.txt b/android/apps/questInterface/CMakeLists.txt new file mode 100644 index 0000000000..97ca46f6e5 --- /dev/null +++ b/android/apps/questInterface/CMakeLists.txt @@ -0,0 +1,16 @@ +set(TARGET_NAME questInterface) +setup_hifi_library() +link_hifi_libraries( + shared task networking qml + image fbx hfm render-utils physics entities octree + oculusMobile oculusMobilePlugin + gl gpu ${PLATFORM_GL_BACKEND} +) +target_opengl() +target_bullet() +target_oculus_mobile() + +add_subdirectory("${CMAKE_SOURCE_DIR}/interface" "libraries/interface") +include_directories("${CMAKE_SOURCE_DIR}/interface/src") +add_subdirectory("${CMAKE_SOURCE_DIR}/plugins/hifiCodec" "libraries/hifiCodecPlugin") +target_link_libraries(questInterface android log m interface) diff --git a/android/apps/questInterface/build.gradle b/android/apps/questInterface/build.gradle new file mode 100644 index 0000000000..3d13877607 --- /dev/null +++ b/android/apps/questInterface/build.gradle @@ -0,0 +1,149 @@ +import org.apache.tools.ant.taskdefs.condition.Os +apply plugin: 'com.android.application' + +task renameHifiACTaskDebug() { + doLast { + def sourceFile = new File("${appDir}/build/intermediates/cmake/debug/obj/arm64-v8a/","libhifiCodec.so") + def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so") + copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) } + } +} +task renameHifiACTaskRelease(type: Copy) { + doLast { + def sourceFile = new File("${appDir}/build/intermediates/cmake/release/obj/arm64-v8a/","libhifiCodec.so") + def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so") + copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) } + } +} + +android { + compileSdkVersion 28 + + defaultConfig { + applicationId "io.highfidelity.questInterface" + minSdkVersion 24 + targetSdkVersion 28 + versionCode 1 + versionName appVersionName + ndk { abiFilters 'arm64-v8a' } + externalNativeBuild { + cmake { + arguments '-DHIFI_ANDROID=1', + '-DHIFI_ANDROID_APP=questInterface', + '-DANDROID_TOOLCHAIN=clang', + '-DANDROID_STL=c++_shared', + '-DCMAKE_VERBOSE_MAKEFILE=ON', + '-DRELEASE_NUMBER=' + RELEASE_NUMBER, + '-DRELEASE_TYPE=' + RELEASE_TYPE, + '-DSTABLE_BUILD=' + STABLE_BUILD, + '-DDISABLE_QML=OFF', + '-DDISABLE_KTX_CACHE=OFF', + '-DUSE_BREAKPAD=OFF' + targets = ['questInterface'] + } + } + signingConfigs { + release { + storeFile project.hasProperty("HIFI_ANDROID_KEYSTORE") ? file(HIFI_ANDROID_KEYSTORE) : null + storePassword project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") ? HIFI_ANDROID_KEYSTORE_PASSWORD : '' + keyAlias project.hasProperty("HIFI_ANDROID_KEY_ALIAS") ? HIFI_ANDROID_KEY_ALIAS : '' + keyPassword project.hasProperty("HIFI_ANDROID_KEY_PASSWORD") ? HIFI_ANDROID_KEY_PASSWORD : '' + v2SigningEnabled false + } + } + } + + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + buildTypes { + debug { + buildConfigField "String", "BACKTRACE_URL", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\"" + buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\"" + buildConfigField "String", "OAUTH_CLIENT_ID", "\"" + (System.getenv("OAUTH_CLIENT_ID") ? System.getenv("OAUTH_CLIENT_ID") : '') + "\"" + buildConfigField "String", "OAUTH_CLIENT_SECRET", "\"" + (System.getenv("OAUTH_CLIENT_SECRET") ? System.getenv("OAUTH_CLIENT_SECRET") : '') + "\"" + buildConfigField "String", "OAUTH_REDIRECT_URI", "\"" + (System.getenv("OAUTH_REDIRECT_URI") ? System.getenv("OAUTH_REDIRECT_URI") : '') + "\"" + } + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release + buildConfigField "String", "BACKTRACE_URL", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\"" + buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\"" + buildConfigField "String", "OAUTH_CLIENT_ID", "\"" + (System.getenv("OAUTH_CLIENT_ID") ? System.getenv("OAUTH_CLIENT_ID") : '') + "\"" + buildConfigField "String", "OAUTH_CLIENT_SECRET", "\"" + (System.getenv("OAUTH_CLIENT_SECRET") ? System.getenv("OAUTH_CLIENT_SECRET") : '') + "\"" + buildConfigField "String", "OAUTH_REDIRECT_URI", "\"" + (System.getenv("OAUTH_REDIRECT_URI") ? System.getenv("OAUTH_REDIRECT_URI") : '') + "\"" + } + } + + externalNativeBuild { + cmake { + path '../../../CMakeLists.txt' + } + } + + applicationVariants.all { variant -> + // Our asset contents depend on items produced in the CMake build + // so our merge has to depend on the external native build + variant.externalNativeBuildTasks.each { task -> + variant.mergeResources.dependsOn(task) + if (Os.isFamily(Os.FAMILY_UNIX)) { + // FIXME + def uploadDumpSymsTask = rootProject.getTasksByName("uploadBreakpadDumpSyms${variant.name.capitalize()}", false).first() + def runDumpSymsTask = rootProject.getTasksByName("runBreakpadDumpSyms${variant.name.capitalize()}", false).first() + def renameHifiACTask = rootProject.getTasksByName("renameHifiACTask${variant.name.capitalize()}", false).first() + runDumpSymsTask.dependsOn(task) + variant.assemble.dependsOn(uploadDumpSymsTask) + variant.mergeResources.dependsOn(renameHifiACTask) + } + } + + variant.mergeAssets.doLast { + def assetList = new LinkedList() + def youngestLastModified = 0 + + // Copy the compiled resources generated by the external native build + copy { + from new File(projectDir, "../../../interface/compiledResources") + into outputDir + duplicatesStrategy DuplicatesStrategy.INCLUDE + eachFile { details -> + youngestLastModified = Math.max(youngestLastModified, details.lastModified) + assetList.add(details.path) + } + } + + // Copy the scripts directory + copy { + from new File(projectDir, "../../../scripts") + into new File(outputDir, "scripts") + duplicatesStrategy DuplicatesStrategy.INCLUDE + eachFile { details-> + youngestLastModified = Math.max(youngestLastModified, details.lastModified) + assetList.add("scripts/" + details.path) + } + } + + // Write a list of files to be unpacked to the cache folder + new File(outputDir, 'cache_assets.txt').withWriter { out -> + out.println(Long.toString(youngestLastModified)) + assetList.each { file -> out.println(file) } + } + } + + variant.outputs.all { + if (RELEASE_NUMBER != '0') { + outputFileName = "app_" + RELEASE_NUMBER + "_" + RELEASE_TYPE + ".apk" + } + } + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs') + implementation project(':oculus') + implementation project(':qt') +} diff --git a/android/apps/questInterface/proguard-rules.pro b/android/apps/questInterface/proguard-rules.pro new file mode 100644 index 0000000000..b3c0078513 --- /dev/null +++ b/android/apps/questInterface/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/apps/questInterface/src/main/AndroidManifest.xml b/android/apps/questInterface/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5de47bdce --- /dev/null +++ b/android/apps/questInterface/src/main/AndroidManifest.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/apps/questInterface/src/main/cpp/native.cpp b/android/apps/questInterface/src/main/cpp/native.cpp new file mode 100644 index 0000000000..3c1563c93d --- /dev/null +++ b/android/apps/questInterface/src/main/cpp/native.cpp @@ -0,0 +1,106 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +void initOculusPlatform(JNIEnv* env, jobject obj) { + static std::once_flag once; + std::call_once(once, [&]{ + // static const char* appID = "2343652845669354"; + // if (ovr_PlatformInitializeAndroid(appID, obj, env) != ovrPlatformInitialize_Success) { + // __android_log_write(ANDROID_LOG_WARN, "QQQ", "Failed to init platform SDK"); + // return; + // } + // ovr_Voip_SetSystemVoipSuppressed(true); + }); +} + +void getClassName(JNIEnv *env, jobject obj){ + jclass cls = env->GetObjectClass(obj); + jmethodID mid = env->GetMethodID(cls,"getClass", "()Ljava/lang/Class;"); + jobject clsObj = env->CallObjectMethod(obj, mid); + + cls= env->GetObjectClass(clsObj); + + mid= env->GetMethodID(cls, "getName", "()Ljava/lang/String;"); + + jstring strObj = (jstring) env->CallObjectMethod(clsObj, mid); + + const char* str = env->GetStringUTFChars(strObj, NULL); + + __android_log_print(ANDROID_LOG_ERROR,__FUNCTION__, "Native Class call: %s",str); + + env->ReleaseStringUTFChars(strObj, str); +} + + +extern "C" { + JNIEXPORT void JNICALL + Java_io_highfidelity_oculus_OculusMobileActivity_nativeInitOculusPlatform(JNIEnv *env, jobject obj){ + initOculusPlatform(env, obj); + } +QAndroidJniObject __interfaceActivity; + + JNIEXPORT void JNICALL + Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnCreate(JNIEnv *env, jobject obj) { + __android_log_print(ANDROID_LOG_INFO, "QQQ", __FUNCTION__); + initOculusPlatform(env, obj); + getClassName(env, obj); + + __interfaceActivity = QAndroidJniObject(obj); + + QObject::connect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, []() { + __interfaceActivity.callMethod("onAppLoadedComplete", "()V"); + + QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, + nullptr, + nullptr); + }); + } + + + +JNIEXPORT void Java_io_highfidelity_oculus_OculusMobileActivity_questOnAppAfterLoad(JNIEnv* env, jobject obj) { + AndroidHelper::instance().moveToThread(qApp->thread()); +} + + JNIEXPORT void JNICALL + Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnPause(JNIEnv *env, jobject obj) { + AndroidHelper::instance().notifyEnterBackground(); + } + + JNIEXPORT void JNICALL + Java_io_highfidelity_oculus_OculusMobileActivity_questNativeOnResume(JNIEnv *env, jobject obj) { + AndroidHelper::instance().notifyEnterForeground(); + } + + JNIEXPORT void JNICALL + Java_io_highfidelity_questInterface_receiver_HeadsetStateReceiver_notifyHeadsetOn(JNIEnv *env, + jobject instance, + jboolean pluggedIn) { + AndroidHelper::instance().notifyHeadsetOn(pluggedIn); + } + +} diff --git a/android/apps/questInterface/src/main/java/io/highfidelity/questInterface/InterfaceActivity.java b/android/apps/questInterface/src/main/java/io/highfidelity/questInterface/InterfaceActivity.java new file mode 100644 index 0000000000..d55f97ad49 --- /dev/null +++ b/android/apps/questInterface/src/main/java/io/highfidelity/questInterface/InterfaceActivity.java @@ -0,0 +1,13 @@ +package io.highfidelity.questInterface; + +import android.os.Bundle; +import io.highfidelity.oculus.OculusMobileActivity; +import io.highfidelity.utils.HifiUtils; + +public class InterfaceActivity extends OculusMobileActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + HifiUtils.upackAssets(getAssets(), getCacheDir().getAbsolutePath()); + super.onCreate(savedInstanceState); + } +} diff --git a/android/apps/questInterface/src/main/java/io/highfidelity/questInterface/PermissionsChecker.java b/android/apps/questInterface/src/main/java/io/highfidelity/questInterface/PermissionsChecker.java new file mode 100644 index 0000000000..154435fcaf --- /dev/null +++ b/android/apps/questInterface/src/main/java/io/highfidelity/questInterface/PermissionsChecker.java @@ -0,0 +1,68 @@ +package io.highfidelity.questInterface; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; + +import io.highfidelity.oculus.OculusMobileActivity; +import io.highfidelity.utils.HifiUtils; + +public class PermissionsChecker extends Activity { + private static final int REQUEST_PERMISSIONS = 20; + private static final String TAG = PermissionsChecker.class.getName(); + private static final String[] REQUIRED_PERMISSIONS = new String[]{ + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.RECORD_AUDIO, + Manifest.permission.CAMERA + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestAppPermissions(REQUIRED_PERMISSIONS,REQUEST_PERMISSIONS); + } + + public void requestAppPermissions(final String[] requestedPermissions, + final int requestCode) { + int permissionCheck = PackageManager.PERMISSION_GRANTED; + boolean shouldShowRequestPermissionRationale = false; + for (String permission : requestedPermissions) { + permissionCheck = permissionCheck + checkSelfPermission(permission); + shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale || shouldShowRequestPermissionRationale(permission); + } + if (permissionCheck != PackageManager.PERMISSION_GRANTED) { + System.out.println("Permission was not granted. Ask for permissions"); + if (shouldShowRequestPermissionRationale) { + requestPermissions(requestedPermissions, requestCode); + } else { + requestPermissions(requestedPermissions, requestCode); + } + } else { + System.out.println("Launching the other activity.."); + launchActivityWithPermissions(); + } + } + + private void launchActivityWithPermissions() { + startActivity(new Intent(this, InterfaceActivity.class)); + finish(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + int permissionCheck = PackageManager.PERMISSION_GRANTED; + for (int permission : grantResults) { + permissionCheck = permissionCheck + permission; + } + if ((grantResults.length > 0) && permissionCheck == PackageManager.PERMISSION_GRANTED) { + launchActivityWithPermissions(); + } else if (grantResults.length > 0) { + System.out.println("User has deliberately denied Permissions. Launching anyways"); + launchActivityWithPermissions(); + } + } +} diff --git a/android/apps/questInterface/src/main/res/drawable/ic_launcher.xml b/android/apps/questInterface/src/main/res/drawable/ic_launcher.xml new file mode 100644 index 0000000000..03b1edc4e9 --- /dev/null +++ b/android/apps/questInterface/src/main/res/drawable/ic_launcher.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/android/apps/questInterface/src/main/res/values/strings.xml b/android/apps/questInterface/src/main/res/values/strings.xml new file mode 100644 index 0000000000..99e8d501ac --- /dev/null +++ b/android/apps/questInterface/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Interface + diff --git a/android/build.gradle b/android/build.gradle index ed2ca1c47e..5a4dbc0033 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -42,6 +42,8 @@ ext { RELEASE_TYPE = project.hasProperty('RELEASE_TYPE') ? project.getProperty('RELEASE_TYPE') : 'DEV' STABLE_BUILD = project.hasProperty('STABLE_BUILD') ? project.getProperty('STABLE_BUILD') : '0' EXEC_SUFFIX = Os.isFamily(Os.FAMILY_WINDOWS) ? '.exe' : '' + appVersionCode = Integer.valueOf(VERSION_CODE ?: 1) + appVersionName = RELEASE_NUMBER ?: "1.0" } def appDir = new File(projectDir, 'apps/interface') diff --git a/android/gradle.properties b/android/gradle.properties index ac639c5ae7..4236282c8b 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xms2g -Xmx4g +android.debug.obsoleteApi=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 36dba9b2f5..4c0f3c1035 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Dec 01 08:32:47 PST 2018 +#Wed Dec 19 13:46:46 PST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip diff --git a/android/libraries/oculus/build.gradle b/android/libraries/oculus/build.gradle index b072f99eb7..f31efcfe95 100644 --- a/android/libraries/oculus/build.gradle +++ b/android/libraries/oculus/build.gradle @@ -15,3 +15,7 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } } + +dependencies { + implementation project(path: ':qt') +} diff --git a/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java b/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java index 01d74ea94d..9ab07bb4dd 100644 --- a/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java +++ b/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java @@ -7,62 +7,65 @@ // package io.highfidelity.oculus; -import android.app.Activity; -import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; -import android.view.WindowManager; + +import org.qtproject.qt5.android.bindings.QtActivity; +import io.highfidelity.utils.HifiUtils; /** * Contains a native surface and forwards the activity lifecycle and surface lifecycle * events to the OculusMobileDisplayPlugin */ -public class OculusMobileActivity extends Activity implements SurfaceHolder.Callback { +public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Callback { private static final String TAG = OculusMobileActivity.class.getSimpleName(); static { System.loadLibrary("oculusMobile"); } + private native void nativeOnCreate(); private native static void nativeOnResume(); private native static void nativeOnPause(); - private native static void nativeOnDestroy(); private native static void nativeOnSurfaceChanged(Surface s); + private native void questNativeOnCreate(); + private native void questNativeOnPause(); + private native void questNativeOnResume(); + private native void questOnAppAfterLoad(); + + private SurfaceView mView; private SurfaceHolder mSurfaceHolder; - - public static void launch(Activity activity) { - if (activity != null) { - activity.runOnUiThread(()->{ - activity.startActivity(new Intent(activity, OculusMobileActivity.class)); - }); - } - } - - @Override public void onCreate(Bundle savedInstanceState) { - Log.w(TAG, "QQQ onCreate"); super.onCreate(savedInstanceState); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + Log.w(TAG, "QQQ onCreate"); // Create a native surface for VR rendering (Qt GL surfaces are not suitable // because of the lack of fine control over the surface callbacks) + // Forward the create message to the JNI code mView = new SurfaceView(this); - setContentView(mView); mView.getHolder().addCallback(this); - // Forward the create message to the JNI code nativeOnCreate(); + questNativeOnCreate(); + } + public void onAppLoadedComplete() { + Log.w(TAG, "QQQ Load Completed"); + runOnUiThread(() -> { + setContentView(mView); + questOnAppAfterLoad(); + }); } @Override protected void onDestroy() { Log.w(TAG, "QQQ onDestroy"); - if (mSurfaceHolder != null) { - nativeOnSurfaceChanged(null); - } - nativeOnDestroy(); + + nativeOnSurfaceChanged(null); + + Log.w(TAG, "QQQ onDestroy -- SUPER onDestroy"); super.onDestroy(); } @@ -70,19 +73,38 @@ public class OculusMobileActivity extends Activity implements SurfaceHolder.Call protected void onResume() { Log.w(TAG, "QQQ onResume"); super.onResume(); + //Reconnect the global reference back to handler + nativeOnCreate(); + + questNativeOnResume(); nativeOnResume(); } @Override protected void onPause() { Log.w(TAG, "QQQ onPause"); - nativeOnPause(); super.onPause(); + + questNativeOnPause(); + nativeOnPause(); + } + + @Override + protected void onStop(){ + super.onStop(); + Log.w(TAG, "QQQ Onstop called"); + } + + @Override + protected void onRestart(){ + super.onRestart(); + Log.w(TAG, "QQQ onRestart called ****"); + questOnAppAfterLoad(); } @Override public void surfaceCreated(SurfaceHolder holder) { - Log.w(TAG, "QQQ surfaceCreated"); + Log.w(TAG, "QQQ surfaceCreated ************************************"); nativeOnSurfaceChanged(holder.getSurface()); mSurfaceHolder = holder; } @@ -96,8 +118,9 @@ public class OculusMobileActivity extends Activity implements SurfaceHolder.Call @Override public void surfaceDestroyed(SurfaceHolder holder) { - Log.w(TAG, "QQQ surfaceDestroyed"); + Log.w(TAG, "QQQ surfaceDestroyed ***************************************************"); nativeOnSurfaceChanged(null); mSurfaceHolder = null; + } } \ No newline at end of file diff --git a/android/libraries/qt/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java b/android/libraries/qt/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java index 6a6688ac41..46f2af46e7 100644 --- a/android/libraries/qt/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java +++ b/android/libraries/qt/src/main/java/org/qtproject/qt5/android/bindings/QtActivity.java @@ -70,9 +70,6 @@ public class QtActivity extends Activity { public final String QT_ANDROID_DEFAULT_THEME = QT_ANDROID_THEMES[0]; // sets the default theme. private QtActivityLoader m_loader = new QtActivityLoader(this); - public boolean isLoading; - public boolean keepInterfaceRunning; - public QtActivity() { } @@ -229,10 +226,13 @@ public class QtActivity extends Activity { //--------------------------------------------------------------------------- protected void onCreateHook(Bundle savedInstanceState) { + m_loader.APPLICATION_PARAMETERS = APPLICATION_PARAMETERS; m_loader.ENVIRONMENT_VARIABLES = ENVIRONMENT_VARIABLES; m_loader.QT_ANDROID_THEMES = QT_ANDROID_THEMES; m_loader.QT_ANDROID_DEFAULT_THEME = QT_ANDROID_DEFAULT_THEME; + + m_loader.onCreate(savedInstanceState); } @@ -364,7 +364,10 @@ public class QtActivity extends Activity { @Override protected void onDestroy() { super.onDestroy(); - QtApplication.invokeDelegate(); + + QtNative.terminateQt(); + QtNative.setActivity(null,null); + System.exit(0); } //--------------------------------------------------------------------------- @@ -506,9 +509,9 @@ public class QtActivity extends Activity { super.onPause(); // GC: this trick allow us to show a splash activity until Qt app finishes // loading - if (!isLoading && !keepInterfaceRunning) { - QtApplication.invokeDelegate(); - } + //QtApplication.invokeDelegate(); + + //TODO(Amer): looking into why this messes up pause. } //--------------------------------------------------------------------------- @@ -647,11 +650,7 @@ public class QtActivity extends Activity { @Override protected void onStop() { super.onStop(); - if (!keepInterfaceRunning) { - QtApplication.invokeDelegate(); - } - QtNative.terminateQt(); - QtNative.setActivity(null,null); + QtApplication.invokeDelegate(); } //--------------------------------------------------------------------------- diff --git a/android/settings.gradle b/android/settings.gradle index 699f617cce..c7b70cfde2 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -12,15 +12,26 @@ project(':qt').projectDir = new File(settingsDir, 'libraries/qt') // Applications // -include ':interface' -project(':interface').projectDir = new File(settingsDir, 'apps/interface') +if (!getSettings().hasProperty("SUPPRESS_INTERFACE")) { + include ':interface' + project(':interface').projectDir = new File(settingsDir, 'apps/interface') +} + +if (!getSettings().hasProperty("SUPPRESS_QUEST_INTERFACE")) { + include ':questInterface' + project(':questInterface').projectDir = new File(settingsDir, 'apps/questInterface') +} // // Test projects // -include ':framePlayer' -project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer') +if (!getSettings().hasProperty("SUPPRESS_FRAME_PLAYER")) { + include ':framePlayer' + project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer') +} -include ':questFramePlayer' -project(':questFramePlayer').projectDir = new File(settingsDir, 'apps/questFramePlayer') +if (!getSettings().hasProperty("SUPPRESS_QUEST_FRAME_PLAYER")) { + include ':questFramePlayer' + project(':questFramePlayer').projectDir = new File(settingsDir, 'apps/questFramePlayer') +} diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index 64fbcc4ea6..c5bb2b4054 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -270,6 +270,16 @@ macro(AUTOSCRIBE_SHADER_LIBS) set(AUTOSCRIBE_SHADERGEN_COMMANDS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shadergen.txt) file(WRITE ${AUTOSCRIBE_SHADERGEN_COMMANDS_FILE} "${AUTOSCRIBE_SHADERGEN_COMMANDS}") + if (HIFI_ANDROID) + if ( + (${HIFI_ANDROID_APP} STREQUAL "questInterface") OR + (${HIFI_ANDROID_APP} STREQUAL "questFramePlayer") OR + (${HIFI_ANDROID_APP} STREQUAL "framePlayer") + ) + set(EXTRA_SHADERGEN_ARGS --extensions EXT_clip_cull_distance) + endif() + endif() + # A custom python script which will generate all our shader artifacts add_custom_command( OUTPUT ${SCRIBED_SHADERS} ${SPIRV_SHADERS} ${REFLECTED_SHADERS} @@ -279,6 +289,7 @@ macro(AUTOSCRIBE_SHADER_LIBS) --tools-dir ${VCPKG_TOOLS_DIR} --build-dir ${CMAKE_CURRENT_BINARY_DIR} --source-dir ${CMAKE_SOURCE_DIR} + ${EXTRA_SHADERGEN_ARGS} DEPENDS ${AUTOSCRIBE_SHADER_HEADERS} ${CMAKE_SOURCE_DIR}/tools/shadergen.py ${ALL_SCRIBE_SHADERS}) add_custom_target(shadergen DEPENDS ${SCRIBED_SHADERS} ${SPIRV_SHADERS} ${REFLECTED_SHADERS}) diff --git a/hifi_android.py b/hifi_android.py index 2e6a42d127..b8a606a82f 100644 --- a/hifi_android.py +++ b/hifi_android.py @@ -53,9 +53,9 @@ ANDROID_PACKAGES = { 'includeLibs': ['libvrapi.so'] }, 'oculusPlatform': { - 'file': 'OVRPlatformSDK_v1.32.0.zip', - 'versionId': 'jG9DB16zOGxSrmtZy4jcQnwO0TJUuaeL', - 'checksum': 'ab5b203b3a39a56ab148d68fff769e05', + 'file': 'OVRPlatformSDK_v1.34.0.zip', + 'versionId': 'vbRUkkyzUAXfTGSEtuiUr_7.Fm5h5BZk', + 'checksum': '16e4c5f39520f122bc49cb6d5bb88289', 'sharedLibFolder': 'Android/libs/arm64-v8a', 'includeLibs': ['libovrplatformloader.so'] }, diff --git a/interface/resources/controllers/touchscreenvirtualpad.json b/interface/resources/controllers/touchscreenvirtualpad.json index bae1172152..e98fb7ff2f 100644 --- a/interface/resources/controllers/touchscreenvirtualpad.json +++ b/interface/resources/controllers/touchscreenvirtualpad.json @@ -23,7 +23,9 @@ "invert" ], "to": "Actions.Pitch" - } + }, + + { "from": "TouchscreenVirtualPad.RB", "to": "Standard.RB"} ] } diff --git a/interface/resources/images/handshake.png b/interface/resources/images/handshake.png new file mode 100644 index 0000000000..ae4252e9e5 Binary files /dev/null and b/interface/resources/images/handshake.png differ diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index a1b89e1529..9a9252112c 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -425,11 +425,12 @@ FocusScope { console.warn("Could not find top level window for " + item); return; } - +/* if (typeof Controller === "undefined") { console.warn("Controller not yet available... can't center"); return; } +*/ var newRecommendedRectJS = (typeof Controller === "undefined") ? Qt.rect(0,0,0,0) : Controller.getRecommendedHUDRect(); var newRecommendedRect = Qt.rect(newRecommendedRectJS.x, newRecommendedRectJS.y, @@ -455,15 +456,17 @@ FocusScope { console.warn("Could not find top level window for " + item); return; } - +/* if (typeof Controller === "undefined") { console.warn("Controller not yet available... can't reposition targetWindow:" + targetWindow); return; } +*/ var oldRecommendedRect = recommendedRect; var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height }; - var newRecommendedRect = Controller.getRecommendedHUDRect(); + var newRecommendedRect = { width: 1280, height: 720, x: 0, y: 0 }; + if (typeof Controller !== "undefined") newRecommendedRect = Controller.getRecommendedHUDRect(); var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height }; repositionWindow(targetWindow, false, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions); } @@ -480,7 +483,8 @@ FocusScope { return; } - var recommended = Controller.getRecommendedHUDRect(); + var recommended = { width: 1280, height: 720, x: 0, y: 0 }; + if (typeof Controller !== "undefined") recommended = Controller.getRecommendedHUDRect(); var maxX = recommended.x + recommended.width; var maxY = recommended.y + recommended.height; var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y); diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index c44ebdbab1..a97d94d91c 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -18,8 +18,8 @@ OriginalDesktop.Desktop { hoverEnabled: true propagateComposedEvents: true scrollGestureEnabled: false // we don't need/want these - onEntered: ApplicationCompositor.reticleOverDesktop = true - onExited: ApplicationCompositor.reticleOverDesktop = false + onEntered: if (typeof ApplicationCompositor !== "undefined") ApplicationCompositor.reticleOverDesktop = true + onExited: if (typeof ApplicationCompositor !== "undefined") ApplicationCompositor.reticleOverDesktop = false acceptedButtons: Qt.NoButton } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4d411696e2..b4234228f7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + #include "Application.h" #include @@ -37,7 +38,6 @@ #include #include - #include #include #include @@ -51,6 +51,7 @@ #include #include + #include #include #include @@ -191,6 +192,9 @@ #include "scripting/WalletScriptingInterface.h" #include "scripting/TTSScriptingInterface.h" #include "scripting/KeyboardScriptingInterface.h" + + + #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif @@ -239,6 +243,7 @@ #include "webbrowser/WebBrowserSuggestionsEngine.h" #include + #include "AboutUtil.h" #if defined(Q_OS_WIN) @@ -622,8 +627,6 @@ public: switch (type) { case NestableType::Entity: return getEntityModelProvider(static_cast(uuid)); - case NestableType::Overlay: - return nullptr; case NestableType::Avatar: return getAvatarModelProvider(uuid); } @@ -1752,7 +1755,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); _applicationStateDevice->setInputVariant(STATE_PLATFORM_ANDROID, []() -> float { #if defined(Q_OS_ANDROID) - return 1; + return 1 ; #else return 0; #endif @@ -1855,6 +1858,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo this->installEventFilter(this); + + #ifdef HAVE_DDE auto ddeTracker = DependencyManager::get(); ddeTracker->init(); @@ -3095,7 +3100,7 @@ void Application::initializeUi() { } if (TouchscreenVirtualPadDevice::NAME == inputPlugin->getName()) { _touchscreenVirtualPadDevice = std::dynamic_pointer_cast(inputPlugin); -#if defined(Q_OS_ANDROID) +#if defined(ANDROID_APP_INTERFACE) auto& virtualPadManager = VirtualPad::Manager::instance(); connect(&virtualPadManager, &VirtualPad::Manager::hapticFeedbackRequested, this, [](int duration) { @@ -3627,10 +3632,14 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { } // Get controller availability +#ifdef ANDROID_APP_QUEST_INTERFACE + bool hasHandControllers = true; +#else bool hasHandControllers = false; if (PluginUtils::isViveControllerAvailable() || PluginUtils::isOculusTouchControllerAvailable()) { hasHandControllers = true; } +#endif // Check HMD use (may be technically available without being in use) bool hasHMD = PluginUtils::isHMDAvailable(); @@ -8247,6 +8256,7 @@ void Application::loadDomainConnectionDialog() { } void Application::toggleLogDialog() { +#ifndef ANDROID_APP_QUEST_INTERFACE if (getLoginDialogPoppedUp()) { return; } @@ -8270,6 +8280,7 @@ void Application::toggleLogDialog() { } else { _logDialog->show(); } +#endif } void Application::recreateLogWindow(int keepOnTop) { @@ -9135,17 +9146,23 @@ void Application::beforeEnterBackground() { void Application::enterBackground() { QMetaObject::invokeMethod(DependencyManager::get().data(), "stop", Qt::BlockingQueuedConnection); +// Quest only supports one plugin which can't be deactivated currently +#if !defined(ANDROID_APP_QUEST_INTERFACE) if (getActiveDisplayPlugin()->isActive()) { getActiveDisplayPlugin()->deactivate(); } +#endif } void Application::enterForeground() { QMetaObject::invokeMethod(DependencyManager::get().data(), "start", Qt::BlockingQueuedConnection); +// Quest only supports one plugin which can't be deactivated currently +#if !defined(ANDROID_APP_QUEST_INTERFACE) if (!getActiveDisplayPlugin() || getActiveDisplayPlugin()->isActive() || !getActiveDisplayPlugin()->activate()) { qWarning() << "Could not re-activate display plugin"; } +#endif auto nodeList = DependencyManager::get(); nodeList->setSendDomainServerCheckInEnabled(true); } diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index ce01db3a56..09f4b68cb9 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -71,6 +71,18 @@ PickFilter getPickFilter(unsigned int filter) { unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) { QVariantMap propMap = properties.toMap(); + +#if defined (Q_OS_ANDROID) + QString jointName { "" }; + if (propMap["joint"].isValid()) { + QString jointName = propMap["joint"].toString(); + const QString MOUSE_JOINT = "Mouse"; + if (jointName == MOUSE_JOINT) { + return PointerEvent::INVALID_POINTER_ID; + } + } +#endif + bool enabled = false; if (propMap["enabled"].isValid()) { enabled = propMap["enabled"].toBool(); diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index 19c20f0c06..1c80caff88 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -150,6 +150,17 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& properties) const { QVariantMap propertyMap = properties.toMap(); +#if defined (Q_OS_ANDROID) + QString jointName { "" }; + if (propertyMap["joint"].isValid()) { + QString jointName = propertyMap["joint"].toString(); + const QString MOUSE_JOINT = "Mouse"; + if (jointName == MOUSE_JOINT) { + return PointerEvent::INVALID_POINTER_ID; + } + } +#endif + bool faceAvatar = false; if (propertyMap["faceAvatar"].isValid()) { faceAvatar = propertyMap["faceAvatar"].toBool(); diff --git a/interface/src/raypick/RayPick.h b/interface/src/raypick/RayPick.h index a781795e55..7625146408 100644 --- a/interface/src/raypick/RayPick.h +++ b/interface/src/raypick/RayPick.h @@ -85,9 +85,10 @@ public: static glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction); static glm::vec2 projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized = true); + static glm::vec2 projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions, const glm::vec3& registrationPoint, bool unNormalized); + private: static glm::vec3 intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat& rotation, const glm::vec3& registration); - static glm::vec2 projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions, const glm::vec3& registrationPoint, bool unNormalized); }; #endif // hifi_RayPick_h diff --git a/interface/src/raypick/StylusPick.cpp b/interface/src/raypick/StylusPick.cpp index 9c3ffb972f..cf19b68a45 100644 --- a/interface/src/raypick/StylusPick.cpp +++ b/interface/src/raypick/StylusPick.cpp @@ -137,13 +137,14 @@ PickResultPointer StylusPick::getDefaultResult(const QVariantMap& pickVariant) c } PickResultPointer StylusPick::getEntityIntersection(const StylusTip& pick) { + auto entityTree = qApp->getEntities()->getTree(); StylusPickResult nearestTarget(pick.toVariantMap()); for (const auto& target : getIncludeItems()) { if (target.isNull()) { continue; } - auto entity = qApp->getEntities()->getTree()->findEntityByEntityItemID(target); + auto entity = entityTree->findEntityByEntityItemID(target); if (!entity) { continue; } @@ -158,8 +159,11 @@ PickResultPointer StylusPick::getEntityIntersection(const StylusTip& pick) { glm::vec3 normal = entityRotation * Vectors::UNIT_Z; float distance = glm::dot(pick.position - entityPosition, normal); if (distance < nearestTarget.distance) { + const auto entityDimensions = entity->getScaledDimensions(); + const auto entityRegistrationPoint = entity->getRegistrationPoint(); glm::vec3 intersection = pick.position - (normal * distance); - glm::vec2 pos2D = RayPick::projectOntoEntityXYPlane(target, intersection, false); + glm::vec2 pos2D = RayPick::projectOntoXYPlane(intersection, entityPosition, entityRotation, + entityDimensions, entityRegistrationPoint, false); if (pos2D == glm::clamp(pos2D, glm::vec2(0), glm::vec2(1))) { IntersectionType type = IntersectionType::ENTITY; if (getFilter().doesPickLocalEntities()) { diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index 8102df6dc6..1ff1c0248b 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -309,12 +309,22 @@ void Keyboard::setRaised(bool raised) { _layerIndex = 0; _capsEnabled = false; _typedCharacters.clear(); + addIncludeItemsToMallets(); }); updateTextDisplay(); } } +void Keyboard::addIncludeItemsToMallets() { + if (_layerIndex >= 0 && _layerIndex < (int)_keyboardLayers.size()) { + QVector includeItems = _keyboardLayers[_layerIndex].keys().toVector(); + auto pointerManager = DependencyManager::get(); + pointerManager->setIncludeItems(_leftHandStylus, includeItems); + pointerManager->setIncludeItems(_rightHandStylus, includeItems); + } +} + void Keyboard::updateTextDisplay() { auto myAvatar = DependencyManager::get()->getMyAvatar(); auto entityScriptingInterface = DependencyManager::get(); @@ -463,6 +473,8 @@ void Keyboard::switchToLayer(int layerIndex) { properties.setRotation(currentOrientation); entityScriptingInterface->editEntity(_anchor.entityID, properties); + addIncludeItemsToMallets(); + startLayerSwitchTimer(); } } @@ -718,8 +730,6 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { clearKeyboardKeys(); auto requestData = request->getData(); - QVector includeItems; - QJsonParseError parseError; QJsonDocument jsonDoc = QJsonDocument::fromJson(requestData, &parseError); @@ -840,7 +850,6 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { key.setKeyString(keyString); key.saveDimensionsAndLocalPosition(); - includeItems.append(key.getID()); _itemsToIgnore.insert(key.getID()); keyboardLayerKeys.insert(id, key); } @@ -886,9 +895,7 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { _itemsToIgnore.insert(_anchor.entityID); }); _layerIndex = 0; - auto pointerManager = DependencyManager::get(); - pointerManager->setIncludeItems(_leftHandStylus, includeItems); - pointerManager->setIncludeItems(_rightHandStylus, includeItems); + addIncludeItemsToMallets(); }); request->send(); diff --git a/interface/src/ui/Keyboard.h b/interface/src/ui/Keyboard.h index 958c862520..1ff1cda1b9 100644 --- a/interface/src/ui/Keyboard.h +++ b/interface/src/ui/Keyboard.h @@ -157,6 +157,7 @@ private: bool shouldProcessEntityAndPointerEvent(const PointerEvent& event) const; bool shouldProcessPointerEvent(const PointerEvent& event) const; bool shouldProcessEntity() const; + void addIncludeItemsToMallets(); void startLayerSwitchTimer(); bool isLayerSwitchTimerFinished() const; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 2dd8008281..660220c731 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -79,7 +79,16 @@ void Overlays::cleanupAllOverlays() { cleanupOverlaysToDelete(); } -void Overlays::init() {} +void Overlays::init() { + auto entityScriptingInterface = DependencyManager::get().data(); + auto pointerManager = DependencyManager::get(); + connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, entityScriptingInterface , &EntityScriptingInterface::hoverEnterEntity); + connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); + connect(pointerManager.data(), &PointerManager::hoverEndOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); + connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); + connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); + connect(pointerManager.data(), &PointerManager::triggerEndOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); +} void Overlays::update(float deltatime) { cleanupOverlaysToDelete(); @@ -343,6 +352,17 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove return "none"; }); + RENAME_PROP_CONVERT(textures, textures, [](const QVariant& v) { + auto map = v.toMap(); + if (!map.isEmpty()) { + auto json = QJsonDocument::fromVariant(map); + if (!json.isNull()) { + return QVariant(QString(json.toJson())); + } + } + return v; + }); + if (type == "Shape" || type == "Box" || type == "Sphere" || type == "Gizmo") { RENAME_PROP(solid, isSolid); RENAME_PROP(isFilled, isSolid); diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index a1809f3438..d710e9d8ff 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -865,10 +865,6 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar //virtual const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) { -#ifdef Q_OS_ANDROID - // disable IK on android - return underPoses; -#endif // allows solutionSource to be overridden by an animVar auto solutionSource = animVars.lookup(_solutionSourceVar, (int)_solutionSource); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 7d0fc3409a..395fc3b7b4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -64,7 +64,11 @@ namespace render { return keyBuilder.build(); } template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) { - return static_pointer_cast(avatar)->getRenderBounds(); + auto avatarPtr = static_pointer_cast(avatar); + if (avatarPtr) { + return avatarPtr->getRenderBounds(); + } + return Item::Bound(); } template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args) { auto avatarPtr = static_pointer_cast(avatar); @@ -75,10 +79,15 @@ namespace render { } template <> uint32_t metaFetchMetaSubItems(const AvatarSharedPointer& avatar, ItemIDs& subItems) { auto avatarPtr = static_pointer_cast(avatar); - if (avatarPtr->getSkeletonModel()) { - auto& metaSubItems = avatarPtr->getSkeletonModel()->fetchRenderItemIDs(); - subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end()); - return (uint32_t) metaSubItems.size(); + if (avatarPtr) { + uint32_t total = 0; + if (avatarPtr->getSkeletonModel()) { + auto& metaSubItems = avatarPtr->getSkeletonModel()->fetchRenderItemIDs(); + subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end()); + total += (uint32_t)metaSubItems.size(); + } + total += avatarPtr->appendSubMetaItems(subItems); + return total; } return 0; } @@ -626,12 +635,18 @@ void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& sc _skeletonModel->setVisibleInScene(_isMeshVisible, scene); processMaterials(); + bool attachmentRenderingNeedsUpdate = false; for (auto& attachmentModel : _attachmentModels) { attachmentModel->addToScene(scene, transaction); attachmentModel->setTagMask(render::hifi::TAG_ALL_VIEWS); - attachmentModel->setGroupCulled(false); + attachmentModel->setGroupCulled(true); attachmentModel->setCanCastShadow(true); attachmentModel->setVisibleInScene(_isMeshVisible, scene); + attachmentRenderingNeedsUpdate = true; + } + + if (attachmentRenderingNeedsUpdate) { + updateAttachmentRenderIDs(); } _mustFadeIn = true; @@ -855,15 +870,17 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { canTryFade = true; _isAnimatingScale = true; } + bool attachmentRenderingNeedsUpdate = false; for (auto attachmentModel : _attachmentModels) { if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) { attachmentModel->removeFromScene(scene, transaction); attachmentModel->addToScene(scene, transaction); attachmentModel->setTagMask(render::hifi::TAG_ALL_VIEWS); - attachmentModel->setGroupCulled(false); + attachmentModel->setGroupCulled(true); attachmentModel->setCanCastShadow(true); attachmentModel->setVisibleInScene(_isMeshVisible, scene); + attachmentRenderingNeedsUpdate = true; } } @@ -886,9 +903,15 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { for (auto attachmentModelToRemove : _attachmentsToRemove) { attachmentModelToRemove->removeFromScene(scene, transaction); + attachmentRenderingNeedsUpdate = true; } _attachmentsToDelete.insert(_attachmentsToDelete.end(), _attachmentsToRemove.begin(), _attachmentsToRemove.end()); _attachmentsToRemove.clear(); + + if (attachmentRenderingNeedsUpdate) { + updateAttachmentRenderIDs(); + } + scene->enqueueTransaction(transaction); } @@ -931,6 +954,11 @@ void Avatar::simulateAttachments(float deltaTime) { } } } + + if (_ancestorChainRenderableVersion != _lastAncestorChainRenderableVersion) { + _lastAncestorChainRenderableVersion = _ancestorChainRenderableVersion; + updateDescendantRenderIDs(); + } } float Avatar::getBoundingRadius() const { @@ -1608,7 +1636,6 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { } } - int Avatar::parseDataFromBuffer(const QByteArray& buffer) { PerformanceTimer perfTimer("unpack"); if (!_initialized) { @@ -2084,3 +2111,60 @@ void Avatar::clearAvatarGrabData(const QUuid& id) { } }); } + +uint32_t Avatar::appendSubMetaItems(render::ItemIDs& subItems) { + return _subItemLock.resultWithReadLock([&] { + uint32_t total = 0; + + if (_attachmentRenderIDs.size() > 0) { + subItems.insert(subItems.end(), _attachmentRenderIDs.begin(), _attachmentRenderIDs.end()); + total += (uint32_t)_attachmentRenderIDs.size(); + } + + if (_descendantRenderIDs.size() > 0) { + subItems.insert(subItems.end(), _descendantRenderIDs.begin(), _descendantRenderIDs.end()); + total += (uint32_t)_descendantRenderIDs.size(); + } + + return total; + }); +} + +void Avatar::updateAttachmentRenderIDs() { + _subItemLock.withWriteLock([&] { + _attachmentRenderIDs.clear(); + for (auto& attachmentModel : _attachmentModels) { + if (attachmentModel && attachmentModel->isRenderable()) { + auto& metaSubItems = attachmentModel->fetchRenderItemIDs(); + _attachmentRenderIDs.insert(_attachmentRenderIDs.end(), metaSubItems.begin(), metaSubItems.end()); + } + } + }); +} + +void Avatar::updateDescendantRenderIDs() { + _subItemLock.withWriteLock([&] { + _descendantRenderIDs.clear(); + auto entityTreeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; + if (entityTree) { + entityTree->withReadLock([&] { + forEachDescendant([&](SpatiallyNestablePointer object) { + if (object && object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + if (entity->isVisible()) { + auto renderer = entityTreeRenderer->renderableForEntityId(object->getID()); + if (renderer) { + render::ItemIDs renderableSubItems; + uint32_t numRenderableSubItems = renderer->metaFetchMetaSubItems(renderableSubItems); + if (numRenderableSubItems > 0) { + _descendantRenderIDs.insert(_descendantRenderIDs.end(), renderableSubItems.begin(), renderableSubItems.end()); + } + } + } + } + }); + }); + } + }); +} diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 06942a13d8..1e6893a410 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -498,6 +498,8 @@ public: const std::vector& getMultiSphereShapes() const { return _multiSphereShapes; } void tearDownGrabs(); + uint32_t appendSubMetaItems(render::ItemIDs& subItems); + signals: void targetScaleChanged(float targetScale); @@ -638,8 +640,6 @@ protected: RateCounter<> _skeletonModelSimulationRate; RateCounter<> _jointDataSimulationRate; - -protected: class AvatarEntityDataHash { public: AvatarEntityDataHash(uint32_t h) : hash(h) {}; @@ -699,6 +699,13 @@ protected: MapOfGrabs _avatarGrabs; SetOfIDs _grabsToChange; // updated grab IDs -- changes needed to entities or physics VectorOfIDs _grabsToDelete; // deleted grab IDs -- changes needed to entities or physics + + ReadWriteLockable _subItemLock; + void updateAttachmentRenderIDs(); + render::ItemIDs _attachmentRenderIDs; + void updateDescendantRenderIDs(); + render::ItemIDs _descendantRenderIDs; + uint32_t _lastAncestorChainRenderableVersion { 0 }; }; #endif // hifi_Avatar_h diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index d8b8cbd54a..9828a8beda 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -74,30 +74,15 @@ void Basic2DWindowOpenGLDisplayPlugin::customizeContext() { } } - - _virtualPadJumpBtnPixelSize = dpi * VirtualPad::Manager::JUMP_BTN_FULL_PIXELS / VirtualPad::Manager::DPI; - if (!_virtualPadJumpBtnTexture) { - auto iconPath = PathUtils::resourcesPath() + "images/fly.png"; - auto image = QImage(iconPath); - if (image.format() != QImage::Format_ARGB32) { - image = image.convertToFormat(QImage::Format_ARGB32); - } - if ((image.width() > 0) && (image.height() > 0)) { - image = image.scaled(_virtualPadJumpBtnPixelSize, _virtualPadJumpBtnPixelSize, Qt::KeepAspectRatio); - image = image.mirrored(); - - _virtualPadJumpBtnTexture = gpu::Texture::createStrict( - gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), - image.width(), image.height(), - gpu::Texture::MAX_NUM_MIPS, - gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); - _virtualPadJumpBtnTexture->setSource("virtualPad jump"); - auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); - _virtualPadJumpBtnTexture->setUsage(usage.build()); - _virtualPadJumpBtnTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - _virtualPadJumpBtnTexture->assignStoredMip(0, image.byteCount(), image.constBits()); - _virtualPadJumpBtnTexture->setAutoGenerateMips(true); - } + if (_virtualPadButtons.size() == 0) { + _virtualPadButtons.append(VirtualPadButton( + dpi * VirtualPad::Manager::BTN_FULL_PIXELS / VirtualPad::Manager::DPI, + PathUtils::resourcesPath() + "images/fly.png", + VirtualPad::Manager::Button::JUMP)); + _virtualPadButtons.append(VirtualPadButton( + dpi * VirtualPad::Manager::BTN_FULL_PIXELS / VirtualPad::Manager::DPI, + PathUtils::resourcesPath() + "images/handshake.png", + VirtualPad::Manager::Button::HANDSHAKE)); } #endif Parent::customizeContext(); @@ -133,8 +118,6 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() { _virtualPadPixelSize, _virtualPadPixelSize); auto stickTransform = DependencyManager::get()->getPoint2DTransform(virtualPadManager.getLeftVirtualPad()->getCurrentTouch(), _virtualPadPixelSize, _virtualPadPixelSize); - auto jumpTransform = DependencyManager::get()->getPoint2DTransform(virtualPadManager.getJumpButtonPosition(), - _virtualPadJumpBtnPixelSize, _virtualPadJumpBtnPixelSize); render([&](gpu::Batch& batch) { batch.enableStereo(false); @@ -151,9 +134,9 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() { batch.setModelTransform(stickTransform); batch.draw(gpu::TRIANGLE_STRIP, 4); - batch.setResourceTexture(0, _virtualPadJumpBtnTexture); - batch.setModelTransform(jumpTransform); - batch.draw(gpu::TRIANGLE_STRIP, 4); + foreach(VirtualPadButton virtualPadButton, _virtualPadButtons) { + virtualPadButton.draw(batch, virtualPadManager.getButtonPosition(virtualPadButton._button)); + } }); } #endif @@ -178,3 +161,47 @@ bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const { QScreen* Basic2DWindowOpenGLDisplayPlugin::getFullscreenTarget() { return qApp->primaryScreen(); } + +#if defined(Q_OS_ANDROID) + +Basic2DWindowOpenGLDisplayPlugin::VirtualPadButton::VirtualPadButton(qreal pixelSize, + QString iconPath, + VirtualPad::Manager::Button button) : + _pixelSize { pixelSize }, + _button { button } +{ + if (!_texture) { + auto image = QImage(iconPath); + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); + } + if ((image.width() > 0) && (image.height() > 0)) { + image = image.scaled(_pixelSize, _pixelSize, Qt::KeepAspectRatio); + image = image.mirrored(); + + _texture = gpu::Texture::createStrict( + gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), + image.width(), image.height(), + gpu::Texture::MAX_NUM_MIPS, + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); + _texture->setSource(iconPath.toStdString()); + auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); + _texture->setUsage(usage.build()); + _texture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + _texture->assignStoredMip(0, image.byteCount(), image.constBits()); + _texture->setAutoGenerateMips(true); + } + } +} + +void Basic2DWindowOpenGLDisplayPlugin::VirtualPadButton::draw(gpu::Batch &batch, + glm::vec2 buttonPosition) { + auto transform = DependencyManager::get()->getPoint2DTransform( + buttonPosition, + _pixelSize, _pixelSize); + batch.setResourceTexture(0, _texture); + batch.setModelTransform(transform); + batch.draw(gpu::TRIANGLE_STRIP, 4); +} + +#endif \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index 944d5e89d1..cc304c19c2 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -9,6 +9,10 @@ #include "OpenGLDisplayPlugin.h" +#if defined(Q_OS_ANDROID) +#include "VirtualPadManager.h" +#endif + const float TARGET_FRAMERATE_Basic2DWindowOpenGL = 60.0f; class QScreen; @@ -51,5 +55,23 @@ private: gpu::TexturePointer _virtualPadJumpBtnTexture; qreal _virtualPadJumpBtnPixelSize; + + gpu::TexturePointer _virtualPadRbBtnTexture; + qreal _virtualPadRbBtnPixelSize; + + class VirtualPadButton { + public: + + VirtualPadButton() {} + VirtualPadButton(qreal pixelSize, QString iconPath, VirtualPad::Manager::Button button); + + void draw(gpu::Batch& batch, glm::vec2 buttonPosition); + + gpu::TexturePointer _texture; + qreal _pixelSize; + VirtualPad::Manager::Button _button; + }; + QVector _virtualPadButtons; + #endif }; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 5c73b9576d..a6826da91b 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -320,6 +320,7 @@ bool EntityRenderer::addToScene(const ScenePointer& scene, Transaction& transact transaction.resetItem(_renderItemID, renderPayload); onAddToScene(_entity); updateInScene(scene, transaction); + _entity->bumpAncestorChainRenderableVersion(); return true; } @@ -327,6 +328,7 @@ void EntityRenderer::removeFromScene(const ScenePointer& scene, Transaction& tra onRemoveFromScene(_entity); transaction.removeItem(_renderItemID); Item::clearID(_renderItemID); + _entity->bumpAncestorChainRenderableVersion(); } void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& transaction) { @@ -352,14 +354,6 @@ void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& trans }); } -void EntityRenderer::clearSubRenderItemIDs() { - _subRenderItemIDs.clear(); -} - -void EntityRenderer::setSubRenderItemIDs(const render::ItemIDs& ids) { - _subRenderItemIDs = ids; -} - // // Internal methods // diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index d5e236a76a..e9a6035e3d 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -52,9 +52,6 @@ public: virtual bool addToScene(const ScenePointer& scene, Transaction& transaction) final; virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction); - void clearSubRenderItemIDs(); - void setSubRenderItemIDs(const render::ItemIDs& ids); - const uint64_t& getUpdateTime() const { return _updateTime; } virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); @@ -64,6 +61,9 @@ public: static glm::vec4 calculatePulseColor(const glm::vec4& color, const PulsePropertyGroup& pulseProperties, quint64 start); + virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override; + virtual Item::Bound getBound() override; + protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } virtual void onAddToScene(const EntityItemPointer& entity); @@ -75,9 +75,7 @@ protected: // Implementing the PayloadProxyInterface methods virtual ItemKey getKey() override; virtual ShapeKey getShapeKey() override; - virtual Item::Bound getBound() override; virtual void render(RenderArgs* args) override final; - virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override; virtual render::hifi::Tag getTagMask() const; virtual render::hifi::Layer getHifiRenderLayer() const; @@ -133,7 +131,6 @@ protected: SharedSoundPointer _collisionSound; QUuid _changeHandlerId; ItemID _renderItemID{ Item::INVALID_ITEM_ID }; - ItemIDs _subRenderItemIDs; uint64_t _fadeStartTime{ usecTimestampNow() }; uint64_t _updateTime{ usecTimestampNow() }; // used when sorting/throttling render updates bool _isFading { EntityTreeRenderer::getEntitiesShouldFadeFunction()() }; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index ae9fdf572a..e842d98714 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1079,7 +1079,7 @@ render::hifi::Tag ModelEntityRenderer::getTagMask() const { uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { if (_model) { - auto metaSubItems = _subRenderItemIDs; + auto metaSubItems = _model->fetchRenderItemIDs(); subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end()); return (uint32_t)metaSubItems.size(); } @@ -1321,11 +1321,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if (!_hasModel) { if (model) { model->removeFromScene(scene, transaction); + entity->bumpAncestorChainRenderableVersion(); withWriteLock([&] { _model.reset(); }); - transaction.updateItem(getRenderItemID(), [](PayloadProxyInterface& data) { - auto entityRenderer = static_cast(&data); - entityRenderer->clearSubRenderItemIDs(); - }); emit DependencyManager::get()-> modelRemovedFromScene(entity->getEntityItemID(), NestableType::Entity, _model); } @@ -1442,12 +1439,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce render::Item::Status::Getters statusGetters; makeStatusGetters(entity, statusGetters); model->addToScene(scene, transaction, statusGetters); - - auto newRenderItemIDs{ model->fetchRenderItemIDs() }; - transaction.updateItem(getRenderItemID(), [newRenderItemIDs](PayloadProxyInterface& data) { - auto entityRenderer = static_cast(&data); - entityRenderer->setSubRenderItemIDs(newRenderItemIDs); - }); + entity->bumpAncestorChainRenderableVersion(); processMaterials(); } } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c0b12c4d1f..9f11b3c018 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2935,6 +2935,7 @@ void EntityItem::setVisible(bool value) { }); if (changed) { + bumpAncestorChainRenderableVersion(); emit requestRenderUpdate(); } } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 150aa6b0cf..22cd26eac6 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -941,7 +941,7 @@ QUuid EntityScriptingInterface::editEntity(const QUuid& id, const EntityItemProp auto nestable = nestableWP.lock(); if (nestable) { NestableType nestableType = nestable->getNestableType(); - if (nestableType == NestableType::Overlay || nestableType == NestableType::Avatar) { + if (nestableType == NestableType::Avatar) { qCWarning(entities) << "attempted edit on non-entity: " << id << nestable->getName(); return QUuid(); // null script value to indicate failure } diff --git a/libraries/gpu/src/gpu/FrameReader.cpp b/libraries/gpu/src/gpu/FrameReader.cpp index 6e39a38097..2fe143ee90 100644 --- a/libraries/gpu/src/gpu/FrameReader.cpp +++ b/libraries/gpu/src/gpu/FrameReader.cpp @@ -388,10 +388,23 @@ ShaderPointer Deserializer::readShader(const json& node) { return nullptr; } + static std::map shadersIdsByName; + if (shadersIdsByName.empty()) { + for (const auto id : shader::allShaders()) { + const auto& shaderSource = shader::Source::get(id); + shadersIdsByName[shaderSource.name] = id; + } + } + // FIXME support procedural shaders Shader::Type type = node[keys::type]; std::string name = node[keys::name]; - uint32_t id = node[keys::id]; + // Using the serialized ID is bad, because it's generated at + // cmake time, and can change across platforms or when + // shaders are added or removed + // uint32_t id = node[keys::id]; + + uint32_t id = shadersIdsByName[name]; ShaderPointer result; switch (type) { //case Shader::Type::GEOMETRY: diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index 43205ba4c2..3015de7e0e 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -167,7 +167,7 @@ TransformObject getTransformObject() { vec4 eyeClipEdge[2]= vec4[2](vec4(-1,0,0,1), vec4(1,0,0,1)); vec2 eyeOffsetScale = vec2(-0.5, +0.5); uint eyeIndex = uint(_stereoSide); -#ifndef GPU_GLES +#if !defined(GPU_GLES) || (defined(HAVE_EXT_clip_cull_distance) && !defined(VULKAN)) gl_ClipDistance[0] = dot(<$clipPos$>, eyeClipEdge[eyeIndex]); #endif float newClipPosX = <$clipPos$>.x * 0.5 + eyeOffsetScale[eyeIndex] * <$clipPos$>.w; diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp index 247f484c95..6bb743bad0 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp @@ -66,7 +66,7 @@ void TouchscreenVirtualPadDevice::resize() { _fixedRadius = _screenDPI * 0.5f * VirtualPad::Manager::BASE_DIAMETER_PIXELS / VirtualPad::Manager::DPI; _fixedRadiusForCalc = _fixedRadius - _screenDPI * VirtualPad::Manager::STICK_RADIUS_PIXELS / VirtualPad::Manager::DPI; - _jumpButtonRadius = _screenDPI * VirtualPad::Manager::JUMP_BTN_TRIMMED_RADIUS_PIXELS / VirtualPad::Manager::DPI; + _buttonRadius = _screenDPI * VirtualPad::Manager::BTN_TRIMMED_RADIUS_PIXELS / VirtualPad::Manager::DPI; } auto& virtualPadManager = VirtualPad::Manager::instance(); @@ -86,11 +86,21 @@ void TouchscreenVirtualPadDevice::setupControlsPositions(VirtualPad::Manager& vi virtualPadManager.getLeftVirtualPad()->setFirstTouch(_moveRefTouchPoint); // Jump button - float jumpBtnPixelSize = _screenDPI * VirtualPad::Manager::JUMP_BTN_FULL_PIXELS / VirtualPad::Manager::DPI; - float rightMargin = _screenDPI * VirtualPad::Manager::JUMP_BTN_RIGHT_MARGIN_PIXELS / VirtualPad::Manager::DPI; - float bottomMargin = _screenDPI * VirtualPad::Manager::JUMP_BTN_BOTTOM_MARGIN_PIXELS/ VirtualPad::Manager::DPI; - _jumpButtonPosition = glm::vec2( eventScreen->availableSize().width() - rightMargin - jumpBtnPixelSize, eventScreen->availableSize().height() - bottomMargin - _jumpButtonRadius - _extraBottomMargin); - virtualPadManager.setJumpButtonPosition(_jumpButtonPosition); + float btnPixelSize = _screenDPI * VirtualPad::Manager::BTN_FULL_PIXELS / VirtualPad::Manager::DPI; + float rightMargin = _screenDPI * VirtualPad::Manager::BTN_RIGHT_MARGIN_PIXELS / VirtualPad::Manager::DPI; + float bottomMargin = _screenDPI * VirtualPad::Manager::BTN_BOTTOM_MARGIN_PIXELS/ VirtualPad::Manager::DPI; + glm::vec2 jumpButtonPosition = glm::vec2( eventScreen->availableSize().width() - rightMargin - btnPixelSize, eventScreen->availableSize().height() - bottomMargin - _buttonRadius - _extraBottomMargin); + glm::vec2 rbButtonPosition = glm::vec2( eventScreen->availableSize().width() - rightMargin - btnPixelSize, eventScreen->availableSize().height() - 2 * bottomMargin - 3 * _buttonRadius - _extraBottomMargin); + + // Avoid generating buttons in portrait mode + if ( eventScreen->availableSize().width() > eventScreen->availableSize().height() && _buttonsManager.buttonsCount() == 0) { + _buttonsManager.addButton(TouchscreenButton(JUMP, JUMP_BUTTON, _buttonRadius, jumpButtonPosition, _inputDevice )); + _buttonsManager.addButton(TouchscreenButton(RB, RB_BUTTON, _buttonRadius, rbButtonPosition, _inputDevice )); + + virtualPadManager.setButtonPosition(VirtualPad::Manager::Button::JUMP, jumpButtonPosition); + virtualPadManager.setButtonPosition(VirtualPad::Manager::Button::HANDSHAKE, rbButtonPosition); + } + } float clip(float n, float lower, float upper) { @@ -237,7 +247,7 @@ void TouchscreenVirtualPadDevice::touchEndEvent(const QTouchEvent* event) { if (!virtualPadManager.isEnabled() && !virtualPadManager.isHidden()) { moveTouchEnd(); viewTouchEnd(); - jumpTouchEnd(); + _buttonsManager.endTouchForAll(); return; } // touch end here is a big reset -> resets both pads @@ -246,7 +256,7 @@ void TouchscreenVirtualPadDevice::touchEndEvent(const QTouchEvent* event) { debugPoints(event, " END ----------------"); moveTouchEnd(); viewTouchEnd(); - jumpTouchEnd(); + _buttonsManager.endTouchForAll(); _inputDevice->_axisStateMap.clear(); _inputDevice->_buttonPressedMap.clear(); } @@ -282,11 +292,11 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { const QList& tPoints = event->touchPoints(); bool moveTouchFound = false; bool viewTouchFound = false; - bool jumpTouchFound = false; int idxMoveStartingPointCandidate = -1; int idxViewStartingPointCandidate = -1; - int idxJumpStartingPointCandidate = -1; + + _buttonsManager.resetEventValues(); glm::vec2 thisPoint; int thisPointId; @@ -311,10 +321,7 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { continue; } - if (!jumpTouchFound && _jumpHasValidTouch && _jumpCurrentTouchId == thisPointId) { - // valid if it's an ongoing touch - jumpTouchFound = true; - jumpTouchUpdate(thisPoint); + if (_buttonsManager.processOngoingTouch(thisPoint, thisPointId)) { continue; } @@ -330,18 +337,16 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { continue; } - if (!jumpTouchFound && idxJumpStartingPointCandidate == -1 && jumpTouchBeginIsValid(thisPoint) && - (!_unusedTouches.count(thisPointId) || _unusedTouches[thisPointId] == JUMP )) { - idxJumpStartingPointCandidate = i; + if (_buttonsManager.findStartingTouchPointCandidate(thisPoint, thisPointId, i, _unusedTouches)) { continue; } if (moveTouchBeginIsValid(thisPoint)) { unusedTouchesInEvent[thisPointId] = MOVE; - } else if (jumpTouchBeginIsValid(thisPoint)) { - unusedTouchesInEvent[thisPointId] = JUMP; } else if (viewTouchBeginIsValid(thisPoint)) { unusedTouchesInEvent[thisPointId] = VIEW; + } else { + _buttonsManager.saveUnusedTouches(unusedTouchesInEvent, thisPoint, thisPointId); } } @@ -370,24 +375,13 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { viewTouchEnd(); } } - if (!jumpTouchFound) { - if (idxJumpStartingPointCandidate != -1) { - _jumpCurrentTouchId = tPoints[idxJumpStartingPointCandidate].id(); - _unusedTouches.erase(_jumpCurrentTouchId); - thisPoint.x = tPoints[idxJumpStartingPointCandidate].pos().x(); - thisPoint.y = tPoints[idxJumpStartingPointCandidate].pos().y(); - jumpTouchBegin(thisPoint); - } else { - if (_jumpHasValidTouch) { - jumpTouchEnd(); - } - } - } + + _buttonsManager.processBeginOrEnd(thisPoint, tPoints, _unusedTouches); } bool TouchscreenVirtualPadDevice::viewTouchBeginIsValid(glm::vec2 touchPoint) { - return !moveTouchBeginIsValid(touchPoint) && !jumpTouchBeginIsValid(touchPoint); + return !moveTouchBeginIsValid(touchPoint) && _buttonsManager.touchBeginInvalidForAllButtons(touchPoint); } bool TouchscreenVirtualPadDevice::moveTouchBeginIsValid(glm::vec2 touchPoint) { @@ -400,30 +394,6 @@ bool TouchscreenVirtualPadDevice::moveTouchBeginIsValid(glm::vec2 touchPoint) { } } -bool TouchscreenVirtualPadDevice::jumpTouchBeginIsValid(glm::vec2 touchPoint) { - // position of button and boundaries - return glm::distance2(touchPoint, _jumpButtonPosition) < _jumpButtonRadius * _jumpButtonRadius; -} - -void TouchscreenVirtualPadDevice::jumpTouchBegin(glm::vec2 touchPoint) { - auto& virtualPadManager = VirtualPad::Manager::instance(); - if (virtualPadManager.isEnabled() && !virtualPadManager.isHidden()) { - _jumpHasValidTouch = true; - - _inputDevice->_buttonPressedMap.insert(TouchButtonChannel::JUMP_BUTTON_PRESS); - } -} - -void TouchscreenVirtualPadDevice::jumpTouchUpdate(glm::vec2 touchPoint) {} - -void TouchscreenVirtualPadDevice::jumpTouchEnd() { - if (_jumpHasValidTouch) { - _jumpHasValidTouch = false; - - _inputDevice->_buttonPressedMap.erase(TouchButtonChannel::JUMP_BUTTON_PRESS); - } -} - void TouchscreenVirtualPadDevice::moveTouchBegin(glm::vec2 touchPoint) { auto& virtualPadManager = VirtualPad::Manager::instance(); if (virtualPadManager.isEnabled() && !virtualPadManager.isHidden()) { @@ -498,7 +468,8 @@ controller::Input::NamedVector TouchscreenVirtualPadDevice::InputDevice::getAvai Input::NamedPair(makeInput(TouchAxisChannel::LY), "LY"), Input::NamedPair(makeInput(TouchAxisChannel::RX), "RX"), Input::NamedPair(makeInput(TouchAxisChannel::RY), "RY"), - Input::NamedPair(makeInput(TouchButtonChannel::JUMP_BUTTON_PRESS), "JUMP_BUTTON_PRESS") + Input::NamedPair(makeInput(TouchButtonChannel::JUMP), "JUMP_BUTTON_PRESS"), + Input::NamedPair(makeInput(TouchButtonChannel::RB), "RB") }; return availableInputs; } @@ -507,3 +478,146 @@ QString TouchscreenVirtualPadDevice::InputDevice::getDefaultMappingConfig() cons static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/touchscreenvirtualpad.json"; return MAPPING_JSON; } + +TouchscreenVirtualPadDevice::TouchscreenButton::TouchscreenButton( + TouchscreenVirtualPadDevice::TouchButtonChannel channelIn, + TouchscreenVirtualPadDevice::TouchType touchTypeIn, float buttonRadiusIn, + glm::vec2 buttonPositionIn, std::shared_ptr inputDeviceIn) : + buttonPosition(buttonPositionIn), + buttonRadius(buttonRadiusIn), + touchType(touchTypeIn), + channel(channelIn), + _inputDevice(inputDeviceIn) +{ +} + +void TouchscreenVirtualPadDevice::TouchscreenButton::touchBegin(glm::vec2 touchPoint) { + auto& virtualPadManager = VirtualPad::Manager::instance(); + if (virtualPadManager.isEnabled() && !virtualPadManager.isHidden()) { + hasValidTouch = true; + + _inputDevice->_buttonPressedMap.insert(channel); + } +} + +void TouchscreenVirtualPadDevice::TouchscreenButton::touchUpdate(glm::vec2 touchPoint) { + +} + +void TouchscreenVirtualPadDevice::TouchscreenButton::touchEnd() { + if (hasValidTouch) { + hasValidTouch = false; + + _inputDevice->_buttonPressedMap.erase(channel); + } +} + +bool TouchscreenVirtualPadDevice::TouchscreenButton::touchBeginIsValid(glm::vec2 touchPoint) { + return glm::distance2(touchPoint, buttonPosition) < buttonRadius * buttonRadius; +} + +void TouchscreenVirtualPadDevice::TouchscreenButton::resetEventValues() { + _candidatePointIdx = -1; + _found = false; +} + +TouchscreenVirtualPadDevice::TouchscreenButtonsManager::TouchscreenButtonsManager() {} + +void TouchscreenVirtualPadDevice::TouchscreenButtonsManager::addButton( + TouchscreenVirtualPadDevice::TouchscreenButton button) { + buttons.push_back(button); +} + +void TouchscreenVirtualPadDevice::TouchscreenButtonsManager::resetEventValues() { + for(int i = 0; i < buttons.size(); i++) { + TouchscreenButton &button = buttons[i]; + button.resetEventValues(); + } +} + +bool +TouchscreenVirtualPadDevice::TouchscreenButtonsManager::processOngoingTouch(glm::vec2 thisPoint, + int thisPointId) { + for(int i = 0; i < buttons.size(); i++) { + TouchscreenButton &button = buttons[i]; + + if (!button._found && button.hasValidTouch && button.currentTouchId == thisPointId) { + // valid if it's an ongoing touch + button._found = true; + button.touchUpdate(thisPoint); + return true; + } + } + return false; + +} + +bool TouchscreenVirtualPadDevice::TouchscreenButtonsManager::findStartingTouchPointCandidate( + glm::vec2 thisPoint, int thisPointId, int thisPointIdx, std::map &globalUnusedTouches) { + + for(int i = 0; i < buttons.size(); i++) { + TouchscreenButton &button = buttons[i]; + if (!button._found && button._candidatePointIdx == -1 && button.touchBeginIsValid(thisPoint)) { + if (!globalUnusedTouches.count(thisPointId) ) { + button._candidatePointIdx = thisPointIdx; + return true; + } else if (globalUnusedTouches[thisPointId] == button.touchType) { + button._candidatePointIdx = thisPointIdx; + return true; + } + } + } + return false; + +} + +void TouchscreenVirtualPadDevice::TouchscreenButtonsManager::saveUnusedTouches( + std::map &unusedTouchesInEvent, glm::vec2 thisPoint, + int thisPointId) { + for(int i = 0; i < buttons.size(); i++) { + TouchscreenButton &button = buttons[i]; + if (button.touchBeginIsValid(thisPoint)) { + unusedTouchesInEvent[thisPointId] = button.touchType; + return; + } + } + +} + +void TouchscreenVirtualPadDevice::TouchscreenButtonsManager::processBeginOrEnd( + glm::vec2 thisPoint, const QList &tPoints, std::map globalUnusedTouches) { + for(int i = 0; i < buttons.size(); i++) { + TouchscreenButton &button = buttons[i]; + if (!button._found) { + if (button._candidatePointIdx != -1) { + button.currentTouchId = tPoints[button._candidatePointIdx].id(); + globalUnusedTouches.erase(button.currentTouchId); + thisPoint.x = tPoints[button._candidatePointIdx].pos().x(); + thisPoint.y = tPoints[button._candidatePointIdx].pos().y(); + button.touchBegin(thisPoint); + } else { + if (button.hasValidTouch) { + button.touchEnd(); + } + } + } + } + +} + +void TouchscreenVirtualPadDevice::TouchscreenButtonsManager::endTouchForAll() { + for(int i = 0; i < buttons.size(); i++) { + TouchscreenButton &button = buttons[i]; + button.touchEnd(); + } +} + +bool TouchscreenVirtualPadDevice::TouchscreenButtonsManager::touchBeginInvalidForAllButtons(glm::vec2 touchPoint) { + for(int i = 0; i < buttons.size(); i++) { + TouchscreenButton &button = buttons[i]; + if (button.touchBeginIsValid(touchPoint)) { + return false; + } + } + return true; +} diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h index ef1e7a4d89..4ef1dbd4f6 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h @@ -15,6 +15,7 @@ #include #include "InputPlugin.h" #include +#include #include "VirtualPadManager.h" class QTouchEvent; @@ -51,7 +52,8 @@ public: }; enum TouchButtonChannel { - JUMP_BUTTON_PRESS + JUMP, + RB }; protected: @@ -82,7 +84,60 @@ protected: enum TouchType { MOVE = 1, VIEW, - JUMP + JUMP_BUTTON, + RB_BUTTON + }; + + class TouchscreenButton { + public: + + TouchscreenButton() {}; + + TouchscreenButton(TouchButtonChannel channelIn, TouchType touchTypeIn, float buttonRadiusIn, glm::vec2 buttonPositionIn, + std::shared_ptr inputDeviceIn); + + void touchBegin(glm::vec2 touchPoint); + void touchUpdate(glm::vec2 touchPoint); + void touchEnd(); + bool touchBeginIsValid(glm::vec2 touchPoint); + + bool hasValidTouch { false }; + int currentTouchId; + + // per event tmp values + int _candidatePointIdx { -1 }; + bool _found { false }; + void resetEventValues(); + + glm::vec2 buttonPosition; + float buttonRadius; + TouchType touchType; + TouchButtonChannel channel; + + std::shared_ptr _inputDevice; + + }; + + class TouchscreenButtonsManager { + public: + + TouchscreenButtonsManager(); + + QVector buttons; + + void addButton(TouchscreenButton button); + int buttonsCount() { + return buttons.size(); + } + + void resetEventValues(); + bool processOngoingTouch(glm::vec2 thisPoint, int thisPointId); + bool findStartingTouchPointCandidate(glm::vec2 thisPoint, int thisPointId, int thisPointIdx, std::map &globalUnusedTouches); + void saveUnusedTouches(std::map &unusedTouchesInEvent, glm::vec2 thisPoint, int thisPointId); + void processBeginOrEnd(glm::vec2 thisPoint, const QList& tPoints, std::map globalUnusedTouches); + + void endTouchForAll(); + bool touchBeginInvalidForAllButtons(glm::vec2 touchPoint); }; float _lastPinchScale; @@ -101,9 +156,6 @@ protected: glm::vec2 _viewCurrentTouchPoint; int _viewCurrentTouchId; - bool _jumpHasValidTouch; - int _jumpCurrentTouchId; - std::map _unusedTouches; int _touchPointCount; @@ -116,8 +168,9 @@ protected: float _fixedRadiusForCalc; int _extraBottomMargin {0}; - glm::vec2 _jumpButtonPosition; - float _jumpButtonRadius; + float _buttonRadius; + + TouchscreenButtonsManager _buttonsManager; void moveTouchBegin(glm::vec2 touchPoint); void moveTouchUpdate(glm::vec2 touchPoint); @@ -129,11 +182,6 @@ protected: void viewTouchEnd(); bool viewTouchBeginIsValid(glm::vec2 touchPoint); - void jumpTouchBegin(glm::vec2 touchPoint); - void jumpTouchUpdate(glm::vec2 touchPoint); - void jumpTouchEnd(); - bool jumpTouchBeginIsValid(glm::vec2 touchPoint); - void setupControlsPositions(VirtualPad::Manager& virtualPadManager, bool force = false); void processInputDeviceForMove(VirtualPad::Manager& virtualPadManager); diff --git a/libraries/material-networking/src/material-networking/TextureCache.cpp b/libraries/material-networking/src/material-networking/TextureCache.cpp index 9a9720c87d..43f467266a 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.cpp +++ b/libraries/material-networking/src/material-networking/TextureCache.cpp @@ -354,7 +354,8 @@ NetworkTexture::NetworkTexture(const NetworkTexture& other) : _originalHeight(other._originalHeight), _width(other._width), _height(other._height), - _maxNumPixels(other._maxNumPixels) + _maxNumPixels(other._maxNumPixels), + _content(other._content) { if (_width == 0 || _height == 0 || other._currentlyLoadingResourceType == ResourceType::META || @@ -368,17 +369,30 @@ static bool isLocalUrl(const QUrl& url) { return (scheme == HIFI_URL_SCHEME_FILE || scheme == URL_SCHEME_QRC || scheme == RESOURCE_SCHEME); } -void NetworkTexture::setExtra(void* extra, bool isNewExtra) { +void NetworkTexture::setExtra(void* extra) { const TextureExtra* textureExtra = static_cast(extra); - _type = textureExtra ? textureExtra->type : image::TextureUsage::DEFAULT_TEXTURE; _maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; - _sourceChannel = textureExtra ? textureExtra->sourceChannel : image::ColorChannel::NONE; - if (isNewExtra && !_loaded) { + bool needsNewTextureSource = false; + auto type = textureExtra ? textureExtra->type : image::TextureUsage::DEFAULT_TEXTURE; + auto sourceChannel = textureExtra ? textureExtra->sourceChannel : image::ColorChannel::NONE; + if (type != _type || sourceChannel != _sourceChannel) { + needsNewTextureSource = true; + } + _type = type; + _sourceChannel = sourceChannel; + + auto content = textureExtra ? textureExtra->content : QByteArray(); + if (_content.isEmpty() && !content.isEmpty()) { + _content = content; + needsNewTextureSource = true; + } + + if (needsNewTextureSource) { _startedLoading = false; } - if (!_textureSource || isNewExtra) { + if (!_textureSource || needsNewTextureSource) { _textureSource = std::make_shared(_url, (int)_type); } _lowestRequestedMipLevel = 0; @@ -405,10 +419,9 @@ void NetworkTexture::setExtra(void* extra, bool isNewExtra) { } // if we have content, load it after we have our self pointer - auto content = textureExtra ? textureExtra->content : QByteArray(); - if (!content.isEmpty()) { + if (!_content.isEmpty()) { _startedLoading = true; - QMetaObject::invokeMethod(this, "downloadFinished", Qt::QueuedConnection, Q_ARG(const QByteArray&, content)); + QMetaObject::invokeMethod(this, "downloadFinished", Qt::QueuedConnection, Q_ARG(const QByteArray&, _content)); } } diff --git a/libraries/material-networking/src/material-networking/TextureCache.h b/libraries/material-networking/src/material-networking/TextureCache.h index a8b152c40e..dcab527e4a 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.h +++ b/libraries/material-networking/src/material-networking/TextureCache.h @@ -64,7 +64,7 @@ public: Q_INVOKABLE void setOriginalDescriptor(ktx::KTXDescriptor* descriptor) { _originalKtxDescriptor.reset(descriptor); } - void setExtra(void* extra, bool isNewExtra) override; + void setExtra(void* extra) override; signals: void networkTextureCreated(const QWeakPointer& self); @@ -136,12 +136,12 @@ private: // mip offsets to change. ktx::KTXDescriptorPointer _originalKtxDescriptor; - int _originalWidth { 0 }; int _originalHeight { 0 }; int _width { 0 }; int _height { 0 }; int _maxNumPixels { ABSOLUTE_MAX_TEXTURE_NUM_PIXELS }; + QByteArray _content; friend class TextureCache; }; diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index b2645d20c8..581196b2cc 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -309,7 +309,7 @@ public: virtual void downloadFinished(const QByteArray& data) override; - void setExtra(void* extra, bool isNewExtra) override; + void setExtra(void* extra) override; protected: Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, QVariantHash mapping); @@ -320,7 +320,7 @@ private: bool _combineParts; }; -void GeometryDefinitionResource::setExtra(void* extra, bool isNewExtra) { +void GeometryDefinitionResource::setExtra(void* extra) { const GeometryExtra* geometryExtra = static_cast(extra); _mapping = geometryExtra ? geometryExtra->mapping : QVariantHash(); _textureBaseUrl = geometryExtra ? resolveTextureBaseUrl(_url, geometryExtra->textureBaseUrl) : QUrl(); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 8ad1b41020..7345081380 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -355,7 +355,7 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& } else if (resourcesWithExtraHash.size() > 0.0f) { // We haven't seen this extra info before, but we've already downloaded the resource. We need a new copy of this object (with any old hash). resource = createResourceCopy(resourcesWithExtraHash.begin().value().lock()); - resource->setExtra(extra, true); + resource->setExtra(extra); resource->setExtraHash(extraHash); resource->setSelf(resource); resource->setCache(this); @@ -375,7 +375,7 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& if (!resource) { resource = createResource(url); - resource->setExtra(extra, false); + resource->setExtra(extra); resource->setExtraHash(extraHash); resource->setSelf(resource); resource->setCache(this); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 62800a6ac2..2096213273 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -417,7 +417,7 @@ public: unsigned int getDownloadAttempts() { return _attempts; } unsigned int getDownloadAttemptsRemaining() { return _attemptsRemaining; } - virtual void setExtra(void* extra, bool isNewExtra) {}; + virtual void setExtra(void* extra) {}; void setExtraHash(size_t extraHash) { _extraHash = extraHash; } size_t getExtraHash() const { return _extraHash; } diff --git a/libraries/oculusMobile/src/ovr/VrHandler.cpp b/libraries/oculusMobile/src/ovr/VrHandler.cpp index de2b4e1ff6..b3b1416785 100644 --- a/libraries/oculusMobile/src/ovr/VrHandler.cpp +++ b/libraries/oculusMobile/src/ovr/VrHandler.cpp @@ -315,10 +315,6 @@ JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_nativeOn SURFACE.onCreate(env, obj); } -JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_nativeOnDestroy(JNIEnv*, jclass) { - __android_log_write(ANDROID_LOG_WARN, "QQQ_JNI", __FUNCTION__); -} - JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_nativeOnResume(JNIEnv*, jclass) { __android_log_write(ANDROID_LOG_WARN, "QQQ_JNI", __FUNCTION__); SURFACE.setResumed(true); diff --git a/libraries/pointers/src/PickManager.cpp b/libraries/pointers/src/PickManager.cpp index d3326ea8b4..0cf5f90e3d 100644 --- a/libraries/pointers/src/PickManager.cpp +++ b/libraries/pointers/src/PickManager.cpp @@ -6,6 +6,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "PickManager.h" +#include "PerfStat.h" +#include "Profile.h" PickManager::PickManager() { setShouldPickHUDOperator([]() { return false; }); @@ -119,10 +121,26 @@ void PickManager::update() { bool shouldPickHUD = _shouldPickHUDOperator(); // FIXME: give each type its own expiry // Each type will update at least one pick, regardless of the expiry - _updatedPickCounts[PickQuery::Stylus] = _stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false); - _updatedPickCounts[PickQuery::Ray] = _rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD); - _updatedPickCounts[PickQuery::Parabola] = _parabolaPickCacheOptimizer.update(cachedPicks[PickQuery::Parabola], _nextPickToUpdate[PickQuery::Parabola], expiry, shouldPickHUD); - _updatedPickCounts[PickQuery::Collision] = _collisionPickCacheOptimizer.update(cachedPicks[PickQuery::Collision], _nextPickToUpdate[PickQuery::Collision], expiry, false); + { + PROFILE_RANGE(picks, "StylusPicks"); + PerformanceTimer perfTimer("StylusPicks"); + _updatedPickCounts[PickQuery::Stylus] = _stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false); + } + { + PROFILE_RANGE(picks, "RayPicks"); + PerformanceTimer perfTimer("RayPicks"); + _updatedPickCounts[PickQuery::Ray] = _rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD); + } + { + PROFILE_RANGE(picks, "ParabolaPick"); + PerformanceTimer perfTimer("ParabolaPick"); + _updatedPickCounts[PickQuery::Parabola] = _parabolaPickCacheOptimizer.update(cachedPicks[PickQuery::Parabola], _nextPickToUpdate[PickQuery::Parabola], expiry, shouldPickHUD); + } + { + PROFILE_RANGE(picks, "CollisoinPicks"); + PerformanceTimer perfTimer("CollisionPicks"); + _updatedPickCounts[PickQuery::Collision] = _collisionPickCacheOptimizer.update(cachedPicks[PickQuery::Collision], _nextPickToUpdate[PickQuery::Collision], expiry, false); + } } bool PickManager::isLeftHand(unsigned int uid) { diff --git a/libraries/render-utils/src/parabola_forward.slv b/libraries/render-utils/src/parabola_forward.slv new file mode 100644 index 0000000000..4eb1456666 --- /dev/null +++ b/libraries/render-utils/src/parabola_forward.slv @@ -0,0 +1,7 @@ +layout(location=0) in vec4 _color; + +layout(location=0) out vec4 _fragColor0; + +void main(void) { + _fragColor0 = _color; +} \ No newline at end of file diff --git a/libraries/shaders/headers/310es/header.glsl b/libraries/shaders/headers/310es/header.glsl index 9a0af85281..a21b2ec5d6 100644 --- a/libraries/shaders/headers/310es/header.glsl +++ b/libraries/shaders/headers/310es/header.glsl @@ -1,6 +1,3 @@ -#version 310 es -#define GPU_GLES -#define GPU_GLES_310 #define BITFIELD highp int #define LAYOUT(X) layout(X) #define LAYOUT_STD140(X) layout(std140, X) @@ -9,6 +6,9 @@ #define gl_VertexID gl_VertexIndex #endif #extension GL_EXT_texture_buffer : enable +#if defined(HAVE_EXT_clip_cull_distance) && !defined(VULKAN) +#extension GL_EXT_clip_cull_distance : enable +#endif precision highp float; precision highp samplerBuffer; precision highp sampler2DShadow; diff --git a/libraries/shaders/headers/310es/version.glsl b/libraries/shaders/headers/310es/version.glsl new file mode 100644 index 0000000000..52e5edea8b --- /dev/null +++ b/libraries/shaders/headers/310es/version.glsl @@ -0,0 +1,3 @@ +#version 310 es +#define GPU_GLES +#define GPU_GLES_310 diff --git a/libraries/shaders/headers/410/header.glsl b/libraries/shaders/headers/410/header.glsl index 901ae6f9db..20bd6b2505 100644 --- a/libraries/shaders/headers/410/header.glsl +++ b/libraries/shaders/headers/410/header.glsl @@ -1,5 +1,3 @@ -#version 410 core -#define GPU_GL410 #define BITFIELD int #if defined(VULKAN) #extension GL_ARB_shading_language_420pack : require diff --git a/libraries/shaders/headers/410/version.glsl b/libraries/shaders/headers/410/version.glsl new file mode 100644 index 0000000000..01ab7e5398 --- /dev/null +++ b/libraries/shaders/headers/410/version.glsl @@ -0,0 +1,2 @@ +#version 410 core +#define GPU_GL410 diff --git a/libraries/shaders/headers/450/header.glsl b/libraries/shaders/headers/450/header.glsl index 6ce61b4378..ef0ec09414 100644 --- a/libraries/shaders/headers/450/header.glsl +++ b/libraries/shaders/headers/450/header.glsl @@ -1,5 +1,3 @@ -#version 450 core -#define GPU_GL450 #define GPU_SSBO_TRANSFORM_OBJECT #define BITFIELD int #define LAYOUT(X) layout(X) diff --git a/libraries/shaders/headers/450/version.glsl b/libraries/shaders/headers/450/version.glsl new file mode 100644 index 0000000000..874d578bd9 --- /dev/null +++ b/libraries/shaders/headers/450/version.glsl @@ -0,0 +1,2 @@ +#version 450 core +#define GPU_GL450 diff --git a/libraries/shaders/src/shaders/Shaders.cpp b/libraries/shaders/src/shaders/Shaders.cpp index c7e39b2940..a074c2f0c9 100644 --- a/libraries/shaders/src/shaders/Shaders.cpp +++ b/libraries/shaders/src/shaders/Shaders.cpp @@ -224,7 +224,7 @@ String Source::getSource(Dialect dialect, Variant variant) const { } } -#ifdef Q_OS_ANDROID +#if defined(Q_OS_ANDROID) || defined(USE_GLES) // SPIRV cross injects "#extension GL_OES_texture_buffer : require" into the GLSL shaders, // which breaks android rendering return variantSource.scribe; diff --git a/libraries/shared/src/Profile.cpp b/libraries/shared/src/Profile.cpp index 778b39aca5..272538e26d 100644 --- a/libraries/shared/src/Profile.cpp +++ b/libraries/shared/src/Profile.cpp @@ -12,6 +12,7 @@ Q_LOGGING_CATEGORY(trace_app, "trace.app") Q_LOGGING_CATEGORY(trace_app_detail, "trace.app.detail") Q_LOGGING_CATEGORY(trace_metadata, "trace.metadata") Q_LOGGING_CATEGORY(trace_network, "trace.network") +Q_LOGGING_CATEGORY(trace_picks, "trace.picks") Q_LOGGING_CATEGORY(trace_parse, "trace.parse") Q_LOGGING_CATEGORY(trace_render, "trace.render") Q_LOGGING_CATEGORY(trace_render_detail, "trace.render.detail") diff --git a/libraries/shared/src/Profile.h b/libraries/shared/src/Profile.h index 01d86f8f2e..dc2ed6e754 100644 --- a/libraries/shared/src/Profile.h +++ b/libraries/shared/src/Profile.h @@ -18,6 +18,7 @@ Q_DECLARE_LOGGING_CATEGORY(trace_app) Q_DECLARE_LOGGING_CATEGORY(trace_app_detail) Q_DECLARE_LOGGING_CATEGORY(trace_metadata) Q_DECLARE_LOGGING_CATEGORY(trace_network) +Q_DECLARE_LOGGING_CATEGORY(trace_picks) Q_DECLARE_LOGGING_CATEGORY(trace_render) Q_DECLARE_LOGGING_CATEGORY(trace_render_detail) Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu) diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index ec1126c92f..47549b639a 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -1269,7 +1269,14 @@ QVariantMap parseTexturesToMap(QString newTextures, const QVariantMap& defaultTe QVariantMap newTexturesMap = newTexturesJson.toVariant().toMap(); QVariantMap toReturn = defaultTextures; for (auto& texture : newTexturesMap.keys()) { - toReturn[texture] = newTexturesMap[texture]; + auto newURL = newTexturesMap[texture]; + if (newURL.canConvert()) { + toReturn[texture] = newURL.toUrl(); + } else if (newURL.canConvert()) { + toReturn[texture] = QUrl(newURL.toString()); + } else { + toReturn[texture] = newURL; + } } return toReturn; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index d3ed79faf4..19fafdccf4 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -67,6 +67,7 @@ const QUuid SpatiallyNestable::getParentID() const { } void SpatiallyNestable::setParentID(const QUuid& parentID) { + bumpAncestorChainRenderableVersion(); _idLock.withWriteLock([&] { if (_parentID != parentID) { _parentID = parentID; @@ -78,6 +79,7 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) { bool success = false; auto parent = getParentPointer(success); if (success && parent) { + bumpAncestorChainRenderableVersion(); parent->updateQueryAACube(); } } @@ -1337,8 +1339,6 @@ QString SpatiallyNestable::nestableTypeToString(NestableType nestableType) { return "entity"; case NestableType::Avatar: return "avatar"; - case NestableType::Overlay: - return "overlay"; default: return "unknown"; } @@ -1419,3 +1419,12 @@ QUuid SpatiallyNestable::getEditSenderID() { }); return editSenderID; } + +void SpatiallyNestable::bumpAncestorChainRenderableVersion() const { + _ancestorChainRenderableVersion++; + bool success = false; + auto parent = getParentPointer(success); + if (success && parent) { + parent->bumpAncestorChainRenderableVersion(); + } +} \ No newline at end of file diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index e7a449f73f..495c941c07 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -30,8 +30,7 @@ static const uint16_t INVALID_JOINT_INDEX = -1; enum class NestableType { Entity, - Avatar, - Overlay + Avatar }; class SpatiallyNestable : public std::enable_shared_from_this { @@ -222,6 +221,8 @@ public: bool hasGrabs(); virtual QUuid getEditSenderID(); + void bumpAncestorChainRenderableVersion() const; + protected: QUuid _id; mutable SpatiallyNestableWeakPointer _parent; @@ -244,6 +245,8 @@ protected: mutable ReadWriteLockable _grabsLock; QSet _grabs; // upon this thing + mutable std::atomic _ancestorChainRenderableVersion { 0 }; + private: SpatiallyNestable() = delete; const NestableType _nestableType; // EntityItem or an AvatarData diff --git a/libraries/shared/src/VariantMapToScriptValue.cpp b/libraries/shared/src/VariantMapToScriptValue.cpp index 1a747a4e5b..437f60a2ed 100644 --- a/libraries/shared/src/VariantMapToScriptValue.cpp +++ b/libraries/shared/src/VariantMapToScriptValue.cpp @@ -26,10 +26,10 @@ QScriptValue variantToScriptValue(QVariant& qValue, QScriptEngine& scriptEngine) case QVariant::Double: return qValue.toDouble(); break; - case QVariant::String: { + case QVariant::String: + case QVariant::Url: return scriptEngine.newVariant(qValue); break; - } case QVariant::Map: { QVariantMap childMap = qValue.toMap(); return variantMapToScriptValue(childMap, scriptEngine); diff --git a/libraries/ui/src/QmlFragmentClass.cpp b/libraries/ui/src/QmlFragmentClass.cpp index 13e3527ded..fbd045fdb1 100644 --- a/libraries/ui/src/QmlFragmentClass.cpp +++ b/libraries/ui/src/QmlFragmentClass.cpp @@ -24,7 +24,7 @@ QmlFragmentClass::QmlFragmentClass(bool restricted, QString id) : QmlWindowClass // Method called by Qt scripts to create a new bottom menu bar in Android QScriptValue QmlFragmentClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) { - +#ifndef DISABLE_QML std::lock_guard guard(_mutex); auto qml = context->argument(0).toVariant().toMap().value("qml"); if (qml.isValid()) { @@ -53,6 +53,9 @@ QScriptValue QmlFragmentClass::internal_constructor(QScriptContext* context, QSc QScriptValue scriptObject = engine->newQObject(retVal); _fragments[qml.toString()] = scriptObject; return scriptObject; +#else + return QScriptValue(); +#endif } void QmlFragmentClass::close() { @@ -61,6 +64,7 @@ void QmlFragmentClass::close() { } QObject* QmlFragmentClass::addButton(const QVariant& properties) { +#ifndef DISABLE_QML QVariant resultVar; Qt::ConnectionType connectionType = Qt::AutoConnection; @@ -79,8 +83,10 @@ QObject* QmlFragmentClass::addButton(const QVariant& properties) { qWarning() << "QmlFragmentClass addButton result not a QObject"; return NULL; } - return qmlButton; +#else + return nullptr; +#endif } void QmlFragmentClass::removeButton(QObject* button) { diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 0182e3adc3..1140dbb079 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -98,6 +98,7 @@ QmlWindowClass::QmlWindowClass(bool restricted) : _restricted(restricted) { * @property {boolean} visible */ void QmlWindowClass::initQml(QVariantMap properties) { +#ifndef DISABLE_QML auto offscreenUi = DependencyManager::get(); _source = properties[SOURCE_PROPERTY].toString(); @@ -150,6 +151,7 @@ void QmlWindowClass::initQml(QVariantMap properties) { Q_ASSERT(_qmlWindow); Q_ASSERT(dynamic_cast(_qmlWindow.data())); +#endif } void QmlWindowClass::qmlToScript(const QVariant& message) { diff --git a/libraries/ui/src/VirtualPadManager.cpp b/libraries/ui/src/VirtualPadManager.cpp index ef2b8670cc..bf325a0bd6 100644 --- a/libraries/ui/src/VirtualPadManager.cpp +++ b/libraries/ui/src/VirtualPadManager.cpp @@ -38,10 +38,10 @@ namespace VirtualPad { const float Manager::BASE_DIAMETER_PIXELS = 512.0f; const float Manager::BASE_MARGIN_PIXELS = 59.0f; const float Manager::STICK_RADIUS_PIXELS = 105.0f; - const float Manager::JUMP_BTN_TRIMMED_RADIUS_PIXELS = 67.0f; - const float Manager::JUMP_BTN_FULL_PIXELS = 164.0f; - const float Manager::JUMP_BTN_BOTTOM_MARGIN_PIXELS = 80.0f; - const float Manager::JUMP_BTN_RIGHT_MARGIN_PIXELS = 13.0f; + const float Manager::BTN_TRIMMED_RADIUS_PIXELS = 67.0f; + const float Manager::BTN_FULL_PIXELS = 164.0f; + const float Manager::BTN_BOTTOM_MARGIN_PIXELS = 80.0f; + const float Manager::BTN_RIGHT_MARGIN_PIXELS = 13.0f; Manager::Manager() { @@ -76,14 +76,6 @@ namespace VirtualPad { _extraBottomMargin = margin; } - glm::vec2 Manager::getJumpButtonPosition() { - return _jumpButtonPosition; - } - - void Manager::setJumpButtonPosition(glm::vec2 point) { - _jumpButtonPosition = point; - } - void Manager::requestHapticFeedback(int duration) { emit hapticFeedbackRequested(duration); } @@ -92,6 +84,17 @@ namespace VirtualPad { return &_leftVPadInstance; } + glm::vec2 Manager::getButtonPosition(Manager::Button button) { + if (_buttonsPositions.count(button)) { + return _buttonsPositions.at(button); + } + return glm::vec2(); + } + + void Manager::setButtonPosition(Manager::Button button, glm::vec2 point) { + _buttonsPositions[button] = point; + } + bool Instance::isShown() { return _shown; } diff --git a/libraries/ui/src/VirtualPadManager.h b/libraries/ui/src/VirtualPadManager.h index 3c3aa9ec9f..a1f2e17597 100644 --- a/libraries/ui/src/VirtualPadManager.h +++ b/libraries/ui/src/VirtualPadManager.h @@ -44,28 +44,35 @@ namespace VirtualPad { void hide(bool hide); int extraBottomMargin(); void setExtraBottomMargin(int margin); - glm::vec2 getJumpButtonPosition(); - void setJumpButtonPosition(glm::vec2 point); + + enum Button { + JUMP, + HANDSHAKE + }; + + glm::vec2 getButtonPosition(Button button); + void setButtonPosition(Button button, glm::vec2 point); + void requestHapticFeedback(int duration); static const float DPI; static const float BASE_DIAMETER_PIXELS; static const float BASE_MARGIN_PIXELS; static const float STICK_RADIUS_PIXELS; - static const float JUMP_BTN_TRIMMED_RADIUS_PIXELS; - static const float JUMP_BTN_FULL_PIXELS; - static const float JUMP_BTN_BOTTOM_MARGIN_PIXELS; - static const float JUMP_BTN_RIGHT_MARGIN_PIXELS; + static const float BTN_TRIMMED_RADIUS_PIXELS; + static const float BTN_FULL_PIXELS; + static const float BTN_BOTTOM_MARGIN_PIXELS; + static const float BTN_RIGHT_MARGIN_PIXELS; signals: void hapticFeedbackRequested(int duration); private: Instance _leftVPadInstance; - bool _enabled {true}; + bool _enabled { true }; bool _hidden; - glm::vec2 _jumpButtonPosition; - int _extraBottomMargin {0}; + int _extraBottomMargin { 0 }; + std::map _buttonsPositions; }; } diff --git a/prebuild.py b/prebuild.py index 060e1fd3b0..5325ca34bc 100644 --- a/prebuild.py +++ b/prebuild.py @@ -58,6 +58,9 @@ logging.setLoggerClass(TrackableLogger) logger = logging.getLogger('prebuild') def headSha(): + if shutil.which('git') is None: + logger.warn("Unable to find git executable, can't caclulate commit ID") + return '0xDEADBEEF' repo_dir = os.path.dirname(os.path.abspath(__file__)) git = subprocess.Popen( 'git rev-parse --short HEAD', @@ -67,7 +70,7 @@ def headSha(): stdout, _ = git.communicate() sha = stdout.split('\n')[0] if not sha: - raise RuntimeError("couldn't find git sha") + raise RuntimeError("couldn't find git sha for repository {}".format(repo_dir)) return sha @contextmanager diff --git a/scripts/+android_interface/defaultScripts.js b/scripts/+android_interface/defaultScripts.js index e6971f5a6b..8b3082d81a 100644 --- a/scripts/+android_interface/defaultScripts.js +++ b/scripts/+android_interface/defaultScripts.js @@ -16,7 +16,8 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/+android_interface/touchscreenvirtualpad.js", "system/+android_interface/actionbar.js", "system/+android_interface/audio.js" , - "system/+android_interface/modes.js"/*, + "system/+android_interface/modes.js", + "system/makeUserConnection.js"/*, "system/away.js", "system/controllers/controllerDisplayManager.js", "system/controllers/handControllerGrabAndroid.js", diff --git a/scripts/system/controllers/+android_questInterface/controllerScripts.js b/scripts/system/controllers/+android_questInterface/controllerScripts.js new file mode 100644 index 0000000000..d313efaca1 --- /dev/null +++ b/scripts/system/controllers/+android_questInterface/controllerScripts.js @@ -0,0 +1,58 @@ +"use strict"; + +// controllerScripts.js +// +// Created by David Rowe on 15 Mar 2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* global Script, Menu */ + +var CONTOLLER_SCRIPTS = [ + "squeezeHands.js", + "controllerDisplayManager.js", + "toggleAdvancedMovementForHandControllers.js", + "controllerDispatcher.js", + "controllerModules/nearParentGrabOverlay.js", + "controllerModules/stylusInput.js", + "controllerModules/equipEntity.js", + "controllerModules/nearTrigger.js", + "controllerModules/webSurfaceLaserInput.js", + "controllerModules/inVREditMode.js", + "controllerModules/disableOtherModule.js", + "controllerModules/farTrigger.js", + "controllerModules/teleport.js", + "controllerModules/hudOverlayPointer.js", + "controllerModules/scaleEntity.js", + "controllerModules/nearGrabHyperLinkEntity.js", + "controllerModules/nearTabletHighlight.js", + "controllerModules/nearGrabEntity.js", + "controllerModules/farGrabEntity.js" +]; + +var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; + +function runDefaultsTogether() { + for (var j in CONTOLLER_SCRIPTS) { + if (CONTOLLER_SCRIPTS.hasOwnProperty(j)) { + Script.include(CONTOLLER_SCRIPTS[j]); + } + } +} + +function runDefaultsSeparately() { + for (var i in CONTOLLER_SCRIPTS) { + if (CONTOLLER_SCRIPTS.hasOwnProperty(i)) { + Script.load(CONTOLLER_SCRIPTS[i]); + } + } +} + +if (Menu.isOptionChecked(DEBUG_MENU_ITEM)) { + runDefaultsSeparately(); +} else { + runDefaultsTogether(); +} diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 2a5cf5a727..28c3e2a299 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -497,6 +497,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); distanceScaleEnd: true, hand: RIGHT_HAND }); + this.mouseRayPick = Pointers.createPointer(PickType.Ray, { joint: "Mouse", filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, diff --git a/tools/shadergen.py b/tools/shadergen.py index ffbe1662ec..f82b471f17 100644 --- a/tools/shadergen.py +++ b/tools/shadergen.py @@ -49,11 +49,36 @@ def getCommonScribeArgs(scribefile, includeLibs): scribeArgs.append(scribefile) return scribeArgs -def getDialectAndVariantHeaders(dialect, variant): +extensionsHeaderMutex = Lock() + +def getExtensionsHeader(dialect, variant, extensions): + extensionHeader = '{}/extensions_{}_{}.glsl'.format(args.build_dir, dialect, variant) + global extensionsHeaderMutex + extensionsHeaderMutex.acquire() + if not os.path.exists(extensionHeader): + extensionsDefines = [] + for extension in extensions: + extensionsDefines.append('#define HAVE_{}'.format(extension)) + # make sure we end with a line feed + extensionsDefines.append("\r\n") + with open(extensionHeader, "w") as f: + f.write('\r\n'.join(extensionsDefines)) + extensionsHeaderMutex.release() + return extensionHeader + + +def getDialectAndVariantHeaders(dialect, variant, extensions=None): + result = [] headerPath = args.source_dir + '/libraries/shaders/headers/' - variantHeader = headerPath + ('stereo.glsl' if (variant == 'stereo') else 'mono.glsl') + versionHeader = headerPath + dialect + '/version.glsl' + result.append(versionHeader) + if extensions is not None: + result.append(getExtensionsHeader(dialect, variant, extensions)) dialectHeader = headerPath + dialect + '/header.glsl' - return [dialectHeader, variantHeader] + result.append(dialectHeader) + variantHeader = headerPath + ('stereo.glsl' if (variant == 'stereo') else 'mono.glsl') + result.append(variantHeader) + return result class ScribeDependenciesCache: cache = {} @@ -170,7 +195,7 @@ def processCommand(line): scribeDepCache.gen(scribeFile, libs, dialect, variant) scribeArgs = getCommonScribeArgs(scribeFile, libs) - for header in getDialectAndVariantHeaders(dialect, variant): + for header in getDialectAndVariantHeaders(dialect, variant, args.extensions): scribeArgs.extend(['-H', header]) scribeArgs.extend(['-o', unoptGlslFile]) executeSubprocess(scribeArgs) @@ -218,6 +243,7 @@ def main(): parser = ArgumentParser(description='Generate shader artifacts.') +parser.add_argument('--extensions', type=str, nargs='*', help='Available extensions for the shaders') parser.add_argument('--commands', type=argparse.FileType('r'), help='list of commands to execute') parser.add_argument('--tools-dir', type=str, help='location of the host compatible binaries') parser.add_argument('--build-dir', type=str, help='The build directory base path') @@ -230,8 +256,8 @@ args = None if len(sys.argv) == 1: # for debugging sourceDir = expanduser('~/git/hifi') - toolsDir = os.path.join(expanduser('~/git/vcpkg'), 'installed', 'x64-windows', 'tools') - buildPath = sourceDir + '/build' + toolsDir = 'd:/hifi/vcpkg/android/fd82f0a8/installed/x64-windows/tools' + buildPath = sourceDir + '/build_android' commandsPath = buildPath + '/libraries/shaders/shadergen.txt' shaderDir = buildPath + '/libraries/shaders' testArgs = '--commands {} --tools-dir {} --build-dir {} --source-dir {}'.format( @@ -239,6 +265,7 @@ if len(sys.argv) == 1: ).split() testArgs.append('--debug') testArgs.append('--force') + testArgs.extend('--extensions EXT_clip_cull_distance'.split()) #testArgs.append('--dry-run') args = parser.parse_args(testArgs) else: