Merge remote-tracking branch 'upstream/master' into animSplineIK

This commit is contained in:
amantley 2019-02-21 14:36:13 -08:00
commit c9db426ce4
116 changed files with 2193 additions and 978 deletions

1
.gitignore vendored
View file

@ -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

65
BUILD_QUEST.md Normal file
View file

@ -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=<your_home_directory>/Android/hifi_externals
HIFI_ANDROID_KEYSTORE=<key_store_directory>/<keystore_name>.jks
HIFI_ANDROID_KEYSTORE_PASSWORD=<password>
HIFI_ANDROID_KEY_ALIAS=<key_alias>
HIFI_ANDROID_KEY_PASSWORD=<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_

View file

@ -101,6 +101,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 ()

View file

@ -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

View file

@ -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()

View file

@ -19,24 +19,6 @@
android:name="org.qtproject.qt5.android.bindings.QtApplication"
tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon">
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<activity
android:name=".QuestQtActivity"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:label="@string/app_name"
android:screenOrientation="landscape"
android:excludeFromRecents="false"
android:alwaysRetainTaskState="true"
android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode"
>
<!-- JNI nonsense -->
<meta-data android:name="android.app.lib_name" android:value="questFramePlayer"/>
<!-- Qt nonsense -->
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
</activity>
<activity
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
@ -50,6 +32,13 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- JNI nonsense -->
<meta-data android:name="android.app.lib_name" android:value="questFramePlayer"/>
<!-- Qt nonsense -->
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
</activity>
</application>
</manifest>

View file

@ -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 <QtCore/QDebug>
#include <QtGui/QGuiApplication>
AndroidHelper::AndroidHelper() {
}
AndroidHelper::~AndroidHelper() {
}
void AndroidHelper::notifyLoadComplete() {
emit qtAppLoadComplete();
}
void AndroidHelper::notifyEnterForeground() {
emit enterForeground();
}
void AndroidHelper::notifyEnterBackground() {
emit enterBackground();
}

View file

@ -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 <QtCore/QObject>
#include <QtCore/QMap>
#include <QtCore/QUrl>
#include <QtCore/QEventLoop>
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

View file

@ -11,15 +11,11 @@
#include <QtWidgets/QFileDialog>
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();
}

View file

@ -8,22 +8,13 @@
#pragma once
#include <QtGui/QWindow>
#include <QtCore/QSettings>
#include <gpu/Forward.h>
#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;
};

View file

@ -16,6 +16,7 @@
#include <QtCore/QFileInfo>
#include <QtGui/QWindow>
#include <QtGui/QImageReader>
#include <QtAndroidExtras/QAndroidJniObject>
#include <gl/QOpenGLContextWrapper.h>
#include <gpu/FrameIO.h>
@ -29,9 +30,7 @@
#include <VrApi.h>
#include <VrApi_Input.h>
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<HandController> 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<void>("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<std::mutex> 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<std::mutex> 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 {

View file

@ -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<gpu::Backend> _backend;
std::atomic<size_t> _presentCount{ 0 };
std::mutex _frameLock;
std::queue<gpu::FramePointer> _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();
};

View file

@ -11,30 +11,33 @@
#include <QtGui/QGuiApplication>
#include <QtCore/QTimer>
#include <QtCore/QFileInfo>
#include <QtAndroidExtras/QAndroidJniObject>
#include <Trace.h>
#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<tracing::Tracer>();
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;
}

View file

@ -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);
}
}
}

View file

@ -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));
}
}

View file

@ -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)

View file

@ -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<String>()
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')
}

View file

@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Android\SDK/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.highfidelity.questInterface"
android:installLocation="auto">
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true"/>
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="true"/>
<uses-feature android:name="android.software.vr.mode" android:required="true"/>
<uses-feature android:name="android.hardware.vr.high_performance" android:required="true"/>
<application
android:name="org.qtproject.qt5.android.bindings.QtApplication"
android:label="@string/app_name"
android:allowBackup="true">
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<activity
android:name=".PermissionsChecker"
android:launchMode="singleTask"
android:screenOrientation="landscape"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.INFO" />
</intent-filter>
</activity>
<activity
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
android:name=".InterfaceActivity"
android:label="@string/app_name"
android:launchMode="singleTask">
<meta-data android:name="android.app.lib_name" android:value="questInterface"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
<meta-data android:name="android.app.background_running" android:value="false"/>
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,106 @@
#include <functional>
#include <QtCore/QBuffer>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QThread>
#include <QtCore/QStringList>
#include <QtCore/QStandardPaths>
#include <QtCore/QTextStream>
#include <QtCore/QObject>
#include <QtAndroidExtras/QAndroidJniObject>
#include <QtAndroidExtras/QtAndroid>
#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <shared/Storage.h>
#include <AddressManager.h>
#include <AndroidHelper.h>
#include <udt/PacketHeaders.h>
#include <OVR_Platform.h>
#include <OVR_Functions_Voip.h>
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<void>("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);
}
}

View file

@ -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);
}
}

View file

@ -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();
}
}
}

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--suppress AndroidUnknownAttribute -->
<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="192"
android:viewportHeight="192"
android:width="192dp"
android:height="192dp">
<path
android:pathData="M189.5 96.5A93.5 93.5 0 0 1 96 190 93.5 93.5 0 0 1 2.5 96.5 93.5 93.5 0 0 1 96 3 93.5 93.5 0 0 1 189.5 96.5Z"
android:fillColor="#333333" />
<path
android:pathData="M96.2 173.1c-10.3 0 -20.4 -2.1 -29.8 -6 -9.2 -3.8 -17.3 -9.4 -24.3 -16.4 -7 -7 -12.6 -15.2 -16.4 -24.3 -4.1 -9.6 -6.2 -19.6 -6.2 -30 0 -10.3 2.1 -20.4 6 -29.8 3.8 -9.2 9.4 -17.3 16.4 -24.3 7 -7 15.2 -12.6 24.3 -16.4 9.5 -4 19.5 -6 29.8 -6 10.3 0 20.4 2.1 29.8 6 9.2 3.8 17.3 9.4 24.3 16.4 7 7 12.6 15.2 16.4 24.3 4 9.5 6 19.5 6 29.8 0 10.3 -2.1 20.4 -6 29.8 -3.8 9.2 -9.4 17.3 -16.4 24.3 -7 7 -15.2 12.6 -24.3 16.4 -9.2 4.1 -19.3 6.2 -29.6 6.2zm0 -145.3c-37.8 0 -68.6 30.8 -68.6 68.6 0 37.8 30.8 68.6 68.6 68.6 37.8 0 68.6 -30.8 68.6 -68.6 0 -37.8 -30.8 -68.6 -68.6 -68.6z"
android:fillColor="#00b4f0" />
<path
android:pathData="M119.6 129l0 -53.8c3.4 -1.1 5.8 -4.3 5.8 -8 0 -4.6 -3.8 -8.4 -8.4 -8.4 -4.6 0 -8.4 3.8 -8.4 8.4 0 3.6 2.2 6.6 5.4 7.9l0 25L79 83.8 79 64c3.4 -1.1 5.8 -4.3 5.8 -8 0 -4.6 -3.8 -8.4 -8.4 -8.4 -4.6 0 -8.4 3.8 -8.4 8.4 0 3.6 2.2 6.6 5.4 7.9l0 54.1c-3.1 1.2 -5.4 4.3 -5.4 7.9 0 4.6 3.8 8.4 8.4 8.4 4.6 0 8.4 -3.8 8.4 -8.4 0 -3.7 -2.4 -6.9 -5.8 -8l0 -27.3 35 16.3 0 22.2c-3.1 1.2 -5.4 4.3 -5.4 7.9 0 4.6 3.8 8.4 8.4 8.4 4.6 0 8.4 -3.8 8.4 -8.4 0 -3.8 -2.4 -6.9 -5.8 -8z"
android:fillColor="#00b4f0" />
</vector>

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name" translatable="false">Interface</string>
</resources>

View file

@ -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')

View file

@ -1 +1,2 @@
org.gradle.jvmargs=-Xms2g -Xmx4g
android.debug.obsoleteApi=true

View file

@ -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

View file

@ -15,3 +15,7 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation project(path: ':qt')
}

View file

@ -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;
}
}

View file

@ -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();
}
//---------------------------------------------------------------------------

View file

@ -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')
}

View file

@ -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})

View file

@ -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']
},

View file

@ -23,7 +23,9 @@
"invert"
],
"to": "Actions.Pitch"
}
},
{ "from": "TouchscreenVirtualPad.RB", "to": "Standard.RB"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -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);

View file

@ -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
}

View file

