merge with master

This commit is contained in:
SamGondelman 2019-01-28 18:13:05 -08:00
commit 0d1e4bed66
198 changed files with 8878 additions and 4566 deletions
.gitignoreCMakeLists.txtINSTALL.md
android
assignment-client/src/audio
cmake
domain-server/src
interface
libraries

4
.gitignore vendored
View file

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

View file

@ -108,8 +108,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()

View file

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

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

View file

@ -0,0 +1,50 @@
apply plugin: 'com.android.application'
android {
signingConfigs {
release {
keyAlias 'key0'
keyPassword 'password'
storeFile file('C:/android/keystore.jks')
storePassword 'password'
}
}
compileSdkVersion 28
defaultConfig {
applicationId "io.highfidelity.frameplayer"
minSdkVersion 25
targetSdkVersion 28
ndk { abiFilters 'arm64-v8a' }
externalNativeBuild {
cmake {
arguments '-DHIFI_ANDROID=1',
'-DHIFI_ANDROID_APP=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
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
externalNativeBuild.cmake.path '../../../CMakeLists.txt'
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
//implementation project(':oculus')
implementation project(':qt')
}

View file

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

View file

@ -0,0 +1,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>

View file

@ -0,0 +1,6 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/">
<file>qml/main.qml</file>
</qresource>
</RCC>

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

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

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

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

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

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

View file

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

View file

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

View file

@ -3,3 +3,6 @@ project(':qt').projectDir = new File(settingsDir, 'libraries/qt')
include ':interface'
project(':interface').projectDir = new File(settingsDir, 'apps/interface')
//include ':framePlayer'
//project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')

View file

@ -68,6 +68,13 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
// hash the available codecs (on the mixer)
_availableCodecs.clear(); // Make sure struct is clean
auto pluginManager = DependencyManager::set<PluginManager>();
// Only load codec plugins; for now assume codec plugins have 'codec' in their name.
auto codecPluginFilter = [](const QJsonObject& metaData) {
QJsonValue nameValue = metaData["MetaData"]["name"];
return nameValue.toString().contains("codec", Qt::CaseInsensitive);
};
pluginManager->setPluginFilter(codecPluginFilter);
auto codecPlugins = pluginManager->getCodecPlugins();
for_each(codecPlugins.cbegin(), codecPlugins.cend(),
[&](const CodecPluginPointer& codec) {

View file

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

View file

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

View file

@ -1,3 +1,3 @@
Source: nvtt
Version: 8c7e6b40ee5095f227b75880fabd89c99d6f34c0
Version: 330c4d56274a0f602a5c70596e2eb670a4ed56c2
Description: Texture processing tools with support for Direct3D 10 and 11 formats.

View file

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

View file

@ -246,6 +246,7 @@ void AssetsBackupHandler::createBackup(const QString& backupName, QuaZip& zip) {
if (_assetServerEnabled && _lastMappingsRefresh.time_since_epoch().count() == 0) {
qCWarning(asset_backup) << "Current mappings not yet loaded.";
_backups.emplace_back(backupName, AssetUtils::Mappings(), true);
return;
}

View file

@ -54,7 +54,8 @@ FocusScope {
Image {
z: -10
id: loginDialogBackground
source: "LoginDialog/images/background.jpg"
fillMode: Image.PreserveAspectCrop
source: "LoginDialog/images/background.png"
anchors.fill: parent
}
@ -119,6 +120,6 @@ FocusScope {
}
Component.onCompleted: {
bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false });
bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false, "linkOculus": false });
}
}

View file

@ -22,11 +22,16 @@ Item {
width: root.width
height: root.height
readonly property string termsContainerText: qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service")
readonly property string termsContainerOculusText: qsTr("By signing up, you agree to High Fidelity's Terms of Service")
readonly property int textFieldHeight: 31
readonly property string fontFamily: "Raleway"
readonly property int fontSize: 15
readonly property bool fontBold: true
readonly property int textFieldFontSize: 18
readonly property var passwordImageRatio: 16 / 23
readonly property bool withSteam: withSteam
property bool withOculus: withOculus
property bool withSteam: withSteam
property string errorString: errorString
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
@ -61,15 +66,20 @@ Item {
Item {
id: contentItem
anchors.fill: parent
width: parent.width
height: errorContainer.height + fields.height + buttons.height + additionalTextContainer.height +
termsContainer.height
anchors.top: parent.top
anchors.topMargin: root.bannerHeight + 0.25 * parent.height
anchors.left: parent.left
Item {
id: errorContainer
width: parent.width
width: root.bannerWidth
height: loginErrorMessageTextMetrics.height
anchors {
bottom: buttons.top;
bottomMargin: hifi.dimensions.contentSpacing.y;
bottom: completeProfileBody.withOculus ? fields.top : buttons.top;
bottomMargin: 1.5 * hifi.dimensions.contentSpacing.y;
left: buttons.left;
}
TextMetrics {
@ -79,8 +89,8 @@ Item {
}
Text {
id: loginErrorMessage;
width: root.bannerWidth
color: "red";
width: root.bannerWidth;
font.family: completeProfileBody.fontFamily
font.pixelSize: 18
font.bold: completeProfileBody.fontBold
@ -88,13 +98,196 @@ Item {
horizontalAlignment: Text.AlignHCenter
text: completeProfileBody.errorString
visible: true
onTextChanged: {
mainContainer.recalculateErrorMessage();
}
Component.onCompleted: {
mainContainer.recalculateErrorMessage();
}
}
Component.onCompleted: {
if (loginErrorMessageTextMetrics.width > root.bannerWidth && root.isTablet) {
loginErrorMessage.wrapMode = Text.WordWrap;
loginErrorMessage.verticalAlignment = Text.AlignLeft;
loginErrorMessage.horizontalAlignment = Text.AlignLeft;
errorContainer.height = 3 * loginErrorMessageTextMetrics.height;
}
Item {
id: fields
width: root.bannerWidth
height: 3 * completeProfileBody.textFieldHeight + 2 * hifi.dimensions.contentSpacing.y
visible: completeProfileBody.withOculus
anchors {
left: parent.left
leftMargin: (parent.width - root.bannerWidth) / 2
bottom: buttons.top
bottomMargin: hifi.dimensions.contentSpacing.y
}
HifiControlsUit.TextField {
id: usernameField
width: root.bannerWidth
height: completeProfileBody.textFieldHeight
placeholderText: "Username"
font.pixelSize: completeProfileBody.textFieldFontSize
styleRenderType: Text.QtRendering
anchors {
top: parent.top
}
Keys.onPressed: {
if (!usernameField.visible) {
return;
}
switch (event.key) {
case Qt.Key_Tab:
event.accepted = true;
if (event.modifiers === Qt.ShiftModifier) {
passwordField.focus = true;
} else {
emailField.focus = true;
}
break;
case Qt.Key_Backtab:
event.accepted = true;
passwordField.focus = true;
break;
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true;
loginDialog.createAccountFromOculus(emailField.text, usernameField.text, passwordField.text);
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam,
"linkSteam": false, "withOculus": completeProfileBody.withOculus, "linkOculus": false, "createOculus": true });
break;
}
}
onFocusChanged: {
root.text = "";
if (focus) {
root.isPassword = false;
}
}
Component.onCompleted: {
var userID = "";
if (completeProfileBody.withOculus) {
userID = loginDialog.oculusUserID();
}
usernameField.text = userID;
}
}
HifiControlsUit.TextField {
id: emailField
width: root.bannerWidth
height: completeProfileBody.textFieldHeight
anchors {
top: usernameField.bottom
topMargin: hifi.dimensions.contentSpacing.y
}
placeholderText: "Email"
font.pixelSize: completeProfileBody.textFieldFontSize
styleRenderType: Text.QtRendering
activeFocusOnPress: true
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Tab:
event.accepted = true;
if (event.modifiers === Qt.ShiftModifier) {
usernameField.focus = true;
} else {
passwordField.focus = true;
}
break;
case Qt.Key_Backtab:
event.accepted = true;
usernameField.focus = true;
break;
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true;
loginDialog.createAccountFromOculus(emailField.text, usernameField.text, passwordField.text);
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam,
"linkSteam": false, "withOculus": completeProfileBody.withOculus, "linkOculus": false, "createOculus": true });
break;
}
}
onFocusChanged: {
root.text = "";
if (focus) {
root.isPassword = false;
}
}
}
HifiControlsUit.TextField {
id: passwordField
width: root.bannerWidth
height: completeProfileBody.textFieldHeight
placeholderText: "Password (optional)"
font.pixelSize: completeProfileBody.textFieldFontSize
styleRenderType: Text.QtRendering
activeFocusOnPress: true
echoMode: passwordFieldMouseArea.showPassword ? TextInput.Normal : TextInput.Password
anchors {
top: emailField.bottom
topMargin: hifi.dimensions.contentSpacing.y
}
onFocusChanged: {
root.text = "";
root.isPassword = focus;
}
Item {
id: showPasswordContainer
z: 10
// width + image's rightMargin
width: showPasswordImage.width + 8
height: parent.height
anchors {
right: parent.right
}
Image {
id: showPasswordImage
width: passwordField.height * passwordImageRatio
height: passwordField.height * passwordImageRatio
anchors {
right: parent.right
rightMargin: 8
top: parent.top
topMargin: passwordFieldMouseArea.showPassword ? 6 : 8
bottom: parent.bottom
bottomMargin: passwordFieldMouseArea.showPassword ? 5 : 8
}
source: passwordFieldMouseArea.showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg"
MouseArea {
id: passwordFieldMouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton
property bool showPassword: false
onClicked: {
showPassword = !showPassword;
}
}
}
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Tab:
event.accepted = true;
if (event.modifiers === Qt.ShiftModifier) {
emailField.focus = true;
} else if (usernameField.visible) {
usernameField.focus = true;
} else {
emailField.focus = true;
}
break;
case Qt.Key_Backtab:
event.accepted = true;
emailField.focus = true;
break;
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true;
loginDialog.createAccountFromOculus(emailField.text, usernameField.text, passwordField.text);
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam,
"linkSteam": false, "withOculus": completeProfileBody.withOculus, "linkOculus": false, "createOculus": true });
break;
}
}
}
}
@ -105,7 +298,7 @@ Item {
height: d.minHeightButton
anchors {
top: parent.top
topMargin: (parent.height - additionalTextContainer.height) / 2 - hifi.dimensions.contentSpacing.y
topMargin: (parent.height - additionalTextContainer.height + fields.height) / 2 - hifi.dimensions.contentSpacing.y
left: parent.left
leftMargin: (parent.width - root.bannerWidth) / 2
}
@ -144,7 +337,7 @@ Item {
width: (parent.width - hifi.dimensions.contentSpacing.x) / 2
height: d.minHeightButton
text: qsTr("Create your profile")
text: completeProfileBody.withOculus ? qsTr("Sign Up") : qsTr("Create your profile")
color: hifi.buttons.blue
fontFamily: completeProfileBody.fontFamily
@ -158,55 +351,12 @@ Item {
UserActivityLogger.logAction("encourageLoginDialog", data);
}
loginErrorMessage.visible = false;
loginDialog.createAccountFromSteam();
}
}
}
Item {
id: additionalTextContainer
width: parent.width
height: additionalTextMetrics.height
anchors {
top: buttons.bottom
horizontalCenter: parent.horizontalCenter
topMargin: hifi.dimensions.contentSpacing.y
left: parent.left
}
TextMetrics {
id: additionalTextMetrics
font: additionalText.font
text: "Already have a High Fidelity profile? Link to an existing profile here."
}
HifiStylesUit.ShortcutText {
id: additionalText
text: "<a href='https://fake.link'>Already have a High Fidelity profile? Link to an existing profile here.</a>"
font.family: completeProfileBody.fontFamily
font.pixelSize: completeProfileBody.fontSize
font.bold: completeProfileBody.fontBold
wrapMode: Text.NoWrap
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
linkColor: hifi.colors.blueAccent
onLinkActivated: {
loginDialog.isLogIn = true;
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": "", "withSteam": true, "linkSteam": true });
}
Component.onCompleted: {
if (additionalTextMetrics.width > root.bannerWidth && root.isTablet) {
additionalText.width = root.bannerWidth;
additionalText.wrapMode = Text.WordWrap;
additionalText.verticalAlignment = Text.AlignLeft;
additionalText.horizontalAlignment = Text.AlignLeft;
additionalTextContainer.height = (additionalTextMetrics.width / root.bannerWidth) * additionalTextMetrics.height;
additionalTextContainer.anchors.left = buttons.left;
} else {
additionalText.anchors.centerIn = additionalTextContainer;
if (completeProfileBody.withOculus) {
loginDialog.createAccountFromOculus(emailField.text, usernameField.text, passwordField.text);
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam,
"linkSteam": false, "withOculus": completeProfileBody.withOculus, "linkOculus": false, "createOculus": true });
} else if (completeProfileBody.withSteam) {
loginDialog.createAccountFromSteam();
}
}
}
@ -217,29 +367,33 @@ Item {
width: parent.width
height: termsTextMetrics.height
anchors {
top: additionalTextContainer.bottom
top: buttons.bottom
horizontalCenter: parent.horizontalCenter
topMargin: 2 * hifi.dimensions.contentSpacing.y
topMargin: hifi.dimensions.contentSpacing.y
left: parent.left
}
TextMetrics {
id: termsTextMetrics
font: termsText.font
text: completeProfileBody.termsContainerText
text: completeProfileBody.withOculus ? completeProfileBody.termsContainerOculusText : completeProfileBody.termsContainerText
Component.onCompleted: {
// with the link.
termsText.text = qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
if (completeProfileBody.withOculus) {
termsText.text = qsTr("By signing up, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
} else {
termsText.text = qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
}
}
}
HifiStylesUit.InfoItem {
id: termsText
text: completeProfileBody.termsContainerText
text: completeProfileBody.withOculus ? completeProfileBody.termsContainerOculusText : completeProfileBody.termsContainerText
font.family: completeProfileBody.fontFamily
font.pixelSize: completeProfileBody.fontSize
font.bold: completeProfileBody.fontBold
wrapMode: Text.WordWrap
color: hifi.colors.lightGray
color: hifi.colors.white
linkColor: hifi.colors.blueAccent
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
@ -247,7 +401,7 @@ Item {
onLinkActivated: loginDialog.openUrl(link);
Component.onCompleted: {
if (termsTextMetrics.width > root.bannerWidth && root.isTablet) {
if (termsTextMetrics.width > root.bannerWidth) {
termsText.width = root.bannerWidth;
termsText.wrapMode = Text.WordWrap;
additionalText.verticalAlignment = Text.AlignLeft;
@ -260,14 +414,86 @@ Item {
}
}
}
Item {
id: additionalTextContainer
width: parent.width
height: additionalTextMetrics.height
anchors {
top: termsContainer.bottom
horizontalCenter: parent.horizontalCenter
topMargin: 2 * hifi.dimensions.contentSpacing.y
left: parent.left
}
TextMetrics {
id: additionalTextMetrics
font: additionalText.font
text: "Already have a High Fidelity profile? Link to an existing profile here."
}
HifiStylesUit.ShortcutText {
id: additionalText
text: "<a href='https://fake.link'>Already have a High Fidelity profile? Link to an existing profile here.</a>"
width: root.bannerWidth;
font.family: completeProfileBody.fontFamily
font.pixelSize: completeProfileBody.fontSize
font.bold: completeProfileBody.fontBold
wrapMode: Text.NoWrap
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
linkColor: hifi.colors.blueAccent
onLinkActivated: {
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": "",
"withSteam": completeProfileBody.withSteam, "linkSteam": completeProfileBody.withSteam, "withOculus": completeProfileBody.withOculus,
"linkOculus": completeProfileBody.withOculus });
}
Component.onCompleted: {
if (additionalTextMetrics.width > root.bannerWidth) {
additionalText.wrapMode = Text.WordWrap;
additionalText.verticalAlignment = Text.AlignLeft;
additionalText.horizontalAlignment = Text.AlignLeft;
additionalTextContainer.height = (additionalTextMetrics.width / root.bannerWidth) * additionalTextMetrics.height;
additionalTextContainer.anchors.left = buttons.left;
} else {
additionalText.anchors.centerIn = additionalTextContainer;
}
}
}
}
}
function recalculateErrorMessage() {
if (completeProfileBody.errorString !== "") {
loginErrorMessage.visible = true;
var errorLength = completeProfileBody.errorString.split(/\r\n|\r|\n/).length;
var errorStringEdited = completeProfileBody.errorString.replace(/[\n\r]+/g, "\n");
loginErrorMessage.text = errorStringEdited;
if (errorLength > 1.0) {
loginErrorMessage.wrapMode = Text.WordWrap;
loginErrorMessage.verticalAlignment = Text.AlignLeft;
loginErrorMessage.horizontalAlignment = Text.AlignLeft;
errorContainer.height = errorLength * loginErrorMessageTextMetrics.height;
} else if (loginErrorMessageTextMetrics.width > root.bannerWidth) {
loginErrorMessage.wrapMode = Text.WordWrap;
loginErrorMessage.verticalAlignment = Text.AlignLeft;
loginErrorMessage.horizontalAlignment = Text.AlignLeft;
errorContainer.height = (loginErrorMessageTextMetrics.width / root.bannerWidth) * loginErrorMessageTextMetrics.height;
} else {
loginErrorMessage.wrapMode = Text.NoWrap;
loginErrorMessage.verticalAlignment = Text.AlignVCenter;
loginErrorMessage.horizontalAlignment = Text.AlignHCenter;
errorContainer.height = loginErrorMessageTextMetrics.height;
}
}
}
}
Connections {
target: loginDialog
onHandleCreateCompleted: {
console.log("Create Succeeded")
console.log("Create Succeeded");
if (completeProfileBody.withSteam) {
if (completeProfileBody.loginDialogPoppedUp) {
var data = {
@ -277,20 +503,24 @@ Item {
}
loginDialog.loginThroughSteam();
}
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam, "linkSteam": false });
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam, "linkSteam": false,
"withOculus": completeProfileBody.withOculus, "linkOculus": false });
}
onHandleCreateFailed: {
console.log("Create Failed: " + error);
if (completeProfileBody.withSteam) {
if (completeProfileBody.withSteam || completeProfileBody.withOculus) {
if (completeProfileBody.loginDialogPoppedUp) {
action = completeProfileBody.withSteam ? "Steam" : "Oculus";
var data = {
"action": "user failed to create a profile with Steam from the complete profile screen"
"action": "user failed to create a profile with " + action + " from the complete profile screen"
}
UserActivityLogger.logAction("encourageLoginDialog", data);
}
}
bodyLoader.setSource("UsernameCollisionBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam });
if (!completeProfileBody.withOculus) {
bodyLoader.setSource("UsernameCollisionBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": completeProfileBody.withSteam,
"withOculus": completeProfileBody.withOculus });
}
}
}
@ -302,5 +532,6 @@ Item {
}
d.resize();
root.text = "";
usernameField.forceActiveFocus();
}
}

View file

