Merge branch 'master' of github.com:highfidelity/hifi into artist-stocking-inventory
4
.gitignore
vendored
|
@ -104,4 +104,6 @@ tools/unity-avatar-exporter/Logs
|
|||
tools/unity-avatar-exporter/Packages
|
||||
tools/unity-avatar-exporter/ProjectSettings
|
||||
tools/unity-avatar-exporter/Temp
|
||||
|
||||
server-console/package-lock.json
|
||||
vcpkg/
|
||||
/tools/nitpick/compiledResources
|
||||
|
|
|
@ -80,8 +80,28 @@ endif()
|
|||
if (ANDROID)
|
||||
set(GLES_OPTION ON)
|
||||
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
|
||||
add_definitions(-DHIFI_ANDROID_APP=\"${HIFI_ANDROID_APP}\")
|
||||
if (
|
||||
(${HIFI_ANDROID_APP} STREQUAL "questInterface") OR
|
||||
(${HIFI_ANDROID_APP} STREQUAL "questFramePlayer") OR
|
||||
(${HIFI_ANDROID_APP} STREQUAL "framePlayer")
|
||||
)
|
||||
# We know the quest hardware has this extension, so we can force the use of instanced stereo
|
||||
add_definitions(-DHAVE_EXT_clip_cull_distance)
|
||||
# We can also use multiview stereo techniques
|
||||
add_definitions(-DHAVE_OVR_multiview2)
|
||||
add_definitions(-DHAVE_OVR_multiview)
|
||||
# We can also use our own foveated textures
|
||||
add_definitions(-DHAVE_QCOM_texture_foveated)
|
||||
|
||||
# if set, the application itself or some library it depends on MUST implement
|
||||
# `DisplayPluginList getDisplayPlugins()` and `InputPluginList getInputPlugins()`
|
||||
add_definitions(-DCUSTOM_INPUT_PLUGINS)
|
||||
add_definitions(-DCUSTOM_DISPLAY_PLUGINS)
|
||||
set(PLATFORM_PLUGIN_LIBRARIES oculusMobile oculusMobilePlugin)
|
||||
endif()
|
||||
else ()
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine)
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine Xml)
|
||||
endif ()
|
||||
|
||||
if (USE_GLES AND (NOT ANDROID))
|
||||
|
@ -108,8 +128,10 @@ set(PLATFORM_QT_GL OpenGL)
|
|||
|
||||
if (USE_GLES)
|
||||
add_definitions(-DUSE_GLES)
|
||||
add_definitions(-DGPU_POINTER_STORAGE_SHARED)
|
||||
set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gles)
|
||||
else()
|
||||
add_definitions(-DGPU_POINTER_STORAGE_RAW)
|
||||
set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gl)
|
||||
endif()
|
||||
|
||||
|
|
75
INSTALL.md
|
@ -4,7 +4,8 @@ During generation, CMake should produce an `install` target and a `package` targ
|
|||
|
||||
### Install
|
||||
|
||||
The `install` target will copy the High Fidelity targets and their dependencies to your `CMAKE_INSTALL_PREFIX`.
|
||||
The `install` target will copy the High Fidelity targets and their dependencies to your `CMAKE_INSTALL_PREFIX`.
|
||||
This variable is set by the `project(hifi)` command in `CMakeLists.txt` to `C:/Program Files/hifi` and stored in `build/CMakeCache.txt`
|
||||
|
||||
### Packaging
|
||||
|
||||
|
@ -14,17 +15,67 @@ To produce an installer, run the `package` target.
|
|||
|
||||
To produce an executable installer on Windows, the following are required:
|
||||
|
||||
- [Nullsoft Scriptable Install System](http://nsis.sourceforge.net/Download) - 3.0b3
|
||||
- [UAC Plug-in for Nullsoft](http://nsis.sourceforge.net/UAC_plug-in) - 0.2.4c
|
||||
- [nsProcess Plug-in for Nullsoft](http://nsis.sourceforge.net/NsProcess_plugin) - 1.6
|
||||
- [Inetc Plug-in for Nullsoft](http://nsis.sourceforge.net/Inetc_plug-in) - 1.0
|
||||
- [NSISpcre Plug-in for Nullsoft](http://nsis.sourceforge.net/NSISpcre_plug-in) - 1.0
|
||||
- [nsisSlideshow Plug-in for Nullsoft](http://nsis.sourceforge.net/NsisSlideshow_plug-in) - 1.7
|
||||
- [Nsisunz plug-in for Nullsoft](http://nsis.sourceforge.net/Nsisunz_plug-in)
|
||||
- [ApplicationID plug-in for Nullsoft](http://nsis.sourceforge.net/ApplicationID_plug-in) - 1.0
|
||||
1. [7-zip](<https://www.7-zip.org/download.html>)
|
||||
|
||||
Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System.
|
||||
1. [Nullsoft Scriptable Install System](http://nsis.sourceforge.net/Download) - 3.04
|
||||
Install using defaults (will install to `C:\Program Files (x86)\NSIS`)
|
||||
1. [UAC Plug-in for Nullsoft](http://nsis.sourceforge.net/UAC_plug-in) - 0.2.4c
|
||||
1. Extract Zip
|
||||
1. Copy `UAC.nsh` to `C:\Program Files (x86)\NSIS\Include\`
|
||||
1. Copy `Plugins\x86-ansi\UAC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `Plugins\x86-unicode\UAC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
1. [nsProcess Plug-in for Nullsoft](http://nsis.sourceforge.net/NsProcess_plugin) - 1.6 (use the link marked **nsProcess_1_6.7z**)
|
||||
1. Extract Zip
|
||||
1. Copy `Include\nsProcess.nsh` to `C:\Program Files (x86)\NSIS\Include\`
|
||||
1. Copy `Plugins\nsProcess.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `Plugins\nsProcessW.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [InetC Plug-in for Nullsoft](http://nsis.sourceforge.net/Inetc_plug-in) - 1.0
|
||||
1. Extract Zip
|
||||
1. Copy `Plugin\x86-ansi\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `Plugin\x86-unicode\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [NSISpcre Plug-in for Nullsoft](http://nsis.sourceforge.net/NSISpcre_plug-in) - 1.0
|
||||
1. Extract Zip
|
||||
1. Copy `NSISpre.nsh` to `C:\Program Files (x86)\NSIS\Include\`
|
||||
1. Copy `NSISpre.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
|
||||
1. [nsisSlideshow Plug-in for Nullsoft](<http://wiz0u.free.fr/prog/nsisSlideshow/>) - 1.7
|
||||
1. Extract Zip
|
||||
1. Copy `bin\nsisSlideshow.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `bin\nsisSlideshowW.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [Nsisunz plug-in for Nullsoft](http://nsis.sourceforge.net/Nsisunz_plug-in)
|
||||
1. Download both Zips and unzip
|
||||
1. Copy `nsisunz\Release\nsisunz.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `NSISunzU\Plugin unicode\nsisunz.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [ApplicationID plug-in for Nullsoft]() - 1.0
|
||||
1. Download [`Pre-built DLLs`](<https://github.com/connectiblutz/NSIS-ApplicationID/releases/download/1.1/NSIS-ApplicationID.zip>)
|
||||
1. Extract Zip
|
||||
1. Copy `Release\ApplicationID.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `ReleaseUnicode\ApplicationID.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [npm](<https://www.npmjs.com/get-npm>)
|
||||
1. Install version 10.15.0 LTS
|
||||
|
||||
1. Perform a clean cmake from a new terminal.
|
||||
1. Open the `hifi.sln` Solution and select the Release configuration.
|
||||
1. Build the Solution.
|
||||
1. Build `packaged-server-console` (found under **Server Console**)
|
||||
This will add 2 folders to `build\server-console\` -
|
||||
`server-console-win32-x64` and `x64`
|
||||
1. Build CMakeTargets->PACKAGE
|
||||
Installer is now available in `build\_CPack_Packages\win64\NSIS`
|
||||
|
||||
#### OS X
|
||||
|
||||
Run the `package` target to create an Apple Disk Image (.dmg).
|
||||
1. [npm](<https://www.npmjs.com/get-npm>)
|
||||
Install version 10.15.0 LTS
|
||||
|
||||
1. Perform a clean cmake.
|
||||
1. Perform a Release build of ALL_BUILD
|
||||
1. Perform a Release build of `packaged-server-console`
|
||||
This will add a folder to `build\server-console\` -
|
||||
Sandbox-darwin-x64
|
||||
1. Perform a Release build of `package`
|
||||
Installer is now available in `build/_CPack_Packages/Darwin/DragNDrop
|
||||
|
|
5
android/apps/framePlayer/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
set(TARGET_NAME framePlayer)
|
||||
setup_hifi_library(AndroidExtras)
|
||||
link_hifi_libraries(shared ktx shaders qml gpu gl ${PLATFORM_GL_BACKEND})
|
||||
target_link_libraries(${TARGET_NAME} android log m)
|
||||
target_opengl()
|
41
android/apps/framePlayer/build.gradle
Normal file
|
@ -0,0 +1,41 @@
|
|||
import com.android.builder.core.BuilderConstants
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
defaultConfig {
|
||||
applicationId "io.highfidelity.frameplayer"
|
||||
minSdkVersion 25
|
||||
targetSdkVersion 28
|
||||
ndk { abiFilters 'arm64-v8a' }
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments '-DHIFI_ANDROID=1',
|
||||
'-DHIFI_ANDROID_APP=framePlayer',
|
||||
'-DANDROID_TOOLCHAIN=clang',
|
||||
'-DANDROID_STL=c++_shared',
|
||||
'-DCMAKE_VERBOSE_MAKEFILE=ON'
|
||||
targets = ['framePlayer']
|
||||
}
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
externalNativeBuild.cmake.path '../../../CMakeLists.txt'
|
||||
|
||||
variantFilter { variant ->
|
||||
def build = variant.buildType.name
|
||||
if (build == BuilderConstants.RELEASE) {
|
||||
variant.setIgnore(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
|
||||
implementation project(':qt')
|
||||
}
|
25
android/apps/framePlayer/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
|
38
android/apps/framePlayer/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?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-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<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="org.qtproject.qt5.android.bindings.QtActivity"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="landscape"
|
||||
android:excludeFromRecents="false"
|
||||
android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode">
|
||||
<!-- JNI nonsense -->
|
||||
<meta-data android:name="android.app.lib_name" android:value="framePlayer"/>
|
||||
<!-- 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"/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
6
android/apps/framePlayer/src/main/cpp/FramePlayer.qrc
Normal file
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0">
|
||||
<qresource prefix="/">
|
||||
<file>qml/main.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
91
android/apps/framePlayer/src/main/cpp/PlayerWindow.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// 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 <QtCore/QFileInfo>
|
||||
#include <QtGui/QImageReader>
|
||||
#include <QtQml/QQmlContext>
|
||||
#include <QtQuick/QQuickItem>
|
||||
|
||||
#include <gpu/Frame.h>
|
||||
#include <gpu/FrameIO.h>
|
||||
|
||||
PlayerWindow::PlayerWindow() {
|
||||
setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
|
||||
setSurfaceType(QSurface::OpenGLSurface);
|
||||
create();
|
||||
showFullScreen();
|
||||
|
||||
// Make sure the window has been created by processing events
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
// Start the rendering thread
|
||||
_renderThread.initialize(this, &_surface);
|
||||
|
||||
// Start the UI
|
||||
_surface.resize(size());
|
||||
connect(&_surface, &hifi::qml::OffscreenSurface::rootContextCreated, this, [](QQmlContext* context){
|
||||
context->setContextProperty("FRAMES_FOLDER", "file:assets:/frames");
|
||||
});
|
||||
_surface.load("qrc:///qml/main.qml");
|
||||
|
||||
// Connect the UI handler
|
||||
QObject::connect(_surface.getRootItem(), SIGNAL(loadFile(QString)),
|
||||
this, SLOT(loadFile(QString))
|
||||
);
|
||||
|
||||
// Turn on UI input events
|
||||
installEventFilter(&_surface);
|
||||
}
|
||||
|
||||
PlayerWindow::~PlayerWindow() {
|
||||
}
|
||||
|
||||
// static const char* FRAME_FILE = "assets:/frames/20190110_1635.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 PlayerWindow::loadFile(QString filename) {
|
||||
QString realFilename = QUrl(filename).toLocalFile();
|
||||
if (QFileInfo(realFilename).exists()) {
|
||||
auto frame = gpu::readFrame(realFilename.toStdString(), _renderThread._externalTexture, &textureLoader);
|
||||
_surface.pause();
|
||||
_renderThread.submitFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerWindow::touchEvent(QTouchEvent* event) {
|
||||
// Super basic input handling when the 3D scene is active.... tap with two finders to return to the
|
||||
// QML UI
|
||||
static size_t touches = 0;
|
||||
switch (event->type()) {
|
||||
case QEvent::TouchBegin:
|
||||
case QEvent::TouchUpdate:
|
||||
touches = std::max<size_t>(touches, event->touchPoints().size());
|
||||
break;
|
||||
|
||||
case QEvent::TouchEnd:
|
||||
if (touches >= 2) {
|
||||
_renderThread.submitFrame(nullptr);
|
||||
_surface.resume();
|
||||
}
|
||||
touches = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
35
android/apps/framePlayer/src/main/cpp/PlayerWindow.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// 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 <qml/OffscreenSurface.h>
|
||||
#include <gpu/Forward.h>
|
||||
#include "RenderThread.h"
|
||||
|
||||
// Create a simple OpenGL window that renders text in various ways
|
||||
class PlayerWindow : public QWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PlayerWindow();
|
||||
virtual ~PlayerWindow();
|
||||
|
||||
protected:
|
||||
void touchEvent(QTouchEvent *ev) override;
|
||||
|
||||
public slots:
|
||||
void loadFile(QString filename);
|
||||
|
||||
private:
|
||||
hifi::qml::OffscreenSurface _surface;
|
||||
QSettings _settings;
|
||||
RenderThread _renderThread;
|
||||
};
|
162
android/apps/framePlayer/src/main/cpp/RenderThread.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
//
|
||||
// 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 <QtGui/QWindow>
|
||||
|
||||
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::setup() {
|
||||
// Wait until the context has been moved to this thread
|
||||
{ std::unique_lock<std::mutex> lock(_frameLock); }
|
||||
|
||||
makeCurrent();
|
||||
// Disable vsync for profiling
|
||||
::gl::setSwapInterval(0);
|
||||
|
||||
glClearColor(1, 1, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_glContext.swapBuffers();
|
||||
|
||||
// GPU library init
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
_gpuContext = std::make_shared<gpu::Context>();
|
||||
_backend = _gpuContext->getBackend();
|
||||
_gpuContext->beginFrame();
|
||||
_gpuContext->endFrame();
|
||||
makeCurrent();
|
||||
|
||||
|
||||
glGenFramebuffers(1, &_uiFbo);
|
||||
glGenTextures(1, &_externalTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, _externalTexture);
|
||||
static const glm::u8vec4 color{ 0 };
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &color);
|
||||
|
||||
glClearColor(0, 1, 1, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_glContext.swapBuffers();
|
||||
}
|
||||
|
||||
void RenderThread::initialize(QWindow* window, hifi::qml::OffscreenSurface* offscreen) {
|
||||
std::unique_lock<std::mutex> lock(_frameLock);
|
||||
setObjectName("RenderThread");
|
||||
Parent::initialize();
|
||||
|
||||
_offscreen = offscreen;
|
||||
_window = window;
|
||||
_glContext.setWindow(_window);
|
||||
_glContext.create();
|
||||
_glContext.makeCurrent();
|
||||
|
||||
hifi::qml::OffscreenSurface::setSharedContext(_glContext.qglContext());
|
||||
glClearColor(1, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_glContext.swapBuffers();
|
||||
_glContext.doneCurrent();
|
||||
_glContext.moveToThread(_thread);
|
||||
_thread->setObjectName("RenderThread");
|
||||
}
|
||||
|
||||
void RenderThread::shutdown() {
|
||||
_activeFrame.reset();
|
||||
while (!_pendingFrames.empty()) {
|
||||
_gpuContext->consumeFrameUpdates(_pendingFrames.front());
|
||||
_pendingFrames.pop();
|
||||
}
|
||||
_gpuContext->shutdown();
|
||||
_gpuContext.reset();
|
||||
}
|
||||
|
||||
void RenderThread::renderFrame() {
|
||||
auto windowSize = _window->geometry().size();
|
||||
uvec2 readFboSize;
|
||||
uint32_t readFbo{ 0 };
|
||||
|
||||
if (_activeFrame) {
|
||||
const auto &frame = _activeFrame;
|
||||
_backend->recycle();
|
||||
_backend->syncCache();
|
||||
_gpuContext->enableStereo(frame->stereoState._enable);
|
||||
if (frame && !frame->batches.empty()) {
|
||||
_gpuContext->executeFrame(frame);
|
||||
}
|
||||
auto &glBackend = static_cast<gpu::gl::GLBackend&>(*_backend);
|
||||
readFbo = glBackend.getFramebufferID(frame->framebuffer);
|
||||
readFboSize = frame->framebuffer->getSize();
|
||||
CHECK_GL_ERROR();
|
||||
} else {
|
||||
hifi::qml::OffscreenSurface::TextureAndFence newTextureAndFence;
|
||||
if (_offscreen->fetchTexture(newTextureAndFence)) {
|
||||
if (_uiTexture != 0) {
|
||||
auto readFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
_offscreen->getDiscardLambda()(_uiTexture, readFence);
|
||||
_uiTexture = 0;
|
||||
}
|
||||
|
||||
glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync((GLsync)newTextureAndFence.second);
|
||||
_uiTexture = newTextureAndFence.first;
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, _uiFbo);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _uiTexture, 0);
|
||||
}
|
||||
|
||||
if (_uiTexture != 0) {
|
||||
readFbo = _uiFbo;
|
||||
readFboSize = { windowSize.width(), windowSize.height() };
|
||||
}
|
||||
}
|
||||
|
||||
if (readFbo) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFbo);
|
||||
glBlitFramebuffer(
|
||||
0, 0, readFboSize.x, readFboSize.y,
|
||||
0, 0, windowSize.width(), windowSize.height(),
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
} else {
|
||||
glClearColor(1, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
_glContext.swapBuffers();
|
||||
}
|
||||
|
||||
void RenderThread::updateFrame() {
|
||||
std::queue<gpu::FramePointer> pendingFrames;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_frameLock);
|
||||
pendingFrames.swap(_pendingFrames);
|
||||
}
|
||||
|
||||
while (!pendingFrames.empty()) {
|
||||
_activeFrame = pendingFrames.front();
|
||||
pendingFrames.pop();
|
||||
if (_activeFrame) {
|
||||
_gpuContext->consumeFrameUpdates(_activeFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderThread::process() {
|
||||
updateFrame();
|
||||
makeCurrent();
|
||||
renderFrame();
|
||||
return true;
|
||||
}
|
54
android/apps/framePlayer/src/main/cpp/RenderThread.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// 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 <GenericThread.h>
|
||||
|
||||
#include <gl/Context.h>
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
#include <qml/OffscreenSurface.h>
|
||||
|
||||
class RenderThread : public GenericThread {
|
||||
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;
|
||||
hifi::qml::OffscreenSurface* _offscreen{ nullptr };
|
||||
|
||||
gl::Context _glContext;
|
||||
uint32_t _uiTexture{ 0 };
|
||||
uint32_t _uiFbo{ 0 };
|
||||
|
||||
void move(const glm::vec3& v);
|
||||
void setup() override;
|
||||
bool process() override;
|
||||
void shutdown() override;
|
||||
|
||||
void initialize(QWindow* window, hifi::qml::OffscreenSurface* offscreen);
|
||||
|
||||
void submitFrame(const gpu::FramePointer& frame);
|
||||
void updateFrame();
|
||||
void renderFrame();
|
||||
|
||||
bool makeCurrent() {
|
||||
return _glContext.makeCurrent();
|
||||
}
|
||||
|
||||
void doneCurrent() {
|
||||
_glContext.doneCurrent();
|
||||
}
|
||||
};
|
54
android/apps/framePlayer/src/main/cpp/main.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// 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;
|
||||
app.exec();
|
||||
qInstallMessageHandler(oldMessageHandler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
36
android/apps/framePlayer/src/main/cpp/qml/main.qml
Normal file
|
@ -0,0 +1,36 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Dialogs 1.1
|
||||
import Qt.labs.folderlistmodel 2.11
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 640
|
||||
height: 480
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
|
||||
FolderListModel {
|
||||
id: folderModel
|
||||
folder: FRAMES_FOLDER
|
||||
nameFilters: ["*.json"]
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileDelegate
|
||||
Text {
|
||||
text: fileName
|
||||
font.pointSize: 36
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.loadFile(folderModel.folder + "/" + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model: folderModel
|
||||
delegate: fileDelegate
|
||||
}
|
||||
|
||||
signal loadFile(string filename);
|
||||
}
|
|
@ -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>
|
3
android/apps/framePlayer/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name" translatable="false">GPU Frame Player</string>
|
||||
</resources>
|
44
android/apps/interface/src/main/assets/avatars.json
Normal file
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"avatars": [
|
||||
{
|
||||
"name": "Wooden Mannequin",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/7fe80a1e-f445-4800-9e89-40e677b03bee/large/hifi-mp-7fe80a1e-f445-4800-9e89-40e677b03bee.jpg",
|
||||
"url": "qrc:////meshes/defaultAvatar_full.fst"
|
||||
},
|
||||
{
|
||||
"name": "Anime-Styled Boy",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/46e0fd52-3cff-462f-ba97-927338d88295/thumbnail/hifi-mp-46e0fd52-3cff-462f-ba97-927338d88295.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/46e0fd52-3cff-462f-ba97-927338d88295-v1/AnimeBoy2.fst"
|
||||
},
|
||||
{
|
||||
"name": "Anime-Styled Girl",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/1e7e43f6-1757-44d3-baa4-756827d96311/large/hifi-mp-1e7e43f6-1757-44d3-baa4-756827d96311.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/0dce3426-55c8-4641-8dd5-d76eb575b64a-v1/Anime_F_Outfit.fst"
|
||||
},
|
||||
{
|
||||
"name": "Last Legends: Male Avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/28569047-6f1a-4100-af67-8054ec397cc3/thumbnail/hifi-mp-28569047-6f1a-4100-af67-8054ec397cc3.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/28569047-6f1a-4100-af67-8054ec397cc3-v1/LLMale2.fst"
|
||||
},
|
||||
{
|
||||
"name": "Last Legends: Female Avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/8d823be5-6197-4418-b984-eb94160ed956/thumbnail/hifi-mp-8d823be5-6197-4418-b984-eb94160ed956.jpg",
|
||||
"url": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/46e0fd52-3cff-462f-ba97-927338d88295/thumbnail/hifi-mp-46e0fd52-3cff-462f-ba97-927338d88295.jpg"
|
||||
},
|
||||
{
|
||||
"name": "Matthew: Photo-real avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/b652081b-a199-425e-ae5c-7815721bdc09/thumbnail/hifi-mp-b652081b-a199-425e-ae5c-7815721bdc09.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/b652081b-a199-425e-ae5c-7815721bdc09-v1/matthew.fst"
|
||||
},
|
||||
{
|
||||
"name": "Priscilla: Photo real avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/e7565f93-8bc5-47c2-b6eb-b3b31d4a1339/thumbnail/hifi-mp-e7565f93-8bc5-47c2-b6eb-b3b31d4a1339.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/e7565f93-8bc5-47c2-b6eb-b3b31d4a1339-v1/priscilla.fst"
|
||||
},
|
||||
{
|
||||
"name": "H1-F1 Optical Interpreter bot",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/469c8b66-e3c2-47fb-9820-e306b1dd15c4/large/hifi-mp-469c8b66-e3c2-47fb-9820-e306b1dd15c4.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/469c8b66-e3c2-47fb-9820-e306b1dd15c4-v1/optical_interpreter[1].fst"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -493,6 +493,34 @@ Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(J
|
|||
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_ProfileFragment_getDisplayName(JNIEnv *env,
|
||||
jobject instance) {
|
||||
|
||||
QString displayName = AndroidHelper::instance().getDisplayName();
|
||||
return env->NewStringUTF(displayName.toLatin1().data());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_ProfileFragment_setDisplayName(JNIEnv *env,
|
||||
jobject instance,
|
||||
jstring name_) {
|
||||
const char *c_name = env->GetStringUTFChars(name_, 0);
|
||||
const QString name = QString::fromUtf8(c_name);
|
||||
env->ReleaseStringUTFChars(name_, c_name);
|
||||
AndroidHelper::instance().setDisplayName(name);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_ProfileFragment_setAvatarUrl(JNIEnv *env,
|
||||
jobject instance,
|
||||
jstring url_) {
|
||||
const char *url = env->GetStringUTFChars(url_, 0);
|
||||
QString avatarUrl = QString::fromUtf8(url);
|
||||
AndroidHelper::instance().setMyAvatarUrl(avatarUrl);
|
||||
env->ReleaseStringUTFChars(url_, url);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_MainActivity_logout(JNIEnv *env, jobject instance) {
|
||||
DependencyManager::get<AccountManager>()->logout();
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import android.net.Uri;
|
|||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -31,6 +32,7 @@ import android.view.WindowManager;
|
|||
import android.widget.FrameLayout;
|
||||
import android.widget.SlidingDrawer;
|
||||
|
||||
import org.qtproject.qt5.android.QtNative;
|
||||
import org.qtproject.qt5.android.QtLayout;
|
||||
import org.qtproject.qt5.android.QtSurface;
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
|
@ -53,6 +55,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
public static final String DOMAIN_URL = "url";
|
||||
public static final String EXTRA_GOTO_USERNAME = "gotousername";
|
||||
private static final String TAG = "Interface";
|
||||
public static final String EXTRA_ARGS = "args";
|
||||
private static final int WEB_DRAWER_RIGHT_MARGIN = 262;
|
||||
private static final int WEB_DRAWER_BOTTOM_MARGIN = 150;
|
||||
private static final int NORMAL_DPI = 160;
|
||||
|
@ -77,6 +80,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
|
||||
private boolean nativeEnterBackgroundCallEnqueued = false;
|
||||
private SlidingDrawer mWebSlidingDrawer;
|
||||
private boolean mStartInDomain;
|
||||
// private GvrApi gvrApi;
|
||||
// Opaque native pointer to the Application C++ object.
|
||||
// This object is owned by the InterfaceActivity instance and passed to the native methods.
|
||||
|
@ -92,8 +96,14 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.isLoading = true;
|
||||
Intent intent = getIntent();
|
||||
if (intent.hasExtra(DOMAIN_URL) && !intent.getStringExtra(DOMAIN_URL).isEmpty()) {
|
||||
if (intent.hasExtra(DOMAIN_URL) && !TextUtils.isEmpty(intent.getStringExtra(DOMAIN_URL))) {
|
||||
intent.putExtra("applicationArguments", "--url " + intent.getStringExtra(DOMAIN_URL));
|
||||
} else if (intent.hasExtra(EXTRA_ARGS)) {
|
||||
String args = intent.getStringExtra(EXTRA_ARGS);
|
||||
if (!TextUtils.isEmpty(args)) {
|
||||
mStartInDomain = true;
|
||||
intent.putExtra("applicationArguments", args);
|
||||
}
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
@ -124,7 +134,10 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
getActionBar().hide();
|
||||
}
|
||||
});
|
||||
startActivity(new Intent(this, SplashActivity.class));
|
||||
Intent splashIntent = new Intent(this, SplashActivity.class);
|
||||
splashIntent.putExtra(SplashActivity.EXTRA_START_IN_DOMAIN, mStartInDomain);
|
||||
startActivity(splashIntent);
|
||||
|
||||
mVibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE);
|
||||
headsetStateReceiver = new HeadsetStateReceiver();
|
||||
}
|
||||
|
@ -166,8 +179,27 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
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
|
||||
|
|
|
@ -33,13 +33,15 @@ import com.squareup.picasso.Picasso;
|
|||
import io.highfidelity.hifiinterface.fragment.FriendsFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.HomeFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.PolicyFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.ProfileFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.SettingsFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.SignupFragment;
|
||||
import io.highfidelity.hifiinterface.task.DownloadProfileImageTask;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener,
|
||||
HomeFragment.OnHomeInteractionListener,
|
||||
FriendsFragment.OnHomeInteractionListener {
|
||||
FriendsFragment.OnHomeInteractionListener,
|
||||
ProfileFragment.OnProfileInteractionListener {
|
||||
|
||||
private static final int PROFILE_PICTURE_PLACEHOLDER = R.drawable.default_profile_avatar;
|
||||
public static final String DEFAULT_FRAGMENT = "Home";
|
||||
|
@ -61,6 +63,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
private View mProfilePanel;
|
||||
private TextView mLogoutOption;
|
||||
private MenuItem mPeopleMenuItem;
|
||||
private MenuItem mProfileMenuItem;
|
||||
|
||||
private boolean backToScene;
|
||||
private String backToUrl;
|
||||
|
@ -83,6 +86,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
|
||||
mPeopleMenuItem = mNavigationView.getMenu().findItem(R.id.action_people);
|
||||
|
||||
mProfileMenuItem = mNavigationView.getMenu().findItem(R.id.action_profile);
|
||||
|
||||
updateDebugMenu(mNavigationView.getMenu());
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
|
@ -162,6 +167,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
loadFragment(fragment, getString(R.string.people), getString(R.string.tagFragmentPeople), true, true);
|
||||
}
|
||||
|
||||
private void loadProfileFragment() {
|
||||
Fragment fragment = ProfileFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.profile), getString(R.string.tagFragmentProfile), true, true);
|
||||
}
|
||||
|
||||
private void loadSettingsFragment() {
|
||||
SettingsFragment fragment = SettingsFragment.newInstance();
|
||||
|
||||
|
@ -261,6 +272,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
case R.id.action_people:
|
||||
loadPeopleFragment();
|
||||
return true;
|
||||
case R.id.action_profile:
|
||||
loadProfileFragment();
|
||||
break;
|
||||
case R.id.action_debug_settings:
|
||||
loadSettingsFragment();
|
||||
return true;
|
||||
|
@ -351,6 +365,21 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
goToUser(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelProfileEdit() {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleteProfileEdit() {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvatarChosen() {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
|
||||
private class RoundProfilePictureCallback implements Callback {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
|
|||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -27,9 +28,14 @@ public class PermissionChecker extends Activity {
|
|||
private static final boolean CHOOSE_AVATAR_ON_STARTUP = false;
|
||||
private static final String TAG = "Interface";
|
||||
|
||||
private static final String EXTRA_ARGS = "args";
|
||||
private String mArgs;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mArgs =(getIntent().getStringExtra(EXTRA_ARGS));
|
||||
|
||||
Intent myIntent = new Intent(this, BreakpadUploaderService.class);
|
||||
startService(myIntent);
|
||||
if (CHOOSE_AVATAR_ON_STARTUP) {
|
||||
|
@ -76,6 +82,11 @@ public class PermissionChecker extends Activity {
|
|||
|
||||
private void launchActivityWithPermissions(){
|
||||
Intent i = new Intent(this, InterfaceActivity.class);
|
||||
|
||||
if (!TextUtils.isEmpty(mArgs)) {
|
||||
i.putExtra(EXTRA_ARGS, mArgs);
|
||||
}
|
||||
|
||||
startActivity(i);
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ import android.view.View;
|
|||
|
||||
public class SplashActivity extends Activity {
|
||||
|
||||
public static final String EXTRA_START_IN_DOMAIN = "start-in-domain";
|
||||
private boolean mStartInDomain;
|
||||
|
||||
private native void registerLoadCompleteListener();
|
||||
|
||||
@Override
|
||||
|
@ -36,13 +39,27 @@ public class SplashActivity extends Activity {
|
|||
}
|
||||
|
||||
public void onAppLoadedComplete() {
|
||||
if (HifiUtils.getInstance().isUserLoggedIn()) {
|
||||
startActivity(new Intent(this, MainActivity.class));
|
||||
} else {
|
||||
Intent menuIntent = new Intent(this, LoginMenuActivity.class);
|
||||
menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true);
|
||||
startActivity(menuIntent);
|
||||
if (!mStartInDomain) {
|
||||
if (HifiUtils.getInstance().isUserLoggedIn()) {
|
||||
startActivity(new Intent(this, MainActivity.class));
|
||||
} else {
|
||||
Intent menuIntent = new Intent(this, LoginMenuActivity.class);
|
||||
menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true);
|
||||
startActivity(menuIntent);
|
||||
}
|
||||
}
|
||||
SplashActivity.this.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(EXTRA_START_IN_DOMAIN, mStartInDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
mStartInDomain = savedInstanceState.getBoolean(EXTRA_START_IN_DOMAIN, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package io.highfidelity.hifiinterface.fragment;
|
||||
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.AvatarProvider;
|
||||
import io.highfidelity.hifiinterface.view.AvatarAdapter;
|
||||
|
||||
public class ProfileFragment extends Fragment {
|
||||
|
||||
private TextView mDisplayName;
|
||||
|
||||
private Button mOkButton;
|
||||
private OnProfileInteractionListener mListener;
|
||||
private AvatarProvider mAvatarsProvider;
|
||||
|
||||
private native String getDisplayName();
|
||||
private native void setDisplayName(String name);
|
||||
private native void setAvatarUrl(String url);
|
||||
|
||||
public ProfileFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
public static ProfileFragment newInstance() {
|
||||
ProfileFragment fragment = new ProfileFragment();
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_profile, container, false);
|
||||
|
||||
mDisplayName = rootView.findViewById(R.id.displayName);
|
||||
mDisplayName.setText(getDisplayName());
|
||||
mDisplayName.setOnEditorActionListener((textView, actionId, keyEvent) -> onDisplayNameEditorAction(textView, actionId, keyEvent));
|
||||
|
||||
mOkButton = rootView.findViewById(R.id.okButton);
|
||||
mOkButton.setOnClickListener(view -> onOkButtonClicked());
|
||||
|
||||
rootView.findViewById(R.id.cancel).setOnClickListener(view -> onCancelProfileEdit());
|
||||
|
||||
RecyclerView avatarsView = rootView.findViewById(R.id.gridview);
|
||||
int numberOfColumns = 1;
|
||||
mAvatarsProvider = new AvatarProvider(getContext());
|
||||
GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns);
|
||||
avatarsView.setLayoutManager(gridLayoutMgr);
|
||||
AvatarAdapter avatarAdapter = new AvatarAdapter(getContext(), mAvatarsProvider);
|
||||
avatarsView.setAdapter(avatarAdapter);
|
||||
avatarAdapter.loadAvatars();
|
||||
|
||||
avatarAdapter.setClickListener((view, position, avatar) -> {
|
||||
setAvatarUrl(avatar.avatarUrl);
|
||||
if (mListener != null) {
|
||||
mListener.onAvatarChosen();
|
||||
}
|
||||
});
|
||||
return rootView;
|
||||
}
|
||||
|
||||
private void onOkButtonClicked() {
|
||||
setDisplayName(mDisplayName.getText().toString());
|
||||
View view = getActivity().getCurrentFocus();
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
if (mListener != null) {
|
||||
mListener.onCompleteProfileEdit();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onDisplayNameEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
mOkButton.performClick();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void onCancelProfileEdit() {
|
||||
View view = getActivity().getCurrentFocus();
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
if (mListener != null) {
|
||||
mListener.onCancelProfileEdit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the back pressed event and returns true if it was managed by this Fragment
|
||||
* @return
|
||||
*/
|
||||
public boolean onBackPressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnProfileInteractionListener) {
|
||||
mListener = (OnProfileInteractionListener) context;
|
||||
} else {
|
||||
throw new RuntimeException(context.toString()
|
||||
+ " must implement OnProfileInteractionListener");
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnProfileInteractionListener {
|
||||
void onCancelProfileEdit();
|
||||
void onCompleteProfileEdit();
|
||||
void onAvatarChosen();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package io.highfidelity.hifiinterface.provider;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.view.AvatarAdapter;
|
||||
|
||||
/**
|
||||
* Created by gcalero on 1/21/19
|
||||
*/
|
||||
public class AvatarProvider {
|
||||
|
||||
private static final String AVATARS_JSON = "avatars.json";
|
||||
private static final String JSON_FIELD_NAME = "name";
|
||||
private static final String JSON_FIELD_URL = "url";
|
||||
private static final String JSON_FIELD_IMAGE = "preview_image";
|
||||
private static final String JSON_FIELD_AVATARS_ARRAY = "avatars";
|
||||
private final Context mContext;
|
||||
|
||||
public interface AvatarsCallback {
|
||||
void retrieveOk(List<AvatarAdapter.Avatar> avatars);
|
||||
void retrieveError(Exception e, String message);
|
||||
}
|
||||
|
||||
public AvatarProvider(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void retrieve(AvatarsCallback avatarsCallback)
|
||||
{
|
||||
try {
|
||||
JSONObject obj = new JSONObject(loadJSONFromAssets());
|
||||
JSONArray m_jArry = obj.getJSONArray(JSON_FIELD_AVATARS_ARRAY);
|
||||
ArrayList<AvatarAdapter.Avatar> avatars = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < m_jArry.length(); i++) {
|
||||
JSONObject jo_inside = m_jArry.getJSONObject(i);
|
||||
AvatarAdapter.Avatar anAvatar = new AvatarAdapter.Avatar();
|
||||
anAvatar.avatarName = jo_inside.getString(JSON_FIELD_NAME);
|
||||
anAvatar.avatarPreviewUrl = jo_inside.getString(JSON_FIELD_IMAGE);
|
||||
anAvatar.avatarUrl = jo_inside.getString(JSON_FIELD_URL);
|
||||
avatars.add(anAvatar);
|
||||
}
|
||||
avatarsCallback.retrieveOk(avatars);
|
||||
} catch (IOException e) {
|
||||
avatarsCallback.retrieveError(e, "Failed retrieving avatar JSON");
|
||||
} catch (JSONException e) {
|
||||
avatarsCallback.retrieveError(e, "Failed parsing avatar JSON");
|
||||
}
|
||||
}
|
||||
|
||||
private String loadJSONFromAssets() throws IOException {
|
||||
String json = null;
|
||||
InputStream is = mContext.getAssets().open(AVATARS_JSON);
|
||||
int size = is.available();
|
||||
byte[] buffer = new byte[size];
|
||||
is.read(buffer);
|
||||
is.close();
|
||||
json = new String(buffer, "UTF-8");
|
||||
return json;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package io.highfidelity.hifiinterface.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.AvatarProvider;
|
||||
|
||||
/**
|
||||
* Created by gcalero on 1/21/19
|
||||
*/
|
||||
public class AvatarAdapter extends RecyclerView.Adapter<AvatarAdapter.ViewHolder> {
|
||||
|
||||
private static final String TAG = "Interface";
|
||||
private final Context mContext;
|
||||
private final LayoutInflater mInflater;
|
||||
private final AvatarProvider mProvider;
|
||||
private List<Avatar> mAvatars = new ArrayList<>();
|
||||
private ItemClickListener mClickListener;
|
||||
|
||||
public AvatarAdapter(Context context, AvatarProvider provider) {
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
mProvider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = mInflater.inflate(R.layout.avatar_item, parent, false);
|
||||
return new AvatarAdapter.ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
AvatarAdapter.Avatar anAvatar = mAvatars.get(position);
|
||||
assert(holder.mName != null);
|
||||
holder.mName.setText(anAvatar.avatarName);
|
||||
Uri uri = Uri.parse(anAvatar.avatarPreviewUrl);
|
||||
Picasso.get().load(uri).into(holder.mPreviewImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mAvatars.size();
|
||||
}
|
||||
|
||||
public void loadAvatars() {
|
||||
mProvider.retrieve(new AvatarProvider.AvatarsCallback() {
|
||||
@Override
|
||||
public void retrieveOk(List<AvatarAdapter.Avatar> avatars) {
|
||||
mAvatars = new ArrayList<>(avatars);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieveError(Exception e, String message) {
|
||||
Log.e(TAG, message, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setClickListener(ItemClickListener clickListener) {
|
||||
mClickListener = clickListener;
|
||||
}
|
||||
|
||||
public interface ItemClickListener {
|
||||
void onItemClick(View view, int position, Avatar avatar);
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
|
||||
TextView mName;
|
||||
ImageView mPreviewImage;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mName = itemView.findViewById(R.id.avatarName);
|
||||
assert (mName != null);
|
||||
mPreviewImage = itemView.findViewById(R.id.avatarPreview);
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int position= getAdapterPosition();
|
||||
if (mClickListener != null) {
|
||||
mClickListener.onItemClick(view, position, mAvatars.get(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Avatar {
|
||||
public String avatarName;
|
||||
public String avatarUrl;
|
||||
public String avatarPreviewUrl;
|
||||
|
||||
public Avatar() { }
|
||||
}
|
||||
}
|
23
android/apps/interface/src/main/res/layout/avatar_item.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp">
|
||||
<TextView
|
||||
android:id="@+id/avatarName"
|
||||
android:fontFamily="@font/raleway_bold"
|
||||
android:textSize="20sp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/colorButton1" />
|
||||
<ImageView
|
||||
android:id="@+id/avatarPreview"
|
||||
android:background="@color/backgroundDark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="190dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintTop_toBottomOf="@id/avatarName"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
107
android/apps/interface/src/main/res/layout/fragment_profile.xml
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/backgroundLight">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/encourage_login_background"
|
||||
android:scaleType="fitXY" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#B2000000" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/header"
|
||||
android:layout_width="@dimen/header_hifi_width"
|
||||
android:layout_height="@dimen/header_hifi_height"
|
||||
android:layout_marginTop="@dimen/header_hifi_margin_top"
|
||||
android:contentDescription="HighFidelity"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/hifi_header" />
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/profileForm"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="@dimen/profile_margin_top"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:visibility="visible">
|
||||
<EditText
|
||||
android:id="@+id/displayName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="27dp"
|
||||
android:layout_marginLeft="@dimen/profile_margin"
|
||||
android:layout_marginRight="@dimen/profile_margin"
|
||||
android:layout_marginBottom="@dimen/profile_margin"
|
||||
android:background="@color/white_opaque"
|
||||
android:paddingLeft="@dimen/edit_text_padding"
|
||||
android:ems="10"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textSize="@dimen/login_edit_text_size"
|
||||
android:inputType="textCapWords"
|
||||
android:textColor="@color/editTextColor"
|
||||
android:textColorHint="@color/editTextColor"
|
||||
android:gravity="left|center_vertical"
|
||||
android:hint="@string/displayName"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/displayName"
|
||||
app:layout_constraintBottom_toTopOf="@id/okButton"
|
||||
android:layout_marginTop="@dimen/profile_margin_avatars_top"
|
||||
android:layout_marginBottom="@dimen/profile_margin_avatars_bottom"
|
||||
android:layout_marginLeft="@dimen/profile_margin"
|
||||
android:layout_marginRight="@dimen/profile_margin">
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/gridview"
|
||||
android:paddingTop="@dimen/list_vertical_padding"
|
||||
android:paddingBottom="@dimen/list_vertical_padding"
|
||||
android:clipToPadding="false"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
</FrameLayout>
|
||||
<TextView
|
||||
android:id="@+id/cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
app:layout_constraintLeft_toLeftOf="@id/displayName"
|
||||
app:layout_constraintTop_toTopOf="@id/okButton"
|
||||
app:layout_constraintBottom_toBottomOf="@id/okButton"
|
||||
app:layout_constraintRight_toLeftOf="@id/okButton"
|
||||
android:textColor="@color/white_opaque"
|
||||
android:fontFamily="@font/raleway_bold"
|
||||
android:textSize="@dimen/button_medium_text_size"
|
||||
android:text="@string/cancel_uppercase" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/okButton"
|
||||
android:layout_width="@dimen/button_medium_width"
|
||||
android:layout_height="@dimen/button_medium_height"
|
||||
android:background="@drawable/rounded_button_color3"
|
||||
android:fontFamily="@font/raleway_bold"
|
||||
android:layout_marginTop="@dimen/button_medium_margin"
|
||||
android:layout_marginBottom="@dimen/button_medium_margin"
|
||||
android:text="@string/ok"
|
||||
android:textColor="@color/white_opaque"
|
||||
android:textAllCaps="false"
|
||||
android:textSize="@dimen/button_medium_text_size"
|
||||
app:layout_constraintRight_toRightOf="@id/displayName"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
|
@ -9,6 +9,10 @@
|
|||
android:id="@+id/action_people"
|
||||
android:title="@string/people"
|
||||
/>
|
||||
<item
|
||||
android:id="@+id/action_profile"
|
||||
android:title="@string/profile"
|
||||
/>
|
||||
<item
|
||||
android:id="@+id/action_debug_settings"
|
||||
android:title="@string/settings"
|
||||
|
|
|
@ -54,5 +54,8 @@
|
|||
<dimen name="login_menu_button_margin_top">77dp</dimen>
|
||||
<dimen name="login_menu_text_size">20sp</dimen>
|
||||
<dimen name="login_edit_text_padding">14dp</dimen>
|
||||
|
||||
<dimen name="profile_margin_top">20dp</dimen>
|
||||
<dimen name="profile_margin">35dp</dimen>
|
||||
<dimen name="profile_margin_avatars_top">40dp</dimen>
|
||||
<dimen name="profile_margin_avatars_bottom">70dp</dimen>
|
||||
</resources>
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
<string name="tagFragmentSignup">tagFragmentSignup</string>
|
||||
<string name="tagFragmentPolicy">tagFragmentPolicy</string>
|
||||
<string name="tagFragmentPeople">tagFragmentPeople</string>
|
||||
<string name="tagFragmentProfile">tagFragmentProfile</string>
|
||||
<string name="tagSettings">tagSettings</string>
|
||||
<string name="tagFragmentSignedIn">tagFragmentSignedIn</string>
|
||||
<string name="settings">Settings</string>
|
||||
|
@ -54,4 +55,7 @@
|
|||
<string name="take_me_in_world">No thanks, take me in-world!</string>
|
||||
<string name="be_anywere">BE ANYWHERE, WITH ANYONE \nRIGHT NOW</string>
|
||||
<string name="steam_log_in">STEAM LOG IN</string>
|
||||
<string name="displayName">Name to display</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="profile">Avatar</string>
|
||||
</resources>
|
||||
|
|
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()
|
||||
|
||||
|
43
android/apps/questFramePlayer/build.gradle
Normal file
|
@ -0,0 +1,43 @@
|
|||
import com.android.builder.core.BuilderConstants
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
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
|
||||
}
|
||||
|
||||
externalNativeBuild.cmake.path '../../../CMakeLists.txt'
|
||||
|
||||
variantFilter { variant ->
|
||||
def build = variant.buildType.name
|
||||
if (build == BuilderConstants.RELEASE) {
|
||||
variant.setIgnore(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
|
@ -7,15 +7,12 @@ docker build --build-arg BUILD_UID=`id -u` -t "${DOCKER_IMAGE_NAME}" -f docker/D
|
|||
|
||||
# The Jenkins PR builds use VERSION_CODE, but the release builds use VERSION
|
||||
# So make sure we use VERSION_CODE consistently
|
||||
if [-z "$VERSION_CODE"]; then
|
||||
export VERSION_CODE=$VERSION
|
||||
fi
|
||||
test -z "$VERSION_CODE" && export VERSION_CODE=$VERSION
|
||||
|
||||
docker run \
|
||||
--rm \
|
||||
--security-opt seccomp:unconfined \
|
||||
-v "${WORKSPACE}":/home/jenkins/hifi \
|
||||
-v /home/jenkins/.gradle:/home/jenkins/.gradle \
|
||||
-e RELEASE_NUMBER \
|
||||
-e RELEASE_TYPE \
|
||||
-e ANDROID_APP \
|
||||
|
|
|
@ -73,13 +73,10 @@ RUN mkdir "$HIFI_BASE" && \
|
|||
|
||||
RUN git clone https://github.com/jherico/hifi.git && \
|
||||
cd ~/hifi && \
|
||||
git checkout feature/quest_move_interface
|
||||
git checkout feature/quest_frame_player
|
||||
|
||||
WORKDIR /home/jenkins/hifi
|
||||
|
||||
RUN touch .test6 && \
|
||||
git fetch && git reset origin/feature/quest_move_interface --hard
|
||||
|
||||
RUN mkdir build
|
||||
|
||||
# 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
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
/*
|
||||
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);
|
||||
QtApplication.invokeDelegate();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -668,6 +650,8 @@ public class QtActivity extends Activity {
|
|||
if (!keepInterfaceRunning) {
|
||||
QtApplication.invokeDelegate();
|
||||
}
|
||||
QtNative.terminateQt();
|
||||
QtNative.setActivity(null,null);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
|
|
@ -1,5 +1,26 @@
|
|||
//
|
||||
// Libraries
|
||||
//
|
||||
|
||||
include ':oculus'
|
||||
project(':oculus').projectDir = new File(settingsDir, 'libraries/oculus')
|
||||
|
||||
include ':qt'
|
||||
project(':qt').projectDir = new File(settingsDir, 'libraries/qt')
|
||||
|
||||
//
|
||||
// Applications
|
||||
//
|
||||
|
||||
include ':interface'
|
||||
project(':interface').projectDir = new File(settingsDir, 'apps/interface')
|
||||
|
||||
//
|
||||
// Test projects
|
||||
//
|
||||
|
||||
include ':framePlayer'
|
||||
project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
|
||||
|
||||
include ':questFramePlayer'
|
||||
project(':questFramePlayer').projectDir = new File(settingsDir, 'apps/questFramePlayer')
|
||||
|
|
|
@ -743,7 +743,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
|
|||
float coefficient = coefficientObject.value(COEFFICIENT).toString().toFloat(&ok);
|
||||
|
||||
|
||||
if (ok && coefficient >= 0.0f && coefficient <= 1.0f &&
|
||||
if (ok && coefficient <= 1.0f &&
|
||||
itSource != end(_audioZones) &&
|
||||
itListener != end(_audioZones)) {
|
||||
|
||||
|
|
|
@ -770,13 +770,29 @@ float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNo
|
|||
break;
|
||||
}
|
||||
}
|
||||
// translate the zone setting to gain per log2(distance)
|
||||
float g = glm::clamp(1.0f - attenuationPerDoublingInDistance, EPSILON, 1.0f);
|
||||
|
||||
// calculate the attenuation using the distance to this node
|
||||
// reference attenuation of 0dB at distance = 1.0m
|
||||
gain *= fastExp2f(fastLog2f(g) * fastLog2f(std::max(distance, HRTF_NEARFIELD_MIN)));
|
||||
gain = std::min(gain, 1.0f / HRTF_NEARFIELD_MIN);
|
||||
if (attenuationPerDoublingInDistance < 0.0f) {
|
||||
// translate a negative zone setting to distance limit
|
||||
const float MIN_DISTANCE_LIMIT = ATTN_DISTANCE_REF + 1.0f; // silent after 1m
|
||||
float distanceLimit = std::max(-attenuationPerDoublingInDistance, MIN_DISTANCE_LIMIT);
|
||||
|
||||
// calculate the LINEAR attenuation using the distance to this node
|
||||
// reference attenuation of 0dB at distance = ATTN_DISTANCE_REF
|
||||
float d = distance - ATTN_DISTANCE_REF;
|
||||
gain *= std::max(1.0f - d / (distanceLimit - ATTN_DISTANCE_REF), 0.0f);
|
||||
gain = std::min(gain, ATTN_GAIN_MAX);
|
||||
|
||||
} else {
|
||||
// translate a positive zone setting to gain per log2(distance)
|
||||
const float MIN_ATTENUATION_COEFFICIENT = 0.001f; // -60dB per log2(distance)
|
||||
float g = glm::clamp(1.0f - attenuationPerDoublingInDistance, MIN_ATTENUATION_COEFFICIENT, 1.0f);
|
||||
|
||||
// calculate the LOGARITHMIC attenuation using the distance to this node
|
||||
// reference attenuation of 0dB at distance = ATTN_DISTANCE_REF
|
||||
float d = (1.0f / ATTN_DISTANCE_REF) * std::max(distance, HRTF_NEARFIELD_MIN);
|
||||
gain *= fastExp2f(fastLog2f(g) * fastLog2f(d));
|
||||
gain = std::min(gain, ATTN_GAIN_MAX);
|
||||
}
|
||||
|
||||
return gain;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,19 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
|
|||
// FIXME - what we'd actually like to do is send to users at ~50% of their present rate down to 30hz. Assume 90 for now.
|
||||
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
|
||||
|
||||
const QRegularExpression AvatarMixer::suffixedNamePattern { R"(^\s*(.+)\s*_(\d)+\s*$)" };
|
||||
|
||||
// Lexicographic comparison:
|
||||
bool AvatarMixer::SessionDisplayName::operator<(const SessionDisplayName& rhs) const {
|
||||
if (_baseName < rhs._baseName) {
|
||||
return true;
|
||||
} else if (rhs._baseName < _baseName) {
|
||||
return false;
|
||||
} else {
|
||||
return _suffix < rhs._suffix;
|
||||
}
|
||||
}
|
||||
|
||||
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message),
|
||||
_slavePool(&_slaveSharedData)
|
||||
|
@ -313,27 +326,40 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
|
|||
bool sendIdentity = false;
|
||||
if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) {
|
||||
AvatarData& avatar = nodeData->getAvatar();
|
||||
const QString& existingBaseDisplayName = nodeData->getBaseDisplayName();
|
||||
if (--_sessionDisplayNames[existingBaseDisplayName].second <= 0) {
|
||||
_sessionDisplayNames.remove(existingBaseDisplayName);
|
||||
const QString& existingBaseDisplayName = nodeData->getAvatar().getSessionDisplayName();
|
||||
if (!existingBaseDisplayName.isEmpty()) {
|
||||
SessionDisplayName existingDisplayName { existingBaseDisplayName };
|
||||
|
||||
auto suffixMatch = suffixedNamePattern.match(existingBaseDisplayName);
|
||||
if (suffixMatch.hasMatch()) {
|
||||
existingDisplayName._baseName = suffixMatch.captured(1);
|
||||
existingDisplayName._suffix = suffixMatch.captured(2).toInt();
|
||||
}
|
||||
_sessionDisplayNames.erase(existingDisplayName);
|
||||
}
|
||||
|
||||
QString baseName = avatar.getDisplayName().trimmed();
|
||||
const QRegularExpression curses { "fuck|shit|damn|cock|cunt" }; // POC. We may eventually want something much more elaborate (subscription?).
|
||||
baseName = baseName.replace(curses, "*"); // Replace rather than remove, so that people have a clue that the person's a jerk.
|
||||
const QRegularExpression trailingDigits { "\\s*(_\\d+\\s*)?(\\s*\\n[^$]*)?$" }; // trailing whitespace "_123" and any subsequent lines
|
||||
static const QRegularExpression trailingDigits { R"(\s*(_\d+\s*)?(\s*\n[^$]*)?$)" }; // trailing whitespace "_123" and any subsequent lines
|
||||
baseName = baseName.remove(trailingDigits);
|
||||
if (baseName.isEmpty()) {
|
||||
baseName = "anonymous";
|
||||
}
|
||||
|
||||
QPair<int, int>& soFar = _sessionDisplayNames[baseName]; // Inserts and answers 0, 0 if not already present, which is what we want.
|
||||
int& highWater = soFar.first;
|
||||
nodeData->setBaseDisplayName(baseName);
|
||||
QString sessionDisplayName = (highWater > 0) ? baseName + "_" + QString::number(highWater) : baseName;
|
||||
SessionDisplayName newDisplayName { baseName };
|
||||
auto nameIter = _sessionDisplayNames.lower_bound(newDisplayName);
|
||||
if (nameIter != _sessionDisplayNames.end() && nameIter->_baseName == baseName) {
|
||||
// Existing instance(s) of name; find first free suffix
|
||||
while (*nameIter == newDisplayName && ++newDisplayName._suffix && ++nameIter != _sessionDisplayNames.end())
|
||||
;
|
||||
}
|
||||
|
||||
_sessionDisplayNames.insert(newDisplayName);
|
||||
QString sessionDisplayName = (newDisplayName._suffix > 0) ? baseName + "_" + QString::number(newDisplayName._suffix) : baseName;
|
||||
avatar.setSessionDisplayName(sessionDisplayName);
|
||||
highWater++;
|
||||
soFar.second++; // refcount
|
||||
nodeData->setBaseDisplayName(baseName);
|
||||
|
||||
nodeData->flagIdentityChange();
|
||||
nodeData->setAvatarSessionDisplayNameMustChange(false);
|
||||
sendIdentity = true;
|
||||
|
@ -409,10 +435,19 @@ void AvatarMixer::handleAvatarKilled(SharedNodePointer avatarNode) {
|
|||
{ // decrement sessionDisplayNames table and possibly remove
|
||||
QMutexLocker nodeDataLocker(&avatarNode->getLinkedData()->getMutex());
|
||||
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
|
||||
const QString& baseDisplayName = nodeData->getBaseDisplayName();
|
||||
// No sense guarding against very rare case of a node with no entry, as this will work without the guard and do one less lookup in the common case.
|
||||
if (--_sessionDisplayNames[baseDisplayName].second <= 0) {
|
||||
_sessionDisplayNames.remove(baseDisplayName);
|
||||
const QString& displayName = nodeData->getAvatar().getSessionDisplayName();
|
||||
SessionDisplayName exitingDisplayName { displayName };
|
||||
|
||||
auto suffixMatch = suffixedNamePattern.match(displayName);
|
||||
if (suffixMatch.hasMatch()) {
|
||||
exitingDisplayName._baseName = suffixMatch.captured(1);
|
||||
exitingDisplayName._suffix = suffixMatch.captured(2).toInt();
|
||||
}
|
||||
auto displayNameIter = _sessionDisplayNames.find(exitingDisplayName);
|
||||
if (displayNameIter == _sessionDisplayNames.end()) {
|
||||
qCDebug(avatars) << "Exiting avatar displayname" << displayName << "not found";
|
||||
} else {
|
||||
_sessionDisplayNames.erase(displayNameIter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#ifndef hifi_AvatarMixer_h
|
||||
#define hifi_AvatarMixer_h
|
||||
|
||||
#include <set>
|
||||
#include <shared/RateCounter.h>
|
||||
#include <PortableHighResolutionClock.h>
|
||||
|
||||
|
@ -88,7 +89,24 @@ private:
|
|||
|
||||
RateCounter<> _broadcastRate;
|
||||
p_high_resolution_clock::time_point _lastDebugMessage;
|
||||
QHash<QString, QPair<int, int>> _sessionDisplayNames;
|
||||
|
||||
// Pair of basename + uniquifying integer suffix.
|
||||
struct SessionDisplayName {
|
||||
explicit SessionDisplayName(QString baseName = QString(), int suffix = 0) :
|
||||
_baseName(baseName),
|
||||
_suffix(suffix) { }
|
||||
// Does lexicographic ordering:
|
||||
bool operator<(const SessionDisplayName& rhs) const;
|
||||
bool operator==(const SessionDisplayName& rhs) const {
|
||||
return _baseName == rhs._baseName && _suffix == rhs._suffix;
|
||||
}
|
||||
|
||||
QString _baseName;
|
||||
int _suffix;
|
||||
};
|
||||
static const QRegularExpression suffixedNamePattern;
|
||||
|
||||
std::set<SessionDisplayName> _sessionDisplayNames;
|
||||
|
||||
quint64 _displayNameManagementElapsedTime { 0 }; // total time spent in broadcastAvatarData/display name management... since last stats window
|
||||
quint64 _ignoreCalculationElapsedTime { 0 };
|
||||
|
|
|
@ -111,7 +111,7 @@ bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
|
|||
int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
|
||||
|
||||
startNewTraversal(newView, root);
|
||||
startNewTraversal(newView, root, isFullScene);
|
||||
|
||||
// When the viewFrustum changed the sort order may be incorrect, so we re-sort
|
||||
// and also use the opportunity to cull anything no longer in view
|
||||
|
@ -220,9 +220,10 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil
|
|||
return hasNewChild || hasNewDescendants;
|
||||
}
|
||||
|
||||
void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
|
||||
void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root,
|
||||
bool forceFirstPass) {
|
||||
|
||||
DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root);
|
||||
DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, forceFirstPass);
|
||||
// there are three types of traversal:
|
||||
//
|
||||
// (1) FirstTime = at login --> find everything in view
|
||||
|
|
|
@ -42,7 +42,7 @@ private:
|
|||
bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
|
||||
bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
|
||||
|
||||
void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root);
|
||||
void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root, bool forceFirstPass = false);
|
||||
bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override;
|
||||
|
||||
void preDistributionProcessing() override;
|
||||
|
|
|
@ -112,7 +112,6 @@ void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<Rec
|
|||
|
||||
if (_entityViewer.getTree() && !_shuttingDown) {
|
||||
qCDebug(entity_script_server) << "Reloading: " << entityID;
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||
checkAndCallPreload(entityID, true);
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +183,6 @@ void EntityScriptServer::updateEntityPPS() {
|
|||
pps = std::min(_maxEntityPPS, pps);
|
||||
}
|
||||
_entityEditSender.setPacketsPerSecond(pps);
|
||||
qDebug() << QString("Updating entity PPS to: %1 @ %2 PPS per script = %3 PPS").arg(numRunningScripts).arg(_entityPPSPerScript).arg(pps);
|
||||
}
|
||||
|
||||
void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
|
@ -525,23 +523,25 @@ void EntityScriptServer::deletingEntity(const EntityItemID& entityID) {
|
|||
|
||||
void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, bool reload) {
|
||||
if (_entityViewer.getTree() && !_shuttingDown) {
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
checkAndCallPreload(entityID, reload);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool reload) {
|
||||
void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload) {
|
||||
if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) {
|
||||
|
||||
EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID);
|
||||
EntityScriptDetails details;
|
||||
bool notRunning = !_entitiesScriptEngine->getEntityScriptDetails(entityID, details);
|
||||
if (entity && (reload || notRunning || details.scriptText != entity->getServerScripts())) {
|
||||
bool isRunning = _entitiesScriptEngine->getEntityScriptDetails(entityID, details);
|
||||
if (entity && (forceRedownload || !isRunning || details.scriptText != entity->getServerScripts())) {
|
||||
if (isRunning) {
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
}
|
||||
|
||||
QString scriptUrl = entity->getServerScripts();
|
||||
if (!scriptUrl.isEmpty()) {
|
||||
scriptUrl = DependencyManager::get<ResourceManager>()->normalizeURL(scriptUrl);
|
||||
qCDebug(entity_script_server) << "Loading entity server script" << scriptUrl << "for" << entityID;
|
||||
_entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload);
|
||||
_entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, forceRedownload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ private:
|
|||
void addingEntity(const EntityItemID& entityID);
|
||||
void deletingEntity(const EntityItemID& entityID);
|
||||
void entityServerScriptChanging(const EntityItemID& entityID, bool reload);
|
||||
void checkAndCallPreload(const EntityItemID& entityID, bool reload = false);
|
||||
void checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload = false);
|
||||
|
||||
void cleanupOldKilledListeners();
|
||||
|
||||
|
|
36
cmake/macros/FixupNitpick.cmake
Normal file
|
@ -0,0 +1,36 @@
|
|||
#
|
||||
# FixupNitpick.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Copyright 2019 High Fidelity, Inc.
|
||||
# Created by Nissim Hadar on January 14th, 2016
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
macro(fixup_nitpick)
|
||||
if (APPLE)
|
||||
string(REPLACE " " "\\ " ESCAPED_BUNDLE_NAME ${NITPICK_BUNDLE_NAME})
|
||||
string(REPLACE " " "\\ " ESCAPED_INSTALL_PATH ${NITPICK_INSTALL_DIR})
|
||||
set(_NITPICK_INSTALL_PATH "${ESCAPED_INSTALL_PATH}/${ESCAPED_BUNDLE_NAME}.app")
|
||||
|
||||
find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS "${QT_DIR}/bin" NO_DEFAULT_PATH)
|
||||
|
||||
if (NOT MACDEPLOYQT_COMMAND AND (PRODUCTION_BUILD OR PR_BUILD))
|
||||
message(FATAL_ERROR "Could not find macdeployqt at ${QT_DIR}/bin.\
|
||||
It is required to produce a relocatable nitpick application.\
|
||||
Check that the environment variable QT_DIR points to your Qt installation.\
|
||||
")
|
||||
endif ()
|
||||
|
||||
install(CODE "
|
||||
execute_process(COMMAND ${MACDEPLOYQT_COMMAND}\
|
||||
\${CMAKE_INSTALL_PREFIX}/${_NITPICK_INSTALL_PATH}/\
|
||||
-verbose=2 -qmldir=${CMAKE_SOURCE_DIR}/interface/resources/qml/\
|
||||
)"
|
||||
COMPONENT ${CLIENT_COMPONENT}
|
||||
)
|
||||
|
||||
endif ()
|
||||
endmacro()
|
|
@ -10,5 +10,5 @@
|
|||
#
|
||||
|
||||
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)
|
|
@ -19,8 +19,8 @@ function(LINK_HIFI_LIBRARIES)
|
|||
endforeach()
|
||||
|
||||
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
|
||||
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
|
||||
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}")
|
||||
target_include_directories(${TARGET_NAME} PRIVATE "${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
|
||||
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
|
||||
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||
endforeach()
|
||||
|
|
|
@ -77,6 +77,9 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
add_definitions(-DDEV_BUILD)
|
||||
endif ()
|
||||
|
||||
set(NITPICK_BUNDLE_NAME "nitpick")
|
||||
set(NITPICK_ICON_PREFIX "nitpick")
|
||||
|
||||
string(TIMESTAMP BUILD_TIME "%d/%m/%Y")
|
||||
|
||||
# if STABLE_BUILD is 1, PRODUCTION_BUILD must be 1 and
|
||||
|
@ -140,8 +143,9 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
|
||||
set(DMG_SUBFOLDER_ICON "${HF_CMAKE_DIR}/installer/install-folder.rsrc")
|
||||
|
||||
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||
set(NITPICK_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||
|
||||
if (CLIENT_ONLY)
|
||||
set(CONSOLE_EXEC_NAME "Console.app")
|
||||
|
@ -159,11 +163,14 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
|
||||
set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app")
|
||||
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns")
|
||||
set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.icns")
|
||||
else ()
|
||||
if (WIN32)
|
||||
set(CONSOLE_INSTALL_DIR "server-console")
|
||||
set(NITPICK_INSTALL_DIR "nitpick")
|
||||
else ()
|
||||
set(CONSOLE_INSTALL_DIR ".")
|
||||
set(NITPICK_INSTALL_DIR ".")
|
||||
endif ()
|
||||
|
||||
set(COMPONENT_INSTALL_DIR ".")
|
||||
|
@ -173,6 +180,7 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
if (WIN32)
|
||||
set(INTERFACE_EXEC_PREFIX "interface")
|
||||
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.ico")
|
||||
set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.ico")
|
||||
|
||||
set(CONSOLE_EXEC_NAME "server-console.exe")
|
||||
|
||||
|
|
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()
|
|
@ -7,6 +7,7 @@
|
|||
#
|
||||
macro(TARGET_GLAD)
|
||||
if (ANDROID)
|
||||
include(SelectLibraryConfigurations)
|
||||
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/glad)
|
||||
set(GLAD_INCLUDE_DIRS "${INSTALL_DIR}/include")
|
||||
set(GLAD_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libglad_d.a)
|
||||
|
@ -31,8 +32,8 @@ macro(TARGET_GLAD)
|
|||
set(GLAD_INCLUDE_DIRS ${${GLAD_UPPER}_INCLUDE_DIRS})
|
||||
set(GLAD_LIBRARY ${${GLAD_UPPER}_LIBRARY})
|
||||
endif()
|
||||
|
||||
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLAD_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${GLAD_LIBRARY})
|
||||
target_link_libraries(${TARGET_NAME} ${GLAD_EXTRA_LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} ${GLAD_EXTRA_LIBRARIES})
|
||||
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()
|
|
@ -6,8 +6,13 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_ZLIB)
|
||||
# using VCPKG for zlib
|
||||
find_package(ZLIB REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
|
||||
if (ANDROID)
|
||||
# zlib is part of the NDK
|
||||
target_link_libraries(${TARGET_NAME} z)
|
||||
else()
|
||||
# using VCPKG for zlib
|
||||
find_package(ZLIB REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
endmacro()
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Source: nvtt
|
||||
Version: 8c7e6b40ee5095f227b75880fabd89c99d6f34c0
|
||||
Version: 330c4d56274a0f602a5c70596e2eb670a4ed56c2
|
||||
Description: Texture processing tools with support for Direct3D 10 and 11 formats.
|
|
@ -10,8 +10,8 @@ include(vcpkg_common_functions)
|
|||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO highfidelity/nvidia-texture-tools
|
||||
REF 8c7e6b40ee5095f227b75880fabd89c99d6f34c0
|
||||
SHA512 f107d19dbbd6651ef2126b1422a5db8db291bf70311ac4fb1dbacb5ceaa8752fee38becbd32964f57596f0b84e1223bb2c3ff9d9c4fdc65c3e77a47836657cef
|
||||
REF 330c4d56274a0f602a5c70596e2eb670a4ed56c2
|
||||
SHA512 4c0bc2f369120d696cc27710b6d33086b27eef55f537ec66b9a5c8b1839bc2426c0413670b0f65be52c5d353468f0126dfe024be1f0690611d4d7e33ac530127
|
||||
HEAD_REF master
|
||||
)
|
||||
|
||||
|
|
|
@ -300,6 +300,8 @@ Var substringResult
|
|||
SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags
|
||||
;Checking lowest bit:
|
||||
IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED}
|
||||
!insertmacro LoadVar ${SecName}_was_installed
|
||||
IntOp $AR_SecFlags $AR_SecFlags | $R0
|
||||
IntCmp $AR_SecFlags 1 "leave_${SecName}"
|
||||
;Section is not selected:
|
||||
|
||||
|
@ -478,18 +480,6 @@ Var GAClientID
|
|||
;--------------------------------
|
||||
; Installation types
|
||||
|
||||
Section "-Previous Install Cleanup"
|
||||
; Remove the resources folder so we don't end up including removed QML files
|
||||
RMDir /r "$INSTDIR\resources"
|
||||
|
||||
; delete old assignment-client and domain-server so they're no longer present
|
||||
; in client only installs.
|
||||
Delete "$INSTDIR\@DS_EXEC_NAME@"
|
||||
Delete "$INSTDIR\@AC_EXEC_NAME@"
|
||||
|
||||
; delete interface so it's not there for server-only installs
|
||||
Delete "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@"
|
||||
SectionEnd
|
||||
|
||||
@CPACK_NSIS_INSTALLATION_TYPES@
|
||||
|
||||
|
|
|
@ -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',
|
||||
'includeLibs': ['libvrapi.so']
|
||||
},
|
||||
'oculusPlatform': {
|
||||
'file': 'OVRPlatformSDK_v1.32.0.zip',
|
||||
'versionId': 'jG9DB16zOGxSrmtZy4jcQnwO0TJUuaeL',
|
||||
'checksum': 'ab5b203b3a39a56ab148d68fff769e05',
|
||||
'sharedLibFolder': 'Android/libs/arm64-v8a',
|
||||
'includeLibs': ['libovrplatformloader.so']
|
||||
},
|
||||
'openssl': {
|
||||
'file': 'openssl-1.1.0g_armv8.tgz',
|
||||
'versionId': 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW',
|
||||
|
|
|
@ -15,8 +15,8 @@ print = functools.partial(print, flush=True)
|
|||
# Encapsulates the vcpkg system
|
||||
class VcpkgRepo:
|
||||
CMAKE_TEMPLATE = """
|
||||
set(CMAKE_TOOLCHAIN_FILE "{}" CACHE FILEPATH "Toolchain file")
|
||||
set(CMAKE_TOOLCHAIN_FILE_UNCACHED "{}")
|
||||
get_filename_component(CMAKE_TOOLCHAIN_FILE "{}" ABSOLUTE CACHE)
|
||||
get_filename_component(CMAKE_TOOLCHAIN_FILE_UNCACHED "{}" ABSOLUTE)
|
||||
set(VCPKG_INSTALL_ROOT "{}")
|
||||
set(VCPKG_TOOLS_DIR "{}")
|
||||
"""
|
||||
|
|
|
@ -211,7 +211,10 @@ link_hifi_libraries(
|
|||
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
|
||||
controllers plugins image trackers
|
||||
ui-plugins display-plugins input-plugins
|
||||
# Platform specific GL libraries
|
||||
${PLATFORM_GL_BACKEND}
|
||||
# Plaform specific input & display plugin libraries
|
||||
${PLATFORM_PLUGIN_LIBRARIES}
|
||||
shaders
|
||||
)
|
||||
|
||||
|
@ -265,7 +268,7 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
|||
endforeach()
|
||||
|
||||
# include headers for interface and InterfaceConfig.
|
||||
include_directories("${PROJECT_SOURCE_DIR}/src")
|
||||
target_include_directories(${TARGET_NAME} PRIVATE "${PROJECT_SOURCE_DIR}/src")
|
||||
|
||||
if (ANDROID)
|
||||
find_library(ANDROID_LOG_LIB log)
|
||||
|
@ -330,7 +333,11 @@ if (APPLE)
|
|||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${PROJECT_SOURCE_DIR}/resources/fonts"
|
||||
"${RESOURCES_DEV_DIR}/fonts"
|
||||
# add redirect json to macOS builds.
|
||||
#copy serverless for android
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${PROJECT_SOURCE_DIR}/resources/serverless"
|
||||
"${RESOURCES_DEV_DIR}/serverless"
|
||||
# add redirect json to macOS builds.
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
|
||||
"${PROJECT_SOURCE_DIR}/resources/serverless/redirect.json"
|
||||
"${RESOURCES_DEV_DIR}/serverless/redirect.json"
|
||||
|
|
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 |