@ -33,15 +33,19 @@ Rectangle {
property bool balanceReceived: false;
property bool availableUpdatesReceived: false;
property bool itemInfoReceived: false;
property bool dataReady: itemInfoReceived && ownershipStatusReceived && balanceReceived && availableUpdatesReceived;
property string baseItemName: "";
property string itemName;
property string itemId;
property string itemHref;
property string itemAuthor;
property int itemEdition: -1;
property bool hasSomethingToTradeIn: alreadyOwned && (itemEdition > 0); // i.e., don't trade in your artist's proof
property bool isTradingIn: isUpdating && hasSomethingToTradeIn;
property bool isStocking: availability === 'not for sale' && creator === Account.username;
property string certificateId;
property double balanceAfterPurchase;
property bool alreadyOwned: false;
property bool alreadyOwned: false; // Including proofs
property int itemPrice: -1;
property bool isCertified;
property string itemType: "unknown";
@ -56,6 +60,8 @@ Rectangle {
property string referrer;
property bool isInstalled;
property bool isUpdating;
property string availability: "available";
property string creator: "";
property string baseAppURL;
property int currentUpdatesPage: 1;
// Style
@ -434,7 +440,7 @@ Rectangle {
anchors.top: parent.top;
anchors.left: itemPreviewImage.right;
anchors.leftMargin: 12;
anchors.right: itemPriceContainer.left;
anchors.right: parent.right;
anchors.rightMargin: 8;
height: 30;
// Style
@ -449,21 +455,22 @@ Rectangle {
Item {
id: itemPriceContainer;
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
anchors.top: itemNameText.bottom;
anchors.topMargin: 8;
anchors.left: itemNameText.left;
height: 30;
width: itemPriceTextLabel.width + itemPriceText.width + 20;
width: itemPriceText.width + 20;
// "HFC" balance label
// "HFC" label
HiFiGlyphs {
id: itemPriceTextLabel;
visible: !(root.isUpdating && root.itemEdition > 0) && (root.itemPrice > 0);
visible: !isTradingIn && (root.itemPrice > 0);
text: hifi.glyphs.hfc;
// Size
size: 30;
// Anchors
anchors.right: itemPriceText.left;
anchors.rightMargin: 4;
anchors.right: parent.right;
//anchors.rightMargin: 4;
anchors.top: parent.top;
anchors.topMargin: 0;
width: paintedWidth;
@ -473,13 +480,15 @@ Rectangle {
}
FiraSansSemiBold {
id: itemPriceText;
text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : ((root.itemPrice > 0) ? root.itemPrice : "FREE"));
text: isTradingIn ? "FREE\nUPDATE" :
(isStocking ? "Free for creator" :
((root.itemPrice === -1) ? "--" : ((root.itemPrice > 0) ? root.itemPrice : "FREE")));
// Text size
size: (root.isUpdating && root.itemEdition > 0) ? 20 : 26;
size: isTradingIn ? 20 : 26;
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
anchors.rightMargin: 16;
anchors.left: itemPriceTextLabel.visible ? itemPriceTextLabel.right : parent.left;
anchors.leftMargin: 4;
width: paintedWidth;
height: paintedHeight;
// Style
@ -571,7 +580,7 @@ Rectangle {
// "View in Inventory" button
HifiControlsUit.Button {
id: viewInMyPurchasesButton;
visible: false;
visible: isCertified && dataReady && (isUpdating ? !hasSomethingToTradeIn : alreadyOwned);
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.top: buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top;
@ -592,8 +601,8 @@ Rectangle {
// "Buy" button
HifiControlsUit.Button {
id: buyButton;
visible: !((root.itemType === "avatar" || root.itemType === "app") && viewInMyPurchasesButton.visible)
enabled: (root.balanceAfterPurchase >= 0 && ownershipStatusReceived && balanceReceived && availableUpdatesReceived) || (!root.isCertified) || root.isUpdating;
visible: isTradingIn || !alreadyOwned || isStocking || !(root.itemType === "avatar" || root.itemType === "app");
enabled: (root.balanceAfterPurchase >= 0 && dataReady) || (!root.isCertified) || root.isUpdating;
color: viewInMyPurchasesButton.visible ? hifi.buttons.white : hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.top: viewInMyPurchasesButton.visible ? viewInMyPurchasesButton.bottom :
@ -602,10 +611,15 @@ Rectangle {
height: 50;
anchors.left: parent.left;
anchors.right: parent.right;
text: (root.isUpdating && root.itemEdition > 0) ? "CONFIRM UPDATE" : (((root.isCertified) ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ?
((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Get It Again" : "Confirm") : "--") : "Get Item"));
text: isTradingIn ?
"CONFIRM UPDATE" :
(((root.isCertified) ?
(dataReady ?
((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Get It Again" : "Confirm") :
"--") :
"Get Item"));
onClicked: {
if (root.isUpdating && root.itemEdition > 0) {
if (isTradingIn) {
// If we're updating an app, the existing app needs to be uninstalled.
// This call will fail/return `false` if the app isn't installed, but that's OK.
if (root.itemType === "app") {
@ -1063,7 +1077,11 @@ Rectangle {
buyButton.color = hifi.buttons.red;
root.shouldBuyWithControlledFailure = true;
} else {
buyButton.text = (root.isCertified ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item");
buyButton.text = (root.isCertified ?
(dataReady ?
(root.alreadyOwned ? "Buy Another" : "Buy") :
"--") :
"Get Item");
buyButton.color = hifi.buttons.blue;
root.shouldBuyWithControlledFailure = false;
}
@ -1091,6 +1109,8 @@ Rectangle {
root.itemPrice = result.data.cost;
root.itemAuthor = result.data.creator;
root.itemType = result.data.item_type || "unknown";
root.availability = result.data.availability;
root.creator = result.data.creator;
if (root.itemType === "unknown") {
root.itemHref = result.data.review_url;
} else {
@ -1139,7 +1159,7 @@ Rectangle {
signal sendToScript(var message);
function canBuyAgain() {
return (root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet" || root.itemType === "unknown");
return root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet" || root.itemType === "unknown" || isStocking;
}
function handleContentSets() {
@ -1185,29 +1205,23 @@ Rectangle {
function refreshBuyUI() {
if (root.isCertified) {
if (root.ownershipStatusReceived && root.balanceReceived && root.availableUpdatesReceived) {
if (dataReady) {
buyText.text = "";
// If the user IS on the checkout page for the updated version of an owned item...
if (root.isUpdating) {
// If the user HAS already selected a specific edition to update...
if (root.itemEdition > 0) {
if (hasSomethingToTradeIn) {
buyText.text = "By pressing \"Confirm Update\", you agree to trade in your old item for the updated item that replaces it.";
buyTextContainer.color = "#FFFFFF";
buyTextContainer.border.color = "#FFFFFF";
// Else if the user HAS NOT selected a specific edition to update...
} else {
viewInMyPurchasesButton.visible = true;
handleBuyAgainLogic();
}
// If the user IS NOT on the checkout page for the updated verison of an owned item...
// (i.e. they are checking out an item "normally")
} else {
if (root.alreadyOwned) {
viewInMyPurchasesButton.visible = true;
}
handleBuyAgainLogic();
}
} else {

View file

@ -121,7 +121,7 @@ Rectangle {
marketplaceItem.description = result.data.description;
marketplaceItem.attributions = result.data.attributions;
marketplaceItem.license = result.data.license;
marketplaceItem.available = result.data.availability === "available";
marketplaceItem.availability = result.data.availability;
marketplaceItem.created_at = result.data.created_at;
marketplaceItemScrollView.contentHeight = marketplaceItemContent.height;
itemsList.visible = false;
@ -539,7 +539,7 @@ Rectangle {
creator: model.creator
category: model.primary_category
price: model.cost
available: model.availability === "available"
availability: model.availability
isLoggedIn: root.isLoggedIn;
onShowItem: {
@ -711,7 +711,7 @@ Rectangle {
topMargin: 10;
leftMargin: 15;
}
height: visible ? childrenRect.height : 0
height: visible ? 36 : 0
RalewayRegular {
id: sortText
@ -733,8 +733,9 @@ Rectangle {
top: parent.top
leftMargin: 20
}
width: root.isLoggedIn ? 342 : 262
height: 36
height: parent.height
radius: 4
border.width: 1

View file

@ -33,11 +33,11 @@ Rectangle {
property string creator: ""
property var categories: []
property int price: 0
property string availability: "unknown"
property var attributions: []
property string description: ""
property string license: ""
property string posted: ""
property bool available: false
property string created_at: ""
property bool isLoggedIn: false;
property int edition: -1;
@ -264,9 +264,15 @@ Rectangle {
}
height: 50
text: root.edition >= 0 ? "UPGRADE FOR FREE" : (root.available ? (root.price ? root.price : "FREE") : "UNAVAILABLE (not for sale)")
enabled: root.edition >= 0 || root.available
buttonGlyph: root.available ? (root.price ? hifi.glyphs.hfc : "") : ""
property bool isNFS: availability === "not for sale" // Note: server will say "sold out" or "invalidated" before it says NFS
property bool isMine: creator === Account.username
property bool isUpgrade: root.edition >= 0
property int costToMe: ((isMine && isNFS) || isUpgrade) ? 0 : price
property bool isAvailable: costToMe >= 0
text: isUpgrade ? "UPGRADE FOR FREE" : (isAvailable ? (costToMe || "FREE") : availability)
enabled: isAvailable
buttonGlyph: isAvailable ? (costToMe ? hifi.glyphs.hfc : "") : ""
color: hifi.buttons.blue
onClicked: root.buy();

View file

@ -34,7 +34,7 @@ Rectangle {
property string creator: ""
property string category: ""
property int price: 0
property bool available: false
property string availability: "unknown"
property bool isLoggedIn: false;
signal buy()
@ -299,8 +299,16 @@ Rectangle {
bottomMargin: 10
}
text: root.price ? root.price : "FREE"
buttonGlyph: root.price ? hifi.glyphs.hfc : ""
property bool isNFS: availability === "not for sale" // Note: server will say "sold out" or "invalidated" before it says NFS
property bool isMine: creator === Account.username
property bool isUpgrade: root.edition >= 0
property int costToMe: ((isMine && isNFS) || isUpgrade) ? 0 : price
property bool isAvailable: costToMe >= 0
text: isUpgrade ? "UPGRADE FOR FREE" : (isAvailable ? (costToMe || "FREE") : availability)
enabled: isAvailable
buttonGlyph: isAvailable ? (costToMe ? hifi.glyphs.hfc : "") : ""
color: hifi.buttons.blue;
onClicked: root.buy();

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Application.h"
#include <chrono>
@ -37,7 +38,6 @@
#include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <QtQml/QQmlContext>
#include <QtQml/QQmlEngine>
#include <QtQuick/QQuickWindow>
@ -51,6 +51,7 @@
#include <QProcessEnvironment>
#include <QTemporaryDir>
#include <gl/QOpenGLContextWrapper.h>
#include <gl/GLWindow.h>
#include <gl/GLHelpers.h>
@ -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 <DesktopPreviewProvider.h>
#include "AboutUtil.h"
#if defined(Q_OS_WIN)
@ -622,8 +627,6 @@ public:
switch (type) {
case NestableType::Entity:
return getEntityModelProvider(static_cast<EntityItemID>(uuid));
case NestableType::Overlay:
return nullptr;
case NestableType::Avatar:
return getAvatarModelProvider(uuid);
}
@ -1747,7 +1750,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
@ -1850,6 +1853,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
this->installEventFilter(this);
#ifdef HAVE_DDE
auto ddeTracker = DependencyManager::get<DdeFaceTracker>();
ddeTracker->init();
@ -1911,46 +1916,34 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
}, Qt::QueuedConnection);
EntityTree::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
EntityTreeRenderer::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
if (_aboutToQuit) {
return false;
}
// try to find the renderable
auto renderable = getEntities()->renderableForEntityId(entityID);
if (renderable) {
renderable->addMaterial(material, parentMaterialName);
}
// even if we don't find it, try to find the entity
auto entity = getEntities()->getEntity(entityID);
if (entity) {
entity->addMaterial(material, parentMaterialName);
return true;
}
return false;
});
EntityTree::setRemoveMaterialFromEntityOperator([this](const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
EntityTreeRenderer::setRemoveMaterialFromEntityOperator([this](const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
if (_aboutToQuit) {
return false;
}
// try to find the renderable
auto renderable = getEntities()->renderableForEntityId(entityID);
if (renderable) {
renderable->removeMaterial(material, parentMaterialName);
}
// even if we don't find it, try to find the entity
auto entity = getEntities()->getEntity(entityID);
if (entity) {
entity->removeMaterial(material, parentMaterialName);
return true;
}
return false;
});
EntityTree::setAddMaterialToAvatarOperator([](const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
EntityTreeRenderer::setAddMaterialToAvatarOperator([](const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
auto avatarManager = DependencyManager::get<AvatarManager>();
auto avatar = avatarManager->getAvatarBySessionID(avatarID);
if (avatar) {
@ -1959,7 +1952,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
return false;
});
EntityTree::setRemoveMaterialFromAvatarOperator([](const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
EntityTreeRenderer::setRemoveMaterialFromAvatarOperator([](const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
auto avatarManager = DependencyManager::get<AvatarManager>();
auto avatar = avatarManager->getAvatarBySessionID(avatarID);
if (avatar) {
@ -2282,7 +2275,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Setup the mouse ray pick and related operators
{
auto mouseRayPick = std::make_shared<RayPick>(Vectors::ZERO, Vectors::UP, PickFilter(PickScriptingInterface::PICK_ENTITIES()), 0.0f, true);
auto mouseRayPick = std::make_shared<RayPick>(Vectors::ZERO, Vectors::UP, PickFilter(PickScriptingInterface::PICK_ENTITIES() | PickScriptingInterface::PICK_LOCAL_ENTITIES()), 0.0f, true);
mouseRayPick->parentTransform = std::make_shared<MouseTransformNode>();
mouseRayPick->setJointState(PickQuery::JOINT_STATE_MOUSE);
auto mouseRayPickID = DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, mouseRayPick);
@ -3099,7 +3092,7 @@ void Application::initializeUi() {
}
if (TouchscreenVirtualPadDevice::NAME == inputPlugin->getName()) {
_touchscreenVirtualPadDevice = std::dynamic_pointer_cast<TouchscreenVirtualPadDevice>(inputPlugin);
#if defined(Q_OS_ANDROID)
#if defined(ANDROID_APP_INTERFACE)
auto& virtualPadManager = VirtualPad::Manager::instance();
connect(&virtualPadManager, &VirtualPad::Manager::hapticFeedbackRequested,
this, [](int duration) {
@ -3631,10 +3624,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();
@ -8251,6 +8248,7 @@ void Application::loadDomainConnectionDialog() {
}
void Application::toggleLogDialog() {
#ifndef ANDROID_APP_QUEST_INTERFACE
if (getLoginDialogPoppedUp()) {
return;
}
@ -8274,6 +8272,7 @@ void Application::toggleLogDialog() {
} else {
_logDialog->show();
}
#endif
}
void Application::recreateLogWindow(int keepOnTop) {
@ -9139,17 +9138,23 @@ void Application::beforeEnterBackground() {
void Application::enterBackground() {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().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<AudioClient>().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>();
nodeList->setSendDomainServerCheckInEnabled(true);
}

View file

@ -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();

View file

@ -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();

View file

@ -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

View file

@ -137,13 +137,14 @@ PickResultPointer StylusPick::getDefaultResult(const QVariantMap& pickVariant) c
}
PickResultPointer StylusPick::getEntityIntersection(const StylusTip& pick) {
std::vector<StylusPickResult> results;
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;
}
@ -157,28 +158,24 @@ PickResultPointer StylusPick::getEntityIntersection(const StylusTip& pick) {
glm::vec3 normal = entityRotation * Vectors::UNIT_Z;
float distance = glm::dot(pick.position - entityPosition, normal);
glm::vec3 intersection = pick.position - (normal * distance);
glm::vec2 pos2D = RayPick::projectOntoEntityXYPlane(target, intersection, false);
if (pos2D == glm::clamp(pos2D, glm::vec2(0), glm::vec2(1))) {
IntersectionType type = IntersectionType::ENTITY;
if (getFilter().doesPickLocalEntities()) {
EntityPropertyFlags desiredProperties;
desiredProperties += PROP_ENTITY_HOST_TYPE;
if (DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(target, desiredProperties).getEntityHostType() == entity::HostType::LOCAL) {
type = IntersectionType::LOCAL_ENTITY;
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::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()) {
if (entity->getEntityHostType() == entity::HostType::LOCAL) {
type = IntersectionType::LOCAL_ENTITY;
}
}
nearestTarget = StylusPickResult(type, target, distance, intersection, pick, normal);
}
results.push_back(StylusPickResult(type, target, distance, intersection, pick, normal));
}
}
StylusPickResult nearestTarget(pick.toVariantMap());
for (const auto& result : results) {
if (result.distance < nearestTarget.distance) {
nearestTarget = result;
}
}
return std::make_shared<StylusPickResult>(nearestTarget);
}

View file

@ -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<QUuid> includeItems = _keyboardLayers[_layerIndex].keys().toVector();
auto pointerManager = DependencyManager::get<PointerManager>();
pointerManager->setIncludeItems(_leftHandStylus, includeItems);
pointerManager->setIncludeItems(_rightHandStylus, includeItems);
}
}
void Keyboard::updateTextDisplay() {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
@ -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<QUuid> 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>();
pointerManager->setIncludeItems(_leftHandStylus, includeItems);
pointerManager->setIncludeItems(_rightHandStylus, includeItems);
addIncludeItemsToMallets();
});
request->send();

View file

@ -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;
@ -178,7 +179,12 @@ private:
mutable ReadWriteLockable _handLaserLock;
mutable ReadWriteLockable _preferMalletsOverLasersSettingLock;
mutable ReadWriteLockable _ignoreItemsLock;
#ifdef Q_OS_ANDROID
Setting::Handle<bool> _use3DKeyboard { "use3DKeyboard", false };
#else
Setting::Handle<bool> _use3DKeyboard { "use3DKeyboard", true };
#endif
QString _typedCharacters;
TextDisplay _textDisplay;

View file

@ -70,7 +70,6 @@ ContextOverlayInterface::ContextOverlayInterface() {
}
});
connect(entityScriptingInterface, &EntityScriptingInterface::deletingEntity, this, &ContextOverlayInterface::deletingEntity);
connect(&qApp->getOverlays(), &Overlays::mousePressOnOverlay, this, &ContextOverlayInterface::contextOverlays_mousePressOnOverlay);
connect(&qApp->getOverlays(), &Overlays::hoverEnterOverlay, this, &ContextOverlayInterface::contextOverlays_hoverEnterOverlay);
connect(&qApp->getOverlays(), &Overlays::hoverLeaveOverlay, this, &ContextOverlayInterface::contextOverlays_hoverLeaveOverlay);
@ -103,10 +102,14 @@ void ContextOverlayInterface::setEnabled(bool enabled) {
}
}
void ContextOverlayInterface::clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event) {
if (_enabled && event.getButton() == PointerEvent::SecondaryButton && contextOverlayFilterPassed(entityItemID)) {
_mouseDownEntity = entityItemID;
void ContextOverlayInterface::clickDownOnEntity(const EntityItemID& id, const PointerEvent& event) {
if (_enabled && event.getButton() == PointerEvent::SecondaryButton && contextOverlayFilterPassed(id)) {
_mouseDownEntity = id;
_mouseDownEntityTimestamp = usecTimestampNow();
} else if (id == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) {
qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "ID:" << id;
emit contextOverlayClicked(_currentEntityWithContextOverlay);
_contextOverlayJustClicked = true;
} else {
if (!_currentEntityWithContextOverlay.isNull()) {
disableEntityHighlight(_currentEntityWithContextOverlay);
@ -249,14 +252,6 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt
return ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent());
}
void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const QUuid& id, const PointerEvent& event) {
if (id == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) {
qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "ID:" << id;
emit contextOverlayClicked(_currentEntityWithContextOverlay);
_contextOverlayJustClicked = true;
}
}
void ContextOverlayInterface::contextOverlays_hoverEnterOverlay(const QUuid& id, const PointerEvent& event) {
if (_contextOverlayID != UNKNOWN_ENTITY_ID) {
qCDebug(context_overlay) << "Started hovering over Context Overlay. ID:" << id;

View file

@ -65,7 +65,6 @@ public slots:
bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
bool destroyContextOverlay(const EntityItemID& entityItemID);
void contextOverlays_mousePressOnOverlay(const QUuid& id, const PointerEvent& event);
void contextOverlays_hoverEnterOverlay(const QUuid& id, const PointerEvent& event);
void contextOverlays_hoverLeaveOverlay(const QUuid& id, const PointerEvent& event);
void contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event);

View file

@ -63,13 +63,6 @@ Overlays::Overlays() {
ADD_TYPE_MAP(PolyLine, line3d);
ADD_TYPE_MAP(Grid, grid);
ADD_TYPE_MAP(Gizmo, circle3d);
auto mouseRayPick = std::make_shared<RayPick>(Vectors::ZERO, Vectors::UP,
PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES) |
PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE)), 0.0f, true);
mouseRayPick->parentTransform = std::make_shared<MouseTransformNode>();
mouseRayPick->setJointState(PickQuery::JOINT_STATE_MOUSE);
_mouseRayPickID = DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, mouseRayPick);
}
void Overlays::cleanupAllOverlays() {
@ -87,13 +80,14 @@ void Overlays::cleanupAllOverlays() {
}
void Overlays::init() {
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
connect(this, &Overlays::hoverEnterOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity);
connect(this, &Overlays::hoverOverOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity);
connect(this, &Overlays::hoverLeaveOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity);
connect(this, &Overlays::mousePressOnOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity);
connect(this, &Overlays::mouseMoveOnOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity);
connect(this, &Overlays::mouseReleaseOnOverlay, entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity);
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>().data();
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, entityScriptingInterface , &EntityScriptingInterface::hoverEnterEntity);
connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity);
connect(pointerManager.data(), &PointerManager::hoverEndOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity);
connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity);
connect(pointerManager.data(), &PointerManager::triggerEndOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity);
}
void Overlays::update(float deltatime) {
@ -358,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);
@ -375,6 +380,8 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove
RENAME_PROP(animationSettings, animation);
} else if (type == "Image") {
RENAME_PROP(url, imageURL);
} else if (type == "Text") {
RENAME_PROP(color, textColor);
} else if (type == "Web") {
RENAME_PROP(url, sourceUrl);
RENAME_PROP_CONVERT(inputMode, inputMode, [](const QVariant& v) { return v.toString() == "Mouse" ? "mouse" : "touch"; });
@ -675,6 +682,8 @@ QVariantMap Overlays::convertEntityToOverlayProperties(const EntityItemPropertie
RENAME_PROP(animation, animationSettings);
} else if (type == "Image") {
RENAME_PROP(imageURL, url);
} else if (type == "Text") {
RENAME_PROP(textColor, color);
} else if (type == "Web") {
RENAME_PROP(sourceUrl, url);
RENAME_PROP_CONVERT(inputMode, inputMode, [](const QVariant& v) { return v.toString() == "mouse" ? "Mouse" : "Touch"; });
@ -1228,12 +1237,12 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) {
}
}
RayToOverlayIntersectionResult getPrevPickResult(unsigned int mouseRayPickID) {
RayToOverlayIntersectionResult getPrevPickResult() {
RayToOverlayIntersectionResult overlayResult;
overlayResult.intersects = false;
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResultTyped<RayPickResult>(mouseRayPickID);
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResultTyped<RayPickResult>(DependencyManager::get<EntityTreeRenderer>()->getMouseRayPickID());
if (pickResult) {
overlayResult.intersects = pickResult->type != IntersectionType::NONE;
overlayResult.intersects = pickResult->type == IntersectionType::LOCAL_ENTITY;
if (overlayResult.intersects) {
overlayResult.intersection = pickResult->intersection;
overlayResult.distance = pickResult->distance;
@ -1281,7 +1290,7 @@ std::pair<float, QUuid> Overlays::mousePressEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mousePressEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID);
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
@ -1305,7 +1314,7 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID);
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
@ -1321,7 +1330,7 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseReleaseEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID);
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release);
mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent);
@ -1343,7 +1352,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseMoveEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(_mouseRayPickID);
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move);
mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent);

View file

@ -719,7 +719,6 @@ private:
PointerEvent calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray, const RayToOverlayIntersectionResult& rayPickResult,
QMouseEvent* event, PointerEvent::EventType eventType);
unsigned int _mouseRayPickID;
QUuid _currentClickingOnOverlayID;
QUuid _currentHoverOverOverlayID;

View file

@ -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);

View file

@ -65,7 +65,11 @@ namespace render {
return keyBuilder.build();
}
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) {
return static_pointer_cast<Avatar>(avatar)->getRenderBounds();
auto avatarPtr = static_pointer_cast<Avatar>(avatar);
if (avatarPtr) {
return avatarPtr->getRenderBounds();
}
return Item::Bound();
}
template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args) {
auto avatarPtr = static_pointer_cast<Avatar>(avatar);
@ -76,10 +80,15 @@ namespace render {
}
template <> uint32_t metaFetchMetaSubItems(const AvatarSharedPointer& avatar, ItemIDs& subItems) {
auto avatarPtr = static_pointer_cast<Avatar>(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;
}
@ -627,12 +636,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;
@ -856,15 +871,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;
}
}
@ -887,9 +904,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);
}
@ -932,6 +955,11 @@ void Avatar::simulateAttachments(float deltaTime) {
}
}
}
if (_ancestorChainRenderableVersion != _lastAncestorChainRenderableVersion) {
_lastAncestorChainRenderableVersion = _ancestorChainRenderableVersion;
updateDescendantRenderIDs();
}
}
float Avatar::getBoundingRadius() const {
@ -1611,7 +1639,6 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
}
}
int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
PerformanceTimer perfTimer("unpack");
if (!_initialized) {
@ -2120,3 +2147,60 @@ void Avatar::clearAvatarGrabData(const QUuid& id) {
}
});
}
uint32_t Avatar::appendSubMetaItems(render::ItemIDs& subItems) {
return _subItemLock.resultWithReadLock<uint32_t>([&] {
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<EntityTreeRenderer>();
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<EntityItem>(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());
}
}
}
}
});
});
}
});
}

View file

@ -500,6 +500,8 @@ public:
const std::vector<MultiSphereShape>& getMultiSphereShapes() const { return _multiSphereShapes; }
void tearDownGrabs();
uint32_t appendSubMetaItems(render::ItemIDs& subItems);
signals:
void targetScaleChanged(float targetScale);
@ -642,8 +644,6 @@ protected:
RateCounter<> _skeletonModelSimulationRate;
RateCounter<> _jointDataSimulationRate;
protected:
class AvatarEntityDataHash {
public:
AvatarEntityDataHash(uint32_t h) : hash(h) {};
@ -705,6 +705,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

View file

@ -208,16 +208,16 @@ AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWe
avatar->setSessionUUID(sessionUUID);
avatar->setOwningAvatarMixer(mixerWeakPointer);
// addAvatar is only called from newOrExistingAvatar, which already locks _hashLock
_avatarHash.insert(sessionUUID, avatar);
{
QWriteLocker locker(&_hashLock);
_avatarHash.insert(sessionUUID, avatar);
}
emit avatarAddedEvent(sessionUUID);
return avatar;
}
AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer,
bool& isNew) {
QWriteLocker locker(&_hashLock);
auto avatar = _avatarHash.value(sessionUUID);
AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer, bool& isNew) {
auto avatar = findAvatar(sessionUUID);
if (!avatar) {
avatar = addAvatar(sessionUUID, mixerWeakPointer);
isNew = true;

View file

@ -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<CompositorHelper>()->getPoint2DTransform(virtualPadManager.getLeftVirtualPad()->getCurrentTouch(),
_virtualPadPixelSize, _virtualPadPixelSize);
auto jumpTransform = DependencyManager::get<CompositorHelper>()->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<CompositorHelper>()->getPoint2DTransform(
buttonPosition,
_pixelSize, _pixelSize);
batch.setResourceTexture(0, _texture);
batch.setModelTransform(transform);
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
#endif

View file

@ -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<VirtualPadButton> _virtualPadButtons;
#endif
};

View file

@ -86,7 +86,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
auto handlePointerEvent = [&](const QUuid& entityID, const PointerEvent& event) {
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;
auto entity = getEntity(entityID);
if (entity && entity->getType() == EntityTypes::Web) {
if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) {
thisEntity = std::static_pointer_cast<render::entities::WebEntityRenderer>(renderableForEntityId(entityID));
}
if (thisEntity) {
@ -99,7 +99,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;
auto entity = getEntity(entityID);
if (entity && entity->getType() == EntityTypes::Web) {
if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) {
thisEntity = std::static_pointer_cast<render::entities::WebEntityRenderer>(renderableForEntityId(entityID));
}
if (thisEntity) {
@ -110,7 +110,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;
auto entity = getEntity(entityID);
if (entity && entity->getType() == EntityTypes::Web) {
if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) {
thisEntity = std::static_pointer_cast<render::entities::WebEntityRenderer>(renderableForEntityId(entityID));
}
if (thisEntity) {
@ -1360,3 +1360,36 @@ EntityEditPacketSender* EntityTreeRenderer::getPacketSender() {
EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr;
return packetSender;
}
std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> EntityTreeRenderer::_addMaterialToEntityOperator = nullptr;
std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> EntityTreeRenderer::_removeMaterialFromEntityOperator = nullptr;
std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> EntityTreeRenderer::_addMaterialToAvatarOperator = nullptr;
std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> EntityTreeRenderer::_removeMaterialFromAvatarOperator = nullptr;
bool EntityTreeRenderer::addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
if (_addMaterialToEntityOperator) {
return _addMaterialToEntityOperator(entityID, material, parentMaterialName);
}
return false;
}
bool EntityTreeRenderer::removeMaterialFromEntity(const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
if (_removeMaterialFromEntityOperator) {
return _removeMaterialFromEntityOperator(entityID, material, parentMaterialName);
}
return false;
}
bool EntityTreeRenderer::addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
if (_addMaterialToAvatarOperator) {
return _addMaterialToAvatarOperator(avatarID, material, parentMaterialName);
}
return false;
}
bool EntityTreeRenderer::removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
if (_removeMaterialFromAvatarOperator) {
return _removeMaterialFromAvatarOperator(avatarID, material, parentMaterialName);
}
return false;
}

View file

@ -73,6 +73,7 @@ public:
static void setEntityLoadingPriorityFunction(CalculateEntityLoadingPriority fn) { _calculateEntityLoadingPriorityFunc = fn; }
void setMouseRayPickID(unsigned int rayPickID) { _mouseRayPickID = rayPickID; }
unsigned int getMouseRayPickID() { return _mouseRayPickID; }
void setMouseRayPickResultOperator(std::function<RayToEntityIntersectionResult(unsigned int)> getPrevRayPickResultOperator) { _getPrevRayPickResultOperator = getPrevRayPickResultOperator; }
void setSetPrecisionPickingOperator(std::function<void(unsigned int, bool)> setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; }
@ -120,6 +121,16 @@ public:
EntityEditPacketSender* getPacketSender();
static void setAddMaterialToEntityOperator(std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> addMaterialToEntityOperator) { _addMaterialToEntityOperator = addMaterialToEntityOperator; }
static void setRemoveMaterialFromEntityOperator(std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> removeMaterialFromEntityOperator) { _removeMaterialFromEntityOperator = removeMaterialFromEntityOperator; }
static bool addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName);
static bool removeMaterialFromEntity(const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName);
static void setAddMaterialToAvatarOperator(std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> addMaterialToAvatarOperator) { _addMaterialToAvatarOperator = addMaterialToAvatarOperator; }
static void setRemoveMaterialFromAvatarOperator(std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> removeMaterialFromAvatarOperator) { _removeMaterialFromAvatarOperator = removeMaterialFromAvatarOperator; }
static bool addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName);
static bool removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName);
signals:
void enterEntity(const EntityItemID& entityItemID);
void leaveEntity(const EntityItemID& entityItemID);
@ -255,6 +266,11 @@ private:
workload::SpacePointer _space{ new workload::Space() };
workload::Transaction::Updates _spaceUpdates;
static std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> _addMaterialToEntityOperator;
static std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> _removeMaterialFromEntityOperator;
static std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> _addMaterialToAvatarOperator;
static std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> _removeMaterialFromAvatarOperator;
};

View file

@ -146,7 +146,6 @@ EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _created(entit
_needsRenderUpdate = true;
emit requestRenderUpdate();
});
_materials = entity->getMaterials();
}
EntityRenderer::~EntityRenderer() { }
@ -321,6 +320,7 @@ bool EntityRenderer::addToScene(const ScenePointer& scene, Transaction& transact
transaction.resetItem(_renderItemID, renderPayload);
onAddToScene(_entity);
updateInScene(scene, transaction);
_entity->bumpAncestorChainRenderableVersion();
return true;
}
@ -328,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) {
@ -353,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
//

View file

@ -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()() };

View file

@ -14,42 +14,210 @@
using namespace render;
using namespace render::entities;
bool MaterialEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
if (entity->getMaterial() != _drawMaterial) {
return true;
}
if (entity->getParentID() != _parentID) {
return true;
}
if (entity->getMaterialMappingPos() != _materialMappingPos || entity->getMaterialMappingScale() != _materialMappingScale || entity->getMaterialMappingRot() != _materialMappingRot) {
bool MaterialEntityRenderer::needsRenderUpdate() const {
if (_retryApply) {
return true;
}
if (!_texturesLoaded) {
return true;
}
return Parent::needsRenderUpdate();
}
bool MaterialEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
if (resultWithReadLock<bool>([&] {
if (entity->getMaterialMappingMode() != _materialMappingMode) {
return true;
}
if (entity->getMaterialRepeat() != _materialRepeat) {
return true;
}
if (entity->getMaterialMappingPos() != _materialMappingPos || entity->getMaterialMappingScale() != _materialMappingScale || entity->getMaterialMappingRot() != _materialMappingRot) {
return true;
}
if (entity->getTransform() != _transform) {
return true;
}
if (entity->getUnscaledDimensions() != _dimensions) {
return true;
}
if (entity->getMaterialURL() != _materialURL) {
return true;
}
if (entity->getMaterialData() != _materialData) {
return true;
}
if (entity->getParentMaterialName() != _parentMaterialName) {
return true;
}
if (entity->getParentID() != _parentID) {
return true;
}
if (entity->getPriority() != _priority) {
return true;
}
return false;
})) {
return true;
}
return false;
}
void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
withWriteLock([&] {
if (_drawMaterial != entity->getMaterial()) {
_texturesLoaded = false;
_drawMaterial = entity->getMaterial();
bool deleteNeeded = false;
bool addNeeded = _retryApply;
bool transformChanged = false;
{
MaterialMappingMode mode = entity->getMaterialMappingMode();
if (mode != _materialMappingMode) {
_materialMappingMode = mode;
transformChanged = true;
}
}
_parentID = entity->getParentID();
_materialMappingPos = entity->getMaterialMappingPos();
_materialMappingScale = entity->getMaterialMappingScale();
_materialMappingRot = entity->getMaterialMappingRot();
{
bool repeat = entity->getMaterialRepeat();
if (repeat != _materialRepeat) {
_materialRepeat = repeat;
transformChanged = true;
}
}
{
glm::vec2 mappingPos = entity->getMaterialMappingPos();
glm::vec2 mappingScale = entity->getMaterialMappingScale();
float mappingRot = entity->getMaterialMappingRot();
if (mappingPos != _materialMappingPos || mappingScale != _materialMappingScale || mappingRot != _materialMappingRot) {
_materialMappingPos = mappingPos;
_materialMappingScale = mappingScale;
_materialMappingRot = mappingRot;
transformChanged |= _materialMappingMode == MaterialMappingMode::UV;
}
}
{
Transform transform = entity->getTransform();
glm::vec3 dimensions = entity->getUnscaledDimensions();
if (transform != _transform || dimensions != _dimensions) {
_transform = transform;
_dimensions = dimensions;
transformChanged |= _materialMappingMode == MaterialMappingMode::PROJECTED;
}
}
{
auto material = getMaterial();
// Update the old material regardless of if it's going to change
if (transformChanged && material && !_parentID.isNull()) {
deleteNeeded = true;
addNeeded = true;
applyTextureTransform(material);
}
}
bool urlChanged = false;
std::string newCurrentMaterialName = _currentMaterialName;
{
QString materialURL = entity->getMaterialURL();
if (materialURL != _materialURL) {
_materialURL = materialURL;
if (_materialURL.contains("?")) {
auto split = _materialURL.split("?");
newCurrentMaterialName = split.last().toStdString();
}
urlChanged = true;
}
}
bool usingMaterialData = _materialURL.startsWith("materialData");
bool materialDataChanged = false;
QUuid oldParentID = _parentID;
QString oldParentMaterialName = _parentMaterialName;
{
QString materialData = entity->getMaterialData();
if (materialData != _materialData) {
_materialData = materialData;
if (usingMaterialData) {
materialDataChanged = true;
}
}
}
{
QString parentMaterialName = entity->getParentMaterialName();
if (parentMaterialName != _parentMaterialName) {
_parentMaterialName = parentMaterialName;
deleteNeeded = true;
addNeeded = true;
}
}
{
QUuid parentID = entity->getParentID();
if (parentID != _parentID) {
_parentID = parentID;
deleteNeeded = true;
addNeeded = true;
}
}
{
quint16 priority = entity->getPriority();
if (priority != _priority) {
_priority = priority;
deleteNeeded = true;
addNeeded = true;
}
}
if (urlChanged && !usingMaterialData) {
_networkMaterial = MaterialCache::instance().getMaterial(_materialURL);
auto onMaterialRequestFinished = [&, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) {
if (success) {
deleteMaterial(oldParentID, oldParentMaterialName);
_texturesLoaded = false;
_parsedMaterials = _networkMaterial->parsedMaterials;
setCurrentMaterialName(newCurrentMaterialName);
applyMaterial();
} else {
deleteMaterial(oldParentID, oldParentMaterialName);
_retryApply = false;
_texturesLoaded = true;
}
};
if (_networkMaterial) {
if (_networkMaterial->isLoaded()) {
onMaterialRequestFinished(!_networkMaterial->isFailed());
} else {
connect(_networkMaterial.data(), &Resource::finished, this, onMaterialRequestFinished);
}
}
} else if (materialDataChanged && usingMaterialData) {
deleteMaterial(oldParentID, oldParentMaterialName);
_texturesLoaded = false;
_parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(_materialData.toUtf8()), _materialURL);
// Since our material changed, the current name might not be valid anymore, so we need to update
setCurrentMaterialName(newCurrentMaterialName);
applyMaterial();
} else {
if (deleteNeeded) {
deleteMaterial(oldParentID, oldParentMaterialName);
}
if (addNeeded) {
applyMaterial();
}
}
{
auto material = getMaterial();
bool newTexturesLoaded = material ? !material->isMissingTexture() : false;
if (!_texturesLoaded && newTexturesLoaded) {
material->checkResetOpacityMap();
}
_texturesLoaded = newTexturesLoaded;
}
_renderTransform = getModelTransform();
const float MATERIAL_ENTITY_SCALE = 0.5f;
_renderTransform.postScale(MATERIAL_ENTITY_SCALE);
_renderTransform.postScale(ENTITY_ITEM_DEFAULT_DIMENSIONS);
bool newTexturesLoaded = _drawMaterial ? !_drawMaterial->isMissingTexture() : false;
if (!_texturesLoaded && newTexturesLoaded) {
_drawMaterial->checkResetOpacityMap();
}
_texturesLoaded = newTexturesLoaded;
});
}
@ -61,8 +229,9 @@ ItemKey MaterialEntityRenderer::getKey() {
builder.withInvisible();
}
if (_drawMaterial) {
auto matKey = _drawMaterial->getKey();
const auto drawMaterial = getMaterial();
if (drawMaterial) {
auto matKey = drawMaterial->getKey();
if (matKey.isTranslucent()) {
builder.withTransparent();
}
@ -73,8 +242,9 @@ ItemKey MaterialEntityRenderer::getKey() {
ShapeKey MaterialEntityRenderer::getShapeKey() {
graphics::MaterialKey drawMaterialKey;
if (_drawMaterial) {
drawMaterialKey = _drawMaterial->getKey();
const auto drawMaterial = getMaterial();
if (drawMaterial) {
drawMaterialKey = drawMaterial->getKey();
}
bool isTranslucent = drawMaterialKey.isTranslucent();
@ -112,18 +282,24 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) {
// Don't render if our parent is set or our material is null
QUuid parentID;
withReadLock([&] {
parentID = _parentID;
});
if (!parentID.isNull()) {
return;
}
Transform renderTransform;
graphics::MaterialPointer drawMaterial;
Transform textureTransform;
withReadLock([&] {
parentID = _parentID;
renderTransform = _renderTransform;
drawMaterial = _drawMaterial;
drawMaterial = getMaterial();
textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0));
textureTransform.setRotation(glm::vec3(0, 0, glm::radians(_materialMappingRot)));
textureTransform.setScale(glm::vec3(_materialMappingScale, 1));
});
if (!parentID.isNull() || !drawMaterial) {
if (!drawMaterial) {
return;
}
@ -142,3 +318,86 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) {
args->_details._trianglesRendered += (int)DependencyManager::get<GeometryCache>()->getSphereTriangleCount();
}
void MaterialEntityRenderer::setCurrentMaterialName(const std::string& currentMaterialName) {
if (_parsedMaterials.networkMaterials.find(currentMaterialName) != _parsedMaterials.networkMaterials.end()) {
_currentMaterialName = currentMaterialName;
} else if (_parsedMaterials.names.size() > 0) {
_currentMaterialName = _parsedMaterials.names[0];
}
}
std::shared_ptr<NetworkMaterial> MaterialEntityRenderer::getMaterial() const {
auto material = _parsedMaterials.networkMaterials.find(_currentMaterialName);
if (material != _parsedMaterials.networkMaterials.end()) {
return material->second;
} else {
return nullptr;
}
}
void MaterialEntityRenderer::deleteMaterial(const QUuid& oldParentID, const QString& oldParentMaterialName) {
std::shared_ptr<NetworkMaterial> material = _appliedMaterial;
if (!material || oldParentID.isNull()) {
return;
}
// Our parent could be an entity or an avatar
std::string oldParentMaterialNameStd = oldParentMaterialName.toStdString();
if (EntityTreeRenderer::removeMaterialFromEntity(oldParentID, material, oldParentMaterialNameStd)) {
_appliedMaterial = nullptr;
return;
}
if (EntityTreeRenderer::removeMaterialFromAvatar(oldParentID, material, oldParentMaterialNameStd)) {
_appliedMaterial = nullptr;
return;
}
// if a remove fails, our parent is gone, so we don't need to retry
}
void MaterialEntityRenderer::applyTextureTransform(std::shared_ptr<NetworkMaterial>& material) {
Transform textureTransform;
if (_materialMappingMode == MaterialMappingMode::UV) {
textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0.0f));
textureTransform.setRotation(glm::vec3(0.0f, 0.0f, glm::radians(_materialMappingRot)));
textureTransform.setScale(glm::vec3(_materialMappingScale, 1.0f));
} else if (_materialMappingMode == MaterialMappingMode::PROJECTED) {
textureTransform = _transform;
textureTransform.postScale(_dimensions);
// Pass the inverse transform here so we don't need to compute it in the shaders
textureTransform.evalFromRawMatrix(textureTransform.getInverseMatrix());
}
material->setTextureTransforms(textureTransform, _materialMappingMode, _materialRepeat);
}
void MaterialEntityRenderer::applyMaterial() {
_retryApply = false;
std::shared_ptr<NetworkMaterial> material = getMaterial();
QUuid parentID = _parentID;
if (!material || parentID.isNull()) {
_appliedMaterial = nullptr;
return;
}
applyTextureTransform(material);
graphics::MaterialLayer materialLayer = graphics::MaterialLayer(material, _priority);
// Our parent could be an entity or an avatar
std::string parentMaterialName = _parentMaterialName.toStdString();
if (EntityTreeRenderer::addMaterialToEntity(parentID, materialLayer, parentMaterialName)) {
_appliedMaterial = material;
return;
}
if (EntityTreeRenderer::addMaterialToAvatar(parentID, materialLayer, parentMaterialName)) {
_appliedMaterial = material;
return;
}
// if we've reached this point, we couldn't find our parent, so we need to try again later
_retryApply = true;
}

View file

@ -13,6 +13,8 @@
#include <MaterialEntityItem.h>
#include <material-networking/MaterialCache.h>
class NetworkMaterial;
namespace render { namespace entities {
@ -22,22 +24,46 @@ class MaterialEntityRenderer : public TypedEntityRenderer<MaterialEntityItem> {
using Pointer = std::shared_ptr<MaterialEntityRenderer>;
public:
MaterialEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {}
~MaterialEntityRenderer() { deleteMaterial(_parentID, _parentMaterialName); }
private:
virtual bool needsRenderUpdate() const override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
virtual void doRender(RenderArgs* args) override;
ItemKey getKey() override;
ShapeKey getShapeKey() override;
QString _materialURL;
QString _materialData;
QString _parentMaterialName;
quint16 _priority;
QUuid _parentID;
MaterialMappingMode _materialMappingMode;
bool _materialRepeat;
glm::vec2 _materialMappingPos;
glm::vec2 _materialMappingScale;
float _materialMappingRot;
bool _texturesLoaded { false };
Transform _transform;
glm::vec3 _dimensions;
bool _texturesLoaded { false };
bool _retryApply { false };
std::shared_ptr<NetworkMaterial> getMaterial() const;
void setCurrentMaterialName(const std::string& currentMaterialName);
void applyTextureTransform(std::shared_ptr<NetworkMaterial>& material);
void applyMaterial();
void deleteMaterial(const QUuid& oldParentID, const QString& oldParentMaterialName);
NetworkMaterialResourcePointer _networkMaterial;
NetworkMaterialResource::ParsedMaterials _parsedMaterials;
std::shared_ptr<NetworkMaterial> _appliedMaterial;
std::string _currentMaterialName;
std::shared_ptr<NetworkMaterial> _drawMaterial;
};
} }

View file

@ -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<PayloadProxyInterface>(getRenderItemID(), [](PayloadProxyInterface& data) {
auto entityRenderer = static_cast<EntityRenderer*>(&data);
entityRenderer->clearSubRenderItemIDs();
});
emit DependencyManager::get<scriptable::ModelProviderFactory>()->
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<PayloadProxyInterface>(getRenderItemID(), [newRenderItemIDs](PayloadProxyInterface& data) {
auto entityRenderer = static_cast<EntityRenderer*>(&data);
entityRenderer->setSubRenderItemIDs(newRenderItemIDs);
});
entity->bumpAncestorChainRenderableVersion();
processMaterials();
}
}

View file

@ -2935,6 +2935,7 @@ void EntityItem::setVisible(bool value) {
});
if (changed) {
bumpAncestorChainRenderableVersion();
emit requestRenderUpdate();
}
}
@ -3250,25 +3251,6 @@ void EntityItem::setSpaceIndex(int32_t index) {
void EntityItem::preDelete() {
}
void EntityItem::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[parentMaterialName].push(material);
}
void EntityItem::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[parentMaterialName].remove(material);
}
std::unordered_map<std::string, graphics::MultiMaterial> EntityItem::getMaterials() {
std::unordered_map<std::string, graphics::MultiMaterial> toReturn;
{
std::lock_guard<std::mutex> lock(_materialsLock);
toReturn = _materials;
}
return toReturn;
}
bool EntityItem::getCloneable() const {
bool result;
withReadLock([&] {

View file

@ -37,8 +37,6 @@
#include "EntityDynamicInterface.h"
#include "GrabPropertyGroup.h"
#include "graphics/Material.h"
class EntitySimulation;
class EntityTreeElement;
class EntityTreeElementExtraEncodeData;
@ -542,10 +540,6 @@ public:
virtual void preDelete();
virtual void postParentFixup() {}
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName);
void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName);
std::unordered_map<std::string, graphics::MultiMaterial> getMaterials();
void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; }
uint64_t getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; }
@ -754,11 +748,7 @@ protected:
QHash<QUuid, EntityDynamicPointer> _grabActions;
private:
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
std::mutex _materialsLock;
static std::function<glm::quat(const glm::vec3&, const glm::quat&, BillboardMode)> _getBillboardRotationOperator;
};
#endif // hifi_EntityItem_h