@ -36,9 +36,10 @@ Item {
property bool keyboardRaised: false
property bool punctuationMode: false
property bool withSteam: false
property bool withSteam: withSteam
property bool linkSteam: linkSteam
property bool withOculus: false
property bool withOculus: withOculus
property bool linkOculus: linkOculus
property string errorString: errorString
property bool lostFocus: false
@ -83,23 +84,24 @@ Item {
}
UserActivityLogger.logAction("encourageLoginDialog", data);
}
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam, "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam });
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam,
"withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam, "linkOculus": linkAccountBody.linkOculus });
}
function init() {
// going to/from sign in/up dialog.
loginDialog.isLogIn = true;
loginErrorMessage.text = linkAccountBody.errorString;
loginErrorMessage.visible = (linkAccountBody.errorString !== "");
loginButton.text = !linkAccountBody.linkSteam ? "Log In" : "Link Account";
if (loginErrorMessageTextMetrics.width > emailField.width) {
loginErrorMessage.wrapMode = Text.WordWrap;
errorContainer.height = (loginErrorMessageTextMetrics.width / emailField.width) * loginErrorMessageTextMetrics.height;
}
loginButton.text = (!linkAccountBody.linkSteam && !linkAccountBody.linkOculus) ? "Log In" : "Link Account";
loginButton.color = hifi.buttons.blue;
emailField.placeholderText = "Username or Email";
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : "";
if (linkAccountBody.linkSteam) {
steamInfoText.anchors.top = passwordField.bottom;
keepMeLoggedInCheckbox.anchors.top = steamInfoText.bottom;
if (linkAccountBody.linkSteam || linkAccountBody.linkOculus) {
loginButton.width = (passwordField.width - hifi.dimensions.contentSpacing.x) / 2;
loginButton.anchors.right = emailField.right;
} else {
@ -125,7 +127,7 @@ Item {
id: loginContainer
width: emailField.width
height: errorContainer.height + emailField.height + passwordField.height + 5.5 * hifi.dimensions.contentSpacing.y +
keepMeLoggedInCheckbox.height + loginButton.height + cantAccessTextMetrics.height + continueButton.height + steamInfoTextMetrics.height
keepMeLoggedInCheckbox.height + loginButton.height + cantAccessTextMetrics.height + continueButton.height
anchors {
top: parent.top
topMargin: root.bannerHeight + 0.25 * parent.height
@ -135,7 +137,7 @@ Item {
Item {
id: errorContainer
width: loginErrorMessageTextMetrics.width
width: parent.width
height: loginErrorMessageTextMetrics.height
anchors {
bottom: emailField.top;
@ -304,7 +306,7 @@ Item {
fontSize: linkAccountBody.fontSize
fontBold: linkAccountBody.fontBold
color: hifi.buttons.noneBorderlessWhite;
visible: linkAccountBody.linkSteam
visible: linkAccountBody.linkSteam || linkAccountBody.linkOculus
anchors {
top: keepMeLoggedInCheckbox.bottom
topMargin: hifi.dimensions.contentSpacing.y
@ -315,10 +317,9 @@ Item {
"action": "user clicked cancel at link account screen"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
loginDialog.dismissLoginDialog();
}
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam, "errorString": "" });
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam,
"withOculus": linkAccountBody.withOculus, "errorString": "" });
}
}
HifiControlsUit.Button {
@ -337,33 +338,6 @@ Item {
linkAccountBody.login();
}
}
TextMetrics {
id: steamInfoTextMetrics
font: steamInfoText.font
text: steamInfoText.text
}
Text {
id: steamInfoText
width: root.bannerWidth
visible: linkAccountBody.linkSteam
anchors {
top: loginButton.bottom
topMargin: hifi.dimensions.contentSpacing.y
left: emailField.left
}
font.family: linkAccountBody.fontFamily
font.pixelSize: linkAccountBody.textFieldFontSize
color: "white"
text: qsTr("Your Steam account information will not be exposed to others.");
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
Component.onCompleted: {
if (steamInfoTextMetrics.width > root.bannerWidth) {
steamInfoText.wrapMode = Text.WordWrap;
}
}
}
TextMetrics {
id: cantAccessTextMetrics
font: cantAccessText.font
@ -372,7 +346,7 @@ Item {
HifiStylesUit.ShortcutText {
id: cantAccessText
z: 10
visible: !linkAccountBody.linkSteam
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus
anchors {
top: loginButton.bottom
topMargin: hifi.dimensions.contentSpacing.y
@ -423,10 +397,10 @@ Item {
buttonGlyphSize: 24
buttonGlyphRightMargin: 10
onClicked: {
// if (loginDialog.isOculusStoreRunning()) {
// linkAccountBody.withOculus = true;
// loginDialog.loginThroughSteam();
// } else
if (loginDialog.isOculusRunning()) {
linkAccountBody.withOculus = true;
loginDialog.loginThroughOculus();
} else
if (loginDialog.isSteamRunning()) {
linkAccountBody.withSteam = true;
loginDialog.loginThroughSteam();
@ -446,18 +420,17 @@ Item {
}
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader,
"withSteam": linkAccountBody.withSteam, "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam });
"withSteam": linkAccountBody.withSteam, "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam, "linkOculus": linkAccountBody.linkOculus });
}
Component.onCompleted: {
if (linkAccountBody.linkSteam) {
if (linkAccountBody.linkSteam || linkAccountBody.linkOculus) {
continueButton.visible = false;
return;
}
// if (loginDialog.isOculusStoreRunning()) {
// continueButton.text = qsTr("CONTINUE WITH OCULUS");
// continueButton.buttonGlyph = hifi.glyphs.oculus;
// } else
if (loginDialog.isSteamRunning()) {
if (loginDialog.isOculusRunning()) {
continueButton.text = qsTr("CONTINUE WITH OCULUS");
continueButton.buttonGlyph = hifi.glyphs.oculus;
} else if (loginDialog.isSteamRunning()) {
continueButton.text = qsTr("CONTINUE WITH STEAM");
continueButton.buttonGlyph = hifi.glyphs.steamSquare;
} else {
@ -470,7 +443,7 @@ Item {
id: signUpContainer
width: loginContainer.width
height: signUpTextMetrics.height
visible: !linkAccountBody.linkSteam
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus
anchors {
left: loginContainer.left
top: loginContainer.bottom
@ -519,7 +492,7 @@ Item {
UserActivityLogger.logAction("encourageLoginDialog", data);
}
bodyLoader.setSource("SignUpBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader,
"errorString": "", "linkSteam": linkAccountBody.linkSteam });
"errorString": "" });
}
}
}
@ -543,7 +516,7 @@ Item {
fontFamily: linkAccountBody.fontFamily
fontSize: linkAccountBody.fontSize
fontBold: linkAccountBody.fontBold
visible: linkAccountBody.loginDialogPoppedUp && !linkAccountBody.linkSteam;
visible: loginDialog.getLoginDialogPoppedUp() && !linkAccountBody.linkSteam && !linkAccountBody.linkOculus;
onClicked: {
if (linkAccountBody.loginDialogPoppedUp) {
var data = {

View file

@ -29,6 +29,8 @@ Item {
property bool withSteam: withSteam
property bool withOculus: withOculus
property bool linkSteam: linkSteam
property bool linkOculus: linkOculus
property bool createOculus: createOculus
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
@ -75,15 +77,25 @@ Item {
}
}
Timer {
id: oculusSuccessTimer
interval: 500;
running: false;
repeat: false;
onTriggered: {
loginDialog.loginThroughOculus();
init();
}
}
function init() {
// For the process of logging in.
loggingInText.wrapMode = Text.NoWrap;
if (loggingInBody.linkSteam) {
if (loggingInBody.createOculus) {
loggingInGlyph.text = hifi.glyphs.oculus;
loggingInGlyph.visible = true;
loggingInText.text = "Linking to Steam";
loggingInText.text = "Creating account with Oculus";
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
loginDialog.linkSteam();
} else if (loggingInBody.withSteam) {
loggingInGlyph.visible = true;
loggingInText.text = "Logging in to Steam";
@ -100,12 +112,18 @@ Item {
loggingInSpinner.visible = true;
}
function loadingSuccess() {
loggingInSpinner.visible = false;
if (loggingInBody.linkSteam) {
loggingInText.text = "Linking to Steam";
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
loginDialog.linkSteam();
return;
} else if (loggingInBody.linkOculus) {
loggingInText.text = "Linking to Oculus";
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
loginDialog.linkOculus();
return;
}
loggingInSpinner.visible = false;
if (loggingInBody.withSteam) {
// reset the flag.
loggingInGlyph.visible = false;
@ -246,6 +264,26 @@ Item {
verticalAlignment: Text.AlignVCenter;
visible: false;
}
HifiControlsUit.Button {
id: okButton;
width: d.minWidthButton
height: d.minHeightButton
text: qsTr("OK")
color: hifi.buttons.white
anchors {
top: loggedInGlyph.bottom
topMargin: 3 * hifi.dimensions.contentSpacing.y
left: parent.left
leftMargin: (parent.width - width) / 2;
}
onClicked: {
root.tryDestroy();
if (loginDialog.getLoginDialogPoppedUp()) {
loginDialog.dismissLoginDialog();
}
}
visible: false
}
}
}
}
@ -257,6 +295,34 @@ Item {
Connections {
target: loginDialog
onHandleCreateCompleted: {
console.log("Create Succeeded")
if (loggingInBody.withOculus) {
if (loggingInBody.loginDialogPoppedUp) {
var data = {
"action": "user created Oculus account successfully"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
loggingInBody.createOculus = false;
loggingInText.text = "Account created!";
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
oculusSuccessTimer.start();
}
}
onHandleCreateFailed: {
console.log("Create Failed: " + error);
if (loggingInBody.withOculus) {
if (loggingInBody.loginDialogPoppedUp) {
var data = {
"action": "user created Oculus account unsuccessfully"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam,
"withOculus": loggingInBody.withOculus, "errorString": error });
}
}
onHandleLinkCompleted: {
console.log("Link Succeeded");
if (loggingInBody.linkSteam) {
@ -267,21 +333,40 @@ Item {
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
loggingInBody.loadingSuccess();
} else if (loggingInBody.linkOculus) {
loggingInBody.linkOculus = false;
if (loggingInBody.loginDialogPoppedUp) {
var data = {
"action": "user linked Oculus with their hifi account credentials successfully"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
}
loggingInBody.loadingSuccess();
}
onHandleLinkFailed: {
console.log("Link Failed: " + error);
if (loggingInBody.linkSteam) {
loggingInSpinner.visible = false;
if (loggingInBody.linkOculus) {
loggingInText.text = "Oculus failed to link";
if (loggingInBody.loginDialogPoppedUp) {
var data = {
"action": "user linked Oculus unsuccessfully"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
okButton.visible = true;
} else if (loggingInBody.linkSteam){
if (loggingInBody.loginDialogPoppedUp) {
var data = {
"action": "user linked Steam unsuccessfully"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
} else {
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": loggingInBody.linkSteam,
"linkOculus": loggingInBody.linkOculus, "errorString": error });
}
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": true, "errorString": error });
}
onHandleLoginCompleted: {
@ -292,8 +377,19 @@ Item {
onHandleLoginFailed: {
console.log("Login Failed")
loggingInSpinner.visible = false;
loggingInGlyph.visible = false;
var errorString = "";
if (loggingInBody.linkSteam && loggingInBody.withSteam) {
if (loggingInBody.linkOculus && loggingInBody.withOculus) {
errorString = "Username or password is incorrect.";
if (loggingInBody.loginDialogPoppedUp) {
var data = {
"action": "user failed to link Oculus with their hifi account credentials"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam,
"withOculus": loggingInBody.withOculus, "linkSteam": loggingInBody.linkSteam, "linkOculus": loggingInBody.linkOculus, "errorString": errorString });
} else if (loggingInBody.linkSteam && loggingInBody.withSteam) {
errorString = "Username or password is incorrect.";
if (loggingInBody.loginDialogPoppedUp) {
var data = {
@ -301,9 +397,9 @@ Item {
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam, "linkSteam": loggingInBody.linkSteam, "errorString": errorString });
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam,
"withOculus": loggingInBody.withOculus, "linkSteam": loggingInBody.linkSteam, "linkOculus": loggingInBody.linkOculus, "errorString": errorString });
} else if (loggingInBody.withSteam) {
loggingInGlyph.visible = false;
errorString = "Your Steam authentication has failed. Please make sure you are logged into Steam and try again.";
if (loggingInBody.loginDialogPoppedUp) {
var data = {
@ -311,19 +407,19 @@ Item {
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam, "errorString": errorString });
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam,
"withOculus": loggingInBody.withOculus, "linkSteam": loggingInBody.linkSteam, "linkOculus": loggingInBody.linkOculus, "errorString": errorString });
} else if (loggingInBody.withOculus) {
loggingInGlyph.visible = false;
errorString = "Your Oculus authentication has failed. Please make sure you are logged into Oculus and try again."
errorString = "Your Oculus account is not connected to an existing High Fidelity account. Please create a new one."
if (loggingInBody.loginDialogPoppedUp) {
var data = {
"action": "user failed to authenticate with Oculus to log in"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": errorString });
}
else {
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": loggingInBody.withSteam,
"withOculus": loggingInBody.withOculus, "linkSteam": loggingInBody.linkSteam, "linkOculus": loggingInBody.linkOculus, "errorString": errorString });
} else {
errorString = "Username or password is incorrect.";
if (loggingInBody.loginDialogPoppedUp) {
var data = {

View file

@ -23,6 +23,7 @@ Item {
clip: true
height: root.height
width: root.width
readonly property string termsContainerText: qsTr("By signing up, you agree to High Fidelity's Terms of Service")
property int textFieldHeight: 31
property string fontFamily: "Raleway"
property int fontSize: 15
@ -37,7 +38,6 @@ Item {
onKeyboardRaisedChanged: d.resize();
property string errorString: errorString
property bool linkSteam: linkSteam
property bool lostFocus: false
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
@ -73,7 +73,6 @@ Item {
function init() {
// going to/from sign in/up dialog.
loginDialog.isLogIn = false;
emailField.placeholderText = "Email";
emailField.text = "";
emailField.anchors.top = usernameField.bottom;
@ -353,7 +352,7 @@ Item {
}
UserActivityLogger.logAction("encourageLoginDialog", data);
}
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": signUpBody.linkSteam });
bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false });
}
}
HifiControlsUit.Button {
@ -380,6 +379,54 @@ Item {
signUpBody.signup();
}
}
Item {
id: termsContainer
width: parent.width
height: termsTextMetrics.height
anchors {
top: signUpButton.bottom
horizontalCenter: parent.horizontalCenter
topMargin: 2 * hifi.dimensions.contentSpacing.y
left: parent.left
}
TextMetrics {
id: termsTextMetrics
font: termsText.font
text: signUpBody.termsContainerText
Component.onCompleted: {
// with the link.
termsText.text = qsTr("By signing up, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
}
}
HifiStylesUit.InfoItem {
id: termsText
text: signUpBody.termsContainerText
font.family: signUpBody.fontFamily
font.pixelSize: signUpBody.fontSize
font.bold: signUpBody.fontBold
wrapMode: Text.WordWrap
color: hifi.colors.white
linkColor: hifi.colors.blueAccent
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
onLinkActivated: loginDialog.openUrl(link);
Component.onCompleted: {
if (termsTextMetrics.width > root.bannerWidth) {
termsText.width = root.bannerWidth;
termsText.wrapMode = Text.WordWrap;
additionalText.verticalAlignment = Text.AlignLeft;
additionalText.horizontalAlignment = Text.AlignLeft;
termsContainer.height = (termsTextMetrics.width / root.bannerWidth) * termsTextMetrics.height;
termsContainer.anchors.left = buttons.left;
} else {
termsText.anchors.centerIn = termsContainer;
}
}
}
}
}
}
@ -433,14 +480,15 @@ Item {
if (errorString !== "") {
loginErrorMessage.visible = true;
var errorLength = errorString.split(/\r\n|\r|\n/).length;
var errorStringEdited = errorString.replace(/[\n\r]+/g, "\n");
loginErrorMessage.text = errorStringEdited;
loginErrorMessageTextMetrics.text = errorString;
if (loginErrorMessageTextMetrics.width > usernameField.width) {
if (errorLength > 1.0) {
loginErrorMessage.width = root.bannerWidth;
loginErrorMessage.wrapMode = Text.WordWrap;
loginErrorMessage.verticalAlignment = Text.AlignLeft;
loginErrorMessage.horizontalAlignment = Text.AlignLeft;
errorContainer.height = (loginErrorMessageTextMetrics.width / usernameField.width) * loginErrorMessageTextMetrics.height;
errorContainer.height = errorLength * loginErrorMessageTextMetrics.height;
}
errorContainer.anchors.bottom = usernameField.top;
errorContainer.anchors.bottomMargin = hifi.dimensions.contentSpacing.y;

View file

@ -19,6 +19,7 @@ import TabletScriptingInterface 1.0
Item {
id: usernameCollisionBody
clip: true
readonly property string termsContainerText: qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service")
width: root.width
height: root.height
readonly property string fontFamily: "Raleway"
@ -26,13 +27,18 @@ Item {
readonly property int textFieldFontSize: 18
readonly property bool fontBold: true
readonly property bool withSteam: withSteam
property bool withSteam: withSteam
property bool withOculus: withOculus
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
function create() {
mainTextContainer.visible = false
loginDialog.createAccountFromSteam(textField.text);
if (usernameCollisionBody.withOculus) {
loginDialog.createAccountFromOculus(textField.text);
} else if (usernameCollisionBody.withSteam) {
loginDialog.createAccountFromSteam(textField.text);
}
}
property bool keyboardEnabled: false
@ -90,12 +96,19 @@ Item {
font.family: usernameCollisionBody.fontFamily
font.pixelSize: usernameCollisionBody.fontSize
font.bold: usernameCollisionBody.fontBold
text: qsTr("Your Steam username is not available.");
text: qsTr("");
wrapMode: Text.WordWrap
color: hifi.colors.redAccent
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
Component.onCompleted: {
if (usernameCollisionBody.withOculus) {
text = qsTr("Your Oculus username is not available.");
} else if (usernameCollisionBody.withSteam) {
text = qsTr("Your Steam username is not available.");
}
}
}
@ -164,7 +177,8 @@ Item {
fontSize: usernameCollisionBody.fontSize
fontBold: usernameCollisionBody.fontBold
onClicked: {
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": "" });
bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": usernameCollisionBody.withSteam,
"withOculus": usernameCollisionBody.withOculus, "errorString": "" });
}
}
HifiControlsUit.Button {
@ -187,6 +201,55 @@ Item {
}
}
}
Item {
id: termsContainer
width: parent.width
height: termsTextMetrics.height
anchors {
top: buttons.bottom
horizontalCenter: parent.horizontalCenter
topMargin: 2 * hifi.dimensions.contentSpacing.y
left: parent.left
leftMargin: (parent.width - buttons.width) / 2
}
TextMetrics {
id: termsTextMetrics
font: termsText.font
text: usernameCollisionBody.termsContainerText
Component.onCompleted: {
// with the link.
termsText.text = qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
}
}
HifiStylesUit.InfoItem {
id: termsText
text: usernameCollisionBody.termsContainerText
font.family: usernameCollisionBody.fontFamily
font.pixelSize: usernameCollisionBody.fontSize
font.bold: usernameCollisionBody.fontBold
wrapMode: Text.WordWrap
color: hifi.colors.white
linkColor: hifi.colors.blueAccent
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
onLinkActivated: loginDialog.openUrl(link);
Component.onCompleted: {
if (termsTextMetrics.width > root.bannerWidth) {
termsText.width = root.bannerWidth;
termsText.wrapMode = Text.WordWrap;
additionalText.verticalAlignment = Text.AlignLeft;
additionalText.horizontalAlignment = Text.AlignLeft;
termsContainer.height = (termsTextMetrics.width / root.bannerWidth) * termsTextMetrics.height;
termsContainer.anchors.left = buttons.left;
} else {
termsText.anchors.centerIn = termsContainer;
}
}
}
}
}
Component.onCompleted: {
@ -201,18 +264,25 @@ Item {
target: loginDialog
onHandleCreateCompleted: {
console.log("Create Succeeded");
if (usernameCollisionBody.withSteam) {
if (usernameCollisionBody.withOculus) {
if (usernameCollisionBody.loginDialogPoppedUp) {
var data = {
"action": "user created a profile with Oculus successfully in the username collision screen"
}
UserActivityLogger.logAction("encourageLoginDialog", data);
}
loginDialog.loginThroughOculus();
} else if (usernameCollisionBody.withSteam) {
if (usernameCollisionBody.loginDialogPoppedUp) {
var data = {
"action": "user created a profile with Steam successfully in the username collision screen"
}
UserActivityLogger.logAction("encourageLoginDialog", data);
}
loginDialog.loginThroughSteam();
}
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": usernameCollisionBody.withSteam, "linkSteam": false })
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": usernameCollisionBody.withSteam,
"withOculus": usernameCollisionBody.withOculus, "linkSteam": false, "linkOculus": false })
}
onHandleCreateFailed: {
console.log("Create Failed: " + error)

Binary file not shown.

Before

(image error) Size: 960 KiB

Binary file not shown.

After

(image error) Size: 2.2 MiB

Binary file not shown.

Before

(image error) Size: 272 KiB

Binary file not shown.

After

(image error) Size: 456 KiB

View file

@ -55,7 +55,8 @@ FocusScope {
Image {
z: -10
id: loginDialogBackground
source: "LoginDialog/images/background.jpg"
fillMode: Image.PreserveAspectCrop
source: "LoginDialog/images/background.png"
anchors.fill: parent
}
@ -149,6 +150,6 @@ FocusScope {
Component.onCompleted: {
keyboardTimer.start();
bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false });
bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false, "linkOculus": false });
}
}

View file

@ -0,0 +1,26 @@
import QtQuick 2.2
import QtQuick.Dialogs 1.1
import Qt.labs.folderlistmodel 2.11
Item {
width: 640
height: 480
ListView {
width: 200; height: 400
FolderListModel {
id: folderModel
folder: "assets:/frames/"
nameFilters: ["*.json"]
}
Component {
id: fileDelegate
Text { text: fileName }
}
model: folderModel
delegate: fileDelegate
}
}

View file

@ -57,16 +57,23 @@ SpinBox {
locale: Qt.locale("en_US")
onValueModified: realValue = value/factor
onValueChanged: realValue = value/factor
onValueModified: {
realValue = value / factor
}
onValueChanged: {
realValue = value / factor
spinBox.editingFinished();
}
onRealValueChanged: {
var newValue = Math.round(realValue*factor);
var newValue = Math.round(realValue * factor);
if(value != newValue) {
value = newValue;
}
}
stepSize: realStepSize*factor
stepSize: realStepSize * factor
to : realTo*factor
from : realFrom*factor
@ -90,11 +97,11 @@ SpinBox {
}
textFromValue: function(value, locale) {
return parseFloat(value/factor).toFixed(decimals);
return parseFloat(value / factor).toFixed(decimals);
}
valueFromText: function(text, locale) {
return Number.fromLocaleString(locale, text)*factor;
return Number.fromLocaleString(locale, text) * factor;
}
@ -102,7 +109,7 @@ SpinBox {
id: spinboxText
z: 2
color: isLightColorScheme
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray)
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.faintGray)
: (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
selectedTextColor: hifi.colors.black
selectionColor: hifi.colors.primaryHighlight
@ -112,8 +119,6 @@ SpinBox {
verticalAlignment: Qt.AlignVCenter
leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding
width: spinBox.width - hifi.dimensions.spinnerSize
onEditingFinished: spinBox.editingFinished()
Text {
id: suffixText
x: metrics.advanceWidth(spinboxText.text + '*')
@ -125,7 +130,7 @@ SpinBox {
}
color: isLightColorScheme
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray)
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.faintGray)
: (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
text: suffix
verticalAlignment: Qt.AlignVCenter
@ -170,6 +175,22 @@ SpinBox {
}
}
Keys.onPressed: {
if (event.key === Qt.Key_Return) {
if (!spinboxText.acceptableInput) {
var number = spinBox.valueFromText(spinboxText.text, spinBox.locale) / spinBox.factor
if (number < spinBox.minimumValue) {
number = spinBox.minimumValue;
} else if (number > maximumValue) {
number = spinBox.maximumValue;
}
spinboxText.text = spinBox.textFromValue(Math.round(number * factor), spinBox.locale)
}
}
}
HifiControls.Label {
id: spinBoxLabel
text: spinBox.label

View file

@ -14,6 +14,8 @@ import QtQuick 2.5
import controlsUit 1.0 as HifiControlsUit
import stylesUit 1.0 as HifiStylesUit
import TabletScriptingInterface 1.0
import "../LoginDialog"
FocusScope {
@ -25,10 +27,9 @@ FocusScope {
width: parent.width
height: parent.height
signal sendToScript(var message);
signal canceled();
property var tabletProxy: Tablet.getTablet("com.highfidelity.interface.tablet.system");
property bool isHMD: false
property bool isHMD: HMD.active
property bool gotoPreviousApp: false;
property bool keyboardEnabled: false
@ -52,6 +53,7 @@ FocusScope {
}
function tryDestroy() {
tabletProxy.gotoHomeScreen();
}
MouseArea {
@ -76,7 +78,7 @@ FocusScope {
interval: 200
onTriggered: {
if (MenuInterface.isOptionChecked("Use 3D Keyboard")) {
if (MenuInterface.isOptionChecked("Use 3D Keyboard") && root.isHMD) {
KeyboardScriptingInterface.raised = true;
}
}
@ -95,7 +97,8 @@ FocusScope {
Image {
z: -10
id: loginDialogBackground
source: "../LoginDialog/images/background_tablet.jpg"
fillMode: Image.PreserveAspectCrop
source: "../LoginDialog/images/background_tablet.png"
anchors.fill: parent
}
@ -168,11 +171,13 @@ FocusScope {
Component.onDestruction: {
loginKeyboard.raised = false;
KeyboardScriptingInterface.raised = false;
if (root.isHMD) {
KeyboardScriptingInterface.raised = false;
}
}
Component.onCompleted: {
keyboardTimer.start();
bodyLoader.setSource("../LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false });
bodyLoader.setSource("../LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "linkSteam": false, "linkOculus": false });
}
}

View file

@ -148,7 +148,7 @@ Windows.ScrollingWindow {
}
function canAddToWorld(path) {
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i];
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i, /\.gltf\b/i];
if (selectedItemCount > 1) {
return false;

View file

@ -254,6 +254,7 @@ Rectangle {
onSaveClicked: function() {
var avatarSettings = {
dominantHand : settings.dominantHandIsLeft ? 'left' : 'right',
hmdAvatarAlignmentType : settings.hmdAvatarAlignmentTypeIsEyes ? 'eyes' : 'head',
collisionsEnabled : settings.environmentCollisionsOn,
otherAvatarsCollisionsEnabled : settings.otherAvatarsCollisionsOn,
animGraphOverrideUrl : settings.avatarAnimationOverrideJSON,

View file

@ -37,6 +37,7 @@ Rectangle {
property alias dominantHandIsLeft: leftHandRadioButton.checked
property alias otherAvatarsCollisionsOn: otherAvatarsCollisionsEnabledRadiobutton.checked
property alias environmentCollisionsOn: environmentCollisionsEnabledRadiobutton.checked
property alias hmdAvatarAlignmentTypeIsEyes: eyesRadioButton.checked
property alias avatarAnimationOverrideJSON: avatarAnimationUrlInputText.text
property alias avatarAnimationJSON: avatarAnimationUrlInputText.placeholderText
property alias avatarCollisionSoundUrl: avatarCollisionSoundUrlInputText.text
@ -65,6 +66,11 @@ Rectangle {
} else {
environmentCollisionsDisabledRadiobutton.checked = true;
}
if (settings.hmdAvatarAlignmentType === 'eyes') {
eyesRadioButton.checked = true;
} else {
headRadioButton.checked = true;
}
avatarAnimationJSON = settings.animGraphUrl;
avatarAnimationOverrideJSON = settings.animGraphOverrideUrl;
@ -210,7 +216,7 @@ Rectangle {
anchors.left: parent.left
anchors.right: parent.right
rows: 2
rows: 4
rowSpacing: 25
columns: 3
@ -233,7 +239,7 @@ Rectangle {
Layout.row: 0
Layout.column: 1
Layout.leftMargin: -20
Layout.leftMargin: -15
ButtonGroup.group: leftRight
checked: true
@ -249,7 +255,7 @@ Rectangle {
id: rightHandRadioButton
Layout.row: 0
Layout.column: 3
Layout.column: 2
Layout.rightMargin: -15
ButtonGroup.group: leftRight
@ -260,7 +266,7 @@ Rectangle {
text: "Right"
boxSize: 20
}
HifiConstants {
id: hifi
}
@ -272,17 +278,17 @@ Rectangle {
Layout.column: 0
text: "Avatar to avatar collision"
}
ButtonGroup {
id: otherAvatarsOnOff
}
HifiControlsUit.RadioButton {
id: otherAvatarsCollisionsEnabledRadiobutton
Layout.row: 1
Layout.column: 1
Layout.leftMargin: -20
Layout.leftMargin: -15
ButtonGroup.group: otherAvatarsOnOff
@ -297,7 +303,7 @@ Rectangle {
id: otherAvatarsCollisionsDisabledRadiobutton
Layout.row: 1
Layout.column: 3
Layout.column: 2
Layout.rightMargin: -15
ButtonGroup.group: otherAvatarsOnOff
@ -320,13 +326,13 @@ Rectangle {
ButtonGroup {
id: worldOnOff
}
HifiControlsUit.RadioButton {
id: environmentCollisionsEnabledRadiobutton
Layout.row: 2
Layout.column: 1
Layout.leftMargin: -20
Layout.leftMargin: -15
ButtonGroup.group: worldOnOff
@ -341,7 +347,7 @@ Rectangle {
id: environmentCollisionsDisabledRadiobutton
Layout.row: 2
Layout.column: 3
Layout.column: 2
Layout.rightMargin: -15
ButtonGroup.group: worldOnOff
@ -352,6 +358,52 @@ Rectangle {
text: "Off"
boxSize: 20
}
// TextStyle9
RalewaySemiBold {
size: 17;
Layout.row: 3
Layout.column: 0
text: "HMD Alignment"
}
ButtonGroup {
id: headEyes
}
HifiControlsUit.RadioButton {
id: headRadioButton
Layout.row: 3
Layout.column: 1
Layout.leftMargin: -15
ButtonGroup.group: headEyes
checked: true
colorScheme: hifi.colorSchemes.light
fontSize: 17
letterSpacing: 1.4
text: "Head"
boxSize: 20
}
HifiControlsUit.RadioButton {
id: eyesRadioButton
Layout.row: 3
Layout.column: 2
Layout.rightMargin: -15
ButtonGroup.group: headEyes
colorScheme: hifi.colorSchemes.light
fontSize: 17
letterSpacing: 1.4
text: "Eyes"
boxSize: 20
}
}
ColumnLayout {

View file

@ -148,7 +148,7 @@ Rectangle {
}
function canAddToWorld(path) {
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i];
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i, /\.gltf\b/i];
if (selectedItemCount > 1) {
return false;

View file

@ -70,7 +70,7 @@ Flickable {
readonly property bool hmdDesktop: hmdInDesktop.checked
property int state: buttonState.disabled
property var lastConfiguration: null
property var lastConfiguration: null
HifiConstants { id: hifi }
@ -90,7 +90,6 @@ Flickable {
anchors.fill: parent
propagateComposedEvents: true
onPressed: {
parent.forceActiveFocus()
mouse.accepted = false;
}
}
@ -169,9 +168,7 @@ Flickable {
boxRadius: 7
visible: viveInDesktop.checked
anchors.top: viveInDesktop.bottom
anchors.topMargin: 5
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 10
onClicked: {
@ -214,13 +211,13 @@ Flickable {
onRealValueChanged: {
sendConfigurationSettings();
openVrConfiguration.forceActiveFocus();
}
}
HifiControls.SpinBox {
id: headZOffset
z: 10
width: 112
label: "Z Offset"
minimumValue: -50
@ -232,7 +229,6 @@ Flickable {
onRealValueChanged: {
sendConfigurationSettings();
openVrConfiguration.forceActiveFocus();
}
}
}
@ -326,7 +322,6 @@ Flickable {
onRealValueChanged: {
sendConfigurationSettings();
openVrConfiguration.forceActiveFocus();
}
}
@ -344,7 +339,6 @@ Flickable {
onRealValueChanged: {
sendConfigurationSettings();
openVrConfiguration.forceActiveFocus();
}
}
}
@ -578,7 +572,6 @@ Flickable {
onRealValueChanged: {
sendConfigurationSettings();
openVrConfiguration.forceActiveFocus();
}
}
@ -596,7 +589,6 @@ Flickable {
onRealValueChanged: {
sendConfigurationSettings();
openVrConfiguration.forceActiveFocus();
}
}
}
@ -747,8 +739,8 @@ Flickable {
}
Component.onCompleted: {
InputConfiguration.calibrationStatus.connect(calibrationStatusInfo);
lastConfiguration = composeConfigurationSettings();
InputConfiguration.calibrationStatus.connect(calibrationStatusInfo);
}
Component.onDestruction: {
@ -777,7 +769,6 @@ Flickable {
calibrationTimer.interval = realValue * 1000;
openVrConfiguration.countDown = realValue;
numberAnimation.duration = calibrationTimer.interval;
openVrConfiguration.forceActiveFocus();
}
}
@ -1048,6 +1039,9 @@ Flickable {
}
function updateButtonState() {
if (lastConfiguration === null) {
lastConfiguration = composeConfigurationSettings();
}
var settings = composeConfigurationSettings();
var bodySetting = settings["bodyConfiguration"];
var headSetting = settings["headConfiguration"];

View file

@ -120,6 +120,7 @@
#include <plugins/PluginManager.h>
#include <plugins/PluginUtils.h>
#include <plugins/SteamClientPlugin.h>
#include <plugins/OculusPlatformPlugin.h>
#include <plugins/InputConfiguration.h>
#include <RecordingScriptingInterface.h>
#include <render/EngineStats.h>
@ -780,7 +781,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
if (auto steamClient = pluginManager->getSteamClientPlugin()) {
steamClient->init();
}
PROFILE_SET_THREAD_NAME("Main Thread");
#if defined(Q_OS_WIN)
@ -2387,7 +2387,6 @@ void Application::updateVerboseLogging() {
bool enable = menu->isOptionChecked(MenuOption::VerboseLogging);
QString rules =
"hifi.*.debug=%1\n"
"hifi.*.info=%1\n"
"hifi.audio-stream.debug=false\n"
"hifi.audio-stream.info=false";
@ -2706,6 +2705,7 @@ Application::~Application() {
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
steamClient->shutdown();
}
DependencyManager::destroy<PluginManager>();
DependencyManager::destroy<CompositorHelper>(); // must be destroyed before the FramebufferCache
@ -3512,8 +3512,10 @@ void Application::resizeGL() {
auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration();
assert(renderConfig);
auto mainView = renderConfig->getConfig("RenderMainView.RenderDeferredTask");
assert(mainView);
mainView->setProperty("resolutionScale", renderResolutionScale);
// mainView can be null if we're rendering in forward mode
if (mainView) {
mainView->setProperty("resolutionScale", renderResolutionScale);
}
displayPlugin->setRenderResolutionScale(renderResolutionScale);
}
@ -4039,6 +4041,19 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
break;
case Qt::Key_G:
if (isShifted && isMeta && Menu::getInstance() && Menu::getInstance()->getMenu("Developer")->isVisible()) {
static const QString HIFI_FRAMES_FOLDER_VAR = "HIFI_FRAMES_FOLDER";
static const QString GPU_FRAME_FOLDER = QProcessEnvironment::systemEnvironment().contains(HIFI_FRAMES_FOLDER_VAR)
? QProcessEnvironment::systemEnvironment().value(HIFI_FRAMES_FOLDER_VAR)
: "hifiFrames";
static QString GPU_FRAME_TEMPLATE = GPU_FRAME_FOLDER + "/{DATE}_{TIME}";
QString fullPath = FileUtils::computeDocumentPath(FileUtils::replaceDateTimeTokens(GPU_FRAME_TEMPLATE));
if (FileUtils::canCreateFile(fullPath)) {
getActiveDisplayPlugin()->captureFrame(fullPath.toStdString());
}
}
break;
case Qt::Key_X:
if (isShifted && isMeta) {
auto offscreenUi = getOffscreenUI();
@ -4820,6 +4835,10 @@ void Application::idle() {
steamClient->runCallbacks();
}
if (auto oculusPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
oculusPlugin->handleOVREvents();
}
float secondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_MSEC / MSECS_PER_SECOND;
_lastTimeUpdated.start();
@ -5958,6 +5977,13 @@ void Application::update(float deltaTime) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
controller::HmdAvatarAlignmentType hmdAvatarAlignmentType;
if (myAvatar->getHmdAvatarAlignmentType() == "eyes") {
hmdAvatarAlignmentType = controller::HmdAvatarAlignmentType::Eyes;
} else {
hmdAvatarAlignmentType = controller::HmdAvatarAlignmentType::Head;
}
controller::InputCalibrationData calibrationData = {
myAvatar->getSensorToWorldMatrix(),
createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition()),
@ -5971,7 +5997,8 @@ void Application::update(float deltaTime) {
myAvatar->getRightArmCalibrationMat(),
myAvatar->getLeftArmCalibrationMat(),
myAvatar->getRightHandCalibrationMat(),
myAvatar->getLeftHandCalibrationMat()
myAvatar->getLeftHandCalibrationMat(),
hmdAvatarAlignmentType
};
InputPluginPointer keyboardMousePlugin;
@ -8131,7 +8158,18 @@ void Application::toggleLogDialog() {
return;
}
if (! _logDialog) {
bool keepOnTop =_keepLogWindowOnTop.get();
#ifdef Q_OS_WIN
_logDialog = new LogDialog(keepOnTop ? qApp->getWindow() : nullptr, getLogger());
#else
_logDialog = new LogDialog(nullptr, getLogger());
if (keepOnTop) {
Qt::WindowFlags flags = _logDialog->windowFlags() | Qt::Tool;
_logDialog->setWindowFlags(flags);
}
#endif
}
if (_logDialog->isVisible()) {
@ -8141,6 +8179,19 @@ void Application::toggleLogDialog() {
}
}
void Application::recreateLogWindow(int keepOnTop) {
_keepLogWindowOnTop.set(keepOnTop != 0);
if (_logDialog) {
bool toggle = _logDialog->isVisible();
_logDialog->close();
_logDialog = nullptr;
if (toggle) {
toggleLogDialog();
}
}
}
void Application::toggleEntityScriptServerLogDialog() {
if (! _entityScriptServerLogDialog) {
_entityScriptServerLogDialog = new EntityScriptServerLogDialog(nullptr);

View file

@ -217,6 +217,8 @@ public:
void setDesktopTabletScale(float desktopTabletScale);
bool getDesktopTabletBecomesToolbarSetting() { return _desktopTabletBecomesToolbarSetting.get(); }
bool getLogWindowOnTopSetting() { return _keepLogWindowOnTop.get(); }
void setLogWindowOnTopSetting(bool keepOnTop) { _keepLogWindowOnTop.set(keepOnTop); }
void setDesktopTabletBecomesToolbarSetting(bool value);
bool getHmdTabletBecomesToolbarSetting() { return _hmdTabletBecomesToolbarSetting.get(); }
void setHmdTabletBecomesToolbarSetting(bool value);
@ -365,6 +367,7 @@ public slots:
Q_INVOKABLE void loadDialog();
Q_INVOKABLE void loadScriptURLDialog() const;
void toggleLogDialog();
void recreateLogWindow(int);
void toggleEntityScriptServerLogDialog();
Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
Q_INVOKABLE void loadAddAvatarBookmarkDialog() const;
@ -653,6 +656,7 @@ private:
Setting::Handle<bool> _constrainToolbarPosition;
Setting::Handle<QString> _preferredCursor;
Setting::Handle<bool> _miniTabletEnabledSetting;
Setting::Handle<bool> _keepLogWindowOnTop { "keepLogWindowOnTop", false };
float _scaleMirror;
float _mirrorYawOffset;

View file

@ -288,39 +288,37 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) {
glm::vec3 oneFrameVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep;
_measuredLinearVelocities[_measuredLinearVelocitiesIndex++] = oneFrameVelocity;
if (_measuredLinearVelocitiesIndex >= AvatarActionHold::velocitySmoothFrames) {
_measuredLinearVelocitiesIndex = 0;
_measuredLinearVelocitiesIndex %= AvatarActionHold::velocitySmoothFrames;
}
if (_kinematicSetVelocity) {
glm::vec3 measuredLinearVelocity = _measuredLinearVelocities[0];
for (int i = 1; i < AvatarActionHold::velocitySmoothFrames; i++) {
// there is a bit of lag between when someone releases the trigger and when the software reacts to
// the release. we calculate the velocity from previous frames but we don't include several
// of the most recent.
//
// if _measuredLinearVelocitiesIndex is
// 0 -- ignore i of 3 4 5
// 1 -- ignore i of 4 5 0
// 2 -- ignore i of 5 0 1
// 3 -- ignore i of 0 1 2
// 4 -- ignore i of 1 2 3
// 5 -- ignore i of 2 3 4
// This code is now disabled, but I'm leaving it commented-out because I suspect it will come back.
// if ((i + 1) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
// (i + 2) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
// (i + 3) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex) {
// continue;
// }
measuredLinearVelocity += _measuredLinearVelocities[i];
}
}
glm::vec3 measuredLinearVelocity;
for (int i = 0; i < AvatarActionHold::velocitySmoothFrames; i++) {
// there is a bit of lag between when someone releases the trigger and when the software reacts to
// the release. we calculate the velocity from previous frames but we don't include several
// of the most recent.
//
// if _measuredLinearVelocitiesIndex is
// 0 -- ignore i of 3 4 5
// 1 -- ignore i of 4 5 0
// 2 -- ignore i of 5 0 1
// 3 -- ignore i of 0 1 2
// 4 -- ignore i of 1 2 3
// 5 -- ignore i of 2 3 4
// This code is now disabled, but I'm leaving it commented-out because I suspect it will come back.
// if ((i + 1) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
// (i + 2) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
// (i + 3) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex) {
// continue;
// }
measuredLinearVelocity += _measuredLinearVelocities[i];
}
measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames
measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames
// - 3 // 3 because of the 3 we skipped, above
);
if (_kinematicSetVelocity) {
rigidBody->setLinearVelocity(glmToBullet(measuredLinearVelocity));
rigidBody->setAngularVelocity(glmToBullet(_angularVelocityTarget));
}

3
interface/src/avatar/AvatarManager.cpp Normal file → Executable file
View file

@ -270,7 +270,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
if (avatar->getSkeletonModel()->isLoaded()) {
// remove the orb if it is there
avatar->removeOrb();
avatar->updateCollisionGroup(_myAvatar->getOtherAvatarsCollisionsEnabled());
if (avatar->needsPhysicsUpdate()) {
_avatarsToChangeInPhysics.insert(avatar);
}
@ -376,7 +375,6 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
}
AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) {
auto otherAvatar = new OtherAvatar(qApp->thread());
otherAvatar->setSessionUUID(sessionUUID);
auto nodeList = DependencyManager::get<NodeList>();
@ -452,6 +450,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
_spaceProxiesToDelete.push_back(avatar->getSpaceIndex());
}
AvatarHashMap::handleRemovedAvatar(avatar, removalReason);
avatar->tearDownGrabs();
avatar->die();
queuePhysicsChange(avatar);

6
interface/src/avatar/AvatarMotionState.cpp Normal file → Executable file
View file

@ -15,7 +15,6 @@
#include <PhysicsEngine.h>
#include <PhysicsHelpers.h>
AvatarMotionState::AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
assert(_avatar);
_type = MOTIONSTATE_TYPE_AVATAR;
@ -172,7 +171,10 @@ QUuid AvatarMotionState::getSimulatorID() const {
// virtual
void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const {
group = _collisionGroup;
mask = _collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ? 0 : Physics::getDefaultCollisionMask(group);
mask = Physics::getDefaultCollisionMask(group);
if (!_avatar->getCollideWithOtherAvatars()) {
mask &= ~(BULLET_COLLISION_GROUP_MY_AVATAR | BULLET_COLLISION_GROUP_OTHER_AVATAR);
}
}
// virtual

View file

@ -95,6 +95,37 @@ const float CENTIMETERS_PER_METER = 100.0f;
const QString AVATAR_SETTINGS_GROUP_NAME { "Avatar" };
static const QString USER_RECENTER_MODEL_FORCE_SIT = QStringLiteral("ForceSit");
static const QString USER_RECENTER_MODEL_FORCE_STAND = QStringLiteral("ForceStand");
static const QString USER_RECENTER_MODEL_AUTO = QStringLiteral("Auto");
static const QString USER_RECENTER_MODEL_DISABLE_HMD_LEAN = QStringLiteral("DisableHMDLean");
MyAvatar::SitStandModelType stringToUserRecenterModel(const QString& str) {
if (str == USER_RECENTER_MODEL_FORCE_SIT) {
return MyAvatar::ForceSit;
} else if (str == USER_RECENTER_MODEL_FORCE_STAND) {
return MyAvatar::ForceStand;
} else if (str == USER_RECENTER_MODEL_DISABLE_HMD_LEAN) {
return MyAvatar::DisableHMDLean;
} else {
return MyAvatar::Auto;
}
}
QString userRecenterModelToString(MyAvatar::SitStandModelType model) {
switch (model) {
case MyAvatar::ForceSit:
return USER_RECENTER_MODEL_FORCE_SIT;
case MyAvatar::ForceStand:
return USER_RECENTER_MODEL_FORCE_STAND;
case MyAvatar::DisableHMDLean:
return USER_RECENTER_MODEL_DISABLE_HMD_LEAN;
case MyAvatar::Auto:
default:
return USER_RECENTER_MODEL_AUTO;
}
}
MyAvatar::MyAvatar(QThread* thread) :
Avatar(thread),
_yawSpeed(YAW_SPEED_DEFAULT),
@ -125,6 +156,7 @@ MyAvatar::MyAvatar(QThread* thread) :
_prevShouldDrawHead(true),
_audioListenerMode(FROM_HEAD),
_dominantHandSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "dominantHand", DOMINANT_RIGHT_HAND),
_hmdAvatarAlignmentTypeSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hmdAvatarAlignmentType", DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE),
_headPitchSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "", 0.0f),
_scaleSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "scale", _targetScale),
_yawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "yawSpeed", _yawSpeed),
@ -138,7 +170,8 @@ MyAvatar::MyAvatar(QThread* thread) :
_useSnapTurnSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "useSnapTurn", _useSnapTurn),
_userHeightSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userHeight", DEFAULT_AVATAR_HEIGHT),
_flyingHMDSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "flyingHMD", _flyingPrefHMD),
_avatarEntityCountSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" << "size", 0)
_avatarEntityCountSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" << "size", 0),
_userRecenterModelSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userRecenterModel", USER_RECENTER_MODEL_AUTO)
{
_clientTraitsHandler.reset(new ClientTraitsHandler(this));
@ -205,12 +238,12 @@ MyAvatar::MyAvatar(QThread* thread) :
if (recordingInterface->getPlayFromCurrentLocation()) {
setRecordingBasis();
}
_previousCollisionGroup = _characterController.computeCollisionGroup();
_previousCollisionMask = _characterController.computeCollisionMask();
_characterController.setCollisionless(true);
} else {
clearRecordingBasis();
useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
if (_previousCollisionGroup != BULLET_COLLISION_GROUP_COLLISIONLESS) {
if (_previousCollisionMask != BULLET_COLLISION_MASK_COLLISIONLESS) {
_characterController.setCollisionless(false);
}
}
@ -286,10 +319,25 @@ MyAvatar::~MyAvatar() {
_myScriptEngine = nullptr;
}
QString MyAvatar::getDominantHand() const {
return _dominantHand.get();
}
void MyAvatar::setDominantHand(const QString& hand) {
if (hand == DOMINANT_LEFT_HAND || hand == DOMINANT_RIGHT_HAND) {
_dominantHand = hand;
emit dominantHandChanged(_dominantHand);
_dominantHand.set(hand);
emit dominantHandChanged(hand);
}
}
QString MyAvatar::getHmdAvatarAlignmentType() const {
return _hmdAvatarAlignmentType.get();
}
void MyAvatar::setHmdAvatarAlignmentType(const QString& type) {
if (type != _hmdAvatarAlignmentType.get()) {
_hmdAvatarAlignmentType.set(type);
emit hmdAvatarAlignmentTypeChanged(type);
}
}
@ -377,6 +425,7 @@ void MyAvatar::resetSensorsAndBody() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "resetSensorsAndBody");
return;
}
qApp->getActiveDisplayPlugin()->resetSensors();
@ -817,7 +866,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
// and all of its joints, now update our attachements.
Avatar::simulateAttachments(deltaTime);
relayJointDataToChildren();
if (updateGrabs()) {
if (applyGrabChanges()) {
_cauterizationNeedsUpdate = true;
}
@ -1277,7 +1326,8 @@ void MyAvatar::resizeAvatarEntitySettingHandles(uint32_t maxIndex) {
}
void MyAvatar::saveData() {
_dominantHandSetting.set(_dominantHand);
_dominantHandSetting.set(getDominantHand());
_hmdAvatarAlignmentTypeSetting.set(getHmdAvatarAlignmentType());
_headPitchSetting.set(getHead()->getBasePitch());
_scaleSetting.set(_targetScale);
_yawSpeedSetting.set(_yawSpeed);
@ -1300,6 +1350,7 @@ void MyAvatar::saveData() {
_useSnapTurnSetting.set(_useSnapTurn);
_userHeightSetting.set(getUserHeight());
_flyingHMDSetting.set(getFlyingHMDPref());
_userRecenterModelSetting.set(userRecenterModelToString(getUserRecenterModel()));
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
saveAvatarEntityDataToSettings();
@ -1882,9 +1933,12 @@ void MyAvatar::loadData() {
setCollisionSoundURL(_collisionSoundURLSetting.get(QUrl(DEFAULT_AVATAR_COLLISION_SOUND_URL)).toString());
setSnapTurn(_useSnapTurnSetting.get());
setDominantHand(_dominantHandSetting.get(DOMINANT_RIGHT_HAND).toLower());
setHmdAvatarAlignmentType(_hmdAvatarAlignmentTypeSetting.get(DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE).toLower());
setUserHeight(_userHeightSetting.get(DEFAULT_AVATAR_HEIGHT));
setTargetScale(_scaleSetting.get());
setUserRecenterModel(stringToUserRecenterModel(_userRecenterModelSetting.get(USER_RECENTER_MODEL_AUTO)));
setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible));
_follow.setToggleHipsFollowing (Menu::getInstance()->isOptionChecked(MenuOption::ToggleHipsFollowing));
setEnableDebugDrawBaseOfSupport(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawBaseOfSupport));
@ -2534,7 +2588,7 @@ void MyAvatar::updateMotors() {
float verticalMotorTimescale;
if (_characterController.getState() == CharacterController::State::Hover ||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE;
verticalMotorTimescale = FLYING_MOTOR_TIMESCALE;
} else {
@ -2544,7 +2598,7 @@ void MyAvatar::updateMotors() {
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
if (_characterController.getState() == CharacterController::State::Hover ||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
motorRotation = getMyHead()->getHeadOrientation();
} else {
// non-hovering = walking: follow camera twist about vertical but not lift
@ -2599,7 +2653,7 @@ void MyAvatar::prepareForPhysicsSimulation() {
qDebug() << "Warning: getParentVelocity failed" << getID();
parentVelocity = glm::vec3();
}
_characterController.handleChangedCollisionGroup();
_characterController.handleChangedCollisionMask();
_characterController.setParentVelocity(parentVelocity);
_characterController.setScaleFactor(getSensorToWorldScale());
@ -3279,7 +3333,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
head->setBaseRoll(ROLL(euler));
} else {
head->setBaseYaw(0.0f);
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime
+ getDriveKey(DELTA_PITCH) * _pitchSpeed / PITCH_SPEED_DEFAULT);
head->setBaseRoll(0.0f);
}
@ -3325,7 +3379,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
glm::vec3 direction = forward + right;
if (state == CharacterController::State::Hover ||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP;
direction += up;
}
@ -3881,7 +3935,7 @@ void MyAvatar::setCollisionsEnabled(bool enabled) {
bool MyAvatar::getCollisionsEnabled() {
// may return 'false' even though the collisionless option was requested
// because the zone may disallow collisionless avatars
return _characterController.computeCollisionGroup() != BULLET_COLLISION_GROUP_COLLISIONLESS;
return _characterController.computeCollisionMask() != BULLET_COLLISION_MASK_COLLISIONLESS;
}
void MyAvatar::setOtherAvatarsCollisionsEnabled(bool enabled) {
@ -3890,7 +3944,11 @@ void MyAvatar::setOtherAvatarsCollisionsEnabled(bool enabled) {
QMetaObject::invokeMethod(this, "setOtherAvatarsCollisionsEnabled", Q_ARG(bool, enabled));
return;
}
bool change = _collideWithOtherAvatars != enabled;
_collideWithOtherAvatars = enabled;
if (change) {
setCollisionWithOtherAvatarsFlags();
}
emit otherAvatarsCollisionsEnabledChanged(enabled);
}
@ -3898,6 +3956,11 @@ bool MyAvatar::getOtherAvatarsCollisionsEnabled() {
return _collideWithOtherAvatars;
}
void MyAvatar::setCollisionWithOtherAvatarsFlags() {
_characterController.setCollideWithOtherAvatars(_collideWithOtherAvatars);
_characterController.setPendingFlagsUpdateCollisionMask();
}
void MyAvatar::updateCollisionCapsuleCache() {
glm::vec3 start, end;
float radius;
@ -4752,7 +4815,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
}
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
const float CYLINDER_TOP = 0.1f;
const float CYLINDER_TOP = 2.0f;
const float CYLINDER_BOTTOM = -1.5f;
const float SITTING_BOTTOM = -0.02f;
@ -5310,7 +5373,7 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
_avatarGrabsLock.withWriteLock([&] {
if (_avatarGrabData.remove(grabID)) {
_deletedAvatarGrabs.insert(grabID);
_grabsToDelete.push_back(grabID);
tellHandler = true;
}
});

30
interface/src/avatar/MyAvatar.h Normal file → Executable file
View file

@ -252,6 +252,7 @@ class MyAvatar : public Avatar {
const QString DOMINANT_LEFT_HAND = "left";
const QString DOMINANT_RIGHT_HAND = "right";
const QString DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE = "head";
using Clock = std::chrono::system_clock;
using TimePoint = Clock::time_point;
@ -297,6 +298,8 @@ public:
void reset(bool andRecenter = false, bool andReload = true, bool andHead = true);
void setCollisionWithOtherAvatarsFlags() override;
/**jsdoc
* @function MyAvatar.resetSensorsAndBody
*/
@ -517,7 +520,18 @@ public:
* @function MyAvatar.getDominantHand
* @returns {string}
*/
Q_INVOKABLE QString getDominantHand() const { return _dominantHand; }
Q_INVOKABLE QString getDominantHand() const;
/**jsdoc
* @function MyAvatar.setHmdAvatarAlignmentType
* @param {string} hand
*/
Q_INVOKABLE void setHmdAvatarAlignmentType(const QString& hand);
/**jsdoc
* @function MyAvatar.setHmdAvatarAlignmentType
* @returns {string}
*/
Q_INVOKABLE QString getHmdAvatarAlignmentType() const;
/**jsdoc
* @function MyAvatar.setCenterOfGravityModelEnabled
@ -1583,6 +1597,13 @@ signals:
*/
void dominantHandChanged(const QString& hand);
/**jsdoc
* @function MyAvatar.hmdAvatarAlignmentTypeChanged
* @param {string} type
* @returns {Signal}
*/
void hmdAvatarAlignmentTypeChanged(const QString& type);
/**jsdoc
* @function MyAvatar.sensorToWorldScaleChanged
* @param {number} scale
@ -1732,7 +1753,7 @@ private:
SharedSoundPointer _collisionSound;
MyCharacterController _characterController;
int32_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR };
int32_t _previousCollisionMask { BULLET_COLLISION_MASK_MY_AVATAR };
AvatarWeakPointer _lookAtTargetAvatar;
glm::vec3 _targetAvatarPosition;
@ -1771,7 +1792,8 @@ private:
ThreadSafeValueCache<QUrl> _prefOverrideAnimGraphUrl;
QUrl _fstAnimGraphOverrideUrl;
bool _useSnapTurn { true };
QString _dominantHand { DOMINANT_RIGHT_HAND };
ThreadSafeValueCache<QString> _dominantHand { DOMINANT_RIGHT_HAND };
ThreadSafeValueCache<QString> _hmdAvatarAlignmentType { DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE };
const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // degrees
const float ROLL_CONTROL_RATE_DEFAULT = 114.0f; // degrees / sec
@ -1944,6 +1966,7 @@ private:
TimePoint _nextTraitsSendWindow;
Setting::Handle<QString> _dominantHandSetting;
Setting::Handle<QString> _hmdAvatarAlignmentTypeSetting;
Setting::Handle<float> _headPitchSetting;
Setting::Handle<float> _scaleSetting;
Setting::Handle<float> _yawSpeedSetting;
@ -1960,6 +1983,7 @@ private:
Setting::Handle<bool> _allowTeleportingSetting { "allowTeleporting", true };
std::vector<Setting::Handle<QUuid>> _avatarEntityIDSettings;
std::vector<Setting::Handle<QByteArray>> _avatarEntityDataSettings;
Setting::Handle<QString> _userRecenterModelSetting;
// AvatarEntities stuff:
// We cache the "map of unfortunately-formatted-binary-blobs" because they are expensive to compute

View file

@ -202,6 +202,29 @@ bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm:
return result.hitFraction < 1.0f;
}
int32_t MyCharacterController::computeCollisionMask() const {
int32_t collisionMask = BULLET_COLLISION_MASK_MY_AVATAR;
if (_collisionless && _collisionlessAllowed) {
collisionMask = BULLET_COLLISION_MASK_COLLISIONLESS;
} else if (!_collideWithOtherAvatars) {
collisionMask &= ~BULLET_COLLISION_GROUP_OTHER_AVATAR;
}
return collisionMask;
}
void MyCharacterController::handleChangedCollisionMask() {
if (_pendingFlags & PENDING_FLAG_UPDATE_COLLISION_MASK) {
// ATM the easiest way to update collision groups/masks is to remove/re-add the RigidBody
if (_dynamicsWorld) {
_dynamicsWorld->removeRigidBody(_rigidBody);
int32_t collisionMask = computeCollisionMask();
_dynamicsWorld->addRigidBody(_rigidBody, BULLET_COLLISION_GROUP_MY_AVATAR, collisionMask);
}
_pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_MASK;
updateCurrentGravity();
}
}
btConvexHullShape* MyCharacterController::computeShape() const {
// HACK: the avatar collides using convex hull with a collision margin equal to
// the old capsule radius. Two points define a capsule and additional points are

6
interface/src/avatar/MyCharacterController.h Normal file → Executable file
View file

@ -42,6 +42,12 @@ public:
void setDensity(btScalar density) { _density = density; }
int32_t computeCollisionMask() const override;
void handleChangedCollisionMask() override;
bool _collideWithOtherAvatars{ true };
void setCollideWithOtherAvatars(bool collideWithOtherAvatars) { _collideWithOtherAvatars = collideWithOtherAvatars; }
protected:
void initRayShotgun(const btCollisionWorld* world);
void updateMassProperties() override;

4
interface/src/avatar/MySkeletonModel.cpp Normal file → Executable file
View file

@ -187,7 +187,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
}
}
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS);
if (isFlying != _prevIsFlying) {
const float FLY_TO_IDLE_HIPS_TRANSITION_TIME = 0.5f;
_flyIdleTimer = FLY_TO_IDLE_HIPS_TRANSITION_TIME;
@ -198,7 +198,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
// if hips are not under direct control, estimate the hips position.
if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) {
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS);
// timescale in seconds
const float TRANS_HORIZ_TIMESCALE = 0.15f;

14
interface/src/avatar/OtherAvatar.cpp Normal file → Executable file
View file

@ -138,17 +138,9 @@ void OtherAvatar::rebuildCollisionShape() {
}
}
void OtherAvatar::updateCollisionGroup(bool myAvatarCollide) {
void OtherAvatar::setCollisionWithOtherAvatarsFlags() {
if (_motionState) {
bool collides = _motionState->getCollisionGroup() == BULLET_COLLISION_GROUP_OTHER_AVATAR && myAvatarCollide;
if (_collideWithOtherAvatars != collides) {
if (!myAvatarCollide) {
_collideWithOtherAvatars = false;
}
auto newCollisionGroup = _collideWithOtherAvatars ? BULLET_COLLISION_GROUP_OTHER_AVATAR : BULLET_COLLISION_GROUP_COLLISIONLESS;
_motionState->setCollisionGroup(newCollisionGroup);
_motionState->addDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
}
_motionState->addDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
}
}
@ -228,7 +220,7 @@ void OtherAvatar::simulate(float deltaTime, bool inView) {
{
PROFILE_RANGE(simulation, "grabs");
updateGrabs();
applyGrabChanges();
}
updateFadingStatus();

4
interface/src/avatar/OtherAvatar.h Normal file → Executable file
View file

@ -44,7 +44,9 @@ public:
bool shouldBeInPhysicsSimulation() const;
bool needsPhysicsUpdate() const;
void updateCollisionGroup(bool myAvatarCollide);
bool getCollideWithOtherAvatars() const { return _collideWithOtherAvatars; }
void setCollisionWithOtherAvatarsFlags() override;
void simulate(float deltaTime, bool inView) override;

View file

@ -20,9 +20,9 @@
#include <PathUtils.h>
const int TOP_BAR_HEIGHT = 124;
const int INITIAL_WIDTH = 720;
const int INITIAL_WIDTH = 800;
const int INITIAL_HEIGHT = 480;
const int MINIMAL_WIDTH = 700;
const int MINIMAL_WIDTH = 780;
const int SEARCH_BUTTON_LEFT = 25;
const int SEARCH_BUTTON_WIDTH = 20;
const int SEARCH_TOGGLE_BUTTON_WIDTH = 50;

View file

@ -19,6 +19,9 @@
#include <shared/AbstractLoggerInterface.h>
#include "Application.h"
#include "MainWindow.h"
const int REVEAL_BUTTON_WIDTH = 122;
const int ALL_LOGS_BUTTON_WIDTH = 90;
const int MARGIN_LEFT = 25;
@ -148,6 +151,16 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog
_messageCount->setObjectName("messageCount");
_messageCount->show();
_keepOnTopBox = new QCheckBox(" Keep window on top", this);
bool isOnTop = qApp-> getLogWindowOnTopSetting();
_keepOnTopBox->setCheckState(isOnTop ? Qt::Checked : Qt::Unchecked);
#ifdef Q_OS_WIN
connect(_keepOnTopBox, &QCheckBox::stateChanged, qApp, &Application::recreateLogWindow);
#else
connect(_keepOnTopBox, &QCheckBox::stateChanged, this, &LogDialog::handleKeepWindowOnTop);
#endif
_keepOnTopBox->show();
_extraDebuggingBox = new QCheckBox("Extra debugging", this);
if (_logger->extraDebugging()) {
_extraDebuggingBox->setCheckState(Qt::Checked);
@ -183,6 +196,11 @@ void LogDialog::resizeEvent(QResizeEvent* event) {
THIRD_ROW,
COMBOBOX_WIDTH,
ELEMENT_HEIGHT);
_keepOnTopBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN,
THIRD_ROW,
COMBOBOX_WIDTH,
ELEMENT_HEIGHT);
_messageCount->setGeometry(_leftPad,
THIRD_ROW,
COMBOBOX_WIDTH,
@ -234,6 +252,23 @@ void LogDialog::handleInfoPrintBox(int state) {
printLogFile();
}
void LogDialog::handleKeepWindowOnTop(int state) {
bool keepOnTop = (state != 0);
Qt::WindowFlags flags = windowFlags();
if (keepOnTop) {
flags |= Qt::Tool;
} else {
flags &= ~Qt::Tool;
}
setWindowFlags(flags);
qApp->setLogWindowOnTopSetting(keepOnTop);
show();
}
void LogDialog::handleCriticalPrintBox(int state) {
_logger->setCriticalPrint(state != 0);
printLogFile();

View file

@ -34,6 +34,7 @@ public slots:
private slots:
void handleRevealButton();
void handleExtraDebuggingCheckbox(int);
void handleKeepWindowOnTop(int);
void handleDebugPrintBox(int);
void handleInfoPrintBox(int);
void handleCriticalPrintBox(int);
@ -55,6 +56,7 @@ protected:
private:
QCheckBox* _extraDebuggingBox;
QCheckBox* _keepOnTopBox;
QPushButton* _revealLogButton;
QPushButton* _allLogsButton;
QCheckBox* _debugPrintBox;

View file

@ -18,6 +18,7 @@
#include <plugins/PluginManager.h>
#include <plugins/SteamClientPlugin.h>
#include <plugins/OculusPlatformPlugin.h>
#include <shared/GlobalAppProperties.h>
#include <ui/TabletScriptingInterface.h>
#include <UserActivityLogger.h>
@ -109,8 +110,16 @@ bool LoginDialog::isSteamRunning() const {
return steamClient && steamClient->isRunning();
}
bool LoginDialog::isOculusStoreRunning() const {
return qApp->property(hifi::properties::OCULUS_STORE).toBool();
bool LoginDialog::isOculusRunning() const {
auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin();
return (oculusPlatformPlugin && oculusPlatformPlugin->isRunning());
}
QString LoginDialog::oculusUserID() const {
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
return oculusPlatformPlugin->getOculusUserID();
}
return "";
}
void LoginDialog::dismissLoginDialog() {
@ -126,6 +135,79 @@ void LoginDialog::login(const QString& username, const QString& password) const
DependencyManager::get<AccountManager>()->requestAccessToken(username, password);
}
void LoginDialog::loginThroughOculus() {
qDebug() << "Attempting to login through Oculus";
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
oculusPlatformPlugin->requestNonceAndUserID([this] (QString nonce, QString oculusID) {
DependencyManager::get<AccountManager>()->requestAccessTokenWithOculus(nonce, oculusID);
});
}
}
void LoginDialog::linkOculus() {
qDebug() << "Attempting to link Oculus account";
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
oculusPlatformPlugin->requestNonceAndUserID([this] (QString nonce, QString oculusID) {
if (nonce.isEmpty() || oculusID.isEmpty()) {
emit handleLoginFailed();
return;
}
JSONCallbackParameters callbackParams;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "linkCompleted";
callbackParams.errorCallbackMethod = "linkFailed";
const QString LINK_OCULUS_PATH = "api/v1/user/oculus/link";
QJsonObject payload;
payload["oculus_nonce"] = nonce;
payload["oculus_id"] = oculusID;
auto accountManager = DependencyManager::get<AccountManager>();
accountManager->sendRequest(LINK_OCULUS_PATH, AccountManagerAuth::Required,
QNetworkAccessManager::PostOperation, callbackParams,
QJsonDocument(payload).toJson());
});
}
}
void LoginDialog::createAccountFromOculus(QString email, QString username, QString password) {
qDebug() << "Attempting to create account from Oculus info";
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
oculusPlatformPlugin->requestNonceAndUserID([this, email, username, password] (QString nonce, QString oculusID) {
if (nonce.isEmpty() || oculusID.isEmpty()) {
emit handleLoginFailed();
return;
}
JSONCallbackParameters callbackParams;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "createCompleted";
callbackParams.errorCallbackMethod = "createFailed";
const QString CREATE_ACCOUNT_FROM_OCULUS_PATH = "api/v1/user/oculus/create";
QJsonObject payload;
payload["oculus_nonce"] = nonce;
payload["oculus_id"] = oculusID;
if (!email.isEmpty()) {
payload["email"] = email;
}
if (!username.isEmpty()) {
payload["username"] = username;
}
if (!password.isEmpty()) {
payload["password"] = password;
}
auto accountManager = DependencyManager::get<AccountManager>();
accountManager->sendRequest(CREATE_ACCOUNT_FROM_OCULUS_PATH, AccountManagerAuth::None,
QNetworkAccessManager::PostOperation, callbackParams,
QJsonDocument(payload).toJson());
});
}
}
void LoginDialog::loginThroughSteam() {
qDebug() << "Attempting to login through Steam";
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
@ -157,7 +239,7 @@ void LoginDialog::linkSteam() {
const QString LINK_STEAM_PATH = "api/v1/user/steam/link";
QJsonObject payload;
payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket)));
payload["steam_auth_ticket"] = QJsonValue::fromVariant(QVariant(ticket));
auto accountManager = DependencyManager::get<AccountManager>();
accountManager->sendRequest(LINK_STEAM_PATH, AccountManagerAuth::Required,
@ -184,9 +266,9 @@ void LoginDialog::createAccountFromSteam(QString username) {
const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "api/v1/user/steam/create";
QJsonObject payload;
payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket)));
payload["steam_auth_ticket"] = QJsonValue::fromVariant(QVariant(ticket));
if (!username.isEmpty()) {
payload.insert("username", QJsonValue::fromVariant(QVariant(username)));
payload["username"] = username;
}
auto accountManager = DependencyManager::get<AccountManager>();
@ -214,6 +296,45 @@ void LoginDialog::createCompleted(QNetworkReply* reply) {
}
void LoginDialog::createFailed(QNetworkReply* reply) {
if (isOculusRunning()) {
auto replyData = reply->readAll();
QJsonParseError parseError;
auto doc = QJsonDocument::fromJson(replyData, &parseError);
if (parseError.error != QJsonParseError::NoError) {
emit handleCreateFailed(reply->errorString());
return;
}
auto data = doc["data"];
auto error = data["error"];
auto oculusError = data["oculus"];
auto user = error["username"].toArray();
auto uid = error["uid"].toArray();
auto email = error["email"].toArray();
auto password = error["password"].toArray();
QString reply;
if (uid[0].isString()) {
emit handleCreateFailed("Oculus ID " + uid[0].toString() + ".");
return;
}
if (user[0].isString()) {
reply = "Username " + user[0].toString() + ".";
}
if (email[0].isString()) {
reply.append((!reply.isEmpty()) ? "\n" : "");
reply.append("Email " + email[0].toString() + ".");
}
if (password[0].isString()) {
reply.append((!reply.isEmpty()) ? "\n" : "");
reply.append("Password " + password[0].toString() + ".");
}
if (!oculusError.isNull() && !oculusError.isUndefined()) {
emit handleCreateFailed("Could not verify token with Oculus. Please try again.");
return;
} else {
emit handleCreateFailed(reply);
return;
}
}
emit handleCreateFailed(reply->errorString());
}

View file

@ -22,7 +22,6 @@ extern const QUrl LOGIN_DIALOG;
class LoginDialog : public OffscreenQmlDialog {
Q_OBJECT
Q_PROPERTY(bool isLogIn READ getIsLogIn WRITE setIsLogIn)
HIFI_QML_DECL
public:
@ -67,24 +66,23 @@ protected slots:
Q_INVOKABLE void dismissLoginDialog();
Q_INVOKABLE bool isSteamRunning() const;
Q_INVOKABLE bool isOculusStoreRunning() const;
Q_INVOKABLE bool isOculusRunning() const;
Q_INVOKABLE QString oculusUserID() const;
Q_INVOKABLE void login(const QString& username, const QString& password) const;
Q_INVOKABLE void loginThroughSteam();
Q_INVOKABLE void linkSteam();
Q_INVOKABLE void createAccountFromSteam(QString username = QString());
Q_INVOKABLE void loginThroughOculus();
Q_INVOKABLE void linkOculus();
Q_INVOKABLE void createAccountFromOculus(QString email = QString(), QString username = QString(), QString password = QString());
Q_INVOKABLE void signup(const QString& email, const QString& username, const QString& password);
Q_INVOKABLE void openUrl(const QString& url) const;
Q_INVOKABLE bool getLoginDialogPoppedUp() const;
private:
bool getIsLogIn() const { return _isLogIn; }
void setIsLogIn(const bool isLogIn) { _isLogIn = isLogIn; }
bool _isLogIn{ false };
};
#endif // hifi_LoginDialog_h

View file

@ -237,8 +237,17 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<HFMJoint>& joints,
_relativeDefaultPoses = _absoluteDefaultPoses;
convertAbsolutePosesToRelative(_relativeDefaultPoses);
// build _jointIndicesByName hash
for (int i = 0; i < _jointsSize; i++) {
_jointIndicesByName[_joints[i].name] = i;
auto iter = _jointIndicesByName.find(_joints[i].name);
if (iter != _jointIndicesByName.end()) {
// prefer joints over meshes if there is a name collision.
if (_joints[i].isSkeletonJoint && !_joints[iter.value()].isSkeletonJoint) {
iter.value() = i;
}
} else {
_jointIndicesByName.insert(_joints[i].name, i);
}
}
// build mirror map.

View file

@ -324,88 +324,79 @@ void Avatar::removeAvatarEntitiesFromTree() {
}
}
bool Avatar::updateGrabs() {
bool Avatar::applyGrabChanges() {
if (!_avatarGrabDataChanged && _grabsToChange.empty() && _grabsToDelete.empty()) {
// early exit for most common case: nothing to do
return false;
}
bool grabAddedOrRemoved = false;
// update the Grabs according to any changes in _avatarGrabData
_avatarGrabsLock.withWriteLock([&] {
if (_avatarGrabDataChanged) {
// collect changes in _avatarGrabData
foreach (auto grabID, _avatarGrabData.keys()) {
AvatarGrabMap::iterator grabItr = _avatarGrabs.find(grabID);
if (grabItr == _avatarGrabs.end()) {
MapOfGrabs::iterator itr = _avatarGrabs.find(grabID);
if (itr == _avatarGrabs.end()) {
GrabPointer grab = std::make_shared<Grab>();
grab->fromByteArray(_avatarGrabData.value(grabID));
_avatarGrabs[grabID] = grab;
_changedAvatarGrabs.insert(grabID);
_grabsToChange.insert(grabID);
} else {
GrabPointer grab = grabItr.value();
bool changed = grab->fromByteArray(_avatarGrabData.value(grabID));
bool changed = itr->second->fromByteArray(_avatarGrabData.value(grabID));
if (changed) {
_changedAvatarGrabs.insert(grabID);
_grabsToChange.insert(grabID);
}
}
}
_avatarGrabDataChanged = false;
}
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
auto entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
EntityEditPacketSender* packetSender = treeRenderer ? treeRenderer->getPacketSender() : nullptr;
auto sessionID = DependencyManager::get<NodeList>()->getSessionUUID();
QMutableSetIterator<QUuid> delItr(_deletedAvatarGrabs);
while (delItr.hasNext()) {
QUuid grabID = delItr.next();
GrabPointer grab = _avatarGrabs[grabID];
if (!grab) {
delItr.remove();
// delete _avatarGrabs
VectorOfIDs undeleted;
for (const auto& id : _grabsToDelete) {
MapOfGrabs::iterator itr = _avatarGrabs.find(id);
if (itr == _avatarGrabs.end()) {
continue;
}
bool success;
const GrabPointer& grab = itr->second;
SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
// only clear this entry from the _deletedAvatarGrabs if we found the entity.
if (success && target) {
bool iShouldTellServer = target->getEditSenderID() == sessionID;
EntityItemPointer entity = std::dynamic_pointer_cast<EntityItem>(target);
if (entity && entity->isAvatarEntity() && (entity->getOwningAvatarID() == sessionID ||
entity->getOwningAvatarID() == AVATAR_SELF_ID)) {
// this is our own avatar-entity, so we always tell the server about the release
iShouldTellServer = true;
}
target->removeGrab(grab);
delItr.remove();
// in case this is the last grab on an entity, we need to shrink the queryAACube and tell the server
// about the final position.
if (entityTree) {
bool force = true;
entityTree->withWriteLock([&] {
entityTree->updateEntityQueryAACube(target, packetSender, force, iShouldTellServer);
});
}
_avatarGrabs.erase(itr);
grabAddedOrRemoved = true;
} else {
undeleted.push_back(id);
}
_avatarGrabs.remove(grabID);
_changedAvatarGrabs.remove(grabID);
}
_grabsToDelete = std::move(undeleted);
QMutableSetIterator<QUuid> changeItr(_changedAvatarGrabs);
while (changeItr.hasNext()) {
QUuid grabID = changeItr.next();
GrabPointer& grab = _avatarGrabs[grabID];
// change _avatarGrabs and add Actions to target
SetOfIDs unchanged;
for (const auto& id : _grabsToChange) {
MapOfGrabs::iterator itr = _avatarGrabs.find(id);
if (itr == _avatarGrabs.end()) {
continue;
}
bool success;
const GrabPointer& grab = itr->second;
SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
if (success && target) {
target->addGrab(grab);
// only clear this entry from the _changedAvatarGrabs if we found the entity.
changeItr.remove();
if (isMyAvatar()) {
EntityItemPointer entity = std::dynamic_pointer_cast<EntityItem>(target);
if (entity) {
entity->upgradeScriptSimulationPriority(PERSONAL_SIMULATION_PRIORITY);
}
}
grabAddedOrRemoved = true;
} else {
unchanged.insert(id);
}
}
_grabsToChange = std::move(unchanged);
});
return grabAddedOrRemoved;
}
@ -413,8 +404,8 @@ bool Avatar::updateGrabs() {
void Avatar::accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& grabAccumulators) {
// relay avatar's joint position to grabbed target in a way that allows for averaging
_avatarGrabsLock.withReadLock([&] {
foreach (auto grabID, _avatarGrabs.keys()) {
const GrabPointer& grab = _avatarGrabs.value(grabID);
for (const auto& entry : _avatarGrabs) {
const GrabPointer& grab = entry.second;
if (!grab || !grab->getActionID().isNull()) {
continue; // the accumulated value isn't used, in this case.
@ -432,6 +423,20 @@ void Avatar::accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& g
});
}
void Avatar::tearDownGrabs() {
_avatarGrabsLock.withWriteLock([&] {
for (const auto& entry : _avatarGrabs) {
_grabsToDelete.push_back(entry.first);
}
_grabsToChange.clear();
});
applyGrabChanges();
if (!_grabsToDelete.empty()) {
// some grabs failed to delete, which is a possible "leak", so we log about it
qWarning() << "Failed to tearDown" << _grabsToDelete.size() << "grabs for Avatar" << getID();
}
}
void Avatar::relayJointDataToChildren() {
forEachChild([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
@ -1929,3 +1934,12 @@ scriptable::ScriptableModelBase Avatar::getScriptableModel() {
}
return result;
}
void Avatar::clearAvatarGrabData(const QUuid& id) {
AvatarData::clearAvatarGrabData(id);
_avatarGrabsLock.withWriteLock([&] {
if (_avatarGrabs.find(id) != _avatarGrabs.end()) {
_grabsToDelete.push_back(id);
}
});
}

View file

@ -14,6 +14,9 @@
#include <functional>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <map>
#include <set>
#include <vector>
#include <QtCore/QUuid>
@ -23,10 +26,12 @@
#include <graphics-scripting/Forward.h>
#include <GLMHelpers.h>
#include <Grab.h>
#include <ThreadSafeValueCache.h>
#include "Head.h"
#include "SkeletonModel.h"
#include "Rig.h"
#include <ThreadSafeValueCache.h>
#include "MetaModelPayload.h"
@ -436,6 +441,8 @@ public:
void accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& grabAccumulators);
void tearDownGrabs();
signals:
void targetScaleChanged(float targetScale);
@ -538,7 +545,7 @@ protected:
// protected methods...
bool isLookingAtMe(AvatarSharedPointer avatar) const;
bool updateGrabs();
bool applyGrabChanges();
void relayJointDataToChildren();
void fade(render::Transaction& transaction, render::Transition::Type type);
@ -625,8 +632,15 @@ protected:
static void metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets,
const QVector<int>& blendedMeshSizes, const render::ItemIDs& subItemIDs);
void clearAvatarGrabData(const QUuid& grabID) override;
AvatarGrabMap _avatarGrabs;
using SetOfIDs = std::set<QUuid>;
using VectorOfIDs = std::vector<QUuid>;
using MapOfGrabs = std::map<QUuid, GrabPointer>;
MapOfGrabs _avatarGrabs;
SetOfIDs _grabsToChange; // updated grab IDs -- changes needed to entities or physics
VectorOfIDs _grabsToDelete; // deleted grab IDs -- changes needed to entities or physics
};
#endif // hifi_Avatar_h

20
libraries/avatars/src/AvatarData.cpp Normal file → Executable file
View file

@ -639,7 +639,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
// compute maxTranslationDimension before we send any joint data.
float maxTranslationDimension = 0.001f;
for (int i = sendStatus.rotationsSent; i < numJoints; ++i) {
for (int i = sendStatus.translationsSent; i < numJoints; ++i) {
const JointData& data = jointData[i];
if (!data.translationIsDefaultPose) {
maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension);
@ -1172,6 +1172,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags);
if (collideWithOtherAvatarsChanged) {
setCollisionWithOtherAvatarsFlags();
}
if (somethingChanged) {
_additionalFlagsChanged = now;
}
@ -2428,7 +2431,8 @@ static const QString JSON_AVATAR_VERSION = QStringLiteral("version");
enum class JsonAvatarFrameVersion : int {
JointRotationsInRelativeFrame = 0,
JointRotationsInAbsoluteFrame,
JointDefaultPoseBits
JointDefaultPoseBits,
JointUnscaledTranslations,
};
QJsonValue toJsonValue(const JointData& joint) {
@ -2445,7 +2449,16 @@ JointData jointDataFromJsonValue(int version, const QJsonValue& json) {
if (json.isArray()) {
QJsonArray array = json.toArray();
result.rotation = quatFromJsonValue(array[0]);
result.translation = vec3FromJsonValue(array[1]);
// In old recordings, translations are scaled by _geometryOffset. Undo that scaling.
if (version < (int)JsonAvatarFrameVersion::JointUnscaledTranslations) {
// because we don't have access to the actual _geometryOffset used. we have to guess.
// most avatar FBX files were authored in centimeters.
const float METERS_TO_CENTIMETERS = 100.0f;
result.translation *= METERS_TO_CENTIMETERS;
}
if (version >= (int)JsonAvatarFrameVersion::JointDefaultPoseBits) {
result.rotationIsDefaultPose = array[2].toBool();
result.translationIsDefaultPose = array[3].toBool();
@ -2464,7 +2477,7 @@ void AvatarData::avatarEntityDataToJson(QJsonObject& root) const {
QJsonObject AvatarData::toJson() const {
QJsonObject root;
root[JSON_AVATAR_VERSION] = (int)JsonAvatarFrameVersion::JointDefaultPoseBits;
root[JSON_AVATAR_VERSION] = (int)JsonAvatarFrameVersion::JointUnscaledTranslations;
if (!getSkeletonModelURL().isEmpty()) {
root[JSON_AVATAR_BODY_MODEL] = getSkeletonModelURL().toString();
@ -2997,7 +3010,6 @@ void AvatarData::clearAvatarGrabData(const QUuid& grabID) {
_avatarGrabsLock.withWriteLock([&] {
if (_avatarGrabData.remove(grabID)) {
_avatarGrabDataChanged = true;
_deletedAvatarGrabs.insert(grabID);
}
});
}

9
libraries/avatars/src/AvatarData.h Normal file → Executable file
View file

@ -54,7 +54,6 @@
#include "AvatarTraits.h"
#include "HeadData.h"
#include "PathUtils.h"
#include "Grab.h"
#include <graphics/Material.h>
@ -67,8 +66,6 @@ using PackedAvatarEntityMap = QMap<QUuid, QByteArray>; // similar to AvatarEntit
using AvatarEntityIDs = QSet<QUuid>;
using AvatarGrabDataMap = QMap<QUuid, QByteArray>;
using AvatarGrabIDs = QSet<QUuid>;
using AvatarGrabMap = QMap<QUuid, GrabPointer>;
using AvatarDataSequenceNumber = uint16_t;
@ -496,6 +493,8 @@ public:
/// \return number of bytes parsed
virtual int parseDataFromBuffer(const QByteArray& buffer);
virtual void setCollisionWithOtherAvatarsFlags() {};
// Body Rotation (degrees)
float getBodyYaw() const;
void setBodyYaw(float bodyYaw);
@ -1473,8 +1472,6 @@ protected:
mutable ReadWriteLockable _avatarGrabsLock;
AvatarGrabDataMap _avatarGrabData;
bool _avatarGrabDataChanged { false }; // by network
AvatarGrabIDs _changedAvatarGrabs; // updated grab IDs -- changes needed to entities or physics
AvatarGrabIDs _deletedAvatarGrabs; // deleted grab IDs -- changes needed to entities or physics
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
ThreadSafeValueCache<glm::mat4> _sensorToWorldMatrixCache { glm::mat4() };
@ -1537,7 +1534,7 @@ protected:
}
bool updateAvatarGrabData(const QUuid& grabID, const QByteArray& grabData);
void clearAvatarGrabData(const QUuid& grabID);
virtual void clearAvatarGrabData(const QUuid& grabID);
private:
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);

View file

@ -15,6 +15,11 @@
namespace controller {
enum class HmdAvatarAlignmentType {
Eyes = 0, // align the user's eyes with the avatars eyes
Head // align the user's head with the avatars head
};
struct InputCalibrationData {
glm::mat4 sensorToWorldMat; // sensor to world
glm::mat4 avatarMat; // avatar to world
@ -29,6 +34,7 @@ struct InputCalibrationData {
glm::mat4 defaultLeftArm; // default pose for leftArm joint in sensor space
glm::mat4 defaultRightHand; // default pose for rightHand joint in sensor space
glm::mat4 defaultLeftHand; // default pose for leftHand joint in sensor space
HmdAvatarAlignmentType hmdAvatarAlignmentType;
};
enum class ChannelType {

View file

@ -31,6 +31,8 @@ public:
virtual void compositeExtra() override;
virtual void pluginUpdate() override {};
protected:
mutable bool _isThrottled = false;

View file

@ -20,6 +20,7 @@ public:
QImage getScreenshot(float aspectRatio = 0.0f) const override;
QImage getSecondaryCameraScreenshot() const override;
void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target, GLsync* fenceSync) override {};
void pluginUpdate() override {};
private:
static const QString NAME;
};

View file

@ -15,8 +15,10 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <QtCore/QFileInfo>
#include <QtGui/QImage>
#include <QtGui/QImageWriter>
#include <QtGui/QOpenGLFramebufferObject>
#include <NumericalConstants.h>
@ -30,6 +32,7 @@
#include <gl/OffscreenGLCanvas.h>
#include <gpu/Texture.h>
#include <gpu/FrameIO.h>
#include <shaders/Shaders.h>
#include <gpu/gl/GLShared.h>
#include <gpu/gl/GLBackend.h>
@ -465,11 +468,43 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
});
}
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor) {
renderFromTexture(batch, texture, viewport, scissor, gpu::FramebufferPointer());
void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const {
withOtherThreadContext([&] {
using namespace gpu;
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
FramebufferPointer framebuffer{ Framebuffer::create("captureFramebuffer") };
TextureCapturer captureLambda = [&](const std::string& filename, const gpu::TexturePointer& texture, uint16 layer) {
QImage image;
if (texture->getUsageType() == TextureUsageType::STRICT_RESOURCE) {
image = QImage{ 1, 1, QImage::Format_ARGB32 };
auto storedImage = texture->accessStoredMipFace(0, 0);
memcpy(image.bits(), storedImage->data(), image.sizeInBytes());
//if (texture == textureCache->getWhiteTexture()) {
//} else if (texture == textureCache->getBlackTexture()) {
//} else if (texture == textureCache->getBlueTexture()) {
//} else if (texture == textureCache->getGrayTexture()) {
} else {
ivec4 rect = { 0, 0, texture->getWidth(), texture->getHeight() };
framebuffer->setRenderBuffer(0, texture, layer);
glBackend->syncGPUObject(*framebuffer);
image = QImage{ rect.z, rect.w, QImage::Format_ARGB32 };
glBackend->downloadFramebuffer(framebuffer, rect, image);
}
QImageWriter(filename.c_str()).write(image);
};
if (_currentFrame) {
gpu::writeFrame(filename, _currentFrame, captureLambda);
}
});
}
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor, gpu::FramebufferPointer copyFbo /*=gpu::FramebufferPointer()*/) {
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor) {
renderFromTexture(batch, texture, viewport, scissor, nullptr);
}
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) {
auto fbo = gpu::FramebufferPointer();
batch.enableStereo(false);
batch.resetViewTransform();

View file

@ -47,7 +47,7 @@ public:
void endSession() override final;
bool eventFilter(QObject* receiver, QEvent* event) override;
bool isDisplayVisible() const override { return true; }
void captureFrame(const std::string& outputName) const override;
void submitFrame(const gpu::FramePointer& newFrame) override;
glm::uvec2 getRecommendedRenderSize() const override {
@ -113,8 +113,8 @@ protected:
// Plugin specific functionality to send the composed scene to the output window or device
virtual void internalPresent();
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor, gpu::FramebufferPointer fbo);
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor);
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& fbo);
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor);
virtual void updateFrameData();
virtual glm::mat4 getViewCorrection() { return glm::mat4(); }

View file

@ -69,7 +69,10 @@ bool DebugHmdDisplayPlugin::internalActivate() {
_eyeInverseProjections[1] = glm::inverse(_eyeProjections[1]);
_eyeOffsets[0][3] = vec4{ -0.0327499993, 0.0, -0.0149999997, 1.0 };
_eyeOffsets[1][3] = vec4{ 0.0327499993, 0.0, -0.0149999997, 1.0 };
_renderTargetSize = { 3024, 1680 };
// Test HMD per-eye resolution
_renderTargetSize = uvec2{ 1214 * 2 , 1344 };
// uncomment to capture a quarter size frame
//_renderTargetSize /= 2;
_cullingProjection = _eyeProjections[0];
// This must come after the initialization, so that the values calculated
// above are available during the customizeContext call (when not running

View file

@ -46,6 +46,8 @@ public:
virtual bool onDisplayTextureReset() override { _clearPreviewFlag = true; return true; };
void pluginUpdate() override {};
signals:
void hmdMountedChanged();
void hmdVisibleChanged(bool visible);

View file

@ -28,6 +28,8 @@ public:
// to the HMD plugins.
//virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override;
virtual void pluginUpdate() override {};
protected:
virtual bool internalActivate() override;
virtual void internalDeactivate() override;

View file

@ -141,7 +141,7 @@ std::shared_ptr<T> make_renderer(const EntityItemPointer& entity) {
return std::shared_ptr<T>(new T(entity), [](T* ptr) { ptr->deleteLater(); });
}
EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _entity(entity), _created(entity->getCreated()) {
EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _created(entity->getCreated()), _entity(entity) {
connect(entity.get(), &EntityItem::requestRenderUpdate, this, [&] {
_needsRenderUpdate = true;
emit requestRenderUpdate();
@ -479,7 +479,7 @@ glm::vec4 EntityRenderer::calculatePulseColor(const glm::vec4& color, const Puls
}
float t = ((float)(usecTimestampNow() - start)) / ((float)USECS_PER_SECOND);
float pulse = 0.5f * (glm::cos(t * (2.0f * M_PI) / pulseProperties.getPeriod()) + 1.0f) * (pulseProperties.getMax() - pulseProperties.getMin()) + pulseProperties.getMin();
float pulse = 0.5f * (cosf(t * (2.0f * (float)M_PI) / pulseProperties.getPeriod()) + 1.0f) * (pulseProperties.getMax() - pulseProperties.getMin()) + pulseProperties.getMin();
float outPulse = (1.0f - pulse);
glm::vec4 result = color;

View file

@ -286,4 +286,4 @@ void GizmoEntityRenderer::doRender(RenderArgs* args) {
}
}
}
}
}

View file

@ -42,10 +42,23 @@ ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Pare
// TODO: move into Procedural.cpp
PrepareStencil::testMaskDrawShape(*_procedural._opaqueState);
PrepareStencil::testMask(*_procedural._transparentState);
addMaterial(graphics::MaterialLayer(_material, 0), "0");
}
bool ShapeEntityRenderer::needsRenderUpdate() const {
if (_procedural.isEnabled() && _procedural.isFading()) {
if (resultWithReadLock<bool>([&] {
if (_procedural.isEnabled() && _procedural.isFading()) {
return true;
}
auto mat = _materials.find("0");
if (mat != _materials.end() && mat->second.needsUpdate()) {
return true;
}
return false;
})) {
return true;
}
@ -56,7 +69,11 @@ bool ShapeEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
if (_lastUserData != entity->getUserData()) {
return true;
}
if (_material != entity->getMaterial()) {
if (_color != entity->getColor()) {
return true;
}
if (_alpha != entity->getAlpha()) {
return true;
}
@ -83,10 +100,6 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
_procedural.setProceduralData(ProceduralData::parse(_lastUserData));
}
removeMaterial(_material, "0");
_material = entity->getMaterial();
addMaterial(graphics::MaterialLayer(_material, 0), "0");
_shape = entity->getShape();
_pulseProperties = entity->getPulseProperties();
});
@ -116,6 +129,20 @@ void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
_procedural.setIsFading(isFading);
}
});
glm::u8vec3 color = entity->getColor();
float alpha = entity->getAlpha();
if (_color != color || _alpha != alpha) {
_color = color;
_alpha = alpha;
_material->setAlbedo(toGlm(_color));
_material->setOpacity(_alpha);
auto materials = _materials.find("0");
if (materials != _materials.end()) {
materials->second.setNeedsUpdate(true);
}
}
}
bool ShapeEntityRenderer::isTransparent() const {
@ -129,18 +156,15 @@ bool ShapeEntityRenderer::isTransparent() const {
auto mat = _materials.find("0");
if (mat != _materials.end()) {
if (mat->second.top().material) {
auto matKey = mat->second.top().material->getKey();
if (matKey.isTranslucent()) {
return true;
}
if (mat->second.getMaterialKey().isTranslucent()) {
return true;
}
}
return Parent::isTransparent();
}
bool ShapeEntityRenderer::useMaterialPipeline() const {
bool ShapeEntityRenderer::useMaterialPipeline(const graphics::MultiMaterial& materials) const {
bool proceduralReady = resultWithReadLock<bool>([&] {
return _procedural.isReady();
});
@ -148,12 +172,7 @@ bool ShapeEntityRenderer::useMaterialPipeline() const {
return false;
}
graphics::MaterialKey drawMaterialKey;
auto mat = _materials.find("0");
if (mat != _materials.end() && mat->second.top().material) {
drawMaterialKey = mat->second.top().material->getKey();
}
graphics::MaterialKey drawMaterialKey = materials.getMaterialKey();
if (drawMaterialKey.isEmissive() || drawMaterialKey.isUnlit() || drawMaterialKey.isMetallic() || drawMaterialKey.isScattering()) {
return true;
}
@ -168,11 +187,13 @@ bool ShapeEntityRenderer::useMaterialPipeline() const {
}
ShapeKey ShapeEntityRenderer::getShapeKey() {
if (useMaterialPipeline()) {
graphics::MaterialKey drawMaterialKey;
if (_materials["0"].top().material) {
drawMaterialKey = _materials["0"].top().material->getKey();
}
auto mat = _materials.find("0");
if (mat != _materials.end() && mat->second.needsUpdate()) {
RenderPipelines::updateMultiMaterial(mat->second);
}
if (mat != _materials.end() && useMaterialPipeline(mat->second)) {
graphics::MaterialKey drawMaterialKey = mat->second.getMaterialKey();
bool isTranslucent = drawMaterialKey.isTranslucent();
bool hasTangents = drawMaterialKey.isNormalMap();
@ -225,7 +246,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
gpu::Batch& batch = *args->_batch;
std::shared_ptr<graphics::Material> mat;
graphics::MultiMaterial materials;
auto geometryCache = DependencyManager::get<GeometryCache>();
GeometryCache::Shape geometryShape;
bool proceduralRender = false;
@ -233,30 +254,25 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
withReadLock([&] {
geometryShape = geometryCache->getShapeForEntityShape(_shape);
batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation
mat = _materials["0"].top().material;
if (mat) {
outColor = glm::vec4(mat->getAlbedo(), mat->getOpacity());
outColor = EntityRenderer::calculatePulseColor(outColor, _pulseProperties, _created);
if (_procedural.isReady()) {
outColor = _procedural.getColor(outColor);
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
_procedural.prepare(batch, _position, _dimensions, _orientation, ProceduralProgramKey(outColor.a < 1.0f));
proceduralRender = true;
}
materials = _materials["0"];
auto& schema = materials.getSchemaBuffer().get<graphics::MultiMaterial::Schema>();
outColor = glm::vec4(schema._albedo, schema._opacity);
outColor = EntityRenderer::calculatePulseColor(outColor, _pulseProperties, _created);
if (_procedural.isReady()) {
outColor = _procedural.getColor(outColor);
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
_procedural.prepare(batch, _position, _dimensions, _orientation, ProceduralProgramKey(outColor.a < 1.0f));
proceduralRender = true;
}
});
if (!mat) {
return;
}
if (proceduralRender) {
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
geometryCache->renderWireShape(batch, geometryShape, outColor);
} else {
geometryCache->renderShape(batch, geometryShape, outColor);
}
} else if (!useMaterialPipeline()) {
} else if (!useMaterialPipeline(materials)) {
// FIXME, support instanced multi-shape rendering using multidraw indirect
outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
render::ShapePipelinePointer pipeline;
@ -272,7 +288,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
}
} else {
if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
RenderPipelines::bindMaterial(mat, batch, args->_enableTexturing);
RenderPipelines::bindMaterials(materials, batch, args->_enableTexturing);
args->_details._materialSwitches++;
}
@ -291,8 +307,9 @@ scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel() {
{
std::lock_guard<std::mutex> lock(_materialsLock);
result.appendMaterials(_materials);
if (_materials["0"].top().material) {
vertexColor = _materials["0"].top().material->getAlbedo();
auto materials = _materials.find("0");
if (materials != _materials.end()) {
vertexColor = materials->second.getSchemaBuffer().get<graphics::MultiMaterial::Schema>()._albedo;
}
}
if (auto mesh = geometryCache->meshFromShape(geometryShape, vertexColor)) {

View file

@ -35,13 +35,17 @@ private:
virtual void doRender(RenderArgs* args) override;
virtual bool isTransparent() const override;
bool useMaterialPipeline() const;
bool useMaterialPipeline(const graphics::MultiMaterial& materials) const;
Procedural _procedural;
QString _lastUserData;
entity::Shape _shape { entity::Sphere };
PulsePropertyGroup _pulseProperties;
std::shared_ptr<graphics::Material> _material;
std::shared_ptr<graphics::Material> _material { std::make_shared<graphics::Material>() };
glm::u8vec3 _color;
float _alpha;
glm::vec3 _position;
glm::vec3 _dimensions;
glm::quat _orientation;

View file

@ -3425,7 +3425,19 @@ void EntityItem::addGrab(GrabPointer grab) {
enableNoBootstrap();
SpatiallyNestable::addGrab(grab);
if (getDynamic() && getParentID().isNull()) {
if (!getParentID().isNull()) {
return;
}
int jointIndex = grab->getParentJointIndex();
bool isFarGrab = jointIndex == FARGRAB_RIGHTHAND_INDEX
|| jointIndex == FARGRAB_LEFTHAND_INDEX
|| jointIndex == FARGRAB_MOUSE_INDEX;
// GRAB HACK: FarGrab doesn't work on non-dynamic things yet, but we really really want NearGrab
// (aka Hold) to work for such ojects, hence we filter the useAction case like so:
bool useAction = getDynamic() || (_physicsInfo && !isFarGrab);
if (useAction) {
EntityTreePointer entityTree = getTree();
assert(entityTree);
EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr;
@ -3436,13 +3448,11 @@ void EntityItem::addGrab(GrabPointer grab) {
EntityDynamicType dynamicType;
QVariantMap arguments;
int grabParentJointIndex =grab->getParentJointIndex();
if (grabParentJointIndex == FARGRAB_RIGHTHAND_INDEX || grabParentJointIndex == FARGRAB_LEFTHAND_INDEX ||
grabParentJointIndex == FARGRAB_MOUSE_INDEX) {
if (isFarGrab) {
// add a far-grab action
dynamicType = DYNAMIC_TYPE_FAR_GRAB;
arguments["otherID"] = grab->getOwnerID();
arguments["otherJointIndex"] = grabParentJointIndex;
arguments["otherJointIndex"] = jointIndex;
arguments["targetPosition"] = vec3ToQMap(grab->getPositionalOffset());
arguments["targetRotation"] = quatToQMap(grab->getRotationalOffset());
arguments["linearTimeScale"] = 0.05;
@ -3463,11 +3473,23 @@ void EntityItem::addGrab(GrabPointer grab) {
grab->setActionID(actionID);
_grabActions[actionID] = action;
simulation->addDynamic(action);
markDirtyFlags(Simulation::DIRTY_MOTION_TYPE);
simulation->changeEntity(getThisPointer());
}
}
void EntityItem::removeGrab(GrabPointer grab) {
int oldNumGrabs = _grabs.size();
SpatiallyNestable::removeGrab(grab);
if (!getDynamic() && _grabs.size() != oldNumGrabs) {
// GRAB HACK: the expected behavior is for non-dynamic grabbed things to NOT be throwable
// so we slam the velocities to zero here whenever the number of grabs change.
// NOTE: if there is still another grab in effect this shouldn't interfere with the object's motion
// because that grab will slam the position+velocities next frame.
setLocalVelocity(glm::vec3(0.0f));
setAngularVelocity(glm::vec3(0.0f));
}
markDirtyFlags(Simulation::DIRTY_MOTION_TYPE);
QUuid actionID = grab->getActionID();
if (!actionID.isNull()) {

View file

@ -1023,7 +1023,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* parse the JSON string into a JavaScript object of name, URL pairs. <em>Read-only.</em>
*
* @property {ShapeType} shapeType="none" - The shape of the collision hull used if collisions are enabled.
* @property {string} compoundShapeURL="" - The OBJ file to use for the compound shape if <code>shapeType</code> is
* @property {string} compoundShapeURL="" - The model file to use for the compound shape if <code>shapeType</code> is
* <code>"compound"</code>.
*
* @property {Entities.AnimationProperties} animation - An animation to play on the model.
@ -1373,7 +1373,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* @property {ShapeType} shapeType="box" - The shape of the volume in which the zone's lighting effects and avatar
* permissions have effect. Reverts to the default value if set to <code>"none"</code>, or set to <code>"compound"</code>
* and <code>compoundShapeURL</code> is <code>""</code>.
* @property {string} compoundShapeURL="" - The OBJ file to use for the compound shape if <code>shapeType</code> is
* @property {string} compoundShapeURL="" - The model file to use for the compound shape if <code>shapeType</code> is
* <code>"compound"</code>.
*
* @property {string} keyLightMode="inherit" - Configures the key light in the zone. Possible values:<br />

View file

@ -112,7 +112,6 @@ EntityItemPointer ShapeEntityItem::sphereFactory(const EntityItemID& entityID, c
ShapeEntityItem::ShapeEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
_type = EntityTypes::Shape;
_volumeMultiplier *= PI / 6.0f;
_material = std::make_shared<graphics::Material>();
}
EntityItemProperties ShapeEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
@ -234,7 +233,6 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
void ShapeEntityItem::setColor(const glm::u8vec3& value) {
withWriteLock([&] {
_color = value;
_material->setAlbedo(toGlm(_color));
});
}
@ -247,7 +245,6 @@ glm::u8vec3 ShapeEntityItem::getColor() const {
void ShapeEntityItem::setAlpha(float alpha) {
withWriteLock([&] {
_alpha = alpha;
_material->setOpacity(alpha);
});
}

View file

@ -101,8 +101,6 @@ public:
virtual void computeShapeInfo(ShapeInfo& info) override;
virtual ShapeType getShapeType() const override;
std::shared_ptr<graphics::Material> getMaterial() { return _material; }
PulsePropertyGroup getPulseProperties() const;
protected:
@ -115,8 +113,6 @@ protected:
//! prior functionality where new or unsupported shapes are treated as
//! ellipsoids.
ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID };
std::shared_ptr<graphics::Material> _material;
};
#endif // hifi_ShapeEntityItem_h

View file

@ -96,11 +96,11 @@ const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1;
// When poking objects with scripts an observer will bid at SCRIPT_EDIT priority.
const uint8_t SCRIPT_GRAB_SIMULATION_PRIORITY = 128;
const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1;
const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = 255;
// PERSONAL priority (needs a better name) is the level at which a simulation observer owns its own avatar
// which really just means: things that collide with it will be bid at a priority level one lower
const uint8_t PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY;
const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = PERSONAL_SIMULATION_PRIORITY;
class SimulationOwner {

View file

@ -131,6 +131,7 @@ public:
glm::vec3 geometricTranslation;
glm::quat geometricRotation;
glm::vec3 geometricScaling;
bool isLimbNode; // is this FBXModel transform is a "LimbNode" i.e. a joint
};
glm::mat4 getGlobalTransform(const QMultiMap<QString, QString>& _connectionParentMap,
@ -559,9 +560,11 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
glm::vec3 geometricRotation;
glm::vec3 rotationMin, rotationMax;
bool isLimbNode = object.properties.size() >= 3 && object.properties.at(2) == "LimbNode";
FBXModel fbxModel = { name, -1, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(),
glm::mat4(), glm::vec3(), glm::vec3(),
false, glm::vec3(), glm::quat(), glm::vec3(1.0f) };
glm::mat4(), glm::vec3(), glm::vec3(),
false, glm::vec3(), glm::quat(), glm::vec3(1.0f), isLimbNode };
ExtractedMesh* mesh = NULL;
QVector<ExtractedBlendshape> blendshapes;
foreach (const FBXNode& subobject, object.children) {
@ -752,17 +755,17 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
} else if (subobject.name == "Texture_Alpha_Source" && subobject.properties.length() >= TEXTURE_ALPHA_SOURCE_MIN_SIZE) {
tex.assign<uint8_t>(tex.alphaSource, subobject.properties.at(0).value<int>());
} else if (subobject.name == "ModelUVTranslation" && subobject.properties.length() >= MODEL_UV_TRANSLATION_MIN_SIZE) {
tex.assign(tex.UVTranslation, glm::vec2(subobject.properties.at(0).value<double>(),
subobject.properties.at(1).value<double>()));
auto newTranslation = glm::vec3(subobject.properties.at(0).value<double>(), subobject.properties.at(1).value<double>(), 0.0);
tex.assign(tex.translation, tex.translation + newTranslation);
} else if (subobject.name == "ModelUVScaling" && subobject.properties.length() >= MODEL_UV_SCALING_MIN_SIZE) {
tex.assign(tex.UVScaling, glm::vec2(subobject.properties.at(0).value<double>(),
subobject.properties.at(1).value<double>()));
if (tex.UVScaling.x == 0.0f) {
tex.UVScaling.x = 1.0f;
auto newScaling = glm::vec3(subobject.properties.at(0).value<double>(), subobject.properties.at(1).value<double>(), 1.0);
if (newScaling.x == 0.0f) {
newScaling.x = 1.0f;
}
if (tex.UVScaling.y == 0.0f) {
tex.UVScaling.y = 1.0f;
if (newScaling.y == 0.0f) {
newScaling.y = 1.0f;
}
tex.assign(tex.scaling, tex.scaling * newScaling);
} else if (subobject.name == "Cropping" && subobject.properties.length() >= CROPPING_MIN_SIZE) {
tex.assign(tex.cropping, glm::vec4(subobject.properties.at(0).value<int>(),
subobject.properties.at(1).value<int>(),
@ -790,20 +793,21 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
} else if (property.properties.at(0) == USE_MATERIAL) {
tex.assign<bool>(tex.useMaterial, property.properties.at(index).value<int>());
} else if (property.properties.at(0) == TRANSLATION) {
tex.assign(tex.translation, getVec3(property.properties, index));
tex.assign(tex.translation, tex.translation + getVec3(property.properties, index));
} else if (property.properties.at(0) == ROTATION) {
tex.assign(tex.rotation, getVec3(property.properties, index));
} else if (property.properties.at(0) == SCALING) {
tex.assign(tex.scaling, getVec3(property.properties, index));
if (tex.scaling.x == 0.0f) {
tex.scaling.x = 1.0f;
auto newScaling = getVec3(property.properties, index);
if (newScaling.x == 0.0f) {
newScaling.x = 1.0f;
}
if (tex.scaling.y == 0.0f) {
tex.scaling.y = 1.0f;
if (newScaling.y == 0.0f) {
newScaling.y = 1.0f;
}
if (tex.scaling.z == 0.0f) {
tex.scaling.z = 1.0f;
if (newScaling.z == 0.0f) {
newScaling.z = 1.0f;
}
tex.assign(tex.scaling, tex.scaling * newScaling);
}
#if defined(DEBUG_FBXSERIALIZER)
else {
@ -848,6 +852,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
}
} else if (object.name == "Material") {
HFMMaterial material;
MaterialParam materialParam;
material.name = (object.properties.at(1).toString());
foreach (const FBXNode& subobject, object.children) {
bool properties = false;
@ -892,6 +897,10 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
static const QVariant MAYA_EMISSIVE_INTENSITY = QByteArray("Maya|emissive_intensity");
static const QVariant MAYA_USE_EMISSIVE_MAP = QByteArray("Maya|use_emissive_map");
static const QVariant MAYA_USE_AO_MAP = QByteArray("Maya|use_ao_map");
static const QVariant MAYA_UV_SCALE = QByteArray("Maya|uv_scale");
static const QVariant MAYA_UV_OFFSET = QByteArray("Maya|uv_offset");
static const int MAYA_UV_OFFSET_PROPERTY_LENGTH = 6;
static const int MAYA_UV_SCALE_PROPERTY_LENGTH = 6;
@ -980,6 +989,27 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
material.isPBSMaterial = true;
material.useOcclusionMap = (bool)property.properties.at(index).value<double>();
} else if (property.properties.at(0) == MAYA_UV_SCALE) {
if (property.properties.size() == MAYA_UV_SCALE_PROPERTY_LENGTH) {
// properties: { "Maya|uv_scale", "Vector2D", "Vector2", nothing, double, double }
glm::vec3 scale = glm::vec3(property.properties.at(4).value<double>(), property.properties.at(5).value<double>(), 1.0);
if (scale.x == 0.0f) {
scale.x = 1.0f;
}
if (scale.y == 0.0f) {
scale.y = 1.0f;
}
if (scale.z == 0.0f) {
scale.z = 1.0f;
}
materialParam.scaling *= scale;
}
} else if (property.properties.at(0) == MAYA_UV_OFFSET) {
if (property.properties.size() == MAYA_UV_OFFSET_PROPERTY_LENGTH) {
// properties: { "Maya|uv_offset", "Vector2D", "Vector2", nothing, double, double }
glm::vec3 translation = glm::vec3(property.properties.at(4).value<double>(), property.properties.at(5).value<double>(), 1.0);
materialParam.translation += translation;
}
} else {
const QString propname = property.properties.at(0).toString();
unknowns.push_back(propname.toStdString());
@ -1001,6 +1031,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
}
material.materialID = getID(object.properties);
_hfmMaterials.insert(material.materialID, material);
_materialParams.insert(material.materialID, materialParam);
} else if (object.name == "NodeAttribute") {
@ -1258,6 +1289,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
// convert the models to joints
QVariantList freeJoints = mapping.values("freeJoint");
hfmModel.hasSkeletonJoints = false;
foreach (const QString& modelID, modelIDs) {
const FBXModel& fbxModel = fbxModels[modelID];
HFMJoint joint;
@ -1288,6 +1320,8 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
joint.geometricTranslation = fbxModel.geometricTranslation;
joint.geometricRotation = fbxModel.geometricRotation;
joint.geometricScaling = fbxModel.geometricScaling;
joint.isSkeletonJoint = fbxModel.isLimbNode;
hfmModel.hasSkeletonJoints = (hfmModel.hasSkeletonJoints || joint.isSkeletonJoint);
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
@ -1311,14 +1345,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
joint.name = hfmModel.hfmToHifiJointNameMapping.key(fbxModel.name);
}
foreach (const QString& childID, _connectionChildMap.values(modelID)) {
QString type = typeFlags.value(childID);
if (!type.isEmpty()) {
hfmModel.hasSkeletonJoints |= (joint.isSkeletonJoint = type.toLower().contains("Skeleton"));
break;
}
}
joint.bindTransformFoundInCluster = false;
hfmModel.joints.append(joint);
@ -1439,7 +1465,9 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
materialIndex++;
} else if (_textureFilenames.contains(childID)) {
HFMTexture texture = getTexture(childID);
// NOTE (Sabrina 2019/01/11): getTextures now takes in the materialID as a second parameter, because FBX material nodes can sometimes have uv transform information (ex: "Maya|uv_scale")
// I'm leaving the second parameter blank right now as this code may never be used.
HFMTexture texture = getTexture(childID, "");
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
int partTexture = extracted.partMaterialTextures.at(j).second;
if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) {
@ -1452,9 +1480,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
}
}
extracted.mesh.createMeshTangents(generateTangents);
extracted.mesh.createBlendShapeTangents(generateTangents);
// find the clusters with which the mesh is associated
QVector<QString> clusterIDs;
foreach (const QString& childID, _connectionChildMap.values(it.key())) {

View file

@ -37,8 +37,6 @@ class FBXNode;
class TextureParam {
public:
glm::vec2 UVTranslation;
glm::vec2 UVScaling;
glm::vec4 cropping;
QString UVSet;
@ -63,8 +61,6 @@ public:
bool isDefault;
TextureParam() :
UVTranslation(0.0f),
UVScaling(1.0f),
cropping(0.0f),
UVSet("map1"),
translation(0.0f),
@ -77,8 +73,6 @@ public:
{}
TextureParam(const TextureParam& src) :
UVTranslation(src.UVTranslation),
UVScaling(src.UVScaling),
cropping(src.cropping),
UVSet(src.UVSet),
translation(src.translation),
@ -92,6 +86,22 @@ public:
};
class MaterialParam {
public:
glm::vec3 translation;
glm::vec3 scaling;
MaterialParam() :
translation(0.0),
scaling(1.0)
{}
MaterialParam(const MaterialParam& src) :
translation(src.translation),
scaling(src.scaling)
{}
};
class ExtractedMesh;
class FBXSerializer : public HFMSerializer {
@ -114,7 +124,7 @@ public:
static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate = true);
QHash<QString, ExtractedMesh> meshes;
HFMTexture getTexture(const QString& textureID);
HFMTexture getTexture(const QString& textureID, const QString& materialID);
QHash<QString, QString> _textureNames;
// Hashes the original RelativeFilename of textures
@ -141,6 +151,7 @@ public:
QHash<QString, QString> occlusionTextures;
QHash<QString, HFMMaterial> _hfmMaterials;
QHash<QString, MaterialParam> _materialParams;
void consolidateHFMMaterials(const QVariantHash& mapping);

View file

@ -27,7 +27,7 @@
#include <hfm/ModelFormatLogging.h>
HFMTexture FBXSerializer::getTexture(const QString& textureID) {
HFMTexture FBXSerializer::getTexture(const QString& textureID, const QString& materialID) {
HFMTexture texture;
const QByteArray& filepath = _textureFilepaths.value(textureID);
texture.content = _textureContent.value(filepath);
@ -45,8 +45,8 @@ HFMTexture FBXSerializer::getTexture(const QString& textureID) {
if (_textureParams.contains(textureID)) {
auto p = _textureParams.value(textureID);
texture.transform.setTranslation(p.translation);
texture.transform.setRotation(glm::quat(glm::radians(p.rotation)));
texture.transform.postTranslate(p.translation);
texture.transform.postRotate(glm::quat(glm::radians(p.rotation)));
auto scaling = p.scaling;
// Protect from bad scaling which should never happen
@ -59,13 +59,19 @@ HFMTexture FBXSerializer::getTexture(const QString& textureID) {
if (scaling.z == 0.0f) {
scaling.z = 1.0f;
}
texture.transform.setScale(scaling);
texture.transform.postScale(scaling);
if ((p.UVSet != "map1") && (p.UVSet != "UVSet0")) {
texture.texcoordSet = 1;
}
texture.texcoordSetName = p.UVSet;
}
auto materialParamItr = _materialParams.find(materialID);
if (materialParamItr != _materialParams.end()) {
auto& materialParam = materialParamItr.value();
texture.transform.postTranslate(materialParam.translation);
texture.transform.postScale(materialParam.scaling);
}
return texture;
}
@ -102,12 +108,12 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
material.diffuseFactor = 1.0;
}
diffuseTexture = getTexture(diffuseTextureID);
diffuseTexture = getTexture(diffuseTextureID, material.materialID);
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
foreach(const QString& childTextureID, _connectionChildMap.values(diffuseTextureID)) {
if (_textureFilenames.contains(childTextureID)) {
diffuseTexture = getTexture(diffuseTextureID);
diffuseTexture = getTexture(diffuseTextureID, material.materialID);
}
}
@ -122,7 +128,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
transparentTextureID = diffuseTextureID;
}
if (!transparentTextureID.isNull()) {
transparentTexture = getTexture(transparentTextureID);
transparentTexture = getTexture(transparentTextureID, material.materialID);
material.opacityTexture = transparentTexture;
detectDifferentUVs |= (transparentTexture.texcoordSet != 0) || (!transparentTexture.transform.isIdentity());
}
@ -131,13 +137,13 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
QString bumpTextureID = bumpTextures.value(material.materialID);
QString normalTextureID = normalTextures.value(material.materialID);
if (!normalTextureID.isNull()) {
normalTexture = getTexture(normalTextureID);
normalTexture = getTexture(normalTextureID, material.materialID);
normalTexture.isBumpmap = false;
material.normalTexture = normalTexture;
detectDifferentUVs |= (normalTexture.texcoordSet != 0) || (!normalTexture.transform.isIdentity());
} else if (!bumpTextureID.isNull()) {
normalTexture = getTexture(bumpTextureID);
normalTexture = getTexture(bumpTextureID, material.materialID);
normalTexture.isBumpmap = true;
material.normalTexture = normalTexture;
@ -147,7 +153,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
HFMTexture specularTexture;
QString specularTextureID = specularTextures.value(material.materialID);
if (!specularTextureID.isNull()) {
specularTexture = getTexture(specularTextureID);
specularTexture = getTexture(specularTextureID, material.materialID);
detectDifferentUVs |= (specularTexture.texcoordSet != 0) || (!specularTexture.transform.isIdentity());
material.specularTexture = specularTexture;
}
@ -155,7 +161,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
HFMTexture metallicTexture;
QString metallicTextureID = metallicTextures.value(material.materialID);
if (!metallicTextureID.isNull()) {
metallicTexture = getTexture(metallicTextureID);
metallicTexture = getTexture(metallicTextureID, material.materialID);
detectDifferentUVs |= (metallicTexture.texcoordSet != 0) || (!metallicTexture.transform.isIdentity());
material.metallicTexture = metallicTexture;
}
@ -163,7 +169,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
HFMTexture roughnessTexture;
QString roughnessTextureID = roughnessTextures.value(material.materialID);
if (!roughnessTextureID.isNull()) {
roughnessTexture = getTexture(roughnessTextureID);
roughnessTexture = getTexture(roughnessTextureID, material.materialID);
material.roughnessTexture = roughnessTexture;
detectDifferentUVs |= (roughnessTexture.texcoordSet != 0) || (!roughnessTexture.transform.isIdentity());
}
@ -171,7 +177,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
HFMTexture shininessTexture;
QString shininessTextureID = shininessTextures.value(material.materialID);
if (!shininessTextureID.isNull()) {
shininessTexture = getTexture(shininessTextureID);
shininessTexture = getTexture(shininessTextureID, material.materialID);
material.glossTexture = shininessTexture;
detectDifferentUVs |= (shininessTexture.texcoordSet != 0) || (!shininessTexture.transform.isIdentity());
}
@ -179,7 +185,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
HFMTexture emissiveTexture;
QString emissiveTextureID = emissiveTextures.value(material.materialID);
if (!emissiveTextureID.isNull()) {
emissiveTexture = getTexture(emissiveTextureID);
emissiveTexture = getTexture(emissiveTextureID, material.materialID);
detectDifferentUVs |= (emissiveTexture.texcoordSet != 0) || (!emissiveTexture.transform.isIdentity());
material.emissiveTexture = emissiveTexture;
@ -202,7 +208,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
}
if (!occlusionTextureID.isNull()) {
occlusionTexture = getTexture(occlusionTextureID);
occlusionTexture = getTexture(occlusionTextureID, material.materialID);
detectDifferentUVs |= (occlusionTexture.texcoordSet != 0) || (!emissiveTexture.transform.isIdentity());
material.occlusionTexture = occlusionTexture;
}
@ -222,7 +228,7 @@ void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
}
if (_loadLightmaps && !ambientTextureID.isNull()) {
ambientTexture = getTexture(ambientTextureID);
ambientTexture = getTexture(ambientTextureID, material.materialID);
detectDifferentUVs |= (ambientTexture.texcoordSet != 0) || (!ambientTexture.transform.isIdentity());
material.lightmapTexture = ambientTexture;
material.lightmapParams = lightmapParams;

View file

@ -118,6 +118,8 @@ void gl::setSwapInterval(int interval) {
wglSwapIntervalEXT(interval);
#elif defined(Q_OS_MAC)
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
#elif defined(Q_OS_ANDROID)
eglSwapInterval(eglGetCurrentDisplay(), interval);
#else
Q_UNUSED(interval);
#endif

View file

@ -34,22 +34,9 @@ bool Context::USE_CUSTOM_CONTEXT { true };
#endif
bool Context::enableDebugLogger() {
#if defined(Q_OS_MAC)
// OSX does not support GL_KHR_debug or GL_ARB_debug_output
return false;
#else
#if defined(DEBUG) || defined(USE_GLES)
static bool enableDebugLogger = true;
#else
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL");
static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
#endif
return enableDebugLogger;
#endif
return gl::debugContextEnabled();
}
std::atomic<size_t> Context::_totalSwapchainMemoryUsage { 0 };
size_t Context::getSwapchainMemoryUsage() { return _totalSwapchainMemoryUsage.load(); }

View file

@ -52,6 +52,11 @@ void Context::moveToThread(QThread* thread) {
}
void Context::debugMessageHandler(const QOpenGLDebugMessage& debugMessage) {
auto type = debugMessage.type();
if (type == QOpenGLDebugMessage::PerformanceType) {
return;
}
auto severity = debugMessage.severity();
switch (severity) {
case QOpenGLDebugMessage::NotificationSeverity:
@ -60,13 +65,13 @@ void Context::debugMessageHandler(const QOpenGLDebugMessage& debugMessage) {
default:
break;
}
qDebug(glLogging) << debugMessage;
qWarning(glLogging) << debugMessage;
return;
}
void Context::setupDebugLogging(QOpenGLContext *context) {
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(context);
QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, nullptr, [](const QOpenGLDebugMessage& message){
QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, context, [](const QOpenGLDebugMessage& message){
Context::debugMessageHandler(message);
});
if (logger->initialize()) {

View file

@ -198,11 +198,48 @@ namespace gl {
bool checkGLErrorDebug(const char* name) {
#ifdef DEBUG
// Disabling error checking macro on Android debug builds for now,
// as it throws off performance testing, which must be done on
// Debug builds
#if defined(DEBUG) && !defined(Q_OS_ANDROID)
return checkGLError(name);
#else
Q_UNUSED(name);
return false;
#endif
}
// Enables annotation of captures made by tools like renderdoc
bool khrDebugEnabled() {
static std::once_flag once;
static bool khrDebug = false;
std::call_once(once, [&] {
khrDebug = nullptr != glPushDebugGroupKHR;
});
return khrDebug;
}
// Enables annotation of captures made by tools like renderdoc
bool extDebugMarkerEnabled() {
static std::once_flag once;
static bool extMarker = false;
std::call_once(once, [&] {
extMarker = nullptr != glPushGroupMarkerEXT;
});
return extMarker;
}
bool debugContextEnabled() {
#if defined(Q_OS_MAC)
// OSX does not support GL_KHR_debug or GL_ARB_debug_output
static bool enableDebugLogger = false;
#elif defined(DEBUG) || defined(USE_GLES)
//static bool enableDebugLogger = true;
static bool enableDebugLogger = false;
#else
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL");
static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
#endif
return enableDebugLogger;
}
}

View file

@ -37,7 +37,13 @@ bool isRenderThread();
namespace gl {
void globalLock();
void globalRelease(bool finish = true);
bool debugContextEnabled();
bool khrDebugEnabled();
bool extDebugMarkerEnabled();
void withSavedContext(const std::function<void()>& f);
bool checkGLError(const char* name);

View file

@ -392,8 +392,38 @@ void GLBackend::renderPassDraw(const Batch& batch) {
}
}
// Support annotating captures in tools like Renderdoc
class GlDuration {
public:
#ifdef USE_GLES
GlDuration(const char* name) {
// We need to use strlen here instead of -1, because the Snapdragon profiler
// will crash otherwise
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, strlen(name), name);
}
~GlDuration() {
glPopDebugGroup();
}
#else
GlDuration(const char* name) {
if (::gl::khrDebugEnabled()) {
glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, 0, -1, name);
}
}
~GlDuration() {
if (::gl::khrDebugEnabled()) {
glPopDebugGroupKHR();
}
}
#endif
};
#define GL_PROFILE_RANGE(category, name) \
PROFILE_RANGE(category, name); \
GlDuration glProfileRangeThis(name);
void GLBackend::render(const Batch& batch) {
PROFILE_RANGE(render_gpu_gl, batch.getName());
GL_PROFILE_RANGE(render_gpu_gl, batch.getName().c_str());
_transform._skybox = _stereo._skybox = batch.isSkyboxEnabled();
// Allow the batch to override the rendering stereo settings
@ -406,7 +436,7 @@ void GLBackend::render(const Batch& batch) {
_transform._projectionJitter = Vec2(0.0f, 0.0f);
{
PROFILE_RANGE(render_gpu_gl_detail, "Transfer");
GL_PROFILE_RANGE(render_gpu_gl_detail, "Transfer");
renderPassTransfer(batch);
}
@ -416,7 +446,7 @@ void GLBackend::render(const Batch& batch) {
}
#endif
{
PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render");
GL_PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render");
renderPassDraw(batch);
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED

View file

@ -54,153 +54,8 @@
#define GPU_STEREO_CAMERA_BUFFER
#endif
//
// GL Backend pointer storage mechanism
// One of the following three defines must be defined.
// GPU_POINTER_STORAGE_SHARED
// The platonic ideal, use references to smart pointers.
// However, this produces artifacts because there are too many places in the code right now that
// create temporary values (undesirable smart pointer duplications) and then those temp variables
// get passed on and have their reference taken, and then invalidated
// GPU_POINTER_STORAGE_REF
// Raw pointer manipulation. Seems more dangerous than the reference wrappers,
// but in practice, the danger of grabbing a reference to a temporary variable
// is causing issues
// GPU_POINTER_STORAGE_RAW
#if defined(USE_GLES)
#define GPU_POINTER_STORAGE_SHARED
#else
#define GPU_POINTER_STORAGE_RAW
#endif
namespace gpu { namespace gl {
#if defined(GPU_POINTER_STORAGE_SHARED)
template <typename T>
static inline bool compare(const std::shared_ptr<T>& a, const std::shared_ptr<T>& b) {
return a == b;
}
template <typename T>
static inline T* acquire(const std::shared_ptr<T>& pointer) {
return pointer.get();
}
template <typename T>
static inline void reset(std::shared_ptr<T>& pointer) {
return pointer.reset();
}
template <typename T>
static inline bool valid(const std::shared_ptr<T>& pointer) {
return pointer.operator bool();
}
template <typename T>
static inline void assign(std::shared_ptr<T>& pointer, const std::shared_ptr<T>& source) {
pointer = source;
}
using BufferReference = BufferPointer;
using TextureReference = TexturePointer;
using FramebufferReference = FramebufferPointer;
using FormatReference = Stream::FormatPointer;
using PipelineReference = PipelinePointer;
#define GPU_REFERENCE_INIT_VALUE nullptr
#elif defined(GPU_POINTER_STORAGE_REF)
template <typename T>
class PointerReferenceWrapper : public std::reference_wrapper<const std::shared_ptr<T>> {
using Parent = std::reference_wrapper<const std::shared_ptr<T>>;
public:
using Pointer = std::shared_ptr<T>;
PointerReferenceWrapper() : Parent(EMPTY()) {}
PointerReferenceWrapper(const Pointer& pointer) : Parent(pointer) {}
void clear() { *this = EMPTY(); }
private:
static const Pointer& EMPTY() {
static const Pointer EMPTY_VALUE;
return EMPTY_VALUE;
};
};
template <typename T>
static bool compare(const PointerReferenceWrapper<T>& reference, const std::shared_ptr<T>& pointer) {
return reference.get() == pointer;
}
template <typename T>
static inline T* acquire(const PointerReferenceWrapper<T>& reference) {
return reference.get().get();
}
template <typename T>
static void assign(PointerReferenceWrapper<T>& reference, const std::shared_ptr<T>& pointer) {
reference = pointer;
}
template <typename T>
static bool valid(const PointerReferenceWrapper<T>& reference) {
return reference.get().operator bool();
}
template <typename T>
static inline void reset(PointerReferenceWrapper<T>& reference) {
return reference.clear();
}
using BufferReference = PointerReferenceWrapper<Buffer>;
using TextureReference = PointerReferenceWrapper<Texture>;
using FramebufferReference = PointerReferenceWrapper<Framebuffer>;
using FormatReference = PointerReferenceWrapper<Stream::Format>;
using PipelineReference = PointerReferenceWrapper<Pipeline>;
#define GPU_REFERENCE_INIT_VALUE
#elif defined(GPU_POINTER_STORAGE_RAW)
template <typename T>
static bool compare(const T* const& rawPointer, const std::shared_ptr<T>& pointer) {
return rawPointer == pointer.get();
}
template <typename T>
static inline T* acquire(T*& rawPointer) {
return rawPointer;
}
template <typename T>
static inline bool valid(const T* const& rawPointer) {
return rawPointer;
}
template <typename T>
static inline void reset(T*& rawPointer) {
rawPointer = nullptr;
}
template <typename T>
static inline void assign(T*& rawPointer, const std::shared_ptr<T>& pointer) {
rawPointer = pointer.get();
}
using BufferReference = Buffer*;
using TextureReference = Texture*;
using FramebufferReference = Framebuffer*;
using FormatReference = Stream::Format*;
using PipelineReference = Pipeline*;
#define GPU_REFERENCE_INIT_VALUE nullptr
#endif
class GLBackend : public Backend, public std::enable_shared_from_this<GLBackend> {
// Context Backend static interface required
friend class gpu::Context;
@ -583,13 +438,13 @@ protected:
BufferState& operator=(const BufferState& other) = delete;
void reset() {
gpu::gl::reset(buffer);
gpu::reset(buffer);
offset = 0;
size = 0;
}
bool compare(const BufferPointer& buffer, GLintptr offset, GLsizeiptr size) {
const auto& self = *this;
return (self.offset == offset && self.size == size && gpu::gl::compare(self.buffer, buffer));
return (self.offset == offset && self.size == size && gpu::compare(self.buffer, buffer));
}
};

View file

@ -73,7 +73,7 @@ void GLBackend::do_advance(const Batch& batch, size_t paramOffset) {
}
void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) {
if (_stereo.isStereo() && !_pipeline._stateCache.scissorEnable) {
if (_stereo.isStereo() && !_pipeline._stateCache.flags.scissorEnable) {
qWarning("Clear without scissor in stereo mode");
}
@ -140,7 +140,7 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) {
}
// Apply scissor if needed and if not already on
bool doEnableScissor = (useScissor && (!_pipeline._stateCache.scissorEnable));
bool doEnableScissor = (useScissor && (!_pipeline._stateCache.flags.scissorEnable));
if (doEnableScissor) {
glEnable(GL_SCISSOR_TEST);
}

View file

@ -100,17 +100,17 @@ void GLBackend::do_setStateCullMode(int32 mode) {
}
void GLBackend::do_setStateFrontFaceClockwise(bool isClockwise) {
if (_pipeline._stateCache.frontFaceClockwise != isClockwise) {
if (_pipeline._stateCache.flags.frontFaceClockwise != isClockwise) {
static GLenum GL_FRONT_FACES[] = { GL_CCW, GL_CW };
glFrontFace(GL_FRONT_FACES[isClockwise]);
(void)CHECK_GL_ERROR();
_pipeline._stateCache.frontFaceClockwise = isClockwise;
_pipeline._stateCache.flags.frontFaceClockwise = isClockwise;
}
}
void GLBackend::do_setStateDepthClampEnable(bool enable) {
if (_pipeline._stateCache.depthClampEnable != enable) {
if (_pipeline._stateCache.flags.depthClampEnable != enable) {
#if !defined(USE_GLES)
if (enable) {
glEnable(GL_DEPTH_CLAMP);
@ -118,13 +118,13 @@ void GLBackend::do_setStateDepthClampEnable(bool enable) {
glDisable(GL_DEPTH_CLAMP);
}
(void)CHECK_GL_ERROR();
_pipeline._stateCache.depthClampEnable = enable;
_pipeline._stateCache.flags.depthClampEnable = enable;
#endif
}
}
void GLBackend::do_setStateScissorEnable(bool enable) {
if (_pipeline._stateCache.scissorEnable != enable) {
if (_pipeline._stateCache.flags.scissorEnable != enable) {
if (enable) {
glEnable(GL_SCISSOR_TEST);
} else {
@ -132,12 +132,12 @@ void GLBackend::do_setStateScissorEnable(bool enable) {
}
(void)CHECK_GL_ERROR();
_pipeline._stateCache.scissorEnable = enable;
_pipeline._stateCache.flags.scissorEnable = enable;
}
}
void GLBackend::do_setStateMultisampleEnable(bool enable) {
if (_pipeline._stateCache.multisampleEnable != enable) {
if (_pipeline._stateCache.flags.multisampleEnable != enable) {
#if !defined(USE_GLES)
if (enable) {
glEnable(GL_MULTISAMPLE);
@ -146,13 +146,13 @@ void GLBackend::do_setStateMultisampleEnable(bool enable) {
}
(void)CHECK_GL_ERROR();
_pipeline._stateCache.multisampleEnable = enable;
_pipeline._stateCache.flags.multisampleEnable = enable;
#endif
}
}
void GLBackend::do_setStateAntialiasedLineEnable(bool enable) {
if (_pipeline._stateCache.antialisedLineEnable != enable) {
if (_pipeline._stateCache.flags.antialisedLineEnable != enable) {
#if !defined(USE_GLES)
if (enable) {
glEnable(GL_LINE_SMOOTH);
@ -161,7 +161,7 @@ void GLBackend::do_setStateAntialiasedLineEnable(bool enable) {
}
(void)CHECK_GL_ERROR();
_pipeline._stateCache.antialisedLineEnable = enable;
_pipeline._stateCache.flags.antialisedLineEnable = enable;
#endif
}
}
@ -206,7 +206,7 @@ void GLBackend::do_setStateDepthTest(State::DepthTest test) {
if (CHECK_GL_ERROR()) {
qCDebug(gpulogging) << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled")
<< "Mask=" << (test.getWriteMask() ? "Write" : "no Write")
<< "Func=" << test.getFunction()
<< "Func=" << (uint16_t)test.getFunction()
<< "Raw=" << test.getRaw();
}
_pipeline._stateCache.depthTest = test;
@ -264,7 +264,7 @@ void GLBackend::do_setStateStencil(State::StencilActivation activation, State::S
}
void GLBackend::do_setStateAlphaToCoverageEnable(bool enable) {
if (_pipeline._stateCache.alphaToCoverageEnable != enable) {
if (_pipeline._stateCache.flags.alphaToCoverageEnable != enable) {
if (enable) {
glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
} else {
@ -272,7 +272,7 @@ void GLBackend::do_setStateAlphaToCoverageEnable(bool enable) {
}
(void)CHECK_GL_ERROR();
_pipeline._stateCache.alphaToCoverageEnable = enable;
_pipeline._stateCache.flags.alphaToCoverageEnable = enable;
}
}
@ -317,7 +317,7 @@ void GLBackend::do_setStateColorWriteMask(uint32 mask) {
mask & State::ColorMask::WRITE_ALPHA);
(void)CHECK_GL_ERROR();
_pipeline._stateCache.colorWriteMask = mask;
_pipeline._stateCache.colorWriteMask = (State::ColorMask)mask;
}
}

View file

@ -174,17 +174,17 @@ void getCurrentGLState(State::Data& state) {
{
GLint winding;
glGetIntegerv(GL_FRONT_FACE, &winding);
state.frontFaceClockwise = (winding == GL_CW);
state.flags.frontFaceClockwise = (winding == GL_CW);
#if defined(USE_GLES)
state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE_EXT);
state.antialisedLineEnable = false;
state.depthClampEnable = false;
state.flags.multisampleEnable = glIsEnabled(GL_MULTISAMPLE_EXT);
state.flags.antialisedLineEnable = false;
state.flags.depthClampEnable = false;
#else
state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE);
state.antialisedLineEnable = glIsEnabled(GL_LINE_SMOOTH);
state.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP);
state.flags.multisampleEnable = glIsEnabled(GL_MULTISAMPLE);
state.flags.antialisedLineEnable = glIsEnabled(GL_LINE_SMOOTH);
state.flags.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP);
#endif
state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST);
state.flags.scissorEnable = glIsEnabled(GL_SCISSOR_TEST);
}
{
if (glIsEnabled(GL_POLYGON_OFFSET_FILL)) {
@ -247,7 +247,7 @@ void getCurrentGLState(State::Data& state) {
state.sampleMask = mask;
}
{
state.alphaToCoverageEnable = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE);
state.flags.alphaToCoverageEnable = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE);
}
{
GLboolean isEnabled = glIsEnabled(GL_BLEND);
@ -272,10 +272,10 @@ void getCurrentGLState(State::Data& state) {
{
GLboolean mask[4];
glGetBooleanv(GL_COLOR_WRITEMASK, mask);
state.colorWriteMask = (mask[0] ? State::WRITE_RED : 0)
state.colorWriteMask = (State::ColorMask)((mask[0] ? State::WRITE_RED : 0)
| (mask[1] ? State::WRITE_GREEN : 0)
| (mask[2] ? State::WRITE_BLUE : 0)
| (mask[3] ? State::WRITE_ALPHA : 0);
| (mask[3] ? State::WRITE_ALPHA : 0));
}
(void)CHECK_GL_ERROR();

View file

@ -56,11 +56,11 @@ const GLState::Commands makeResetStateCommands() {
return {
std::make_shared<Command1I>(&GLBackend::do_setStateFillMode, DEFAULT.fillMode),
std::make_shared<Command1I>(&GLBackend::do_setStateCullMode, DEFAULT.cullMode),
std::make_shared<Command1B>(&GLBackend::do_setStateFrontFaceClockwise, DEFAULT.frontFaceClockwise),
std::make_shared<Command1B>(&GLBackend::do_setStateDepthClampEnable, DEFAULT.depthClampEnable),
std::make_shared<Command1B>(&GLBackend::do_setStateScissorEnable, DEFAULT.scissorEnable),
std::make_shared<Command1B>(&GLBackend::do_setStateMultisampleEnable, DEFAULT.multisampleEnable),
std::make_shared<Command1B>(&GLBackend::do_setStateAntialiasedLineEnable, DEFAULT.antialisedLineEnable),
std::make_shared<Command1B>(&GLBackend::do_setStateFrontFaceClockwise, DEFAULT.flags.frontFaceClockwise),
std::make_shared<Command1B>(&GLBackend::do_setStateDepthClampEnable, DEFAULT.flags.depthClampEnable),
std::make_shared<Command1B>(&GLBackend::do_setStateScissorEnable, DEFAULT.flags.scissorEnable),
std::make_shared<Command1B>(&GLBackend::do_setStateMultisampleEnable, DEFAULT.flags.multisampleEnable),
std::make_shared<Command1B>(&GLBackend::do_setStateAntialiasedLineEnable, DEFAULT.flags.antialisedLineEnable),
// Depth bias has 2 fields in State but really one call in GLBackend
CommandPointer(depthBiasCommand),
@ -75,7 +75,7 @@ const GLState::Commands makeResetStateCommands() {
std::make_shared<Command1U>(&GLBackend::do_setStateSampleMask, DEFAULT.sampleMask),
std::make_shared<Command1B>(&GLBackend::do_setStateAlphaToCoverageEnable, DEFAULT.alphaToCoverageEnable),
std::make_shared<Command1B>(&GLBackend::do_setStateAlphaToCoverageEnable, DEFAULT.flags.alphaToCoverageEnable),
std::make_shared<CommandBlend>(&GLBackend::do_setStateBlend, DEFAULT.blendFunction),

View file

@ -4,3 +4,4 @@ setup_hifi_library()
link_hifi_libraries(shared ktx shaders)
target_nsight()
target_json()

View file

@ -45,7 +45,7 @@ size_t Batch::_dataMax{ BATCH_PREALLOCATE_MIN };
size_t Batch::_objectsMax{ BATCH_PREALLOCATE_MIN };
size_t Batch::_drawCallInfosMax{ BATCH_PREALLOCATE_MIN };
Batch::Batch(const char* name) {
Batch::Batch(const std::string& name) {
_name = name;
_commands.reserve(_commandsMax);
_commandOffsets.reserve(_commandOffsetsMax);
@ -64,7 +64,7 @@ Batch::~Batch() {
_drawCallInfosMax = std::max(_drawCallInfos.size(), _drawCallInfosMax);
}
void Batch::setName(const char* name) {
void Batch::setName(const std::string& name) {
_name = name;
}
@ -96,7 +96,7 @@ void Batch::clear() {
_textureTables.clear();
_transforms.clear();
_name = nullptr;
_name.clear();
_invalidModel = true;
_currentModel = Transform();
_drawcallUniform = 0;

View file

@ -89,14 +89,14 @@ public:
void captureDrawCallInfo();
void captureNamedDrawCallInfo(std::string name);
Batch(const char* name = nullptr);
Batch(const std::string& name = "");
// Disallow copy construction and assignement of batches
Batch(const Batch& batch) = delete;
Batch& operator=(const Batch& batch) = delete;
~Batch();
void setName(const char* name);
const char* getName() const { return _name; }
void setName(const std::string& name);
const std::string& getName() const { return _name; }
void clear();
// Batches may need to override the context level stereo settings
@ -440,6 +440,18 @@ public:
};
};
using CommandHandler = std::function<void(Command, const Param*)>;
void forEachCommand(const CommandHandler& handler) const {
size_t count = _commands.size();
for (size_t i = 0; i < count; ++i) {
const auto command = _commands[i];
const auto offset = _commandOffsets[i];
const Param* params = _params.data() + offset;
handler(command, params);
}
}
typedef Cache<BufferPointer>::Vector BufferCaches;
typedef Cache<TexturePointer>::Vector TextureCaches;
typedef Cache<TextureTablePointer>::Vector TextureTableCaches;
@ -519,7 +531,7 @@ public:
bool _enableSkybox { false };
protected:
const char* _name;
std::string _name;
friend class Context;
friend class Frame;

View file

@ -149,7 +149,8 @@ protected:
Size _end{ 0 };
Sysmem _sysmem;
friend class Serializer;
friend class Deserializer;
friend class BufferView;
friend class Frame;
friend class Batch;

View file

@ -47,10 +47,7 @@ Context::Context(const Context& context) {
}
Context::~Context() {
for (auto batch : _batchPool) {
delete batch;
}
_batchPool.clear();
clearBatches();
_syncedPrograms.clear();
}
@ -97,6 +94,12 @@ FramePointer Context::endFrame() {
return result;
}
void Context::executeBatch(const char* name, std::function<void(Batch&)> lambda) const {
auto batch = acquireBatch(name);
lambda(*batch);
executeBatch(*batch);
}
void Context::executeBatch(Batch& batch) const {
PROFILE_RANGE(render_gpu, __FUNCTION__);
batch.flush();
@ -117,28 +120,27 @@ void Context::executeFrame(const FramePointer& frame) const {
PROFILE_RANGE(render_gpu, __FUNCTION__);
// Grab the stats at the around the frame and delta to have a consistent sampling
ContextStats beginStats;
static ContextStats beginStats;
getStats(beginStats);
// FIXME? probably not necessary, but safe
consumeFrameUpdates(frame);
_backend->setStereoState(frame->stereoState);
{
Batch beginBatch("Context::executeFrame::begin");
_frameRangeTimer->begin(beginBatch);
_backend->render(beginBatch);
// Execute the frame rendering commands
for (auto& batch : frame->batches) {
_backend->render(*batch);
}
Batch endBatch("Context::executeFrame::end");
_frameRangeTimer->end(endBatch);
_backend->render(endBatch);
executeBatch("Context::executeFrame::begin", [&](Batch& batch){
batch.pushProfileRange("Frame");
_frameRangeTimer->begin(batch);
});
// Execute the frame rendering commands
for (auto& batch : frame->batches) {
_backend->render(*batch);
}
executeBatch("Context::executeFrame::end", [&](Batch& batch){
batch.popProfileRange();
_frameRangeTimer->end(batch);
});
ContextStats endStats;
static ContextStats endStats;
getStats(endStats);
_frameStats.evalDelta(beginStats, endStats);
}
@ -381,6 +383,16 @@ void Context::processProgramsToSync() {
}
}
std::mutex Context::_batchPoolMutex;
std::list<Batch*> Context::_batchPool;
void Context::clearBatches() {
for (auto batch : _batchPool) {
delete batch;
}
_batchPool.clear();
}
BatchPointer Context::acquireBatch(const char* name) {
Batch* rawBatch = nullptr;
{
@ -393,8 +405,10 @@ BatchPointer Context::acquireBatch(const char* name) {
if (!rawBatch) {
rawBatch = new Batch();
}
rawBatch->setName(name);
return BatchPointer(rawBatch, [this](Batch* batch) { releaseBatch(batch); });
if (name) {
rawBatch->setName(name);
}
return BatchPointer(rawBatch, [](Batch* batch) { releaseBatch(batch); });
}
void Context::releaseBatch(Batch* batch) {
@ -406,7 +420,7 @@ void Context::releaseBatch(Batch* batch) {
void gpu::doInBatch(const char* name,
const std::shared_ptr<gpu::Context>& context,
const std::function<void(Batch& batch)>& f) {
auto batch = context->acquireBatch(name);
auto batch = Context::acquireBatch(name);
f(*batch);
context->appendFrameBatch(batch);
}

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