Merge remote-tracking branch 'upstream/master' into NOverlaysFinal
|
@ -80,6 +80,7 @@ endif()
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
set(GLES_OPTION ON)
|
set(GLES_OPTION ON)
|
||||||
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
|
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
|
||||||
|
add_definitions(-DHIFI_ANDROID_APP=\"${HIFI_ANDROID_APP}\")
|
||||||
else ()
|
else ()
|
||||||
set(PLATFORM_QT_COMPONENTS WebEngine)
|
set(PLATFORM_QT_COMPONENTS WebEngine)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
|
@ -3,10 +3,10 @@ apply plugin: 'com.android.application'
|
||||||
android {
|
android {
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
release {
|
release {
|
||||||
keyAlias 'key0'
|
storeFile project.hasProperty("HIFI_ANDROID_KEYSTORE") ? file(HIFI_ANDROID_KEYSTORE) : null
|
||||||
keyPassword 'password'
|
storePassword project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") ? HIFI_ANDROID_KEYSTORE_PASSWORD : ''
|
||||||
storeFile file('C:/android/keystore.jks')
|
keyAlias project.hasProperty("HIFI_ANDROID_KEY_ALIAS") ? HIFI_ANDROID_KEY_ALIAS : ''
|
||||||
storePassword 'password'
|
keyPassword project.hasProperty("HIFI_ANDROID_KEY_PASSWORD") ? HIFI_ANDROID_KEY_PASSWORD : ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
//
|
|
||||||
// InterfaceActivity.java
|
|
||||||
// gvr-interface/java
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 1/26/15.
|
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
package io.highfidelity.gvrinterface;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.util.Log;
|
|
||||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
|
||||||
|
|
||||||
public class InterfaceActivity extends QtActivity {
|
|
||||||
|
|
||||||
public static native void handleHifiURL(String hifiURLString);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
||||||
|
|
||||||
// Get the intent that started this activity in case we have a hifi:// URL to parse
|
|
||||||
Intent intent = getIntent();
|
|
||||||
if (intent.getAction() == Intent.ACTION_VIEW) {
|
|
||||||
Uri data = intent.getData();
|
|
||||||
|
|
||||||
if (data.getScheme().equals("hifi")) {
|
|
||||||
handleHifiURL(data.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -31,6 +31,7 @@ import android.view.WindowManager;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.SlidingDrawer;
|
import android.widget.SlidingDrawer;
|
||||||
|
|
||||||
|
import org.qtproject.qt5.android.QtNative;
|
||||||
import org.qtproject.qt5.android.QtLayout;
|
import org.qtproject.qt5.android.QtLayout;
|
||||||
import org.qtproject.qt5.android.QtSurface;
|
import org.qtproject.qt5.android.QtSurface;
|
||||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||||
|
@ -166,8 +167,27 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
|
||||||
nativeOnDestroy();
|
nativeOnDestroy();
|
||||||
|
/*
|
||||||
|
cduarte https://highfidelity.manuscript.com/f/cases/16712/App-freezes-on-opening-randomly
|
||||||
|
After Qt upgrade to 5.11 we had a black screen crash after closing the application with
|
||||||
|
the hardware button "Back" and trying to start the app again. It could only be fixed after
|
||||||
|
totally closing the app swiping it in the list of running apps.
|
||||||
|
This problem did not happen with the previous Qt version.
|
||||||
|
After analysing changes we came up with this case and change:
|
||||||
|
https://codereview.qt-project.org/#/c/218882/
|
||||||
|
In summary they've moved libs loading to the same thread as main() and as a matter of correctness
|
||||||
|
in the onDestroy method in QtActivityDelegate, they exit that thread with `QtNative.m_qtThread.exit();`
|
||||||
|
That exit call is the main reason of this problem.
|
||||||
|
|
||||||
|
In this fix we just replace the `QtApplication.invokeDelegate();` call that may end using the
|
||||||
|
entire onDestroy method including that thread exit line for other three lines that purposely
|
||||||
|
terminate qt (borrowed from QtActivityDelegate::onDestroy as well).
|
||||||
|
*/
|
||||||
|
QtNative.terminateQt();
|
||||||
|
QtNative.setActivity(null, null);
|
||||||
|
System.exit(0);
|
||||||
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
9
android/apps/questFramePlayer/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
51
android/apps/questFramePlayer/build.gradle
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
storeFile project.hasProperty("HIFI_ANDROID_KEYSTORE") ? file(HIFI_ANDROID_KEYSTORE) : null
|
||||||
|
storePassword project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") ? HIFI_ANDROID_KEYSTORE_PASSWORD : ''
|
||||||
|
keyAlias project.hasProperty("HIFI_ANDROID_KEY_ALIAS") ? HIFI_ANDROID_KEY_ALIAS : ''
|
||||||
|
keyPassword project.hasProperty("HIFI_ANDROID_KEY_PASSWORD") ? HIFI_ANDROID_KEY_PASSWORD : ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileSdkVersion 28
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "io.highfidelity.frameplayer"
|
||||||
|
minSdkVersion 25
|
||||||
|
targetSdkVersion 28
|
||||||
|
ndk { abiFilters 'arm64-v8a' }
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
arguments '-DHIFI_ANDROID=1',
|
||||||
|
'-DHIFI_ANDROID_APP=questFramePlayer',
|
||||||
|
'-DANDROID_TOOLCHAIN=clang',
|
||||||
|
'-DANDROID_STL=c++_shared',
|
||||||
|
|
||||||
|
'-DCMAKE_VERBOSE_MAKEFILE=ON'
|
||||||
|
targets = ['questFramePlayer']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
signingConfig signingConfigs.release
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
externalNativeBuild.cmake.path '../../../CMakeLists.txt'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
|
||||||
|
implementation project(':oculus')
|
||||||
|
implementation project(':qt')
|
||||||
|
}
|
25
android/apps/questFramePlayer/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in C:\Android\SDK/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
55
android/apps/questFramePlayer/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="io.highfidelity.frameplayer"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0"
|
||||||
|
android:installLocation="auto">
|
||||||
|
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true"/>
|
||||||
|
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="true"/>
|
||||||
|
<uses-feature android:name="android.software.vr.mode" android:required="true"/>
|
||||||
|
<uses-feature android:name="android.hardware.vr.high_performance" android:required="true"/>
|
||||||
|
|
||||||
|
|
||||||
|
<application android:label="Frame Viewer"
|
||||||
|
android:allowBackup="false"
|
||||||
|
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"
|
||||||
|
android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode"
|
||||||
|
android:name=".QuestRenderActivity"
|
||||||
|
android:label="Frame Player"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:screenOrientation="landscape"
|
||||||
|
android:excludeFromRecents="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
25
android/apps/questFramePlayer/src/main/cpp/PlayerWindow.cpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2018/10/21
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "PlayerWindow.h"
|
||||||
|
|
||||||
|
#include <QtWidgets/QFileDialog>
|
||||||
|
|
||||||
|
PlayerWindow::PlayerWindow() {
|
||||||
|
installEventFilter(this);
|
||||||
|
setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
|
||||||
|
setSurfaceType(QSurface::OpenGLSurface);
|
||||||
|
create();
|
||||||
|
showFullScreen();
|
||||||
|
// Ensure the window is visible and the GL context is valid
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
_renderThread.initialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerWindow::~PlayerWindow() {
|
||||||
|
}
|
29
android/apps/questFramePlayer/src/main/cpp/PlayerWindow.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2018/10/21
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSettings _settings;
|
||||||
|
RenderThread _renderThread;
|
||||||
|
};
|
240
android/apps/questFramePlayer/src/main/cpp/RenderThread.cpp
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2018/10/21
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "RenderThread.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
#include <QtGui/QImageReader>
|
||||||
|
|
||||||
|
#include <gl/QOpenGLContextWrapper.h>
|
||||||
|
#include <gpu/FrameIO.h>
|
||||||
|
#include <gpu/Texture.h>
|
||||||
|
|
||||||
|
#include <VrApi_Types.h>
|
||||||
|
#include <VrApi_Helpers.h>
|
||||||
|
#include <ovr/VrHandler.h>
|
||||||
|
#include <ovr/Helpers.h>
|
||||||
|
|
||||||
|
#include <VrApi.h>
|
||||||
|
#include <VrApi_Input.h>
|
||||||
|
|
||||||
|
static JNIEnv* _env { nullptr };
|
||||||
|
static JavaVM* _vm { nullptr };
|
||||||
|
static jobject _activity { nullptr };
|
||||||
|
|
||||||
|
struct HandController{
|
||||||
|
ovrInputTrackedRemoteCapabilities caps {};
|
||||||
|
ovrInputStateTrackedRemote state {};
|
||||||
|
ovrResult stateResult{ ovrSuccess };
|
||||||
|
ovrTracking tracking {};
|
||||||
|
ovrResult trackingResult{ ovrSuccess };
|
||||||
|
|
||||||
|
void update(ovrMobile* session, double time = 0.0) {
|
||||||
|
const auto& deviceId = caps.Header.DeviceID;
|
||||||
|
stateResult = vrapi_GetCurrentInputState(session, deviceId, &state.Header);
|
||||||
|
trackingResult = vrapi_GetInputTrackingState(session, deviceId, 0.0, &tracking);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<HandController> devices;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *, void *) {
|
||||||
|
__android_log_write(ANDROID_LOG_WARN, "QQQ", __FUNCTION__);
|
||||||
|
return JNI_VERSION_1_6;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_io_highfidelity_frameplayer_QuestQtActivity_nativeOnCreate(JNIEnv* env, jobject obj) {
|
||||||
|
env->GetJavaVM(&_vm);
|
||||||
|
_activity = env->NewGlobalRef(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* FRAME_FILE = "assets:/frames/20190121_1220.json";
|
||||||
|
|
||||||
|
static void textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer) {
|
||||||
|
QImage image;
|
||||||
|
QImageReader(filename.c_str()).read(&image);
|
||||||
|
if (layer > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
texture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderThread::submitFrame(const gpu::FramePointer& frame) {
|
||||||
|
std::unique_lock<std::mutex> lock(_frameLock);
|
||||||
|
_pendingFrames.push(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderThread::move(const glm::vec3& v) {
|
||||||
|
std::unique_lock<std::mutex> lock(_frameLock);
|
||||||
|
_correction = glm::inverse(glm::translate(mat4(), v)) * _correction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderThread::initialize(QWindow* window) {
|
||||||
|
std::unique_lock<std::mutex> lock(_frameLock);
|
||||||
|
setObjectName("RenderThread");
|
||||||
|
Parent::initialize();
|
||||||
|
_window = window;
|
||||||
|
_thread->setObjectName("RenderThread");
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// GPU library init
|
||||||
|
gpu::Context::init<gpu::gl::GLBackend>();
|
||||||
|
_gpuContext = std::make_shared<gpu::Context>();
|
||||||
|
_backend = _gpuContext->getBackend();
|
||||||
|
_gpuContext->beginFrame();
|
||||||
|
_gpuContext->endFrame();
|
||||||
|
|
||||||
|
makeCurrent();
|
||||||
|
glGenTextures(1, &_externalTexture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, _externalTexture);
|
||||||
|
static const glm::u8vec4 color{ 0,1,0,0 };
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &color);
|
||||||
|
|
||||||
|
if (QFileInfo(FRAME_FILE).exists()) {
|
||||||
|
auto frame = gpu::readFrame(FRAME_FILE, _externalTexture, &textureLoader);
|
||||||
|
submitFrame(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderThread::shutdown() {
|
||||||
|
_activeFrame.reset();
|
||||||
|
while (!_pendingFrames.empty()) {
|
||||||
|
_gpuContext->consumeFrameUpdates(_pendingFrames.front());
|
||||||
|
_pendingFrames.pop();
|
||||||
|
}
|
||||||
|
_gpuContext->shutdown();
|
||||||
|
_gpuContext.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderThread::handleInput() {
|
||||||
|
static std::once_flag once;
|
||||||
|
std::call_once(once, [&]{
|
||||||
|
withOvrMobile([&](ovrMobile* session){
|
||||||
|
int deviceIndex = 0;
|
||||||
|
ovrInputCapabilityHeader capsHeader;
|
||||||
|
while (vrapi_EnumerateInputDevices(session, deviceIndex, &capsHeader) >= 0) {
|
||||||
|
if (capsHeader.Type == ovrControllerType_TrackedRemote) {
|
||||||
|
HandController controller = {};
|
||||||
|
controller.caps.Header = capsHeader;
|
||||||
|
controller.state.Header.ControllerType = ovrControllerType_TrackedRemote;
|
||||||
|
vrapi_GetInputDeviceCapabilities( session, &controller.caps.Header);
|
||||||
|
devices.push_back(controller);
|
||||||
|
}
|
||||||
|
++deviceIndex;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
auto readResult = ovr::VrHandler::withOvrMobile([&](ovrMobile *session) {
|
||||||
|
for (auto &controller : devices) {
|
||||||
|
controller.update(session);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (readResult) {
|
||||||
|
for (auto &controller : devices) {
|
||||||
|
const auto &caps = controller.caps;
|
||||||
|
if (controller.stateResult >= 0) {
|
||||||
|
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 {
|
||||||
|
translation = glm::vec3{remote.Joystick.x, 0.0f, -remote.Joystick.y};
|
||||||
|
}
|
||||||
|
float scale = 0.1f + (1.9f * remote.GripTrigger);
|
||||||
|
_correction = glm::translate(glm::mat4(), translation * scale) * _correction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderThread::renderFrame() {
|
||||||
|
GLuint finalTexture = 0;
|
||||||
|
glm::uvec2 finalTextureSize;
|
||||||
|
const auto& tracking = beginFrame();
|
||||||
|
if (_activeFrame) {
|
||||||
|
const auto& frame = _activeFrame;
|
||||||
|
auto& eyeProjections = frame->stereoState._eyeProjections;
|
||||||
|
auto& eyeOffsets = frame->stereoState._eyeViews;
|
||||||
|
// Quest
|
||||||
|
auto frameCorrection = _correction * ovr::toGlm(tracking.HeadPose.Pose);
|
||||||
|
_backend->setCameraCorrection(glm::inverse(frameCorrection), frame->view);
|
||||||
|
ovr::for_each_eye([&](ovrEye eye){
|
||||||
|
const auto& eyeInfo = tracking.Eye[eye];
|
||||||
|
eyeProjections[eye] = ovr::toGlm(eyeInfo.ProjectionMatrix);
|
||||||
|
eyeOffsets[eye] = ovr::toGlm(eyeInfo.ViewMatrix);
|
||||||
|
});
|
||||||
|
_backend->recycle();
|
||||||
|
_backend->syncCache();
|
||||||
|
_gpuContext->enableStereo(true);
|
||||||
|
if (frame && !frame->batches.empty()) {
|
||||||
|
_gpuContext->executeFrame(frame);
|
||||||
|
}
|
||||||
|
auto& glbackend = (gpu::gl::GLBackend&)(*_backend);
|
||||||
|
finalTextureSize = { frame->framebuffer->getWidth(), frame->framebuffer->getHeight() };
|
||||||
|
finalTexture = glbackend.getTextureID(frame->framebuffer->getRenderBuffer(0));
|
||||||
|
}
|
||||||
|
presentFrame(finalTexture, finalTextureSize, tracking);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenderThread::process() {
|
||||||
|
pollTask();
|
||||||
|
|
||||||
|
if (!vrActive()) {
|
||||||
|
QThread::msleep(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::queue<gpu::FramePointer> pendingFrames;
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(_frameLock);
|
||||||
|
pendingFrames.swap(_pendingFrames);
|
||||||
|
}
|
||||||
|
|
||||||
|
makeCurrent();
|
||||||
|
while (!pendingFrames.empty()) {
|
||||||
|
_activeFrame = pendingFrames.front();
|
||||||
|
pendingFrames.pop();
|
||||||
|
_gpuContext->consumeFrameUpdates(_activeFrame);
|
||||||
|
_activeFrame->stereoState._enable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInput();
|
||||||
|
renderFrame();
|
||||||
|
return true;
|
||||||
|
}
|
44
android/apps/questFramePlayer/src/main/cpp/RenderThread.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2018/10/21
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtCore/QElapsedTimer>
|
||||||
|
|
||||||
|
#include <GenericThread.h>
|
||||||
|
#include <shared/RateCounter.h>
|
||||||
|
#include <gl/Config.h>
|
||||||
|
#include <gl/Context.h>
|
||||||
|
#include <gpu/gl/GLBackend.h>
|
||||||
|
#include <ovr/VrHandler.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
uint32_t _externalTexture{ 0 };
|
||||||
|
glm::mat4 _correction;
|
||||||
|
|
||||||
|
void move(const glm::vec3& v);
|
||||||
|
void setup() override;
|
||||||
|
bool process() override;
|
||||||
|
void shutdown() override;
|
||||||
|
|
||||||
|
void handleInput();
|
||||||
|
|
||||||
|
void submitFrame(const gpu::FramePointer& frame);
|
||||||
|
void initialize(QWindow* window);
|
||||||
|
void renderFrame();
|
||||||
|
};
|
56
android/apps/questFramePlayer/src/main/cpp/main.cpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2018/11/22
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <android/log.h>
|
||||||
|
|
||||||
|
#include <QtGui/QGuiApplication>
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
|
|
||||||
|
#include <Trace.h>
|
||||||
|
|
||||||
|
#include "PlayerWindow.h"
|
||||||
|
|
||||||
|
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||||
|
if (!message.isEmpty()) {
|
||||||
|
const char * local=message.toStdString().c_str();
|
||||||
|
switch (type) {
|
||||||
|
case QtDebugMsg:
|
||||||
|
__android_log_write(ANDROID_LOG_DEBUG,"Interface",local);
|
||||||
|
break;
|
||||||
|
case QtInfoMsg:
|
||||||
|
__android_log_write(ANDROID_LOG_INFO,"Interface",local);
|
||||||
|
break;
|
||||||
|
case QtWarningMsg:
|
||||||
|
__android_log_write(ANDROID_LOG_WARN,"Interface",local);
|
||||||
|
break;
|
||||||
|
case QtCriticalMsg:
|
||||||
|
__android_log_write(ANDROID_LOG_ERROR,"Interface",local);
|
||||||
|
break;
|
||||||
|
case QtFatalMsg:
|
||||||
|
default:
|
||||||
|
__android_log_write(ANDROID_LOG_FATAL,"Interface",local);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
setupHifiApplication("gpuFramePlayer");
|
||||||
|
QGuiApplication app(argc, argv);
|
||||||
|
auto oldMessageHandler = qInstallMessageHandler(messageHandler);
|
||||||
|
DependencyManager::set<tracing::Tracer>();
|
||||||
|
PlayerWindow window;
|
||||||
|
__android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec");
|
||||||
|
app.exec();
|
||||||
|
__android_log_write(ANDROID_LOG_FATAL,"QQQ","Exec done");
|
||||||
|
qInstallMessageHandler(oldMessageHandler);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
//
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<resources>
|
||||||
|
<string name="app_name" translatable="false">GPU Frame Player</string>
|
||||||
|
</resources>
|
|
@ -73,13 +73,10 @@ RUN mkdir "$HIFI_BASE" && \
|
||||||
|
|
||||||
RUN git clone https://github.com/jherico/hifi.git && \
|
RUN git clone https://github.com/jherico/hifi.git && \
|
||||||
cd ~/hifi && \
|
cd ~/hifi && \
|
||||||
git checkout feature/quest_move_interface
|
git checkout feature/quest_frame_player
|
||||||
|
|
||||||
WORKDIR /home/jenkins/hifi
|
WORKDIR /home/jenkins/hifi
|
||||||
|
|
||||||
RUN touch .test6 && \
|
|
||||||
git fetch && git reset origin/feature/quest_move_interface --hard
|
|
||||||
|
|
||||||
RUN mkdir build
|
RUN mkdir build
|
||||||
|
|
||||||
# Pre-cache the vcpkg managed dependencies
|
# Pre-cache the vcpkg managed dependencies
|
||||||
|
|
17
android/libraries/oculus/build.gradle
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 28
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 24
|
||||||
|
targetSdkVersion 28
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
2
android/libraries/oculus/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.highfidelity.shared.oculus"/>
|
|
@ -0,0 +1,103 @@
|
||||||
|
//
|
||||||
|
// 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.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a native surface and forwards the activity lifecycle and surface lifecycle
|
||||||
|
* events to the OculusMobileDisplayPlugin
|
||||||
|
*/
|
||||||
|
public class OculusMobileActivity extends Activity 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 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);
|
||||||
|
// Create a native surface for VR rendering (Qt GL surfaces are not suitable
|
||||||
|
// because of the lack of fine control over the surface callbacks)
|
||||||
|
mView = new SurfaceView(this);
|
||||||
|
setContentView(mView);
|
||||||
|
mView.getHolder().addCallback(this);
|
||||||
|
|
||||||
|
// Forward the create message to the JNI code
|
||||||
|
nativeOnCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
Log.w(TAG, "QQQ onDestroy");
|
||||||
|
if (mSurfaceHolder != null) {
|
||||||
|
nativeOnSurfaceChanged(null);
|
||||||
|
}
|
||||||
|
nativeOnDestroy();
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
Log.w(TAG, "QQQ onResume");
|
||||||
|
super.onResume();
|
||||||
|
nativeOnResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
Log.w(TAG, "QQQ onPause");
|
||||||
|
nativeOnPause();
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
|
Log.w(TAG, "QQQ surfaceCreated");
|
||||||
|
nativeOnSurfaceChanged(holder.getSurface());
|
||||||
|
mSurfaceHolder = holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||||
|
Log.w(TAG, "QQQ surfaceChanged");
|
||||||
|
nativeOnSurfaceChanged(holder.getSurface());
|
||||||
|
mSurfaceHolder = holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
|
Log.w(TAG, "QQQ surfaceDestroyed");
|
||||||
|
nativeOnSurfaceChanged(null);
|
||||||
|
mSurfaceHolder = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -364,25 +364,7 @@ public class QtActivity extends Activity {
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
/*
|
QtApplication.invokeDelegate();
|
||||||
cduarte https://highfidelity.manuscript.com/f/cases/16712/App-freezes-on-opening-randomly
|
|
||||||
After Qt upgrade to 5.11 we had a black screen crash after closing the application with
|
|
||||||
the hardware button "Back" and trying to start the app again. It could only be fixed after
|
|
||||||
totally closing the app swiping it in the list of running apps.
|
|
||||||
This problem did not happen with the previous Qt version.
|
|
||||||
After analysing changes we came up with this case and change:
|
|
||||||
https://codereview.qt-project.org/#/c/218882/
|
|
||||||
In summary they've moved libs loading to the same thread as main() and as a matter of correctness
|
|
||||||
in the onDestroy method in QtActivityDelegate, they exit that thread with `QtNative.m_qtThread.exit();`
|
|
||||||
That exit call is the main reason of this problem.
|
|
||||||
|
|
||||||
In this fix we just replace the `QtApplication.invokeDelegate();` call that may end using the
|
|
||||||
entire onDestroy method including that thread exit line for other three lines that purposely
|
|
||||||
terminate qt (borrowed from QtActivityDelegate::onDestroy as well).
|
|
||||||
*/
|
|
||||||
QtNative.terminateQt();
|
|
||||||
QtNative.setActivity(null, null);
|
|
||||||
System.exit(0);
|
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,26 @@
|
||||||
|
//
|
||||||
|
// Libraries
|
||||||
|
//
|
||||||
|
|
||||||
|
include ':oculus'
|
||||||
|
project(':oculus').projectDir = new File(settingsDir, 'libraries/oculus')
|
||||||
|
|
||||||
include ':qt'
|
include ':qt'
|
||||||
project(':qt').projectDir = new File(settingsDir, 'libraries/qt')
|
project(':qt').projectDir = new File(settingsDir, 'libraries/qt')
|
||||||
|
|
||||||
|
//
|
||||||
|
// Applications
|
||||||
|
//
|
||||||
|
|
||||||
include ':interface'
|
include ':interface'
|
||||||
project(':interface').projectDir = new File(settingsDir, 'apps/interface')
|
project(':interface').projectDir = new File(settingsDir, 'apps/interface')
|
||||||
|
|
||||||
//include ':framePlayer'
|
//
|
||||||
//project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
|
// Test projects
|
||||||
|
//
|
||||||
|
|
||||||
|
include ':framePlayer'
|
||||||
|
project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
|
||||||
|
|
||||||
|
include ':questFramePlayer'
|
||||||
|
project(':questFramePlayer').projectDir = new File(settingsDir, 'apps/questFramePlayer')
|
||||||
|
|
|
@ -10,5 +10,5 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
macro(include_hifi_library_headers LIBRARY)
|
macro(include_hifi_library_headers LIBRARY)
|
||||||
include_directories("${HIFI_LIBRARY_DIR}/${LIBRARY}/src")
|
target_include_directories(${TARGET_NAME} PRIVATE "${HIFI_LIBRARY_DIR}/${LIBRARY}/src")
|
||||||
endmacro(include_hifi_library_headers _library _root_dir)
|
endmacro(include_hifi_library_headers _library _root_dir)
|
|
@ -19,8 +19,8 @@ function(LINK_HIFI_LIBRARIES)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
|
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
|
||||||
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
|
target_include_directories(${TARGET_NAME} PRIVATE "${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
|
||||||
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}")
|
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}")
|
||||||
# link the actual library - it is static so don't bubble it up
|
# link the actual library - it is static so don't bubble it up
|
||||||
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
|
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
4
cmake/macros/TargetEGL.cmake
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
macro(target_egl)
|
||||||
|
find_library(EGL EGL)
|
||||||
|
target_link_libraries(${TARGET_NAME} ${EGL})
|
||||||
|
endmacro()
|
20
cmake/macros/TargetOculusMobile.cmake
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
macro(target_oculus_mobile)
|
||||||
|
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/oculus/VrApi)
|
||||||
|
|
||||||
|
# Mobile SDK
|
||||||
|
set(OVR_MOBILE_INCLUDE_DIRS ${INSTALL_DIR}/Include)
|
||||||
|
target_include_directories(${TARGET_NAME} PRIVATE ${OVR_MOBILE_INCLUDE_DIRS})
|
||||||
|
set(OVR_MOBILE_LIBRARY_DIR ${INSTALL_DIR}/Libs/Android/arm64-v8a)
|
||||||
|
set(OVR_MOBILE_LIBRARY_RELEASE ${OVR_MOBILE_LIBRARY_DIR}/Release/libvrapi.so)
|
||||||
|
set(OVR_MOBILE_LIBRARY_DEBUG ${OVR_MOBILE_LIBRARY_DIR}/Debug/libvrapi.so)
|
||||||
|
select_library_configurations(OVR_MOBILE)
|
||||||
|
target_link_libraries(${TARGET_NAME} ${OVR_MOBILE_LIBRARIES})
|
||||||
|
|
||||||
|
# Platform SDK
|
||||||
|
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/oculusPlatform)
|
||||||
|
set(OVR_PLATFORM_INCLUDE_DIRS ${INSTALL_DIR}/Include)
|
||||||
|
target_include_directories(${TARGET_NAME} PRIVATE ${OVR_PLATFORM_INCLUDE_DIRS})
|
||||||
|
set(OVR_PLATFORM_LIBRARIES ${INSTALL_DIR}/Android/libs/arm64-v8a/libovrplatformloader.so)
|
||||||
|
target_link_libraries(${TARGET_NAME} ${OVR_PLATFORM_LIBRARIES})
|
||||||
|
endmacro()
|
|
@ -1,85 +0,0 @@
|
||||||
set(TARGET_NAME gvr-interface)
|
|
||||||
|
|
||||||
if (ANDROID)
|
|
||||||
set(ANDROID_APK_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk-build")
|
|
||||||
set(ANDROID_APK_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk")
|
|
||||||
|
|
||||||
set(ANDROID_SDK_ROOT $ENV{ANDROID_HOME})
|
|
||||||
set(ANDROID_APP_DISPLAY_NAME Interface)
|
|
||||||
set(ANDROID_API_LEVEL 19)
|
|
||||||
set(ANDROID_APK_PACKAGE io.highfidelity.gvrinterface)
|
|
||||||
set(ANDROID_ACTIVITY_NAME io.highfidelity.gvrinterface.InterfaceActivity)
|
|
||||||
set(ANDROID_APK_VERSION_NAME "0.1")
|
|
||||||
set(ANDROID_APK_VERSION_CODE 1)
|
|
||||||
set(ANDROID_APK_FULLSCREEN TRUE)
|
|
||||||
set(ANDROID_DEPLOY_QT_INSTALL "--install")
|
|
||||||
|
|
||||||
set(BUILD_SHARED_LIBS ON)
|
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}")
|
|
||||||
|
|
||||||
setup_hifi_library(Gui AndroidExtras)
|
|
||||||
else ()
|
|
||||||
setup_hifi_project(Gui)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
link_hifi_libraries(shared networking audio-client avatars)
|
|
||||||
|
|
||||||
if (ANDROID)
|
|
||||||
find_package(LibOVR)
|
|
||||||
|
|
||||||
if (LIBOVR_FOUND)
|
|
||||||
add_definitions(-DHAVE_LIBOVR)
|
|
||||||
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES} ${LIBOVR_ANDROID_LIBRARIES} ${TURBOJPEG_LIBRARY})
|
|
||||||
include_directories(SYSTEM ${LIBOVR_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
# we need VRLib, so add a project.properties to our apk build folder that says that
|
|
||||||
file(RELATIVE_PATH RELATIVE_VRLIB_PATH ${ANDROID_APK_OUTPUT_DIR} "${LIBOVR_VRLIB_DIR}")
|
|
||||||
file(WRITE "${ANDROID_APK_BUILD_DIR}/project.properties" "android.library.reference.1=${RELATIVE_VRLIB_PATH}")
|
|
||||||
|
|
||||||
list(APPEND IGNORE_COPY_LIBS ${LIBOVR_ANDROID_LIBRARIES})
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# the presence of a HOCKEY_APP_ID means we are making a beta build
|
|
||||||
if (ANDROID AND HOCKEY_APP_ID)
|
|
||||||
set(HOCKEY_APP_ENABLED true)
|
|
||||||
set(HOCKEY_APP_ACTIVITY "<activity android:name='net.hockeyapp.android.UpdateActivity' />\n")
|
|
||||||
set(ANDROID_ACTIVITY_NAME io.highfidelity.gvrinterface.InterfaceBetaActivity)
|
|
||||||
set(ANDROID_DEPLOY_QT_INSTALL "")
|
|
||||||
set(ANDROID_APK_CUSTOM_NAME "Interface-beta.apk")
|
|
||||||
|
|
||||||
# set the ANDROID_APK_VERSION_CODE to the number of git commits
|
|
||||||
execute_process(
|
|
||||||
COMMAND git rev-list --first-parent --count HEAD
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
||||||
OUTPUT_VARIABLE GIT_COMMIT_COUNT
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
)
|
|
||||||
|
|
||||||
set(ANDROID_APK_VERSION_CODE ${GIT_COMMIT_COUNT})
|
|
||||||
|
|
||||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/templates/InterfaceBetaActivity.java.in" "${ANDROID_APK_BUILD_DIR}/src/io/highfidelity/gvrinterface/InterfaceBetaActivity.java")
|
|
||||||
elseif (ANDROID)
|
|
||||||
set(HOCKEY_APP_ENABLED false)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (ANDROID)
|
|
||||||
|
|
||||||
set(HIFI_URL_INTENT "<intent-filter>\
|
|
||||||
\n <action android:name='android.intent.action.VIEW' />\
|
|
||||||
\n <category android:name='android.intent.category.DEFAULT' />\
|
|
||||||
\n <category android:name='android.intent.category.BROWSABLE' />\
|
|
||||||
\n <data android:scheme='hifi' />\
|
|
||||||
\n </intent-filter>"
|
|
||||||
)
|
|
||||||
|
|
||||||
set(ANDROID_EXTRA_APPLICATION_XML "${HOCKEY_APP_ACTIVITY}")
|
|
||||||
set(ANDROID_EXTRA_ACTIVITY_XML "${HIFI_URL_INTENT}")
|
|
||||||
|
|
||||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/templates/hockeyapp.xml.in" "${ANDROID_APK_BUILD_DIR}/res/values/hockeyapp.xml")
|
|
||||||
qt_create_apk()
|
|
||||||
|
|
||||||
endif (ANDROID)
|
|
Before Width: | Height: | Size: 9.7 KiB |
|
@ -1,73 +0,0 @@
|
||||||
//
|
|
||||||
// Client.cpp
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 1/20/15.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "Client.h"
|
|
||||||
|
|
||||||
#include <AccountManager.h>
|
|
||||||
#include <AddressManager.h>
|
|
||||||
#include <HifiSockAddr.h>
|
|
||||||
#include <NodeList.h>
|
|
||||||
#include <PacketHeaders.h>
|
|
||||||
|
|
||||||
Client::Client(QObject* parent) :
|
|
||||||
QObject(parent)
|
|
||||||
{
|
|
||||||
// we need to make sure that required dependencies are created
|
|
||||||
DependencyManager::set<AddressManager>();
|
|
||||||
|
|
||||||
setupNetworking();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Client::setupNetworking() {
|
|
||||||
// once Application order of instantiation is fixed this should be done from AccountManager
|
|
||||||
AccountManager::getInstance().setAuthURL(DEFAULT_NODE_AUTH_URL);
|
|
||||||
|
|
||||||
// setup the NodeList for this client
|
|
||||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
|
||||||
auto nodeList = DependencyManager::set<NodeList>(NodeType::Agent, 0);
|
|
||||||
|
|
||||||
// while datagram processing remains simple for targets using Client, we'll handle datagrams
|
|
||||||
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &Client::processDatagrams);
|
|
||||||
|
|
||||||
// every second, ask the NodeList to check in with the domain server
|
|
||||||
QTimer* domainCheckInTimer = new QTimer(this);
|
|
||||||
domainCheckInTimer->setInterval(DOMAIN_SERVER_CHECK_IN_MSECS);
|
|
||||||
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
|
|
||||||
|
|
||||||
// TODO: once the Client knows its Address on start-up we should be able to immediately send a check in here
|
|
||||||
domainCheckInTimer->start();
|
|
||||||
|
|
||||||
// handle the case where the domain stops talking to us
|
|
||||||
// TODO: can we just have the nodelist do this when it sets up? Is there a user of the NodeList that wouldn't want this?
|
|
||||||
connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Client::processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket) {
|
|
||||||
DependencyManager::get<NodeList>()->processNodeData(senderSockAddr, incomingPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Client::processDatagrams() {
|
|
||||||
HifiSockAddr senderSockAddr;
|
|
||||||
|
|
||||||
static QByteArray incomingPacket;
|
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
|
||||||
|
|
||||||
while (DependencyManager::get<NodeList>()->getNodeSocket().hasPendingDatagrams()) {
|
|
||||||
incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
|
|
||||||
nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(),
|
|
||||||
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
|
|
||||||
|
|
||||||
if (nodeList->packetVersionAndHashMatch(incomingPacket)) {
|
|
||||||
processVerifiedPacket(senderSockAddr, incomingPacket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
//
|
|
||||||
// Client.h
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 1/20/15.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_Client_h
|
|
||||||
#define hifi_Client_h
|
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
|
|
||||||
#include <HifiSockAddr.h>
|
|
||||||
|
|
||||||
class Client : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
Client(QObject* parent = 0);
|
|
||||||
|
|
||||||
virtual void cleanupBeforeQuit() = 0;
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void setupNetworking();
|
|
||||||
virtual void processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket);
|
|
||||||
private slots:
|
|
||||||
void processDatagrams();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_Client_h
|
|
|
@ -1,191 +0,0 @@
|
||||||
//
|
|
||||||
// GVRInterface.cpp
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 11/18/14.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "GVRInterface.h"
|
|
||||||
|
|
||||||
#ifdef ANDROID
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
#include <qpa/qplatformnativeinterface.h>
|
|
||||||
#include <QtAndroidExtras/QAndroidJniEnvironment>
|
|
||||||
#include <QtAndroidExtras/QAndroidJniObject>
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBOVR
|
|
||||||
|
|
||||||
#include <KeyState.h>
|
|
||||||
#include <VrApi/VrApi.h>
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <QtCore/QTimer>
|
|
||||||
#include <QtGui/QKeyEvent>
|
|
||||||
#include <QtWidgets/QMenuBar>
|
|
||||||
|
|
||||||
#include "GVRMainWindow.h"
|
|
||||||
#include "RenderingClient.h"
|
|
||||||
|
|
||||||
static QString launchURLString = QString();
|
|
||||||
|
|
||||||
#ifdef ANDROID
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
JNIEXPORT void Java_io_highfidelity_gvrinterface_InterfaceActivity_handleHifiURL(JNIEnv *jni, jclass clazz, jstring hifiURLString) {
|
|
||||||
launchURLString = QAndroidJniObject(hifiURLString).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GVRInterface::GVRInterface(int argc, char* argv[]) :
|
|
||||||
QApplication(argc, argv),
|
|
||||||
_mainWindow(NULL),
|
|
||||||
_inVRMode(false)
|
|
||||||
{
|
|
||||||
setApplicationName("gvr-interface");
|
|
||||||
setOrganizationName("highfidelity");
|
|
||||||
setOrganizationDomain("io");
|
|
||||||
|
|
||||||
if (!launchURLString.isEmpty()) {
|
|
||||||
// did we get launched with a lookup URL? If so it is time to give that to the AddressManager
|
|
||||||
qDebug() << "We were opened via a hifi URL -" << launchURLString;
|
|
||||||
}
|
|
||||||
|
|
||||||
_client = new RenderingClient(this, launchURLString);
|
|
||||||
|
|
||||||
launchURLString = QString();
|
|
||||||
|
|
||||||
connect(this, &QGuiApplication::applicationStateChanged, this, &GVRInterface::handleApplicationStateChange);
|
|
||||||
|
|
||||||
#if defined(ANDROID) && defined(HAVE_LIBOVR)
|
|
||||||
QAndroidJniEnvironment jniEnv;
|
|
||||||
|
|
||||||
QPlatformNativeInterface* interface = QApplication::platformNativeInterface();
|
|
||||||
jobject activity = (jobject) interface->nativeResourceForIntegration("QtActivity");
|
|
||||||
|
|
||||||
ovr_RegisterHmtReceivers(&*jniEnv, activity);
|
|
||||||
|
|
||||||
// PLATFORMACTIVITY_REMOVAL: Temp workaround for PlatformActivity being
|
|
||||||
// stripped from UnityPlugin. Alternate is to use LOCAL_WHOLE_STATIC_LIBRARIES
|
|
||||||
// but that increases the size of the plugin by ~1MiB
|
|
||||||
OVR::linkerPlatformActivity++;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// call our idle function whenever we can
|
|
||||||
QTimer* idleTimer = new QTimer(this);
|
|
||||||
connect(idleTimer, &QTimer::timeout, this, &GVRInterface::idle);
|
|
||||||
idleTimer->start(0);
|
|
||||||
|
|
||||||
// call our quit handler before we go down
|
|
||||||
connect(this, &QCoreApplication::aboutToQuit, this, &GVRInterface::handleApplicationQuit);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRInterface::handleApplicationQuit() {
|
|
||||||
_client->cleanupBeforeQuit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRInterface::idle() {
|
|
||||||
#if defined(ANDROID) && defined(HAVE_LIBOVR)
|
|
||||||
if (!_inVRMode && ovr_IsHeadsetDocked()) {
|
|
||||||
qDebug() << "The headset just got docked - enter VR mode.";
|
|
||||||
enterVRMode();
|
|
||||||
} else if (_inVRMode) {
|
|
||||||
|
|
||||||
if (ovr_IsHeadsetDocked()) {
|
|
||||||
static int counter = 0;
|
|
||||||
|
|
||||||
// Get the latest head tracking state, predicted ahead to the midpoint of the time
|
|
||||||
// it will be displayed. It will always be corrected to the real values by
|
|
||||||
// time warp, but the closer we get, the less black will be pulled in at the edges.
|
|
||||||
const double now = ovr_GetTimeInSeconds();
|
|
||||||
static double prev;
|
|
||||||
const double rawDelta = now - prev;
|
|
||||||
prev = now;
|
|
||||||
const double clampedPrediction = std::min( 0.1, rawDelta * 2);
|
|
||||||
ovrSensorState sensor = ovrHmd_GetSensorState(OvrHmd, now + clampedPrediction, true );
|
|
||||||
|
|
||||||
auto ovrOrientation = sensor.Predicted.Pose.Orientation;
|
|
||||||
glm::quat newOrientation(ovrOrientation.w, ovrOrientation.x, ovrOrientation.y, ovrOrientation.z);
|
|
||||||
_client->setOrientation(newOrientation);
|
|
||||||
|
|
||||||
if (counter++ % 100000 == 0) {
|
|
||||||
qDebug() << "GetSensorState in frame" << counter << "-"
|
|
||||||
<< ovrOrientation.x << ovrOrientation.y << ovrOrientation.z << ovrOrientation.w;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "The headset was undocked - leaving VR mode.";
|
|
||||||
|
|
||||||
leaveVRMode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OVR::KeyState& backKeyState = _mainWindow->getBackKeyState();
|
|
||||||
auto backEvent = backKeyState.Update(ovr_GetTimeInSeconds());
|
|
||||||
|
|
||||||
if (backEvent == OVR::KeyState::KEY_EVENT_LONG_PRESS) {
|
|
||||||
qDebug() << "Attemping to start the Platform UI Activity.";
|
|
||||||
ovr_StartPackageActivity(_ovr, PUI_CLASS_NAME, PUI_GLOBAL_MENU);
|
|
||||||
} else if (backEvent == OVR::KeyState::KEY_EVENT_DOUBLE_TAP || backEvent == OVR::KeyState::KEY_EVENT_SHORT_PRESS) {
|
|
||||||
qDebug() << "Got an event we should cancel for!";
|
|
||||||
} else if (backEvent == OVR::KeyState::KEY_EVENT_DOUBLE_TAP) {
|
|
||||||
qDebug() << "The button is down!";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRInterface::handleApplicationStateChange(Qt::ApplicationState state) {
|
|
||||||
switch(state) {
|
|
||||||
case Qt::ApplicationActive:
|
|
||||||
qDebug() << "The application is active.";
|
|
||||||
break;
|
|
||||||
case Qt::ApplicationSuspended:
|
|
||||||
qDebug() << "The application is being suspended.";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRInterface::enterVRMode() {
|
|
||||||
#if defined(ANDROID) && defined(HAVE_LIBOVR)
|
|
||||||
// Default vrModeParms
|
|
||||||
ovrModeParms vrModeParms;
|
|
||||||
vrModeParms.AsynchronousTimeWarp = true;
|
|
||||||
vrModeParms.AllowPowerSave = true;
|
|
||||||
vrModeParms.DistortionFileName = NULL;
|
|
||||||
vrModeParms.EnableImageServer = false;
|
|
||||||
vrModeParms.CpuLevel = 2;
|
|
||||||
vrModeParms.GpuLevel = 2;
|
|
||||||
vrModeParms.GameThreadTid = 0;
|
|
||||||
|
|
||||||
QAndroidJniEnvironment jniEnv;
|
|
||||||
|
|
||||||
QPlatformNativeInterface* interface = QApplication::platformNativeInterface();
|
|
||||||
jobject activity = (jobject) interface->nativeResourceForIntegration("QtActivity");
|
|
||||||
|
|
||||||
vrModeParms.ActivityObject = activity;
|
|
||||||
|
|
||||||
ovrHmdInfo hmdInfo;
|
|
||||||
_ovr = ovr_EnterVrMode(vrModeParms, &hmdInfo);
|
|
||||||
|
|
||||||
_inVRMode = true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRInterface::leaveVRMode() {
|
|
||||||
#if defined(ANDROID) && defined(HAVE_LIBOVR)
|
|
||||||
ovr_LeaveVrMode(_ovr);
|
|
||||||
_inVRMode = false;
|
|
||||||
#endif
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
//
|
|
||||||
// GVRInterface.h
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 11/18/14.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_GVRInterface_h
|
|
||||||
#define hifi_GVRInterface_h
|
|
||||||
|
|
||||||
#include <QtWidgets/QApplication>
|
|
||||||
|
|
||||||
#if defined(ANDROID) && defined(HAVE_LIBOVR)
|
|
||||||
class ovrMobile;
|
|
||||||
class ovrHmdInfo;
|
|
||||||
|
|
||||||
// This is set by JNI_OnLoad() when the .so is initially loaded.
|
|
||||||
// Must use to attach each thread that will use JNI:
|
|
||||||
namespace OVR {
|
|
||||||
// PLATFORMACTIVITY_REMOVAL: Temp workaround for PlatformActivity being
|
|
||||||
// stripped from UnityPlugin. Alternate is to use LOCAL_WHOLE_STATIC_LIBRARIES
|
|
||||||
// but that increases the size of the plugin by ~1MiB
|
|
||||||
extern int linkerPlatformActivity;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class GVRMainWindow;
|
|
||||||
class RenderingClient;
|
|
||||||
class QKeyEvent;
|
|
||||||
|
|
||||||
#if defined(qApp)
|
|
||||||
#undef qApp
|
|
||||||
#endif
|
|
||||||
#define qApp (static_cast<GVRInterface*>(QApplication::instance()))
|
|
||||||
|
|
||||||
class GVRInterface : public QApplication {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
GVRInterface(int argc, char* argv[]);
|
|
||||||
RenderingClient* getClient() { return _client; }
|
|
||||||
|
|
||||||
void setMainWindow(GVRMainWindow* mainWindow) { _mainWindow = mainWindow; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void keyPressEvent(QKeyEvent* event);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void handleApplicationStateChange(Qt::ApplicationState state);
|
|
||||||
void idle();
|
|
||||||
private:
|
|
||||||
void handleApplicationQuit();
|
|
||||||
|
|
||||||
void enterVRMode();
|
|
||||||
void leaveVRMode();
|
|
||||||
|
|
||||||
#if defined(ANDROID) && defined(HAVE_LIBOVR)
|
|
||||||
ovrMobile* _ovr;
|
|
||||||
ovrHmdInfo* _hmdInfo;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GVRMainWindow* _mainWindow;
|
|
||||||
|
|
||||||
RenderingClient* _client;
|
|
||||||
bool _inVRMode;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_GVRInterface_h
|
|
|
@ -1,176 +0,0 @@
|
||||||
//
|
|
||||||
// GVRMainWindow.cpp
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 1/20/14.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "GVRMainWindow.h"
|
|
||||||
|
|
||||||
#include <QtGui/QKeyEvent>
|
|
||||||
#include <QtWidgets/QApplication>
|
|
||||||
#include <QtWidgets/QInputDialog>
|
|
||||||
#include <QtWidgets/QLabel>
|
|
||||||
#include <QtWidgets/QLineEdit>
|
|
||||||
#include <QtWidgets/QMenuBar>
|
|
||||||
#include <QtWidgets/QMessageBox>
|
|
||||||
#include <QtWidgets/QVBoxLayout>
|
|
||||||
|
|
||||||
#ifndef ANDROID
|
|
||||||
|
|
||||||
#include <QtWidgets/QDesktopWidget>
|
|
||||||
|
|
||||||
#elif defined(HAVE_LIBOVR)
|
|
||||||
|
|
||||||
#include <OVR_CAPI.h>
|
|
||||||
|
|
||||||
const float LIBOVR_DOUBLE_TAP_DURATION = 0.25f;
|
|
||||||
const float LIBOVR_LONG_PRESS_DURATION = 0.75f;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <AddressManager.h>
|
|
||||||
|
|
||||||
#include "InterfaceView.h"
|
|
||||||
#include "LoginDialog.h"
|
|
||||||
#include "RenderingClient.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GVRMainWindow::GVRMainWindow(QWidget* parent) :
|
|
||||||
QMainWindow(parent),
|
|
||||||
#if defined(ANDROID) && defined(HAVE_LIBOVR)
|
|
||||||
_backKeyState(LIBOVR_DOUBLE_TAP_DURATION, LIBOVR_LONG_PRESS_DURATION),
|
|
||||||
_wasBackKeyDown(false),
|
|
||||||
#endif
|
|
||||||
_mainLayout(NULL),
|
|
||||||
_menuBar(NULL),
|
|
||||||
_loginAction(NULL)
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifndef ANDROID
|
|
||||||
const int NOTE_4_WIDTH = 2560;
|
|
||||||
const int NOTE_4_HEIGHT = 1440;
|
|
||||||
setFixedSize(NOTE_4_WIDTH / 2, NOTE_4_HEIGHT / 2);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
setupMenuBar();
|
|
||||||
|
|
||||||
QWidget* baseWidget = new QWidget(this);
|
|
||||||
|
|
||||||
// setup a layout so we can vertically align to top
|
|
||||||
_mainLayout = new QVBoxLayout(baseWidget);
|
|
||||||
_mainLayout->setAlignment(Qt::AlignTop);
|
|
||||||
|
|
||||||
// set the layout on the base widget
|
|
||||||
baseWidget->setLayout(_mainLayout);
|
|
||||||
|
|
||||||
setCentralWidget(baseWidget);
|
|
||||||
|
|
||||||
// add the interface view
|
|
||||||
new InterfaceView(baseWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
GVRMainWindow::~GVRMainWindow() {
|
|
||||||
delete _menuBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRMainWindow::keyPressEvent(QKeyEvent* event) {
|
|
||||||
#ifdef ANDROID
|
|
||||||
if (event->key() == Qt::Key_Back) {
|
|
||||||
// got the Android back key, hand off to OVR KeyState
|
|
||||||
_backKeyState.HandleEvent(ovr_GetTimeInSeconds(), true, (_wasBackKeyDown ? 1 : 0));
|
|
||||||
_wasBackKeyDown = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
QWidget::keyPressEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRMainWindow::keyReleaseEvent(QKeyEvent* event) {
|
|
||||||
#ifdef ANDROID
|
|
||||||
if (event->key() == Qt::Key_Back) {
|
|
||||||
// release on the Android back key, hand off to OVR KeyState
|
|
||||||
_backKeyState.HandleEvent(ovr_GetTimeInSeconds(), false, 0);
|
|
||||||
_wasBackKeyDown = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
QWidget::keyReleaseEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRMainWindow::setupMenuBar() {
|
|
||||||
QMenu* fileMenu = new QMenu("File");
|
|
||||||
QMenu* helpMenu = new QMenu("Help");
|
|
||||||
|
|
||||||
_menuBar = new QMenuBar(0);
|
|
||||||
|
|
||||||
_menuBar->addMenu(fileMenu);
|
|
||||||
_menuBar->addMenu(helpMenu);
|
|
||||||
|
|
||||||
QAction* goToAddress = new QAction("Go to Address", fileMenu);
|
|
||||||
connect(goToAddress, &QAction::triggered, this, &GVRMainWindow::showAddressBar);
|
|
||||||
fileMenu->addAction(goToAddress);
|
|
||||||
|
|
||||||
_loginAction = new QAction("Login", fileMenu);
|
|
||||||
fileMenu->addAction(_loginAction);
|
|
||||||
|
|
||||||
// change the login action depending on our logged in/out state
|
|
||||||
AccountManager& accountManager = AccountManager::getInstance();
|
|
||||||
connect(&accountManager, &AccountManager::loginComplete, this, &GVRMainWindow::refreshLoginAction);
|
|
||||||
connect(&accountManager, &AccountManager::logoutComplete, this, &GVRMainWindow::refreshLoginAction);
|
|
||||||
|
|
||||||
// refresh the state now
|
|
||||||
refreshLoginAction();
|
|
||||||
|
|
||||||
QAction* aboutQt = new QAction("About Qt", helpMenu);
|
|
||||||
connect(aboutQt, &QAction::triggered, qApp, &QApplication::aboutQt);
|
|
||||||
helpMenu->addAction(aboutQt);
|
|
||||||
|
|
||||||
setMenuBar(_menuBar);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRMainWindow::showAddressBar() {
|
|
||||||
// setup the address QInputDialog
|
|
||||||
QInputDialog* addressDialog = new QInputDialog(this);
|
|
||||||
addressDialog->setLabelText("Address");
|
|
||||||
|
|
||||||
// add the address dialog to the main layout
|
|
||||||
_mainLayout->addWidget(addressDialog);
|
|
||||||
|
|
||||||
connect(addressDialog, &QInputDialog::textValueSelected,
|
|
||||||
DependencyManager::get<AddressManager>().data(), &AddressManager::handleLookupString);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRMainWindow::showLoginDialog() {
|
|
||||||
LoginDialog* loginDialog = new LoginDialog(this);
|
|
||||||
|
|
||||||
// have the acccount manager handle credentials from LoginDialog
|
|
||||||
AccountManager& accountManager = AccountManager::getInstance();
|
|
||||||
connect(loginDialog, &LoginDialog::credentialsEntered, &accountManager, &AccountManager::requestAccessToken);
|
|
||||||
connect(&accountManager, &AccountManager::loginFailed, this, &GVRMainWindow::showLoginFailure);
|
|
||||||
|
|
||||||
_mainLayout->addWidget(loginDialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRMainWindow::showLoginFailure() {
|
|
||||||
QMessageBox::warning(this, "Login Failed",
|
|
||||||
"Could not log in with that username and password. Please try again!");
|
|
||||||
}
|
|
||||||
|
|
||||||
void GVRMainWindow::refreshLoginAction() {
|
|
||||||
AccountManager& accountManager = AccountManager::getInstance();
|
|
||||||
disconnect(_loginAction, &QAction::triggered, &accountManager, 0);
|
|
||||||
|
|
||||||
if (accountManager.isLoggedIn()) {
|
|
||||||
_loginAction->setText("Logout");
|
|
||||||
connect(_loginAction, &QAction::triggered, &accountManager, &AccountManager::logout);
|
|
||||||
} else {
|
|
||||||
_loginAction->setText("Login");
|
|
||||||
connect(_loginAction, &QAction::triggered, this, &GVRMainWindow::showLoginDialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
//
|
|
||||||
// GVRMainWindow.h
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 1/20/14.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_GVRMainWindow_h
|
|
||||||
#define hifi_GVRMainWindow_h
|
|
||||||
|
|
||||||
#include <QtWidgets/QMainWindow>
|
|
||||||
|
|
||||||
#if defined(ANDROID) && defined(HAVE_LIBOVR)
|
|
||||||
#include <KeyState.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class QKeyEvent;
|
|
||||||
class QMenuBar;
|
|
||||||
class QVBoxLayout;
|
|
||||||
|
|
||||||
class GVRMainWindow : public QMainWindow {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
GVRMainWindow(QWidget* parent = 0);
|
|
||||||
~GVRMainWindow();
|
|
||||||
public slots:
|
|
||||||
void showAddressBar();
|
|
||||||
void showLoginDialog();
|
|
||||||
|
|
||||||
void showLoginFailure();
|
|
||||||
|
|
||||||
#if defined(ANDROID) && defined(HAVE_LIBOVR)
|
|
||||||
OVR::KeyState& getBackKeyState() { return _backKeyState; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void keyPressEvent(QKeyEvent* event);
|
|
||||||
void keyReleaseEvent(QKeyEvent* event);
|
|
||||||
private slots:
|
|
||||||
void refreshLoginAction();
|
|
||||||
private:
|
|
||||||
void setupMenuBar();
|
|
||||||
|
|
||||||
#if defined(ANDROID) && defined(HAVE_LIBOVR)
|
|
||||||
OVR::KeyState _backKeyState;
|
|
||||||
bool _wasBackKeyDown;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QVBoxLayout* _mainLayout;
|
|
||||||
QMenuBar* _menuBar;
|
|
||||||
QAction* _loginAction;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_GVRMainWindow_h
|
|
|
@ -1,18 +0,0 @@
|
||||||
//
|
|
||||||
// InterfaceView.cpp
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 1/28/14.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "InterfaceView.h"
|
|
||||||
|
|
||||||
InterfaceView::InterfaceView(QWidget* parent, Qt::WindowFlags flags) :
|
|
||||||
QOpenGLWidget(parent, flags)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
//
|
|
||||||
// InterfaceView.h
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 1/28/14.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_InterfaceView_h
|
|
||||||
#define hifi_InterfaceView_h
|
|
||||||
|
|
||||||
#include <QtWidgets/QOpenGLWidget>
|
|
||||||
|
|
||||||
class InterfaceView : public QOpenGLWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
InterfaceView(QWidget* parent = 0, Qt::WindowFlags flags = 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_InterfaceView_h
|
|
|
@ -1,69 +0,0 @@
|
||||||
//
|
|
||||||
// LoginDialog.cpp
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 2015-02-03.
|
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "LoginDialog.h"
|
|
||||||
|
|
||||||
#include <QtWidgets/QDialogButtonBox>
|
|
||||||
#include <QtWidgets/QGridLayout>
|
|
||||||
#include <QtWidgets/QLabel>
|
|
||||||
#include <QtWidgets/QLineEdit>
|
|
||||||
#include <QtWidgets/QPushButton>
|
|
||||||
|
|
||||||
LoginDialog::LoginDialog(QWidget* parent) :
|
|
||||||
QDialog(parent)
|
|
||||||
{
|
|
||||||
setupGUI();
|
|
||||||
setWindowTitle("Login");
|
|
||||||
setModal(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoginDialog::setupGUI() {
|
|
||||||
// setup a grid layout
|
|
||||||
QGridLayout* formGridLayout = new QGridLayout(this);
|
|
||||||
|
|
||||||
_usernameLineEdit = new QLineEdit(this);
|
|
||||||
|
|
||||||
QLabel* usernameLabel = new QLabel(this);
|
|
||||||
usernameLabel->setText("Username");
|
|
||||||
usernameLabel->setBuddy(_usernameLineEdit);
|
|
||||||
|
|
||||||
formGridLayout->addWidget(usernameLabel, 0, 0);
|
|
||||||
formGridLayout->addWidget(_usernameLineEdit, 1, 0);
|
|
||||||
|
|
||||||
_passwordLineEdit = new QLineEdit(this);
|
|
||||||
_passwordLineEdit->setEchoMode(QLineEdit::Password);
|
|
||||||
|
|
||||||
QLabel* passwordLabel = new QLabel(this);
|
|
||||||
passwordLabel->setText("Password");
|
|
||||||
passwordLabel->setBuddy(_passwordLineEdit);
|
|
||||||
|
|
||||||
formGridLayout->addWidget(passwordLabel, 2, 0);
|
|
||||||
formGridLayout->addWidget(_passwordLineEdit, 3, 0);
|
|
||||||
|
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(this);
|
|
||||||
|
|
||||||
QPushButton* okButton = buttons->addButton(QDialogButtonBox::Ok);
|
|
||||||
QPushButton* cancelButton = buttons->addButton(QDialogButtonBox::Cancel);
|
|
||||||
|
|
||||||
okButton->setText("Login");
|
|
||||||
|
|
||||||
connect(cancelButton, &QPushButton::clicked, this, &QDialog::close);
|
|
||||||
connect(okButton, &QPushButton::clicked, this, &LoginDialog::loginButtonClicked);
|
|
||||||
|
|
||||||
formGridLayout->addWidget(buttons, 4, 0, 1, 2);
|
|
||||||
|
|
||||||
setLayout(formGridLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoginDialog::loginButtonClicked() {
|
|
||||||
emit credentialsEntered(_usernameLineEdit->text(), _passwordLineEdit->text());
|
|
||||||
close();
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
//
|
|
||||||
// LoginDialog.h
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 2015-02-03.
|
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_LoginDialog_h
|
|
||||||
#define hifi_LoginDialog_h
|
|
||||||
|
|
||||||
#include <QtWidgets/QDialog>
|
|
||||||
|
|
||||||
class QLineEdit;
|
|
||||||
|
|
||||||
class LoginDialog : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
LoginDialog(QWidget* parent = 0);
|
|
||||||
signals:
|
|
||||||
void credentialsEntered(const QString& username, const QString& password);
|
|
||||||
private slots:
|
|
||||||
void loginButtonClicked();
|
|
||||||
private:
|
|
||||||
void setupGUI();
|
|
||||||
|
|
||||||
QLineEdit* _usernameLineEdit;
|
|
||||||
QLineEdit* _passwordLineEdit;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_LoginDialog_h
|
|
|
@ -1,156 +0,0 @@
|
||||||
//
|
|
||||||
// RenderingClient.cpp
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 1/20/15.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "RenderingClient.h"
|
|
||||||
|
|
||||||
#include <QtCore/QThread>
|
|
||||||
#include <QtWidgets/QInputDialog>
|
|
||||||
|
|
||||||
#include <AddressManager.h>
|
|
||||||
#include <AudioClient.h>
|
|
||||||
#include <AvatarHashMap.h>
|
|
||||||
#include <NodeList.h>
|
|
||||||
|
|
||||||
RenderingClient* RenderingClient::_instance = NULL;
|
|
||||||
|
|
||||||
RenderingClient::RenderingClient(QObject *parent, const QString& launchURLString) :
|
|
||||||
Client(parent)
|
|
||||||
{
|
|
||||||
_instance = this;
|
|
||||||
|
|
||||||
// connect to AddressManager and pass it the launch URL, if we have one
|
|
||||||
auto addressManager = DependencyManager::get<AddressManager>();
|
|
||||||
connect(addressManager.data(), &AddressManager::locationChangeRequired, this, &RenderingClient::goToLocation);
|
|
||||||
addressManager->loadSettings(launchURLString);
|
|
||||||
|
|
||||||
// tell the NodeList which node types all rendering clients will want to know about
|
|
||||||
DependencyManager::get<NodeList>()->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer);
|
|
||||||
|
|
||||||
DependencyManager::set<AvatarHashMap>();
|
|
||||||
|
|
||||||
// get our audio client setup on its own thread
|
|
||||||
auto audioClient = DependencyManager::set<AudioClient>();
|
|
||||||
audioClient->setPositionGetter(getPositionForAudio);
|
|
||||||
audioClient->setOrientationGetter(getOrientationForAudio);
|
|
||||||
audioClient->startThread();
|
|
||||||
|
|
||||||
|
|
||||||
connect(&_avatarTimer, &QTimer::timeout, this, &RenderingClient::sendAvatarPacket);
|
|
||||||
_avatarTimer.setInterval(16); // 60 FPS
|
|
||||||
_avatarTimer.start();
|
|
||||||
_fakeAvatar.setDisplayName("GearVR");
|
|
||||||
_fakeAvatar.setFaceModelURL(QUrl(DEFAULT_HEAD_MODEL_URL));
|
|
||||||
_fakeAvatar.setSkeletonModelURL(QUrl(DEFAULT_BODY_MODEL_URL));
|
|
||||||
_fakeAvatar.toByteArray(); // Creates HeadData
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderingClient::sendAvatarPacket() {
|
|
||||||
_fakeAvatar.setPosition(_position);
|
|
||||||
_fakeAvatar.setHeadOrientation(_orientation);
|
|
||||||
|
|
||||||
QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
|
|
||||||
packet.append(_fakeAvatar.toByteArray());
|
|
||||||
DependencyManager::get<NodeList>()->broadcastToNodes(packet, NodeSet() << NodeType::AvatarMixer);
|
|
||||||
_fakeAvatar.sendIdentityPacket();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderingClient::cleanupBeforeQuit() {
|
|
||||||
DependencyManager::get<AudioClient>()->cleanupBeforeQuit();
|
|
||||||
// destroy the AudioClient so it and its thread will safely go down
|
|
||||||
DependencyManager::destroy<AudioClient>();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderingClient::processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket) {
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
|
||||||
PacketType incomingType = packetTypeForPacket(incomingPacket);
|
|
||||||
|
|
||||||
switch (incomingType) {
|
|
||||||
case PacketTypeAudioEnvironment:
|
|
||||||
case PacketTypeAudioStreamStats:
|
|
||||||
case PacketTypeMixedAudio:
|
|
||||||
case PacketTypeSilentAudioFrame: {
|
|
||||||
|
|
||||||
if (incomingType == PacketTypeAudioStreamStats) {
|
|
||||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "parseAudioStreamStatsPacket",
|
|
||||||
Qt::QueuedConnection,
|
|
||||||
Q_ARG(QByteArray, incomingPacket));
|
|
||||||
} else if (incomingType == PacketTypeAudioEnvironment) {
|
|
||||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "parseAudioEnvironmentData",
|
|
||||||
Qt::QueuedConnection,
|
|
||||||
Q_ARG(QByteArray, incomingPacket));
|
|
||||||
} else {
|
|
||||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "addReceivedAudioToStream",
|
|
||||||
Qt::QueuedConnection,
|
|
||||||
Q_ARG(QByteArray, incomingPacket));
|
|
||||||
}
|
|
||||||
|
|
||||||
// update having heard from the audio-mixer and record the bytes received
|
|
||||||
SharedNodePointer audioMixer = nodeList->sendingNodeForPacket(incomingPacket);
|
|
||||||
|
|
||||||
if (audioMixer) {
|
|
||||||
audioMixer->setLastHeardMicrostamp(usecTimestampNow());
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PacketTypeBulkAvatarData:
|
|
||||||
case PacketTypeKillAvatar:
|
|
||||||
case PacketTypeAvatarIdentity:
|
|
||||||
case PacketTypeAvatarBillboard: {
|
|
||||||
// update having heard from the avatar-mixer and record the bytes received
|
|
||||||
SharedNodePointer avatarMixer = nodeList->sendingNodeForPacket(incomingPacket);
|
|
||||||
|
|
||||||
if (avatarMixer) {
|
|
||||||
avatarMixer->setLastHeardMicrostamp(usecTimestampNow());
|
|
||||||
|
|
||||||
QMetaObject::invokeMethod(DependencyManager::get<AvatarHashMap>().data(),
|
|
||||||
"processAvatarMixerDatagram",
|
|
||||||
Q_ARG(const QByteArray&, incomingPacket),
|
|
||||||
Q_ARG(const QWeakPointer<Node>&, avatarMixer));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
Client::processVerifiedPacket(senderSockAddr, incomingPacket);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderingClient::goToLocation(const glm::vec3& newPosition,
|
|
||||||
bool hasOrientationChange, const glm::quat& newOrientation,
|
|
||||||
bool shouldFaceLocation) {
|
|
||||||
qDebug().nospace() << "RenderingClient goToLocation - moving to " << newPosition.x << ", "
|
|
||||||
<< newPosition.y << ", " << newPosition.z;
|
|
||||||
|
|
||||||
glm::vec3 shiftedPosition = newPosition;
|
|
||||||
|
|
||||||
if (hasOrientationChange) {
|
|
||||||
qDebug().nospace() << "RenderingClient goToLocation - new orientation is "
|
|
||||||
<< newOrientation.x << ", " << newOrientation.y << ", " << newOrientation.z << ", " << newOrientation.w;
|
|
||||||
|
|
||||||
// orient the user to face the target
|
|
||||||
glm::quat quatOrientation = newOrientation;
|
|
||||||
|
|
||||||
if (shouldFaceLocation) {
|
|
||||||
|
|
||||||
quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
|
||||||
|
|
||||||
// move the user a couple units away
|
|
||||||
const float DISTANCE_TO_USER = 2.0f;
|
|
||||||
shiftedPosition = newPosition - quatOrientation * glm::vec3( 0.0f, 0.0f,-1.0f) * DISTANCE_TO_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
_orientation = quatOrientation;
|
|
||||||
}
|
|
||||||
|
|
||||||
_position = shiftedPosition;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
//
|
|
||||||
// RenderingClient.h
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 1/20/15.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef hifi_RenderingClient_h
|
|
||||||
#define hifi_RenderingClient_h
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
|
||||||
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
#include <AvatarData.h>
|
|
||||||
|
|
||||||
#include "Client.h"
|
|
||||||
|
|
||||||
class RenderingClient : public Client {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
RenderingClient(QObject* parent = 0, const QString& launchURLString = QString());
|
|
||||||
|
|
||||||
const glm::vec3& getPosition() const { return _position; }
|
|
||||||
const glm::quat& getOrientation() const { return _orientation; }
|
|
||||||
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
|
|
||||||
|
|
||||||
static glm::vec3 getPositionForAudio() { return _instance->getPosition(); }
|
|
||||||
static glm::quat getOrientationForAudio() { return _instance->getOrientation(); }
|
|
||||||
|
|
||||||
virtual void cleanupBeforeQuit();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void goToLocation(const glm::vec3& newPosition,
|
|
||||||
bool hasOrientationChange, const glm::quat& newOrientation,
|
|
||||||
bool shouldFaceLocation);
|
|
||||||
void sendAvatarPacket();
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual void processVerifiedPacket(const HifiSockAddr& senderSockAddr, const QByteArray& incomingPacket);
|
|
||||||
|
|
||||||
static RenderingClient* _instance;
|
|
||||||
|
|
||||||
glm::vec3 _position;
|
|
||||||
glm::quat _orientation;
|
|
||||||
|
|
||||||
QTimer _avatarTimer;
|
|
||||||
AvatarData _fakeAvatar;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_RenderingClient_h
|
|
|
@ -1,41 +0,0 @@
|
||||||
//
|
|
||||||
// InterfaceActivity.java
|
|
||||||
// gvr-interface/java
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 1/26/15.
|
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
package io.highfidelity.gvrinterface;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.util.Log;
|
|
||||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
|
||||||
|
|
||||||
public class InterfaceActivity extends QtActivity {
|
|
||||||
|
|
||||||
public static native void handleHifiURL(String hifiURLString);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
||||||
|
|
||||||
// Get the intent that started this activity in case we have a hifi:// URL to parse
|
|
||||||
Intent intent = getIntent();
|
|
||||||
if (intent.getAction() == Intent.ACTION_VIEW) {
|
|
||||||
Uri data = intent.getData();
|
|
||||||
|
|
||||||
if (data.getScheme().equals("hifi")) {
|
|
||||||
handleHifiURL(data.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
//
|
|
||||||
// main.cpp
|
|
||||||
// gvr-interface/src
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 11/17/14.
|
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "GVRMainWindow.h"
|
|
||||||
#include "GVRInterface.h"
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
GVRInterface app(argc, argv);
|
|
||||||
|
|
||||||
GVRMainWindow mainWindow;
|
|
||||||
#ifdef ANDROID
|
|
||||||
mainWindow.showFullScreen();
|
|
||||||
#else
|
|
||||||
mainWindow.showMaximized();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
app.setMainWindow(&mainWindow);
|
|
||||||
|
|
||||||
return app.exec();
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
//
|
|
||||||
// InterfaceBetaActivity.java
|
|
||||||
// gvr-interface/java
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 1/27/15.
|
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
package io.highfidelity.gvrinterface;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import net.hockeyapp.android.CrashManager;
|
|
||||||
import net.hockeyapp.android.UpdateManager;
|
|
||||||
|
|
||||||
public class InterfaceBetaActivity extends InterfaceActivity {
|
|
||||||
|
|
||||||
public String _hockeyAppID;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
_hockeyAppID = getString(R.string.HockeyAppID);
|
|
||||||
|
|
||||||
checkForUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
UpdateManager.unregister();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
checkForCrashes();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkForCrashes() {
|
|
||||||
CrashManager.register(this, _hockeyAppID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkForUpdates() {
|
|
||||||
// Remove this for store / production builds!
|
|
||||||
UpdateManager.register(this, _hockeyAppID);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<resources>
|
|
||||||
<string name="HockeyAppID">${HOCKEY_APP_ID}</string>
|
|
||||||
<bool name="HockeyAppEnabled">${HOCKEY_APP_ENABLED}</bool>
|
|
||||||
</resources>
|
|
|
@ -52,6 +52,13 @@ ANDROID_PACKAGES = {
|
||||||
'sharedLibFolder': 'VrApi/Libs/Android/arm64-v8a/Release',
|
'sharedLibFolder': 'VrApi/Libs/Android/arm64-v8a/Release',
|
||||||
'includeLibs': ['libvrapi.so']
|
'includeLibs': ['libvrapi.so']
|
||||||
},
|
},
|
||||||
|
'oculusPlatform': {
|
||||||
|
'file': 'OVRPlatformSDK_v1.32.0.zip',
|
||||||
|
'versionId': 'jG9DB16zOGxSrmtZy4jcQnwO0TJUuaeL',
|
||||||
|
'checksum': 'ab5b203b3a39a56ab148d68fff769e05',
|
||||||
|
'sharedLibFolder': 'Android/libs/arm64-v8a',
|
||||||
|
'includeLibs': ['libovrplatformloader.so']
|
||||||
|
},
|
||||||
'openssl': {
|
'openssl': {
|
||||||
'file': 'openssl-1.1.0g_armv8.tgz',
|
'file': 'openssl-1.1.0g_armv8.tgz',
|
||||||
'versionId': 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW',
|
'versionId': 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW',
|
||||||
|
|
|
@ -265,7 +265,7 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
# include headers for interface and InterfaceConfig.
|
# include headers for interface and InterfaceConfig.
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/src")
|
target_include_directories(${TARGET_NAME} PRIVATE "${PROJECT_SOURCE_DIR}/src")
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
find_library(ANDROID_LOG_LIB log)
|
find_library(ANDROID_LOG_LIB log)
|
||||||
|
|
0
interface/resources/icons/+android/backward.svg → interface/resources/icons/+android_interface/backward.svg
Executable file → Normal file
Before Width: | Height: | Size: 735 B After Width: | Height: | Size: 735 B |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
0
interface/resources/icons/+android/go-a.svg → interface/resources/icons/+android_interface/go-a.svg
Executable file → Normal file
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
0
interface/resources/icons/+android/mic-unmute-a.svg → interface/resources/icons/+android_interface/mic-unmute-a.svg
Executable file → Normal file
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
0
interface/resources/icons/+android/myview-a.svg → interface/resources/icons/+android_interface/myview-a.svg
Executable file → Normal file
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
0
interface/resources/icons/+android/myview-hover.svg → interface/resources/icons/+android_interface/myview-hover.svg
Executable file → Normal file
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
0
interface/resources/icons/+android/myview-i.svg → interface/resources/icons/+android_interface/myview-i.svg
Executable file → Normal file
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 655 B After Width: | Height: | Size: 655 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
@ -662,7 +662,7 @@ Rectangle {
|
||||||
anchors.right: parent.right;
|
anchors.right: parent.right;
|
||||||
text: "Cancel"
|
text: "Cancel"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
sendToScript({method: 'checkout_cancelClicked', params: itemId});
|
sendToScript({method: 'checkout_cancelClicked', itemId: itemId});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,8 @@ Item {
|
||||||
HifiConstants { id: hifi; }
|
HifiConstants { id: hifi; }
|
||||||
|
|
||||||
id: root;
|
id: root;
|
||||||
property string referrerURL: (Account.metaverseServerURL + "/marketplace?");
|
|
||||||
readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin;
|
|
||||||
property alias usernameDropdownVisible: usernameDropdown.visible;
|
|
||||||
|
|
||||||
height: mainContainer.height + additionalDropdownHeight;
|
height: mainContainer.height;
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Commerce;
|
target: Commerce;
|
||||||
|
@ -93,77 +90,7 @@ Item {
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent;
|
anchors.fill: parent;
|
||||||
onClicked: {
|
onClicked: {
|
||||||
sendToParent({method: "header_marketplaceImageClicked", referrerURL: root.referrerURL});
|
sendToParent({method: "header_marketplaceImageClicked"});
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: buttonAndUsernameContainer;
|
|
||||||
anchors.left: marketplaceHeaderImage.right;
|
|
||||||
anchors.leftMargin: 8;
|
|
||||||
anchors.top: parent.top;
|
|
||||||
anchors.bottom: parent.bottom;
|
|
||||||
anchors.bottomMargin: 10;
|
|
||||||
anchors.right: securityImage.left;
|
|
||||||
anchors.rightMargin: 6;
|
|
||||||
|
|
||||||
TextMetrics {
|
|
||||||
id: textMetrics;
|
|
||||||
font.family: "Raleway"
|
|
||||||
text: usernameText.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: myUsernameButton;
|
|
||||||
anchors.right: parent.right;
|
|
||||||
anchors.verticalCenter: parent.verticalCenter;
|
|
||||||
height: 40;
|
|
||||||
width: usernameText.width + 25;
|
|
||||||
color: "white";
|
|
||||||
radius: 4;
|
|
||||||
border.width: 1;
|
|
||||||
border.color: hifi.colors.lightGray;
|
|
||||||
|
|
||||||
// Username Text
|
|
||||||
RalewayRegular {
|
|
||||||
id: usernameText;
|
|
||||||
text: Account.username;
|
|
||||||
// Text size
|
|
||||||
size: 18;
|
|
||||||
// Style
|
|
||||||
color: hifi.colors.baseGray;
|
|
||||||
elide: Text.ElideRight;
|
|
||||||
horizontalAlignment: Text.AlignHCenter;
|
|
||||||
verticalAlignment: Text.AlignVCenter;
|
|
||||||
width: Math.min(textMetrics.width + 25, 110);
|
|
||||||
// Anchors
|
|
||||||
anchors.centerIn: parent;
|
|
||||||
rightPadding: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
HiFiGlyphs {
|
|
||||||
id: dropdownIcon;
|
|
||||||
text: hifi.glyphs.caratDn;
|
|
||||||
// Size
|
|
||||||
size: 50;
|
|
||||||
// Anchors
|
|
||||||
anchors.right: parent.right;
|
|
||||||
anchors.rightMargin: -14;
|
|
||||||
anchors.verticalCenter: parent.verticalCenter;
|
|
||||||
horizontalAlignment: Text.AlignHCenter;
|
|
||||||
// Style
|
|
||||||
color: hifi.colors.baseGray;
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent;
|
|
||||||
hoverEnabled: enabled;
|
|
||||||
onClicked: {
|
|
||||||
usernameDropdown.visible = !usernameDropdown.visible;
|
|
||||||
}
|
|
||||||
onEntered: usernameText.color = hifi.colors.baseGrayShadow;
|
|
||||||
onExited: usernameText.color = hifi.colors.baseGray;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,92 +132,6 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
|
||||||
id: usernameDropdown;
|
|
||||||
z: 998;
|
|
||||||
visible: false;
|
|
||||||
anchors.top: buttonAndUsernameContainer.bottom;
|
|
||||||
anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin;
|
|
||||||
anchors.right: buttonAndUsernameContainer.right;
|
|
||||||
height: childrenRect.height;
|
|
||||||
width: 150;
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: myItemsButton;
|
|
||||||
color: hifi.colors.white;
|
|
||||||
anchors.top: parent.top;
|
|
||||||
anchors.left: parent.left;
|
|
||||||
anchors.right: parent.right;
|
|
||||||
height: 50;
|
|
||||||
|
|
||||||
RalewaySemiBold {
|
|
||||||
anchors.fill: parent;
|
|
||||||
text: "My Submissions"
|
|
||||||
color: hifi.colors.baseGray;
|
|
||||||
horizontalAlignment: Text.AlignHCenter;
|
|
||||||
verticalAlignment: Text.AlignVCenter;
|
|
||||||
size: 18;
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent;
|
|
||||||
hoverEnabled: true;
|
|
||||||
onEntered: {
|
|
||||||
myItemsButton.color = hifi.colors.blueHighlight;
|
|
||||||
}
|
|
||||||
onExited: {
|
|
||||||
myItemsButton.color = hifi.colors.white;
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
sendToParent({method: "header_myItemsClicked"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: logOutButton;
|
|
||||||
color: hifi.colors.white;
|
|
||||||
anchors.top: myItemsButton.bottom;
|
|
||||||
anchors.left: parent.left;
|
|
||||||
anchors.right: parent.right;
|
|
||||||
height: 50;
|
|
||||||
|
|
||||||
RalewaySemiBold {
|
|
||||||
anchors.fill: parent;
|
|
||||||
text: "Log Out"
|
|
||||||
color: hifi.colors.baseGray;
|
|
||||||
horizontalAlignment: Text.AlignHCenter;
|
|
||||||
verticalAlignment: Text.AlignVCenter;
|
|
||||||
size: 18;
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent;
|
|
||||||
hoverEnabled: true;
|
|
||||||
onEntered: {
|
|
||||||
logOutButton.color = hifi.colors.blueHighlight;
|
|
||||||
}
|
|
||||||
onExited: {
|
|
||||||
logOutButton.color = hifi.colors.white;
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
Account.logOut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DropShadow {
|
|
||||||
z: 997;
|
|
||||||
visible: usernameDropdown.visible;
|
|
||||||
anchors.fill: usernameDropdown;
|
|
||||||
horizontalOffset: 3;
|
|
||||||
verticalOffset: 3;
|
|
||||||
radius: 8.0;
|
|
||||||
samples: 17;
|
|
||||||
color: "#80000000";
|
|
||||||
source: usernameDropdown;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ Rectangle {
|
||||||
HifiConstants { id: hifi; }
|
HifiConstants { id: hifi; }
|
||||||
|
|
||||||
id: root;
|
id: root;
|
||||||
property string marketplaceUrl: "";
|
|
||||||
property string entityId: "";
|
property string entityId: "";
|
||||||
property string certificateId: "";
|
property string certificateId: "";
|
||||||
property string itemName: "--";
|
property string itemName: "--";
|
||||||
|
@ -30,6 +29,7 @@ Rectangle {
|
||||||
property string itemEdition: "--";
|
property string itemEdition: "--";
|
||||||
property string dateAcquired: "--";
|
property string dateAcquired: "--";
|
||||||
property string itemCost: "--";
|
property string itemCost: "--";
|
||||||
|
property string marketplace_item_id: "";
|
||||||
property string certTitleTextColor: hifi.colors.darkGray;
|
property string certTitleTextColor: hifi.colors.darkGray;
|
||||||
property string certTextColor: hifi.colors.white;
|
property string certTextColor: hifi.colors.white;
|
||||||
property string infoTextColor: hifi.colors.blueAccent;
|
property string infoTextColor: hifi.colors.blueAccent;
|
||||||
|
@ -69,7 +69,7 @@ Rectangle {
|
||||||
errorText.text = "Information about this certificate is currently unavailable. Please try again later.";
|
errorText.text = "Information about this certificate is currently unavailable. Please try again later.";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
root.marketplaceUrl = result.data.marketplace_item_url;
|
root.marketplace_item_id = result.data.marketplace_item_id;
|
||||||
root.isMyCert = result.isMyCert ? result.isMyCert : false;
|
root.isMyCert = result.isMyCert ? result.isMyCert : false;
|
||||||
|
|
||||||
if (root.certInfoReplaceMode > 3) {
|
if (root.certInfoReplaceMode > 3) {
|
||||||
|
@ -352,7 +352,7 @@ Rectangle {
|
||||||
anchors.fill: parent;
|
anchors.fill: parent;
|
||||||
hoverEnabled: enabled;
|
hoverEnabled: enabled;
|
||||||
onClicked: {
|
onClicked: {
|
||||||
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
|
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplace_item_id});
|
||||||
}
|
}
|
||||||
onEntered: itemName.color = hifi.colors.blueHighlight;
|
onEntered: itemName.color = hifi.colors.blueHighlight;
|
||||||
onExited: itemName.color = root.certTextColor;
|
onExited: itemName.color = root.certTextColor;
|
||||||
|
@ -391,7 +391,7 @@ Rectangle {
|
||||||
// "Show In Marketplace" button
|
// "Show In Marketplace" button
|
||||||
HifiControlsUit.Button {
|
HifiControlsUit.Button {
|
||||||
id: showInMarketplaceButton;
|
id: showInMarketplaceButton;
|
||||||
enabled: root.marketplaceUrl;
|
enabled: root.marketplace_item_id && marketplace_item_id !== "";
|
||||||
color: hifi.buttons.blue;
|
color: hifi.buttons.blue;
|
||||||
colorScheme: hifi.colorSchemes.light;
|
colorScheme: hifi.colorSchemes.light;
|
||||||
anchors.bottom: parent.bottom;
|
anchors.bottom: parent.bottom;
|
||||||
|
@ -401,7 +401,7 @@ Rectangle {
|
||||||
height: 40;
|
height: 40;
|
||||||
text: "View In Market"
|
text: "View In Market"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
|
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplace_item_id});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -620,7 +620,7 @@ Rectangle {
|
||||||
root.itemOwner = "--";
|
root.itemOwner = "--";
|
||||||
root.itemEdition = "--";
|
root.itemEdition = "--";
|
||||||
root.dateAcquired = "--";
|
root.dateAcquired = "--";
|
||||||
root.marketplaceUrl = "";
|
root.marketplace_item_id = "";
|
||||||
root.itemCost = "--";
|
root.itemCost = "--";
|
||||||
root.isMyCert = false;
|
root.isMyCert = false;
|
||||||
errorText.text = "";
|
errorText.text = "";
|
||||||
|
|
1216
interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml
Normal file
|
@ -0,0 +1,648 @@
|
||||||
|
//
|
||||||
|
// MarketplaceListItem.qml
|
||||||
|
// qml/hifi/commerce/marketplace
|
||||||
|
//
|
||||||
|
// MarketplaceListItem
|
||||||
|
//
|
||||||
|
// Created by Roxanne Skelly on 2019-01-22
|
||||||
|
// Copyright 2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import Hifi 1.0 as Hifi
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtWebEngine 1.5
|
||||||
|
import stylesUit 1.0
|
||||||
|
import controlsUit 1.0 as HifiControlsUit
|
||||||
|
import "../../../controls" as HifiControls
|
||||||
|
import "../common" as HifiCommerceCommon
|
||||||
|
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
|
||||||
|
import "../common/sendAsset"
|
||||||
|
import "../.." as HifiCommon
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root;
|
||||||
|
|
||||||
|
property string item_id: ""
|
||||||
|
property string image_url: ""
|
||||||
|
property string name: ""
|
||||||
|
property int likes: 0
|
||||||
|
property bool liked: false
|
||||||
|
property string creator: ""
|
||||||
|
property var categories: []
|
||||||
|
property int price: 0
|
||||||
|
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;
|
||||||
|
property bool supports3DHTML: false;
|
||||||
|
|
||||||
|
|
||||||
|
onCategoriesChanged: {
|
||||||
|
categoriesListModel.clear();
|
||||||
|
categories.forEach(function(category) {
|
||||||
|
categoriesListModel.append({"category":category});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDescriptionChanged: {
|
||||||
|
|
||||||
|
if(root.supports3DHTML) {
|
||||||
|
descriptionTextModel.clear();
|
||||||
|
descriptionTextModel.append({text: description});
|
||||||
|
} else {
|
||||||
|
descriptionText.text = description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAttributionsChanged: {
|
||||||
|
attributionsModel.clear();
|
||||||
|
if(root.attributions) {
|
||||||
|
root.attributions.forEach(function(attribution) {
|
||||||
|
attributionsModel.append(attribution);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
footer.evalHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
signal buy()
|
||||||
|
signal categoryClicked(string category)
|
||||||
|
signal showLicense(string url)
|
||||||
|
|
||||||
|
HifiConstants {
|
||||||
|
id: hifi
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: MarketplaceScriptingInterface
|
||||||
|
|
||||||
|
onMarketplaceItemLikeResult: {
|
||||||
|
if (result.status !== 'success') {
|
||||||
|
console.log("Like/Unlike item", result.data.message);
|
||||||
|
} else {
|
||||||
|
root.liked = !root.liked;
|
||||||
|
root.likes = root.liked ? root.likes + 1 : root.likes - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getFormattedDate(timestamp) {
|
||||||
|
function addLeadingZero(n) {
|
||||||
|
return n < 10 ? '0' + n : '' + n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = new Date(timestamp);
|
||||||
|
|
||||||
|
var year = a.getFullYear();
|
||||||
|
var month = addLeadingZero(a.getMonth() + 1);
|
||||||
|
var day = addLeadingZero(a.getDate());
|
||||||
|
var hour = a.getHours();
|
||||||
|
var drawnHour = hour;
|
||||||
|
if (hour === 0) {
|
||||||
|
drawnHour = 12;
|
||||||
|
} else if (hour > 12) {
|
||||||
|
drawnHour -= 12;
|
||||||
|
}
|
||||||
|
drawnHour = addLeadingZero(drawnHour);
|
||||||
|
|
||||||
|
var amOrPm = "AM";
|
||||||
|
if (hour >= 12) {
|
||||||
|
amOrPm = "PM";
|
||||||
|
}
|
||||||
|
|
||||||
|
var min = addLeadingZero(a.getMinutes());
|
||||||
|
var sec = addLeadingZero(a.getSeconds());
|
||||||
|
return a.toDateString() + " " + drawnHour + ':' + min + amOrPm;
|
||||||
|
}
|
||||||
|
function evalHeight() {
|
||||||
|
height = footer.y - header.y + footer.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal resized()
|
||||||
|
|
||||||
|
onHeightChanged: {
|
||||||
|
resized();
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: 15
|
||||||
|
rightMargin: 15
|
||||||
|
}
|
||||||
|
height: footer.y - header.y + footer.height
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: header
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
}
|
||||||
|
height: 50
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: nameText
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: root.name
|
||||||
|
size: 24
|
||||||
|
color: hifi.colors.baseGray
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: likes
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
rightMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: heart
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 0
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
text: "\u2665"
|
||||||
|
size: 20
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: root.liked ? hifi.colors.redAccent : hifi.colors.lightGrayText
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: likesText
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
right: heart.left
|
||||||
|
rightMargin: 5
|
||||||
|
leftMargin: 15
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: root.likes
|
||||||
|
size: hifi.fontSizes.overlayTitle
|
||||||
|
color: hifi.colors.baseGray
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors {
|
||||||
|
left: likesText.left
|
||||||
|
right: heart.right
|
||||||
|
top: likesText.top
|
||||||
|
bottom: likesText.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (isLoggedIn) {
|
||||||
|
MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, !root.liked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: itemImage
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: header.bottom
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
height: width*0.5625
|
||||||
|
|
||||||
|
source: root.image_url
|
||||||
|
fillMode: Image.PreserveAspectCrop;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: footer
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left;
|
||||||
|
right: parent.right;
|
||||||
|
top: itemImage.bottom;
|
||||||
|
}
|
||||||
|
height: categoriesList.y - buyButton.y + categoriesList.height
|
||||||
|
|
||||||
|
function evalHeight() {
|
||||||
|
height = categoriesList.y - buyButton.y + categoriesList.height;
|
||||||
|
console.log("HEIGHT: " + height);
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControlsUit.Button {
|
||||||
|
id: buyButton
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
topMargin: 15
|
||||||
|
}
|
||||||
|
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 : "") : ""
|
||||||
|
color: hifi.buttons.blue
|
||||||
|
|
||||||
|
onClicked: root.buy();
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: creatorItem
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: buyButton.bottom
|
||||||
|
leftMargin: 15
|
||||||
|
topMargin: 15
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
height: childrenRect.height
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: creatorLabel
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: "CREATOR:"
|
||||||
|
size: 14
|
||||||
|
color: hifi.colors.lightGrayText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: creatorText
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: creatorLabel.bottom
|
||||||
|
left: parent.left
|
||||||
|
topMargin: 10
|
||||||
|
}
|
||||||
|
width: paintedWidth
|
||||||
|
text: root.creator
|
||||||
|
|
||||||
|
size: 18
|
||||||
|
color: hifi.colors.blueHighlight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: posted
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: creatorItem.bottom
|
||||||
|
leftMargin: 15
|
||||||
|
topMargin: 15
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
height: childrenRect.height
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: postedLabel
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: "POSTED:"
|
||||||
|
size: 14
|
||||||
|
color: hifi.colors.lightGrayText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: created_at
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: postedLabel.bottom
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
topMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
text: { getFormattedDate(root.created_at); }
|
||||||
|
size: 14
|
||||||
|
color: hifi.colors.lightGray
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: posted.bottom
|
||||||
|
leftMargin: 15
|
||||||
|
topMargin: 15
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
|
||||||
|
color: hifi.colors.lightGrayText
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: attributions
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: posted.bottom
|
||||||
|
topMargin: 30
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
height: attributionsModel.count > 0 ? childrenRect.height : 0
|
||||||
|
visible: attributionsModel.count > 0
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: attributionsLabel
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: paintedWidth
|
||||||
|
height: paintedHeight
|
||||||
|
|
||||||
|
text: "ATTRIBUTIONS:"
|
||||||
|
size: 14
|
||||||
|
color: hifi.colors.lightGrayText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
ListModel {
|
||||||
|
id: attributionsModel
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: attributionsLabel.bottom
|
||||||
|
bottomMargin: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 24*model.count+10
|
||||||
|
|
||||||
|
model: attributionsModel
|
||||||
|
delegate: Item {
|
||||||
|
height: 24
|
||||||
|
width: parent.width
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: attributionName
|
||||||
|
|
||||||
|
anchors.leftMargin: 15
|
||||||
|
height: 24
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
text: model.name
|
||||||
|
elide: Text.ElideRight
|
||||||
|
size: 14
|
||||||
|
|
||||||
|
color: model.link ? hifi.colors.blueHighlight : hifi.colors.baseGray
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (model.link) {
|
||||||
|
sendToScript({method: 'marketplace_open_link', link: model.link});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
bottom: attributions.bottom
|
||||||
|
leftMargin: 15
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
|
||||||
|
color: hifi.colors.lightGrayText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
id: licenseItem;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: attributions.bottom
|
||||||
|
left: parent.left
|
||||||
|
topMargin: 15
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
height: childrenRect.height
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: licenseLabel
|
||||||
|
|
||||||
|
anchors.top: parent.top;
|
||||||
|
anchors.left: parent.left;
|
||||||
|
width: paintedWidth;
|
||||||
|
|
||||||
|
text: "SHARED UNDER:";
|
||||||
|
size: 14;
|
||||||
|
color: hifi.colors.lightGrayText;
|
||||||
|
verticalAlignment: Text.AlignVCenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: licenseText
|
||||||
|
|
||||||
|
anchors.top: licenseLabel.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.topMargin: 5
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: root.license
|
||||||
|
size: 14
|
||||||
|
color: hifi.colors.lightGray
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: licenseHelp
|
||||||
|
|
||||||
|
anchors.top: licenseText.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.topMargin: 5
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: "More about this license"
|
||||||
|
size: 14
|
||||||
|
color: hifi.colors.blueHighlight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
licenseInfo.visible = true;
|
||||||
|
var url;
|
||||||
|
if (root.license === "No Rights Reserved (CC0)") {
|
||||||
|
url = "https://creativecommons.org/publicdomain/zero/1.0/"
|
||||||
|
} else if (root.license === "Attribution (CC BY)") {
|
||||||
|
url = "https://creativecommons.org/licenses/by/4.0/legalcode.txt"
|
||||||
|
} else if (root.license === "Attribution-ShareAlike (CC BY-SA)") {
|
||||||
|
url = "https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt"
|
||||||
|
} else if (root.license === "Attribution-NoDerivs (CC BY-ND)") {
|
||||||
|
url = "https://creativecommons.org/licenses/by-nd/4.0/legalcode.txt"
|
||||||
|
} else if (root.license === "Attribution-NonCommercial (CC BY-NC)") {
|
||||||
|
url = "https://creativecommons.org/licenses/by-nc/4.0/legalcode.txt"
|
||||||
|
} else if (root.license === "Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)") {
|
||||||
|
url = "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt"
|
||||||
|
} else if (root.license === "Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)") {
|
||||||
|
url = "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode.txt"
|
||||||
|
} else if (root.license === "Proof of Provenance License (PoP License)") {
|
||||||
|
url = "https://digitalassetregistry.com/PoP-License/v1/"
|
||||||
|
}
|
||||||
|
if(url) {
|
||||||
|
showLicense(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: descriptionItem
|
||||||
|
property string text: ""
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: licenseItem.bottom
|
||||||
|
topMargin: 15
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
height: childrenRect.height
|
||||||
|
onHeightChanged: {
|
||||||
|
footer.evalHeight();
|
||||||
|
}
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: descriptionLabel
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: paintedWidth
|
||||||
|
height: 20
|
||||||
|
|
||||||
|
text: "DESCRIPTION:"
|
||||||
|
size: 14
|
||||||
|
color: hifi.colors.lightGrayText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: descriptionText
|
||||||
|
|
||||||
|
anchors.top: descriptionLabel.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.topMargin: 5
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
text: root.description
|
||||||
|
size: 14
|
||||||
|
color: hifi.colors.lightGray
|
||||||
|
linkColor: hifi.colors.blueHighlight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
textFormat: Text.RichText
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
onLinkActivated: {
|
||||||
|
sendToScript({method: 'marketplace_open_link', link: link});
|
||||||
|
}
|
||||||
|
|
||||||
|
onHeightChanged: { footer.evalHeight(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: categoriesList
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: descriptionItem.bottom
|
||||||
|
topMargin: 15
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
height: categoriesListModel.count*24 + categoryLabel.height + (isLoggedIn ? 50 : 150)
|
||||||
|
|
||||||
|
onHeightChanged: { footer.evalHeight(); }
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: categoryLabel
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: "IN:"
|
||||||
|
size: 14
|
||||||
|
color: hifi.colors.lightGrayText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
ListModel {
|
||||||
|
id: categoriesListModel
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: categoryLabel.bottom
|
||||||
|
bottomMargin: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 24*model.count+10
|
||||||
|
|
||||||
|
model: categoriesListModel
|
||||||
|
delegate: RalewaySemiBold {
|
||||||
|
id: categoryText
|
||||||
|
|
||||||
|
anchors.leftMargin: 15
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: model.category
|
||||||
|
size: 14
|
||||||
|
height: 24
|
||||||
|
color: hifi.colors.blueHighlight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: categoryText
|
||||||
|
|
||||||
|
onClicked: root.categoryClicked(model.category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,321 @@
|
||||||
|
//
|
||||||
|
// MarketplaceListItem.qml
|
||||||
|
// qml/hifi/commerce/marketplace
|
||||||
|
//
|
||||||
|
// MarketplaceListItem
|
||||||
|
//
|
||||||
|
// Created by Roxanne Skelly on 2019-01-22
|
||||||
|
// Copyright 2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import Hifi 1.0 as Hifi
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import stylesUit 1.0
|
||||||
|
import controlsUit 1.0 as HifiControlsUit
|
||||||
|
import "../../../controls" as HifiControls
|
||||||
|
import "../common" as HifiCommerceCommon
|
||||||
|
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
|
||||||
|
import "../common/sendAsset"
|
||||||
|
import "../.." as HifiCommon
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string item_id: ""
|
||||||
|
property string image_url: ""
|
||||||
|
property string name: ""
|
||||||
|
property int likes: 0
|
||||||
|
property bool liked: false
|
||||||
|
property string creator: ""
|
||||||
|
property string category: ""
|
||||||
|
property int price: 0
|
||||||
|
property bool available: false
|
||||||
|
property bool isLoggedIn: false;
|
||||||
|
|
||||||
|
signal buy()
|
||||||
|
signal showItem()
|
||||||
|
signal categoryClicked(string category)
|
||||||
|
signal requestReload()
|
||||||
|
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: childrenRect.height+50
|
||||||
|
|
||||||
|
DropShadow {
|
||||||
|
anchors.fill: shadowBase
|
||||||
|
|
||||||
|
source: shadowBase
|
||||||
|
verticalOffset: 4
|
||||||
|
horizontalOffset: 4
|
||||||
|
radius: 6
|
||||||
|
samples: 9
|
||||||
|
color: hifi.colors.baseGrayShadow
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: shadowBase
|
||||||
|
|
||||||
|
anchors.fill: itemRect
|
||||||
|
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: itemRect
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 20
|
||||||
|
bottomMargin: 10
|
||||||
|
leftMargin: 15
|
||||||
|
rightMargin: 15
|
||||||
|
}
|
||||||
|
height: childrenRect.height
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onClicked: root.showItem();
|
||||||
|
onEntered: hoverRect.visible = true;
|
||||||
|
onExited: hoverRect.visible = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: header
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
}
|
||||||
|
height: 50
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: nameText
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 50
|
||||||
|
leftMargin: 15
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: root.name
|
||||||
|
size: hifi.fontSizes.overlayTitle
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: hifi.colors.blueHighlight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: likes
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 15
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
width: heart.width + likesText.width
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: MarketplaceScriptingInterface
|
||||||
|
|
||||||
|
onMarketplaceItemLikeResult: {
|
||||||
|
if (result.status !== 'success') {
|
||||||
|
console.log("Failed to get Marketplace Categories", result.data.message);
|
||||||
|
root.requestReload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: heart
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 0
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
text: "\u2665"
|
||||||
|
size: 20
|
||||||
|
horizontalAlignment: Text.AlignHCenter;
|
||||||
|
color: root.liked ? hifi.colors.redAccent : hifi.colors.lightGrayText
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: likesText
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
right: heart.left
|
||||||
|
rightMargin: 5
|
||||||
|
leftMargin: 15
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: root.likes
|
||||||
|
size: hifi.fontSizes.overlayTitle
|
||||||
|
color: hifi.colors.baseGray
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors {
|
||||||
|
left: likesText.left
|
||||||
|
right: heart.right
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
if (isLoggedIn) {
|
||||||
|
root.liked = !root.liked;
|
||||||
|
root.likes = root.liked ? root.likes + 1 : root.likes - 1;
|
||||||
|
MarketplaceScriptingInterface.marketplaceItemLike(root.item_id, root.liked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: itemImage
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: header.bottom
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
height: width * 0.5625
|
||||||
|
|
||||||
|
source: root.image_url
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: footer
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: itemImage.bottom
|
||||||
|
}
|
||||||
|
height: 60
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: creatorLabel
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 15
|
||||||
|
}
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: "CREATOR:"
|
||||||
|
size: 14
|
||||||
|
color: hifi.colors.lightGrayText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: creatorText
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: creatorLabel.top;
|
||||||
|
left: creatorLabel.right;
|
||||||
|
leftMargin: 15;
|
||||||
|
}
|
||||||
|
width: paintedWidth;
|
||||||
|
|
||||||
|
text: root.creator;
|
||||||
|
size: 14;
|
||||||
|
color: hifi.colors.lightGray;
|
||||||
|
verticalAlignment: Text.AlignVCenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: categoryLabel
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: creatorLabel.bottom
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 15
|
||||||
|
}
|
||||||
|
width: paintedWidth;
|
||||||
|
|
||||||
|
text: "IN:";
|
||||||
|
size: 14;
|
||||||
|
color: hifi.colors.lightGrayText;
|
||||||
|
verticalAlignment: Text.AlignVCenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: categoryText
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: categoryLabel.top
|
||||||
|
left: categoryLabel.right
|
||||||
|
leftMargin: 15
|
||||||
|
}
|
||||||
|
width: paintedWidth
|
||||||
|
|
||||||
|
text: root.category
|
||||||
|
size: 14
|
||||||
|
color: hifi.colors.blueHighlight;
|
||||||
|
verticalAlignment: Text.AlignVCenter;
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: root.categoryClicked(root.category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControlsUit.Button {
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
rightMargin: 15
|
||||||
|
topMargin:10
|
||||||
|
bottomMargin: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
text: root.price ? root.price : "FREE"
|
||||||
|
buttonGlyph: root.price ? hifi.glyphs.hfc : ""
|
||||||
|
color: hifi.buttons.blue;
|
||||||
|
|
||||||
|
onClicked: root.buy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: hoverRect
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
border.color: hifi.colors.blueHighlight
|
||||||
|
border.width: 2
|
||||||
|
color: "#00000000"
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
//
|
||||||
|
// SortButton.qml
|
||||||
|
// qml/hifi/commerce/marketplace
|
||||||
|
//
|
||||||
|
// SortButton
|
||||||
|
//
|
||||||
|
// Created by Roxanne Skelly on 2019-01-18
|
||||||
|
// Copyright 2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import Hifi 1.0 as Hifi
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import stylesUit 1.0
|
||||||
|
import controlsUit 1.0 as HifiControlsUit
|
||||||
|
import "../../../controls" as HifiControls
|
||||||
|
import "../common" as HifiCommerceCommon
|
||||||
|
import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere.
|
||||||
|
import "../common/sendAsset"
|
||||||
|
import "../.." as HifiCommon
|
||||||
|
|
||||||
|
Item {
|
||||||
|
HifiConstants { id: hifi; }
|
||||||
|
|
||||||
|
id: root;
|
||||||
|
|
||||||
|
|
||||||
|
property string glyph: "";
|
||||||
|
property string text: "";
|
||||||
|
property bool checked: false;
|
||||||
|
signal clicked();
|
||||||
|
|
||||||
|
width: childrenRect.width;
|
||||||
|
height: parent.height;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.top: parent.top;
|
||||||
|
anchors.left: parent.left;
|
||||||
|
height: parent.height;
|
||||||
|
width: 2;
|
||||||
|
color: hifi.colors.faintGray;
|
||||||
|
visible: index > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
id: buttonGlyph;
|
||||||
|
text: root.glyph;
|
||||||
|
// Size
|
||||||
|
size: 14;
|
||||||
|
// Anchors
|
||||||
|
anchors.left: parent.left;
|
||||||
|
anchors.leftMargin: 0;
|
||||||
|
anchors.top: parent.top;
|
||||||
|
anchors.verticalCenter: parent.verticalCenter;
|
||||||
|
height: parent.height;
|
||||||
|
horizontalAlignment: Text.AlignHCenter;
|
||||||
|
// Style
|
||||||
|
color: hifi.colors.lightGray;
|
||||||
|
}
|
||||||
|
RalewayRegular {
|
||||||
|
id: buttonText;
|
||||||
|
text: root.text;
|
||||||
|
// Text size
|
||||||
|
size: 14;
|
||||||
|
// Style
|
||||||
|
color: hifi.colors.lightGray;
|
||||||
|
elide: Text.ElideRight;
|
||||||
|
horizontalAlignment: Text.AlignHCenter;
|
||||||
|
verticalAlignment: Text.AlignVCenter;
|
||||||
|
// Anchors
|
||||||
|
anchors.left: parent.left;
|
||||||
|
anchors.leftMargin: 20;
|
||||||
|
anchors.top: parent.top;
|
||||||
|
height: parent.height;
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent;
|
||||||
|
hoverEnabled: enabled;
|
||||||
|
onClicked: {
|
||||||
|
root.clicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ Item {
|
||||||
property string purchaseStatus;
|
property string purchaseStatus;
|
||||||
property string itemName;
|
property string itemName;
|
||||||
property string itemId;
|
property string itemId;
|
||||||
|
property string updateItemId;
|
||||||
property string itemPreviewImageUrl;
|
property string itemPreviewImageUrl;
|
||||||
property string itemHref;
|
property string itemHref;
|
||||||
property string certificateId;
|
property string certificateId;
|
||||||
|
@ -45,9 +46,9 @@ Item {
|
||||||
property bool cardBackVisible;
|
property bool cardBackVisible;
|
||||||
property bool isInstalled;
|
property bool isInstalled;
|
||||||
property string wornEntityID;
|
property string wornEntityID;
|
||||||
property string upgradeUrl;
|
property string updatedItemId;
|
||||||
property string upgradeTitle;
|
property string upgradeTitle;
|
||||||
property bool updateAvailable: root.upgradeUrl !== "";
|
property bool updateAvailable: root.updateItemId && root.updateItemId !== "";
|
||||||
property bool valid;
|
property bool valid;
|
||||||
|
|
||||||
property string originalStatusText;
|
property string originalStatusText;
|
||||||
|
@ -175,7 +176,7 @@ Item {
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property alias buttonGlyphText: buttonGlyph.text;
|
property alias buttonGlyphText: buttonGlyph.text;
|
||||||
property alias buttonText: buttonText.text;
|
property alias itemButtonText: buttonText.text;
|
||||||
property alias glyphSize: buttonGlyph.size;
|
property alias glyphSize: buttonGlyph.size;
|
||||||
property string buttonColor: hifi.colors.black;
|
property string buttonColor: hifi.colors.black;
|
||||||
property string buttonColor_hover: hifi.colors.blueHighlight;
|
property string buttonColor_hover: hifi.colors.blueHighlight;
|
||||||
|
@ -243,7 +244,7 @@ Item {
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
item.enabled = root.valid;
|
item.enabled = root.valid;
|
||||||
item.buttonGlyphText = hifi.glyphs.gift;
|
item.buttonGlyphText = hifi.glyphs.gift;
|
||||||
item.buttonText = "Gift";
|
item.itemButtonText = "Gift";
|
||||||
item.buttonClicked = function() {
|
item.buttonClicked = function() {
|
||||||
sendToPurchases({ method: 'flipCard', closeAll: true });
|
sendToPurchases({ method: 'flipCard', closeAll: true });
|
||||||
sendToPurchases({
|
sendToPurchases({
|
||||||
|
@ -270,7 +271,7 @@ Item {
|
||||||
|
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
item.buttonGlyphText = hifi.glyphs.market;
|
item.buttonGlyphText = hifi.glyphs.market;
|
||||||
item.buttonText = "View in Marketplace";
|
item.itemButtonText = "View in Marketplace";
|
||||||
item.buttonClicked = function() {
|
item.buttonClicked = function() {
|
||||||
sendToPurchases({ method: 'flipCard', closeAll: true });
|
sendToPurchases({ method: 'flipCard', closeAll: true });
|
||||||
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
|
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
|
||||||
|
@ -288,7 +289,7 @@ Item {
|
||||||
|
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
item.buttonGlyphText = hifi.glyphs.certificate;
|
item.buttonGlyphText = hifi.glyphs.certificate;
|
||||||
item.buttonText = "View Certificate";
|
item.itemButtonText = "View Certificate";
|
||||||
item.buttonClicked = function() {
|
item.buttonClicked = function() {
|
||||||
sendToPurchases({ method: 'flipCard', closeAll: true });
|
sendToPurchases({ method: 'flipCard', closeAll: true });
|
||||||
sendToPurchases({method: 'purchases_itemCertificateClicked', itemCertificateId: root.certificateId});
|
sendToPurchases({method: 'purchases_itemCertificateClicked', itemCertificateId: root.certificateId});
|
||||||
|
@ -307,7 +308,7 @@ Item {
|
||||||
|
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
item.buttonGlyphText = hifi.glyphs.uninstall;
|
item.buttonGlyphText = hifi.glyphs.uninstall;
|
||||||
item.buttonText = "Uninstall";
|
item.itemButtonText = "Uninstall";
|
||||||
item.buttonClicked = function() {
|
item.buttonClicked = function() {
|
||||||
sendToPurchases({ method: 'flipCard', closeAll: true });
|
sendToPurchases({ method: 'flipCard', closeAll: true });
|
||||||
Commerce.uninstallApp(root.itemHref);
|
Commerce.uninstallApp(root.itemHref);
|
||||||
|
@ -330,15 +331,14 @@ Item {
|
||||||
|
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
item.buttonGlyphText = hifi.glyphs.update;
|
item.buttonGlyphText = hifi.glyphs.update;
|
||||||
item.buttonText = "Update";
|
item.itemButtonText = "Update";
|
||||||
item.buttonColor = "#E2334D";
|
item.buttonColor = "#E2334D";
|
||||||
item.buttonClicked = function() {
|
item.buttonClicked = function() {
|
||||||
sendToPurchases({ method: 'flipCard', closeAll: true });
|
sendToPurchases({ method: 'flipCard', closeAll: true });
|
||||||
sendToPurchases({
|
sendToPurchases({
|
||||||
method: 'updateItemClicked',
|
method: 'updateItemClicked',
|
||||||
itemId: root.itemId,
|
itemId: root.updateAvailable ? root.updateItemId : root.itemId,
|
||||||
itemEdition: root.itemEdition,
|
itemEdition: root.itemEdition,
|
||||||
upgradeUrl: root.upgradeUrl,
|
|
||||||
itemHref: root.itemHref,
|
itemHref: root.itemHref,
|
||||||
itemType: root.itemType,
|
itemType: root.itemType,
|
||||||
isInstalled: root.isInstalled,
|
isInstalled: root.isInstalled,
|
||||||
|
@ -378,10 +378,10 @@ Item {
|
||||||
|
|
||||||
function updateProperties() {
|
function updateProperties() {
|
||||||
if (updateButton.visible && uninstallButton.visible) {
|
if (updateButton.visible && uninstallButton.visible) {
|
||||||
item.buttonText = "";
|
item.itemButtonText = "";
|
||||||
item.glyphSize = 20;
|
item.glyphSize = 20;
|
||||||
} else {
|
} else {
|
||||||
item.buttonText = "Send to Trash";
|
item.itemButtonText = "Send to Trash";
|
||||||
item.glyphSize = 30;
|
item.glyphSize = 30;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ Rectangle {
|
||||||
|
|
||||||
id: root;
|
id: root;
|
||||||
property string activeView: "initialize";
|
property string activeView: "initialize";
|
||||||
property string referrerURL: "";
|
|
||||||
property bool securityImageResultReceived: false;
|
property bool securityImageResultReceived: false;
|
||||||
property bool purchasesReceived: false;
|
property bool purchasesReceived: false;
|
||||||
property bool punctuationMode: false;
|
property bool punctuationMode: false;
|
||||||
|
@ -154,55 +153,10 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// TITLE BAR START
|
|
||||||
//
|
|
||||||
HifiCommerceCommon.EmulatedMarketplaceHeader {
|
|
||||||
id: titleBarContainer;
|
|
||||||
z: 997;
|
|
||||||
visible: false;
|
|
||||||
height: 100;
|
|
||||||
// Size
|
|
||||||
width: parent.width;
|
|
||||||
// Anchors
|
|
||||||
anchors.left: parent.left;
|
|
||||||
anchors.top: parent.top;
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
onSendToParent: {
|
|
||||||
if (msg.method === 'needsLogIn' && root.activeView !== "needsLogIn") {
|
|
||||||
root.activeView = "needsLogIn";
|
|
||||||
} else if (msg.method === 'showSecurityPicLightbox') {
|
|
||||||
lightboxPopup.titleText = "Your Security Pic";
|
|
||||||
lightboxPopup.bodyImageSource = msg.securityImageSource;
|
|
||||||
lightboxPopup.bodyText = lightboxPopup.securityPicBodyText;
|
|
||||||
lightboxPopup.button1text = "CLOSE";
|
|
||||||
lightboxPopup.button1method = function() {
|
|
||||||
lightboxPopup.visible = false;
|
|
||||||
}
|
|
||||||
lightboxPopup.visible = true;
|
|
||||||
} else {
|
|
||||||
sendToScript(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
enabled: titleBarContainer.usernameDropdownVisible;
|
|
||||||
anchors.fill: parent;
|
|
||||||
onClicked: {
|
|
||||||
titleBarContainer.usernameDropdownVisible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// TITLE BAR END
|
|
||||||
//
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: initialize;
|
id: initialize;
|
||||||
visible: root.activeView === "initialize";
|
visible: root.activeView === "initialize";
|
||||||
anchors.top: titleBarContainer.bottom;
|
anchors.top: parent.top;
|
||||||
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
|
|
||||||
anchors.bottom: parent.bottom;
|
anchors.bottom: parent.bottom;
|
||||||
anchors.left: parent.left;
|
anchors.left: parent.left;
|
||||||
anchors.right: parent.right;
|
anchors.right: parent.right;
|
||||||
|
@ -219,8 +173,7 @@ Rectangle {
|
||||||
id: installedAppsContainer;
|
id: installedAppsContainer;
|
||||||
z: 998;
|
z: 998;
|
||||||
visible: false;
|
visible: false;
|
||||||
anchors.top: titleBarContainer.bottom;
|
anchors.top: parent.top;
|
||||||
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
|
|
||||||
anchors.left: parent.left;
|
anchors.left: parent.left;
|
||||||
anchors.bottom: parent.bottom;
|
anchors.bottom: parent.bottom;
|
||||||
width: parent.width;
|
width: parent.width;
|
||||||
|
@ -422,8 +375,8 @@ Rectangle {
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.left: parent.left;
|
anchors.left: parent.left;
|
||||||
anchors.right: parent.right;
|
anchors.right: parent.right;
|
||||||
anchors.top: titleBarContainer.bottom;
|
anchors.top: parent.top;
|
||||||
anchors.topMargin: 8 - titleBarContainer.additionalDropdownHeight;
|
anchors.topMargin: 8;
|
||||||
anchors.bottom: parent.bottom;
|
anchors.bottom: parent.bottom;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -585,6 +538,7 @@ Rectangle {
|
||||||
delegate: PurchasedItem {
|
delegate: PurchasedItem {
|
||||||
itemName: title;
|
itemName: title;
|
||||||
itemId: id;
|
itemId: id;
|
||||||
|
updateItemId: model.upgrade_id ? model.upgrade_id : "";
|
||||||
itemPreviewImageUrl: preview;
|
itemPreviewImageUrl: preview;
|
||||||
itemHref: download_url;
|
itemHref: download_url;
|
||||||
certificateId: certificate_id;
|
certificateId: certificate_id;
|
||||||
|
@ -596,7 +550,6 @@ Rectangle {
|
||||||
cardBackVisible: model.cardBackVisible || false;
|
cardBackVisible: model.cardBackVisible || false;
|
||||||
isInstalled: model.isInstalled || false;
|
isInstalled: model.isInstalled || false;
|
||||||
wornEntityID: model.wornEntityID;
|
wornEntityID: model.wornEntityID;
|
||||||
upgradeUrl: model.upgrade_url;
|
|
||||||
upgradeTitle: model.upgrade_title;
|
upgradeTitle: model.upgrade_title;
|
||||||
itemType: model.item_type;
|
itemType: model.item_type;
|
||||||
valid: model.valid;
|
valid: model.valid;
|
||||||
|
@ -1083,8 +1036,6 @@ Rectangle {
|
||||||
function fromScript(message) {
|
function fromScript(message) {
|
||||||
switch (message.method) {
|
switch (message.method) {
|
||||||
case 'updatePurchases':
|
case 'updatePurchases':
|
||||||
referrerURL = message.referrerURL || "";
|
|
||||||
titleBarContainer.referrerURL = message.referrerURL || "";
|
|
||||||
filterBar.text = message.filterText ? message.filterText : "";
|
filterBar.text = message.filterText ? message.filterText : "";
|
||||||
break;
|
break;
|
||||||
case 'purchases_showMyItems':
|
case 'purchases_showMyItems':
|
||||||
|
|
|
@ -335,7 +335,7 @@ Item {
|
||||||
if (link.indexOf("users/") !== -1) {
|
if (link.indexOf("users/") !== -1) {
|
||||||
sendSignalToWallet({method: 'transactionHistory_usernameLinkClicked', usernameLink: link});
|
sendSignalToWallet({method: 'transactionHistory_usernameLinkClicked', usernameLink: link});
|
||||||
} else {
|
} else {
|
||||||
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
|
sendSignalToWallet({method: 'transactionHistory_linkClicked', itemId: model.marketplace_item});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,6 +233,7 @@
|
||||||
#include "commerce/Ledger.h"
|
#include "commerce/Ledger.h"
|
||||||
#include "commerce/Wallet.h"
|
#include "commerce/Wallet.h"
|
||||||
#include "commerce/QmlCommerce.h"
|
#include "commerce/QmlCommerce.h"
|
||||||
|
#include "commerce/QmlMarketplace.h"
|
||||||
#include "ResourceRequestObserver.h"
|
#include "ResourceRequestObserver.h"
|
||||||
|
|
||||||
#include "webbrowser/WebBrowserSuggestionsEngine.h"
|
#include "webbrowser/WebBrowserSuggestionsEngine.h"
|
||||||
|
@ -2985,7 +2986,23 @@ void Application::initializeUi() {
|
||||||
QUrl{ "hifi/dialogs/security/SecurityImageModel.qml" },
|
QUrl{ "hifi/dialogs/security/SecurityImageModel.qml" },
|
||||||
QUrl{ "hifi/dialogs/security/SecurityImageSelection.qml" },
|
QUrl{ "hifi/dialogs/security/SecurityImageSelection.qml" },
|
||||||
QUrl{ "hifi/tablet/TabletMenu.qml" },
|
QUrl{ "hifi/tablet/TabletMenu.qml" },
|
||||||
|
QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
|
||||||
}, commerceCallback);
|
}, commerceCallback);
|
||||||
|
|
||||||
|
QmlContextCallback marketplaceCallback = [](QQmlContext* context) {
|
||||||
|
context->setContextProperty("MarketplaceScriptingInterface", new QmlMarketplace());
|
||||||
|
};
|
||||||
|
OffscreenQmlSurface::addWhitelistContextHandler({
|
||||||
|
QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
|
||||||
|
}, marketplaceCallback);
|
||||||
|
|
||||||
|
QmlContextCallback platformInfoCallback = [](QQmlContext* context) {
|
||||||
|
context->setContextProperty("PlatformInfo", new PlatformInfoScriptingInterface());
|
||||||
|
};
|
||||||
|
OffscreenQmlSurface::addWhitelistContextHandler({
|
||||||
|
QUrl{ "hifi/commerce/marketplace/Marketplace.qml" },
|
||||||
|
}, platformInfoCallback);
|
||||||
|
|
||||||
QmlContextCallback ttsCallback = [](QQmlContext* context) {
|
QmlContextCallback ttsCallback = [](QQmlContext* context) {
|
||||||
context->setContextProperty("TextToSpeech", DependencyManager::get<TTSScriptingInterface>().data());
|
context->setContextProperty("TextToSpeech", DependencyManager::get<TTSScriptingInterface>().data());
|
||||||
};
|
};
|
||||||
|
@ -6231,6 +6248,8 @@ void Application::update(float deltaTime) {
|
||||||
_physicsEngine->processTransaction(transaction);
|
_physicsEngine->processTransaction(transaction);
|
||||||
myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction);
|
myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction);
|
||||||
myAvatar->prepareForPhysicsSimulation();
|
myAvatar->prepareForPhysicsSimulation();
|
||||||
|
_physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying());
|
||||||
|
|
||||||
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
||||||
dynamic->prepareForPhysicsSimulation();
|
dynamic->prepareForPhysicsSimulation();
|
||||||
});
|
});
|
||||||
|
|
|
@ -75,7 +75,6 @@ const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec
|
||||||
|
|
||||||
const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed
|
const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed
|
||||||
const float MIN_AVATAR_SPEED = 0.05f;
|
const float MIN_AVATAR_SPEED = 0.05f;
|
||||||
const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this
|
|
||||||
|
|
||||||
float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f;
|
float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f;
|
||||||
float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;
|
float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;
|
||||||
|
@ -847,6 +846,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
||||||
|
|
||||||
updateOrientation(deltaTime);
|
updateOrientation(deltaTime);
|
||||||
updatePosition(deltaTime);
|
updatePosition(deltaTime);
|
||||||
|
updateViewBoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
// update sensorToWorldMatrix for camera and hand controllers
|
// update sensorToWorldMatrix for camera and hand controllers
|
||||||
|
@ -3323,21 +3323,22 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
||||||
direction = Vectors::ZERO;
|
direction = Vectors::ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float sensorToWorldScale = getSensorToWorldScale();
|
||||||
if (state == CharacterController::State::Hover) {
|
if (state == CharacterController::State::Hover) {
|
||||||
// we're flying --> complex acceleration curve that builds on top of current motor speed and caps at some max speed
|
// we're flying --> complex acceleration curve that builds on top of current motor speed and caps at some max speed
|
||||||
|
|
||||||
float motorSpeed = glm::length(_actionMotorVelocity);
|
float motorSpeed = glm::length(_actionMotorVelocity);
|
||||||
float finalMaxMotorSpeed = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_FLYING_SPEED * _walkSpeedScalar;
|
float finalMaxMotorSpeed = sensorToWorldScale * DEFAULT_AVATAR_MAX_FLYING_SPEED * _walkSpeedScalar;
|
||||||
float speedGrowthTimescale = 2.0f;
|
float speedGrowthTimescale = 2.0f;
|
||||||
float speedIncreaseFactor = 1.8f * _walkSpeedScalar;
|
float speedIncreaseFactor = 1.8f * _walkSpeedScalar;
|
||||||
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale, 0.0f, 1.0f) * speedIncreaseFactor;
|
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale, 0.0f, 1.0f) * speedIncreaseFactor;
|
||||||
const float maxBoostSpeed = getSensorToWorldScale() * MAX_BOOST_SPEED;
|
const float maxBoostSpeed = sensorToWorldScale * MAX_BOOST_SPEED;
|
||||||
|
|
||||||
if (_isPushing) {
|
if (_isPushing) {
|
||||||
if (motorSpeed < maxBoostSpeed) {
|
if (motorSpeed < maxBoostSpeed) {
|
||||||
// an active action motor should never be slower than this
|
// an active action motor should never be slower than this
|
||||||
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
||||||
motorSpeed += getSensorToWorldScale() * MIN_AVATAR_SPEED * boostCoefficient;
|
motorSpeed += sensorToWorldScale * MIN_AVATAR_SPEED * boostCoefficient;
|
||||||
} else if (motorSpeed > finalMaxMotorSpeed) {
|
} else if (motorSpeed > finalMaxMotorSpeed) {
|
||||||
motorSpeed = finalMaxMotorSpeed;
|
motorSpeed = finalMaxMotorSpeed;
|
||||||
}
|
}
|
||||||
|
@ -3348,45 +3349,21 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
||||||
const glm::vec2 currentVel = { direction.x, direction.z };
|
const glm::vec2 currentVel = { direction.x, direction.z };
|
||||||
float scaledSpeed = scaleSpeedByDirection(currentVel, _walkSpeed.get(), _walkBackwardSpeed.get());
|
float scaledSpeed = scaleSpeedByDirection(currentVel, _walkSpeed.get(), _walkBackwardSpeed.get());
|
||||||
// _walkSpeedScalar is a multiplier if we are in sprint mode, otherwise 1.0
|
// _walkSpeedScalar is a multiplier if we are in sprint mode, otherwise 1.0
|
||||||
_actionMotorVelocity = getSensorToWorldScale() * (scaledSpeed * _walkSpeedScalar) * direction;
|
_actionMotorVelocity = sensorToWorldScale * (scaledSpeed * _walkSpeedScalar) * direction;
|
||||||
}
|
|
||||||
|
|
||||||
float previousBoomLength = _boomLength;
|
|
||||||
float boomChange = getDriveKey(ZOOM);
|
|
||||||
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
|
|
||||||
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
|
|
||||||
|
|
||||||
// May need to change view if boom length has changed
|
|
||||||
if (previousBoomLength != _boomLength) {
|
|
||||||
qApp->changeViewAsNeeded(_boomLength);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::updatePosition(float deltaTime) {
|
void MyAvatar::updatePosition(float deltaTime) {
|
||||||
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
|
if (_characterController.isEnabledAndReady()) {
|
||||||
updateActionMotor(deltaTime);
|
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
|
||||||
}
|
updateActionMotor(deltaTime);
|
||||||
|
|
||||||
vec3 velocity = getWorldVelocity();
|
|
||||||
float sensorToWorldScale = getSensorToWorldScale();
|
|
||||||
float sensorToWorldScale2 = sensorToWorldScale * sensorToWorldScale;
|
|
||||||
const float MOVING_SPEED_THRESHOLD_SQUARED = 0.0001f; // 0.01 m/s
|
|
||||||
if (!_characterController.isEnabledAndReady()) {
|
|
||||||
// _characterController is not in physics simulation but it can still compute its target velocity
|
|
||||||
updateMotors();
|
|
||||||
_characterController.computeNewVelocity(deltaTime, velocity);
|
|
||||||
|
|
||||||
float speed2 = glm::length(velocity);
|
|
||||||
if (speed2 > sensorToWorldScale2 * MIN_AVATAR_SPEED_SQUARED) {
|
|
||||||
// update position ourselves
|
|
||||||
applyPositionDelta(deltaTime * velocity);
|
|
||||||
}
|
}
|
||||||
measureMotionDerivatives(deltaTime);
|
float sensorToWorldScale = getSensorToWorldScale();
|
||||||
_moving = speed2 > sensorToWorldScale2 * MOVING_SPEED_THRESHOLD_SQUARED;
|
float sensorToWorldScale2 = sensorToWorldScale * sensorToWorldScale;
|
||||||
} else {
|
vec3 velocity = getWorldVelocity();
|
||||||
float speed2 = glm::length2(velocity);
|
float speed2 = glm::length2(velocity);
|
||||||
|
const float MOVING_SPEED_THRESHOLD_SQUARED = 0.0001f; // 0.01 m/s
|
||||||
_moving = speed2 > sensorToWorldScale2 * MOVING_SPEED_THRESHOLD_SQUARED;
|
_moving = speed2 > sensorToWorldScale2 * MOVING_SPEED_THRESHOLD_SQUARED;
|
||||||
|
|
||||||
if (_moving) {
|
if (_moving) {
|
||||||
// scan for walkability
|
// scan for walkability
|
||||||
glm::vec3 position = getWorldPosition();
|
glm::vec3 position = getWorldPosition();
|
||||||
|
@ -3398,6 +3375,18 @@ void MyAvatar::updatePosition(float deltaTime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyAvatar::updateViewBoom() {
|
||||||
|
float previousBoomLength = _boomLength;
|
||||||
|
float boomChange = getDriveKey(ZOOM);
|
||||||
|
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
|
||||||
|
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
|
||||||
|
|
||||||
|
// May need to change view if boom length has changed
|
||||||
|
if (previousBoomLength != _boomLength) {
|
||||||
|
qApp->changeViewAsNeeded(_boomLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {
|
void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {
|
||||||
// COLLISION SOUND API in Audio has been removed
|
// COLLISION SOUND API in Audio has been removed
|
||||||
}
|
}
|
||||||
|
|
|
@ -1732,6 +1732,7 @@ private:
|
||||||
void updateOrientation(float deltaTime);
|
void updateOrientation(float deltaTime);
|
||||||
void updateActionMotor(float deltaTime);
|
void updateActionMotor(float deltaTime);
|
||||||
void updatePosition(float deltaTime);
|
void updatePosition(float deltaTime);
|
||||||
|
void updateViewBoom();
|
||||||
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
|
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
|
||||||
void initHeadBones();
|
void initHeadBones();
|
||||||
void initAnimGraph();
|
void initAnimGraph();
|
||||||
|
|
|
@ -54,11 +54,10 @@ void QmlCommerce::openSystemApp(const QString& appName) {
|
||||||
{"GOTO", "hifi/tablet/TabletAddressDialog.qml"},
|
{"GOTO", "hifi/tablet/TabletAddressDialog.qml"},
|
||||||
{"PEOPLE", "hifi/Pal.qml"},
|
{"PEOPLE", "hifi/Pal.qml"},
|
||||||
{"WALLET", "hifi/commerce/wallet/Wallet.qml"},
|
{"WALLET", "hifi/commerce/wallet/Wallet.qml"},
|
||||||
{"MARKET", "/marketplace.html"}
|
{"MARKET", "hifi/commerce/marketplace/Marketplace.qml"}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const QMap<QString, QString> systemInject{
|
static const QMap<QString, QString> systemInject{
|
||||||
{"MARKET", "/scripts/system/html/js/marketplacesInject.js"}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|