View file

@ -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
}

View file

@ -2953,41 +2953,9 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const {
return entity->getJointNames();
}
std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> EntityTree::_addMaterialToEntityOperator = nullptr;
std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> EntityTree::_removeMaterialFromEntityOperator = nullptr;
std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> EntityTree::_addMaterialToAvatarOperator = nullptr;
std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> EntityTree::_removeMaterialFromAvatarOperator = nullptr;
std::function<QObject*(const QUuid&)> EntityTree::_getEntityObjectOperator = nullptr;
std::function<QSizeF(const QUuid&, const QString&)> EntityTree::_textSizeOperator = nullptr;
bool EntityTree::addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
if (_addMaterialToEntityOperator) {
return _addMaterialToEntityOperator(entityID, material, parentMaterialName);
}
return false;
}
bool EntityTree::removeMaterialFromEntity(const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
if (_removeMaterialFromEntityOperator) {
return _removeMaterialFromEntityOperator(entityID, material, parentMaterialName);
}
return false;
}
bool EntityTree::addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName) {
if (_addMaterialToAvatarOperator) {
return _addMaterialToAvatarOperator(avatarID, material, parentMaterialName);
}
return false;
}
bool EntityTree::removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName) {
if (_removeMaterialFromAvatarOperator) {
return _removeMaterialFromAvatarOperator(avatarID, material, parentMaterialName);
}
return false;
}
QObject* EntityTree::getEntityObject(const QUuid& id) {
if (_getEntityObjectOperator) {
return _getEntityObjectOperator(id);

View file

@ -262,16 +262,6 @@ public:
void setIsServerlessMode(bool value) { _serverlessDomain = value; }
bool isServerlessMode() const { return _serverlessDomain; }
static void setAddMaterialToEntityOperator(std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> addMaterialToEntityOperator) { _addMaterialToEntityOperator = addMaterialToEntityOperator; }
static void setRemoveMaterialFromEntityOperator(std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> removeMaterialFromEntityOperator) { _removeMaterialFromEntityOperator = removeMaterialFromEntityOperator; }
static bool addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName);
static bool removeMaterialFromEntity(const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName);
static void setAddMaterialToAvatarOperator(std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> addMaterialToAvatarOperator) { _addMaterialToAvatarOperator = addMaterialToAvatarOperator; }
static void setRemoveMaterialFromAvatarOperator(std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> removeMaterialFromAvatarOperator) { _removeMaterialFromAvatarOperator = removeMaterialFromAvatarOperator; }
static bool addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName);
static bool removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName);
static void setGetEntityObjectOperator(std::function<QObject*(const QUuid&)> getEntityObjectOperator) { _getEntityObjectOperator = getEntityObjectOperator; }
static QObject* getEntityObject(const QUuid& id);
@ -386,10 +376,6 @@ private:
std::shared_ptr<AvatarData> _myAvatar{ nullptr };
static std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> _addMaterialToEntityOperator;
static std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> _removeMaterialFromEntityOperator;
static std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> _addMaterialToAvatarOperator;
static std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> _removeMaterialFromAvatarOperator;
static std::function<QObject*(const QUuid&)> _getEntityObjectOperator;
static std::function<QSizeF(const QUuid&, const QString&)> _textSizeOperator;

View file

@ -16,9 +16,6 @@
EntityItemPointer MaterialEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
Pointer entity(new MaterialEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); });
entity->setProperties(properties);
// When you reload content, setProperties doesn't have any of the propertiesChanged flags set, so it won't trigger a material add
entity->removeMaterial();
entity->applyMaterial();
return entity;
}
@ -27,10 +24,6 @@ MaterialEntityItem::MaterialEntityItem(const EntityItemID& entityItemID) : Entit
_type = EntityTypes::Material;
}
MaterialEntityItem::~MaterialEntityItem() {
removeMaterial();
}
EntityItemProperties MaterialEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
COPY_ENTITY_PROPERTY_TO_PROPERTIES(materialURL, getMaterialURL);
@ -131,7 +124,6 @@ void MaterialEntityItem::debugDump() const {
qCDebug(entities) << " MATERIAL EntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " name:" << _name;
qCDebug(entities) << " material url:" << _materialURL;
qCDebug(entities) << " current material name:" << _currentMaterialName.c_str();
qCDebug(entities) << " material mapping mode:" << _materialMappingMode;
qCDebug(entities) << " material repeat:" << _materialRepeat;
qCDebug(entities) << " priority:" << _priority;
@ -154,208 +146,101 @@ void MaterialEntityItem::setUnscaledDimensions(const glm::vec3& value) {
}
}
std::shared_ptr<NetworkMaterial> MaterialEntityItem::getMaterial() const {
auto material = _parsedMaterials.networkMaterials.find(_currentMaterialName);
if (material != _parsedMaterials.networkMaterials.end()) {
return material->second;
} else {
return nullptr;
}
QString MaterialEntityItem::getMaterialURL() const {
return resultWithReadLock<QString>([&] {
return _materialURL;
});
}
void MaterialEntityItem::setMaterialURL(const QString& materialURLString, bool materialDataChanged) {
bool usingMaterialData = materialDataChanged || materialURLString.startsWith("materialData");
if (_materialURL != materialURLString || (usingMaterialData && materialDataChanged)) {
removeMaterial();
_materialURL = materialURLString;
if (materialURLString.contains("?")) {
auto split = materialURLString.split("?");
_currentMaterialName = split.last().toStdString();
}
if (usingMaterialData) {
_parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(getMaterialData().toUtf8()), materialURLString);
// Since our material changed, the current name might not be valid anymore, so we need to update
setCurrentMaterialName(_currentMaterialName);
applyMaterial();
} else {
_networkMaterial = MaterialCache::instance().getMaterial(materialURLString);
auto onMaterialRequestFinished = [&](bool success) {
if (success) {
_parsedMaterials = _networkMaterial->parsedMaterials;
setCurrentMaterialName(_currentMaterialName);
applyMaterial();
}
};
if (_networkMaterial) {
if (_networkMaterial->isLoaded()) {
onMaterialRequestFinished(!_networkMaterial->isFailed());
} else {
connect(_networkMaterial.data(), &Resource::finished, this, onMaterialRequestFinished);
}
}
}
}
void MaterialEntityItem::setMaterialURL(const QString& materialURL) {
withWriteLock([&] {
_materialURL = materialURL;
});
}
void MaterialEntityItem::setCurrentMaterialName(const std::string& currentMaterialName) {
if (_parsedMaterials.networkMaterials.find(currentMaterialName) != _parsedMaterials.networkMaterials.end()) {
_currentMaterialName = currentMaterialName;
} else if (_parsedMaterials.names.size() > 0) {
_currentMaterialName = _parsedMaterials.names[0];
}
QString MaterialEntityItem::getMaterialData() const {
return resultWithReadLock<QString>([&] {
return _materialData;
});
}
void MaterialEntityItem::setMaterialData(const QString& materialData) {
if (_materialData != materialData) {
withWriteLock([&] {
_materialData = materialData;
if (_materialURL.startsWith("materialData")) {
// Trigger material update when material data changes
setMaterialURL(_materialURL, true);
}
}
});
}
MaterialMappingMode MaterialEntityItem::getMaterialMappingMode() const {
return resultWithReadLock<MaterialMappingMode>([&] {
return _materialMappingMode;
});
}
void MaterialEntityItem::setMaterialMappingMode(MaterialMappingMode mode) {
if (_materialMappingMode != mode) {
removeMaterial();
withWriteLock([&] {
_materialMappingMode = mode;
setUnscaledDimensions(_desiredDimensions);
applyMaterial();
}
});
setUnscaledDimensions(_desiredDimensions);
}
void MaterialEntityItem::setMaterialRepeat(bool repeat) {
if (_materialRepeat != repeat) {
removeMaterial();
_materialRepeat = repeat;
applyMaterial();
}
}
void MaterialEntityItem::setMaterialMappingPos(const glm::vec2& materialMappingPos) {
if (_materialMappingPos != materialMappingPos) {
removeMaterial();
_materialMappingPos = materialMappingPos;
applyMaterial();
}
}
void MaterialEntityItem::setMaterialMappingScale(const glm::vec2& materialMappingScale) {
if (_materialMappingScale != materialMappingScale) {
removeMaterial();
_materialMappingScale = materialMappingScale;
applyMaterial();
}
}
void MaterialEntityItem::setMaterialMappingRot(const float& materialMappingRot) {
if (_materialMappingRot != materialMappingRot) {
removeMaterial();
_materialMappingRot = materialMappingRot;
applyMaterial();
}
quint16 MaterialEntityItem::getPriority() const {
return resultWithReadLock<quint16>([&] {
return _priority;
});
}
void MaterialEntityItem::setPriority(quint16 priority) {
if (_priority != priority) {
removeMaterial();
withWriteLock([&] {
_priority = priority;
applyMaterial();
}
});
}
QString MaterialEntityItem::getParentMaterialName() const {
return resultWithReadLock<QString>([&] {
return _parentMaterialName;
});
}
void MaterialEntityItem::setParentMaterialName(const QString& parentMaterialName) {
if (_parentMaterialName != parentMaterialName) {
removeMaterial();
withWriteLock([&] {
_parentMaterialName = parentMaterialName;
applyMaterial();
}
});
}
void MaterialEntityItem::setParentID(const QUuid& parentID) {
if (getParentID() != parentID) {
removeMaterial();
EntityItem::setParentID(parentID);
applyMaterial();
}
glm::vec2 MaterialEntityItem::getMaterialMappingPos() const {
return resultWithReadLock<glm::vec2>([&] {
return _materialMappingPos;
});
}
void MaterialEntityItem::locationChanged(bool tellPhysics) {
EntityItem::locationChanged();
if (_materialMappingMode == MaterialMappingMode::PROJECTED) {
removeMaterial();
applyMaterial();
}
void MaterialEntityItem::setMaterialMappingPos(const glm::vec2& materialMappingPos) {
withWriteLock([&] {
_materialMappingPos = materialMappingPos;
});
}
void MaterialEntityItem::dimensionsChanged() {
EntityItem::dimensionsChanged();
if (_materialMappingMode == MaterialMappingMode::PROJECTED) {
removeMaterial();
applyMaterial();
}
glm::vec2 MaterialEntityItem::getMaterialMappingScale() const {
return resultWithReadLock<glm::vec2>([&] {
return _materialMappingScale;
});
}
void MaterialEntityItem::removeMaterial() {
graphics::MaterialPointer material = getMaterial();
if (!material) {
return;
}
QUuid parentID = getParentID();
if (parentID.isNull()) {
return;
}
// Our parent could be an entity or an avatar
if (EntityTree::removeMaterialFromEntity(parentID, material, getParentMaterialName().toStdString())) {
return;
}
if (EntityTree::removeMaterialFromAvatar(parentID, material, getParentMaterialName().toStdString())) {
return;
}
// if a remove fails, our parent is gone, so we don't need to retry
void MaterialEntityItem::setMaterialMappingScale(const glm::vec2& materialMappingScale) {
withWriteLock([&] {
_materialMappingScale = materialMappingScale;
});
}
void MaterialEntityItem::applyMaterial() {
_retryApply = false;
graphics::MaterialPointer material = getMaterial();
QUuid parentID = getParentID();
if (!material || parentID.isNull()) {
return;
}
float MaterialEntityItem::getMaterialMappingRot() const {
return resultWithReadLock<float>([&] {
return _materialMappingRot;
});
}
Transform textureTransform;
if (_materialMappingMode == MaterialMappingMode::UV) {
textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0.0f));
textureTransform.setRotation(glm::vec3(0.0f, 0.0f, glm::radians(_materialMappingRot)));
textureTransform.setScale(glm::vec3(_materialMappingScale, 1.0f));
} else if (_materialMappingMode == MaterialMappingMode::PROJECTED) {
textureTransform = getTransform();
textureTransform.postScale(getUnscaledDimensions());
// Pass the inverse transform here so we don't need to compute it in the shaders
textureTransform.evalFromRawMatrix(textureTransform.getInverseMatrix());
}
material->setTextureTransforms(textureTransform, _materialMappingMode, _materialRepeat);
graphics::MaterialLayer materialLayer = graphics::MaterialLayer(material, getPriority());
// Our parent could be an entity or an avatar
if (EntityTree::addMaterialToEntity(parentID, materialLayer, getParentMaterialName().toStdString())) {
return;
}
if (EntityTree::addMaterialToAvatar(parentID, materialLayer, getParentMaterialName().toStdString())) {
return;
}
// if we've reached this point, we couldn't find our parent, so we need to try again later
_retryApply = true;
void MaterialEntityItem::setMaterialMappingRot(float materialMappingRot) {
withWriteLock([&] {
_materialMappingRot = materialMappingRot;
});
}
AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) {
@ -372,18 +257,3 @@ AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) {
}
return aaCube;
}
void MaterialEntityItem::postParentFixup() {
removeMaterial();
_queryAACubeSet = false; // force an update so we contain our parent
updateQueryAACube();
applyMaterial();
}
void MaterialEntityItem::update(const quint64& now) {
if (_retryApply) {
applyMaterial();
}
EntityItem::update(now);
}

View file

@ -12,8 +12,6 @@
#include "EntityItem.h"
#include "MaterialMappingMode.h"
#include <model-networking/ModelCache.h>
#include <material-networking/MaterialCache.h>
class MaterialEntityItem : public EntityItem {
using Pointer = std::shared_ptr<MaterialEntityItem>;
@ -21,13 +19,9 @@ public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
MaterialEntityItem(const EntityItemID& entityItemID);
~MaterialEntityItem();
ALLOW_INSTANTIATION // This class can be instantiated
void update(const quint64& now) override;
bool needsToCallUpdate() const override { return true; }
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
@ -52,44 +46,30 @@ public:
virtual void setUnscaledDimensions(const glm::vec3& value) override;
QString getMaterialURL() const { return _materialURL; }
void setMaterialURL(const QString& materialURLString, bool materialDataChanged = false);
QString getMaterialURL() const;
void setMaterialURL(const QString& materialURL);
void setCurrentMaterialName(const std::string& currentMaterialName);
QString getMaterialData() const;
void setMaterialData(const QString& materialData);
MaterialMappingMode getMaterialMappingMode() const { return _materialMappingMode; }
MaterialMappingMode getMaterialMappingMode() const;
void setMaterialMappingMode(MaterialMappingMode mode);
bool getMaterialRepeat() const { return _materialRepeat; }
void setMaterialRepeat(bool repeat);
void setMaterialRepeat(bool repeat) { _materialRepeat = repeat; }
quint16 getPriority() const { return _priority; }
quint16 getPriority() const;
void setPriority(quint16 priority);
QString getParentMaterialName() const { return _parentMaterialName; }
QString getParentMaterialName() const;
void setParentMaterialName(const QString& parentMaterialName);
glm::vec2 getMaterialMappingPos() const { return _materialMappingPos; }
glm::vec2 getMaterialMappingPos() const;
void setMaterialMappingPos(const glm::vec2& materialMappingPos);
glm::vec2 getMaterialMappingScale() const { return _materialMappingScale; }
glm::vec2 getMaterialMappingScale() const;
void setMaterialMappingScale(const glm::vec2& materialMappingScale);
float getMaterialMappingRot() const { return _materialMappingRot; }
void setMaterialMappingRot(const float& materialMappingRot);
QString getMaterialData() const { return _materialData; }
void setMaterialData(const QString& materialData);
std::shared_ptr<NetworkMaterial> getMaterial() const;
void setParentID(const QUuid& parentID) override;
void locationChanged(bool tellPhysics) override;
void dimensionsChanged() override;
void applyMaterial();
void removeMaterial();
void postParentFixup() override;
float getMaterialMappingRot() const;
void setMaterialMappingRot(float materialMappingRot);
AACube calculateInitialQueryAACube(bool& success) override;
@ -128,12 +108,6 @@ private:
float _materialMappingRot { 0 };
QString _materialData;
NetworkMaterialResourcePointer _networkMaterial;
NetworkMaterialResource::ParsedMaterials _parsedMaterials;
std::string _currentMaterialName;
bool _retryApply { false };
};
#endif // hifi_MaterialEntityItem_h

View file

@ -388,10 +388,23 @@ ShaderPointer Deserializer::readShader(const json& node) {
return nullptr;
}
static std::map<std::string, uint32_t> 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:

View file

@ -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;

View file

@ -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<QTouchEvent::TouchPoint>& 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<InputDevice> 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<int, TouchType> &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<int, TouchscreenVirtualPadDevice::TouchType> &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<QTouchEvent::TouchPoint> &tPoints, std::map<int, TouchType> 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;
}

View file

@ -15,6 +15,7 @@
#include <controllers/InputDevice.h>
#include "InputPlugin.h"
#include <QtGui/qtouchdevice.h>
#include <QtGui/QList>
#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<InputDevice> 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> _inputDevice;
};
class TouchscreenButtonsManager {
public:
TouchscreenButtonsManager();
QVector<TouchscreenButton> 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<int, TouchType> &globalUnusedTouches);
void saveUnusedTouches(std::map<int, TouchType> &unusedTouchesInEvent, glm::vec2 thisPoint, int thisPointId);
void processBeginOrEnd(glm::vec2 thisPoint, const QList<QTouchEvent::TouchPoint>& tPoints, std::map<int, TouchType> 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<int, TouchType> _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);

View file

@ -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<const TextureExtra*>(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<gpu::TextureSource>(_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));
}
}

View file

@ -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<NetworkTexture>& 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;
};

View file

@ -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<const GeometryExtra*>(extra);
_mapping = geometryExtra ? geometryExtra->mapping : QVariantHash();
_textureBaseUrl = geometryExtra ? resolveTextureBaseUrl(_url, geometryExtra->textureBaseUrl) : QUrl();

View file

@ -355,7 +355,7 @@ QSharedPointer<Resource> 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<Resource> 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);

View file

@ -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; }

View file

@ -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);

View file

@ -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) {

View file

@ -205,7 +205,7 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() {
}
AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() :
render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false),
render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion"),
perspectiveScale{ 1.0f },
edgeSharpness{ 1.0f },
blurRadius{ 4 },

View file

@ -118,7 +118,7 @@ protected:
float enableSkinning{ 1.0f };
float enableBlendshape{ 1.0f };
float enableAmbientOcclusion{ 0.0f };
float enableAmbientOcclusion{ 0.0f }; // false by default
float enableShadow{ 1.0f };
float spare1{ 1.0f };
float spare2{ 1.0f };
@ -196,15 +196,13 @@ public:
bool enableSkinning{ true };
bool enableBlendshape{ true };
bool enableAmbientOcclusion{ true };
bool enableAmbientOcclusion{ false }; // false by default
bool enableShadow{ true };
void setAmbientOcclusion(bool enable) { enableAmbientOcclusion = enable; emit dirty();}
bool isAmbientOcclusionEnabled() const { return enableAmbientOcclusion; }
void setShadow(bool enable) {
enableShadow = enable; emit dirty();
}
void setShadow(bool enable) { enableShadow = enable; emit dirty(); }
bool isShadowEnabled() const { return enableShadow; }
signals:

View file

@ -0,0 +1,7 @@
layout(location=0) in vec4 _color;
layout(location=0) out vec4 _fragColor0;
void main(void) {
_fragColor0 = _color;
}

View file

@ -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;

View file

@ -0,0 +1,3 @@
#version 310 es
#define GPU_GLES
#define GPU_GLES_310

View file

@ -1,5 +1,3 @@
#version 410 core
#define GPU_GL410
#define BITFIELD int
#if defined(VULKAN)
#extension GL_ARB_shading_language_420pack : require

View file

@ -0,0 +1,2 @@
#version 410 core
#define GPU_GL410

View file

@ -1,5 +1,3 @@
#version 450 core
#define GPU_GL450
#define GPU_SSBO_TRANSFORM_OBJECT
#define BITFIELD int
#define LAYOUT(X) layout(X)

View file

@ -0,0 +1,2 @@
#version 450 core
#define GPU_GL450

View file

@ -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;

View file

@ -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")

View file

@ -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)

View file

@ -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<QUrl>()) {
toReturn[texture] = newURL.toUrl();
} else if (newURL.canConvert<QString>()) {
toReturn[texture] = QUrl(newURL.toString());
} else {
toReturn[texture] = newURL;
}
}
return toReturn;

View file

@ -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();
}
}

View file

@ -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<SpatiallyNestable> {
@ -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<GrabPointer> _grabs; // upon this thing
mutable std::atomic<uint32_t> _ancestorChainRenderableVersion { 0 };
private:
SpatiallyNestable() = delete;
const NestableType _nestableType; // EntityItem or an AvatarData

View file

@ -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);

View file

@ -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<std::mutex> 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) {

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